diff --git a/DEPS b/DEPS
index 721cd10..00ef99c 100644
--- a/DEPS
+++ b/DEPS
@@ -181,11 +181,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '447a4e0982e0dd7b8cc49d4cbbd613d2c780c93b',
+  'skia_revision': 'cedab522227a909f57b270081af6130133b2c70b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'd9e36f4a5808ed584d118ccf0a23d1290e5278f8',
+  'v8_revision': 'b28ca6f38f9b637fd639be49546867e07bcaa0ac',
   # 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.
@@ -193,11 +193,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '8075d3edde6cbce9d1fe48c2e2417de5f5f71915',
+  'angle_revision': '7f8b6e3fabbd579065944112095ffb69a869316b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '11cb891a01a2b47cc2135ef19ca18046662476e0',
+  'swiftshader_revision': 'b766e5e7fbf42e48be36afc80e2c448e339c599e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -244,7 +244,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'c533f76b91f8b0d9098954032ef3d62e1bd908d0',
+  'catapult_revision': 'c8ebd366bc298f77563a494be710b3275bc898b1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -312,7 +312,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'f2ed2482a449f48a74632642e3ae9e67fc08be51',
+  'dawn_revision': '3003aa622b2fc452a66707922b99a64f89b84bce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -544,7 +544,7 @@
   },
 
   'src/ios/third_party/material_font_disk_loader_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-foundation/material-font-disk-loader-ios.git' + '@' + '8e30188777b016182658fbaa0a4a020a48183224',
+      'url': Var('chromium_git') + '/external/github.com/material-foundation/material-font-disk-loader-ios.git' + '@' + '93acc021e3034898716028822cb802a3a816be7e',
       'condition': 'checkout_ios',
   },
 
@@ -554,7 +554,7 @@
   },
 
   'src/ios/third_party/material_roboto_font_loader_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-foundation/material-roboto-font-loader-ios.git' + '@' + 'bc63eabbbd1e14cee0779b05827e08db2e413553',
+      'url': Var('chromium_git') + '/external/github.com/material-foundation/material-roboto-font-loader-ios.git' + '@' + '4aa51e906e5671c71d24e991f1f10d782a58409f',
       'condition': 'checkout_ios',
   },
 
@@ -879,7 +879,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '53ad544d73bcefd23e9f684e3476f9fbcf51cfc3',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '2cdf874112460094703a6c37c33eb5fd2d3dca7a',
       'condition': 'checkout_linux',
   },
 
@@ -904,7 +904,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8effa4d0637c83d2e8cd5869c90ced50cdf3c099',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd339e36642df06f0e26f9a5143ce044bf9376b72',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1297,7 +1297,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '42a5f6fa3728b6ba97882b14a56386e0520712aa',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'e5fb5497abf6dc6b59eab0d44f29748313cc8f9d',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1501,7 +1501,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'ec18cc3262922e7dcdbe70243c6f40606f979144',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b7dc45f8e8119d367b7513045db7cce68861709f',
+    Var('webrtc_git') + '/src.git' + '@' + '06df1e1c4655a1048bfeb30e090ab52005f1e841',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1571,7 +1571,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@fa01d48c5cede4b8fe7199796c4c774c867c81db',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8f5aa560c98d070293830e4a9272957f5428263e',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/docs/commandline-flags.md b/android_webview/docs/commandline-flags.md
index 57eecee9..f9e88cbd 100644
--- a/android_webview/docs/commandline-flags.md
+++ b/android_webview/docs/commandline-flags.md
@@ -93,6 +93,7 @@
    useful for identifying which content in the app is rendered by a WebView.
  * `--force-enable-metrics-reporting`: enable UMA metrics reporting (does not
    override app opt-out)
+ * `--finch-seed-expiration-age=0 --finch-seed-min-update-period=0 --finch-seed-min-download-period=0 --finch-seed-ignore-pending-download`: always request a new finch seed when an app starts
 
 WebView also defines its own flags and Features:
 
diff --git a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
index 61f2c70..3b2145d 100644
--- a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
+++ b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
@@ -17,6 +17,7 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.android_webview.common.AwSwitches;
 import org.chromium.android_webview.common.services.IVariationsSeedServer;
 import org.chromium.android_webview.common.services.ServiceNames;
 import org.chromium.android_webview.common.variations.VariationsUtils;
@@ -144,12 +145,15 @@
         if (lastRequestTime == 0) {
             return false;
         }
-        return now < lastRequestTime + MAX_REQUEST_PERIOD_MILLIS;
+        long maxRequestPeriodMillis = VariationsUtils.getDurationSwitchValueInMillis(
+                AwSwitches.FINCH_SEED_MIN_UPDATE_PERIOD, MAX_REQUEST_PERIOD_MILLIS);
+        return now < lastRequestTime + maxRequestPeriodMillis;
     }
 
     private boolean isSeedExpired(long seedFileTime) {
-        long expirationTime = seedFileTime + SEED_EXPIRATION_MILLIS;
-        return getCurrentTimeMillis() > expirationTime;
+        long expirationDuration = VariationsUtils.getDurationSwitchValueInMillis(
+                AwSwitches.FINCH_SEED_EXPIRATION_AGE, SEED_EXPIRATION_MILLIS);
+        return getCurrentTimeMillis() > seedFileTime + expirationDuration;
     }
 
     // Loads our local copy of the seed, if any, and then renames our local copy and/or requests a
diff --git a/android_webview/java/src/org/chromium/android_webview/common/AwSwitches.java b/android_webview/java/src/org/chromium/android_webview/common/AwSwitches.java
index 7188652..c5a99b7 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/AwSwitches.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/AwSwitches.java
@@ -33,6 +33,28 @@
     public static final String WEBVIEW_SAFEBROWSING_BLOCK_ALL_RESOURCES =
             "webview-safebrowsing-block-all-resources";
 
+    // The length of time in seconds that an app's copy of the variations seed should be considered
+    // fresh. If an app's seed is older than this, a new seed will be requested from WebView's
+    // IVariationsSeedServer.
+    // No native switch.
+    public static final String FINCH_SEED_EXPIRATION_AGE = "finch-seed-expiration-age";
+
+    // Forces WebView's service to always schedule a new variations seed download job, even if one
+    // is already pending.
+    // No native switch.
+    public static final String FINCH_SEED_IGNORE_PENDING_DOWNLOAD =
+            "finch-seed-ignore-pending-download";
+
+    // The minimum amount of time in seconds that WebView's service will wait between two
+    // variations seed downloads from the variations server.
+    // No native switch.
+    public static final String FINCH_SEED_MIN_DOWNLOAD_PERIOD = "finch-seed-min-download-period";
+
+    // The minimum amount of time in seconds that the embedded WebView implementation will wait
+    // between two requests to WebView's service for a new variations seed.
+    // No native switch.
+    public static final String FINCH_SEED_MIN_UPDATE_PERIOD = "finch-seed-min-update-period";
+
     // Do not instantiate this class.
     private AwSwitches() {}
 }
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 58899499..a014398 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
@@ -26,6 +26,15 @@
             Flag.commandLine("show-composited-layer-borders",
                     "Renders a border around compositor layers to help debug and study layer "
                             + "compositing."),
+            Flag.commandLine(AwSwitches.FINCH_SEED_EXPIRATION_AGE + "=0",
+                    "Forces all variations seeds to be considered stale."),
+            Flag.commandLine(AwSwitches.FINCH_SEED_IGNORE_PENDING_DOWNLOAD,
+                    "Forces the WebView service to reschedule a variations seed download job even "
+                            + "if one is already pending."),
+            Flag.commandLine(AwSwitches.FINCH_SEED_MIN_DOWNLOAD_PERIOD + "=0",
+                    "Disables throttling of variations seed download jobs."),
+            Flag.commandLine(AwSwitches.FINCH_SEED_MIN_UPDATE_PERIOD + "=0",
+                    "Disables throttling of new variations seed requests to the WebView service."),
             Flag.commandLine("webview-log-js-console-messages",
                     "Mirrors JavaScript console messages to system logs."),
             Flag.commandLine(AwSwitches.CRASH_UPLOADS_ENABLED_FOR_TESTING_SWITCH,
diff --git a/android_webview/java/src/org/chromium/android_webview/common/variations/VariationsUtils.java b/android_webview/java/src/org/chromium/android_webview/common/variations/VariationsUtils.java
index d69249d..fb322634 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/variations/VariationsUtils.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/variations/VariationsUtils.java
@@ -5,12 +5,14 @@
 package org.chromium.android_webview.common.variations;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import com.google.protobuf.ByteString;
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import org.chromium.android_webview.proto.AwVariationsSeedOuterClass.AwVariationsSeed;
 import org.chromium.base.BuildInfo;
+import org.chromium.base.CommandLine;
 import org.chromium.base.Log;
 import org.chromium.base.PathUtils;
 import org.chromium.components.variations.firstrun.VariationsSeedFetcher.SeedInfo;
@@ -22,6 +24,7 @@
 import java.io.IOException;
 import java.text.ParseException;
 import java.util.Date;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Utilities for manipulating variations seeds, used by both WebView and WebView's services.
@@ -77,10 +80,15 @@
 
     // Creates/updates the timestamp with the current time.
     public static void updateStampTime() {
+        updateStampTime(new Date().getTime());
+    }
+
+    // Creates/updates the timestamp with the specified time.
+    @VisibleForTesting
+    public static void updateStampTime(long now) {
         File file = getStampFile();
         try {
             if (!file.createNewFile()) {
-                long now = (new Date()).getTime();
                 file.setLastModified(now);
             }
         } catch (IOException e) {
@@ -157,6 +165,22 @@
         }
     }
 
+    // Returns the value of the |switchName| flag converted from seconds to milliseconds. If the
+    // |switchName| flag isn't present, or contains an invalid value, |defaultValueMillis| will be
+    // returned.
+    public static long getDurationSwitchValueInMillis(String switchName, long defaultValueMillis) {
+        CommandLine cli = CommandLine.getInstance();
+        if (!cli.hasSwitch(switchName)) {
+            return defaultValueMillis;
+        }
+        try {
+            return TimeUnit.SECONDS.toMillis(Long.parseLong(cli.getSwitchValue(switchName)));
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "Invalid value for flag " + switchName, e);
+            return defaultValueMillis;
+        }
+    }
+
     // Logs an INFO message if running in a debug build of Android.
     public static void debugLog(String message) {
         if (BuildInfo.isDebugAndroid()) {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwVariationsSeedFetcherTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwVariationsSeedFetcherTest.java
index e6e3930..fc2e3bf0 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwVariationsSeedFetcherTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwVariationsSeedFetcherTest.java
@@ -21,11 +21,13 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.android_webview.common.AwSwitches;
 import org.chromium.android_webview.common.variations.VariationsUtils;
 import org.chromium.android_webview.services.AwVariationsSeedFetcher;
 import org.chromium.android_webview.test.util.VariationsTestUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.components.background_task_scheduler.TaskIds;
 import org.chromium.components.variations.firstrun.VariationsSeedFetcher;
 
@@ -200,6 +202,50 @@
         }
     }
 
+    // Tests that the --finch-seed-min-download-period flag can override the job throttling.
+    @Test
+    @SmallTest
+    @CommandLineFlags.Add(AwSwitches.FINCH_SEED_MIN_DOWNLOAD_PERIOD + "=0")
+    public void testFinchSeedMinDownloadPeriodFlag() throws IOException {
+        File stamp = VariationsUtils.getStampFile();
+        try {
+            // Create a recent stamp file that would usually prevent job scheduling.
+            Assert.assertFalse("Stamp file already exists", stamp.exists());
+            Assert.assertTrue("Failed to create stamp file", stamp.createNewFile());
+
+            AwVariationsSeedFetcher.scheduleIfNeeded();
+
+            mScheduler.assertScheduled();
+        } finally {
+            mScheduler.clear();
+            VariationsTestUtils.deleteSeeds(); // Remove the stamp file.
+        }
+    }
+
+    // Tests that the --finch-seed-ignore-pending-download flag results in jobs being rescheduled.
+    @Test
+    @SmallTest
+    @CommandLineFlags.Add(AwSwitches.FINCH_SEED_IGNORE_PENDING_DOWNLOAD)
+    public void testFinchSeedIgnorePendingDownloadFlag() {
+        File stamp = VariationsUtils.getStampFile();
+        try {
+            AwVariationsSeedFetcher.scheduleIfNeeded();
+            JobInfo originalJob = mScheduler.getPendingJob(JOB_ID);
+            Assert.assertNotNull("Job should have been scheduled", originalJob);
+
+            AwVariationsSeedFetcher.scheduleIfNeeded();
+
+            // Check that the job got rescheduled.
+            JobInfo rescheduledJob = mScheduler.getPendingJob(JOB_ID);
+            Assert.assertNotNull("Job should have been rescheduled", rescheduledJob);
+            Assert.assertNotSame(
+                    "Rescheduled job should not be equal to the originally scheduled job",
+                    originalJob, rescheduledJob);
+        } finally {
+            mScheduler.clear();
+        }
+    }
+
     @Test
     @SmallTest
     public void testFetch() throws IOException, TimeoutException {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java
index cc52ee8..a5ce562 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java
@@ -19,12 +19,14 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.android_webview.VariationsSeedLoader;
+import org.chromium.android_webview.common.AwSwitches;
 import org.chromium.android_webview.common.variations.VariationsUtils;
 import org.chromium.android_webview.test.services.MockVariationsSeedServer;
 import org.chromium.android_webview.test.util.VariationsTestUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
 
 import java.io.File;
 import java.io.IOException;
@@ -401,4 +403,42 @@
                         VariationsSeedLoader.APP_SEED_FRESHNESS_HISTOGRAM_NAME,
                         (int) TimeUnit.HOURS.toMinutes(seedAgeHours)));
     }
+
+    // Tests that the finch-seed-expiration-age flag works.
+    @Test
+    @MediumTest
+    @CommandLineFlags.Add(AwSwitches.FINCH_SEED_EXPIRATION_AGE + "=0")
+    public void testFinchSeedExpirationAgeFlag() throws Exception {
+        try {
+            // Create a new seed file with a recent timestamp.
+            File oldFile = VariationsUtils.getSeedFile();
+            VariationsTestUtils.writeMockSeed(oldFile);
+            oldFile.setLastModified(CURRENT_TIME_MILLIS);
+
+            boolean seedRequested = runTestLoaderBlocking();
+
+            Assert.assertTrue("Seed file should be requested", seedRequested);
+        } finally {
+            VariationsTestUtils.deleteSeeds();
+        }
+    }
+
+    // Tests that the finch-seed-min-update-period flag overrides the seed request throttling.
+    @Test
+    @MediumTest
+    @CommandLineFlags.Add(AwSwitches.FINCH_SEED_MIN_UPDATE_PERIOD + "=0")
+    public void testFinchSeedMinUpdatePeriodFlag() throws Exception {
+        try {
+            // Update the last modified time of the stamp file to simulate having just requested a
+            // new seed from the service.
+            VariationsUtils.getStampFile().createNewFile();
+            VariationsUtils.updateStampTime(CURRENT_TIME_MILLIS);
+
+            boolean seedRequested = runTestLoaderBlocking();
+
+            Assert.assertTrue("Seed file should be requested", seedRequested);
+        } finally {
+            VariationsTestUtils.deleteSeeds();
+        }
+    }
 }
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
index 3e431dc2..6ac2dcf 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
@@ -13,7 +13,9 @@
 import android.content.Context;
 import android.os.Build;
 
+import org.chromium.android_webview.common.AwSwitches;
 import org.chromium.android_webview.common.variations.VariationsUtils;
+import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.compat.ApiHelperForN;
@@ -86,7 +88,8 @@
         if (scheduler == null) return;
 
         // Check if it's already scheduled.
-        if (getPendingJob(scheduler, JOB_ID) != null) {
+        if (!CommandLine.getInstance().hasSwitch(AwSwitches.FINCH_SEED_IGNORE_PENDING_DOWNLOAD)
+                && getPendingJob(scheduler, JOB_ID) != null) {
             VariationsUtils.debugLog("Seed download job already scheduled");
             return;
         }
@@ -95,7 +98,9 @@
         long lastRequestTime = VariationsUtils.getStampTime();
         if (lastRequestTime != 0) {
             long now = (new Date()).getTime();
-            if (now < lastRequestTime + MIN_JOB_PERIOD_MILLIS) {
+            long minJobPeriodMillis = VariationsUtils.getDurationSwitchValueInMillis(
+                    AwSwitches.FINCH_SEED_MIN_DOWNLOAD_PERIOD, MIN_JOB_PERIOD_MILLIS);
+            if (now < lastRequestTime + minJobPeriodMillis) {
                 VariationsUtils.debugLog("Throttling seed download job");
                 return;
             }
diff --git a/android_webview/renderer/aw_render_frame_ext.cc b/android_webview/renderer/aw_render_frame_ext.cc
index 0cdccb32..a0831d5 100644
--- a/android_webview/renderer/aw_render_frame_ext.cc
+++ b/android_webview/renderer/aw_render_frame_ext.cc
@@ -281,7 +281,7 @@
     return;
 
   const blink::WebHitTestResult result = webview->HitTestResultForTap(
-      blink::WebPoint(touch_center.x(), touch_center.y()),
+      gfx::Point(touch_center.x(), touch_center.y()),
       blink::WebSize(touch_area.width(), touch_area.height()));
   AwHitTestData data;
 
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 33fee216..c0fd830e7 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -764,16 +764,14 @@
   }
   users_ = std::move(new_users);
 
-  // If there are no users, show gaia signin if login, otherwise crash.
-  if (users.empty()) {
-    LOG_IF(FATAL, screen_type_ != LockScreen::ScreenType::kLogin)
-        << "Empty user list received";
+  // If there are no users, show gaia signin if login.
+  if (users.empty() && screen_type_ == LockScreen::ScreenType::kLogin) {
     Shell::Get()->login_screen_controller()->ShowGaiaSignin(
         false /*can_close*/, EmptyAccountId() /*prefilled_account*/);
     return;
   }
 
-  // Allocate layout and big user, which are common between all densities.
+  // Allocate layout which is common between all densities.
   auto* main_layout =
       main_view_->SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::Orientation::kHorizontal));
@@ -781,20 +779,23 @@
       views::BoxLayout::MainAxisAlignment::kCenter);
   main_layout->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kCenter);
-  primary_big_view_ = AllocateLoginBigUserView(users[0], true /*is_primary*/);
 
-  // Build layout for additional users.
-  if (users.size() <= 2)
-    CreateLowDensityLayout(users);
-  else if (users.size() >= 3 && users.size() <= 6)
-    CreateMediumDensityLayout(users);
-  else if (users.size() >= 7)
-    CreateHighDensityLayout(users, main_layout);
+  if (!users.empty()) {
+    primary_big_view_ = AllocateLoginBigUserView(users[0], true /*is_primary*/);
 
-  LayoutAuth(primary_big_view_, opt_secondary_big_view_, false /*animate*/);
+    // Build layout for additional users.
+    if (users.size() <= 2)
+      CreateLowDensityLayout(users);
+    else if (users.size() >= 3 && users.size() <= 6)
+      CreateMediumDensityLayout(users);
+    else if (users.size() >= 7)
+      CreateHighDensityLayout(users, main_layout);
 
-  // Big user may be the same if we already built lock screen.
-  OnBigUserChanged();
+    LayoutAuth(primary_big_view_, opt_secondary_big_view_, false /*animate*/);
+
+    // Big user may be the same if we already built lock screen.
+    OnBigUserChanged();
+  }
 
   // Force layout.
   PreferredSizeChanged();
@@ -802,7 +803,7 @@
 
   // If one of the child views had focus before we deleted them, then this view
   // will get focused. Move focus back to the primary big view.
-  if (HasFocus())
+  if (primary_big_view_ && HasFocus())
     primary_big_view_->RequestFocus();
 }
 
@@ -1885,7 +1886,7 @@
 
 LoginBigUserView* LockContentsView::CurrentBigUserView() {
   if (opt_secondary_big_view_ && opt_secondary_big_view_->IsAuthEnabled()) {
-    DCHECK(!primary_big_view_->IsAuthEnabled());
+    DCHECK(!primary_big_view_ || !primary_big_view_->IsAuthEnabled());
     return opt_secondary_big_view_;
   }
 
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index a5119ae..154a84b7 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -54,15 +54,18 @@
 #include "chromeos/dbus/power_manager/suspend.pb.h"
 #include "components/prefs/pref_service.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/test/display_manager_test_api.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
 using ::testing::_;
+using ::testing::IsNull;
 using ::testing::Mock;
 
 namespace ash {
@@ -2867,4 +2870,19 @@
       << "The hotseat widget should not appear on the lock screen.";
 }
 
+TEST_F(LockContentsViewUnitTest, NoUsersToShow) {
+  // Build lock screen with 0 users.
+  auto* contents = new LockContentsView(
+      mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
+  std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
+  LockContentsView::TestApi test_api(contents);
+
+  // Verify that primary big view is null.
+  EXPECT_THAT(test_api.primary_big_view(), IsNull());
+  // Verify that the main view has no children.
+  EXPECT_TRUE(test_api.main_view()->children().empty());
+}
+
 }  // namespace ash
diff --git a/ash/shelf/hotseat_transition_animator.cc b/ash/shelf/hotseat_transition_animator.cc
index c3de531..75307a6 100644
--- a/ash/shelf/hotseat_transition_animator.cc
+++ b/ash/shelf/hotseat_transition_animator.cc
@@ -84,6 +84,9 @@
 
 void HotseatTransitionAnimator::OnImplicitAnimationsCompleted() {
   std::move(animation_complete_callback_).Run();
+
+  if (test_observer_)
+    test_observer_->OnTransitionTestAnimationEnded();
 }
 
 void HotseatTransitionAnimator::OnTabletModeStarting() {
@@ -102,6 +105,10 @@
   tablet_mode_transitioning_ = false;
 }
 
+void HotseatTransitionAnimator::SetTestObserver(TestObserver* test_observer) {
+  test_observer_ = test_observer;
+}
+
 void HotseatTransitionAnimator::DoAnimation(HotseatState old_state,
                                             HotseatState new_state) {
   if (!ShouldDoAnimation(old_state, new_state))
@@ -119,17 +126,19 @@
   target_bounds.set_y(
       animating_to_shown_hotseat ? ShelfConfig::Get()->system_shelf_size() : 0);
   shelf_widget_->GetAnimatingBackground()->SetBounds(target_bounds);
+  shelf_widget_->GetAnimatingDragHandle()->SetBounds(
+      shelf_widget_->GetDragHandle()->bounds());
 
   int starting_y;
+  // This animation is triggered after bounds have been set in the shelf, or
+  // while the shelf is beginning to animate to new bounds. To prevent the
+  // background from jumping in either case, adjust the y position to account
+  // for the current size of the |shelf_widget_|.
   if (animating_to_shown_hotseat) {
-    // This animation is triggered after bounds have been set in the shelf, or
-    // while the shelf is beginning to animate to new bounds. To prevent the
-    // background from jumping in either case, adjust the y position to account
-    // for the current size of the |shelf_widget_|.
     starting_y = shelf_widget_->GetWindowBoundsInScreen().height() -
                  ShelfConfig::Get()->in_app_shelf_size();
   } else {
-    starting_y = ShelfConfig::Get()->shelf_size();
+    starting_y = shelf_widget_->GetWindowBoundsInScreen().height();
   }
   gfx::Transform transform;
   const int y_offset = starting_y - target_bounds.y();
diff --git a/ash/shelf/hotseat_transition_animator.h b/ash/shelf/hotseat_transition_animator.h
index abf41a6e..deb0ee2 100644
--- a/ash/shelf/hotseat_transition_animator.h
+++ b/ash/shelf/hotseat_transition_animator.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SHELF_HOTSEAT_TRANSITION_ANIMATOR_H_
 #define ASH_SHELF_HOTSEAT_TRANSITION_ANIMATOR_H_
 
+#include "ash/ash_export.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/tablet_mode_observer.h"
 #include "base/callback.h"
@@ -17,9 +18,16 @@
 
 // Makes it appear that the background of the shelf and hotseat animate to/from
 // one another.
-class HotseatTransitionAnimator : public TabletModeObserver,
-                                  public ui::ImplicitAnimationObserver {
+class ASH_EXPORT HotseatTransitionAnimator
+    : public TabletModeObserver,
+      public ui::ImplicitAnimationObserver {
  public:
+  class TestObserver {
+   public:
+    virtual ~TestObserver() = default;
+    virtual void OnTransitionTestAnimationEnded() = 0;
+  };
+
   class Observer : public base::CheckedObserver {
    public:
     // Called when hotseat transition animations begin.
@@ -48,6 +56,9 @@
   void OnTabletModeEnding() override;
   void OnTabletModeEnded() override;
 
+  // Set the test observer to watch for animations completed.
+  void SetTestObserver(TestObserver* test_observer);
+
  private:
   class TransitionAnimationMetricsReporter;
   // Starts the animation between |old_state| and |target_state|.
@@ -72,6 +83,9 @@
 
   base::ObserverList<Observer> observers_;
 
+  // A test observer used to wait for the hotseat transition animation.
+  TestObserver* test_observer_ = nullptr;
+
   // Metric reporter for hotseat transitions.
   std::unique_ptr<TransitionAnimationMetricsReporter>
       animation_metrics_reporter_;
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index c32d1cd9..d75113d 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -143,6 +143,8 @@
 
   ui::Layer* opaque_background() { return &opaque_background_; }
   ui::Layer* animating_background() { return &animating_background_; }
+  ui::Layer* animating_drag_handle() { return &animating_drag_handle_; }
+  views::View* drag_handle() { return drag_handle_; }
 
  private:
   // Whether |opaque_background_| is explicitly hidden during an animation.
@@ -158,6 +160,9 @@
   // A background layer used to animate hotseat transitions.
   ui::Layer animating_background_;
 
+  // A layer to animate the drag handle during hotseat transitions.
+  ui::Layer animating_drag_handle_;
+
   // A drag handle shown in tablet mode when we are not on the home screen.
   // Owned by the view hierarchy.
   views::View* drag_handle_ = nullptr;
@@ -176,9 +181,11 @@
     : shelf_widget_(shelf_widget),
       focus_cycler_(nullptr),
       opaque_background_(ui::LAYER_SOLID_COLOR),
-      animating_background_(ui::LAYER_SOLID_COLOR) {
+      animating_background_(ui::LAYER_SOLID_COLOR),
+      animating_drag_handle_(ui::LAYER_SOLID_COLOR) {
   opaque_background_.SetName("shelf/Background");
   animating_background_.SetName("shelf/Animation");
+  animating_background_.Add(&animating_drag_handle_);
 
   DCHECK(shelf_widget_);
   set_owned_by_client();  // Deleted by DeleteDelegate().
@@ -205,6 +212,11 @@
   drag_handle_->layer()->SetRoundedCornerRadius(
       {radius, radius, radius, radius});
   drag_handle_->SetSize(kDragHandleSize);
+
+  animating_drag_handle_.SetColor(ripple_attributes.base_color);
+  animating_drag_handle_.SetOpacity(ripple_attributes.inkdrop_opacity + 0.075);
+  animating_drag_handle_.SetRoundedCornerRadius(
+      {radius, radius, radius, radius});
 }
 
 ShelfWidget::DelegateView::~DelegateView() = default;
@@ -236,11 +248,13 @@
 void ShelfWidget::DelegateView::HideOpaqueBackground() {
   hide_background_for_transitions_ = true;
   opaque_background_.SetVisible(false);
+  drag_handle_->SetVisible(false);
 }
 
 void ShelfWidget::DelegateView::ShowOpaqueBackground() {
   hide_background_for_transitions_ = false;
   UpdateOpaqueBackground();
+  UpdateDragHandle();
   UpdateBackgroundBlur();
 }
 
@@ -338,7 +352,8 @@
 
 void ShelfWidget::DelegateView::UpdateDragHandle() {
   if (!IsInTabletMode() || !ShelfConfig::Get()->is_in_app() ||
-      !chromeos::switches::ShouldShowShelfHotseat()) {
+      !chromeos::switches::ShouldShowShelfHotseat() ||
+      hide_background_for_transitions_) {
     drag_handle_->SetVisible(false);
     return;
   }
@@ -347,9 +362,8 @@
   const int x = (shelf_widget_->GetClientAreaBoundsInScreen().width() -
                  kDragHandleSize.width()) /
                 2;
-  const int y = (shelf_widget_->GetClientAreaBoundsInScreen().height() -
-                 kDragHandleSize.height()) /
-                2;
+  const int y =
+      (ShelfConfig::Get()->in_app_shelf_size() - kDragHandleSize.height()) / 2;
   drag_handle_->SetBounds(x, y, kDragHandleSize.width(),
                           kDragHandleSize.height());
 }
@@ -444,6 +458,14 @@
   return delegate_view_->animating_background();
 }
 
+ui::Layer* ShelfWidget::GetAnimatingDragHandle() {
+  return delegate_view_->animating_drag_handle();
+}
+
+views::View* ShelfWidget::GetDragHandle() {
+  return delegate_view_->drag_handle();
+}
+
 void ShelfWidget::ForceToHideHotseat() {
   if (!is_hotseat_forced_to_show_)
     return;
diff --git a/ash/shelf/shelf_widget.h b/ash/shelf/shelf_widget.h
index 6b644a5..d30c67b0 100644
--- a/ash/shelf/shelf_widget.h
+++ b/ash/shelf/shelf_widget.h
@@ -136,6 +136,13 @@
   // background.
   ui::Layer* GetAnimatingBackground();
 
+  // Gets the layer used to animate drag handle transitions between in-app and
+  // home.
+  ui::Layer* GetAnimatingDragHandle();
+
+  // Gets the view used to display the drag handle on the in-app shelf.
+  views::View* GetDragHandle();
+
   // Internal implementation detail. Do not expose outside of tests.
   ShelfView* shelf_view_for_testing() const {
     return hotseat_widget()->GetShelfView();
@@ -145,6 +152,10 @@
     return &background_animator_;
   }
 
+  HotseatTransitionAnimator* hotseat_transition_animator_for_testing() {
+    return hotseat_transition_animator_.get();
+  }
+
  private:
   class DelegateView;
   friend class DelegateView;
diff --git a/ash/shelf/shelf_widget_unittest.cc b/ash/shelf/shelf_widget_unittest.cc
index 73e2d9b3..ce009c8 100644
--- a/ash/shelf/shelf_widget_unittest.cc
+++ b/ash/shelf/shelf_widget_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
+#include "ash/shelf/hotseat_transition_animator.h"
 #include "ash/shelf/login_shelf_view.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_layout_manager.h"
@@ -26,10 +27,12 @@
 #include "ash/test_shell_delegate.h"
 #include "ash/wm/window_util.h"
 #include "base/bind_helpers.h"
+#include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/session_manager/session_manager_types.h"
 #include "ui/aura/window_event_dispatcher.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/display/display.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
@@ -369,6 +372,68 @@
   }
 }
 
+class TransitionAnimationWaiter
+    : public HotseatTransitionAnimator::TestObserver {
+ public:
+  explicit TransitionAnimationWaiter(
+      HotseatTransitionAnimator* hotseat_transition_animator)
+      : hotseat_transition_animator_(hotseat_transition_animator) {
+    hotseat_transition_animator_->SetTestObserver(this);
+  }
+  ~TransitionAnimationWaiter() override {
+    hotseat_transition_animator_->SetTestObserver(nullptr);
+  }
+
+  void Wait() {
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
+  }
+
+ private:
+  void OnTransitionTestAnimationEnded() override {
+    DCHECK(run_loop_.get());
+    run_loop_->Quit();
+  }
+
+  HotseatTransitionAnimator* hotseat_transition_animator_ = nullptr;
+  std::unique_ptr<base::RunLoop> run_loop_;
+};
+
+// Tests the drag handle bounds and visibility when the in-app shelf is shown.
+TEST_F(ShelfWidgetTest, OpaqueBackgroundAndDragHandleTransition) {
+  ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
+  UpdateDisplay("800x800");
+
+  ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible());
+  ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible());
+
+  // Create a window to transition to the in-app shelf.
+  auto window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 800, 800));
+
+  {
+    TransitionAnimationWaiter waiter(
+        GetShelfWidget()->hotseat_transition_animator_for_testing());
+    waiter.Wait();
+  }
+
+  // Ensure the ShelfWidget and drag handle have the correct bounds and
+  // visibility for in-app shelf.
+  EXPECT_EQ(GetShelfWidget()->GetWindowBoundsInScreen(),
+            gfx::Rect(0, 760, 800, 40));
+  EXPECT_EQ(GetShelfWidget()->GetDragHandle()->bounds(),
+            gfx::Rect(360, 18, 80, 4));
+  ASSERT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible());
+  ASSERT_TRUE(GetShelfWidget()->GetOpaqueBackground()->visible());
+
+  window->Hide();
+
+  ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible());
+  ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible());
+}
+
 class ShelfWidgetAfterLoginTest : public AshTestBase {
  public:
   ShelfWidgetAfterLoginTest() { set_start_session(false); }
diff --git a/ash/system/network/network_tray_view.cc b/ash/system/network/network_tray_view.cc
index 95a44d2..f0ab02d3 100644
--- a/ash/system/network/network_tray_view.cc
+++ b/ash/system/network/network_tray_view.cc
@@ -56,7 +56,6 @@
 void NetworkTrayView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   node_data->SetName(accessible_name_);
   node_data->SetDescription(accessible_description_);
-  node_data->role = ax::mojom::Role::kButton;
 }
 
 views::View* NetworkTrayView::GetTooltipHandlerForPoint(
diff --git a/ash/system/power/tray_power.cc b/ash/system/power/tray_power.cc
index 3c50d42e..7a99611 100644
--- a/ash/system/power/tray_power.cc
+++ b/ash/system/power/tray_power.cc
@@ -64,7 +64,6 @@
 
 void PowerTrayView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   node_data->SetName(accessible_name_);
-  node_data->role = ax::mojom::Role::kButton;
 }
 
 views::View* PowerTrayView::GetTooltipHandlerForPoint(const gfx::Point& point) {
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 14d8a008..2c9ab12 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8890986310162385184
\ No newline at end of file
+8890671759613905216
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index db53a042..e30a0bd 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8891823802666694096
\ No newline at end of file
+8890671760545878432
\ No newline at end of file
diff --git a/cc/animation/animation.cc b/cc/animation/animation.cc
index f61cb82..61b6ace8 100644
--- a/cc/animation/animation.cc
+++ b/cc/animation/animation.cc
@@ -15,6 +15,7 @@
 #include "cc/animation/animation_timeline.h"
 #include "cc/animation/keyframe_effect.h"
 #include "cc/animation/scroll_offset_animation_curve.h"
+#include "cc/animation/scroll_timeline.h"
 #include "cc/animation/transform_operations.h"
 #include "cc/trees/property_animation_state.h"
 
@@ -260,4 +261,19 @@
   DispatchAndDelegateAnimationEvent(event);
 }
 
+void Animation::PromoteScrollTimelinePendingToActive() {
+  if (!animation_timeline_)
+    return;
+
+  animation_timeline_->ActivateTimeline();
+}
+
+void Animation::UpdateScrollTimeline(base::Optional<ElementId> scroller_id,
+                                     base::Optional<double> start_scroll_offset,
+                                     base::Optional<double> end_scroll_offset) {
+  ToScrollTimeline(animation_timeline_)
+      ->UpdateScrollerIdAndScrollOffsets(scroller_id, start_scroll_offset,
+                                         end_scroll_offset);
+}
+
 }  // namespace cc
diff --git a/cc/animation/animation.h b/cc/animation/animation.h
index 453f192..02039b1 100644
--- a/cc/animation/animation.h
+++ b/cc/animation/animation.h
@@ -65,9 +65,18 @@
   }
   void SetAnimationTimeline(AnimationTimeline* timeline);
 
-  // TODO(smcgruer): If/once ScrollTimeline is supported on normal Animations,
-  // we will need to move the promotion logic from WorkletAnimation to here.
-  virtual void PromoteScrollTimelinePendingToActive() {}
+  // TODO(yigu): There is a reverse dependency between AnimationTimeline and
+  // Animation. ScrollTimeline promotion and update should be handled by
+  // AnimationHost instead of Animation. https://crbug.com/1023508.
+  //
+  // Should be called when the pending tree is promoted to active, as this may
+  // require updating the ElementId for the ScrollTimeline scroll source.
+  virtual void PromoteScrollTimelinePendingToActive();
+  // Should be called when the ScrollTimeline attached to this animation has a
+  // change, such as when the scroll source changes ElementId.
+  void UpdateScrollTimeline(base::Optional<ElementId> scroller_id,
+                            base::Optional<double> start_scroll_offset,
+                            base::Optional<double> end_scroll_offset);
 
   scoped_refptr<ElementAnimations> element_animations() const;
 
diff --git a/cc/animation/animation_host_unittest.cc b/cc/animation/animation_host_unittest.cc
index 353b7b0..bcdec53 100644
--- a/cc/animation/animation_host_unittest.cc
+++ b/cc/animation/animation_host_unittest.cc
@@ -332,7 +332,7 @@
 
   // Create scroll timeline that links scroll animation and worklet animation
   // together. Use timerange so that we have 1:1 time & scroll mapping.
-  auto scroll_timeline = std::make_unique<ScrollTimeline>(
+  auto scroll_timeline = ScrollTimeline::Create(
       element_id, ScrollTimeline::ScrollDown, base::nullopt, base::nullopt, 100,
       KeyframeModel::FillMode::NONE);
 
diff --git a/cc/animation/animation_timeline.cc b/cc/animation/animation_timeline.cc
index 143745c..4f06245 100644
--- a/cc/animation/animation_timeline.cc
+++ b/cc/animation/animation_timeline.cc
@@ -135,4 +135,8 @@
   }
 }
 
+bool AnimationTimeline::IsScrollTimeline() const {
+  return false;
+}
+
 }  // namespace cc
diff --git a/cc/animation/animation_timeline.h b/cc/animation/animation_timeline.h
index aebbef7e..34f0cf7 100644
--- a/cc/animation/animation_timeline.h
+++ b/cc/animation/animation_timeline.h
@@ -44,19 +44,23 @@
 
   void ClearAnimations();
 
-  void PushPropertiesTo(AnimationTimeline* timeline_impl);
+  virtual void PushPropertiesTo(AnimationTimeline* timeline_impl);
+  virtual void ActivateTimeline() {}
 
   Animation* GetAnimationById(int animation_id) const;
 
   void SetNeedsPushProperties();
   bool needs_push_properties() const { return needs_push_properties_; }
 
- private:
-  friend class base::RefCounted<AnimationTimeline>;
+  virtual bool IsScrollTimeline() const;
 
+ protected:
   explicit AnimationTimeline(int id);
   virtual ~AnimationTimeline();
 
+ private:
+  friend class base::RefCounted<AnimationTimeline>;
+
   void PushAttachedAnimationsToImplThread(AnimationTimeline* timeline) const;
   void RemoveDetachedAnimationsFromImplThread(
       AnimationTimeline* timeline) const;
diff --git a/cc/animation/scroll_timeline.cc b/cc/animation/scroll_timeline.cc
index cc4b5bf..e95489071 100644
--- a/cc/animation/scroll_timeline.cc
+++ b/cc/animation/scroll_timeline.cc
@@ -4,6 +4,7 @@
 
 #include "cc/animation/scroll_timeline.h"
 
+#include "cc/animation/animation_id_provider.h"
 #include "cc/trees/property_tree.h"
 #include "cc/trees/scroll_node.h"
 #include "ui/gfx/geometry/scroll_offset.h"
@@ -31,7 +32,7 @@
                                base::Optional<double> end_scroll_offset,
                                double time_range,
                                KeyframeModel::FillMode fill)
-    : active_id_(),
+    : AnimationTimeline(AnimationIdProvider::NextTimelineId()),
       pending_id_(scroller_id),
       direction_(direction),
       start_scroll_offset_(start_scroll_offset),
@@ -39,12 +40,24 @@
       time_range_(time_range),
       fill_(fill) {}
 
-ScrollTimeline::~ScrollTimeline() {}
+ScrollTimeline::~ScrollTimeline() = default;
 
-std::unique_ptr<ScrollTimeline> ScrollTimeline::CreateImplInstance() const {
-  return std::make_unique<ScrollTimeline>(
-      pending_id_, direction_, start_scroll_offset_, end_scroll_offset_,
-      time_range_, fill_);
+scoped_refptr<ScrollTimeline> ScrollTimeline::Create(
+    base::Optional<ElementId> scroller_id,
+    ScrollTimeline::ScrollDirection direction,
+    base::Optional<double> start_scroll_offset,
+    base::Optional<double> end_scroll_offset,
+    double time_range,
+    KeyframeModel::FillMode fill) {
+  return base::WrapRefCounted(
+      new ScrollTimeline(scroller_id, direction, start_scroll_offset,
+                         end_scroll_offset, time_range, fill));
+}
+
+scoped_refptr<ScrollTimeline> ScrollTimeline::CreateImplInstance() const {
+  return base::WrapRefCounted(
+      new ScrollTimeline(pending_id_, direction_, start_scroll_offset_,
+                         end_scroll_offset_, time_range_, fill_));
 }
 
 bool ScrollTimeline::IsActive(const ScrollTree& scroll_tree,
@@ -139,34 +152,38 @@
              time_range_);
 }
 
-void ScrollTimeline::PushPropertiesTo(ScrollTimeline* impl_timeline) {
+void ScrollTimeline::PushPropertiesTo(AnimationTimeline* impl_timeline) {
   DCHECK(impl_timeline);
-  impl_timeline->pending_id_ = pending_id_;
+  ScrollTimeline* scroll_timeline = ToScrollTimeline(impl_timeline);
+  scroll_timeline->pending_id_ = pending_id_;
   // TODO(smcgruer): This leads to incorrect behavior in the current design,
   // because we end up using the pending start/end scroll offset for the active
   // tree too. Instead we need to either split these (like pending_id_ and
   // active_id_) or have a ScrollTimeline per tree.
-  impl_timeline->start_scroll_offset_ = start_scroll_offset_;
-  impl_timeline->end_scroll_offset_ = end_scroll_offset_;
+  scroll_timeline->start_scroll_offset_ = start_scroll_offset_;
+  scroll_timeline->end_scroll_offset_ = end_scroll_offset_;
 }
 
-void ScrollTimeline::PromoteScrollTimelinePendingToActive() {
+void ScrollTimeline::ActivateTimeline() {
   active_id_ = pending_id_;
 }
 
-void ScrollTimeline::UpdateStartAndEndScrollOffsets(
+void ScrollTimeline::UpdateScrollerIdAndScrollOffsets(
+    base::Optional<ElementId> pending_id,
     base::Optional<double> start_scroll_offset,
     base::Optional<double> end_scroll_offset) {
-  start_scroll_offset_ = start_scroll_offset;
-  end_scroll_offset_ = end_scroll_offset;
-}
-
-void ScrollTimeline::SetScrollerId(base::Optional<ElementId> pending_id) {
   // When the scroller id changes it will first be modified in the pending tree.
   // Then later (when the pending tree is promoted to active)
-  // |PromoteScrollTimelinePendingToActive| will be called and will set the
-  // |active_id_|.
+  // |ActivateTimeline| will be called and will set the |active_id_|.
   pending_id_ = pending_id;
+  start_scroll_offset_ = start_scroll_offset;
+  end_scroll_offset_ = end_scroll_offset;
+
+  SetNeedsPushProperties();
+}
+
+bool ScrollTimeline::IsScrollTimeline() const {
+  return true;
 }
 
 }  // namespace cc
diff --git a/cc/animation/scroll_timeline.h b/cc/animation/scroll_timeline.h
index d3003e9..b8be066 100644
--- a/cc/animation/scroll_timeline.h
+++ b/cc/animation/scroll_timeline.h
@@ -8,6 +8,7 @@
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "cc/animation/animation_export.h"
+#include "cc/animation/animation_timeline.h"
 #include "cc/animation/keyframe_model.h"
 #include "cc/paint/element_id.h"
 
@@ -20,7 +21,7 @@
 //
 // This is the compositor-side representation of the web concept expressed in
 // https://wicg.github.io/scroll-animations/#scrolltimeline-interface.
-class CC_ANIMATION_EXPORT ScrollTimeline {
+class CC_ANIMATION_EXPORT ScrollTimeline : public AnimationTimeline {
  public:
   // cc does not know about writing modes. The ScrollDirection below is
   // converted using blink::scroll_timeline_util::ConvertOrientation which takes
@@ -39,11 +40,18 @@
                  base::Optional<double> end_scroll_offset,
                  double time_range,
                  KeyframeModel::FillMode fill);
-  virtual ~ScrollTimeline();
+
+  static scoped_refptr<ScrollTimeline> Create(
+      base::Optional<ElementId> scroller_id,
+      ScrollDirection direction,
+      base::Optional<double> start_scroll_offset,
+      base::Optional<double> end_scroll_offset,
+      double time_range,
+      KeyframeModel::FillMode fill);
 
   // Create a copy of this ScrollTimeline intended for the impl thread in the
   // compositor.
-  std::unique_ptr<ScrollTimeline> CreateImplInstance() const;
+  scoped_refptr<ScrollTimeline> CreateImplInstance() const;
 
   // ScrollTimeline is active if the scroll node exists in active or pending
   // scroll tree.
@@ -58,14 +66,13 @@
       const ScrollTree& scroll_tree,
       bool is_active_tree) const;
 
-  void SetScrollerId(base::Optional<ElementId> scroller_id);
-  void UpdateStartAndEndScrollOffsets(
+  void UpdateScrollerIdAndScrollOffsets(
+      base::Optional<ElementId> scroller_id,
       base::Optional<double> start_scroll_offset,
       base::Optional<double> end_scroll_offset);
 
-  void PushPropertiesTo(ScrollTimeline* impl_timeline);
-
-  void PromoteScrollTimelinePendingToActive();
+  void PushPropertiesTo(AnimationTimeline* impl_timeline) override;
+  void ActivateTimeline() override;
 
   base::Optional<ElementId> GetActiveIdForTest() const { return active_id_; }
   base::Optional<ElementId> GetPendingIdForTest() const { return pending_id_; }
@@ -78,6 +85,11 @@
   }
   double GetTimeRangeForTest() const { return time_range_; }
 
+  bool IsScrollTimeline() const override;
+
+ protected:
+  ~ScrollTimeline() override;
+
  private:
   // The scroller which this ScrollTimeline is based on. The same underlying
   // scroll source may have different ids in the pending and active tree (see
@@ -105,6 +117,11 @@
   KeyframeModel::FillMode fill_;
 };
 
+inline ScrollTimeline* ToScrollTimeline(AnimationTimeline* timeline) {
+  DCHECK(timeline->IsScrollTimeline());
+  return static_cast<ScrollTimeline*>(timeline);
+}
+
 }  // namespace cc
 
 #endif  // CC_ANIMATION_SCROLL_TIMELINE_H_
diff --git a/cc/animation/scroll_timeline_unittest.cc b/cc/animation/scroll_timeline_unittest.cc
index 643c29d7..998ea9f 100644
--- a/cc/animation/scroll_timeline_unittest.cc
+++ b/cc/animation/scroll_timeline_unittest.cc
@@ -113,19 +113,19 @@
   // just compute one edge and use it for vertical/horizontal.
   double time_range = content_size().height() - container_size().height();
 
-  ScrollTimeline vertical_timeline(scroller_id(), ScrollTimeline::ScrollDown,
-                                   base::nullopt, base::nullopt, time_range,
-                                   KeyframeModel::FillMode::NONE);
-  ScrollTimeline horizontal_timeline(scroller_id(), ScrollTimeline::ScrollRight,
-                                     base::nullopt, base::nullopt, time_range,
-                                     KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> vertical_timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, base::nullopt, base::nullopt,
+      time_range, KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> horizontal_timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollRight, base::nullopt, base::nullopt,
+      time_range, KeyframeModel::FillMode::NONE);
 
   // Unscrolled, both timelines should read a current time of 0.
   SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset());
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      0, vertical_timeline.CurrentTime(scroll_tree(), false));
+      0, vertical_timeline->CurrentTime(scroll_tree(), false));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      0, horizontal_timeline.CurrentTime(scroll_tree(), false));
+      0, horizontal_timeline->CurrentTime(scroll_tree(), false));
 
   // Now do some scrolling and make sure that the ScrollTimelines update.
   SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(75, 50));
@@ -133,24 +133,24 @@
   // As noted above, we have mapped the time range such that current time should
   // just be the scroll offset.
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      50, vertical_timeline.CurrentTime(scroll_tree(), false));
+      50, vertical_timeline->CurrentTime(scroll_tree(), false));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      75, horizontal_timeline.CurrentTime(scroll_tree(), false));
+      75, horizontal_timeline->CurrentTime(scroll_tree(), false));
 }
 
 TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForTimeRange) {
   // Here we set a time range to 100, which gives the current time the form of
   // 'percentage scrolled'.
-  ScrollTimeline timeline(scroller_id(), ScrollTimeline::ScrollDown,
-                          base::nullopt, base::nullopt, 100,
-                          KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, base::nullopt, base::nullopt,
+      100, KeyframeModel::FillMode::NONE);
 
   double halfwayY = (content_size().height() - container_size().height()) / 2.;
   SetScrollOffset(&property_trees(), scroller_id(),
                   gfx::ScrollOffset(0, halfwayY));
 
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(50,
-                                   timeline.CurrentTime(scroll_tree(), false));
+                                   timeline->CurrentTime(scroll_tree(), false));
 }
 
 // This test ensures that the ScrollTimeline's active scroller id is correct. We
@@ -176,15 +176,15 @@
   double halfwayY = (content_size().height() - container_size().height()) / 2.;
   SetScrollOffset(&pending_tree, scroller_id, gfx::ScrollOffset(0, halfwayY));
 
-  ScrollTimeline main_timeline(scroller_id, ScrollTimeline::ScrollDown,
-                               base::nullopt, base::nullopt, 100,
-                               KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> main_timeline = ScrollTimeline::Create(
+      scroller_id, ScrollTimeline::ScrollDown, base::nullopt, base::nullopt,
+      100, KeyframeModel::FillMode::NONE);
 
   // Now create an impl version of the ScrollTimeline. Initially this should
   // only have a pending scroller id, as the active tree may not yet have the
   // scroller in it (as in this case).
-  std::unique_ptr<ScrollTimeline> impl_timeline =
-      main_timeline.CreateImplInstance();
+  scoped_refptr<ScrollTimeline> impl_timeline =
+      main_timeline->CreateImplInstance();
 
   EXPECT_TRUE(std::isnan(
       ToDouble(impl_timeline->CurrentTime(active_tree.scroll_tree, true))));
@@ -195,7 +195,7 @@
   // its active scroller id. Note that we deliberately pass in the pending_tree
   // and just claim it is the active tree; this avoids needing to properly
   // implement tree swapping just for the test.
-  impl_timeline->PromoteScrollTimelinePendingToActive();
+  impl_timeline->ActivateTimeline();
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
       50, impl_timeline->CurrentTime(pending_tree.scroll_tree, true));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
@@ -204,9 +204,9 @@
 
 TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForPixelSnapping) {
   double time_range = content_size().height() - container_size().height();
-  ScrollTimeline timeline(scroller_id(), ScrollTimeline::ScrollDown,
-                          base::nullopt, base::nullopt, time_range,
-                          KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, base::nullopt, base::nullopt,
+      time_range, KeyframeModel::FillMode::NONE);
 
   SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 50));
 
@@ -217,107 +217,115 @@
   transform_node->snap_amount = gfx::Vector2dF(0, 0.5);
 
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(49.5,
-                                   timeline.CurrentTime(scroll_tree(), false));
+                                   timeline->CurrentTime(scroll_tree(), false));
 }
 
 TEST_F(ScrollTimelineTest, CurrentTimeHandlesStartScrollOffset) {
   double time_range = content_size().height() - container_size().height();
   const double start_scroll_offset = 20;
-  ScrollTimeline timeline(scroller_id(), ScrollTimeline::ScrollDown,
-                          start_scroll_offset, base::nullopt, time_range,
-                          KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, start_scroll_offset,
+      base::nullopt, time_range, KeyframeModel::FillMode::NONE);
 
   // Unscrolled, the timeline should read a current time of unresolved, since
   // the current offset (0) will be less than the startScrollOffset.
   SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset());
-  EXPECT_TRUE(std::isnan(ToDouble(timeline.CurrentTime(scroll_tree(), false))));
+  EXPECT_TRUE(
+      std::isnan(ToDouble(timeline->CurrentTime(scroll_tree(), false))));
 
   SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 19));
-  EXPECT_TRUE(std::isnan(ToDouble(timeline.CurrentTime(scroll_tree(), false))));
+  EXPECT_TRUE(
+      std::isnan(ToDouble(timeline->CurrentTime(scroll_tree(), false))));
   SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 20));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(0,
-                                   timeline.CurrentTime(scroll_tree(), false));
+                                   timeline->CurrentTime(scroll_tree(), false));
   SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 50));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
       CalculateCurrentTime(50, start_scroll_offset, time_range, time_range),
-      timeline.CurrentTime(scroll_tree(), false));
+      timeline->CurrentTime(scroll_tree(), false));
   SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 200));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
       CalculateCurrentTime(200, start_scroll_offset, time_range, time_range),
-      timeline.CurrentTime(scroll_tree(), false));
+      timeline->CurrentTime(scroll_tree(), false));
 }
 
 TEST_F(ScrollTimelineTest, CurrentTimeHandlesEndScrollOffset) {
   double time_range = content_size().height() - container_size().height();
   const double end_scroll_offset = time_range - 20;
-  ScrollTimeline timeline(scroller_id(), ScrollTimeline::ScrollDown,
-                          base::nullopt, end_scroll_offset, time_range,
-                          KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, base::nullopt,
+      end_scroll_offset, time_range, KeyframeModel::FillMode::NONE);
 
   SetScrollOffset(&property_trees(), scroller_id(),
                   gfx::ScrollOffset(0, time_range));
-  EXPECT_TRUE(std::isnan(ToDouble(timeline.CurrentTime(scroll_tree(), false))));
+  EXPECT_TRUE(
+      std::isnan(ToDouble(timeline->CurrentTime(scroll_tree(), false))));
   SetScrollOffset(&property_trees(), scroller_id(),
                   gfx::ScrollOffset(0, time_range - 20));
-  EXPECT_TRUE(std::isnan(ToDouble(timeline.CurrentTime(scroll_tree(), false))));
+  EXPECT_TRUE(
+      std::isnan(ToDouble(timeline->CurrentTime(scroll_tree(), false))));
   SetScrollOffset(&property_trees(), scroller_id(),
                   gfx::ScrollOffset(0, time_range - 50));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
       CalculateCurrentTime(time_range - 50, 0, end_scroll_offset, time_range),
-      timeline.CurrentTime(scroll_tree(), false));
+      timeline->CurrentTime(scroll_tree(), false));
   SetScrollOffset(&property_trees(), scroller_id(),
                   gfx::ScrollOffset(0, time_range - 200));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
       CalculateCurrentTime(time_range - 200, 0, end_scroll_offset, time_range),
-      timeline.CurrentTime(scroll_tree(), false));
+      timeline->CurrentTime(scroll_tree(), false));
 }
 
 TEST_F(ScrollTimelineTest, CurrentTimeHandlesEndScrollOffsetInclusive) {
   double time_range = 100;
   const double end_scroll_offset =
       content_size().height() - container_size().height();
-  ScrollTimeline timeline(scroller_id(), ScrollTimeline::ScrollDown,
-                          base::nullopt, end_scroll_offset, time_range,
-                          KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, base::nullopt,
+      end_scroll_offset, time_range, KeyframeModel::FillMode::NONE);
 
   const double current_offset = end_scroll_offset;
   SetScrollOffset(&property_trees(), scroller_id(),
                   gfx::ScrollOffset(0, current_offset));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
       CalculateCurrentTime(current_offset, 0, end_scroll_offset, time_range),
-      timeline.CurrentTime(scroll_tree(), false));
+      timeline->CurrentTime(scroll_tree(), false));
 }
 
 TEST_F(ScrollTimelineTest, CurrentTimeHandlesCombinedStartAndEndScrollOffset) {
   double time_range = content_size().height() - container_size().height();
   double start_scroll_offset = 20;
   double end_scroll_offset = time_range - 50;
-  ScrollTimeline timeline(scroller_id(), ScrollTimeline::ScrollDown,
-                          start_scroll_offset, end_scroll_offset, time_range,
-                          KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, start_scroll_offset,
+      end_scroll_offset, time_range, KeyframeModel::FillMode::NONE);
   SetScrollOffset(&property_trees(), scroller_id(),
                   gfx::ScrollOffset(0, time_range - 150));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
       CalculateCurrentTime(time_range - 150, start_scroll_offset,
                            end_scroll_offset, time_range),
-      timeline.CurrentTime(scroll_tree(), false));
+      timeline->CurrentTime(scroll_tree(), false));
 }
 
 TEST_F(ScrollTimelineTest, CurrentTimeHandlesEqualStartAndEndScrollOffset) {
   double time_range = content_size().height() - container_size().height();
-  ScrollTimeline timeline(scroller_id(), ScrollTimeline::ScrollDown, 20, 20,
-                          time_range, KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> timeline =
+      ScrollTimeline::Create(scroller_id(), ScrollTimeline::ScrollDown, 20, 20,
+                             time_range, KeyframeModel::FillMode::NONE);
   SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 150));
-  EXPECT_TRUE(std::isnan(ToDouble(timeline.CurrentTime(scroll_tree(), false))));
+  EXPECT_TRUE(
+      std::isnan(ToDouble(timeline->CurrentTime(scroll_tree(), false))));
 }
 
 TEST_F(ScrollTimelineTest,
        CurrentTimeHandlesStartOffsetLargerThanEndScrollOffset) {
   double time_range = content_size().height() - container_size().height();
-  ScrollTimeline timeline(scroller_id(), ScrollTimeline::ScrollDown, 50, 10,
-                          time_range, KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> timeline =
+      ScrollTimeline::Create(scroller_id(), ScrollTimeline::ScrollDown, 50, 10,
+                             time_range, KeyframeModel::FillMode::NONE);
   SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 150));
-  EXPECT_TRUE(std::isnan(ToDouble(timeline.CurrentTime(scroll_tree(), false))));
+  EXPECT_TRUE(
+      std::isnan(ToDouble(timeline->CurrentTime(scroll_tree(), false))));
 }
 
 TEST_F(ScrollTimelineTest, CurrentTimeHandlesFillMode) {
@@ -327,92 +335,94 @@
       content_size().height() - container_size().height();
   const double end_scroll_offset = scroller_height - 20;
 
-  ScrollTimeline fill_forwards_timeline(
+  scoped_refptr<ScrollTimeline> fill_forwards_timeline = ScrollTimeline::Create(
       scroller_id(), ScrollTimeline::ScrollDown, start_scroll_offset,
       end_scroll_offset, time_range, KeyframeModel::FillMode::FORWARDS);
-  ScrollTimeline fill_backwards_timeline(
+  scoped_refptr<ScrollTimeline> fill_backwards_timeline =
+      ScrollTimeline::Create(scroller_id(), ScrollTimeline::ScrollDown,
+                             start_scroll_offset, end_scroll_offset, time_range,
+                             KeyframeModel::FillMode::BACKWARDS);
+  scoped_refptr<ScrollTimeline> fill_both_timeline = ScrollTimeline::Create(
       scroller_id(), ScrollTimeline::ScrollDown, start_scroll_offset,
-      end_scroll_offset, time_range, KeyframeModel::FillMode::BACKWARDS);
-  ScrollTimeline fill_both_timeline(scroller_id(), ScrollTimeline::ScrollDown,
-                                    start_scroll_offset, end_scroll_offset,
-                                    time_range, KeyframeModel::FillMode::BOTH);
+      end_scroll_offset, time_range, KeyframeModel::FillMode::BOTH);
   // AUTO should be equivalent to BOTH.
-  ScrollTimeline fill_auto_timeline(scroller_id(), ScrollTimeline::ScrollDown,
-                                    start_scroll_offset, end_scroll_offset,
-                                    time_range, KeyframeModel::FillMode::AUTO);
+  scoped_refptr<ScrollTimeline> fill_auto_timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, start_scroll_offset,
+      end_scroll_offset, time_range, KeyframeModel::FillMode::AUTO);
 
   // Before the start_scroll_offset the current time should be 0 for backwards
   // or both, and unresolved otherwise.
   SetScrollOffset(&property_trees(), scroller_id(),
                   gfx::ScrollOffset(0, start_scroll_offset - 10));
-  EXPECT_FALSE(fill_forwards_timeline.CurrentTime(scroll_tree(), false));
+  EXPECT_FALSE(fill_forwards_timeline->CurrentTime(scroll_tree(), false));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      0, fill_backwards_timeline.CurrentTime(scroll_tree(), false));
+      0, fill_backwards_timeline->CurrentTime(scroll_tree(), false));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      0, fill_both_timeline.CurrentTime(scroll_tree(), false));
+      0, fill_both_timeline->CurrentTime(scroll_tree(), false));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      0, fill_auto_timeline.CurrentTime(scroll_tree(), false));
+      0, fill_auto_timeline->CurrentTime(scroll_tree(), false));
 
   // At the end_scroll_offset the current time should be time-range for
   // forwards or both, and unresolved otherwise.
   SetScrollOffset(&property_trees(), scroller_id(),
                   gfx::ScrollOffset(0, end_scroll_offset));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      time_range, fill_forwards_timeline.CurrentTime(scroll_tree(), false));
-  EXPECT_FALSE(fill_backwards_timeline.CurrentTime(scroll_tree(), false));
+      time_range, fill_forwards_timeline->CurrentTime(scroll_tree(), false));
+  EXPECT_FALSE(fill_backwards_timeline->CurrentTime(scroll_tree(), false));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      time_range, fill_both_timeline.CurrentTime(scroll_tree(), false));
+      time_range, fill_both_timeline->CurrentTime(scroll_tree(), false));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      time_range, fill_auto_timeline.CurrentTime(scroll_tree(), false));
+      time_range, fill_auto_timeline->CurrentTime(scroll_tree(), false));
 
   // After the end_scroll_offset the current time should be time-range for
   // forwards or both, and unresolved otherwise.
   SetScrollOffset(&property_trees(), scroller_id(),
                   gfx::ScrollOffset(0, end_scroll_offset + 10));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      time_range, fill_forwards_timeline.CurrentTime(scroll_tree(), false));
-  EXPECT_FALSE(fill_backwards_timeline.CurrentTime(scroll_tree(), false));
+      time_range, fill_forwards_timeline->CurrentTime(scroll_tree(), false));
+  EXPECT_FALSE(fill_backwards_timeline->CurrentTime(scroll_tree(), false));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      time_range, fill_both_timeline.CurrentTime(scroll_tree(), false));
+      time_range, fill_both_timeline->CurrentTime(scroll_tree(), false));
   EXPECT_SCROLL_TIMELINE_TIME_NEAR(
-      time_range, fill_auto_timeline.CurrentTime(scroll_tree(), false));
+      time_range, fill_auto_timeline->CurrentTime(scroll_tree(), false));
 }
 
 TEST_F(ScrollTimelineTest, Activeness) {
   // ScrollTimeline with zero scroller id is inactive.
-  ScrollTimeline inactive_timeline1(base::nullopt, ScrollTimeline::ScrollDown,
-                                    base::nullopt, base::nullopt, 100,
-                                    KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> inactive_timeline1 = ScrollTimeline::Create(
+      base::nullopt, ScrollTimeline::ScrollDown, base::nullopt, base::nullopt,
+      100, KeyframeModel::FillMode::NONE);
   EXPECT_FALSE(
-      inactive_timeline1.IsActive(scroll_tree(), false /*is_active_tree*/));
+      inactive_timeline1->IsActive(scroll_tree(), false /*is_active_tree*/));
   EXPECT_FALSE(
-      inactive_timeline1.IsActive(scroll_tree(), true /*is_active_tree*/));
+      inactive_timeline1->IsActive(scroll_tree(), true /*is_active_tree*/));
 
   // ScrollTimeline with a scroller that is not in the scroll tree is
   // inactive.
-  ScrollTimeline inactive_timeline2(ElementId(2), ScrollTimeline::ScrollDown,
-                                    base::nullopt, base::nullopt, 100,
-                                    KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> inactive_timeline2 = ScrollTimeline::Create(
+      ElementId(2), ScrollTimeline::ScrollDown, base::nullopt, base::nullopt,
+      100, KeyframeModel::FillMode::NONE);
   EXPECT_FALSE(
-      inactive_timeline2.IsActive(scroll_tree(), false /*is_active_tree*/));
+      inactive_timeline2->IsActive(scroll_tree(), false /*is_active_tree*/));
   // Activate the scroll tree.
-  inactive_timeline2.PromoteScrollTimelinePendingToActive();
+  inactive_timeline2->ActivateTimeline();
   EXPECT_FALSE(
-      inactive_timeline2.IsActive(scroll_tree(), true /*is_active_tree*/));
+      inactive_timeline2->IsActive(scroll_tree(), true /*is_active_tree*/));
 
-  ScrollTimeline active_timeline(scroller_id(), ScrollTimeline::ScrollDown,
-                                 base::nullopt, base::nullopt, 100,
-                                 KeyframeModel::FillMode::NONE);
+  scoped_refptr<ScrollTimeline> active_timeline = ScrollTimeline::Create(
+      scroller_id(), ScrollTimeline::ScrollDown, base::nullopt, base::nullopt,
+      100, KeyframeModel::FillMode::NONE);
   EXPECT_TRUE(
-      active_timeline.IsActive(scroll_tree(), false /*is_active_tree*/));
+      active_timeline->IsActive(scroll_tree(), false /*is_active_tree*/));
   EXPECT_FALSE(
-      active_timeline.IsActive(scroll_tree(), true /*is_active_tree*/));
+      active_timeline->IsActive(scroll_tree(), true /*is_active_tree*/));
 
   // Activate the scroll tree.
-  active_timeline.PromoteScrollTimelinePendingToActive();
+  active_timeline->ActivateTimeline();
   EXPECT_TRUE(
-      active_timeline.IsActive(scroll_tree(), false /*is_active_tree*/));
-  EXPECT_TRUE(active_timeline.IsActive(scroll_tree(), true /*is_active_tree*/));
+      active_timeline->IsActive(scroll_tree(), false /*is_active_tree*/));
+  EXPECT_TRUE(
+      active_timeline->IsActive(scroll_tree(), true /*is_active_tree*/));
 }
 
 }  // namespace cc
diff --git a/cc/animation/worklet_animation.cc b/cc/animation/worklet_animation.cc
index 9f9867b7..895a391 100644
--- a/cc/animation/worklet_animation.cc
+++ b/cc/animation/worklet_animation.cc
@@ -20,7 +20,7 @@
     WorkletAnimationId worklet_animation_id,
     const std::string& name,
     double playback_rate,
-    std::unique_ptr<ScrollTimeline> scroll_timeline,
+    scoped_refptr<ScrollTimeline> scroll_timeline,
     std::unique_ptr<AnimationOptions> options,
     std::unique_ptr<AnimationEffectTimings> effect_timings,
     bool is_controlling_instance)
@@ -39,7 +39,7 @@
     WorkletAnimationId worklet_animation_id,
     const std::string& name,
     double playback_rate,
-    std::unique_ptr<ScrollTimeline> scroll_timeline,
+    scoped_refptr<ScrollTimeline> scroll_timeline,
     std::unique_ptr<AnimationOptions> options,
     std::unique_ptr<AnimationEffectTimings> effect_timings,
     bool is_controlling_instance,
@@ -65,7 +65,7 @@
     WorkletAnimationId worklet_animation_id,
     const std::string& name,
     double playback_rate,
-    std::unique_ptr<ScrollTimeline> scroll_timeline,
+    scoped_refptr<ScrollTimeline> scroll_timeline,
     std::unique_ptr<AnimationOptions> options,
     std::unique_ptr<AnimationEffectTimings> effect_timings) {
   return WrapRefCounted(new WorkletAnimation(
@@ -75,7 +75,7 @@
 }
 
 scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const {
-  std::unique_ptr<ScrollTimeline> impl_timeline;
+  scoped_refptr<ScrollTimeline> impl_timeline;
   if (scroll_timeline_)
     impl_timeline = scroll_timeline_->CreateImplInstance();
 
@@ -271,21 +271,8 @@
          scroll_timeline_->IsActive(scroll_tree, is_active_tree);
 }
 
-void WorkletAnimation::UpdateScrollTimeline(
-    base::Optional<ElementId> scroller_id,
-    base::Optional<double> start_scroll_offset,
-    base::Optional<double> end_scroll_offset) {
-  // Calling this method implies that we are a ScrollTimeline based animation,
-  // so the below call is done unchecked.
-  scroll_timeline_->SetScrollerId(scroller_id);
-  scroll_timeline_->UpdateStartAndEndScrollOffsets(start_scroll_offset,
-                                                   end_scroll_offset);
-  SetNeedsPushProperties();
-}
-
 void WorkletAnimation::PromoteScrollTimelinePendingToActive() {
-  if (scroll_timeline_)
-    scroll_timeline_->PromoteScrollTimelinePendingToActive();
+  Animation::PromoteScrollTimelinePendingToActive();
   ReleasePendingTreeLock();
 }
 
diff --git a/cc/animation/worklet_animation.h b/cc/animation/worklet_animation.h
index 3bda2ac..e3df01c 100644
--- a/cc/animation/worklet_animation.h
+++ b/cc/animation/worklet_animation.h
@@ -40,7 +40,7 @@
                    WorkletAnimationId worklet_animation_id,
                    const std::string& name,
                    double playback_rate,
-                   std::unique_ptr<ScrollTimeline> scroll_timeline,
+                   scoped_refptr<ScrollTimeline> scroll_timeline,
                    std::unique_ptr<AnimationOptions> options,
                    std::unique_ptr<AnimationEffectTimings> effect_timings,
                    bool is_controlling_instance);
@@ -48,7 +48,7 @@
       WorkletAnimationId worklet_animation_id,
       const std::string& name,
       double playback_rate,
-      std::unique_ptr<ScrollTimeline> scroll_timeline,
+      scoped_refptr<ScrollTimeline> scroll_timeline,
       std::unique_ptr<AnimationOptions> options,
       std::unique_ptr<AnimationEffectTimings> effect_timings);
   scoped_refptr<Animation> CreateImplInstance() const override;
@@ -75,14 +75,6 @@
 
   void PushPropertiesTo(Animation* animation_impl) override;
 
-  // Should be called when the ScrollTimeline attached to this animation has a
-  // change, such as when the scroll source changes ElementId.
-  void UpdateScrollTimeline(base::Optional<ElementId> scroller_id,
-                            base::Optional<double> start_scroll_offset,
-                            base::Optional<double> end_scroll_offset);
-
-  // Should be called when the pending tree is promoted to active, as this may
-  // require updating the ElementId for the ScrollTimeline scroll source.
   void PromoteScrollTimelinePendingToActive() override;
 
   // Called by Blink WorkletAnimation when its playback rate is updated.
@@ -92,7 +84,6 @@
   }
 
   void RemoveKeyframeModel(int keyframe_model_id) override;
-
   void ReleasePendingTreeLock() { has_pending_tree_lock_ = false; }
 
  private:
@@ -102,7 +93,7 @@
                    WorkletAnimationId worklet_animation_id,
                    const std::string& name,
                    double playback_rate,
-                   std::unique_ptr<ScrollTimeline> scroll_timeline,
+                   scoped_refptr<ScrollTimeline> scroll_timeline,
                    std::unique_ptr<AnimationOptions> options,
                    std::unique_ptr<AnimationEffectTimings> effect_timings,
                    bool is_controlling_instance,
@@ -144,11 +135,10 @@
 
   // The ScrollTimeline associated with the underlying animation. If null, the
   // animation is based on a DocumentTimeline.
-  //
-  // TODO(crbug.com/780148): A WorkletAnimation should own an AnimationTimeline
-  // which must exist but can either be a DocumentTimeline, ScrollTimeline, or
-  // some other future implementation.
-  std::unique_ptr<ScrollTimeline> scroll_timeline_;
+  // TODO(crbug.com/1023508): Remove scroll_timeline_. For scroll-linked
+  // animations, construct animation_timeline_ with ScrollTimeline directly via
+  // blink::Animation::AttachCompositorTimeline.
+  scoped_refptr<ScrollTimeline> scroll_timeline_;
 
   // Controls speed of the animation.
   // https://drafts.csswg.org/web-animations-2/#animation-effect-playback-rate
@@ -183,7 +173,6 @@
   // lock in the worklet. The lock is established when updating the input state
   // for the pending tree and release on pending tree activation.
   bool has_pending_tree_lock_;
-
   State state_;
 
   bool is_impl_instance_;
diff --git a/cc/animation/worklet_animation_unittest.cc b/cc/animation/worklet_animation_unittest.cc
index 71181b1..80a93fb 100644
--- a/cc/animation/worklet_animation_unittest.cc
+++ b/cc/animation/worklet_animation_unittest.cc
@@ -63,6 +63,9 @@
   MOCK_CONST_METHOD2(CurrentTime,
                      base::Optional<base::TimeTicks>(const ScrollTree&, bool));
   MOCK_CONST_METHOD2(IsActive, bool(const ScrollTree&, bool));
+
+ protected:
+  ~MockScrollTimeline() override = default;
 };
 
 TEST_F(WorkletAnimationTest, NonImplInstanceDoesNotTickKeyframe) {
@@ -165,7 +168,7 @@
 }
 
 TEST_F(WorkletAnimationTest, CurrentTimeCorrectlyUsesScrollTimeline) {
-  auto scroll_timeline = std::make_unique<MockScrollTimeline>();
+  auto scroll_timeline = base::WrapRefCounted(new MockScrollTimeline());
   EXPECT_CALL(*scroll_timeline, IsActive(_, _)).WillRepeatedly(Return(true));
   EXPECT_CALL(*scroll_timeline, CurrentTime(_, _))
       .WillRepeatedly(Return(
@@ -273,7 +276,7 @@
 TEST_F(WorkletAnimationTest, ScrollTimelineSetPlaybackRate) {
   const double playback_rate_double = 2;
   const double playback_rate_half = 0.5;
-  auto scroll_timeline = std::make_unique<MockScrollTimeline>();
+  auto scroll_timeline = base::WrapRefCounted(new MockScrollTimeline());
 
   scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
       worklet_animation_id_, "test_name", playback_rate_double,
@@ -323,7 +326,7 @@
 // Verifies correcteness of worklet animation current time when inactive
 // timeline becomes active and then inactive again.
 TEST_F(WorkletAnimationTest, InactiveScrollTimeline) {
-  auto scroll_timeline = std::make_unique<MockScrollTimeline>();
+  auto scroll_timeline = base::WrapRefCounted(new MockScrollTimeline());
 
   scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
       worklet_animation_id_, "test_name", /*playback_rate*/ 1,
@@ -490,7 +493,7 @@
 // This test verifies that worklet animation gets skipped properly if a pending
 // mutation cycle is holding a lock on the worklet.
 TEST_F(WorkletAnimationTest, SkipLockedAnimations) {
-  auto scroll_timeline = std::make_unique<MockScrollTimeline>();
+  auto scroll_timeline = base::WrapRefCounted(new MockScrollTimeline());
   EXPECT_CALL(*scroll_timeline, IsActive(_, _)).WillRepeatedly(Return(true));
   EXPECT_CALL(*scroll_timeline, CurrentTime(_, _))
       .WillRepeatedly(Invoke(FakeIncreasingScrollTimelineTime));
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index cf41931..bb5319b6 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -14,6 +14,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -48,6 +49,7 @@
 #include "components/viz/test/fake_skia_output_surface.h"
 #include "components/viz/test/test_context_provider.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
+#include "gpu/config/gpu_finch_features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gl/gl_switches.h"
@@ -639,8 +641,8 @@
         ::switches::kUseVulkan,
         use_gpu ? ::switches::kVulkanImplementationNameNative
                 : ::switches::kVulkanImplementationNameSwiftshader);
-    command_line->AppendSwitchASCII(::switches::kGrContextType,
-                                    ::switches::kGrContextTypeVulkan);
+    scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
+    scoped_feature_list_->InitAndEnableFeature(features::kVulkan);
   }
 }
 
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h
index e063cf1..f5c42f4 100644
--- a/cc/test/layer_tree_test.h
+++ b/cc/test/layer_tree_test.h
@@ -16,8 +16,15 @@
 #include "cc/trees/layer_tree_host_impl.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/test/test_gpu_memory_buffer_manager.h"
+#include "components/viz/test/test_gpu_service_holder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace base {
+namespace test {
+class ScopedFeatureList;
+}
+}  // namespace base
+
 namespace viz {
 class BeginFrameSource;
 class TestContextProvider;
@@ -233,6 +240,11 @@
   void DispatchCompositeImmediately();
   void DispatchNextCommitWaitsForActivation();
 
+  // |scoped_feature_list_| must be the first member to ensure that it is
+  // destroyed after any member that might be using it.
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
+  viz::TestGpuServiceHolder::ScopedResetter gpu_service_resetter_;
+
   LayerTreeSettings settings_;
   float initial_device_scale_factor_ = 1.f;
   gfx::Size initial_root_bounds_;
diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc
index d54c281..51f56f8 100644
--- a/cc/test/pixel_test.cc
+++ b/cc/test/pixel_test.cc
@@ -45,6 +45,7 @@
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "gpu/command_buffer/service/service_utils.h"
 #include "gpu/config/gpu_feature_type.h"
+#include "gpu/config/gpu_finch_features.h"
 #include "gpu/config/gpu_info.h"
 #include "gpu/ipc/gpu_in_process_thread_service.h"
 #include "gpu/ipc/service/gpu_memory_buffer_factory.h"
@@ -53,13 +54,23 @@
 
 namespace cc {
 
-PixelTest::PixelTest()
+PixelTest::PixelTest(bool enable_vulkan)
     : device_viewport_size_(gfx::Size(200, 200)),
       disable_picture_quad_image_filtering_(false),
       output_surface_client_(std::make_unique<FakeOutputSurfaceClient>()) {
   // Keep texture sizes exactly matching the bounds of the RenderPass to avoid
   // floating point badness in texcoords.
   renderer_settings_.dont_round_texture_sizes_for_pixel_tests = true;
+  if (enable_vulkan) {
+    auto* command_line = base::CommandLine::ForCurrentProcess();
+    bool use_gpu = command_line->HasSwitch(::switches::kUseGpuInTests);
+    command_line->AppendSwitchASCII(
+        ::switches::kUseVulkan,
+        use_gpu ? ::switches::kVulkanImplementationNameNative
+                : ::switches::kVulkanImplementationNameSwiftshader);
+    scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
+    scoped_feature_list_->InitAndEnableFeature(features::kVulkan);
+  }
 }
 
 PixelTest::~PixelTest() = default;
@@ -247,19 +258,8 @@
   renderer_->SetVisible(true);
 }
 
-void PixelTest::SetUpSkiaRenderer(bool flipped_output_surface,
-                                  bool enable_vulkan) {
+void PixelTest::SetUpSkiaRenderer(bool flipped_output_surface) {
   enable_pixel_output_ = std::make_unique<gl::DisableNullDrawGLBindings>();
-  if (enable_vulkan) {
-    auto* command_line = base::CommandLine::ForCurrentProcess();
-    bool use_gpu = command_line->HasSwitch(::switches::kUseGpuInTests);
-    command_line->AppendSwitchASCII(
-        ::switches::kUseVulkan,
-        use_gpu ? ::switches::kVulkanImplementationNameNative
-                : ::switches::kVulkanImplementationNameSwiftshader);
-    command_line->AppendSwitchASCII(
-        ::switches::kGrContextType, ::switches::kGrContextTypeVulkan);
-  }
   // Set up the GPU service.
   gpu_service_holder_ = viz::TestGpuServiceHolder::GetInstance();
 
@@ -301,7 +301,6 @@
   renderer_.reset();
   resource_provider_.reset();
   output_surface_.reset();
-  scoped_feature_list_.reset();
 }
 
 void PixelTest::EnableExternalStencilTest() {
diff --git a/cc/test/pixel_test.h b/cc/test/pixel_test.h
index 872066ef..d000088 100644
--- a/cc/test/pixel_test.h
+++ b/cc/test/pixel_test.h
@@ -45,10 +45,11 @@
 namespace cc {
 class FakeOutputSurfaceClient;
 class OutputSurface;
+class VulkanSkiaRenderer;
 
 class PixelTest : public testing::Test {
  protected:
-  PixelTest();
+  explicit PixelTest(bool use_vulkan = false);
   ~PixelTest() override;
 
   bool RunPixelTest(viz::RenderPassList* pass_list,
@@ -93,6 +94,11 @@
   viz::ResourceId AllocateAndFillSoftwareResource(const gfx::Size& size,
                                                   const SkBitmap& source);
 
+  // |scoped_feature_list_| must be the first member to ensure that it is
+  // destroyed after any member that might be using it.
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
+  viz::TestGpuServiceHolder::ScopedResetter gpu_service_resetter_;
+
   // For SkiaRenderer.
   viz::TestGpuServiceHolder* gpu_service_holder_ = nullptr;
 
@@ -111,7 +117,7 @@
 
   void SetUpGLWithoutRenderer(bool flipped_output_surface);
   void SetUpGLRenderer(bool flipped_output_surface);
-  void SetUpSkiaRenderer(bool flipped_output_surface, bool enable_vulkan);
+  void SetUpSkiaRenderer(bool flipped_output_surface);
   void SetUpSoftwareRenderer();
 
   void TearDown() override;
@@ -126,12 +132,13 @@
                             const PixelComparator& comparator);
 
   std::unique_ptr<gl::DisableNullDrawGLBindings> enable_pixel_output_;
-  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
 };
 
 template<typename RendererType>
 class RendererPixelTest : public PixelTest {
  public:
+  RendererPixelTest() : PixelTest(use_vulkan()) {}
+
   RendererType* renderer() {
     return static_cast<RendererType*>(renderer_.get());
   }
@@ -148,7 +155,10 @@
     return "unknown";
   }
 
-  bool use_gpu() { return !!child_context_provider_; }
+  bool use_gpu() const { return !!child_context_provider_; }
+  bool use_vulkan() const {
+    return std::is_base_of<VulkanSkiaRenderer, RendererType>::value;
+  }
 
  protected:
   void SetUp() override;
@@ -268,22 +278,22 @@
 
 template <>
 inline void RendererPixelTest<viz::SkiaRenderer>::SetUp() {
-  SetUpSkiaRenderer(false, false);
+  SetUpSkiaRenderer(false);
 }
 
 template <>
 inline void RendererPixelTest<SkiaRendererWithFlippedSurface>::SetUp() {
-  SetUpSkiaRenderer(true, false);
+  SetUpSkiaRenderer(true);
 }
 
 template <>
 inline void RendererPixelTest<VulkanSkiaRenderer>::SetUp() {
-  SetUpSkiaRenderer(false, true);
+  SetUpSkiaRenderer(false);
 }
 
 template <>
 inline void RendererPixelTest<VulkanSkiaRendererWithFlippedSurface>::SetUp() {
-  SetUpSkiaRenderer(true, true);
+  SetUpSkiaRenderer(true);
 }
 
 typedef RendererPixelTest<viz::GLRenderer> GLRendererPixelTest;
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index f4a77dd9..2ddf877e 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -43,7 +43,6 @@
   "java/src/org/chromium/chrome/browser/KeyboardShortcuts.java",
   "java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java",
   "java/src/org/chromium/chrome/browser/LauncherShortcutActivity.java",
-  "java/src/org/chromium/chrome/browser/LegacyChromeFeatureList.java",
   "java/src/org/chromium/chrome/browser/MenuOrKeyboardActionController.java",
   "java/src/org/chromium/chrome/browser/NavigationPopup.java",
   "java/src/org/chromium/chrome/browser/NearOomMonitor.java",
@@ -1453,10 +1452,10 @@
   "java/src/org/chromium/chrome/browser/settings/search_engine/SearchEngineAdapter.java",
   "java/src/org/chromium/chrome/browser/settings/search_engine/SearchEngineSettings.java",
   "java/src/org/chromium/chrome/browser/settings/sync/AccountManagementFragment.java",
-  "java/src/org/chromium/chrome/browser/settings/sync/ManageSyncPreferences.java",
+  "java/src/org/chromium/chrome/browser/settings/sync/ManageSyncSettings.java",
   "java/src/org/chromium/chrome/browser/settings/sync/SignInPreference.java",
-  "java/src/org/chromium/chrome/browser/settings/sync/SyncAndServicesPreferences.java",
-  "java/src/org/chromium/chrome/browser/settings/sync/SyncPreferenceUtils.java",
+  "java/src/org/chromium/chrome/browser/settings/sync/SyncAndServicesSettings.java",
+  "java/src/org/chromium/chrome/browser/settings/sync/SyncSettingsUtils.java",
   "java/src/org/chromium/chrome/browser/settings/themes/RadioButtonGroupThemePreference.java",
   "java/src/org/chromium/chrome/browser/settings/themes/ThemeSettingsFragment.java",
   "java/src/org/chromium/chrome/browser/settings/themes/ThemeType.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 7dd498d..0c2f94c 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -450,9 +450,9 @@
   "javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java",
   "javatests/src/org/chromium/chrome/browser/sync/FirstRunTest.java",
-  "javatests/src/org/chromium/chrome/browser/sync/ManageSyncPreferencesTest.java",
+  "javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/OpenTabsTest.java",
-  "javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesPreferencesTest.java",
+  "javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesSettingsTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/SyncTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java",
   "javatests/src/org/chromium/chrome/browser/sync/TypedUrlsTest.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
index 3a2b0a9..0e8495a 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
@@ -16,7 +16,7 @@
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChipViewHolder;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.sync.SyncAndServicesPreferences;
+import org.chromium.chrome.browser.settings.sync.SyncAndServicesSettings;
 import org.chromium.chrome.browser.ui.widget.textbubble.TextBubble;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
@@ -142,7 +142,7 @@
             int itemId = item.getItemId();
             if (itemId == R.id.settings) {
                 SettingsLauncher.getInstance().launchSettingsPage(
-                        view.mHeader.getContext(), SyncAndServicesPreferences.class);
+                        view.mHeader.getContext(), SyncAndServicesSettings.class);
                 return true;
             } else if (itemId == R.id.send_feedback) {
                 if (feedbackCallback != null) {
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java
index 1bd6cca..733c9c0 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java
@@ -14,7 +14,6 @@
 import static org.hamcrest.Matchers.not;
 
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.checkElementExists;
-import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getAbsoluteBoundingRect;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getBoundingRectForElement;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getViewport;
 import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
@@ -26,7 +25,6 @@
 import android.graphics.RectF;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
-import android.util.DisplayMetrics;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -44,7 +42,6 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
-import org.chromium.content_public.browser.test.util.TestTouchUtils;
 
 import java.util.Collections;
 import java.util.concurrent.ExecutionException;
@@ -216,23 +213,10 @@
         }
     }
 
-    /** Performs a single tap on the center of the specified element. */
-    private void tapElement(String elementId) throws Exception {
-        Rect coords = getAbsoluteBoundingRect(elementId, mTestRule);
-        float x = coords.left + 0.5f * (coords.right - coords.left);
-        float y = coords.top + 0.5f * (coords.bottom - coords.top);
-
-        // Sanity check, can only click on coordinates on screen.
-        DisplayMetrics displayMetrics = mTestRule.getActivity().getResources().getDisplayMetrics();
-        if (x < 0 || x > displayMetrics.widthPixels || y < 0 || y > displayMetrics.heightPixels) {
-            throw new IllegalArgumentException(elementId + " not on screen: tried to tap x=" + x
-                    + ", y=" + y + ", which is outside of display with w="
-                    + displayMetrics.widthPixels + ", h=" + displayMetrics.heightPixels);
-        }
-        TestTouchUtils.singleClick(InstrumentationRegistry.getInstrumentation(), x, y);
+    void tapElement(String elementId) throws Exception {
+        AutofillAssistantUiTestUtil.tapElement(elementId, mTestRule);
     }
 
-
     /**
      * Scrolls to the specified element on the webpage, if necessary.
      */
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentTest.java
index a46efd9..eef1d6c 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentTest.java
@@ -23,6 +23,7 @@
 
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getElementValue;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.startAutofillAssistant;
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.tapElement;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilKeyboardMatchesCondition;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewMatchesCondition;
 
@@ -42,8 +43,12 @@
 import org.chromium.chrome.browser.autofill_assistant.proto.ChipProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.CollectUserDataProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.CollectUserDataProto.TermsAndConditionsState;
+import org.chromium.chrome.browser.autofill_assistant.proto.ElementAreaProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ElementAreaProto.Rectangle;
 import org.chromium.chrome.browser.autofill_assistant.proto.ElementReferenceProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.FocusElementProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.PromptProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.PromptProto.Choice;
 import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto.PresentationProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.TextInputProto;
@@ -51,6 +56,7 @@
 import org.chromium.chrome.browser.autofill_assistant.proto.TextInputSectionProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.UseCreditCardProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.UserFormSectionProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.VisibilityRequirement;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -148,6 +154,78 @@
     }
 
     /**
+     * Showcasts an element of the webpage and checks that it can be interacted with.
+     */
+    @Test
+    @MediumTest
+    public void testTermsAndConditionsWithFocusElement() throws Exception {
+        String profileId = mHelper.addDummyProfile("John Doe", "johndoe@gmail.com");
+        mHelper.addDummyCreditCard(profileId);
+
+        ArrayList<ActionProto> list = new ArrayList<>();
+        list.add(
+                (ActionProto) ActionProto.newBuilder()
+                        .setFocusElement(
+                                FocusElementProto.newBuilder()
+                                        .setElement(ElementReferenceProto.newBuilder().addSelectors(
+                                                "div.terms"))
+                                        .setTouchableElementArea(
+                                                ElementAreaProto.newBuilder().addTouchable(
+                                                        Rectangle.newBuilder().addElements(
+                                                                ElementReferenceProto.newBuilder()
+                                                                        .addSelectors(
+                                                                                "div.terms")))))
+                        .build());
+        list.add(
+                (ActionProto) ActionProto.newBuilder()
+                        .setCollectUserData(CollectUserDataProto.newBuilder()
+                                                    .setRequestPaymentMethod(true)
+                                                    .addSupportedBasicCardNetworks("visa")
+                                                    .setPrivacyNoticeText("3rd party privacy text")
+                                                    .setShowTermsAsCheckbox(true)
+                                                    .setRequestTermsAndConditions(true)
+                                                    .setAcceptTermsAndConditionsText("accept terms")
+                                                    .setTermsAndConditionsState(
+                                                            TermsAndConditionsState.ACCEPTED))
+                        .build());
+        Choice toggle_chip = Choice.newBuilder()
+                                     .setChip(ChipProto.newBuilder().setText("Toggle"))
+                                     .addShowOnlyIfElementExists(
+                                             ElementReferenceProto.newBuilder()
+                                                     .addSelectors("div#toggle_on")
+                                                     .setVisibilityRequirement(
+                                                             VisibilityRequirement.MUST_BE_VISIBLE))
+                                     .build();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setPrompt(PromptProto.newBuilder()
+                                            .setMessage("Finish")
+                                            .addChoices(Choice.newBuilder())
+                                            .addChoices(toggle_chip))
+                         .build());
+
+        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
+                (SupportedScriptProto) SupportedScriptProto.newBuilder()
+                        .setPath("form_target_website.html")
+                        .setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
+                                ChipProto.newBuilder().setText("Continue")))
+                        .build(),
+                list);
+
+        AutofillAssistantTestService testService =
+                new AutofillAssistantTestService(Collections.singletonList(script));
+        startAutofillAssistant(mTestRule.getActivity(), testService);
+
+        waitUntilViewMatchesCondition(withText("Continue"), isDisplayed());
+        tapElement("button", mTestRule);
+        onView(withText("Continue")).perform(click());
+        waitUntilViewMatchesCondition(withText("Toggle"), isDisplayed());
+
+        // Verify that in the next step the touchable window is not present anymore.
+        tapElement("button", mTestRule);
+        onView(withText("Toggle")).check(matches(isDisplayed()));
+    }
+
+    /**
      * Check that sending an empty privacy notice text removes the privacy notice section.
      */
     @Test
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
index eed3f5e5..8db2221 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
@@ -27,6 +27,7 @@
 import android.support.test.espresso.matcher.BoundedMatcher;
 import android.text.Spanned;
 import android.text.style.ClickableSpan;
+import android.util.DisplayMetrics;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -61,6 +62,7 @@
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.content_public.browser.test.util.TestTouchUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -406,6 +408,23 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> AutofillAssistantFacade.start(activity));
     }
 
+    /** Performs a single tap on the center of the specified element. */
+    public static void tapElement(String elementId, CustomTabActivityTestRule testRule)
+            throws Exception {
+        Rect coords = getAbsoluteBoundingRect(elementId, testRule);
+        float x = coords.left + 0.5f * (coords.right - coords.left);
+        float y = coords.top + 0.5f * (coords.bottom - coords.top);
+
+        // Sanity check, can only click on coordinates on screen.
+        DisplayMetrics displayMetrics = testRule.getActivity().getResources().getDisplayMetrics();
+        if (x < 0 || x > displayMetrics.widthPixels || y < 0 || y > displayMetrics.heightPixels) {
+            throw new IllegalArgumentException(elementId + " not on screen: tried to tap x=" + x
+                    + ", y=" + y + ", which is outside of display with w="
+                    + displayMetrics.widthPixels + ", h=" + displayMetrics.heightPixels);
+        }
+        TestTouchUtils.singleClick(InstrumentationRegistry.getInstrumentation(), x, y);
+    }
+
     /** Computes the bounding rectangle of the specified DOM element in absolute screen space. */
     public static Rect getAbsoluteBoundingRect(String elementId, CustomTabActivityTestRule testRule)
             throws Exception {
@@ -470,6 +489,21 @@
         return result.getBoolean(0);
     }
 
+    /** Checks whether the specified element is displayed in the DOM tree. */
+    public static boolean checkElementIsDisplayed(String elementId, WebContents webContents)
+            throws Exception {
+        TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper javascriptHelper =
+                new TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper();
+        javascriptHelper.evaluateJavaScriptForTests(webContents,
+                "(function() {"
+                        + " return [document.getElementById('" + elementId
+                        + "').style.display != \"none\"]; "
+                        + "})()");
+        javascriptHelper.waitUntilHasValue();
+        JSONArray result = new JSONArray(javascriptHelper.getJsonResultAndClear());
+        return result.getBoolean(0);
+    }
+
     /**
      * Retrieves the visual viewport of the webpage in CSS pixel coordinates.
      */
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
index 983be51..1f373ed 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
@@ -336,6 +336,7 @@
             case ActionType.DOWNLOAD:
                 return WindowOpenDisposition.SAVE_TO_DISK;
             case ActionType.LEARN_MORE:
+            case ActionType.MANAGE_INTERESTS:
             case ActionType.UNKNOWN:
             default:
                 return WindowOpenDisposition.UNKNOWN;
@@ -353,6 +354,9 @@
             case ActionType.LEARN_MORE:
                 NewTabPageUma.recordAction(NewTabPageUma.ACTION_CLICKED_LEARN_MORE);
                 break;
+            case ActionType.MANAGE_INTERESTS:
+                NewTabPageUma.recordAction(NewTabPageUma.ACTION_CLICKED_MANAGE_INTERESTS);
+                break;
             case ActionType.DOWNLOAD:
             case ActionType.UNKNOWN:
             default:
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/api/host/logging/ActionType.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/api/host/logging/ActionType.java
index bc619a2..4959219c 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/api/host/logging/ActionType.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/api/host/logging/ActionType.java
@@ -15,7 +15,7 @@
  */
 @IntDef({ActionType.UNKNOWN, ActionType.OPEN_URL, ActionType.OPEN_URL_INCOGNITO,
         ActionType.OPEN_URL_NEW_TAB, ActionType.OPEN_URL_NEW_WINDOW, ActionType.DOWNLOAD,
-        ActionType.LEARN_MORE, ActionType.NEXT_VALUE})
+        ActionType.LEARN_MORE, ActionType.MANAGE_INTERESTS, ActionType.NEXT_VALUE})
 // LINT.IfChange
 public @interface ActionType {
     int UNKNOWN = -1;
@@ -25,6 +25,7 @@
     int OPEN_URL_NEW_WINDOW = 3;
     int DOWNLOAD = 5;
     int LEARN_MORE = 6;
-    int NEXT_VALUE = 7;
+    int MANAGE_INTERESTS = 7;
+    int NEXT_VALUE = 8;
 }
 // LINT.ThenChange
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParser.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParser.java
index c8adb29..0a20e7a 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParser.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParser.java
@@ -7,6 +7,7 @@
 import static org.chromium.chrome.browser.feed.library.common.Validators.checkState;
 import static org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata.Type.DOWNLOAD;
 import static org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata.Type.LEARN_MORE;
+import static org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata.Type.MANAGE_INTERESTS;
 import static org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata.Type.OPEN_URL;
 import static org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata.Type.OPEN_URL_INCOGNITO;
 import static org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata.Type.OPEN_URL_NEW_TAB;
@@ -44,6 +45,8 @@
  */
 public final class FeedActionParser implements ActionParser {
     private static final String TAG = "FeedActionParser";
+    static final String EXPECTED_MANAGE_INTERESTS_URL =
+            "https://www.google.com/preferences/interests";
 
     private final PietFeedActionPayloadRetriever mPietFeedActionPayloadRetriever;
     private final ProtocolAdapter mProtocolAdapter;
@@ -81,6 +84,13 @@
             case OPEN_URL_NEW_WINDOW:
             case OPEN_URL_INCOGNITO:
             case OPEN_URL_NEW_TAB:
+                // TODO(freedjm): Use a different action type for Manage Interests to handle it
+                // separately from a simple OPEN_URL action.
+                if (feedActionMetadata.getOpenUrlData().hasUrl()
+                        && feedActionMetadata.getOpenUrlData().getUrl().equals(
+                                EXPECTED_MANAGE_INTERESTS_URL)) {
+                    streamActionApi.onClientAction(ActionTypesConverter.convert(MANAGE_INTERESTS));
+                }
                 handleOpenUrl(feedActionMetadata.getType(), feedActionMetadata.getOpenUrlData(),
                         streamActionApi);
                 break;
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionparser/internal/ActionTypesConverter.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionparser/internal/ActionTypesConverter.java
index 91b04f4..59bd71fd 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionparser/internal/ActionTypesConverter.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionparser/internal/ActionTypesConverter.java
@@ -27,6 +27,8 @@
                 return ActionType.DOWNLOAD;
             case LEARN_MORE:
                 return ActionType.LEARN_MORE;
+            case MANAGE_INTERESTS:
+                return ActionType.MANAGE_INTERESTS;
             default:
                 return ActionType.UNKNOWN;
         }
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParserTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParserTest.java
index dba0bd67..95259b0b 100644
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParserTest.java
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParserTest.java
@@ -216,6 +216,19 @@
             .build())
         .build();
 
+    private static final FeedActionPayload MANAGE_INTERESTS_FEED_ACTION =
+        FeedActionPayload.newBuilder()
+        .setExtension(FeedAction.feedActionExtension,
+            FeedAction.newBuilder()
+            .setMetadata(
+                FeedActionMetadata.newBuilder()
+                .setType(Type.OPEN_URL)
+                .setOpenUrlData(
+                    OpenUrlData.newBuilder().setUrl(
+                        FeedActionParser.EXPECTED_MANAGE_INTERESTS_URL)))
+            .build())
+        .build();
+
     private static final Action OPEN_URL_ACTION =
         Action.newBuilder()
         .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
@@ -284,6 +297,15 @@
             .setFeedActionPayload(LEARN_MORE_FEED_ACTION)
             .build())
         .build();
+
+    private static final Action MANAGE_INTERESTS_ACTION =
+        Action.newBuilder()
+        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
+            PietFeedActionPayload.newBuilder()
+            .setFeedActionPayload(MANAGE_INTERESTS_FEED_ACTION)
+            .build())
+        .build();
+
     private static final ContentId DISMISS_CONTENT_ID = ContentId.newBuilder().setId(123).build();
 
     private static final String DISMISS_CONTENT_ID_STRING = "dismissContentId";
@@ -438,6 +460,16 @@
     }
 
     @Test
+    public void testParseAction_manageInterests() {
+        when(mStreamActionApi.canOpenUrl()).thenReturn(true);
+        mFeedActionParser.parseAction(MANAGE_INTERESTS_ACTION, mStreamActionApi,
+                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
+
+        verify(mStreamActionApi).openUrl(FeedActionParser.EXPECTED_MANAGE_INTERESTS_URL);
+        verify(mStreamActionApi).onClientAction(ActionType.MANAGE_INTERESTS);
+    }
+
+    @Test
     public void testParseAction_openUrlWithParam() {
         when(mStreamActionApi.canOpenUrl()).thenReturn(true);
         mFeedActionParser.parseAction(OPEN_URL_WITH_PARAM_ACTION, mStreamActionApi,
diff --git a/chrome/android/java/res/values/ids.xml b/chrome/android/java/res/values/ids.xml
index e8f9863..9ba9c42d 100644
--- a/chrome/android/java/res/values/ids.xml
+++ b/chrome/android/java/res/values/ids.xml
@@ -97,6 +97,7 @@
     <item type="id" name="contextmenu_open_image" />
     <item type="id" name="contextmenu_open_image_in_new_tab" />
     <item type="id" name="contextmenu_open_image_in_ephemeral_tab" />
+    <item type="id" name="contextmenu_copy_image" />
     <item type="id" name="contextmenu_search_by_image" />
     <item type="id" name="contextmenu_search_with_google_lens" />
     <item type="id" name="contextmenu_share_image" />
diff --git a/chrome/android/java/res/xml/main_preferences.xml b/chrome/android/java/res/xml/main_preferences.xml
index d9e55c1..a90c729d 100644
--- a/chrome/android/java/res/xml/main_preferences.xml
+++ b/chrome/android/java/res/xml/main_preferences.xml
@@ -19,7 +19,7 @@
         android:order="2"
         android:layout="@layout/account_management_account_row"
         android:title="@string/prefs_sync_and_services"
-        android:fragment="org.chromium.chrome.browser.settings.sync.SyncAndServicesPreferences"/>
+        android:fragment="org.chromium.chrome.browser.settings.sync.SyncAndServicesSettings"/>
 
     <PreferenceCategory
         android:key="basics_section"
diff --git a/chrome/android/java/res/xml/single_website_preferences.xml b/chrome/android/java/res/xml/single_website_preferences.xml
index 7d7ce211..225aefc 100644
--- a/chrome/android/java/res/xml/single_website_preferences.xml
+++ b/chrome/android/java/res/xml/single_website_preferences.xml
@@ -74,6 +74,10 @@
         android:key="nfc_permission_list" />
     <ListPreference
         android:key="bluetooth_scanning_permission_list" />
+    <ListPreference
+        android:key="vr_permission_list" />
+    <ListPreference
+        android:key="ar_permission_list" />
 
     <org.chromium.chrome.browser.settings.ButtonPreference
         android:key="reset_site_button"
diff --git a/chrome/android/java/res/xml/site_settings_preferences.xml b/chrome/android/java/res/xml/site_settings_preferences.xml
index da634a8..c25b985 100644
--- a/chrome/android/java/res/xml/site_settings_preferences.xml
+++ b/chrome/android/java/res/xml/site_settings_preferences.xml
@@ -89,4 +89,12 @@
     <org.chromium.chrome.browser.settings.website.SiteSettingsPreference
         android:fragment="org.chromium.chrome.browser.settings.website.SingleCategorySettings"
         android:key="bluetooth_scanning" />
+    <!-- VR -->
+    <org.chromium.chrome.browser.settings.website.SiteSettingsPreference
+        android:fragment="org.chromium.chrome.browser.settings.website.SingleCategorySettings"
+        android:key="virtual_reality" />
+    <!-- AR -->
+    <org.chromium.chrome.browser.settings.website.SiteSettingsPreference
+        android:fragment="org.chromium.chrome.browser.settings.website.SingleCategorySettings"
+        android:key="augmented_reality" />
 </PreferenceScreen>
diff --git a/chrome/android/java/res/xml/sync_and_services_preferences.xml b/chrome/android/java/res/xml/sync_and_services_preferences.xml
index 7c5a7c4..12617bf 100644
--- a/chrome/android/java/res/xml/sync_and_services_preferences.xml
+++ b/chrome/android/java/res/xml/sync_and_services_preferences.xml
@@ -38,7 +38,7 @@
         <org.chromium.chrome.browser.settings.ChromeBasePreference
             android:key="manage_sync"
             android:title="@string/manage_sync_title"
-            android:fragment="org.chromium.chrome.browser.settings.sync.ManageSyncPreferences"/>
+            android:fragment="org.chromium.chrome.browser.settings.sync.ManageSyncSettings"/>
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index da25482e..0ce3d5a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -758,6 +758,11 @@
         IntentHandler.setTestIntentsEnabled(
                 CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_TEST_INTENTS));
         mIntentHandler = new IntentHandler(createIntentHandlerDelegate(), getPackageName());
+
+        // This also ensures that subsequent native library and resource loading takes place
+        // immediately, needed by restored tabs that use DFM.
+        // TODO(https://crbug.com/1008162): Have the Module system register its own observer.
+        Module.doDeferredNativeRegistrations();
     }
 
     @Override
@@ -1349,9 +1354,6 @@
         maybeRemoveWindowBackground();
         DownloadManagerService.getDownloadManagerService().onActivityLaunched();
 
-        // TODO(https://crbug.com/1008162): Have the Module system register its own observer.
-        Module.doDeferredNativeRegistrations();
-
         VrModuleProvider.maybeInit();
         VrModuleProvider.getDelegate().onNativeLibraryAvailable();
         ArDelegate arDelegate = ArDelegateProvider.getDelegate();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LegacyChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/LegacyChromeFeatureList.java
deleted file mode 100644
index 51d80f5..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/LegacyChromeFeatureList.java
+++ /dev/null
@@ -1,18 +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.
-
-package org.chromium.chrome.browser;
-
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-
-/**
- * Temporary class, identical to {@link ChromeFeatureList}, for migration.
- *
- * This class will be referenced temporarily while ChromeFeatureList is moved to the .flags
- * package.
- *
- * TODO(crbug.com/1041468): Remove this class after downstream uses flags.ChromeFeatureList.
- */
-public abstract class LegacyChromeFeatureList extends ChromeFeatureList {
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
index cd598a5..0da84cdf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
@@ -630,6 +630,14 @@
         createCompositorSurfaceManager(!mIsSurfaceControlEnabled);
     }
 
+    /**
+     * Notifies the native compositor that a tab change has occurred. This
+     * should be called when changing to a valid tab.
+     */
+    public void onTabChanged() {
+        CompositorViewJni.get().onTabChanged(mNativeCompositorView, CompositorView.this);
+    }
+
     @NativeMethods
     interface Natives {
         long init(CompositorView caller, boolean lowMemDevice, WindowAndroid windowAndroid,
@@ -653,5 +661,6 @@
                 long nativeCompositorView, CompositorView caller, WindowAndroid window);
         void cacheBackBufferForCurrentSurface(long nativeCompositorView, CompositorView caller);
         void evictCachedBackBuffer(long nativeCompositorView, CompositorView caller);
+        void onTabChanged(long nativeCompositorView, CompositorView caller);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index fffb32b..4bd445a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -1179,7 +1179,10 @@
 
         if (mTabVisible != tab) {
             if (mTabVisible != null) mTabVisible.removeObserver(mTabObserver);
-            if (tab != null) tab.addObserver(mTabObserver);
+            if (tab != null) {
+                tab.addObserver(mTabObserver);
+                mCompositorView.onTabChanged();
+            }
         }
 
         mTabVisible = tab;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java
index e24c377..df26691 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java
@@ -34,8 +34,9 @@
             Item.OPEN_IN_OTHER_WINDOW, Item.OPEN_IN_EPHEMERAL_TAB, Item.COPY_LINK_ADDRESS,
             Item.COPY_LINK_TEXT, Item.SAVE_LINK_AS, Item.LOAD_ORIGINAL_IMAGE, Item.SAVE_IMAGE,
             Item.OPEN_IMAGE, Item.OPEN_IMAGE_IN_NEW_TAB, Item.OPEN_IMAGE_IN_EPHEMERAL_TAB,
-            Item.SEARCH_BY_IMAGE, Item.SEARCH_WITH_GOOGLE_LENS, Item.CALL, Item.SEND_MESSAGE,
-            Item.ADD_TO_CONTACTS, Item.COPY, Item.SAVE_VIDEO, Item.OPEN_IN_CHROME})
+            Item.COPY_IMAGE, Item.SEARCH_BY_IMAGE, Item.SEARCH_WITH_GOOGLE_LENS, Item.CALL,
+            Item.SEND_MESSAGE, Item.ADD_TO_CONTACTS, Item.COPY, Item.SAVE_VIDEO,
+            Item.OPEN_IN_CHROME})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Item {
         // Values are numerated from 0 and can't have gaps.
@@ -58,19 +59,20 @@
         int OPEN_IMAGE = 12;
         int OPEN_IMAGE_IN_NEW_TAB = 13;
         int OPEN_IMAGE_IN_EPHEMERAL_TAB = 14;
-        int SEARCH_BY_IMAGE = 15;
-        int SEARCH_WITH_GOOGLE_LENS = 16;
+        int COPY_IMAGE = 15;
+        int SEARCH_BY_IMAGE = 16;
+        int SEARCH_WITH_GOOGLE_LENS = 17;
         // Message Group
-        int CALL = 17;
-        int SEND_MESSAGE = 18;
-        int ADD_TO_CONTACTS = 19;
-        int COPY = 20;
+        int CALL = 18;
+        int SEND_MESSAGE = 19;
+        int ADD_TO_CONTACTS = 20;
+        int COPY = 21;
         // Video Group
-        int SAVE_VIDEO = 21;
+        int SAVE_VIDEO = 22;
         // Other
-        int OPEN_IN_CHROME = 22;
+        int OPEN_IN_CHROME = 23;
         // ALWAYS UPDATE!
-        int NUM_ENTRIES = 23;
+        int NUM_ENTRIES = 24;
     }
 
     /**
@@ -92,6 +94,7 @@
             R.id.contextmenu_open_image, // Item.OPEN_IMAGE
             R.id.contextmenu_open_image_in_new_tab, // Item.OPEN_IMAGE_IN_NEW_TAB
             R.id.contextmenu_open_image_in_ephemeral_tab, // Item.OPEN_IMAGE_IN_EPHEMERAL_TAB
+            R.id.contextmenu_copy_image, // Item.COPY_IMAGE
             R.id.contextmenu_search_by_image, // Item.SEARCH_BY_IMAGE
             R.id.contextmenu_search_with_google_lens, // Item.SEARCH_WITH_GOOGLE_LENS
             R.id.contextmenu_call, // Item.CALL
@@ -121,6 +124,7 @@
             R.string.contextmenu_open_image, // Item.OPEN_IMAGE:
             R.string.contextmenu_open_image_in_new_tab, // Item.OPEN_IMAGE_IN_NEW_TAB:
             R.string.contextmenu_open_image_in_ephemeral_tab, // Item.OPEN_IMAGE_IN_EPHEMERAL_TAB:
+            R.string.contextmenu_copy_image, // Item.COPY_IMAGE:
             R.string.contextmenu_search_web_for_image, // Item.SEARCH_BY_IMAGE:
             R.string.contextmenu_search_with_google_lens, // Item.SEARCH_WITH_GOOGLE_LENS:
             R.string.contextmenu_call, // Item.CALL:
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 6e6267e..4aad0697 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
@@ -94,8 +94,8 @@
                 Action.COPY_PHONE_NUMBER, Action.OPEN_IN_NEW_CHROME_TAB,
                 Action.OPEN_IN_CHROME_INCOGNITO_TAB, Action.OPEN_IN_BROWSER, Action.OPEN_IN_CHROME,
                 Action.SHARE_LINK, Action.OPEN_IN_EPHEMERAL_TAB, Action.OPEN_IMAGE_IN_EPHEMERAL_TAB,
-                Action.DIRECT_SHARE_LINK, Action.DIRECT_SHARE_IMAGE,
-                Action.SEARCH_WITH_GOOGLE_LENS})
+                Action.DIRECT_SHARE_LINK, Action.DIRECT_SHARE_IMAGE, Action.SEARCH_WITH_GOOGLE_LENS,
+                Action.COPY_IMAGE})
         @Retention(RetentionPolicy.SOURCE)
         public @interface Action {
             int OPEN_IN_NEW_TAB = 0;
@@ -131,8 +131,9 @@
             int DIRECT_SHARE_IMAGE = 27;
 
             int SEARCH_WITH_GOOGLE_LENS = 28;
+            int COPY_IMAGE = 29;
 
-            int NUM_ENTRIES = 29;
+            int NUM_ENTRIES = 30;
         }
 
         // Note: these values must match the ContextMenuSaveLinkType enum in enums.xml.
@@ -396,6 +397,9 @@
                     if (mShowEphemeralTabNewLabel) item.setShowInProductHelp();
                     imageTab.add(item);
                 }
+                if (ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXT_MENU_COPY_IMAGE)) {
+                    imageTab.add(new ChromeContextMenuItem(Item.COPY_IMAGE));
+                }
                 if (isSrcDownloadableScheme) {
                     imageTab.add(new ChromeContextMenuItem(Item.SAVE_IMAGE));
                     hasSaveImage = true;
@@ -527,6 +531,9 @@
                 title = URLUtil.guessFileName(params.getSrcUrl(), null, null);
             }
             mDelegate.onOpenInEphemeralTab(params.getSrcUrl(), title);
+        } else if (itemId == R.id.contextmenu_copy_image) {
+            ContextMenuUma.record(params, ContextMenuUma.Action.COPY_IMAGE);
+            helper.copyImageToClipboard(mDelegate);
         } else if (itemId == R.id.contextmenu_copy_link_address) {
             ContextMenuUma.record(params, ContextMenuUma.Action.COPY_LINK_ADDRESS);
             mDelegate.onSaveToClipboard(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
index dfae43d..d5a2064 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
@@ -240,6 +240,15 @@
     }
 
     /**
+     * Copy the image, that triggered the current context menu, to system clipboard.
+     * @param delegate The {@link ContextMenuItemDelegate} from context menu for copying image to
+     *         the clipboard.
+     */
+    void copyImageToClipboard(ContextMenuItemDelegate delegate) {
+        retrieveImage((Uri imageUri) -> { delegate.onSaveImageToClipboard(imageUri); });
+    }
+
+    /**
      * Share the image that triggered the current context menu with the last app used to share.
      */
     private void shareImageWithLastShareComponent() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemDelegate.java
index 5d8684a..3d21920 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemDelegate.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.contextmenu;
 
+import android.net.Uri;
+
 import androidx.annotation.IntDef;
 
 import org.chromium.chrome.browser.tab.Tab;
@@ -111,6 +113,12 @@
     void onSaveToClipboard(String text, @ClipboardType int clipboardType);
 
     /**
+     * Called when the image should be saved to the clipboard.
+     * @param Uri The (@link Uri) of the image to save to the clipboard.
+     */
+    void onSaveImageToClipboard(Uri uri);
+
+    /**
      * @return whether an activity is available to handle an intent to call a phone number.
      */
     public boolean supportsCall();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
index d9f2172..3f49c906 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
@@ -13,7 +13,6 @@
 import android.view.View;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.download.home.metrics.UmaUtils;
 import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.RecyclerViewAdapter;
 import org.chromium.ui.modelutil.SimpleRecyclerViewMcp;
@@ -69,7 +68,6 @@
     public void onChipsChanged() {
         List<Chip> chips = mProvider.getChips();
         mModel.set(chips);
-        UmaUtils.recordChipStats(chips.size());
     }
 
     private static RecyclerView createView(Context context) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java
index ac9f5b9..0e3b9a8e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java
@@ -240,15 +240,6 @@
     }
 
     /**
-     * Records the number of chips enabled whenever the chip row is changed.
-     * @param numEnabledChips The number of chips being shown.
-     */
-    public static void recordChipStats(int numEnabledChips) {
-        RecordHistogram.recordCustomCountHistogram(
-                "Android.DownloadManager.Chips.Enabled", numEnabledChips, 1, 10, 10);
-    }
-
-    /**
      * Called to record metrics for the given rename action.
      * @param action The given rename action.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
index 0186668..d9de0df 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
@@ -16,7 +16,7 @@
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.sync.SyncAndServicesPreferences;
+import org.chromium.chrome.browser.settings.sync.SyncAndServicesSettings;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.SigninManager;
 import org.chromium.chrome.browser.signin.SigninManager.SignInCallback;
@@ -103,8 +103,8 @@
      * Opens sign in settings as requested in the FRE sign-in dialog.
      */
     private static void openSignInSettings(Activity activity) {
-        final Class<? extends Fragment> fragment = SyncAndServicesPreferences.class;
-        final Bundle arguments = SyncAndServicesPreferences.createArguments(true);
+        final Class<? extends Fragment> fragment = SyncAndServicesSettings.class;
+        final Bundle arguments = SyncAndServicesSettings.createArguments(true);
         SettingsLauncher.getInstance().launchSettingsPage(activity, fragment, arguments);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 1e350bb..c10593f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -23,7 +23,7 @@
     private static Map<String, Boolean> sTestFeatures;
 
     // Prevent instantiation.
-    protected ChromeFeatureList() {}
+    private ChromeFeatureList() {}
 
     /**
      * Sets the feature flags to use in JUnit tests, since native calls are not available there.
@@ -220,6 +220,7 @@
             "ContentSuggestionsScrollToLoad";
     public static final String CONTENT_INDEXING_NTP = "ContentIndexingNTP";
     public static final String CONTENT_INDEXING_DOWNLOAD_HOME = "ContentIndexingDownloadHome";
+    public static final String CONTEXT_MENU_COPY_IMAGE = "ContextMenuCopyImage";
     public static final String CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS =
             "ContextMenuSearchWithGoogleLens";
     public static final String CONTEXTUAL_SEARCH_DEFINITIONS = "ContextualSearchDefinitions";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/flags/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/flags/OWNERS
index aabf8926..8c319162b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/flags/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/flags/OWNERS
@@ -1,10 +1,4 @@
-dtrainor@chromium.org
-hnakashima@chromium.org
-twellington@chromium.org
-
-# This is For simple changes of adding/removing features.  For more structural
-# changes, use the normal OWNERS rules.
-per-file ChromeFeatureList.java=*
+file://chrome/browser/flags/OWNERS
 
 # TEAM: clank-modularization@chromium.org
 # COMPONENT: UI>Browser>Mobile
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java
index 2fa76cf..3a790cc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java
@@ -19,9 +19,9 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.sync.SyncAndServicesPreferences;
-import org.chromium.chrome.browser.settings.sync.SyncPreferenceUtils;
-import org.chromium.chrome.browser.settings.sync.SyncPreferenceUtils.SyncError;
+import org.chromium.chrome.browser.settings.sync.SyncAndServicesSettings;
+import org.chromium.chrome.browser.settings.sync.SyncSettingsUtils;
+import org.chromium.chrome.browser.settings.sync.SyncSettingsUtils.SyncError;
 import org.chromium.content_public.browser.WebContents;
 
 import java.lang.annotation.Retention;
@@ -71,10 +71,10 @@
     private static InfoBar show() {
         Context context = getApplicationContext();
         @SyncError
-        int error = SyncPreferenceUtils.getSyncError();
+        int error = SyncSettingsUtils.getSyncError();
         String error_message = (error == SyncError.SYNC_SETUP_INCOMPLETE)
                 ? context.getString(R.string.sync_settings_not_confirmed_title)
-                : SyncPreferenceUtils.getSyncErrorHint(context, error);
+                : SyncSettingsUtils.getSyncErrorHint(context, error);
         return new SyncErrorInfoBar(getSyncErrorInfoBarType(),
                 context.getString(R.string.sync_error_card_title), error_message,
                 context.getString(R.string.open_settings_button));
@@ -85,8 +85,7 @@
         recordHistogram(mType, SyncErrorInfoBarAction.OPEN_SETTINGS_CLICKED);
 
         SettingsLauncher.getInstance().launchSettingsPage(getApplicationContext(),
-                SyncAndServicesPreferences.class,
-                SyncAndServicesPreferences.createArguments(false));
+                SyncAndServicesSettings.class, SyncAndServicesSettings.createArguments(false));
     }
 
     @CalledByNative
@@ -142,7 +141,7 @@
     @SyncErrorInfoBarType
     private static int getSyncErrorInfoBarType() {
         @SyncError
-        int error = SyncPreferenceUtils.getSyncError();
+        int error = SyncSettingsUtils.getSyncError();
         switch (error) {
             case SyncError.AUTH_ERROR:
                 return SyncErrorInfoBarType.AUTH_ERROR;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
index 07bd901..721f9d3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -353,10 +353,8 @@
         deferredStartupHandler.addDeferredTask(new Runnable() {
             @Override
             public void run() {
-                RecordHistogram.recordBooleanHistogram("Settings.ShowHomeButtonPreferenceState",
-                        HomepageManager.isHomepageEnabled());
-                RecordHistogram.recordBooleanHistogram("Settings.HomePageIsCustomized",
-                        !HomepageManager.getInstance().getPrefHomepageUseDefaultUri());
+                HomepageManager.recordHomeButtonPreferenceState();
+                HomepageManager.recordHomepageIsCustomized(HomepageManager.isHomepageCustomized());
             }
         });
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeReparentingController.java b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeReparentingController.java
index 0c49800b..7ae47601 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeReparentingController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeReparentingController.java
@@ -44,20 +44,19 @@
 
     @Override
     public void onStartWithNative() {
-        // Note: for now only the current tab is added to the AsyncTabParamsManager when the theme
-        // is changed. In the future these will be added in tab index order and read at reverse
-        // tab index order.
-        final SparseArray<AsyncTabParams> paramsArray = AsyncTabParamsManager.getAsyncTabParams();
-        for (int i = 0; i < paramsArray.size(); i++) {
-            final int tabId = paramsArray.keyAt(i);
+        // Iterate through the params stored in AsyncTabParams and find the tabs stored by
+        // #onNightModeStateChanged. Reparent the background tabs and store the foreground tab
+        // to be reparented last.
+        TabReparentingParams foregroundTabParams = null;
+        SparseArray<AsyncTabParams> paramsArray = AsyncTabParamsManager.getAsyncTabParams().clone();
+        for (int i = paramsArray.size() - 1; i >= 0; i--) {
+            int tabId = paramsArray.keyAt(i);
             AsyncTabParams params = paramsArray.get(tabId);
             if (!(params instanceof TabReparentingParams)) continue;
-            if (params == null) continue;
 
             final TabReparentingParams reparentingParams = (TabReparentingParams) params;
             if (!reparentingParams.isFromNightModeReparenting()) continue;
             if (!reparentingParams.hasTabToReparent()) continue;
-            if (!reparentingParams.hasTabIndex()) continue;
 
             final ReparentingTask reparentingTask =
                     ReparentingTask.get(reparentingParams.getTabToReparent());
@@ -70,12 +69,29 @@
                 return;
             }
 
-            reparentingTask.finish(mReparentingDelegate, () -> {
-                tabModel.addTab(reparentingParams.getTabToReparent(),
-                        reparentingParams.getTabIndex(), TabLaunchType.FROM_REPARENTING);
-                AsyncTabParamsManager.remove(tabId);
-            });
+            if (reparentingParams.isForegroundTab()) {
+                foregroundTabParams = reparentingParams;
+            } else {
+                reattachTab(reparentingTask, reparentingParams, tabModel);
+            }
         }
+
+        if (foregroundTabParams == null) return;
+        final TabModel tabModel = mDelegate.getTabModelSelector().getModel(
+                foregroundTabParams.getTabToReparent().isIncognito());
+        if (tabModel == null) return;
+
+        ReparentingTask task = ReparentingTask.get(foregroundTabParams.getTabToReparent());
+        reattachTab(task, foregroundTabParams, tabModel);
+    }
+
+    /** Reattach the given task/pair to the activity/tab-model. */
+    private void reattachTab(ReparentingTask task, TabReparentingParams params, TabModel tabModel) {
+        task.finish(mReparentingDelegate, () -> {
+            tabModel.addTab(params.getTabToReparent(), params.getTabIndex(),
+                    TabLaunchType.FROM_REPARENTING);
+            AsyncTabParamsManager.remove(params.getTabToReparent().getId());
+        });
     }
 
     @Override
@@ -83,18 +99,31 @@
 
     @Override
     public void onNightModeStateChanged() {
-        // TODO(crbug.com/1031332): Reparent all tabs in the current tab model.
-        Tab tabToDetach = mDelegate.getActivityTabProvider().get();
-        if (tabToDetach == null) return;
-        TabModel currentTabModel = mDelegate.getTabModelSelector().getCurrentModel();
-        if (currentTabModel == null) return;
+        ActivityTabProvider tabProvider = mDelegate.getActivityTabProvider();
 
-        TabReparentingParams params = new TabReparentingParams(tabToDetach, null, null);
-        params.setTabIndex(currentTabModel.indexOf(tabToDetach));
-        params.setFromNightModeReparenting(true);
+        boolean isForegroundTab = true;
+        while (tabProvider.get() != null) {
+            Tab tabToDetach = tabProvider.get();
+            TabModel tabModel = mDelegate.getTabModelSelector().getModel(tabToDetach.isIncognito());
+            if (tabModel == null) continue;
 
-        AsyncTabParamsManager.add(tabToDetach.getId(), params);
-        currentTabModel.removeTab(tabToDetach);
-        ReparentingTask.from(tabToDetach).detach();
+            TabReparentingParams params = new TabReparentingParams(tabToDetach, null, null);
+            params.setFromNightModeReparenting(true);
+
+            // Only the first tab is considered foreground and that is the only one for which
+            // we need an index. It will be reattached at the end. All remaining tabs will be
+            // reattached in reverse order, therefore we can ignore the index.
+            if (isForegroundTab) {
+                params.setIsForegroundTab(true);
+                params.setTabIndex(tabModel.indexOf(tabToDetach));
+                isForegroundTab = false;
+            } else {
+                params.setIsForegroundTab(false);
+            }
+
+            AsyncTabParamsManager.add(tabToDetach.getId(), params);
+            tabModel.removeTab(tabToDetach);
+            ReparentingTask.from(tabToDetach).detach();
+        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java
index 153ceb2..a7a7e95d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java
@@ -75,8 +75,11 @@
     /** User opened an explore sites tile. */
     public static final int ACTION_OPENED_EXPLORE_SITES_TILE = 11;
 
+    /** User clicked on the "Manage Interests" item in the snippet card menu. */
+    public static final int ACTION_CLICKED_MANAGE_INTERESTS = 12;
+
     /** The number of possible actions. */
-    private static final int NUM_ACTIONS = 12;
+    private static final int NUM_ACTIONS = 13;
 
     /** User navigated to a page using the omnibox. */
     private static final int RAPPOR_ACTION_NAVIGATED_USING_OMNIBOX = 0;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/AutoFetchNotifier.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/AutoFetchNotifier.java
index 9a54b42..a3af8c20 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/AutoFetchNotifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/AutoFetchNotifier.java
@@ -36,6 +36,8 @@
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 import org.chromium.chrome.browser.notifications.PendingIntentProvider;
 import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.util.IntentUtils;
@@ -57,15 +59,6 @@
     private static final String EXTRA_URL = "org.chromium.chrome.browser.offlinepages.URL";
     private static final String EXTRA_ACTION = "notification_action";
 
-    // Name of an application preference variable used to track whether or not the in-progress
-    // notification is being shown. This is an alternative to
-    // NotificationManager.getActiveNotifications, which isn't available prior to API level 23.
-    private static final String PREF_SHOWING_IN_PROGRESS = "offline_auto_fetch_showing_in_progress";
-    // The application preference variable which is set to the NotificationAction that triggered the
-    // cancellation, when a cancellation is requested by the user.
-    private static final String PREF_USER_CANCEL_ACTION_IN_PROGRESS =
-            "offline_auto_fetch_user_cancel_action_in_progress";
-
     @VisibleForTesting
     public static TestHooks mTestHooks;
 
@@ -117,10 +110,8 @@
             // the cancellation if Chrome is running. If Chrome isn't running,
             // runNowOrAfterNativeInitialization() will never call our runnable, so set a pref to
             // remember to cancel on next startup.
-            ContextUtils.getAppSharedPreferences()
-                    .edit()
-                    .putInt(PREF_USER_CANCEL_ACTION_IN_PROGRESS, action)
-                    .apply();
+            SharedPreferencesManager.getInstance().writeInt(
+                    ChromePreferenceKeys.OFFLINE_AUTO_FETCH_USER_CANCEL_ACTION_IN_PROGRESS, action);
             // This will call us back with cancellationComplete().
             ChromeBrowserInitializer.getInstance().runNowOrAfterNativeInitialization(
                     AutoFetchNotifier::cancelInProgress);
@@ -207,17 +198,16 @@
     // user interacting with the in-progress notification.
     @CalledByNative
     private static void cancellationComplete() {
+        SharedPreferencesManager prefs = SharedPreferencesManager.getInstance();
         @NotificationAction
-        int currentAction = ContextUtils.getAppSharedPreferences().getInt(
-                PREF_USER_CANCEL_ACTION_IN_PROGRESS, NotificationAction.NUM_ENTRIES);
+        int currentAction = prefs.readInt(
+                ChromePreferenceKeys.OFFLINE_AUTO_FETCH_USER_CANCEL_ACTION_IN_PROGRESS,
+                NotificationAction.NUM_ENTRIES);
         if (currentAction == NotificationAction.NUM_ENTRIES) {
             return;
         }
         reportInProgressNotificationAction(currentAction);
-        ContextUtils.getAppSharedPreferences()
-                .edit()
-                .remove(PREF_USER_CANCEL_ACTION_IN_PROGRESS)
-                .apply();
+        prefs.removeKey(ChromePreferenceKeys.OFFLINE_AUTO_FETCH_USER_CANCEL_ACTION_IN_PROGRESS);
     }
 
     /**
@@ -227,8 +217,9 @@
     @VisibleForTesting
     @CalledByNative
     public static boolean autoFetchInProgressNotificationCanceled() {
-        return ContextUtils.getAppSharedPreferences().getInt(
-                       PREF_USER_CANCEL_ACTION_IN_PROGRESS, NotificationAction.NUM_ENTRIES)
+        return SharedPreferencesManager.getInstance().readInt(
+                       ChromePreferenceKeys.OFFLINE_AUTO_FETCH_USER_CANCEL_ACTION_IN_PROGRESS,
+                       NotificationAction.NUM_ENTRIES)
                 != NotificationAction.NUM_ENTRIES;
     }
 
@@ -363,14 +354,13 @@
     }
 
     private static boolean isShowingInProgressNotification() {
-        return ContextUtils.getAppSharedPreferences().getBoolean(PREF_SHOWING_IN_PROGRESS, false);
+        return SharedPreferencesManager.getInstance().readBoolean(
+                ChromePreferenceKeys.OFFLINE_AUTO_FETCH_SHOWING_IN_PROGRESS, false);
     }
 
     private static void setIsShowingInProgressNotification(boolean showing) {
-        ContextUtils.getAppSharedPreferences()
-                .edit()
-                .putBoolean(PREF_SHOWING_IN_PROGRESS, showing)
-                .apply();
+        SharedPreferencesManager.getInstance().writeBoolean(
+                ChromePreferenceKeys.OFFLINE_AUTO_FETCH_SHOWING_IN_PROGRESS, showing);
     }
 
     private static void cancelInProgress() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 9b9c11ec..31c1c75 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -221,7 +221,7 @@
         StatusView statusView = findViewById(R.id.location_bar_status);
         statusView.setCompositeTouchDelegate(mCompositeTouchDelegate);
         mStatusViewCoordinator = new StatusViewCoordinator(mIsTablet, statusView, mUrlCoordinator);
-        mUrlCoordinator.addTextChangedListener(mStatusViewCoordinator);
+        mUrlCoordinator.addUrlTextChangeListener(mStatusViewCoordinator);
 
         updateShouldAnimateIconChanges();
         mUrlBar.setOnKeyListener(new UrlBarKeyListener());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtils.java
index 3bde23d..aa5d24f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtils.java
@@ -171,7 +171,7 @@
     /** @return Whether the status icon should be hidden when the LocationBar is unfocused. */
     public static boolean currentlyOnNTP(
             ToolbarCommonPropertiesModel toolbarCommonPropertiesModel) {
-        return toolbarCommonPropertiesModel.getNewTabPageForCurrentTab() != null
+        return toolbarCommonPropertiesModel != null
                 && NewTabPage.isNTPUrl(toolbarCommonPropertiesModel.getCurrentUrl());
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index 20ad351..573c3ae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -38,6 +38,8 @@
     class StatusMediatorDelegate {
         /** @see {@link AutocompleteCoordinatorFactory#qualifyPartialURLQuery} */
         boolean isUrlValid(String partialUrl) {
+            if (TextUtils.isEmpty(partialUrl)) return false;
+
             return BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
                            .isFullBrowserStarted()
                     && AutocompleteCoordinatorFactory.qualifyPartialURLQuery(partialUrl) != null;
@@ -256,6 +258,10 @@
         mUrlHasFocus = urlHasFocus;
         updateStatusVisibility();
         updateLocationBarIcon();
+
+        // Set the autocomplete text to be empty on an unfocus event to avoid the globe sticking
+        // around for subsequent focus events.
+        if (!mUrlHasFocus) updateLocationBarIconForUrlBarAutocompleteText("");
     }
 
     void setUrlAnimationFinished(boolean urlHasFocus) {
@@ -480,10 +486,11 @@
     /** @return True if the security icon has been set for the search engine icon. */
     @VisibleForTesting
     boolean maybeUpdateStatusIconForSearchEngineIcon() {
-        // When the search engine logo should be shown, but the engine isn't Google. In this case,
-        // we download the icon on the fly.
-        boolean showFocused =
-                (mUrlHasFocus || mUrlFocusPercent > 0) && mShowStatusIconWhenUrlFocused;
+        boolean showIconWhenFocused = mUrlHasFocus && mShowStatusIconWhenUrlFocused;
+        boolean showIconWhenScrollingOnNTP =
+                SearchEngineLogoUtils.currentlyOnNTP(mToolbarCommonPropertiesModel)
+                && mUrlFocusPercent > 0 && !mUrlHasFocus
+                && !mToolbarCommonPropertiesModel.isLoading() && mShowStatusIconWhenUrlFocused;
         // Show the logo unfocused if "Query in the omnibox" is active or we're on the NTP. Current
         // "Query in the omnibox" behavior makes it active for non-dse searches if you've just
         // changed your default search engine.The included workaround below
@@ -498,7 +505,8 @@
         boolean isIncognito = mToolbarCommonPropertiesModel != null
                 && mToolbarCommonPropertiesModel.isIncognito();
         if (mDelegate.shouldShowSearchEngineLogo(isIncognito) && mIsSearchEngineStateSetup
-                && (showFocused || showUnfocusedSearchResultsPage)) {
+                && (showIconWhenFocused || showIconWhenScrollingOnNTP
+                        || showUnfocusedSearchResultsPage)) {
             getStatusIconResourceForSearchEngineIcon(isIncognito, (statusIconRes) -> {
                 mModel.set(StatusProperties.STATUS_ICON_RESOURCE, statusIconRes);
             });
@@ -590,8 +598,29 @@
         return 0;
     }
 
-    /** @see android.text.TextWatcher#onTextChanged */
+    /** @see org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener */
     void onTextChanged(CharSequence urlBarText) {
+        updateLocationBarIconForUrlBarAutocompleteText(
+                resolveUrlBarTextWithAutocomplete(urlBarText));
+    }
+
+    /**
+     * Updates variables and possibly the status icon based on the given urlBarTextWithAutocomplete.
+     */
+    private void updateLocationBarIconForUrlBarAutocompleteText(String urlBarTextWithAutocomplete) {
+        // Ignore text we've already seen to avoid unnecessary updates to the drawable resource.
+        if (TextUtils.equals(mUrlBarTextWithAutocomplete, urlBarTextWithAutocomplete)) return;
+
+        mUrlBarTextWithAutocomplete = urlBarTextWithAutocomplete;
+        boolean isValid = mDelegate.isUrlValid(mUrlBarTextWithAutocomplete);
+        if (isValid != mUrlBarTextIsValidUrl) {
+            mUrlBarTextIsValidUrl = isValid;
+            updateLocationBarIcon();
+        }
+    }
+
+    @VisibleForTesting
+    protected String resolveUrlBarTextWithAutocomplete(CharSequence urlBarText) {
         String currentAutocompleteText = mUrlBarEditingTextStateProvider.getTextWithAutocomplete();
         String urlTextWithAutocomplete;
         if (TextUtils.isEmpty(urlBarText)) {
@@ -607,14 +636,7 @@
             urlTextWithAutocomplete = urlBarText.toString();
         }
 
-        if (TextUtils.equals(mUrlBarTextWithAutocomplete, urlTextWithAutocomplete)) return;
-
-        mUrlBarTextWithAutocomplete = urlTextWithAutocomplete;
-        boolean isValid = mDelegate.isUrlValid(mUrlBarTextWithAutocomplete);
-        if (isValid != mUrlBarTextIsValidUrl) {
-            mUrlBarTextIsValidUrl = isValid;
-            updateLocationBarIcon();
-        }
+        return urlTextWithAutocomplete;
     }
 
     void setDelegateForTesting(StatusMediatorDelegate delegate) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
index bd391b31..efbc299 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
@@ -5,8 +5,6 @@
 package org.chromium.chrome.browser.omnibox.status;
 
 import android.content.res.Resources;
-import android.text.Editable;
-import android.text.TextWatcher;
 import android.view.View;
 
 import androidx.annotation.DrawableRes;
@@ -14,6 +12,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
+import org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener;
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
 import org.chromium.chrome.browser.page_info.PageInfoController;
 import org.chromium.chrome.browser.tab.Tab;
@@ -26,7 +25,7 @@
  * A component for displaying a status icon (e.g. security icon or navigation icon) and optional
  * verbose status text.
  */
-public class StatusViewCoordinator implements View.OnClickListener, TextWatcher {
+public class StatusViewCoordinator implements View.OnClickListener, UrlTextChangeListener {
     private final StatusView mStatusView;
     private final StatusMediator mMediator;
     private final PropertyModel mModel;
@@ -248,13 +247,7 @@
     }
 
     @Override
-    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
-    @Override
-    public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
-        mMediator.onTextChanged(charSequence);
+    public void onTextChanged(String textWithoutAutocomplete, String textWithAutocomplete) {
+        mMediator.onTextChanged(textWithoutAutocomplete);
     }
-
-    @Override
-    public void afterTextChanged(Editable editable) {}
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/HomepageManager.java b/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/HomepageManager.java
index 05362ef..15c25e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/HomepageManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/HomepageManager.java
@@ -6,14 +6,20 @@
 
 import android.text.TextUtils;
 
+import androidx.annotation.IntDef;
+
 import org.chromium.base.ObserverList;
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.browser.flags.FeatureUtilities;
 import org.chromium.chrome.browser.homepage.HomepagePolicyManager;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.util.UrlConstants;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Provides information regarding homepage enabled states and URI.
  *
@@ -21,6 +27,25 @@
  */
 public class HomepageManager implements HomepagePolicyManager.HomepagePolicyStateListener {
     /**
+     * Possible states for HomeButton. Used for Histogram
+     * Settings.ShowHomeButtonPreferenceStateManaged. Currently {@link
+     * HomeButtonPreferenceState.MANAGED_DISABLED } is not used.
+     *
+     * These values are persisted to logs, and should therefore never be renumbered nor reused.
+     */
+    @IntDef({HomeButtonPreferenceState.USER_DISABLED, HomeButtonPreferenceState.USER_ENABLED,
+            HomeButtonPreferenceState.MANAGED_DISABLED, HomeButtonPreferenceState.MANAGED_ENABLED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HomeButtonPreferenceState {
+        int USER_DISABLED = 0;
+        int USER_ENABLED = 1;
+        int MANAGED_DISABLED = 2;
+        int MANAGED_ENABLED = 3;
+
+        int NUM_ENTRIES = 4;
+    }
+
+    /**
      * An interface to use for getting homepage related updates.
      */
     public interface HomepageStateListener {
@@ -84,6 +109,14 @@
     }
 
     /**
+     * @return Whether or not current homepage is customized.
+     */
+    public static boolean isHomepageCustomized() {
+        return !HomepagePolicyManager.isHomepageManagedByPolicy()
+                && !getInstance().getPrefHomepageUseDefaultUri();
+    }
+
+    /**
      * @return Whether to close the app when the user has zero tabs.
      */
     public static boolean shouldCloseAppWithZeroTabs() {
@@ -98,9 +131,14 @@
         if (!isHomepageEnabled()) return null;
 
         HomepageManager manager = getInstance();
-        String homepageUri = manager.getPrefHomepageUseDefaultUri()
-                ? getDefaultHomepageUri()
-                : manager.getPrefHomepageCustomUri();
+        String homepageUri;
+        if (HomepagePolicyManager.isHomepageManagedByPolicy()) {
+            homepageUri = HomepagePolicyManager.getHomepageUrl();
+        } else if (manager.getPrefHomepageUseDefaultUri()) {
+            homepageUri = getDefaultHomepageUri();
+        } else {
+            homepageUri = manager.getPrefHomepageCustomUri();
+        }
         return TextUtils.isEmpty(homepageUri) ? null : homepageUri;
     }
 
@@ -108,10 +146,7 @@
      * @return The default homepage URI if the homepage is partner provided or the new tab page
      *         if the homepage button is force enabled via flag.
      */
-    public static String getDefaultHomepageUri() {
-        if (HomepagePolicyManager.isHomepageManagedByPolicy()) {
-            return HomepagePolicyManager.getHomepageUrl();
-        }
+    private static String getDefaultHomepageUri() {
         if (PartnerBrowserCustomizations.isHomepageProviderAvailableAndEnabled()) {
             return PartnerBrowserCustomizations.getHomePageUrl();
         }
@@ -135,7 +170,7 @@
         mSharedPreferencesManager.writeBoolean(ChromePreferenceKeys.HOMEPAGE_ENABLED, enabled);
         RecordHistogram.recordBooleanHistogram(
                 "Settings.ShowHomeButtonPreferenceStateChanged", enabled);
-        RecordHistogram.recordBooleanHistogram("Settings.ShowHomeButtonPreferenceState", enabled);
+        recordHomeButtonPreferenceState();
         notifyHomepageUpdated();
     }
 
@@ -158,21 +193,52 @@
      */
     public boolean getPrefHomepageUseDefaultUri() {
         return mSharedPreferencesManager.readBoolean(
-                       ChromePreferenceKeys.HOMEPAGE_USE_DEFAULT_URI, true)
-                || HomepagePolicyManager.isHomepageManagedByPolicy();
+                ChromePreferenceKeys.HOMEPAGE_USE_DEFAULT_URI, true);
     }
 
     /**
      * Sets whether the homepage URL is the default value.
      */
     public void setPrefHomepageUseDefaultUri(boolean useDefaultUri) {
-        RecordHistogram.recordBooleanHistogram("Settings.HomePageIsCustomized", !useDefaultUri);
+        assert !HomepagePolicyManager.isHomepageManagedByPolicy();
+
+        recordHomepageIsCustomized(!useDefaultUri);
         mSharedPreferencesManager.writeBoolean(
                 ChromePreferenceKeys.HOMEPAGE_USE_DEFAULT_URI, useDefaultUri);
     }
 
+    /**
+     * Get the homepage button preference state.
+     */
+    public static void recordHomeButtonPreferenceState() {
+        if (!FeatureUtilities.isHomepageLocationPolicyEnabled()) {
+            RecordHistogram.recordBooleanHistogram(
+                    "Settings.ShowHomeButtonPreferenceState", HomepageManager.isHomepageEnabled());
+            return;
+        }
+
+        int state = HomeButtonPreferenceState.USER_DISABLED;
+        if (HomepagePolicyManager.isHomepageManagedByPolicy()) {
+            state = HomeButtonPreferenceState.MANAGED_ENABLED;
+        } else if (isHomepageEnabled()) {
+            state = HomeButtonPreferenceState.USER_ENABLED;
+        }
+
+        RecordHistogram.recordEnumeratedHistogram("Settings.ShowHomeButtonPreferenceStateManaged",
+                state, HomeButtonPreferenceState.NUM_ENTRIES);
+    }
+
+    public static void recordHomepageIsCustomized(boolean isCustomized) {
+        RecordHistogram.recordBooleanHistogram("Settings.HomePageIsCustomized", isCustomized);
+    }
+
     @Override
     public void onHomepagePolicyUpdate() {
         notifyHomepageUpdated();
+
+        boolean isPolicyEnabled = HomepagePolicyManager.isHomepageManagedByPolicy();
+        if (isPolicyEnabled) {
+            recordHomepageIsCustomized(false);
+        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/MainPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/MainPreferences.java
index 6b2702c7..07f1050 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/MainPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/MainPreferences.java
@@ -29,7 +29,7 @@
 import org.chromium.chrome.browser.settings.datareduction.DataReductionPreferenceFragment;
 import org.chromium.chrome.browser.settings.developer.DeveloperSettings;
 import org.chromium.chrome.browser.settings.sync.SignInPreference;
-import org.chromium.chrome.browser.settings.sync.SyncPreferenceUtils;
+import org.chromium.chrome.browser.settings.sync.SyncSettingsUtils;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.SigninManager;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
@@ -227,8 +227,8 @@
     private void updateSyncAndServicesPreference() {
         ChromeBasePreference syncAndServices =
                 (ChromeBasePreference) findPreference(PREF_SYNC_AND_SERVICES);
-        syncAndServices.setIcon(SyncPreferenceUtils.getSyncStatusIcon(getActivity()));
-        syncAndServices.setSummary(SyncPreferenceUtils.getSyncStatusSummary(getActivity()));
+        syncAndServices.setIcon(SyncSettingsUtils.getSyncStatusIcon(getActivity()));
+        syncAndServices.setSummary(SyncSettingsUtils.getSyncStatusSummary(getActivity()));
     }
 
     private void updateSearchEnginePreference() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageSettings.java
index 50b92e2b..c42b35f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageSettings.java
@@ -70,9 +70,7 @@
     private void updateCurrentHomepageUrl() {
         if (HomepagePolicyManager.isHomepageManagedByPolicy()) mHomepageEdit.setEnabled(false);
 
-        mHomepageEdit.setSummary(mHomepageManager.getPrefHomepageUseDefaultUri()
-                        ? HomepageManager.getDefaultHomepageUri()
-                        : mHomepageManager.getPrefHomepageCustomUri());
+        mHomepageEdit.setSummary(HomepageManager.getHomepageUri());
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/PrivacySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/PrivacySettings.java
index bdc26ab..ad31ab2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/PrivacySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/PrivacySettings.java
@@ -23,7 +23,7 @@
 import org.chromium.chrome.browser.settings.ManagedPreferenceDelegate;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
 import org.chromium.chrome.browser.settings.SettingsUtils;
-import org.chromium.chrome.browser.settings.sync.SyncAndServicesPreferences;
+import org.chromium.chrome.browser.settings.sync.SyncAndServicesSettings;
 import org.chromium.chrome.browser.usage_stats.UsageStatsConsentDialog;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
@@ -65,8 +65,7 @@
         Preference syncAndServicesLink = findPreference(PREF_SYNC_AND_SERVICES_LINK);
         NoUnderlineClickableSpan linkSpan = new NoUnderlineClickableSpan(getResources(), view -> {
             SettingsLauncher.getInstance().launchSettingsPage(getActivity(),
-                    SyncAndServicesPreferences.class,
-                    SyncAndServicesPreferences.createArguments(false));
+                    SyncAndServicesSettings.class, SyncAndServicesSettings.createArguments(false));
         });
         syncAndServicesLink.setSummary(
                 SpanApplier.applySpans(getString(R.string.privacy_sync_and_services_link),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/ManageSyncPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/ManageSyncSettings.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/sync/ManageSyncPreferences.java
rename to chrome/android/java/src/org/chromium/chrome/browser/settings/sync/ManageSyncSettings.java
index a5a6044..e6c459d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/ManageSyncPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/ManageSyncSettings.java
@@ -49,9 +49,9 @@
 
 /**
  * Settings fragment to customize Sync options (data types, encryption). Can be accessed from
- * {@link SyncAndServicesPreferences}.
+ * {@link SyncAndServicesSettings}.
  */
-public class ManageSyncPreferences extends PreferenceFragmentCompat
+public class ManageSyncSettings extends PreferenceFragmentCompat
         implements PassphraseDialogFragment.Listener, PassphraseCreationDialogFragment.Listener,
                    PassphraseTypeDialogFragment.Listener, Preference.OnPreferenceChangeListener,
                    ProfileSyncService.SyncStateChangedListener {
@@ -126,10 +126,10 @@
         mGoogleActivityControls = findPreference(PREF_GOOGLE_ACTIVITY_CONTROLS);
         mSyncEncryption = findPreference(PREF_ENCRYPTION);
         mSyncEncryption.setOnPreferenceClickListener(
-                SyncPreferenceUtils.toOnClickListener(this, this::onSyncEncryptionClicked));
+                SyncSettingsUtils.toOnClickListener(this, this::onSyncEncryptionClicked));
         mManageSyncData = findPreference(PREF_SYNC_MANAGE_DATA);
-        mManageSyncData.setOnPreferenceClickListener(SyncPreferenceUtils.toOnClickListener(
-                this, () -> SyncPreferenceUtils.openSyncDashboard(getActivity())));
+        mManageSyncData.setOnPreferenceClickListener(SyncSettingsUtils.toOnClickListener(
+                this, () -> SyncSettingsUtils.openSyncDashboard(getActivity())));
 
         mSyncTypePreferences =
                 new CheckBoxPreference[] {mSyncAutofill, mSyncBookmarks, mSyncPaymentsIntegration,
@@ -225,7 +225,7 @@
             return;
         }
 
-        mGoogleActivityControls.setOnPreferenceClickListener(SyncPreferenceUtils.toOnClickListener(
+        mGoogleActivityControls.setOnPreferenceClickListener(SyncSettingsUtils.toOnClickListener(
                 this, () -> onGoogleActivityControlsClicked(signedInAccountName)));
 
         updateDataTypeState();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SignInPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SignInPreference.java
index 995ff28..5aa23a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SignInPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SignInPreference.java
@@ -119,8 +119,8 @@
     }
 
     /**
-     * Should be called when the {@link PreferenceFragment} which used {@link SignInPreference} gets
-     * destroyed. Used to record "ImpressionsTilDismiss" histogram.
+     * Should be called when the {@link PreferenceFragmentCompat} which used {@link
+     * SignInPreference} gets destroyed. Used to record "ImpressionsTilDismiss" histogram.
      */
     public void onPreferenceFragmentDestroyed() {
         if (mSigninPromoController != null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncAndServicesPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncAndServicesSettings.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncAndServicesPreferences.java
rename to chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncAndServicesSettings.java
index 5e37855b..7eab3f1b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncAndServicesPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncAndServicesSettings.java
@@ -54,7 +54,7 @@
 import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.settings.password.PasswordUIView;
 import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
-import org.chromium.chrome.browser.settings.sync.SyncPreferenceUtils.SyncError;
+import org.chromium.chrome.browser.settings.sync.SyncSettingsUtils.SyncError;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.UnifiedConsentServiceBridge;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
@@ -74,7 +74,7 @@
 /**
  * Settings fragment to enable Sync and other services that communicate with Google.
  */
-public class SyncAndServicesPreferences extends PreferenceFragmentCompat
+public class SyncAndServicesSettings extends PreferenceFragmentCompat
         implements PassphraseDialogFragment.Listener, Preference.OnPreferenceChangeListener,
                    ProfileSyncService.SyncStateChangedListener,
                    SettingsActivity.OnBackPressedListener {
@@ -178,15 +178,15 @@
         mSigninPreference = (SignInPreference) findPreference(PREF_SIGNIN);
         mSigninPreference.setPersonalizedPromoEnabled(false);
         mManageYourGoogleAccount = findPreference(PREF_MANAGE_YOUR_GOOGLE_ACCOUNT);
-        mManageYourGoogleAccount.setOnPreferenceClickListener(SyncPreferenceUtils.toOnClickListener(
-                this, () -> SyncPreferenceUtils.openGoogleMyAccount(getActivity())));
+        mManageYourGoogleAccount.setOnPreferenceClickListener(SyncSettingsUtils.toOnClickListener(
+                this, () -> SyncSettingsUtils.openGoogleMyAccount(getActivity())));
 
         mSyncCategory = (PreferenceCategory) findPreference(PREF_SYNC_CATEGORY);
         mSyncErrorCard = findPreference(PREF_SYNC_ERROR_CARD);
         mSyncErrorCard.setIcon(UiUtils.getTintedDrawable(
                 getActivity(), R.drawable.ic_sync_error_40dp, R.color.default_red));
         mSyncErrorCard.setOnPreferenceClickListener(
-                SyncPreferenceUtils.toOnClickListener(this, this::onSyncErrorCardClicked));
+                SyncSettingsUtils.toOnClickListener(this, this::onSyncErrorCardClicked));
         mSyncDisabledByAdministrator = findPreference(PREF_SYNC_DISABLED_BY_ADMINISTRATOR);
         mSyncDisabledByAdministrator.setIcon(
                 ManagedPreferencesUtils.getManagedByEnterpriseIconId());
@@ -257,7 +257,7 @@
             // without turning sync on, then mark first setup as complete (so that we won't show the
             // error again), but turn sync off.
             assert !mSyncRequested.isChecked();
-            SyncPreferenceUtils.enableSync(false);
+            SyncSettingsUtils.enableSync(false);
             mProfileSyncService.setFirstSetupComplete(
                     SyncFirstSetupCompleteSource.ADVANCED_FLOW_INTERRUPTED_LEAVE_SYNC_OFF);
         }
@@ -346,7 +346,7 @@
         String key = preference.getKey();
         if (PREF_SYNC_REQUESTED.equals(key)) {
             assert canDisableSync();
-            SyncPreferenceUtils.enableSync((boolean) newValue);
+            SyncSettingsUtils.enableSync((boolean) newValue);
             if (wasSigninFlowInterrupted()) {
                 // This flow should only be reached when user toggles sync on.
                 assert (boolean) newValue;
@@ -446,7 +446,7 @@
     @SyncError
     private int getSyncError() {
         @SyncError
-        int error = SyncPreferenceUtils.getSyncError();
+        int error = SyncSettingsUtils.getSyncError();
         if (error == SyncError.SYNC_SETUP_INCOMPLETE && mIsFromSigninScreen) {
             return SyncError.NO_ERROR;
         }
@@ -592,7 +592,7 @@
         } else {
             mSyncErrorCard.setTitle(getSyncErrorTitle(mCurrentSyncError));
             mSyncErrorCard.setSummary(
-                    SyncPreferenceUtils.getSyncErrorHint(getActivity(), mCurrentSyncError));
+                    SyncSettingsUtils.getSyncErrorHint(getActivity(), mCurrentSyncError));
             mSyncCategory.addPreference(mSyncErrorCard);
         }
 
@@ -692,7 +692,7 @@
 
     /**
      * The dialog that offers the user to cancel sync. Only shown when
-     * {@link SyncAndServicesPreferences} is opened from the sign-in screen. Shown when the user
+     * {@link SyncAndServicesSettings} is opened from the sign-in screen. Shown when the user
      * tries to close the settings page without confirming settings.
      */
     public static class CancelSyncDialog extends DialogFragment {
@@ -718,7 +718,7 @@
 
         public void onCancelSyncPressed() {
             RecordUserAction.record("Signin_Signin_ConfirmCancelAdvancedSyncSettings");
-            SyncAndServicesPreferences fragment = (SyncAndServicesPreferences) getTargetFragment();
+            SyncAndServicesSettings fragment = (SyncAndServicesSettings) getTargetFragment();
             fragment.cancelSync();
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncPreferenceUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncSettingsUtils.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncPreferenceUtils.java
rename to chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncSettingsUtils.java
index 97efc0dd..a64b53c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncPreferenceUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/sync/SyncSettingsUtils.java
@@ -37,9 +37,9 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Helper methods for sync preferences.
+ * Helper methods for sync settings.
  */
-public class SyncPreferenceUtils {
+public class SyncSettingsUtils {
     private static final String DASHBOARD_URL = "https://www.google.com/settings/chrome/sync";
     private static final String MY_ACCOUNT_URL = "https://myaccount.google.com/smartlink/home";
 
@@ -251,8 +251,8 @@
 
     /**
      * Creates a wrapper around {@link Runnable} that calls the runnable only if
-     * {@link PreferenceFragment} is still in resumed state. Click events that arrive after the
-     * fragment has been paused will be ignored. See http://b/5983282.
+     * {@link PreferenceFragmentCompat} is still in resumed state. Click events that arrive after
+     * the fragment has been paused will be ignored. See http://b/5983282.
      * @param fragment The fragment that hosts the preference.
      * @param runnable The runnable to call from {@link Preference.OnPreferenceClickListener}.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/ContentSettingsResources.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/ContentSettingsResources.java
index ed6855e..d29d9922 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/ContentSettingsResources.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/ContentSettingsResources.java
@@ -102,6 +102,12 @@
                             R.string.ads_permission_title, ContentSettingValues.ALLOW,
                             ContentSettingValues.BLOCK, 0,
                             R.string.website_settings_category_ads_blocked));
+            // TODO(crbug.com/1041009) Finalize WebXr Permissions Icons.
+            localMap.put(ContentSettingsType.AR,
+                    new ResourceItem(R.drawable.vr_headset, R.string.ar_permission_title,
+                            R.string.ar_permission_title, ContentSettingValues.ASK,
+                            ContentSettingValues.BLOCK, R.string.website_settings_category_ar_ask,
+                            R.string.website_settings_category_ar_blocked));
             localMap.put(ContentSettingsType.AUTOMATIC_DOWNLOADS,
                     new ResourceItem(R.drawable.infobar_downloading,
                             R.string.automatic_downloads_permission_title,
@@ -214,6 +220,12 @@
                             R.string.website_settings_usb, ContentSettingValues.ASK,
                             ContentSettingValues.BLOCK, R.string.website_settings_category_usb_ask,
                             R.string.website_settings_category_usb_blocked));
+            // TODO(crbug.com/1041009) Finalize WebXr Permissions Icons/Strings.
+            localMap.put(ContentSettingsType.VR,
+                    new ResourceItem(R.drawable.vr_headset, R.string.vr_permission_title,
+                            R.string.vr_permission_title, ContentSettingValues.ASK,
+                            ContentSettingValues.BLOCK, R.string.website_settings_category_vr_ask,
+                            R.string.website_settings_category_vr_blocked));
             sResourceInfo = localMap;
         }
         return sResourceInfo;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/PermissionInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/PermissionInfo.java
index 9b59b32..4a6e8d57 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/PermissionInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/PermissionInfo.java
@@ -17,26 +17,29 @@
  * Permission information for a given origin.
  */
 public class PermissionInfo implements Serializable {
-    @IntDef({Type.CAMERA, Type.CLIPBOARD, Type.GEOLOCATION, Type.MICROPHONE, Type.MIDI,
-            Type.NOTIFICATION, Type.PROTECTED_MEDIA_IDENTIFIER, Type.SENSORS})
+    @IntDef({Type.AUGMENTED_REALITY, Type.CAMERA, Type.CLIPBOARD, Type.GEOLOCATION, Type.MICROPHONE,
+            Type.MIDI, Type.NOTIFICATION, Type.PROTECTED_MEDIA_IDENTIFIER, Type.SENSORS,
+            Type.VIRTUAL_REALITY})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Type {
         // Values used to address index - should be enumerated from 0 and can't have gaps.
         // All updates here must also be reflected in {@link #getContentSettingsType(int)
         // getContentSettingsType} and {@link SingleWebsiteSettings.PERMISSION_PREFERENCE_KEYS}.
-        int CAMERA = 0;
-        int CLIPBOARD = 1;
-        int GEOLOCATION = 2;
-        int MICROPHONE = 3;
-        int MIDI = 4;
-        int NFC = 5;
-        int NOTIFICATION = 6;
-        int PROTECTED_MEDIA_IDENTIFIER = 7;
-        int SENSORS = 8;
+        int AUGMENTED_REALITY = 0;
+        int CAMERA = 1;
+        int CLIPBOARD = 2;
+        int GEOLOCATION = 3;
+        int MICROPHONE = 4;
+        int MIDI = 5;
+        int NFC = 6;
+        int NOTIFICATION = 7;
+        int PROTECTED_MEDIA_IDENTIFIER = 8;
+        int SENSORS = 9;
+        int VIRTUAL_REALITY = 10;
         /**
          * Number of handled permissions used for example inside for loops.
          */
-        int NUM_ENTRIES = 9;
+        int NUM_ENTRIES = 11;
     }
 
     private final boolean mIsIncognito;
@@ -76,6 +79,9 @@
      */
     public @ContentSettingValues @Nullable Integer getContentSetting() {
         switch (mType) {
+            case Type.AUGMENTED_REALITY:
+                return WebsitePreferenceBridgeJni.get().getArSettingForOrigin(
+                        mOrigin, getEmbedderSafe(), mIsIncognito);
             case Type.CAMERA:
                 return WebsitePreferenceBridgeJni.get().getCameraSettingForOrigin(
                         mOrigin, getEmbedderSafe(), mIsIncognito);
@@ -103,6 +109,9 @@
             case Type.SENSORS:
                 return WebsitePreferenceBridgeJni.get().getSensorsSettingForOrigin(
                         mOrigin, getEmbedderSafe(), mIsIncognito);
+            case Type.VIRTUAL_REALITY:
+                return WebsitePreferenceBridgeJni.get().getVrSettingForOrigin(
+                        mOrigin, getEmbedderSafe(), mIsIncognito);
             default:
                 assert false;
                 return null;
@@ -114,6 +123,10 @@
      */
     public void setContentSetting(@ContentSettingValues int value) {
         switch (mType) {
+            case Type.AUGMENTED_REALITY:
+                WebsitePreferenceBridgeJni.get().setArSettingForOrigin(
+                        mOrigin, getEmbedderSafe(), value, mIsIncognito);
+                break;
             case Type.CAMERA:
                 WebsitePreferenceBridgeJni.get().setCameraSettingForOrigin(
                         mOrigin, value, mIsIncognito);
@@ -150,6 +163,10 @@
                 WebsitePreferenceBridgeJni.get().setSensorsSettingForOrigin(
                         mOrigin, getEmbedderSafe(), value, mIsIncognito);
                 break;
+            case Type.VIRTUAL_REALITY:
+                WebsitePreferenceBridgeJni.get().setVrSettingForOrigin(
+                        mOrigin, getEmbedderSafe(), value, mIsIncognito);
+                break;
             default:
                 assert false;
         }
@@ -157,6 +174,8 @@
 
     public static @ContentSettingsType int getContentSettingsType(@Type int type) {
         switch (type) {
+            case Type.AUGMENTED_REALITY:
+                return ContentSettingsType.AR;
             case Type.CAMERA:
                 return ContentSettingsType.MEDIASTREAM_CAMERA;
             case Type.CLIPBOARD:
@@ -175,6 +194,8 @@
                 return ContentSettingsType.PROTECTED_MEDIA_IDENTIFIER;
             case Type.SENSORS:
                 return ContentSettingsType.SENSORS;
+            case Type.VIRTUAL_REALITY:
+                return ContentSettingsType.VR;
             default:
                 assert false;
                 return ContentSettingsType.DEFAULT;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SingleWebsiteSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SingleWebsiteSettings.java
index 36e3166e..63e57af3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SingleWebsiteSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SingleWebsiteSettings.java
@@ -77,6 +77,7 @@
     private static final String[] PERMISSION_PREFERENCE_KEYS = {
             // Permission keys mapped for next {@link ContentSettingException.Type} values.
             "ads_permission_list", // ContentSettingException.Type.ADS
+            "ar_permission_list", // PermissionInfo.Type.AUGMENTED_REALITY
             "automatic_downloads_permission_list",
             // ContentSettingException.Type.AUTOMATIC_DOWNLOADS
             "background_sync_permission_list", // ContentSettingException.Type.BACKGROUND_SYNC
@@ -96,6 +97,7 @@
             "protected_media_identifier_permission_list",
             // PermissionInfo.Type.PROTECTED_MEDIA_IDENTIFIER
             "sensors_permission_list", // PermissionInfo.Type.SENSORS
+            "vr_permission_list", // PermissionInfo.Type.VIRTUAL_REALITY
     };
 
     private static final int REQUEST_CODE_NOTIFICATION_CHANNEL_SETTINGS = 1;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettings.java
index 387e207f..027d5e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettings.java
@@ -63,6 +63,10 @@
         if (!ContentFeatureList.isEnabled(ContentFeatureList.WEB_NFC)) {
             getPreferenceScreen().removePreference(findPreference(Type.NFC));
         }
+        if (!ContentFeatureList.isEnabled(ContentFeatureList.WEBXR_PERMISSIONS_API)) {
+            getPreferenceScreen().removePreference(findPreference(Type.AUGMENTED_REALITY));
+            getPreferenceScreen().removePreference(findPreference(Type.VIRTUAL_REALITY));
+        }
     }
 
     private void updatePreferenceStates() {
@@ -71,6 +75,9 @@
         if (SiteSettingsCategory.adsCategoryEnabled()) {
             websitePrefs.add(Type.ADS);
         }
+        if (ContentFeatureList.isEnabled(ContentFeatureList.WEBXR_PERMISSIONS_API)) {
+            websitePrefs.add(Type.AUGMENTED_REALITY);
+        }
         websitePrefs.add(Type.AUTOMATIC_DOWNLOADS);
         websitePrefs.add(Type.BACKGROUND_SYNC);
         CommandLine commandLine = CommandLine.getInstance();
@@ -92,6 +99,9 @@
         websitePrefs.add(Type.PROTECTED_MEDIA);
         websitePrefs.add(Type.SOUND);
         websitePrefs.add(Type.USB);
+        if (ContentFeatureList.isEnabled(ContentFeatureList.WEBXR_PERMISSIONS_API)) {
+            websitePrefs.add(Type.VIRTUAL_REALITY);
+        }
 
         // Initialize the summary and icon for all preferences that have an
         // associated content settings entry.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettingsCategory.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettingsCategory.java
index fffef69..da0e67e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettingsCategory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettingsCategory.java
@@ -37,11 +37,11 @@
  * A base class for dealing with website settings categories.
  */
 public class SiteSettingsCategory {
-    @IntDef({Type.ALL_SITES, Type.ADS, Type.AUTOMATIC_DOWNLOADS, Type.BACKGROUND_SYNC,
-            Type.BLUETOOTH_SCANNING, Type.CAMERA, Type.CLIPBOARD, Type.COOKIES,
-            Type.DEVICE_LOCATION, Type.JAVASCRIPT, Type.MICROPHONE, Type.NFC, Type.NOTIFICATIONS,
-            Type.POPUPS, Type.PROTECTED_MEDIA, Type.SENSORS, Type.SOUND, Type.USB,
-            Type.USE_STORAGE})
+    @IntDef({Type.ALL_SITES, Type.ADS, Type.AUGMENTED_REALITY, Type.AUTOMATIC_DOWNLOADS,
+            Type.BACKGROUND_SYNC, Type.BLUETOOTH_SCANNING, Type.CAMERA, Type.CLIPBOARD,
+            Type.COOKIES, Type.DEVICE_LOCATION, Type.JAVASCRIPT, Type.MICROPHONE, Type.NFC,
+            Type.NOTIFICATIONS, Type.POPUPS, Type.PROTECTED_MEDIA, Type.SENSORS, Type.SOUND,
+            Type.USB, Type.VIRTUAL_REALITY, Type.USE_STORAGE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Type {
         // Values used to address array index - should be enumerated from 0 and can't have gaps.
@@ -49,27 +49,29 @@
         // preferenceKey} and {@link #contentSettingsType(int) contentSettingsType}.
         int ALL_SITES = 0; // Always first as it should appear in the UI at the top.
         int ADS = 1;
-        int AUTOMATIC_DOWNLOADS = 2;
-        int BACKGROUND_SYNC = 3;
-        int BLUETOOTH_SCANNING = 4;
-        int CAMERA = 5;
-        int CLIPBOARD = 6;
-        int COOKIES = 7;
-        int DEVICE_LOCATION = 8;
-        int JAVASCRIPT = 9;
-        int MICROPHONE = 10;
-        int NFC = 11;
-        int NOTIFICATIONS = 12;
-        int POPUPS = 13;
-        int PROTECTED_MEDIA = 14;
-        int SENSORS = 15;
-        int SOUND = 16;
-        int USB = 17;
-        int USE_STORAGE = 18; // Always last as it should appear in the UI at the bottom.
+        int AUGMENTED_REALITY = 2;
+        int AUTOMATIC_DOWNLOADS = 3;
+        int BACKGROUND_SYNC = 4;
+        int BLUETOOTH_SCANNING = 5;
+        int CAMERA = 6;
+        int CLIPBOARD = 7;
+        int COOKIES = 8;
+        int DEVICE_LOCATION = 9;
+        int JAVASCRIPT = 10;
+        int MICROPHONE = 11;
+        int NFC = 12;
+        int NOTIFICATIONS = 13;
+        int POPUPS = 14;
+        int PROTECTED_MEDIA = 15;
+        int SENSORS = 16;
+        int SOUND = 17;
+        int USB = 18;
+        int VIRTUAL_REALITY = 19;
+        int USE_STORAGE = 20; // Always last as it should appear in the UI at the bottom.
         /**
          * Number of handled categories used for calculating array sizes.
          */
-        int NUM_ENTRIES = 19;
+        int NUM_ENTRIES = 21;
     }
 
     // The id of this category.
@@ -134,6 +136,8 @@
         switch (type) {
             case Type.ADS:
                 return ContentSettingsType.ADS;
+            case Type.AUGMENTED_REALITY:
+                return ContentSettingsType.AR;
             case Type.AUTOMATIC_DOWNLOADS:
                 return ContentSettingsType.AUTOMATIC_DOWNLOADS;
             case Type.BACKGROUND_SYNC:
@@ -166,6 +170,8 @@
                 return ContentSettingsType.SOUND;
             case Type.USB:
                 return ContentSettingsType.USB_GUARD;
+            case Type.VIRTUAL_REALITY:
+                return ContentSettingsType.VR;
             // case Type.ALL_SITES
             // case Type.USE_STORAGE
             default:
@@ -193,6 +199,8 @@
         switch (type) {
             case Type.ADS:
                 return "ads";
+            case Type.AUGMENTED_REALITY:
+                return "augmented_reality";
             case Type.ALL_SITES:
                 return "all_sites";
             case Type.AUTOMATIC_DOWNLOADS:
@@ -229,6 +237,8 @@
                 return "usb";
             case Type.USE_STORAGE:
                 return "use_storage";
+            case Type.VIRTUAL_REALITY:
+                return "virtual_reality";
             default:
                 assert false;
                 return "";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/WebsitePermissionsFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/WebsitePermissionsFetcher.java
index c32650d..c711cead 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/WebsitePermissionsFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/WebsitePermissionsFetcher.java
@@ -136,6 +136,12 @@
             // NFC permission is per-origin and per-embedder.
             queue.add(new PermissionInfoFetcher(PermissionInfo.Type.NFC));
         }
+        if (ContentFeatureList.isEnabled(ContentFeatureList.WEBXR_PERMISSIONS_API)) {
+            // VIRTUAL_REALITY permission is per-origin and per-embedder.
+            queue.add(new PermissionInfoFetcher(PermissionInfo.Type.VIRTUAL_REALITY));
+            // AR permission is per-origin and per-embedder.
+            queue.add(new PermissionInfoFetcher(PermissionInfo.Type.AUGMENTED_REALITY));
+        }
 
         queue.add(new PermissionsAvailableCallbackRunner(callback));
 
@@ -222,6 +228,16 @@
                 // NFC permission is per-origin and per-embedder.
                 queue.add(new PermissionInfoFetcher(PermissionInfo.Type.NFC));
             }
+        } else if (category.showSites(SiteSettingsCategory.Type.VIRTUAL_REALITY)) {
+            if (ContentFeatureList.isEnabled(ContentFeatureList.WEBXR_PERMISSIONS_API)) {
+                // VIRTUAL_REALITY permission is per-origin and per-embedder.
+                queue.add(new PermissionInfoFetcher(PermissionInfo.Type.VIRTUAL_REALITY));
+            }
+        } else if (category.showSites(SiteSettingsCategory.Type.AUGMENTED_REALITY)) {
+            if (ContentFeatureList.isEnabled(ContentFeatureList.WEBXR_PERMISSIONS_API)) {
+                // AUGMENTED_REALITY permission is per-origin and per-embedder.
+                queue.add(new PermissionInfoFetcher(PermissionInfo.Type.AUGMENTED_REALITY));
+            }
         }
         queue.add(new PermissionsAvailableCallbackRunner(callback));
         queue.next();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/WebsitePreferenceBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/WebsitePreferenceBridge.java
index eea0113d..ec857e54 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/WebsitePreferenceBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/WebsitePreferenceBridge.java
@@ -52,7 +52,9 @@
         ArrayList<PermissionInfo> list = new ArrayList<PermissionInfo>();
         // Camera, Location & Microphone can be managed by the custodian
         // of a supervised account or by enterprise policy.
-        if (type == PermissionInfo.Type.CAMERA) {
+        if (type == PermissionInfo.Type.AUGMENTED_REALITY) {
+            WebsitePreferenceBridgeJni.get().getArOrigins(list);
+        } else if (type == PermissionInfo.Type.CAMERA) {
             boolean managedOnly = !isCameraUserModifiable();
             WebsitePreferenceBridgeJni.get().getCameraOrigins(list, managedOnly);
         } else if (type == PermissionInfo.Type.CLIPBOARD) {
@@ -73,6 +75,8 @@
             WebsitePreferenceBridgeJni.get().getProtectedMediaIdentifierOrigins(list);
         } else if (type == PermissionInfo.Type.SENSORS) {
             WebsitePreferenceBridgeJni.get().getSensorsOrigins(list);
+        } else if (type == PermissionInfo.Type.VIRTUAL_REALITY) {
+            WebsitePreferenceBridgeJni.get().getVrOrigins(list);
         } else {
             assert false;
         }
@@ -92,6 +96,12 @@
     }
 
     @CalledByNative
+    private static void insertArInfoIntoList(
+            ArrayList<PermissionInfo> list, String origin, String embedder) {
+        insertInfoIntoList(PermissionInfo.Type.AUGMENTED_REALITY, list, origin, embedder);
+    }
+
+    @CalledByNative
     private static void insertCameraInfoIntoList(
             ArrayList<PermissionInfo> list, String origin, String embedder) {
         insertInfoIntoList(PermissionInfo.Type.CAMERA, list, origin, embedder);
@@ -152,6 +162,12 @@
     }
 
     @CalledByNative
+    private static void insertVrInfoIntoList(
+            ArrayList<PermissionInfo> list, String origin, String embedder) {
+        insertInfoIntoList(PermissionInfo.Type.VIRTUAL_REALITY, list, origin, embedder);
+    }
+
+    @CalledByNative
     private static Object createStorageInfoList() {
         return new ArrayList<StorageInfo>();
     }
@@ -348,6 +364,9 @@
             case ContentSettingsType.USB_GUARD:
                 setContentSettingEnabled(contentSettingsType, allow);
                 break;
+            case ContentSettingsType.AR:
+                WebsitePreferenceBridgeJni.get().setArEnabled(allow);
+                break;
             case ContentSettingsType.AUTOMATIC_DOWNLOADS:
                 WebsitePreferenceBridgeJni.get().setAutomaticDownloadsEnabled(allow);
                 break;
@@ -381,6 +400,9 @@
             case ContentSettingsType.SOUND:
                 WebsitePreferenceBridgeJni.get().setSoundEnabled(allow);
                 break;
+            case ContentSettingsType.VR:
+                WebsitePreferenceBridgeJni.get().setVrEnabled(allow);
+                break;
             default:
                 assert false;
         }
@@ -400,6 +422,8 @@
             case ContentSettingsType.USB_GUARD:
             case ContentSettingsType.BLUETOOTH_SCANNING:
                 return isContentSettingEnabled(contentSettingsType);
+            case ContentSettingsType.AR:
+                return WebsitePreferenceBridgeJni.get().getArEnabled();
             case ContentSettingsType.AUTOMATIC_DOWNLOADS:
                 return WebsitePreferenceBridgeJni.get().getAutomaticDownloadsEnabled();
             case ContentSettingsType.BACKGROUND_SYNC:
@@ -418,6 +442,8 @@
                 return WebsitePreferenceBridgeJni.get().getSensorsEnabled();
             case ContentSettingsType.SOUND:
                 return WebsitePreferenceBridgeJni.get().getSoundEnabled();
+            case ContentSettingsType.VR:
+                return WebsitePreferenceBridgeJni.get().getVrEnabled();
             default:
                 assert false;
                 return false;
@@ -554,6 +580,7 @@
     @VisibleForTesting
     @NativeMethods
     public interface Natives {
+        void getArOrigins(Object list);
         void getCameraOrigins(Object list, boolean managedOnly);
         void getClipboardOrigins(Object list);
         void getGeolocationOrigins(Object list, boolean managedOnly);
@@ -564,6 +591,8 @@
         void getProtectedMediaIdentifierOrigins(Object list);
         boolean getNfcEnabled();
         void getSensorsOrigins(Object list);
+        void getVrOrigins(Object list);
+        int getArSettingForOrigin(String origin, String embedder, boolean isIncognito);
         int getCameraSettingForOrigin(String origin, String embedder, boolean isIncognito);
         int getClipboardSettingForOrigin(String origin, boolean isIncognito);
         int getGeolocationSettingForOrigin(String origin, String embedder, boolean isIncognito);
@@ -575,6 +604,8 @@
         int getProtectedMediaIdentifierSettingForOrigin(
                 String origin, String embedder, boolean isIncognito);
         int getSensorsSettingForOrigin(String origin, String embedder, boolean isIncognito);
+        int getVrSettingForOrigin(String origin, String embedder, boolean isIncognito);
+        void setArSettingForOrigin(String origin, String embedder, int value, boolean isIncognito);
         void setCameraSettingForOrigin(String origin, int value, boolean isIncognito);
         void setClipboardSettingForOrigin(String origin, int value, boolean isIncognito);
         void setGeolocationSettingForOrigin(
@@ -590,6 +621,7 @@
                 String origin, String embedder, int value, boolean isIncognito);
         void setSensorsSettingForOrigin(
                 String origin, String embedder, int value, boolean isIncognito);
+        void setVrSettingForOrigin(String origin, String embedder, int value, boolean isIncognito);
         void clearBannerData(String origin);
         void clearMediaLicenses(String origin);
         void clearCookieData(String path);
@@ -618,6 +650,7 @@
         boolean getAcceptCookiesEnabled();
         boolean getAcceptCookiesUserModifiable();
         boolean getAcceptCookiesManagedByCustodian();
+        boolean getArEnabled();
         boolean getAutomaticDownloadsEnabled();
         boolean getBackgroundSyncEnabled();
         boolean getAllowLocationUserModifiable();
@@ -633,8 +666,10 @@
         boolean getMicManagedByCustodian();
         boolean getSensorsEnabled();
         boolean getSoundEnabled();
+        boolean getVrEnabled();
         void setAutomaticDownloadsEnabled(boolean enabled);
         void setAllowCookiesEnabled(boolean enabled);
+        void setArEnabled(boolean enabled);
         void setBackgroundSyncEnabled(boolean enabled);
         void setClipboardEnabled(boolean enabled);
         boolean getAllowLocationEnabled();
@@ -644,6 +679,7 @@
         void setNfcEnabled(boolean enabled);
         void setSensorsEnabled(boolean enabled);
         void setSoundEnabled(boolean enabled);
+        void setVrEnabled(boolean enabled);
         boolean getQuietNotificationsUiEnabled(Profile profile);
         void setQuietNotificationsUiEnabled(Profile profile, boolean enabled);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
index c2593e6e..f3ea148 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
@@ -16,7 +16,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.SyncFirstSetupCompleteSource;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.sync.SyncAndServicesPreferences;
+import org.chromium.chrome.browser.settings.sync.SyncAndServicesSettings;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
@@ -143,8 +143,8 @@
                                 true);
                         if (settingsClicked) {
                             SettingsLauncher.getInstance().launchSettingsPage(getActivity(),
-                                    SyncAndServicesPreferences.class,
-                                    SyncAndServicesPreferences.createArguments(true));
+                                    SyncAndServicesSettings.class,
+                                    SyncAndServicesSettings.createArguments(true));
                         } else {
                             ProfileSyncService.get().setFirstSetupComplete(
                                     SyncFirstSetupCompleteSource.BASIC_FLOW);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SyncPromoView.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SyncPromoView.java
index cb92288..588faf6a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SyncPromoView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SyncPromoView.java
@@ -18,7 +18,7 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.sync.SyncAndServicesPreferences;
+import org.chromium.chrome.browser.settings.sync.SyncAndServicesSettings;
 import org.chromium.chrome.browser.signin.SigninActivity.AccessPoint;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
@@ -177,8 +177,8 @@
         ButtonState positiveButton = new ButtonPresent(R.string.enable_sync_button,
                 view
                 -> SettingsLauncher.getInstance().launchSettingsPage(getContext(),
-                        SyncAndServicesPreferences.class,
-                        SyncAndServicesPreferences.createArguments(false)));
+                        SyncAndServicesSettings.class,
+                        SyncAndServicesSettings.createArguments(false)));
 
         return new ViewState(descId, positiveButton);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
index 8aa60c73..0d425229 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
@@ -25,7 +25,7 @@
 import org.chromium.chrome.browser.notifications.PendingIntentProvider;
 import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.sync.SyncAndServicesPreferences;
+import org.chromium.chrome.browser.settings.sync.SyncAndServicesSettings;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.sync.GoogleServiceAuthError.State;
 import org.chromium.chrome.browser.sync.ui.PassphraseActivity;
@@ -197,8 +197,8 @@
      */
     private Intent createSettingsIntent() {
         return SettingsLauncher.getInstance().createIntentForSettingsPage(
-                ContextUtils.getApplicationContext(), SyncAndServicesPreferences.class.getName(),
-                SyncAndServicesPreferences.createArguments(false));
+                ContextUtils.getApplicationContext(), SyncAndServicesSettings.class.getName(),
+                SyncAndServicesSettings.createArguments(false));
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
index 548dae9..eb91161 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
@@ -97,6 +97,11 @@
     }
 
     @Override
+    public void onSaveImageToClipboard(Uri uri) {
+        Clipboard.getInstance().setImage(uri);
+    }
+
+    @Override
     public boolean supportsCall() {
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setData(Uri.parse("tel:"));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabReparentingParams.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabReparentingParams.java
index 821b9e49..16879aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabReparentingParams.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabReparentingParams.java
@@ -25,6 +25,7 @@
 
     private int mTabIndex = TAB_INDEX_NOT_SET;
     private boolean mIsFromNightModeReparenting;
+    private boolean mIsForegroundTab;
 
     /**
      * Basic constructor for {@link TabReparentingParams}.
@@ -108,4 +109,14 @@
     public boolean isFromNightModeReparenting() {
         return mIsFromNightModeReparenting;
     }
+
+    /** Set whether this is the foreground tab. */
+    public void setIsForegroundTab(boolean isForegroundTab) {
+        mIsForegroundTab = isForegroundTab;
+    }
+
+    /** @return Whether this is a foreground tab. */
+    public boolean isForegroundTab() {
+        return mIsForegroundTab;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/IdentityDiscController.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/IdentityDiscController.java
index 400f3264..7129f75 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/IdentityDiscController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/IdentityDiscController.java
@@ -17,7 +17,7 @@
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.sync.SyncAndServicesPreferences;
+import org.chromium.chrome.browser.settings.sync.SyncAndServicesSettings;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.ProfileDataCache;
 import org.chromium.chrome.browser.signin.SigninManager;
@@ -170,7 +170,7 @@
         mToolbarManager.enableExperimentalButton(view -> {
             recordIdentityDiscUsed();
             SettingsLauncher.getInstance().launchSettingsPage(
-                    mContext, SyncAndServicesPreferences.class);
+                    mContext, SyncAndServicesSettings.class);
         }, getProfileImage(accountName), R.string.accessibility_toolbar_btn_identity_disc);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarCommonPropertiesModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarCommonPropertiesModel.java
index 6e085f3..1134f02 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarCommonPropertiesModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarCommonPropertiesModel.java
@@ -29,6 +29,9 @@
      */
     boolean isIncognito();
 
+    /** @return Whether the current {@link Tab} is loading. */
+    boolean isLoading();
+
     /**
      * If the current tab state is eligible for displaying the search query terms instead of the
      * URL, this extracts the query terms from the current URL.
@@ -37,7 +40,7 @@
      *         instead of the URL.
      */
     @Nullable
-    default public String getDisplaySearchTerms() {
+    default String getDisplaySearchTerms() {
         return null;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java
index 6a01845e..e6342830 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java
@@ -31,6 +31,12 @@
     @Nullable
     Tab getTab();
 
+    @Override
+    default boolean isLoading() {
+        Tab tab = getTab();
+        return tab != null && tab.isLoading();
+    }
+
     /**
      * @return Whether ToolbarDataProvider currently has a tab related to it.
      */
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 cff61e3..90fd985b 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
@@ -28,6 +28,7 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.FeatureUtilities;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
+import org.chromium.chrome.browser.partnercustomizations.HomepageManager.HomeButtonPreferenceState;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
@@ -64,6 +65,10 @@
     public static final String TEST_URL = "http://127.0.0.1:8000/foo.html";
     public static final String GOOGLE_HTML = "/chrome/test/data/android/google.html";
 
+    private static final String METRICS_HOME_BUTTON_STATE_ENUM =
+            "Settings.ShowHomeButtonPreferenceStateManaged";
+    private static final String METRICS_HOMEPAGE_IS_CUSTOMIZED = "Settings.HomePageIsCustomized";
+
     private EmbeddedTestServer mTestServer;
 
     @Rule
@@ -72,7 +77,6 @@
     @Before
     public void setUp() {
         // Disable Histogram for tests.
-        RecordHistogram.setDisabledForTests(true);
         FeatureUtilities.setHomepageLocationPolicyEnabledForTesting(true);
 
         // Give some user pref setting, simulate user that have their customized preference.
@@ -91,7 +95,6 @@
     @After
     public void tearDown() {
         mTestServer.stopAndDestroyServer();
-        RecordHistogram.setDisabledForTests(false);
         FeatureUtilities.setHomepageLocationPolicyEnabledForTesting(null);
     }
 
@@ -112,6 +115,18 @@
                 SharedPreferencesManager.getInstance().readString(
                         ChromePreferenceKeys.HOMEPAGE_LOCATION_POLICY, ""));
 
+        // METRICS_HOMEPAGE_IS_CUSTOMIZED Should be collected twice it is called in:
+        // 1. ProcessInitializationHandler#handleDeferredStartupTasksInitialization;
+        // 2. HomepageManager#onHomepagePolicyUpdate, which will be called when native initialized.
+        Assert.assertEquals(
+                "Settings.HomepageIsCustomized should be recorded twice when policy enabled", 2,
+                RecordHistogram.getHistogramTotalCountForTesting(METRICS_HOMEPAGE_IS_CUSTOMIZED));
+
+        // METRICS_HOME_BUTTON_STATE_ENUM should be collected once in deferred start up tasks.
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        METRICS_HOME_BUTTON_STATE_ENUM, HomeButtonPreferenceState.MANAGED_ENABLED));
+
         // Start the page again. This time, the homepage should be set to what policy is.
         destroyAndRestartActivity();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/website/SiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/website/SiteSettingsTest.java
index e2da22d..eba2fa9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/website/SiteSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/website/SiteSettingsTest.java
@@ -530,7 +530,7 @@
         NfcSystemLevelSetting.setNfcSettingForTesting(true);
 
         // If you add a category in the SiteSettings UI, please add a test for it below.
-        Assert.assertEquals(19, SiteSettingsCategory.Type.NUM_ENTRIES);
+        Assert.assertEquals(21, SiteSettingsCategory.Type.NUM_ENTRIES);
 
         String[] nullArray = new String[0];
         String[] binaryToggle = new String[] {"binary_toggle"};
@@ -559,6 +559,8 @@
                 new HashMap<Integer, Pair<String[], String[]>>();
         testCases.put(SiteSettingsCategory.Type.ADS, new Pair<>(binaryToggle, binaryToggle));
         testCases.put(SiteSettingsCategory.Type.ALL_SITES, new Pair<>(nullArray, nullArray));
+        testCases.put(SiteSettingsCategory.Type.AUGMENTED_REALITY,
+                new Pair<>(binaryToggle, binaryToggle));
         testCases.put(SiteSettingsCategory.Type.AUTOMATIC_DOWNLOADS,
                 new Pair<>(binaryToggleWithException, binaryToggle));
         testCases.put(SiteSettingsCategory.Type.BACKGROUND_SYNC,
@@ -580,6 +582,8 @@
                 new Pair<>(binaryToggleWithException, binaryToggleWithException));
         testCases.put(SiteSettingsCategory.Type.USB, new Pair<>(binaryToggle, binaryToggle));
         testCases.put(SiteSettingsCategory.Type.USE_STORAGE, new Pair<>(nullArray, nullArray));
+        testCases.put(
+                SiteSettingsCategory.Type.VIRTUAL_REALITY, new Pair<>(binaryToggle, binaryToggle));
         testCases.put(SiteSettingsCategory.Type.BLUETOOTH_SCANNING,
                 new Pair<>(binaryToggle, binaryToggle));
 
@@ -879,6 +883,60 @@
         doTestNfcPermission(false);
     }
 
+    /**
+     * Helper function to test allowing and blocking AUGMENTED_REALITY feature.
+     * @param enabled true to test enabling AUGMENTED_REALITY feature, false to test disabling the
+     *         feature.
+     */
+    private void doTestArPermission(final boolean enabled) {
+        setGlobalToggleForCategory(SiteSettingsCategory.Type.AUGMENTED_REALITY, enabled);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertEquals("AR should be " + (enabled ? "enabled" : "disabled"),
+                    WebsitePreferenceBridge.isCategoryEnabled(ContentSettingsType.AR), enabled);
+        });
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    public void testAllowAr() {
+        doTestArPermission(true);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    public void testBlockAr() {
+        doTestArPermission(false);
+    }
+
+    /**
+     * Helper function to test allowing and blocking VIRTUAL_REALITY feature.
+     * @param enabled true to test enabling VIRTUAL_REALITY feature, false to test disabling the
+     *         feature.
+     */
+    private void doTestVrPermission(final boolean enabled) {
+        setGlobalToggleForCategory(SiteSettingsCategory.Type.VIRTUAL_REALITY, enabled);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertEquals("VR should be " + (enabled ? "enabled" : "disabled"),
+                    WebsitePreferenceBridge.isCategoryEnabled(ContentSettingsType.VR), enabled);
+        });
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    public void testAllowVr() {
+        doTestVrPermission(true);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    public void testBlockVr() {
+        doTestVrPermission(false);
+    }
+
     private int getTabCount() {
         return TestThreadUtils.runOnUiThreadBlockingNoException(
                 () -> mActivityTestRule.getActivity().getTabModelSelector().getTotalTabCount());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/website/WebsitePermissionsFetcherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/website/WebsitePermissionsFetcherTest.java
index fac317d..3fbebf74 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/website/WebsitePermissionsFetcherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/website/WebsitePermissionsFetcherTest.java
@@ -19,7 +19,9 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.content_settings.ContentSettingsType;
+import org.chromium.content_public.browser.ContentFeatureList;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 import java.util.ArrayList;
@@ -268,16 +270,19 @@
 
     @Test
     @SmallTest
+    @EnableFeatures(ContentFeatureList.WEBXR_PERMISSIONS_API)
     public void testFetchAllPreferencesForSingleOrigin() {
         WebsitePermissionsFetcher fetcher = new WebsitePermissionsFetcher();
         FakeWebsitePreferenceBridge websitePreferenceBridge = new FakeWebsitePreferenceBridge();
         fetcher.setWebsitePreferenceBridgeForTesting(websitePreferenceBridge);
 
         // Add permission info types.
-        Assert.assertEquals(9, PermissionInfo.Type.NUM_ENTRIES);
+        Assert.assertEquals(11, PermissionInfo.Type.NUM_ENTRIES);
         String googleOrigin = "https://google.com";
 
         websitePreferenceBridge.addPermissionInfo(new PermissionInfo(
+                PermissionInfo.Type.AUGMENTED_REALITY, googleOrigin, SITE_WILDCARD, false));
+        websitePreferenceBridge.addPermissionInfo(new PermissionInfo(
                 PermissionInfo.Type.GEOLOCATION, googleOrigin, SITE_WILDCARD, false));
         websitePreferenceBridge.addPermissionInfo(
                 new PermissionInfo(PermissionInfo.Type.MIDI, googleOrigin, SITE_WILDCARD, false));
@@ -296,6 +301,8 @@
                 PermissionInfo.Type.CLIPBOARD, googleOrigin, SITE_WILDCARD, false));
         websitePreferenceBridge.addPermissionInfo(new PermissionInfo(
                 PermissionInfo.Type.SENSORS, googleOrigin, SITE_WILDCARD, false));
+        websitePreferenceBridge.addPermissionInfo(new PermissionInfo(
+                PermissionInfo.Type.VIRTUAL_REALITY, googleOrigin, SITE_WILDCARD, false));
 
         // Add content setting exception types.
         String preferenceSource = "preference";
@@ -354,6 +361,8 @@
             Assert.assertNotNull(site.getPermissionInfo(PermissionInfo.Type.MICROPHONE));
             Assert.assertNotNull(site.getPermissionInfo(PermissionInfo.Type.CLIPBOARD));
             Assert.assertNotNull(site.getPermissionInfo(PermissionInfo.Type.SENSORS));
+            Assert.assertNotNull(site.getPermissionInfo(PermissionInfo.Type.VIRTUAL_REALITY));
+            Assert.assertNotNull(site.getPermissionInfo(PermissionInfo.Type.AUGMENTED_REALITY));
 
             // Check content setting exception types.
             Assert.assertEquals(Integer.valueOf(ContentSettingValues.DEFAULT),
@@ -479,17 +488,19 @@
 
     @Test
     @SmallTest
+    @EnableFeatures(ContentFeatureList.WEBXR_PERMISSIONS_API)
     public void testFetchPreferencesForCategoryPermissionInfoTypes() {
         WebsitePermissionsFetcher fetcher = new WebsitePermissionsFetcher();
         FakeWebsitePreferenceBridge websitePreferenceBridge = new FakeWebsitePreferenceBridge();
         fetcher.setWebsitePreferenceBridgeForTesting(websitePreferenceBridge);
 
         String googleOrigin = "https://google.com";
-        ArrayList<Integer> permissionInfoTypes = new ArrayList<>(Arrays.asList(
-                PermissionInfo.Type.CAMERA, PermissionInfo.Type.CLIPBOARD,
-                PermissionInfo.Type.GEOLOCATION, PermissionInfo.Type.MICROPHONE,
-                PermissionInfo.Type.NOTIFICATION, PermissionInfo.Type.PROTECTED_MEDIA_IDENTIFIER,
-                PermissionInfo.Type.SENSORS));
+        ArrayList<Integer> permissionInfoTypes = new ArrayList<>(
+                Arrays.asList(PermissionInfo.Type.AUGMENTED_REALITY, PermissionInfo.Type.CAMERA,
+                        PermissionInfo.Type.CLIPBOARD, PermissionInfo.Type.GEOLOCATION,
+                        PermissionInfo.Type.MICROPHONE, PermissionInfo.Type.NOTIFICATION,
+                        PermissionInfo.Type.PROTECTED_MEDIA_IDENTIFIER, PermissionInfo.Type.SENSORS,
+                        PermissionInfo.Type.VIRTUAL_REALITY));
 
         for (@PermissionInfo.Type int type : permissionInfoTypes) {
             PermissionInfo fakePermissionInfo =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
similarity index 83%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncPreferencesTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
index fc61645..a630f34d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
@@ -30,7 +30,7 @@
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
-import org.chromium.chrome.browser.settings.sync.ManageSyncPreferences;
+import org.chromium.chrome.browser.settings.sync.ManageSyncSettings;
 import org.chromium.chrome.browser.sync.ui.PassphraseCreationDialogFragment;
 import org.chromium.chrome.browser.sync.ui.PassphraseDialogFragment;
 import org.chromium.chrome.browser.sync.ui.PassphraseTypeDialogFragment;
@@ -49,12 +49,12 @@
 import java.util.Set;
 
 /**
- * Tests for ManageSyncPreferences.
+ * Tests for ManageSyncSettings.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public class ManageSyncPreferencesTest {
-    private static final String TAG = "ManageSyncPreferencesTest";
+public class ManageSyncSettingsTest {
+    private static final String TAG = "ManageSyncSettingsTest";
 
     /**
      * Maps ModelTypes to their UI element IDs.
@@ -62,12 +62,12 @@
     private static final Map<Integer, String> UI_DATATYPES = new HashMap<>();
 
     static {
-        UI_DATATYPES.put(ModelType.AUTOFILL, ManageSyncPreferences.PREF_SYNC_AUTOFILL);
-        UI_DATATYPES.put(ModelType.BOOKMARKS, ManageSyncPreferences.PREF_SYNC_BOOKMARKS);
-        UI_DATATYPES.put(ModelType.TYPED_URLS, ManageSyncPreferences.PREF_SYNC_HISTORY);
-        UI_DATATYPES.put(ModelType.PASSWORDS, ManageSyncPreferences.PREF_SYNC_PASSWORDS);
-        UI_DATATYPES.put(ModelType.PROXY_TABS, ManageSyncPreferences.PREF_SYNC_RECENT_TABS);
-        UI_DATATYPES.put(ModelType.PREFERENCES, ManageSyncPreferences.PREF_SYNC_SETTINGS);
+        UI_DATATYPES.put(ModelType.AUTOFILL, ManageSyncSettings.PREF_SYNC_AUTOFILL);
+        UI_DATATYPES.put(ModelType.BOOKMARKS, ManageSyncSettings.PREF_SYNC_BOOKMARKS);
+        UI_DATATYPES.put(ModelType.TYPED_URLS, ManageSyncSettings.PREF_SYNC_HISTORY);
+        UI_DATATYPES.put(ModelType.PASSWORDS, ManageSyncSettings.PREF_SYNC_PASSWORDS);
+        UI_DATATYPES.put(ModelType.PROXY_TABS, ManageSyncSettings.PREF_SYNC_RECENT_TABS);
+        UI_DATATYPES.put(ModelType.PREFERENCES, ManageSyncSettings.PREF_SYNC_SETTINGS);
     }
 
     private SettingsActivity mSettingsActivity;
@@ -86,7 +86,7 @@
     public void testSyncEverythingAndDataTypes() {
         mSyncTestRule.setUpTestAccountAndSignIn();
         SyncTestUtil.waitForSyncActive();
-        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ManageSyncSettings fragment = startManageSyncPreferences();
         ChromeSwitchPreference syncEverything = getSyncEverything(fragment);
         Collection<CheckBoxPreference> dataTypes = getDataTypes(fragment).values();
 
@@ -106,7 +106,7 @@
     public void testSettingDataTypes() {
         mSyncTestRule.setUpTestAccountAndSignIn();
         SyncTestUtil.waitForSyncActive();
-        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ManageSyncSettings fragment = startManageSyncPreferences();
         ChromeSwitchPreference syncEverything = getSyncEverything(fragment);
         Map<Integer, CheckBoxPreference> dataTypes = getDataTypes(fragment);
 
@@ -135,11 +135,11 @@
         mSyncTestRule.setUpTestAccountAndSignIn();
         mSyncTestRule.setPaymentsIntegrationEnabled(true);
 
-        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ManageSyncSettings fragment = startManageSyncPreferences();
         assertSyncOnState(fragment);
 
         CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+                ManageSyncSettings.PREF_SYNC_PAYMENTS_INTEGRATION);
 
         Assert.assertFalse(paymentsIntegration.isEnabled());
         Assert.assertTrue(paymentsIntegration.isChecked());
@@ -153,10 +153,10 @@
         mSyncTestRule.setPaymentsIntegrationEnabled(false);
 
         mSyncTestRule.setChosenDataTypes(false, UI_DATATYPES.keySet());
-        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ManageSyncSettings fragment = startManageSyncPreferences();
 
         CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+                ManageSyncSettings.PREF_SYNC_PAYMENTS_INTEGRATION);
 
         // All data types are enabled by default as syncEverything is toggled off.
         Assert.assertTrue(paymentsIntegration.isEnabled());
@@ -170,13 +170,13 @@
         mSyncTestRule.setUpTestAccountAndSignIn();
         mSyncTestRule.setPaymentsIntegrationEnabled(true);
 
-        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ManageSyncSettings fragment = startManageSyncPreferences();
         assertSyncOnState(fragment);
         ChromeSwitchPreference syncEverything = getSyncEverything(fragment);
         mSyncTestRule.togglePreference(syncEverything);
 
         CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+                ManageSyncSettings.PREF_SYNC_PAYMENTS_INTEGRATION);
         mSyncTestRule.togglePreference(paymentsIntegration);
 
         closeFragment(fragment);
@@ -192,10 +192,10 @@
         mSyncTestRule.setPaymentsIntegrationEnabled(false);
 
         mSyncTestRule.setChosenDataTypes(false, UI_DATATYPES.keySet());
-        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ManageSyncSettings fragment = startManageSyncPreferences();
 
         CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+                ManageSyncSettings.PREF_SYNC_PAYMENTS_INTEGRATION);
         mSyncTestRule.togglePreference(paymentsIntegration);
 
         closeFragment(fragment);
@@ -216,13 +216,13 @@
         Assert.assertTrue(
                 "There should be server cards", mSyncTestRule.hasServerAutofillCreditCards());
 
-        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ManageSyncSettings fragment = startManageSyncPreferences();
         assertSyncOnState(fragment);
         ChromeSwitchPreference syncEverything = getSyncEverything(fragment);
         mSyncTestRule.togglePreference(syncEverything);
 
         CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+                ManageSyncSettings.PREF_SYNC_PAYMENTS_INTEGRATION);
         mSyncTestRule.togglePreference(paymentsIntegration);
 
         closeFragment(fragment);
@@ -239,17 +239,17 @@
         mSyncTestRule.setUpTestAccountAndSignIn();
         mSyncTestRule.setPaymentsIntegrationEnabled(true);
 
-        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ManageSyncSettings fragment = startManageSyncPreferences();
         assertSyncOnState(fragment);
         ChromeSwitchPreference syncEverything = getSyncEverything(fragment);
         mSyncTestRule.togglePreference(syncEverything);
 
-        CheckBoxPreference syncAutofill = (CheckBoxPreference) fragment.findPreference(
-                ManageSyncPreferences.PREF_SYNC_AUTOFILL);
+        CheckBoxPreference syncAutofill =
+                (CheckBoxPreference) fragment.findPreference(ManageSyncSettings.PREF_SYNC_AUTOFILL);
         mSyncTestRule.togglePreference(syncAutofill);
 
         CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+                ManageSyncSettings.PREF_SYNC_PAYMENTS_INTEGRATION);
         Assert.assertFalse(paymentsIntegration.isEnabled());
         Assert.assertFalse(paymentsIntegration.isChecked());
 
@@ -266,12 +266,12 @@
         mSyncTestRule.disableDataType(ModelType.AUTOFILL);
 
         // Get the UI elements.
-        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ManageSyncSettings fragment = startManageSyncPreferences();
         ChromeSwitchPreference syncEverything = getSyncEverything(fragment);
-        CheckBoxPreference syncAutofill = (CheckBoxPreference) fragment.findPreference(
-                ManageSyncPreferences.PREF_SYNC_AUTOFILL);
+        CheckBoxPreference syncAutofill =
+                (CheckBoxPreference) fragment.findPreference(ManageSyncSettings.PREF_SYNC_AUTOFILL);
         CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+                ManageSyncSettings.PREF_SYNC_PAYMENTS_INTEGRATION);
 
         // All three are unchecked and payments is disabled.
         Assert.assertFalse(syncEverything.isChecked());
@@ -304,7 +304,7 @@
     public void testChoosePassphraseTypeWhenSyncIsOff() {
         mSyncTestRule.setUpTestAccountAndSignIn();
         SyncTestUtil.waitForSyncActive();
-        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ManageSyncSettings fragment = startManageSyncPreferences();
         Preference encryption = getEncryption(fragment);
         clickPreference(encryption);
 
@@ -325,7 +325,7 @@
     public void testEnterPassphraseWhenSyncIsOff() {
         mSyncTestRule.setUpTestAccountAndSignIn();
         SyncTestUtil.waitForSyncActive();
-        final ManageSyncPreferences fragment = startManageSyncPreferences();
+        final ManageSyncSettings fragment = startManageSyncPreferences();
         mSyncTestRule.stopSync();
         TestThreadUtils.runOnUiThreadBlockingNoException(
                 () -> fragment.onPassphraseEntered("passphrase"));
@@ -347,7 +347,7 @@
         // Trigger PassphraseDialogFragment to be shown when taping on Encryption.
         pss.setPassphraseRequiredForPreferredDataTypes(true);
 
-        final ManageSyncPreferences fragment = startManageSyncPreferences();
+        final ManageSyncSettings fragment = startManageSyncPreferences();
         Preference encryption = getEncryption(fragment);
         clickPreference(encryption);
 
@@ -363,7 +363,7 @@
             fragment.getFragmentManager().executePendingTransactions();
             Assert.assertNull("PassphraseDialogFragment should be dismissed.",
                     mSettingsActivity.getFragmentManager().findFragmentByTag(
-                            ManageSyncPreferences.FRAGMENT_ENTER_PASSPHRASE));
+                            ManageSyncSettings.FRAGMENT_ENTER_PASSPHRASE));
         });
     }
 
@@ -373,7 +373,7 @@
     public void testPassphraseCreation() {
         mSyncTestRule.setUpTestAccountAndSignIn();
         SyncTestUtil.waitForSyncActive();
-        final ManageSyncPreferences fragment = startManageSyncPreferences();
+        final ManageSyncSettings fragment = startManageSyncPreferences();
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> fragment.onPassphraseTypeSelected(PassphraseType.CUSTOM_PASSPHRASE));
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -441,14 +441,13 @@
         });
     }
 
-    private ManageSyncPreferences startManageSyncPreferences() {
-        mSettingsActivity =
-                mSyncTestRule.startSettingsActivity(ManageSyncPreferences.class.getName());
+    private ManageSyncSettings startManageSyncPreferences() {
+        mSettingsActivity = mSyncTestRule.startSettingsActivity(ManageSyncSettings.class.getName());
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        return (ManageSyncPreferences) mSettingsActivity.getMainFragment();
+        return (ManageSyncSettings) mSettingsActivity.getMainFragment();
     }
 
-    private void closeFragment(ManageSyncPreferences fragment) {
+    private void closeFragment(ManageSyncSettings fragment) {
         FragmentTransaction transaction =
                 mSettingsActivity.getSupportFragmentManager().beginTransaction();
         transaction.remove(fragment);
@@ -456,12 +455,12 @@
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
 
-    private ChromeSwitchPreference getSyncEverything(ManageSyncPreferences fragment) {
+    private ChromeSwitchPreference getSyncEverything(ManageSyncSettings fragment) {
         return (ChromeSwitchPreference) fragment.findPreference(
-                ManageSyncPreferences.PREF_SYNC_EVERYTHING);
+                ManageSyncSettings.PREF_SYNC_EVERYTHING);
     }
 
-    private Map<Integer, CheckBoxPreference> getDataTypes(ManageSyncPreferences fragment) {
+    private Map<Integer, CheckBoxPreference> getDataTypes(ManageSyncSettings fragment) {
         Map<Integer, CheckBoxPreference> dataTypes = new HashMap<>();
         for (Map.Entry<Integer, String> uiDataType : UI_DATATYPES.entrySet()) {
             Integer modelType = uiDataType.getKey();
@@ -471,34 +470,34 @@
         return dataTypes;
     }
 
-    private Preference getGoogleActivityControls(ManageSyncPreferences fragment) {
-        return fragment.findPreference(ManageSyncPreferences.PREF_GOOGLE_ACTIVITY_CONTROLS);
+    private Preference getGoogleActivityControls(ManageSyncSettings fragment) {
+        return fragment.findPreference(ManageSyncSettings.PREF_GOOGLE_ACTIVITY_CONTROLS);
     }
 
-    private Preference getEncryption(ManageSyncPreferences fragment) {
-        return fragment.findPreference(ManageSyncPreferences.PREF_ENCRYPTION);
+    private Preference getEncryption(ManageSyncSettings fragment) {
+        return fragment.findPreference(ManageSyncSettings.PREF_ENCRYPTION);
     }
 
-    private Preference getManageData(ManageSyncPreferences fragment) {
-        return fragment.findPreference(ManageSyncPreferences.PREF_SYNC_MANAGE_DATA);
+    private Preference getManageData(ManageSyncSettings fragment) {
+        return fragment.findPreference(ManageSyncSettings.PREF_SYNC_MANAGE_DATA);
     }
 
     private PassphraseDialogFragment getPassphraseDialogFragment() {
         return ActivityUtils.waitForFragment(
-                mSettingsActivity, ManageSyncPreferences.FRAGMENT_ENTER_PASSPHRASE);
+                mSettingsActivity, ManageSyncSettings.FRAGMENT_ENTER_PASSPHRASE);
     }
 
     private PassphraseTypeDialogFragment getPassphraseTypeDialogFragment() {
         return ActivityUtils.waitForFragment(
-                mSettingsActivity, ManageSyncPreferences.FRAGMENT_PASSPHRASE_TYPE);
+                mSettingsActivity, ManageSyncSettings.FRAGMENT_PASSPHRASE_TYPE);
     }
 
     private PassphraseCreationDialogFragment getPassphraseCreationDialogFragment() {
         return ActivityUtils.waitForFragment(
-                mSettingsActivity, ManageSyncPreferences.FRAGMENT_CUSTOM_PASSPHRASE);
+                mSettingsActivity, ManageSyncSettings.FRAGMENT_CUSTOM_PASSPHRASE);
     }
 
-    private void assertSyncOnState(ManageSyncPreferences fragment) {
+    private void assertSyncOnState(ManageSyncSettings fragment) {
         ChromeSwitchPreference syncEverything = getSyncEverything(fragment);
         Assert.assertTrue("The sync everything switch should be on.", syncEverything.isChecked());
         Assert.assertTrue(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesSettingsTest.java
similarity index 80%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesPreferencesTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesSettingsTest.java
index f50d6ae..5739858 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesSettingsTest.java
@@ -32,7 +32,7 @@
 import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.sync.SyncAndServicesPreferences;
+import org.chromium.chrome.browser.settings.sync.SyncAndServicesSettings;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ApplicationTestUtils;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
@@ -42,13 +42,11 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 /**
- * Tests for SyncAndServicesPreferences.
+ * Tests for SyncAndServicesSettings.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public class SyncAndServicesPreferencesTest {
-    private static final String TAG = "SyncAndServicesPreferencesTest";
-
+public class SyncAndServicesSettingsTest {
     @Rule
     public SyncTestRule mSyncTestRule = new SyncTestRule();
 
@@ -65,7 +63,7 @@
     public void testSyncSwitch() {
         mSyncTestRule.setUpTestAccountAndSignIn();
         SyncTestUtil.waitForSyncActive();
-        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        SyncAndServicesSettings fragment = startSyncAndServicesPreferences();
         final ChromeSwitchPreference syncSwitch = getSyncSwitch(fragment);
 
         Assert.assertTrue(syncSwitch.isChecked());
@@ -87,7 +85,7 @@
     public void testOpeningSettingsDoesntEnableSync() {
         mSyncTestRule.setUpTestAccountAndSignIn();
         mSyncTestRule.stopSync();
-        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        SyncAndServicesSettings fragment = startSyncAndServicesPreferences();
         closeFragment(fragment);
         Assert.assertFalse(AndroidSyncSettings.get().isChromeSyncEnabled());
     }
@@ -113,7 +111,7 @@
     public void testDefaultControlStatesWithSyncOffThenOn() {
         mSyncTestRule.setUpTestAccountAndSignIn();
         mSyncTestRule.stopSync();
-        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        SyncAndServicesSettings fragment = startSyncAndServicesPreferences();
         assertSyncOffState(fragment);
         mSyncTestRule.togglePreference(getSyncSwitch(fragment));
         SyncTestUtil.waitForEngineInitialized();
@@ -126,7 +124,7 @@
     public void testDefaultControlStatesWithSyncOnThenOff() {
         mSyncTestRule.setUpTestAccountAndSignIn();
         SyncTestUtil.waitForSyncActive();
-        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        SyncAndServicesSettings fragment = startSyncAndServicesPreferences();
         assertSyncOnState(fragment);
         mSyncTestRule.togglePreference(getSyncSwitch(fragment));
         assertSyncOffState(fragment);
@@ -147,7 +145,7 @@
                 "There should be server cards", mSyncTestRule.hasServerAutofillCreditCards());
 
         Assert.assertTrue(AndroidSyncSettings.get().isChromeSyncEnabled());
-        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        SyncAndServicesSettings fragment = startSyncAndServicesPreferences();
         assertSyncOnState(fragment);
         ChromeSwitchPreference syncSwitch = getSyncSwitch(fragment);
         Assert.assertTrue(syncSwitch.isChecked());
@@ -179,7 +177,7 @@
     public void testDismissedSettingsShowsSyncSwitchOffByDefault() throws Exception {
         mSyncTestRule.setUpTestAccountAndSignInWithSyncSetupAsIncomplete();
         startPreferencesForAdvancedSyncFlowAndInterruptIt();
-        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        SyncAndServicesSettings fragment = startSyncAndServicesPreferences();
         assertSyncOffState(fragment);
     }
 
@@ -189,7 +187,7 @@
     public void testDismissedSettingsShowsSyncErrorCard() throws Exception {
         mSyncTestRule.setUpTestAccountAndSignInWithSyncSetupAsIncomplete();
         startPreferencesForAdvancedSyncFlowAndInterruptIt();
-        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        SyncAndServicesSettings fragment = startSyncAndServicesPreferences();
         Assert.assertNotNull("Sync error card should be shown", getSyncErrorCard(fragment));
     }
 
@@ -200,7 +198,7 @@
         mSyncTestRule.setUpTestAccountAndSignInWithSyncSetupAsIncomplete();
         startPreferencesForAdvancedSyncFlowAndInterruptIt();
         // Open Settings and leave sync off.
-        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        SyncAndServicesSettings fragment = startSyncAndServicesPreferences();
         pressBackAndDismissActivity(fragment.getActivity());
         // FirstSetupComplete should be set.
         TestThreadUtils.runOnUiThreadBlocking(
@@ -218,7 +216,7 @@
         mSyncTestRule.setUpTestAccountAndSignInWithSyncSetupAsIncomplete();
         startPreferencesForAdvancedSyncFlowAndInterruptIt();
         // Open Settings and leave sync off.
-        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        SyncAndServicesSettings fragment = startSyncAndServicesPreferences();
         ApplicationTestUtils.finishActivity(fragment.getActivity());
         // FirstSetupComplete should be set.
         TestThreadUtils.runOnUiThreadBlocking(
@@ -236,7 +234,7 @@
         mSyncTestRule.setUpTestAccountAndSignInWithSyncSetupAsIncomplete();
         startPreferencesForAdvancedSyncFlowAndInterruptIt();
         // Open Settings and turn sync on.
-        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        SyncAndServicesSettings fragment = startSyncAndServicesPreferences();
         ChromeSwitchPreference syncSwitch = getSyncSwitch(fragment);
         mSyncTestRule.togglePreference(syncSwitch);
         Assert.assertTrue(syncSwitch.isChecked());
@@ -257,7 +255,7 @@
         pss.setEngineInitialized(true);
         pss.setTrustedVaultKeyRequiredForPreferredDataTypes(true);
 
-        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        SyncAndServicesSettings fragment = startSyncAndServicesPreferences();
 
         Assert.assertNotNull("Sync error card should be shown", getSyncErrorCard(fragment));
     }
@@ -265,7 +263,7 @@
     /**
      * Test: if the onboarding was never shown, the AA chrome preference should not exist.
      *
-     * Note: presence of the {@link SyncAndServicesPreferences.PREF_AUTOFILL_ASSISTANT}
+     * Note: presence of the {@link SyncAndServicesSettings.PREF_AUTOFILL_ASSISTANT}
      * shared preference indicates whether onboarding was shown or not.
      */
     @Test
@@ -273,17 +271,17 @@
     @Feature({"Sync"})
     @EnableFeatures(ChromeFeatureList.AUTOFILL_ASSISTANT)
     public void testAutofillAssistantNoPreferenceIfOnboardingNeverShown() {
-        final SyncAndServicesPreferences syncPrefs = startSyncAndServicesPreferences();
+        final SyncAndServicesSettings syncPrefs = startSyncAndServicesPreferences();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             Assert.assertNull(
-                    syncPrefs.findPreference(SyncAndServicesPreferences.PREF_AUTOFILL_ASSISTANT));
+                    syncPrefs.findPreference(SyncAndServicesSettings.PREF_AUTOFILL_ASSISTANT));
         });
     }
 
     /**
      * Test: if the onboarding was shown at least once, the AA chrome preference should also exist.
      *
-     * Note: presence of the {@link SyncAndServicesPreferences.PREF_AUTOFILL_ASSISTANT}
+     * Note: presence of the {@link SyncAndServicesSettings.PREF_AUTOFILL_ASSISTANT}
      * shared preference indicates whether onboarding was shown or not.
      */
     @Test
@@ -292,10 +290,10 @@
     @EnableFeatures(ChromeFeatureList.AUTOFILL_ASSISTANT)
     public void testAutofillAssistantPreferenceShownIfOnboardingShown() {
         setAutofillAssistantSwitchValue(true);
-        final SyncAndServicesPreferences syncPrefs = startSyncAndServicesPreferences();
+        final SyncAndServicesSettings syncPrefs = startSyncAndServicesPreferences();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             Assert.assertNotNull(
-                    syncPrefs.findPreference(SyncAndServicesPreferences.PREF_AUTOFILL_ASSISTANT));
+                    syncPrefs.findPreference(SyncAndServicesSettings.PREF_AUTOFILL_ASSISTANT));
         });
     }
 
@@ -308,10 +306,10 @@
     @DisableFeatures(ChromeFeatureList.AUTOFILL_ASSISTANT)
     public void testAutofillAssistantNoPreferenceIfFeatureDisabled() {
         setAutofillAssistantSwitchValue(true);
-        final SyncAndServicesPreferences syncPrefs = startSyncAndServicesPreferences();
+        final SyncAndServicesSettings syncPrefs = startSyncAndServicesPreferences();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             Assert.assertNull(
-                    syncPrefs.findPreference(SyncAndServicesPreferences.PREF_AUTOFILL_ASSISTANT));
+                    syncPrefs.findPreference(SyncAndServicesSettings.PREF_AUTOFILL_ASSISTANT));
         });
     }
 
@@ -324,19 +322,18 @@
     @EnableFeatures(ChromeFeatureList.AUTOFILL_ASSISTANT)
     public void testAutofillAssistantSwitchOn() {
         TestThreadUtils.runOnUiThreadBlocking(() -> { setAutofillAssistantSwitchValue(true); });
-        final SyncAndServicesPreferences syncAndServicesPreferences =
-                startSyncAndServicesPreferences();
+        final SyncAndServicesSettings syncAndServicesSettings = startSyncAndServicesPreferences();
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             ChromeSwitchPreference autofillAssistantSwitch =
-                    (ChromeSwitchPreference) syncAndServicesPreferences.findPreference(
-                            SyncAndServicesPreferences.PREF_AUTOFILL_ASSISTANT);
+                    (ChromeSwitchPreference) syncAndServicesSettings.findPreference(
+                            SyncAndServicesSettings.PREF_AUTOFILL_ASSISTANT);
             Assert.assertTrue(autofillAssistantSwitch.isChecked());
 
             autofillAssistantSwitch.performClick();
-            Assert.assertFalse(syncAndServicesPreferences.isAutofillAssistantSwitchOn());
+            Assert.assertFalse(syncAndServicesSettings.isAutofillAssistantSwitchOn());
             autofillAssistantSwitch.performClick();
-            Assert.assertTrue(syncAndServicesPreferences.isAutofillAssistantSwitchOn());
+            Assert.assertTrue(syncAndServicesSettings.isAutofillAssistantSwitchOn());
         });
     }
 
@@ -346,13 +343,12 @@
     @EnableFeatures(ChromeFeatureList.AUTOFILL_ASSISTANT)
     public void testAutofillAssistantSwitchOff() {
         TestThreadUtils.runOnUiThreadBlocking(() -> { setAutofillAssistantSwitchValue(false); });
-        final SyncAndServicesPreferences syncAndServicesPreferences =
-                startSyncAndServicesPreferences();
+        final SyncAndServicesSettings syncAndServicesSettings = startSyncAndServicesPreferences();
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             ChromeSwitchPreference autofillAssistantSwitch =
-                    (ChromeSwitchPreference) syncAndServicesPreferences.findPreference(
-                            SyncAndServicesPreferences.PREF_AUTOFILL_ASSISTANT);
+                    (ChromeSwitchPreference) syncAndServicesSettings.findPreference(
+                            SyncAndServicesSettings.PREF_AUTOFILL_ASSISTANT);
             Assert.assertFalse(autofillAssistantSwitch.isChecked());
         });
     }
@@ -373,13 +369,13 @@
     }
 
     /**
-     * Start SyncAndServicesPreferences signin screen and dissmiss it without pressing confirm or
+     * Start SyncAndServicesSettings signin screen and dissmiss it without pressing confirm or
      * cancel.
      */
     private void startPreferencesForAdvancedSyncFlowAndInterruptIt() throws Exception {
         Context context = InstrumentationRegistry.getTargetContext();
-        String fragmentName = SyncAndServicesPreferences.class.getName();
-        final Bundle arguments = SyncAndServicesPreferences.createArguments(true);
+        String fragmentName = SyncAndServicesSettings.class.getName();
+        final Bundle arguments = SyncAndServicesSettings.createArguments(true);
         Intent intent = SettingsLauncher.getInstance().createIntentForSettingsPage(
                 context, fragmentName, arguments);
         Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
@@ -387,14 +383,14 @@
         ApplicationTestUtils.finishActivity(activity);
     }
 
-    private SyncAndServicesPreferences startSyncAndServicesPreferences() {
+    private SyncAndServicesSettings startSyncAndServicesPreferences() {
         mSettingsActivity =
-                mSyncTestRule.startSettingsActivity(SyncAndServicesPreferences.class.getName());
+                mSyncTestRule.startSettingsActivity(SyncAndServicesSettings.class.getName());
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        return (SyncAndServicesPreferences) mSettingsActivity.getMainFragment();
+        return (SyncAndServicesSettings) mSettingsActivity.getMainFragment();
     }
 
-    private void closeFragment(SyncAndServicesPreferences fragment) {
+    private void closeFragment(SyncAndServicesSettings fragment) {
         FragmentTransaction transaction =
                 mSettingsActivity.getSupportFragmentManager().beginTransaction();
         transaction.remove(fragment);
@@ -408,24 +404,24 @@
         ApplicationTestUtils.finishActivity(activity);
     }
 
-    private ChromeSwitchPreference getSyncSwitch(SyncAndServicesPreferences fragment) {
+    private ChromeSwitchPreference getSyncSwitch(SyncAndServicesSettings fragment) {
         return (ChromeSwitchPreference) fragment.findPreference(
-                SyncAndServicesPreferences.PREF_SYNC_REQUESTED);
+                SyncAndServicesSettings.PREF_SYNC_REQUESTED);
     }
 
-    private Preference getSyncErrorCard(SyncAndServicesPreferences fragment) {
+    private Preference getSyncErrorCard(SyncAndServicesSettings fragment) {
         return ((PreferenceCategory) fragment.findPreference(
-                        SyncAndServicesPreferences.PREF_SYNC_CATEGORY))
-                .findPreference(SyncAndServicesPreferences.PREF_SYNC_ERROR_CARD);
+                        SyncAndServicesSettings.PREF_SYNC_CATEGORY))
+                .findPreference(SyncAndServicesSettings.PREF_SYNC_ERROR_CARD);
     }
 
-    private void assertSyncOnState(SyncAndServicesPreferences fragment) {
+    private void assertSyncOnState(SyncAndServicesSettings fragment) {
         Assert.assertTrue("The sync switch should be on.", getSyncSwitch(fragment).isChecked());
         Assert.assertTrue(
                 "The sync switch should be enabled.", getSyncSwitch(fragment).isEnabled());
     }
 
-    private void assertSyncOffState(SyncAndServicesPreferences fragment) {
+    private void assertSyncOffState(SyncAndServicesSettings fragment) {
         Assert.assertFalse("The sync switch should be off.", getSyncSwitch(fragment).isChecked());
         Assert.assertTrue(
                 "The sync switch should be enabled.", getSyncSwitch(fragment).isEnabled());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java
index c1d84d75..67ab1dd9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java
@@ -35,7 +35,6 @@
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.UrlUtils;
@@ -224,11 +223,8 @@
      */
     @Test
     @MediumTest
-    @CommandLineFlags
-            .Add({"enable-features=WebXR"})
+            @CommandLineFlags.Add({"enable-features=WebXR"})
             @Restriction(RESTRICTION_TYPE_SVR)
-            @DisableIf.
-            Build(message = "https://crbug.com/1039890", sdk_is_less_than = Build.VERSION_CODES.M)
             public void testControlsVisibleAfterExitingVr_WebXr() throws InterruptedException {
         controlsVisibleAfterExitingVrImpl(
                 WebXrVrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"),
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/night_mode/NightModeReparentingControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/night_mode/NightModeReparentingControllerTest.java
index f91e610..225a18f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/night_mode/NightModeReparentingControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/night_mode/NightModeReparentingControllerTest.java
@@ -6,6 +6,7 @@
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -34,7 +35,9 @@
 import org.chromium.chrome.browser.tabmodel.TabReparentingParams;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Unit tests for {@link NightModeReparentingControllerTest}.
@@ -51,7 +54,7 @@
             if (mActivityTabProvider == null) {
                 // setup
                 mActivityTabProvider = Mockito.mock(ActivityTabProvider.class);
-                doReturn(getNextTabFromList()).when(mActivityTabProvider).get();
+                doAnswer(invocation -> getNextTabFromList()).when(mActivityTabProvider).get();
             }
             return mActivityTabProvider;
         }
@@ -61,8 +64,12 @@
             if (mTabModelSelector == null) {
                 // setup
                 mTabModelSelector = Mockito.mock(TabModelSelector.class);
-                doReturn(getCurrentTabModel()).when(mTabModelSelector).getCurrentModel();
-                doReturn(getCurrentTabModel()).when(mTabModelSelector).getModel(anyBoolean());
+                doAnswer(invocation -> getCurrentTabModel())
+                        .when(mTabModelSelector)
+                        .getCurrentModel();
+                doAnswer(invocation -> getCurrentTabModel())
+                        .when(mTabModelSelector)
+                        .getModel(anyBoolean());
             }
 
             return mTabModelSelector;
@@ -79,6 +86,7 @@
     ArgumentCaptor<Tab> mTabCaptor;
 
     List<Tab> mTabs = new ArrayList<>();
+    Map<Tab, Integer> mTabIndexMapping = new HashMap<>();
 
     NightModeReparentingController mController;
     NightModeReparentingController.Delegate mDelegate;
@@ -87,12 +95,17 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        Mockito.doAnswer(invocation -> {
-                   removeTab(mTabCaptor.getValue());
-                   return null;
-               })
+        doAnswer(invocation -> {
+            removeTab(mTabCaptor.getValue());
+            return null;
+        })
                 .when(mTabModel)
                 .removeTab(mTabCaptor.capture());
+
+        doAnswer(invocation -> getTabIndex(mTabCaptor.getValue()))
+                .when(mTabModel)
+                .indexOf(mTabCaptor.capture());
+
         mDelegate = new FakeNightModeReparentingDelegate();
         mController = new NightModeReparentingController(mDelegate, mReparentingTaskDelegate);
     }
@@ -101,6 +114,7 @@
     public void tearDown() {
         AsyncTabParamsManager.getAsyncTabParams().clear();
         mTabs.clear();
+        mTabIndexMapping.clear();
     }
 
     @Test
@@ -145,23 +159,97 @@
         createAndAddMockTab(2, 1);
         mController.onNightModeStateChanged();
 
+        TabReparentingParams trp =
+                (TabReparentingParams) AsyncTabParamsManager.getAsyncTabParams().get(1);
+        Assert.assertEquals(
+                "The index of the first tab stored should match its index in the tab stack.", 0,
+                trp.getTabIndex());
+        Assert.assertTrue(trp.isFromNightModeReparenting());
+        Assert.assertTrue(trp.isForegroundTab());
+
+        Tab tab = trp.getTabToReparent();
+        Assert.assertNotNull(tab);
+        Assert.assertEquals(1, tab.getId());
+
+        trp = (TabReparentingParams) AsyncTabParamsManager.getAsyncTabParams().get(2);
+        Assert.assertFalse("The index of the background tabs stored shouldn't have a tab index.",
+                trp.hasTabIndex());
+        Assert.assertTrue(trp.isFromNightModeReparenting());
+        Assert.assertFalse(trp.isForegroundTab());
+
+        tab = trp.getTabToReparent();
+        Assert.assertNotNull(tab);
+        Assert.assertEquals(2, tab.getId());
+
+        verify(mTask, times(2)).detach();
+
+        mController.onStartWithNative();
+        verify(mTask, times(2)).finish(anyObject(), anyObject());
+    }
+
+    @Test
+    public void testReparenting_twoTabsOutOfOrder() {
+        createAndAddMockTab(1, 1);
+        createAndAddMockTab(2, 0);
+        mController.onNightModeStateChanged();
+
         AsyncTabParams params = AsyncTabParamsManager.getAsyncTabParams().get(1);
         Assert.assertNotNull(params);
         Assert.assertTrue(params instanceof TabReparentingParams);
 
         TabReparentingParams trp = (TabReparentingParams) params;
         Assert.assertEquals(
-                "The index of the first tab stored should match it's index in the tab stack.", 0,
+                "The index of the first tab stored should match its index in the tab stack.", 1,
                 trp.getTabIndex());
         Assert.assertTrue(trp.isFromNightModeReparenting());
+        Assert.assertTrue(trp.isForegroundTab());
 
         Tab tab = trp.getTabToReparent();
         Assert.assertNotNull(tab);
         Assert.assertEquals(1, tab.getId());
-        verify(mTask, times(1)).detach();
+
+        verify(mTask, times(2)).detach();
 
         mController.onStartWithNative();
-        verify(mTask, times(1)).finish(anyObject(), anyObject());
+        verify(mTask, times(2)).finish(anyObject(), anyObject());
+    }
+
+    @Test
+    public void testReparenting_threeTabsOutOfOrder() {
+        createAndAddMockTab(1, 1);
+        createAndAddMockTab(2, 0);
+        createAndAddMockTab(3, 2);
+        mController.onNightModeStateChanged();
+
+        // Check the foreground tab.
+        TabReparentingParams trp =
+                (TabReparentingParams) AsyncTabParamsManager.getAsyncTabParams().get(1);
+        Assert.assertEquals(
+                "The index of the first tab stored should match its index in the tab stack.", 1,
+                trp.getTabIndex());
+        Assert.assertTrue(trp.isFromNightModeReparenting());
+        Assert.assertTrue(trp.isForegroundTab());
+
+        Tab tab = trp.getTabToReparent();
+        Assert.assertNotNull(tab);
+        Assert.assertEquals(1, tab.getId());
+
+        // Check the background tabs.
+        trp = (TabReparentingParams) AsyncTabParamsManager.getAsyncTabParams().get(2);
+        Assert.assertFalse("The index of the background tabs stored shouldn't have a tab index.",
+                trp.hasTabIndex());
+        Assert.assertTrue(trp.isFromNightModeReparenting());
+        Assert.assertFalse(trp.isForegroundTab());
+        trp = (TabReparentingParams) AsyncTabParamsManager.getAsyncTabParams().get(3);
+        Assert.assertFalse("The index of the background tabs stored shouldn't have a tab index.",
+                trp.hasTabIndex());
+        Assert.assertTrue(trp.isFromNightModeReparenting());
+        Assert.assertFalse(trp.isForegroundTab());
+
+        verify(mTask, times(3)).detach();
+
+        mController.onStartWithNative();
+        verify(mTask, times(3)).finish(anyObject(), anyObject());
     }
 
     @Test
@@ -183,7 +271,8 @@
         udh.setUserData(ReparentingTask.class, mTask);
         doReturn(udh).when(tab).getUserDataHost();
         doReturn(id).when(tab).getId();
-        mTabs.add(index, tab);
+        mTabs.add(tab);
+        mTabIndexMapping.put(tab, index);
     }
 
     private Tab getNextTabFromList() {
@@ -198,4 +287,8 @@
     private TabModel getCurrentTabModel() {
         return mTabModel;
     }
+
+    private int getTabIndex(Tab tab) {
+        return mTabIndexMapping.get(tab);
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
index 82c199e..117c25ef 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
@@ -36,6 +36,7 @@
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
 import org.chromium.chrome.browser.toolbar.ToolbarCommonPropertiesModel;
+import org.chromium.chrome.browser.util.UrlConstants;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -141,6 +142,9 @@
     @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO)
     public void searchEngineLogo_showGoogleLogo_whenScrolled() {
         setupSearchEngineLogoForTesting(true, false, false);
+        doReturn(false).when(mToolbarCommonPropertiesModel).isLoading();
+        doReturn(UrlConstants.NTP_URL).when(mToolbarCommonPropertiesModel).getCurrentUrl();
+        doReturn(mNewTabPage).when(mToolbarCommonPropertiesModel).getNewTabPageForCurrentTab();
 
         mMediator.setUrlHasFocus(false);
         mMediator.setShowIconsWhenUrlFocused(true);
@@ -361,6 +365,24 @@
                 mMediator.getSecurityIconTintForSearchEngineIcon(R.drawable.ic_search));
     }
 
+    @Test
+    @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO)
+    public void resolveUrlBarTextWithAutocomplete_urlBarTextEmpty() {
+        Assert.assertEquals("Empty urlBarText should resolve to empty urlBarTextWithAutocomplete",
+                "", mMediator.resolveUrlBarTextWithAutocomplete(""));
+    }
+
+    @Test
+    @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO)
+    public void resolveUrlBarTextWithAutocomplete_urlBarTextMismatchesAutocompleteText() {
+        doReturn("https://foo.com").when(mUrlBarEditingTextStateProvider).getTextWithAutocomplete();
+        String msg = "The urlBarText should only resolve to the autocomplete text if it's a "
+                + "substring of the autocomplete text.";
+        Assert.assertEquals(
+                msg, "https://foo.com", mMediator.resolveUrlBarTextWithAutocomplete("foo.com"));
+        Assert.assertEquals(msg, "bar.com", mMediator.resolveUrlBarTextWithAutocomplete("bar.com"));
+    }
+
     private void setupSearchEngineLogoForTesting(
             boolean shouldShowLogo, boolean showGoogle, boolean loupeEverywhere) {
         doReturn(shouldShowLogo).when(mDelegate).shouldShowSearchEngineLogo(false);
diff --git a/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java b/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java
index 74d76c2e..3d2fa669 100644
--- a/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java
+++ b/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java
@@ -71,6 +71,7 @@
         HashMap<String, Boolean> features = new HashMap<String, Boolean>();
         features.put(ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS, false);
         features.put(ChromeFeatureList.EPHEMERAL_TAB, false);
+        features.put(ChromeFeatureList.CONTEXT_MENU_COPY_IMAGE, true);
 
         ChromeFeatureList.setTestFeatures(features);
     }
@@ -279,18 +280,19 @@
         FirstRunStatus.setFirstRunFlowComplete(true);
 
         initializePopulator(ChromeContextMenuPopulator.ContextMenuMode.NORMAL);
-        int[] expected2 = {R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_save_image,
-                R.id.contextmenu_share_image};
+        int[] expected2 = {R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_copy_image,
+                R.id.contextmenu_save_image, R.id.contextmenu_share_image};
         checkMenuOptions(contextMenuParams, expected2);
 
         initializePopulator(ChromeContextMenuPopulator.ContextMenuMode.CUSTOM_TAB);
         int[] expected3 = {R.id.contextmenu_open_in_browser_id, R.id.contextmenu_open_image,
-                R.id.contextmenu_save_image, R.id.contextmenu_share_image};
+                R.id.contextmenu_copy_image, R.id.contextmenu_save_image,
+                R.id.contextmenu_share_image};
         checkMenuOptions(contextMenuParams, expected3);
 
         initializePopulator(ChromeContextMenuPopulator.ContextMenuMode.WEB_APP);
-        int[] expected4 = {R.id.contextmenu_save_image, R.id.contextmenu_share_image,
-                R.id.contextmenu_open_in_chrome};
+        int[] expected4 = {R.id.contextmenu_copy_image, R.id.contextmenu_save_image,
+                R.id.contextmenu_share_image, R.id.contextmenu_open_in_chrome};
         checkMenuOptions(contextMenuParams, expected4);
     }
 
@@ -317,23 +319,23 @@
                 R.id.contextmenu_open_in_incognito_tab, R.id.contextmenu_open_in_other_window,
                 R.id.contextmenu_copy_link_address, R.id.contextmenu_save_link_as,
                 R.id.contextmenu_share_link};
-        int[] expected2Tab2 = {R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_save_image,
-                R.id.contextmenu_share_image};
+        int[] expected2Tab2 = {R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_copy_image,
+                R.id.contextmenu_save_image, R.id.contextmenu_share_image};
         checkMenuOptions(contextMenuParams, expected2Tab1, expected2Tab2);
 
         initializePopulator(ChromeContextMenuPopulator.ContextMenuMode.CUSTOM_TAB);
         int[] expected3Tab1 = {R.id.contextmenu_open_in_browser_id,
                 R.id.contextmenu_copy_link_address, R.id.contextmenu_save_link_as,
                 R.id.contextmenu_share_link};
-        int[] expected3Tab2 = {R.id.contextmenu_open_image, R.id.contextmenu_save_image,
-                R.id.contextmenu_share_image};
+        int[] expected3Tab2 = {R.id.contextmenu_open_image, R.id.contextmenu_copy_image,
+                R.id.contextmenu_save_image, R.id.contextmenu_share_image};
         checkMenuOptions(contextMenuParams, expected3Tab1, expected3Tab2);
 
         initializePopulator(ChromeContextMenuPopulator.ContextMenuMode.WEB_APP);
         int[] expected4Tab1 = {R.id.contextmenu_copy_link_address, R.id.contextmenu_save_link_as,
                 R.id.contextmenu_share_link};
-        int[] expected4Tab2 = {R.id.contextmenu_save_image, R.id.contextmenu_share_image,
-                R.id.contextmenu_open_in_chrome};
+        int[] expected4Tab2 = {R.id.contextmenu_copy_image, R.id.contextmenu_save_image,
+                R.id.contextmenu_share_image, R.id.contextmenu_open_in_chrome};
         checkMenuOptions(contextMenuParams, expected4Tab1, expected4Tab2);
     }
 }
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index a687fa6..de647b6 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1955,6 +1955,9 @@
   <message name="IDS_LOGIN_ERROR_DETACHABLE_BASE_CHANGED" desc="Text for error dialog shown on the login screen when a detachable base different than the base last used by the user is attached.">
         A different keyboard has been connected since you last entered your password. It may be attempting to steal your keystrokes.
       </message>
+  <message name="IDS_LOGIN_ERROR_TPM_UPDATE_REQUIRED" desc="Text for error dialog shown on the login screen when the Trusted Platform Module needs to be updated due to a vulnerability.">
+    An update for the Trusted Platform Module firmware needs to be installed. See <ph name="TPM_FIRMWARE_UPDATE_LINK">https://www.chromium.org/chromium-os/tpm_firmware_update</ph>
+  </message>
   <message name="IDS_LOGIN_PROXY_ERROR_MESSAGE" desc="An offline message shown when a proxy error has appeared.">
     The sign-in page failed to load using the current proxy settings. Please <ph name="GAIA_RELOAD_LINK_START">$1<ex>&gt;a&lt;</ex></ph>try to sign in again<ph name="GAIA_RELOAD_LINK_END">$2<ex>&gt;/a&lt;</ex></ph>, or use different <ph name="PROXY_SETTINGS_LINK_START">$3<ex>&gt;a&lt;</ex></ph>proxy settings<ph name="PROXY_SETTINGS_LINK_END">$4<ex>&gt;/a&lt;</ex></ph>.
   </message>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_ERROR_TPM_UPDATE_REQUIRED.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_ERROR_TPM_UPDATE_REQUIRED.png.sha1
new file mode 100644
index 0000000..a7c1db6
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_ERROR_TPM_UPDATE_REQUIRED.png.sha1
@@ -0,0 +1 @@
+581bc3a9a0051e1bbe94038777af1763df2053ed
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 04a80de4..f6fbd20 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1806,10 +1806,6 @@
      flag_descriptions::kUpdatedCellularActivationUiName,
      flag_descriptions::kUpdatedCellularActivationUiDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kUpdatedCellularActivationUi)},
-    {"use_messages_google_com_domain",
-     flag_descriptions::kUseMessagesGoogleComDomainName,
-     flag_descriptions::kUseMessagesGoogleComDomainDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(chromeos::features::kUseMessagesGoogleComDomain)},
     {"use_messages_staging_url", flag_descriptions::kUseMessagesStagingUrlName,
      flag_descriptions::kUseMessagesStagingUrlDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kUseMessagesStagingUrl)},
@@ -2294,9 +2290,6 @@
     {"mac-v2-gpu-sandbox", flag_descriptions::kMacV2GPUSandboxName,
      flag_descriptions::kMacV2GPUSandboxDescription, kOsMac,
      FEATURE_VALUE_TYPE(features::kMacV2GPUSandbox)},
-    {"mac-views-task-manager", flag_descriptions::kMacViewsTaskManagerName,
-     flag_descriptions::kMacViewsTaskManagerDescription, kOsMac,
-     FEATURE_VALUE_TYPE(features::kViewsTaskManager)},
 #endif  // OS_MACOSX
 #if BUILDFLAG(ENABLE_VR)
     {"webxr", flag_descriptions::kWebXrName,
@@ -2923,13 +2916,6 @@
      kOsAll,
      FEATURE_VALUE_TYPE(omnibox::kSpeculativeServiceWorkerStartOnQueryInput)},
 
-    // NOTE: This feature is generic and marked kOsAll but is used only in
-    // CrOS for AndroidMessagesIntegration feature.
-    {"enable-service-worker-long-running-message",
-     flag_descriptions::kServiceWorkerLongRunningMessageName,
-     flag_descriptions::kServiceWorkerLongRunningMessageDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kServiceWorkerLongRunningMessage)},
-
     {"enable-service-worker-on-ui", flag_descriptions::kServiceWorkerOnUIName,
      flag_descriptions::kServiceWorkerOnUIDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kServiceWorkerOnUI)},
@@ -4684,6 +4670,9 @@
      flag_descriptions::kOmniboxRemoveSuggestionsFromClipboardDescription,
      kOsAndroid,
      FEATURE_VALUE_TYPE(omnibox::kOmniboxRemoveSuggestionsFromClipboard)},
+    {"context-menu-copy-image", flag_descriptions::kContextMenuCopyImageName,
+     flag_descriptions::kContextMenuCopyImageDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kContextMenuCopyImage)},
 #endif  // defined(OS_ANDROID)
 
     {"impulse-scroll-animations",
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 063c2dd..a8d3e01 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -138,6 +138,7 @@
     &kContentIndexingDownloadHome,
     &kContentIndexingNTP,
     &kContentSuggestionsScrollToLoad,
+    &kContextMenuCopyImage,
     &kContextMenuSearchWithGoogleLens,
     &kContextualSearchDefinitions,
     &kContextualSearchLongpressResolve,
@@ -400,6 +401,9 @@
 const base::Feature kContentSuggestionsScrollToLoad{
     "ContentSuggestionsScrollToLoad", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kContextMenuCopyImage{"ContextMenuCopyImage",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kContextMenuSearchWithGoogleLens{
     "ContextMenuSearchWithGoogleLens", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -460,7 +464,7 @@
 // Enable the HomePage Location feature that allows enterprise policy set and
 // force the home page url for managed devices.
 const base::Feature kHomepageLocation{"HomepageLocationPolicy",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
+                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kHomepageSettingsUIConversion{
     "HomepageSettingsUIConversion", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 82b0e8d..a31e391 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -55,6 +55,7 @@
 extern const base::Feature kContactsPickerSelectAll;
 extern const base::Feature kContentIndexingDownloadHome;
 extern const base::Feature kContentIndexingNTP;
+extern const base::Feature kContextMenuCopyImage;
 extern const base::Feature kContextMenuSearchWithGoogleLens;
 extern const base::Feature kContentSuggestionsScrollToLoad;
 extern const base::Feature kContextualSearchDefinitions;
diff --git a/chrome/browser/android/compositor/compositor_view.cc b/chrome/browser/android/compositor/compositor_view.cc
index 7fc148b..c7a0940 100644
--- a/chrome/browser/android/compositor/compositor_view.cc
+++ b/chrome/browser/android/compositor/compositor_view.cc
@@ -15,6 +15,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/containers/id_map.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/rand_util.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/layers/layer.h"
@@ -28,6 +29,7 @@
 #include "chrome/browser/android/compositor/tab_content_manager.h"
 #include "content/public/browser/android/compositor.h"
 #include "content/public/browser/child_process_data.h"
+#include "content/public/browser/peak_gpu_memory_tracker.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/process_type.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -342,4 +344,26 @@
   compositor_->EvictCachedBackBuffer();
 }
 
+void CompositorView::OnTabChanged(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& object) {
+  if (!compositor_)
+    return;
+  std::unique_ptr<content::PeakGpuMemoryTracker> tracker =
+      content::PeakGpuMemoryTracker::Create(
+          base::BindOnce([](uint64_t peak_memory) {
+            // Converting Bytes to Kilobytes.
+            UMA_HISTOGRAM_MEMORY_KB("Memory.GPU.PeakMemoryUsage.ChangeTab",
+                                    peak_memory / 1024u);
+          }));
+  compositor_->RequestPresentationTimeForNextFrame(base::BindOnce(
+      [](std::unique_ptr<content::PeakGpuMemoryTracker> tracker,
+         const gfx::PresentationFeedback& feedback) {
+        // This callback will be ran once the content::Compositor presents the
+        // next frame. The destruction of |tracker| will get the peak GPU memory
+        // and record a histogram.
+      },
+      std::move(tracker)));
+}
+
 }  // namespace android
diff --git a/chrome/browser/android/compositor/compositor_view.h b/chrome/browser/android/compositor/compositor_view.h
index 35f261a..8141c1e 100644
--- a/chrome/browser/android/compositor/compositor_view.h
+++ b/chrome/browser/android/compositor/compositor_view.h
@@ -97,6 +97,8 @@
   void EvictCachedBackBuffer(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& object);
+  void OnTabChanged(JNIEnv* env,
+                    const base::android::JavaParamRef<jobject>& object);
 
   // CompositorClient implementation:
   void RecreateSurface() override;
diff --git a/chrome/browser/android/preferences/website_preference_bridge.cc b/chrome/browser/android/preferences/website_preference_bridge.cc
index 5e91c9c..8e35c20 100644
--- a/chrome/browser/android/preferences/website_preference_bridge.cc
+++ b/chrome/browser/android/preferences/website_preference_bridge.cc
@@ -897,6 +897,34 @@
       url, GURL(), ContentSettingsType::ADS_DATA, std::string(), nullptr);
 }
 
+static void JNI_WebsitePreferenceBridge_GetArOrigins(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& list) {
+  JNI_WebsitePreferenceBridge_GetOrigins(
+      env, ContentSettingsType::AR,
+      &Java_WebsitePreferenceBridge_insertArInfoIntoList, list, false);
+}
+
+static jint JNI_WebsitePreferenceBridge_GetArSettingForOrigin(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& origin,
+    const JavaParamRef<jstring>& embedder,
+    jboolean is_incognito) {
+  return JNI_WebsitePreferenceBridge_GetSettingForOrigin(
+      env, ContentSettingsType::AR, origin, embedder, is_incognito);
+}
+
+static void JNI_WebsitePreferenceBridge_SetArSettingForOrigin(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& origin,
+    const JavaParamRef<jstring>& embedder,
+    jint value,
+    jboolean is_incognito) {
+  JNI_WebsitePreferenceBridge_SetSettingForOrigin(
+      env, ContentSettingsType::AR, origin, embedder,
+      static_cast<ContentSetting>(value), is_incognito);
+}
+
 static void JNI_WebsitePreferenceBridge_GetNfcOrigins(
     JNIEnv* env,
     const JavaParamRef<jobject>& list) {
@@ -953,6 +981,34 @@
       static_cast<ContentSetting>(value), is_incognito);
 }
 
+static void JNI_WebsitePreferenceBridge_GetVrOrigins(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& list) {
+  JNI_WebsitePreferenceBridge_GetOrigins(
+      env, ContentSettingsType::VR,
+      &Java_WebsitePreferenceBridge_insertVrInfoIntoList, list, false);
+}
+
+static jint JNI_WebsitePreferenceBridge_GetVrSettingForOrigin(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& origin,
+    const JavaParamRef<jstring>& embedder,
+    jboolean is_incognito) {
+  return JNI_WebsitePreferenceBridge_GetSettingForOrigin(
+      env, ContentSettingsType::VR, origin, embedder, is_incognito);
+}
+
+static void JNI_WebsitePreferenceBridge_SetVrSettingForOrigin(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& origin,
+    const JavaParamRef<jstring>& embedder,
+    jint value,
+    jboolean is_incognito) {
+  JNI_WebsitePreferenceBridge_SetSettingForOrigin(
+      env, ContentSettingsType::VR, origin, embedder,
+      static_cast<ContentSetting>(value), is_incognito);
+}
+
 // On Android O+ notification channels are not stored in the Chrome profile and
 // so are persisted across tests. This function resets them.
 static void JNI_WebsitePreferenceBridge_ResetNotificationsSettingsForTest(
@@ -1074,6 +1130,14 @@
       static_cast<ContentSetting>(setting));
 }
 
+static jboolean JNI_WebsitePreferenceBridge_GetArEnabled(JNIEnv* env) {
+  return GetBooleanForContentSetting(ContentSettingsType::AR);
+}
+
+static jboolean JNI_WebsitePreferenceBridge_GetVrEnabled(JNIEnv* env) {
+  return GetBooleanForContentSetting(ContentSettingsType::VR);
+}
+
 static jboolean JNI_WebsitePreferenceBridge_GetAcceptCookiesEnabled(
     JNIEnv* env) {
   return GetBooleanForContentSetting(ContentSettingsType::COOKIES);
@@ -1142,6 +1206,15 @@
   return IsContentSettingManagedByCustodian(ContentSettingsType::GEOLOCATION);
 }
 
+static void JNI_WebsitePreferenceBridge_SetArEnabled(JNIEnv* env,
+                                                     jboolean allow) {
+  HostContentSettingsMap* host_content_settings_map =
+      HostContentSettingsMapFactory::GetForProfile(GetOriginalProfile());
+  host_content_settings_map->SetDefaultContentSetting(
+      ContentSettingsType::AR,
+      allow ? CONTENT_SETTING_ASK : CONTENT_SETTING_BLOCK);
+}
+
 static void JNI_WebsitePreferenceBridge_SetClipboardEnabled(JNIEnv* env,
                                                             jboolean allow) {
   HostContentSettingsMap* host_content_settings_map =
@@ -1160,6 +1233,15 @@
       allow ? CONTENT_SETTING_ASK : CONTENT_SETTING_BLOCK);
 }
 
+static void JNI_WebsitePreferenceBridge_SetVrEnabled(JNIEnv* env,
+                                                     jboolean allow) {
+  HostContentSettingsMap* host_content_settings_map =
+      HostContentSettingsMapFactory::GetForProfile(GetOriginalProfile());
+  host_content_settings_map->SetDefaultContentSetting(
+      ContentSettingsType::VR,
+      allow ? CONTENT_SETTING_ASK : CONTENT_SETTING_BLOCK);
+}
+
 static void JNI_WebsitePreferenceBridge_SetSensorsEnabled(JNIEnv* env,
                                                           jboolean allow) {
   HostContentSettingsMap* host_content_settings_map =
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 978ba63..78ed2d9 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4750,10 +4750,9 @@
 
 std::unique_ptr<content::AuthenticatorRequestClientDelegate>
 ChromeContentBrowserClient::GetWebAuthenticationRequestDelegate(
-    content::RenderFrameHost* render_frame_host,
-    const std::string& relying_party_id) {
-  return AuthenticatorRequestScheduler::CreateRequestDelegate(render_frame_host,
-                                                              relying_party_id);
+    content::RenderFrameHost* render_frame_host) {
+  return AuthenticatorRequestScheduler::CreateRequestDelegate(
+      render_frame_host);
 }
 #endif
 
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index af5b783..ffb9d9dc 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -514,8 +514,7 @@
   content::HidDelegate* GetHidDelegate() override;
   std::unique_ptr<content::AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
-      content::RenderFrameHost* render_frame_host,
-      const std::string& relying_party_id) override;
+      content::RenderFrameHost* render_frame_host) override;
 #endif
   bool ShowPaymentHandlerWindow(
       content::BrowserContext* browser_context,
diff --git a/chrome/browser/chromeos/android_sms/android_sms_urls.cc b/chrome/browser/chromeos/android_sms/android_sms_urls.cc
index 9b26907..d930dc2 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_urls.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_urls.cc
@@ -49,10 +49,7 @@
   if (base::FeatureList::IsEnabled(features::kUseMessagesStagingUrl))
     return PwaDomain::kStaging;
 
-  if (base::FeatureList::IsEnabled(features::kUseMessagesGoogleComDomain))
-    return PwaDomain::kProdGoogle;
-
-  return PwaDomain::kProdAndroid;
+  return PwaDomain::kProdGoogle;
 }
 
 GURL GetAndroidMessagesURL(bool use_install_url, PwaDomain pwa_domain) {
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc
index 7c7150e..26abc84c 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -898,6 +898,8 @@
         base::TimeDelta::FromMilliseconds(kSafeModeRestartUiDelayMs));
   } else if (failure.reason() == AuthFailure::TPM_ERROR) {
     ShowTPMError();
+  } else if (failure.reason() == AuthFailure::TPM_UPDATE_REQUIRED) {
+    ShowError(IDS_LOGIN_ERROR_TPM_UPDATE_REQUIRED, error);
   } else if (last_login_attempt_account_id_ == user_manager::GuestAccountId()) {
     // Show no errors, just re-enable input.
     GetLoginDisplay()->ClearAndEnablePassword();
diff --git a/chrome/browser/chromeos/login/lock/screen_locker.cc b/chrome/browser/chromeos/login/lock/screen_locker.cc
index be94995..eafb7bf5 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
 
+#include <algorithm>
+
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/login_screen.h"
 #include "ash/public/cpp/login_screen_model.h"
@@ -555,6 +557,17 @@
   delegate_->ShowErrorMessage(error_msg_id, help_topic_id);
 }
 
+user_manager::UserList ScreenLocker::GetUsersToShow() const {
+  user_manager::UserList users_to_show;
+  // Filter out Managed Guest Session users as they should not appear on the UI.
+  std::copy_if(users_.begin(), users_.end(), std::back_inserter(users_to_show),
+               [](const user_manager::User* user) -> bool {
+                 return user->GetType() !=
+                        user_manager::UserType::USER_TYPE_PUBLIC_ACCOUNT;
+               });
+  return users_to_show;
+}
+
 void ScreenLocker::SetLoginStatusConsumer(
     chromeos::AuthStatusConsumer* consumer) {
   auth_status_consumer_ = consumer;
diff --git a/chrome/browser/chromeos/login/lock/screen_locker.h b/chrome/browser/chromeos/login/lock/screen_locker.h
index 8a64a3c..5715213 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker.h
+++ b/chrome/browser/chromeos/login/lock/screen_locker.h
@@ -130,6 +130,10 @@
   // Returns the users to authenticate.
   const user_manager::UserList& users() const { return users_; }
 
+  // Returns the users to show on the lock screen UI. Will be a subset of
+  // |users()|.
+  user_manager::UserList GetUsersToShow() const;
+
   // Allow a AuthStatusConsumer to listen for
   // the same login events that ScreenLocker does.
   void SetLoginStatusConsumer(chromeos::AuthStatusConsumer* consumer);
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_unittest.cc b/chrome/browser/chromeos/login/lock/screen_locker_unittest.cc
index bc43479..6fce8a0d 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker_unittest.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker_unittest.cc
@@ -8,9 +8,12 @@
 
 #include "ash/public/cpp/login_screen_model.h"
 #include "ash/public/cpp/login_types.h"
+#include "base/bind.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service.h"
+#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h"
 #include "chrome/browser/chromeos/input_method/mock_input_method_manager_impl.h"
 #include "chrome/browser/chromeos/lock_screen_apps/state_controller.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
@@ -23,6 +26,7 @@
 #include "chrome/browser/ui/ash/session_controller_client_impl.h"
 #include "chrome/browser/ui/ash/test_login_screen.h"
 #include "chrome/browser/ui/ash/test_session_controller.h"
+#include "chrome/common/chrome_constants.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/audio/cras_audio_handler.h"
@@ -36,6 +40,7 @@
 #include "chromeos/system/fake_statistics_provider.h"
 #include "chromeos/tpm/stub_install_attributes.h"
 #include "components/account_id/account_id.h"
+#include "components/keyed_service/core/keyed_service.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "content/public/browser/audio_service.h"
@@ -49,6 +54,15 @@
 
 namespace chromeos {
 
+namespace {
+
+std::unique_ptr<KeyedService> CreateCertificateProviderService(
+    content::BrowserContext* context) {
+  return std::make_unique<chromeos::CertificateProviderService>();
+}
+
+}  // namespace
+
 class ScreenLockerUnitTest : public testing::Test {
  public:
   ScreenLockerUnitTest() = default;
@@ -66,6 +80,12 @@
     // Initialize SessionControllerClientImpl and dependencies:
     LoginState::Initialize();
     CHECK(testing_profile_manager_.SetUp());
+
+    // Set up certificate provider service for the signin profile.
+    CertificateProviderServiceFactory::GetInstance()->SetTestingFactory(
+        testing_profile_manager_.CreateTestingProfile(chrome::kInitialProfile),
+        base::BindRepeating(&CreateCertificateProviderService));
+
     session_controller_client_ =
         std::make_unique<SessionControllerClientImpl>();
     session_controller_client_->Init();
@@ -82,14 +102,21 @@
         // Owned by InputMethodManager
         new input_method::MockInputMethodManagerImpl());
     CrasAudioHandler::InitializeForTesting();
-    chromeos::AccessibilityManager::Initialize();
+    AccessibilityManager::Initialize();
 
     // Initialize ScreenLocker dependencies:
     chromeos::ProfileHelper::GetSigninProfile();
     SystemSaltGetter::Initialize();
+  }
+
+  void CreateSessionForUser(bool is_public_account) {
     const AccountId account_id =
         AccountId::FromUserEmail("testemail@example.com");
-    fake_user_manager_->AddUser(account_id);
+    if (is_public_account) {
+      fake_user_manager_->AddPublicAccountUser(account_id);
+    } else {
+      fake_user_manager_->AddUser(account_id);
+    }
     fake_user_manager_->LoginUser(account_id);
     CHECK(user_manager::UserManager::Get()->GetPrimaryUser());
     session_manager::SessionManager::Get()->CreateSession(
@@ -97,16 +124,21 @@
   }
 
   void TearDown() override {
+    SystemSaltGetter::Shutdown();
+    AccessibilityManager::Shutdown();
+    CrasAudioHandler::Shutdown();
     input_method::InputMethodManager::Shutdown();
     audio::SoundsManager::Shutdown();
+    audio::AudioStreamHandler::SetObserverForTesting(nullptr);
+    observer_.reset();
     assistant_client_.reset();
     session_controller_client_.reset();
     LoginState::Shutdown();
+    bluez::BluezDBusManager::Shutdown();
     CryptohomeClient::Shutdown();
+    CrasAudioClient::Shutdown();
     BiodClient::Shutdown();
     DBusThreadManager::Shutdown();
-    audio::AudioStreamHandler::SetObserverForTesting(NULL);
-    observer_.reset();
   }
 
  protected:
@@ -147,6 +179,8 @@
 // Chrome notifies Ash when screen is locked. Ash is responsible for suspending
 // the device.
 TEST_F(ScreenLockerUnitTest, VerifyAshIsNotifiedOfScreenLocked) {
+  CreateSessionForUser(/*is_public_account=*/false);
+
   EXPECT_EQ(0, test_session_controller_.lock_animation_complete_call_count());
   ScreenLocker::Show();
   base::RunLoop().RunUntilIdle();
@@ -156,4 +190,17 @@
   base::RunLoop().RunUntilIdle();
 }
 
+// Tests that |GetUsersToShow()| returns an empty list when the user is a
+// Managed Guest Session.
+TEST_F(ScreenLockerUnitTest, GetUsersToShow) {
+  CreateSessionForUser(/*is_public_account=*/true);
+
+  ScreenLocker::Show();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(ScreenLocker::default_screen_locker()->GetUsersToShow().empty());
+  ScreenLocker::Hide();
+  // Needed to perform internal cleanup scheduled in ScreenLocker::Hide()
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/lock/views_screen_locker.cc b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
index 6f276365..69b2b41 100644
--- a/chrome/browser/chromeos/login/lock/views_screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
@@ -100,7 +100,7 @@
 
 void ViewsScreenLocker::Init() {
   lock_time_ = base::TimeTicks::Now();
-  user_selection_screen_->Init(screen_locker_->users());
+  user_selection_screen_->Init(screen_locker_->GetUsersToShow());
   if (!ime_state_.get())
     ime_state_ = input_method::InputMethodManager::Get()->GetActiveIMEState();
 
diff --git a/chrome/browser/chromeos/login/ui/login_display_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_mojo.cc
index a286fa6..7c0088b0 100644
--- a/chrome/browser/chromeos/login/ui/login_display_mojo.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_mojo.cc
@@ -169,7 +169,8 @@
       error_msg_id != IDS_ENTERPRISE_LOGIN_ERROR_WHITELIST &&
       error_msg_id != IDS_LOGIN_ERROR_OWNER_KEY_LOST &&
       error_msg_id != IDS_LOGIN_ERROR_OWNER_REQUIRED &&
-      error_msg_id != IDS_LOGIN_ERROR_GOOGLE_ACCOUNT_NOT_ALLOWED) {
+      error_msg_id != IDS_LOGIN_ERROR_GOOGLE_ACCOUNT_NOT_ALLOWED &&
+      error_msg_id != IDS_LOGIN_ERROR_TPM_UPDATE_REQUIRED) {
     input_method::InputMethodManager* ime_manager =
         input_method::InputMethodManager::Get();
     // Display a hint to switch keyboards if there are other active input
diff --git a/chrome/browser/chromeos/login/ui/login_display_webui.cc b/chrome/browser/chromeos/login/ui/login_display_webui.cc
index f0334481..7cb0286 100644
--- a/chrome/browser/chromeos/login/ui/login_display_webui.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_webui.cc
@@ -129,7 +129,8 @@
       error_msg_id != IDS_ENTERPRISE_LOGIN_ERROR_WHITELIST &&
       error_msg_id != IDS_LOGIN_ERROR_OWNER_KEY_LOST &&
       error_msg_id != IDS_LOGIN_ERROR_OWNER_REQUIRED &&
-      error_msg_id != IDS_LOGIN_ERROR_GOOGLE_ACCOUNT_NOT_ALLOWED) {
+      error_msg_id != IDS_LOGIN_ERROR_GOOGLE_ACCOUNT_NOT_ALLOWED &&
+      error_msg_id != IDS_LOGIN_ERROR_TPM_UPDATE_REQUIRED) {
     input_method::InputMethodManager* ime_manager =
         input_method::InputMethodManager::Get();
 
diff --git a/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.cc b/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.cc
index 0ff7924f..b99732d 100644
--- a/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.cc
+++ b/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.cc
@@ -11,6 +11,7 @@
 #include "base/files/file_util.h"
 #include "base/files/important_file_writer.h"
 #include "base/location.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/task_runner_util.h"
@@ -48,12 +49,33 @@
 constexpr char kSavedFileName[] = "past_charging_events.pb";
 constexpr char kSavedDir[] = "smartcharging";
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class SmartChargingMessage {
+  kSerializeProtoError = 0,
+  kWriteFileError = 1,
+  kWriteFileSuccess = 2,
+  kReadFileError = 3,
+  kParseProtoError = 4,
+  kReadFileSuccess = 5,
+  kGetPrimaryProfileError = 6,
+  kCreateFolderError = 7,
+  kCreatePathSuccess = 8,
+  kPathDoesntExists = 9,
+  kGetPathSuccess = 10,
+  kMaxValue = kGetPathSuccess
+};
+
+void LogSmartChargingMessage(SmartChargingMessage message) {
+  UMA_HISTOGRAM_ENUMERATION("Power.SmartCharging.Messages", message);
+}
+
 // Given a proto and file path, writes to disk and logs the error(if any).
 void WriteProtoToDisk(const PastChargingEvents& proto,
                       const base::FilePath& file_path) {
   std::string proto_string;
   if (!proto.SerializeToString(&proto_string)) {
-    // TODO(crbug.com/1028853): adds a UMA log here.
+    LogSmartChargingMessage(SmartChargingMessage::kSerializeProtoError);
     return;
   }
   bool write_result;
@@ -65,10 +87,10 @@
   }
 
   if (!write_result) {
-    // TODO(crbug.com/1028853): adds a UMA log here.
+    LogSmartChargingMessage(SmartChargingMessage::kWriteFileError);
     return;
   }
-  // TODO(crbug.com/1028853): adds a UMA log here.
+  LogSmartChargingMessage(SmartChargingMessage::kWriteFileSuccess);
 }
 
 // Reads a proto from a file path.
@@ -77,16 +99,16 @@
                                                 base::BlockingType::MAY_BLOCK);
   std::string proto_str;
   if (!base::ReadFileToString(file_path, &proto_str)) {
-    // TODO(crbug.com/1028853): adds a UMA log here.
+    LogSmartChargingMessage(SmartChargingMessage::kReadFileError);
     return nullptr;
   }
 
   auto proto = std::make_unique<PastChargingEvents>();
   if (!proto->ParseFromString(proto_str)) {
-    // TODO(crbug.com/1028853): adds a UMA log here.
+    LogSmartChargingMessage(SmartChargingMessage::kParseProtoError);
     return nullptr;
   }
-  // TODO(crbug.com/1028853): adds a UMA log here.
+  LogSmartChargingMessage(SmartChargingMessage::kReadFileSuccess);
   return proto;
 }
 
@@ -100,15 +122,15 @@
   const base::FilePath path = profile_path.AppendASCII(kSavedDir);
   if (create_new_path) {
     if (!base::DirectoryExists(path) && !base::CreateDirectory(path)) {
-      // TODO(crbug.com/1028853): adds a UMA log here.
+      LogSmartChargingMessage(SmartChargingMessage::kCreateFolderError);
       return false;
     }
   } else if (!base::PathExists(path.AppendASCII(kSavedFileName))) {
-    // TODO(crbug.com/1028853): adds a UMA log here.
+    LogSmartChargingMessage(SmartChargingMessage::kPathDoesntExists);
     return false;
   }
-  // TODO(crbug.com/1028853): adds a UMA log here.
   *file_path = path.AppendASCII(kSavedFileName);
+  LogSmartChargingMessage(SmartChargingMessage::kGetPathSuccess);
   return true;
 }
 
@@ -331,7 +353,7 @@
   if (loaded_from_disk_)
     return;
   if (!ProfileManager::GetPrimaryUserProfile()) {
-    // TODO(crbug.com/1028853): adds a UMA log here.
+    LogSmartChargingMessage(SmartChargingMessage::kGetPrimaryProfileError);
     return;
   }
   profile_path_ = ProfileManager::GetPrimaryUserProfile()->GetPath();
@@ -438,7 +460,7 @@
   if (profile_path_.has_value()) {
     MaybeSaveToDisk(profile_path_.value());
   } else {
-    // TODO(crbug.com/1028853): adds a UMA log here.
+    LogSmartChargingMessage(SmartChargingMessage::kGetPrimaryProfileError);
   }
 }
 
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc
index c18e670..3278daa 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/services/cros_healthd/public/cpp/service_connection.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
@@ -373,4 +374,10 @@
   LOG(ERROR) << "Unrecognized event " << event << " event";
 }
 
+void WilcoDtcSupportdBridge::GetCrosHealthdDiagnosticsService(
+    cros_healthd::mojom::CrosHealthdDiagnosticsServiceRequest service) {
+  cros_healthd::ServiceConnection::GetInstance()->GetDiagnosticsService(
+      std::move(service));
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.h b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.h
index 242d70b7..4140eb6 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.h
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.h
@@ -116,6 +116,9 @@
   void GetConfigurationData(GetConfigurationDataCallback callback) override;
   void HandleEvent(
       wilco_dtc_supportd::mojom::WilcoDtcSupportdEvent event) override;
+  void GetCrosHealthdDiagnosticsService(
+      cros_healthd::mojom::CrosHealthdDiagnosticsServiceRequest service)
+      override;
 
   std::unique_ptr<Delegate> delegate_;
 
diff --git a/chrome/browser/download/download_frame_policy_browsertest.cc b/chrome/browser/download/download_frame_policy_browsertest.cc
index 52efe9b1..70d04071 100644
--- a/chrome/browser/download/download_frame_policy_browsertest.cc
+++ b/chrome/browser/download/download_frame_policy_browsertest.cc
@@ -88,10 +88,10 @@
 }
 
 const char kSandboxTokensDisallowDownloads[] =
-    "'allow-scripts allow-same-origin allow-top-navigation allow-popups'";
+    "allow-scripts allow-same-origin allow-top-navigation allow-popups";
 const char kSandboxTokensAllowDownloads[] =
-    "'allow-scripts allow-same-origin allow-top-navigation allow-popups "
-    "allow-downloads'";
+    "allow-scripts allow-same-origin allow-top-navigation allow-popups "
+    "allow-downloads";
 
 // Allow PageLoadMetricsTestWaiter to be initialized for a new web content
 // before the first commit.
@@ -186,24 +186,26 @@
     ui_test_utils::NavigateToURL(browser(), top_frame_url);
 
     const char* method = is_ad_frame ? "createAdFrame" : "createFrame";
-    std::string subframe_url =
-        embedded_test_server()
-            ->GetURL(is_cross_origin ? "bar.com" : host_name,
-                     "/frame_factory.html")
-            .spec();
-    std::string sandbox_param =
-        sandbox_option == SandboxOption::kNotSandboxed
-            ? "undefined"
-            : sandbox_option == SandboxOption::kDisallowDownloads
-                  ? kSandboxTokensDisallowDownloads
-                  : kSandboxTokensAllowDownloads;
-    std::string script =
-        base::StringPrintf("%s('%s','%s',%s);", method, subframe_url.c_str(),
-                           GetSubframeId().c_str(), sandbox_param.c_str());
+    GURL subframe_url = embedded_test_server()->GetURL(
+        is_cross_origin ? "bar.com" : host_name, "/frame_factory.html");
+
+    std::string script;
+    if (sandbox_option == SandboxOption::kNotSandboxed) {
+      script = content::JsReplace("window[$1]($2, $3)", method, subframe_url,
+                                  GetSubframeId());
+    } else {
+      const char* sandbox_token =
+          (sandbox_option == SandboxOption::kDisallowDownloads)
+              ? kSandboxTokensDisallowDownloads
+              : kSandboxTokensAllowDownloads;
+      script = content::JsReplace("window[$1]($2, $3, $4)", method,
+                                  subframe_url, GetSubframeId(), sandbox_token);
+    }
 
     content::TestNavigationObserver navigation_observer(web_contents());
-    web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
-        base::ASCIIToUTF16(script), base::NullCallback());
+    EXPECT_TRUE(content::ExecJs(web_contents()->GetMainFrame(), script,
+                                content::EXECUTE_SCRIPT_NO_USER_GESTURE));
+
     navigation_observer.Wait();
 
     subframe_rfh_ = content::FrameMatchingPredicate(
@@ -711,6 +713,110 @@
                                          SandboxOption::kDisallowDownloads,
                                          SandboxOption::kAllowDownloads)));
 
+class DownloadFramePolicyBrowserTest_UpdateIframeSandboxFlags
+    : public DownloadFramePolicyBrowserTest,
+      public ::testing::WithParamInterface<
+          std::tuple<bool /* is_cross_origin */,
+                     bool /* from_allow_to_disallow */>> {};
+
+// Test that when the iframe sandbox attribute is updated before navigation,
+// the updated flag will be controlling the navigation-instantiating frame's
+// policy for the download intervention. Given that the feature is disabled by
+// default, the download will occur, but the point of the test is to track the
+// use counter for whether or not the download would have been blocked by the
+// policy.
+IN_PROC_BROWSER_TEST_P(
+    DownloadFramePolicyBrowserTest_UpdateIframeSandboxFlags,
+    PendingSandboxPolicyUsedForNavigationInstantiatingFrame) {
+  bool is_cross_origin;
+  bool from_allow_to_disallow;
+  std::tie(is_cross_origin, from_allow_to_disallow) = GetParam();
+
+  SandboxOption initial_sandbox_option =
+      from_allow_to_disallow ? SandboxOption::kAllowDownloads
+                             : SandboxOption::kDisallowDownloads;
+
+  const char* update_to_token = from_allow_to_disallow
+                                    ? kSandboxTokensDisallowDownloads
+                                    : kSandboxTokensAllowDownloads;
+
+  InitializeHistogramTesterAndWebFeatureWaiter();
+  SetNumDownloadsExpectation(1);
+  InitializeOneSubframeSetup(initial_sandbox_option, false /* is_ad_frame */,
+                             is_cross_origin);
+
+  EXPECT_TRUE(
+      ExecJs(web_contents()->GetMainFrame(),
+             content::JsReplace("document.querySelector('iframe').sandbox = $1",
+                                update_to_token)));
+
+  GURL download_url = embedded_test_server()->GetURL("bar.com", "/allow.zip");
+  content::TestNavigationManager navigation_observer(web_contents(),
+                                                     download_url);
+  EXPECT_TRUE(
+      ExecJs(web_contents()->GetMainFrame(),
+             content::JsReplace("document.querySelector('iframe').src = $1",
+                                download_url)));
+  navigation_observer.WaitForNavigationFinished();
+  EXPECT_TRUE(!navigation_observer.was_successful());
+
+  GetHistogramTester()->ExpectBucketCount(
+      "Blink.UseCounter.Features", blink::mojom::WebFeature::kDownloadInSandbox,
+      from_allow_to_disallow);
+
+  CheckNumDownloadsExpectation();
+}
+
+// Test that when the iframe sandbox attribute is updated before navigation,
+// the updated flag will NOT be controlling the navigation-initiator frame's
+// policy for the download intervention. Given that the feature is disabled by
+// default, the download will occur, but the point of the test is to track the
+// use counter for whether or not the download would have been blocked by the
+// policy.
+IN_PROC_BROWSER_TEST_P(DownloadFramePolicyBrowserTest_UpdateIframeSandboxFlags,
+                       EffectiveSandboxPolicyUsedForNavigationInitiatorFrame) {
+  bool is_cross_origin;
+  bool from_allow_to_disallow;
+  std::tie(is_cross_origin, from_allow_to_disallow) = GetParam();
+
+  SandboxOption initial_sandbox_option =
+      from_allow_to_disallow ? SandboxOption::kAllowDownloads
+                             : SandboxOption::kDisallowDownloads;
+
+  const char* update_to_token = from_allow_to_disallow
+                                    ? kSandboxTokensDisallowDownloads
+                                    : kSandboxTokensAllowDownloads;
+
+  InitializeHistogramTesterAndWebFeatureWaiter();
+  SetNumDownloadsExpectation(1);
+  InitializeOneSubframeSetup(initial_sandbox_option, false /* is_ad_frame */,
+                             is_cross_origin);
+
+  EXPECT_TRUE(
+      ExecJs(web_contents()->GetMainFrame(),
+             content::JsReplace("document.querySelector('iframe').sandbox = $1",
+                                update_to_token)));
+
+  GURL download_url = embedded_test_server()->GetURL("bar.com", "/allow.zip");
+  content::TestNavigationManager navigation_observer(web_contents(),
+                                                     download_url);
+  EXPECT_TRUE(ExecJs(GetSubframeRfh(),
+                     content::JsReplace("top.location = $1", download_url)));
+  navigation_observer.WaitForNavigationFinished();
+  EXPECT_TRUE(!navigation_observer.was_successful());
+
+  GetHistogramTester()->ExpectBucketCount(
+      "Blink.UseCounter.Features", blink::mojom::WebFeature::kDownloadInSandbox,
+      !from_allow_to_disallow);
+
+  CheckNumDownloadsExpectation();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    DownloadFramePolicyBrowserTest_UpdateIframeSandboxFlags,
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+
 // Download gets blocked when LoadPolicy is DISALLOW for the navigation to
 // download. This test is technically unrelated to policy on frame, but stays
 // here for convenience.
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc
index 696fb4b6..202bceee 100644
--- a/chrome/browser/download/download_ui_model.cc
+++ b/chrome/browser/download/download_ui_model.cc
@@ -281,7 +281,7 @@
       request_ap_verdicts =
           safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
               profile())
-              ->RequestsAdvancedProtectionVerdicts();
+              ->IsUnderAdvancedProtection();
 #endif
       return l10n_util::GetStringFUTF16(
           request_ap_verdicts
diff --git a/chrome/browser/download/notification/download_item_notification.cc b/chrome/browser/download/notification/download_item_notification.cc
index 8b2644a..380a30a7 100644
--- a/chrome/browser/download/notification/download_item_notification.cc
+++ b/chrome/browser/download/notification/download_item_notification.cc
@@ -728,7 +728,7 @@
       bool requests_ap_verdicts =
           safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
               profile())
-              ->RequestsAdvancedProtectionVerdicts();
+              ->IsUnderAdvancedProtection();
       return l10n_util::GetStringFUTF16(
           requests_ap_verdicts
               ? IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT_IN_ADVANCED_PROTECTION
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index d155f74..3293581c 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -181,6 +181,8 @@
     "api/i18n/i18n_api.h",
     "api/identity/extension_token_key.cc",
     "api/identity/extension_token_key.h",
+    "api/identity/gaia_remote_consent_flow.cc",
+    "api/identity/gaia_remote_consent_flow.h",
     "api/identity/gaia_web_auth_flow.cc",
     "api/identity/gaia_web_auth_flow.h",
     "api/identity/identity_api.cc",
diff --git a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc
new file mode 100644
index 0000000..09da31f5
--- /dev/null
+++ b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc
@@ -0,0 +1,152 @@
+// 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/extensions/api/identity/gaia_remote_consent_flow.h"
+
+#include "base/bind.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/extensions/api/identity/identity_api.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "components/signin/public/base/multilogin_parameters.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h"
+#include "content/public/browser/storage_partition.h"
+#include "google_apis/gaia/gaia_auth_fetcher.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "net/base/escape.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
+#include "url/gurl.h"
+#include "url/url_constants.h"
+
+namespace extensions {
+
+GaiaRemoteConsentFlow::Delegate::~Delegate() = default;
+
+GaiaRemoteConsentFlow::GaiaRemoteConsentFlow(
+    Delegate* delegate,
+    Profile* profile,
+    const ExtensionTokenKey& token_key,
+    const RemoteConsentResolutionData& resolution_data)
+    : delegate_(delegate),
+      profile_(profile),
+      account_id_(token_key.account_id),
+      resolution_data_(resolution_data) {}
+
+GaiaRemoteConsentFlow::~GaiaRemoteConsentFlow() {
+  if (web_flow_)
+    web_flow_.release()->DetachDelegateAndDelete();
+}
+
+void GaiaRemoteConsentFlow::Start() {
+  if (!web_flow_) {
+    web_flow_ = std::make_unique<WebAuthFlow>(
+        this, profile_, resolution_data_.url, WebAuthFlow::INTERACTIVE);
+  }
+
+  auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
+  std::vector<CoreAccountId> accounts;
+  auto chrome_accounts_with_refresh_tokens =
+      identity_manager->GetAccountsWithRefreshTokens();
+  for (const auto& chrome_account : chrome_accounts_with_refresh_tokens) {
+    // An account in persistent error state would make multilogin fail. Showing
+    // only a subset of accounts seems to be a better alternative than failing
+    // with an error.
+    if (identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
+            chrome_account.account_id)) {
+      continue;
+    }
+    accounts.push_back(chrome_account.account_id);
+  }
+
+  set_accounts_in_cookie_task_ =
+      identity_manager->GetAccountsCookieMutator()
+          ->SetAccountsInCookieForPartition(
+              this,
+              {gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
+               accounts},
+              base::BindOnce(&GaiaRemoteConsentFlow::OnSetAccountsComplete,
+                             base::Unretained(this)));
+}
+
+void GaiaRemoteConsentFlow::OnSetAccountsComplete(
+    signin::SetAccountsInCookieResult result) {
+  if (result != signin::SetAccountsInCookieResult::kSuccess) {
+    delegate_->OnGaiaRemoteConsentFlowFailure(
+        GaiaRemoteConsentFlow::Failure::SET_ACCOUNTS_IN_COOKIE_FAILED);
+    return;
+  }
+
+  network::mojom::CookieManager* cookie_manager =
+      GetCookieManagerForPartition();
+  net::CookieOptions options;
+  for (const auto& cookie : resolution_data_.cookies) {
+    cookie_manager->SetCanonicalCookie(
+        cookie, url::kHttpsScheme, options,
+        network::mojom::CookieManager::SetCanonicalCookieCallback());
+  }
+
+  identity_api_set_consent_result_subscription_ =
+      IdentityAPI::GetFactoryInstance()
+          ->Get(profile_)
+          ->RegisterOnSetConsentResultCallback(
+              base::Bind(&GaiaRemoteConsentFlow::OnConsentResultSet,
+                         base::Unretained(this)));
+
+  set_accounts_in_cookie_task_.reset();
+  web_flow_->Start();
+}
+
+void GaiaRemoteConsentFlow::OnConsentResultSet(
+    const std::string& consent_result,
+    const std::string& window_id) {
+  if (!web_flow_ || window_id != web_flow_->GetAppWindowKey())
+    return;
+
+  identity_api_set_consent_result_subscription_.reset();
+  // TODO(crbug.com/1026237): parse consent result to check whether the consent
+  // was approved.
+  delegate_->OnGaiaRemoteConsentFlowCompleted(consent_result);
+}
+
+void GaiaRemoteConsentFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) {
+  GaiaRemoteConsentFlow::Failure gaia_failure;
+
+  switch (failure) {
+    case WebAuthFlow::WINDOW_CLOSED:
+      gaia_failure = GaiaRemoteConsentFlow::WINDOW_CLOSED;
+      break;
+    case WebAuthFlow::LOAD_FAILED:
+      gaia_failure = GaiaRemoteConsentFlow::LOAD_FAILED;
+      break;
+    case WebAuthFlow::INTERACTION_REQUIRED:
+      NOTREACHED() << "Unexpected error from web auth flow: " << failure;
+      gaia_failure = GaiaRemoteConsentFlow::LOAD_FAILED;
+      break;
+  }
+
+  delegate_->OnGaiaRemoteConsentFlowFailure(gaia_failure);
+}
+
+std::unique_ptr<GaiaAuthFetcher>
+GaiaRemoteConsentFlow::CreateGaiaAuthFetcherForPartition(
+    GaiaAuthConsumer* consumer) {
+  return std::make_unique<GaiaAuthFetcher>(
+      consumer, gaia::GaiaSource::kChrome,
+      web_flow_->GetGuestPartition()->GetURLLoaderFactoryForBrowserProcess());
+}
+
+network::mojom::CookieManager*
+GaiaRemoteConsentFlow::GetCookieManagerForPartition() {
+  return web_flow_->GetGuestPartition()->GetCookieManagerForBrowserProcess();
+}
+
+void GaiaRemoteConsentFlow::SetWebAuthFlowForTesting(
+    std::unique_ptr<WebAuthFlow> web_auth_flow) {
+  if (web_flow_)
+    web_flow_.release()->DetachDelegateAndDelete();
+  web_flow_ = std::move(web_auth_flow);
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h
new file mode 100644
index 0000000..85292dec
--- /dev/null
+++ b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h
@@ -0,0 +1,85 @@
+// 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_EXTENSIONS_API_IDENTITY_GAIA_REMOTE_CONSENT_FLOW_H_
+#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_GAIA_REMOTE_CONSENT_FLOW_H_
+
+#include "base/callback_list.h"
+
+#include "base/macros.h"
+#include "chrome/browser/extensions/api/identity/extension_token_key.h"
+#include "chrome/browser/extensions/api/identity/web_auth_flow.h"
+#include "components/signin/public/identity_manager/accounts_cookie_mutator.h"
+#include "google_apis/gaia/core_account_id.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_mint_token_flow.h"
+
+namespace extensions {
+
+class GaiaRemoteConsentFlow
+    : public WebAuthFlow::Delegate,
+      public signin::AccountsCookieMutator::PartitionDelegate {
+ public:
+  enum Failure {
+    WINDOW_CLOSED,
+    LOAD_FAILED,
+    SET_ACCOUNTS_IN_COOKIE_FAILED,
+  };
+
+  class Delegate {
+   public:
+    virtual ~Delegate();
+    // Called when the flow fails prior to the ConsentResult returned from
+    // JavaScript.
+    virtual void OnGaiaRemoteConsentFlowFailure(Failure failure) = 0;
+    // Called when the OAuth2 flow completes.
+    virtual void OnGaiaRemoteConsentFlowCompleted(
+        const std::string& consent_result) = 0;
+  };
+
+  GaiaRemoteConsentFlow(Delegate* delegate,
+                        Profile* profile,
+                        const ExtensionTokenKey& token_key,
+                        const RemoteConsentResolutionData& resolution_data);
+  ~GaiaRemoteConsentFlow() override;
+
+  GaiaRemoteConsentFlow(const GaiaRemoteConsentFlow& other) = delete;
+  GaiaRemoteConsentFlow& operator=(const GaiaRemoteConsentFlow& other) = delete;
+
+  // Starts the flow by setting accounts in cookie.
+  void Start();
+
+  // Set accounts in cookie completion callback.
+  void OnSetAccountsComplete(signin::SetAccountsInCookieResult result);
+
+  // setConsentResult() JavaScript callback.
+  void OnConsentResultSet(const std::string& consent_result,
+                          const std::string& window_id);
+
+  // WebAuthFlow::Delegate implementation.
+  void OnAuthFlowFailure(WebAuthFlow::Failure failure) override;
+
+  // AccountsCookieMutator::PartitionDelegate:
+  std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcherForPartition(
+      GaiaAuthConsumer* consumer) override;
+  network::mojom::CookieManager* GetCookieManagerForPartition() override;
+
+  void SetWebAuthFlowForTesting(std::unique_ptr<WebAuthFlow> web_auth_flow);
+
+ private:
+  Delegate* delegate_;
+  Profile* profile_;
+  CoreAccountId account_id_;
+  RemoteConsentResolutionData resolution_data_;
+  std::unique_ptr<WebAuthFlow> web_flow_;
+  std::unique_ptr<signin::AccountsCookieMutator::SetAccountsInCookieTask>
+      set_accounts_in_cookie_task_;
+  std::unique_ptr<base::CallbackList<void(const std::string&,
+                                          const std::string&)>::Subscription>
+      identity_api_set_consent_result_subscription_;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_GAIA_REMOTE_CONSENT_FLOW_H_
diff --git a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc
new file mode 100644
index 0000000..3af1821
--- /dev/null
+++ b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc
@@ -0,0 +1,151 @@
+// 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/extensions/api/identity/gaia_remote_consent_flow.h"
+
+#include <vector>
+
+#include "base/run_loop.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+const char kWindowKey[] = "window_key";
+const char kConsentResult[] = "abcdef";
+
+class FakeWebAuthFlowWithWindowKey : public WebAuthFlow {
+ public:
+  explicit FakeWebAuthFlowWithWindowKey(WebAuthFlow::Delegate* delegate,
+                                        std::string window_key)
+      : WebAuthFlow(delegate, nullptr, GURL(), WebAuthFlow::INTERACTIVE),
+        fake_window_key_(window_key) {}
+
+  ~FakeWebAuthFlowWithWindowKey() override = default;
+
+  void Start() override {}
+
+  const std::string& GetAppWindowKey() const override {
+    return fake_window_key_;
+  }
+
+ private:
+  const std::string fake_window_key_;
+};
+
+class TestGaiaRemoteConsentFlow : public GaiaRemoteConsentFlow {
+ public:
+  TestGaiaRemoteConsentFlow(GaiaRemoteConsentFlow::Delegate* delegate,
+                            const ExtensionTokenKey& token_key,
+                            const RemoteConsentResolutionData& resolution_data,
+                            const std::string& window_key)
+      : GaiaRemoteConsentFlow(delegate, nullptr, token_key, resolution_data),
+        window_key_(window_key) {
+    SetWebAuthFlowForTesting(
+        std::make_unique<FakeWebAuthFlowWithWindowKey>(this, window_key_));
+  }
+
+ private:
+  const std::string window_key_;
+};
+
+class MockGaiaRemoteConsentFlowDelegate
+    : public GaiaRemoteConsentFlow::Delegate {
+ public:
+  MOCK_METHOD1(OnGaiaRemoteConsentFlowFailure,
+               void(GaiaRemoteConsentFlow::Failure failure));
+  MOCK_METHOD1(OnGaiaRemoteConsentFlowCompleted,
+               void(const std::string& consent_result));
+};
+
+class IdentityGaiaRemoteConsentFlowTest : public testing::Test {
+ public:
+  IdentityGaiaRemoteConsentFlowTest() = default;
+
+  void TearDown() override {
+    testing::Test::TearDown();
+    base::RunLoop()
+        .RunUntilIdle();  // Run tasks so all FakeWebAuthFlowWithWindowKey get
+                          // deleted.
+  }
+
+  std::unique_ptr<TestGaiaRemoteConsentFlow> CreateTestFlow(
+      const std::string& window_key) {
+    return CreateTestFlow(window_key, &delegate_);
+  }
+
+  std::unique_ptr<TestGaiaRemoteConsentFlow> CreateTestFlow(
+      const std::string& window_key,
+      GaiaRemoteConsentFlow::Delegate* delegate) {
+    ExtensionTokenKey token_key("extension_id", CoreAccountId("account_id"),
+                                std::set<std::string>());
+    RemoteConsentResolutionData resolution_data;
+    resolution_data.url = GURL("https://example.com/auth/");
+    return std::unique_ptr<TestGaiaRemoteConsentFlow>(
+        new TestGaiaRemoteConsentFlow(delegate, token_key, resolution_data,
+                                      window_key));
+  }
+
+ protected:
+  base::test::TaskEnvironment task_env_;
+  testing::StrictMock<MockGaiaRemoteConsentFlowDelegate> delegate_;
+};
+
+TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult) {
+  std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
+  EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowCompleted(kConsentResult));
+  flow->OnConsentResultSet(kConsentResult, kWindowKey);
+}
+
+TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult_WrongWindowIgnored) {
+  std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
+  // No call is expected.
+  flow->OnConsentResultSet(kConsentResult, "another_window_key");
+}
+
+TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult_TwoWindows) {
+  std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
+  const char kWindowKey2[] = "window_key2";
+  testing::StrictMock<MockGaiaRemoteConsentFlowDelegate> delegate2;
+  std::unique_ptr<TestGaiaRemoteConsentFlow> flow2 =
+      CreateTestFlow(kWindowKey2, &delegate2);
+
+  const char kConsentResult2[] = "ghijkl";
+  EXPECT_CALL(delegate2, OnGaiaRemoteConsentFlowCompleted(kConsentResult2));
+  flow2->OnConsentResultSet(kConsentResult2, kWindowKey2);
+
+  EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowCompleted(kConsentResult));
+  flow->OnConsentResultSet(kConsentResult, kWindowKey);
+}
+
+TEST_F(IdentityGaiaRemoteConsentFlowTest, SetAccountsFailure) {
+  std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
+  EXPECT_CALL(
+      delegate_,
+      OnGaiaRemoteConsentFlowFailure(
+          GaiaRemoteConsentFlow::Failure::SET_ACCOUNTS_IN_COOKIE_FAILED));
+  flow->OnSetAccountsComplete(
+      signin::SetAccountsInCookieResult::kPersistentError);
+}
+
+TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_WindowClosed) {
+  std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
+  EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailure(
+                             GaiaRemoteConsentFlow::Failure::WINDOW_CLOSED));
+  flow->OnAuthFlowFailure(WebAuthFlow::Failure::WINDOW_CLOSED);
+}
+
+TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_LoadFailed) {
+  std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
+  EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailure(
+                             GaiaRemoteConsentFlow::Failure::LOAD_FAILED));
+  flow->OnAuthFlowFailure(WebAuthFlow::Failure::LOAD_FAILED);
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/identity/identity_api.cc b/chrome/browser/extensions/api/identity/identity_api.cc
index 2f46064..7a5daadb 100644
--- a/chrome/browser/extensions/api/identity/identity_api.cc
+++ b/chrome/browser/extensions/api/identity/identity_api.cc
@@ -149,6 +149,18 @@
   return token_cache_;
 }
 
+void IdentityAPI::SetConsentResult(const std::string& result,
+                                   const std::string& window_id) {
+  on_set_consent_result_callback_list_.Notify(result, window_id);
+}
+
+std::unique_ptr<
+    base::CallbackList<IdentityAPI::OnSetConsentResultSignature>::Subscription>
+IdentityAPI::RegisterOnSetConsentResultCallback(
+    const base::RepeatingCallback<OnSetConsentResultSignature>& callback) {
+  return on_set_consent_result_callback_list_.Add(callback);
+}
+
 void IdentityAPI::Shutdown() {
   on_shutdown_callback_list_.Notify();
   IdentityManagerFactory::GetForProfile(profile_)->RemoveObserver(this);
@@ -162,6 +174,11 @@
   return g_identity_api_factory.Pointer();
 }
 
+std::unique_ptr<base::CallbackList<void()>::Subscription>
+IdentityAPI::RegisterOnShutdownCallback(const base::Closure& cb) {
+  return on_shutdown_callback_list_.Add(cb);
+}
+
 bool IdentityAPI::AreExtensionsRestrictedToPrimaryAccount() {
   return !AccountConsistencyModeManager::IsDiceEnabledForProfile(profile_);
 }
diff --git a/chrome/browser/extensions/api/identity/identity_api.h b/chrome/browser/extensions/api/identity/identity_api.h
index acfe93a..5b8ff1b 100644
--- a/chrome/browser/extensions/api/identity/identity_api.h
+++ b/chrome/browser/extensions/api/identity/identity_api.h
@@ -81,7 +81,9 @@
 class IdentityAPI : public BrowserContextKeyedAPI,
                     public signin::IdentityManager::Observer {
  public:
-  typedef std::map<ExtensionTokenKey, IdentityTokenCacheValue> CachedTokens;
+  using CachedTokens = std::map<ExtensionTokenKey, IdentityTokenCacheValue>;
+  using OnSetConsentResultSignature = void(const std::string&,
+                                           const std::string&);
 
   explicit IdentityAPI(content::BrowserContext* context);
   ~IdentityAPI() override;
@@ -99,14 +101,19 @@
 
   const CachedTokens& GetAllCachedTokens();
 
+  // Consent result.
+  void SetConsentResult(const std::string& result,
+                        const std::string& window_id);
+  std::unique_ptr<base::CallbackList<OnSetConsentResultSignature>::Subscription>
+  RegisterOnSetConsentResultCallback(
+      const base::RepeatingCallback<OnSetConsentResultSignature>& callback);
+
   // BrowserContextKeyedAPI:
   void Shutdown() override;
   static BrowserContextKeyedAPIFactory<IdentityAPI>* GetFactoryInstance();
 
   std::unique_ptr<base::CallbackList<void()>::Subscription>
-  RegisterOnShutdownCallback(const base::Closure& cb) {
-    return on_shutdown_callback_list_.Add(cb);
-  }
+  RegisterOnShutdownCallback(const base::Closure& cb);
 
   // Callback that is used in testing contexts to test the implementation of
   // the chrome.identity.onSignInChanged event. Note that the passed-in Event is
@@ -146,6 +153,8 @@
 
   OnSignInChangedCallback on_signin_changed_callback_for_testing_;
 
+  base::CallbackList<OnSetConsentResultSignature>
+      on_set_consent_result_callback_list_;
   base::CallbackList<void()> on_shutdown_callback_list_;
 };
 
diff --git a/chrome/browser/extensions/api/identity/identity_constants.cc b/chrome/browser/extensions/api/identity/identity_constants.cc
index fefac47..5556c1cf 100644
--- a/chrome/browser/extensions/api/identity/identity_constants.cc
+++ b/chrome/browser/extensions/api/identity/identity_constants.cc
@@ -18,6 +18,7 @@
 const char kInvalidRedirect[] = "Did not redirect to the right URL.";
 const char kOffTheRecord[] = "Identity API is disabled in incognito windows.";
 const char kPageLoadFailure[] = "Authorization page could not be loaded.";
+const char kSetAccountsInCookieFailure[] = "Account cookies could not be set.";
 const char kCanceled[] = "canceled";
 
 const int kCachedIssueAdviceTTLSeconds = 1;
diff --git a/chrome/browser/extensions/api/identity/identity_constants.h b/chrome/browser/extensions/api/identity/identity_constants.h
index e849ccd..d923314e 100644
--- a/chrome/browser/extensions/api/identity/identity_constants.h
+++ b/chrome/browser/extensions/api/identity/identity_constants.h
@@ -19,6 +19,7 @@
 extern const char kInvalidRedirect[];
 extern const char kOffTheRecord[];
 extern const char kPageLoadFailure[];
+extern const char kSetAccountsInCookieFailure[];
 extern const char kCanceled[];
 
 extern const int kCachedIssueAdviceTTLSeconds;
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
index 6b7db32e..83ce91ea 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
@@ -704,6 +704,34 @@
   CompleteFunctionWithResult(access_token);
 }
 
+void IdentityGetAuthTokenFunction::OnGaiaRemoteConsentFlowFailure(
+    GaiaRemoteConsentFlow::Failure failure) {
+  CompleteMintTokenFlow();
+  std::string error;
+
+  switch (failure) {
+    case GaiaRemoteConsentFlow::WINDOW_CLOSED:
+      error = identity_constants::kUserRejected;
+      break;
+
+    case GaiaRemoteConsentFlow::SET_ACCOUNTS_IN_COOKIE_FAILED:
+      error = identity_constants::kSetAccountsInCookieFailure;
+      break;
+
+    case GaiaRemoteConsentFlow::LOAD_FAILED:
+      error = identity_constants::kPageLoadFailure;
+      break;
+  }
+
+  CompleteFunctionWithError(error);
+}
+
+void IdentityGetAuthTokenFunction::OnGaiaRemoteConsentFlowCompleted(
+    const std::string& consent_result) {
+  // TODO(crbug.com/1026237): implement this.
+  NOTIMPLEMENTED();
+}
+
 void IdentityGetAuthTokenFunction::OnGetAccessTokenComplete(
     const base::Optional<std::string>& access_token,
     base::Time expiration_time,
@@ -863,8 +891,9 @@
 
 void IdentityGetAuthTokenFunction::ShowRemoteConsentDialog(
     const RemoteConsentResolutionData& resolution_data) {
-  // TODO(crbug.com/1026237): implement this.
-  NOTIMPLEMENTED();
+  gaia_remote_consent_flow_ = std::make_unique<GaiaRemoteConsentFlow>(
+      this, GetProfile(), token_key_, resolution_data);
+  gaia_remote_consent_flow_->Start();
 }
 
 std::unique_ptr<OAuth2MintTokenFlow>
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h
index 84da39d..381b041 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/scoped_observer.h"
+#include "chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h"
 #include "chrome/browser/extensions/api/identity/gaia_web_auth_flow.h"
 #include "chrome/browser/extensions/api/identity/identity_mint_queue.h"
 #include "chrome/browser/extensions/chrome_extension_function.h"
@@ -47,6 +48,7 @@
 // successfully, getAuthToken proceeds to the non-interactive flow.
 class IdentityGetAuthTokenFunction : public ChromeAsyncExtensionFunction,
                                      public GaiaWebAuthFlow::Delegate,
+                                     public GaiaRemoteConsentFlow::Delegate,
                                      public IdentityMintRequestQueue::Request,
                                      public signin::IdentityManager::Observer,
 #if defined(OS_CHROMEOS)
@@ -75,6 +77,12 @@
   void OnGaiaFlowCompleted(const std::string& access_token,
                            const std::string& expiration) override;
 
+  // GaiaRemoteConsentFlow::Delegate implementation:
+  void OnGaiaRemoteConsentFlowFailure(
+      GaiaRemoteConsentFlow::Failure failure) override;
+  void OnGaiaRemoteConsentFlowCompleted(
+      const std::string& consent_result) override;
+
   // Starts a login access token request.
   virtual void StartTokenKeyAccountAccessTokenRequest();
 
@@ -219,6 +227,7 @@
   std::unique_ptr<GaiaWebAuthFlow> gaia_web_auth_flow_;
   // The browser resolution consent flow.
   RemoteConsentResolutionData resolution_data_;
+  std::unique_ptr<GaiaRemoteConsentFlow> gaia_remote_consent_flow_;
 
   // Invoked when IdentityAPI is shut down.
   std::unique_ptr<base::CallbackList<void()>::Subscription>
diff --git a/chrome/browser/extensions/api/identity/web_auth_flow.cc b/chrome/browser/extensions/api/identity/web_auth_flow.cc
index d26238c..82656844 100644
--- a/chrome/browser/extensions/api/identity/web_auth_flow.cc
+++ b/chrome/browser/extensions/api/identity/web_auth_flow.cc
@@ -21,6 +21,7 @@
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "components/guest_view/browser/guest_view_base.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
@@ -34,6 +35,7 @@
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/guest_view/web_view/web_view_guest.h"
 #include "net/http/http_response_headers.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
@@ -118,6 +120,19 @@
   base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
 }
 
+content::StoragePartition* WebAuthFlow::GetGuestPartition() const {
+  GURL guest_partition_url =
+      extensions::WebViewGuest::GetSiteForGuestPartitionConfig(
+          extension_misc::kIdentityApiUiAppId, /*partition_name=*/std::string(),
+          /*in_memory=*/true);
+  return content::BrowserContext::GetStoragePartitionForSite(
+      profile_, guest_partition_url);
+}
+
+const std::string& WebAuthFlow::GetAppWindowKey() const {
+  return app_window_key_;
+}
+
 void WebAuthFlow::OnAppWindowAdded(AppWindow* app_window) {
   if (app_window->window_key() == app_window_key_ &&
       app_window->extension_id() == extension_misc::kIdentityApiUiAppId) {
diff --git a/chrome/browser/extensions/api/identity/web_auth_flow.h b/chrome/browser/extensions/api/identity/web_auth_flow.h
index 2252a9f..8fc6446 100644
--- a/chrome/browser/extensions/api/identity/web_auth_flow.h
+++ b/chrome/browser/extensions/api/identity/web_auth_flow.h
@@ -21,6 +21,7 @@
 namespace content {
 class NotificationDetails;
 class NotificationSource;
+class StoragePartition;
 }
 
 namespace extensions {
@@ -64,9 +65,9 @@
     virtual void OnAuthFlowFailure(Failure failure) = 0;
     // Called on redirects and other navigations to see if the URL should stop
     // the flow.
-    virtual void OnAuthFlowURLChange(const GURL& redirect_url) = 0;
+    virtual void OnAuthFlowURLChange(const GURL& redirect_url) {}
     // Called when the title of the current page changes.
-    virtual void OnAuthFlowTitleChange(const std::string& title) = 0;
+    virtual void OnAuthFlowTitleChange(const std::string& title) {}
 
    protected:
     virtual ~Delegate() {}
@@ -87,6 +88,13 @@
   // Prevents further calls to the delegate and deletes the flow.
   void DetachDelegateAndDelete();
 
+  // Returns a StoragePartition of the guest webview. Used to inject cookies
+  // into Gaia page.
+  content::StoragePartition* GetGuestPartition() const;
+
+  // Returns an ID string attached to the window. Can override for testing.
+  virtual const std::string& GetAppWindowKey() const;
+
  private:
   friend class ::WebAuthFlowTest;
 
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
index 196cbcd1..12fc95bb 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
@@ -28,6 +28,17 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chromeos/tpm/stub_install_attributes.h"
+#include "components/account_id/account_id.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "components/user_manager/user.h"
+#endif
+
 using ::testing::_;
 using ::testing::Mock;
 using ::testing::SaveArg;
@@ -817,12 +828,30 @@
 
     TestingBrowserProcess::GetGlobal()->local_state()->SetBoolean(
         prefs::kUnsafeEventsReportingEnabled, is_policy_enabled_);
+
+#if defined(OS_CHROMEOS)
+    auto user_manager = std::make_unique<chromeos::FakeChromeUserManager>();
+    const AccountId account_id(
+        AccountId::FromUserEmail(profile_->GetProfileUserName()));
+    const user_manager::User* user = user_manager->AddUserWithAffiliation(
+        account_id, /*is_affiliated=*/true);
+    chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(user,
+                                                                      profile_);
+    user_manager->UserLoggedIn(account_id, user->username_hash(),
+                               /*browser_restart=*/false,
+                               /*is_child=*/false);
+    scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
+        std::move(user_manager));
+    profile_->ScopedCrosSettingsTestHelper()
+        ->InstallAttributes()
+        ->SetCloudManaged("domain.com", "device_id");
+    profile_->GetProfilePolicyConnector()->OverrideIsManagedForTesting(
+        is_manageable_);
+#endif
   }
 
   bool should_init() {
-#if defined(OS_CHROMEOS)
-    return false;
-#elif BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && !defined(OS_CHROMEOS)
     return is_feature_flag_enabled_;
 #else
     return is_feature_flag_enabled_ && is_manageable_;
@@ -835,6 +864,11 @@
   const bool is_manageable_;
   const bool is_policy_enabled_;
   const bool is_authorized_;
+
+#if defined(OS_CHROMEOS)
+ private:
+  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
+#endif
 };
 
 TEST_P(SafeBrowsingIsRealtimeReportingEnabledTest,
@@ -853,9 +887,7 @@
       api::safe_browsing_private::OnPolicySpecifiedPasswordChanged::kEventName);
   event_router_->AddEventObserver(&event_observer);
 
-#if defined(OS_CHROMEOS)
-  bool should_report = false;
-#elif BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && !defined(OS_CHROMEOS)
   bool should_report =
       is_feature_flag_enabled_ && is_policy_enabled_ && is_authorized_;
 #else
diff --git a/chrome/browser/extensions/app_background_page_apitest.cc b/chrome/browser/extensions/app_background_page_apitest.cc
index 08bcdb27..579850a 100644
--- a/chrome/browser/extensions/app_background_page_apitest.cc
+++ b/chrome/browser/extensions/app_background_page_apitest.cc
@@ -173,8 +173,9 @@
 
 }  // namespace
 
-// Disable on Mac only.  http://crbug.com/95139
-#if defined(OS_MACOSX)
+// Flaky test disabled on Mac (http://crbug.com/95139) and Windows
+// (http://crbug.com/1044265).
+#if defined(OS_MACOSX) || defined(OS_WIN)
 #define MAYBE_Basic DISABLED_Basic
 #else
 #define MAYBE_Basic Basic
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index bed0e26..335d74d4 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -522,6 +522,11 @@
     "expiry_milestone": 85
   },
   {
+    "name": "context-menu-copy-image",
+    "owners": [ "gangwu" ],
+    "expiry_milestone": 88
+  },
+  {
     "name": "context-menu-search-with-google-lens",
     "owners": [ "benwgold", "lens-chrome" ],
     "expiry_milestone": 88
@@ -1015,7 +1020,7 @@
   {
     "name": "enable-autofill-account-wallet-storage",
     "owners": [ "treib", "jsaul@google.com", "butter-team@google.com" ],
-    "expiry_milestone": 81
+    "expiry_milestone": 83
   },
   {
     "name": "enable-autofill-credit-card-ablation-experiment",
@@ -1795,11 +1800,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "enable-service-worker-long-running-message",
-    "owners": [ "azeemarshad", "jlklein", "falken" ],
-    "expiry_milestone": 77
-  },
-  {
     "name": "enable-service-worker-on-ui",
     "owners": [ "falken" ],
     "expiry_milestone": 82
@@ -2545,11 +2545,6 @@
     "expiry_milestone": 88
   },
   {
-    "name": "mac-views-task-manager",
-    "owners": [ "ellyjones" ],
-    "expiry_milestone": 77
-  },
-  {
     "name": "media-app",
     "owners": [ "//chromeos/components/media_app_ui/OWNERS" ],
     "expiry_milestone": 90
@@ -2663,7 +2658,7 @@
   {
     "name": "notification-scheduler-debug-options",
     "owners": [ "//chrome/browser/notifications/scheduler/OWNERS" ],
-    "expiry_milestone": 81
+    "expiry_milestone": 83
   },
   {
     "name": "ntp-confirm-suggestion-removals",
@@ -3524,12 +3519,12 @@
   {
     "name": "trim-on-all-frames-frozen",
     "owners": [ "bgeffon", "sonnyrao" ],
-    "expiry_milestone": 81
+    "expiry_milestone": 85
   },
   {
     "name": "trim-on-memory-pressure",
     "owners": [ "bgeffon", "sonnyrao" ],
-    "expiry_milestone": 81
+    "expiry_milestone": 85
   },
   {
     "name": "try-supported-channel-layouts",
@@ -3607,11 +3602,6 @@
     "expiry_milestone": 81
   },
   {
-    "name": "use_messages_google_com_domain",
-    "owners": [ "azeemarshad", "khorimoto" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "use_messages_staging_url",
     "owners": [ "azeemarshad", "khorimoto", "jonmann" ],
     // This flag is required for QA and dogfood testing.
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 39080ff..2791f91e 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -110,11 +110,6 @@
 const char kUpdatedCellularActivationUiDescription[] =
     "Enables the updated cellular activation UI.";
 
-const char kUseMessagesGoogleComDomainName[] = "Use messages.google.com domain";
-const char kUseMessagesGoogleComDomainDescription[] =
-    "Use the messages.google.com domain as part of the \"Messages\" "
-    "feature under \"Connected Devices\" settings.";
-
 const char kUseMessagesStagingUrlName[] = "Use Messages staging URL";
 const char kUseMessagesStagingUrlDescription[] =
     "Use the staging server as part of the \"Messages\" feature under "
@@ -1856,13 +1851,6 @@
     "but not necessarily syncing. The tab-share data is thus ephemeral, "
     "rather than persistent sync data.";
 
-const char kServiceWorkerLongRunningMessageName[] =
-    "Service worker long running message dispatch.";
-const char kServiceWorkerLongRunningMessageDescription[] =
-    "Enables long running message dispatch method for service workers. "
-    "Messages sent with this method do not timeout, allowing the service "
-    "worker to run indefinitely.";
-
 const char kServiceWorkerOnUIName[] = "Service worker on UI thread";
 const char kServiceWorkerOnUIDescription[] =
     "Enables browser process logic related to service workers to run on the UI "
@@ -2525,6 +2513,10 @@
 const char kContentIndexingNTPDescription[] =
     "Shows content indexing entry point UI in NTP";
 
+extern const char kContextMenuCopyImageName[] = "Copy Image";
+extern const char kContextMenuCopyImageDescription[] =
+    "Enable copying image to system clipboard via context menu.";
+
 const char kContextualSearchDefinitionsName[] = "Contextual Search definitions";
 const char kContextualSearchDefinitionsDescription[] =
     "Enables tap-activated contextual definitions of words on a page to be "
@@ -3167,10 +3159,6 @@
 const char kMacV2GPUSandboxDescription[] =
     "Controls whether the GPU process on macOS uses the V1 or V2 sandbox.";
 
-const char kMacViewsTaskManagerName[] = "Toolkit-Views Task Manager.";
-const char kMacViewsTaskManagerDescription[] =
-    "Controls whether to use the Toolkit-Views based Task Manager.";
-
 const char kMacSystemMediaPermissionsInfoUiName[] =
     "System media permissions info UI";
 const char kMacSystemMediaPermissionsInfoUiDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index e407877..2ef68bf3 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -96,9 +96,6 @@
 extern const char kUpdatedCellularActivationUiName[];
 extern const char kUpdatedCellularActivationUiDescription[];
 
-extern const char kUseMessagesGoogleComDomainName[];
-extern const char kUseMessagesGoogleComDomainDescription[];
-
 extern const char kUseMessagesStagingUrlName[];
 extern const char kUseMessagesStagingUrlDescription[];
 
@@ -1103,9 +1100,6 @@
 extern const char kSendTabToSelfWhenSignedInName[];
 extern const char kSendTabToSelfWhenSignedInDescription[];
 
-extern const char kServiceWorkerLongRunningMessageName[];
-extern const char kServiceWorkerLongRunningMessageDescription[];
-
 extern const char kServiceWorkerOnUIName[];
 extern const char kServiceWorkerOnUIDescription[];
 
@@ -1504,6 +1498,9 @@
 extern const char kContentIndexingNTPName[];
 extern const char kContentIndexingNTPDescription[];
 
+extern const char kContextMenuCopyImageName[];
+extern const char kContextMenuCopyImageDescription[];
+
 extern const char kContextualSearchDefinitionsName[];
 extern const char kContextualSearchDefinitionsDescription[];
 
@@ -1868,9 +1865,6 @@
 extern const char kMacV2GPUSandboxName[];
 extern const char kMacV2GPUSandboxDescription[];
 
-extern const char kMacViewsTaskManagerName[];
-extern const char kMacViewsTaskManagerDescription[];
-
 extern const char kMacSystemMediaPermissionsInfoUiName[];
 extern const char kMacSystemMediaPermissionsInfoUiDescription[];
 
diff --git a/chrome/browser/flags/BUILD.gn b/chrome/browser/flags/BUILD.gn
new file mode 100644
index 0000000..238e8fb9
--- /dev/null
+++ b/chrome/browser/flags/BUILD.gn
@@ -0,0 +1,7 @@
+# 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")
+
+java_group("java") {}
diff --git a/chrome/browser/flags/OWNERS b/chrome/browser/flags/OWNERS
new file mode 100644
index 0000000..d1622b35
--- /dev/null
+++ b/chrome/browser/flags/OWNERS
@@ -0,0 +1,11 @@
+dtrainor@chromium.org
+hnakashima@chromium.org
+twellington@chromium.org
+
+# This is for simple changes of adding/removing features.  For more structural
+# changes, use the normal OWNERS rules.
+per-file ChromeFeatureList.java=*
+
+# TEAM: clank-modularization@chromium.org
+# COMPONENT: UI>Browser>Mobile
+# OS: Android
diff --git a/chrome/browser/media/webrtc/webrtc_rtp_dump_writer_unittest.cc b/chrome/browser/media/webrtc/webrtc_rtp_dump_writer_unittest.cc
index 606aca3..d6aec5d5 100644
--- a/chrome/browser/media/webrtc/webrtc_rtp_dump_writer_unittest.cc
+++ b/chrome/browser/media/webrtc/webrtc_rtp_dump_writer_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
 #include "base/stl_util.h"
+#include "build/build_config.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
@@ -284,7 +285,13 @@
   VerifyDumps(1, 1);
 }
 
-TEST_F(WebRtcRtpDumpWriterTest, WriteOverMaxLimit) {
+// Flaky test disabled on Windows (https://crbug.com/1044271).
+#if defined(OS_WIN)
+#define MAYBE_WriteOverMaxLimit DISABLED_WriteOverMaxLimit
+#else
+#define MAYBE_WriteOverMaxLimit WriteOverMaxLimit
+#endif
+TEST_F(WebRtcRtpDumpWriterTest, MAYBE_WriteOverMaxLimit) {
   // Reset the writer with a small max size limit.
   writer_.reset(new WebRtcRtpDumpWriter(
       incoming_dump_path_,
diff --git a/chrome/browser/metrics/perf/perf_events_collector.cc b/chrome/browser/metrics/perf/perf_events_collector.cc
index 85589905..a2b5e49 100644
--- a/chrome/browser/metrics/perf/perf_events_collector.cc
+++ b/chrome/browser/metrics/perf/perf_events_collector.cc
@@ -115,21 +115,7 @@
 // we sample on the branches retired event.
 const char kPerfLBRCmdAtom[] = "perf record -a -e rc4 -b -c 300001";
 
-// The following events count misses in the level 1 caches and TLBs.
-
-// Perf doesn't support the generic dTLB-misses event for Goldmont. We define it
-// in terms of raw event number and umask value. Event codes taken from
-// "Intel 64 and IA-32 Architectures Software Developer's Manual, Vol 3".
-const char kPerfInstructionTLBMissesCmdGLM[] =
-    "perf record -a -e r0481 -c 2003";
-
-const char kPerfDataTLBMissesCmdGLM[] = "perf record -a -e r13d0 -c 2003";
-
-// Use the generic event names for the other microarchitectures.
-const char kPerfInstructionTLBMissesCmd[] =
-    "perf record -a -e iTLB-misses -c 2003";
-
-const char kPerfDataTLBMissesCmd[] = "perf record -a -e dTLB-misses -c 2003";
+// The following events count misses in the level 1 caches and level 2 TLBs.
 
 // TLB miss cycles for IvyBridge, Haswell, Broadwell and SandyBridge.
 const char kPerfITLBMissCyclesCmdIvyBridge[] =
@@ -163,8 +149,6 @@
 
   // We use different perf events for iTLB, dTLB and LBR profiling on different
   // microarchitectures. Customize each command based on the microarchitecture.
-  const char* itlb_misses_cmd = kPerfInstructionTLBMissesCmd;
-  const char* dtlb_misses_cmd = kPerfDataTLBMissesCmd;
   const char* itlb_miss_cycles_cmd = kPerfITLBMissCyclesCmdIvyBridge;
   const char* dtlb_miss_cycles_cmd = kPerfDTLBMissCyclesCmdIvyBridge;
   const char* lbr_cmd = kPerfLBRCmd;
@@ -179,17 +163,13 @@
     dtlb_miss_cycles_cmd = kPerfDTLBMissCyclesCmdAtom;
     lbr_cmd = kPerfLBRCmdAtom;
   }
-  if (cpu_uarch == "Goldmont" || cpu_uarch == "GoldmontPlus") {
-    itlb_misses_cmd = kPerfInstructionTLBMissesCmdGLM;
-    dtlb_misses_cmd = kPerfDataTLBMissesCmdGLM;
-  }
 
   if (cpu_uarch == "IvyBridge" || cpu_uarch == "Haswell" ||
       cpu_uarch == "Broadwell" || cpu_uarch == "SandyBridge" ||
       cpu_uarch == "Skylake" || cpu_uarch == "Kabylake" ||
       cpu_uarch == "Silvermont" || cpu_uarch == "Airmont" ||
       cpu_uarch == "Goldmont" || cpu_uarch == "GoldmontPlus") {
-    cmds.push_back(WeightAndValue(40.0, kPerfCyclesCmd));
+    cmds.push_back(WeightAndValue(50.0, kPerfCyclesCmd));
     // Haswell and newer big Intel cores support LBR callstack profiling. This
     // requires kernel support, which was added in kernel 4.4, and it was
     // backported to kernel 3.18. Collect LBR callstack profiling where
@@ -205,18 +185,14 @@
       cmds.push_back(WeightAndValue(20.0, kPerfFPCallgraphCmd));
     }
     cmds.push_back(WeightAndValue(15.0, lbr_cmd));
-    cmds.push_back(WeightAndValue(5.0, itlb_misses_cmd));
-    cmds.push_back(WeightAndValue(5.0, dtlb_misses_cmd));
     cmds.push_back(WeightAndValue(5.0, itlb_miss_cycles_cmd));
     cmds.push_back(WeightAndValue(5.0, dtlb_miss_cycles_cmd));
     cmds.push_back(WeightAndValue(5.0, kPerfCacheMissesCmd));
     return cmds;
   }
   // Other 64-bit x86
-  cmds.push_back(WeightAndValue(65.0, kPerfCyclesCmd));
+  cmds.push_back(WeightAndValue(75.0, kPerfCyclesCmd));
   cmds.push_back(WeightAndValue(20.0, kPerfFPCallgraphCmd));
-  cmds.push_back(WeightAndValue(5.0, kPerfInstructionTLBMissesCmd));
-  cmds.push_back(WeightAndValue(5.0, kPerfDataTLBMissesCmd));
   cmds.push_back(WeightAndValue(5.0, kPerfCacheMissesCmd));
   return cmds;
 }
diff --git a/chrome/browser/metrics/perf/perf_events_collector_unittest.cc b/chrome/browser/metrics/perf/perf_events_collector_unittest.cc
index d66aca5..680ec727 100644
--- a/chrome/browser/metrics/perf/perf_events_collector_unittest.cc
+++ b/chrome/browser/metrics/perf/perf_events_collector_unittest.cc
@@ -34,8 +34,6 @@
     "perf record -a -e cycles -c 4000037 --call-graph lbr";
 const char kPerfLBRCmd[] = "perf record -a -e r20c4 -b -c 200011";
 const char kPerfLBRCmdAtom[] = "perf record -a -e rc4 -b -c 300001";
-const char kPerfDataTLBMissesCmdGLM[] = "perf record -a -e r13d0 -c 2003";
-const char kPerfDataTLBMissesCmd[] = "perf record -a -e dTLB-misses -c 2003";
 const char kPerfITLBMissCyclesCmdIvyBridge[] =
     "perf record -a -e itlb_misses.walk_duration -c 2003";
 const char kPerfITLBMissCyclesCmdSkylake[] =
@@ -504,16 +502,6 @@
   EXPECT_NE(cmds.end(), found);
   found = std::find_if(cmds.begin(), cmds.end(),
                        [](const RandomSelector::WeightAndValue& cmd) -> bool {
-                         return cmd.value == kPerfDataTLBMissesCmd;
-                       });
-  EXPECT_EQ(cmds.end(), found) << "Goldmont requires specialized dTLB command";
-  found = std::find_if(cmds.begin(), cmds.end(),
-                       [](const RandomSelector::WeightAndValue& cmd) -> bool {
-                         return cmd.value == kPerfDataTLBMissesCmdGLM;
-                       });
-  EXPECT_NE(cmds.end(), found);
-  found = std::find_if(cmds.begin(), cmds.end(),
-                       [](const RandomSelector::WeightAndValue& cmd) -> bool {
                          return cmd.value == kPerfITLBMissCyclesCmdAtom;
                        });
   EXPECT_NE(cmds.end(), found);
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
index b9464c4b..9f089a98 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -141,6 +141,55 @@
   return matched_page_hint;
 }
 
+// Returns whether |optimization_type| is whitelisted by the |page_hint|. If
+// it is whitelisted, this will return true and |optimization_metadata| will be
+// populated with the metadata provided by the hint, if applicable. If
+// |page_hint| is not provided or |optimization_type| is not whitelisted, this
+// will return false.
+bool IsOptimizationTypeSupportedByPageHint(
+    const optimization_guide::proto::PageHint* page_hint,
+    optimization_guide::proto::OptimizationType optimization_type,
+    optimization_guide::OptimizationMetadata* optimization_metadata) {
+  if (!page_hint)
+    return false;
+
+  for (const auto& optimization : page_hint->whitelisted_optimizations()) {
+    if (optimization_type != optimization.optimization_type())
+      continue;
+
+    if (optimization_guide::IsDisabledPerOptimizationHintExperiment(
+            optimization)) {
+      continue;
+    }
+
+    // We found an optimization that can be applied. Populate optimization
+    // metadata if applicable and return.
+    if (optimization_metadata) {
+      switch (optimization.metadata_case()) {
+        case optimization_guide::proto::Optimization::kPreviewsMetadata:
+          optimization_metadata->previews_metadata =
+              optimization.previews_metadata();
+          break;
+        case optimization_guide::proto::Optimization::kPerformanceHintsMetadata:
+          optimization_metadata->performance_hints_metadata =
+              optimization.performance_hints_metadata();
+          break;
+        case optimization_guide::proto::Optimization::kPublicImageMetadata:
+          optimization_metadata->public_image_metadata =
+              optimization.public_image_metadata();
+          break;
+        case optimization_guide::proto::Optimization::METADATA_NOT_SET:
+          // Some optimization types do not have metadata, make sure we do not
+          // DCHECK.
+          break;
+      }
+    }
+    return true;
+  }
+
+  return false;
+}
+
 // Util class for recording whether a hints fetch race against the current
 // navigation was attempted. The result is recorded when it goes out of scope
 // and its destructor is called.
@@ -867,7 +916,24 @@
     }
   }
 
+  // First, check if the optimization type is whitelisted by a URL-keyed hint.
+  const optimization_guide::proto::Hint* url_keyed_hint =
+      hint_cache_->GetURLKeyedHint(url);
+  if (url_keyed_hint) {
+    DCHECK(url_keyed_hint->page_hints_size() == 1);
+    if (url_keyed_hint->page_hints_size() > 0 &&
+        IsOptimizationTypeSupportedByPageHint(&url_keyed_hint->page_hints(0),
+                                              optimization_type,
+                                              optimization_metadata)) {
+      return optimization_guide::OptimizationTypeDecision::kAllowedByHint;
+    }
+  }
+
   if (!loaded_hint) {
+    if (IsHintBeingFetched(url.host())) {
+      return optimization_guide::OptimizationTypeDecision::
+          kHintFetchStartedButNotAvailableInTime;
+    }
     // If we do not have a hint already loaded and we do not have one in the
     // cache, we do not know what to do with the URL so just return.
     // Otherwise, we do have information, but we just do not know it yet.
@@ -875,54 +941,13 @@
       return optimization_guide::OptimizationTypeDecision::
           kHadHintButNotLoadedInTime;
     }
-    if (IsHintBeingFetched(url.host())) {
-      return optimization_guide::OptimizationTypeDecision::
-          kHintFetchStartedButNotAvailableInTime;
-    }
     return optimization_guide::OptimizationTypeDecision::kNoHintAvailable;
   }
 
-  if (!matched_page_hint) {
-    return optimization_guide::OptimizationTypeDecision::kNoMatchingPageHint;
-  }
-
-  // Now check if we have any optimizations for it.
-  for (const auto& optimization :
-       matched_page_hint->whitelisted_optimizations()) {
-    if (optimization_type != optimization.optimization_type())
-      continue;
-
-    if (optimization_guide::IsDisabledPerOptimizationHintExperiment(
-            optimization)) {
-      continue;
-    }
-
-    // We found an optimization that can be applied. Populate optimization
-    // metadata if applicable and return.
-    if (optimization_metadata) {
-      switch (optimization.metadata_case()) {
-        case optimization_guide::proto::Optimization::kPreviewsMetadata:
-          optimization_metadata->previews_metadata =
-              optimization.previews_metadata();
-          break;
-        case optimization_guide::proto::Optimization::kPerformanceHintsMetadata:
-          optimization_metadata->performance_hints_metadata =
-              optimization.performance_hints_metadata();
-          break;
-        case optimization_guide::proto::Optimization::kPublicImageMetadata:
-          optimization_metadata->public_image_metadata =
-              optimization.public_image_metadata();
-          break;
-        default:
-          NOTREACHED();
-          break;
-      }
-    }
-    return optimization_guide::OptimizationTypeDecision::kAllowedByHint;
-  }
-
-  // We didn't find anything, so it's not allowed by the hint.
-  return optimization_guide::OptimizationTypeDecision::kNotAllowedByHint;
+  return IsOptimizationTypeSupportedByPageHint(
+             matched_page_hint, optimization_type, optimization_metadata)
+             ? optimization_guide::OptimizationTypeDecision::kAllowedByHint
+             : optimization_guide::OptimizationTypeDecision::kNotAllowedByHint;
 }
 
 void OptimizationGuideHintsManager::OnEffectiveConnectionTypeChanged(
@@ -987,9 +1012,6 @@
   std::vector<GURL> urls;
   if (!hint_cache_->HasHint(navigation_handle->GetURL().host())) {
     hosts.push_back(navigation_handle->GetURL().host());
-    page_navigation_hosts_being_fetched_.clear();
-    page_navigation_hosts_being_fetched_.push_back(
-        navigation_handle->GetURL().host());
 
     OptimizationGuideNavigationData* navigation_data =
         OptimizationGuideNavigationData::GetFromNavigationHandle(
@@ -1014,6 +1036,10 @@
     return;
   }
 
+  page_navigation_hosts_being_fetched_.clear();
+  page_navigation_hosts_being_fetched_.push_back(
+      navigation_handle->GetURL().host());
+
   if (!hints_fetcher_) {
     hints_fetcher_ = std::make_unique<optimization_guide::HintsFetcher>(
         url_loader_factory_,
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
index 810fed84..f89ff5f 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -80,8 +80,8 @@
 }
 
 std::unique_ptr<optimization_guide::proto::GetHintsResponse> BuildHintsResponse(
-    std::vector<std::string> hosts,
-    std::vector<std::string> urls) {
+    const std::vector<std::string>& hosts,
+    const std::vector<std::string>& urls) {
   std::unique_ptr<optimization_guide::proto::GetHintsResponse>
       get_hints_response =
           std::make_unique<optimization_guide::proto::GetHintsResponse>();
@@ -97,10 +97,15 @@
     optimization_guide::proto::Hint* hint = get_hints_response->add_hints();
     hint->set_key_representation(optimization_guide::proto::FULL_URL);
     hint->set_key(url);
+    hint->mutable_max_cache_duration()->set_seconds(60 * 60);
     optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
-    page_hint->set_page_pattern("page pattern");
+    page_hint->set_page_pattern(url);
+    optimization_guide::proto::Optimization* opt =
+        page_hint->add_whitelisted_optimizations();
+    opt->set_optimization_type(
+        optimization_guide::proto::COMPRESS_PUBLIC_IMAGES);
+    opt->mutable_public_image_metadata()->add_url("someurl");
   }
-
   return get_hints_response;
 }
 
@@ -136,7 +141,7 @@
 // A mock class implementation of TopHostProvider.
 class FakeTopHostProvider : public optimization_guide::TopHostProvider {
  public:
-  explicit FakeTopHostProvider(std::vector<std::string> top_hosts)
+  explicit FakeTopHostProvider(const std::vector<std::string>& top_hosts)
       : top_hosts_(top_hosts) {}
 
   std::vector<std::string> GetTopHosts() override {
@@ -393,6 +398,10 @@
     return GURL("https://somedomain.org/news/whatever");
   }
 
+  GURL url_with_url_keyed_hint() const {
+    return GURL("https://somedomain.org/news/whatever");
+  }
+
   GURL url_without_hints() const {
     return GURL("https://url_without_hints.org/");
   }
@@ -1451,7 +1460,7 @@
           /*optimization_metadata=*/nullptr);
 
   // Make sure decisions are logged correctly.
-  EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNoMatchingPageHint,
+  EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNotAllowedByHint,
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
@@ -1593,7 +1602,7 @@
                                             /*optimization_metadata=*/nullptr);
 
   // Make sure decisions are logged correctly.
-  EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNoMatchingPageHint,
+  EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNotAllowedByHint,
             optimization_type_decision);
   // Make sure navigation data is populated correctly.
   OptimizationGuideNavigationData* navigation_data =
@@ -2394,3 +2403,168 @@
   EXPECT_EQ(optimization_type_decision,
             optimization_guide::OptimizationTypeDecision::kNoHintAvailable);
 }
+
+TEST_F(OptimizationGuideHintsManagerFetchingTest,
+       CanApplyOptimizationWithURLKeyedHintApplicableForOptimizationType) {
+  hints_manager()->RegisterOptimizationTypes(
+      {optimization_guide::proto::COMPRESS_PUBLIC_IMAGES});
+  InitializeWithDefaultConfig("1.0.0");
+
+  hints_manager()->SetHintsFetcherForTesting(
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithURLHints));
+
+  // Set ECT estimate so fetch is activated.
+  hints_manager()->OnEffectiveConnectionTypeChanged(
+      net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+  std::unique_ptr<content::MockNavigationHandle> navigation_handle =
+      CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
+          url_with_url_keyed_hint());
+  // Make sure URL-keyed hint is fetched and processed.
+  hints_manager()->OnNavigationStartOrRedirect(navigation_handle.get(),
+                                               base::DoNothing());
+  RunUntilIdle();
+
+  optimization_guide::OptimizationMetadata optimization_metadata;
+  optimization_guide::OptimizationTypeDecision optimization_type_decision =
+      hints_manager()->CanApplyOptimization(
+          navigation_handle.get(),
+          optimization_guide::proto::COMPRESS_PUBLIC_IMAGES,
+          &optimization_metadata);
+
+  // Make sure decisions are logged correctly and metadata is populated off of
+  // a URL-keyed hint.
+  EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kAllowedByHint,
+            optimization_type_decision);
+  EXPECT_EQ("someurl", optimization_metadata.public_image_metadata.url(0));
+}
+
+TEST_F(OptimizationGuideHintsManagerFetchingTest,
+       CanApplyOptimizationNotAllowedByURLButAllowedByHostKeyedHint) {
+  hints_manager()->RegisterOptimizationTypes(
+      {optimization_guide::proto::NOSCRIPT});
+
+  InitializeWithDefaultConfig("1.0.0.0");
+
+  // Set ECT estimate so fetch is activated.
+  hints_manager()->OnEffectiveConnectionTypeChanged(
+      net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+
+  // Make sure both URL-Keyed and host-keyed hints are processed and cached.
+  hints_manager()->SetHintsFetcherForTesting(
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithURLHints));
+  std::unique_ptr<content::MockNavigationHandle> navigation_handle =
+      CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
+          url_with_url_keyed_hint());
+  hints_manager()->OnNavigationStartOrRedirect(navigation_handle.get(),
+                                               base::DoNothing());
+  RunUntilIdle();
+
+  optimization_guide::OptimizationMetadata optimization_metadata;
+  optimization_guide::OptimizationTypeDecision optimization_type_decision =
+      hints_manager()->CanApplyOptimization(navigation_handle.get(),
+                                            optimization_guide::proto::NOSCRIPT,
+                                            &optimization_metadata);
+
+  // Make sure decisions are logged correctly.
+  EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kAllowedByHint,
+            optimization_type_decision);
+}
+
+TEST_F(OptimizationGuideHintsManagerFetchingTest,
+       CanApplyOptimizationNotAllowedByURLOrHostKeyedHint) {
+  hints_manager()->RegisterOptimizationTypes(
+      {optimization_guide::proto::RESOURCE_LOADING});
+
+  InitializeWithDefaultConfig("1.0.0.0");
+
+  // Set ECT estimate so fetch is activated.
+  hints_manager()->OnEffectiveConnectionTypeChanged(
+      net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+
+  // Make sure both URL-Keyed and host-keyed hints are processed and cached.
+  hints_manager()->SetHintsFetcherForTesting(
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithURLHints));
+  std::unique_ptr<content::MockNavigationHandle> navigation_handle =
+      CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
+          url_with_url_keyed_hint());
+  hints_manager()->OnNavigationStartOrRedirect(navigation_handle.get(),
+                                               base::DoNothing());
+  RunUntilIdle();
+
+  optimization_guide::OptimizationMetadata optimization_metadata;
+  optimization_guide::OptimizationTypeDecision optimization_type_decision =
+      hints_manager()->CanApplyOptimization(
+          navigation_handle.get(), optimization_guide::proto::RESOURCE_LOADING,
+          &optimization_metadata);
+
+  // Make sure decisions are logged correctly.
+  EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNotAllowedByHint,
+            optimization_type_decision);
+}
+
+TEST_F(OptimizationGuideHintsManagerFetchingTest,
+       CanApplyOptimizationNoURLKeyedHintOrHostKeyedHint) {
+  hints_manager()->RegisterOptimizationTypes(
+      {optimization_guide::proto::COMPRESS_PUBLIC_IMAGES});
+
+  InitializeWithDefaultConfig("1.0.0.0");
+
+  // Set ECT estimate so fetch is activated.
+  hints_manager()->OnEffectiveConnectionTypeChanged(
+      net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+
+  hints_manager()->SetHintsFetcherForTesting(
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithNoHints));
+  std::unique_ptr<content::MockNavigationHandle> navigation_handle =
+      CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
+          url_without_hints());
+
+  // Attempt to fetch a hint but ensure nothing comes back.
+  hints_manager()->OnNavigationStartOrRedirect(navigation_handle.get(),
+                                               base::DoNothing());
+  RunUntilIdle();
+
+  optimization_guide::OptimizationMetadata optimization_metadata;
+  optimization_guide::OptimizationTypeDecision optimization_type_decision =
+      hints_manager()->CanApplyOptimization(
+          navigation_handle.get(),
+          optimization_guide::proto::COMPRESS_PUBLIC_IMAGES,
+          &optimization_metadata);
+
+  // Make sure decisions are logged correctly.
+  EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNoHintAvailable,
+            optimization_type_decision);
+}
+
+TEST_F(OptimizationGuideHintsManagerFetchingTest,
+       CanApplyOptimizationCalledMidFetchForURLKeyedOptimization) {
+  hints_manager()->RegisterOptimizationTypes(
+      {optimization_guide::proto::COMPRESS_PUBLIC_IMAGES});
+
+  InitializeWithDefaultConfig("1.0.0.0");
+
+  // Set ECT estimate so fetch is activated.
+  hints_manager()->OnEffectiveConnectionTypeChanged(
+      net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+
+  // Attempt to fetch a hint but call CanApplyOptimization right away to
+  // simulate being mid-fetch.
+  std::unique_ptr<content::MockNavigationHandle> navigation_handle =
+      CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
+          url_without_hints());
+  hints_manager()->SetHintsFetcherForTesting(
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithNoHints));
+  hints_manager()->OnNavigationStartOrRedirect(navigation_handle.get(),
+                                               base::DoNothing());
+  optimization_guide::OptimizationMetadata optimization_metadata;
+  optimization_guide::OptimizationTypeDecision optimization_type_decision =
+      hints_manager()->CanApplyOptimization(
+          navigation_handle.get(),
+          optimization_guide::proto::COMPRESS_PUBLIC_IMAGES,
+          &optimization_metadata);
+
+  // Make sure decisions are logged correctly.
+  EXPECT_EQ(optimization_guide::OptimizationTypeDecision::
+                kHintFetchStartedButNotAvailableInTime,
+            optimization_type_decision);
+}
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 4e3175f0..b93b124 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -1104,7 +1104,7 @@
 #if BUILDFLAG(FULL_SAFE_BROWSING)
   return safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
              profile_)
-      ->is_under_advanced_protection();
+      ->IsUnderAdvancedProtection();
 #else
   return false;
 #endif
diff --git a/chrome/browser/predictors/autocomplete_action_predictor_table.cc b/chrome/browser/predictors/autocomplete_action_predictor_table.cc
index 7286a658..fb9267a 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor_table.cc
+++ b/chrome/browser/predictors/autocomplete_action_predictor_table.cc
@@ -220,11 +220,11 @@
 
 AutocompleteActionPredictorTable::AutocompleteActionPredictorTable(
     scoped_refptr<base::SequencedTaskRunner> db_task_runner)
-    : PredictorTableBase(std::move(db_task_runner)) {}
+    : sqlite_proto::TableManager(std::move(db_task_runner)) {}
 
 AutocompleteActionPredictorTable::~AutocompleteActionPredictorTable() = default;
 
-void AutocompleteActionPredictorTable::CreateTableIfNonExistent() {
+void AutocompleteActionPredictorTable::CreateTablesIfNonExistent() {
   DCHECK(GetTaskRunner()->RunsTasksInCurrentSequence());
   if (CantAccessDatabase())
     return;
diff --git a/chrome/browser/predictors/autocomplete_action_predictor_table.h b/chrome/browser/predictors/autocomplete_action_predictor_table.h
index 913f36c..b1719ec 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor_table.h
+++ b/chrome/browser/predictors/autocomplete_action_predictor_table.h
@@ -10,7 +10,7 @@
 
 #include "base/macros.h"
 #include "base/strings/string16.h"
-#include "components/sqlite_proto/predictor_table_base.h"
+#include "components/sqlite_proto/table_manager.h"
 #include "url/gurl.h"
 
 namespace predictors {
@@ -33,7 +33,7 @@
 //
 // All the functions apart from constructor and destructor have to be called in
 // the DB sequence provided to the constructor of this class.
-class AutocompleteActionPredictorTable : public PredictorTableBase {
+class AutocompleteActionPredictorTable : public sqlite_proto::TableManager {
  public:
   struct Row {
     // TODO(dominich): Make this 64-bit integer as an optimization. This
@@ -78,8 +78,8 @@
       scoped_refptr<base::SequencedTaskRunner> db_task_runner);
   ~AutocompleteActionPredictorTable() override;
 
-  // PredictorTableBase methods (DB sequence).
-  void CreateTableIfNonExistent() override;
+  // TableManager methods (DB sequence).
+  void CreateTablesIfNonExistent() override;
   void LogDatabaseStats() override;
 
   DISALLOW_COPY_AND_ASSIGN(AutocompleteActionPredictorTable);
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.h b/chrome/browser/predictors/resource_prefetch_predictor.h
index 36f1285..ebe73a3 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor.h
+++ b/chrome/browser/predictors/resource_prefetch_predictor.h
@@ -28,7 +28,7 @@
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/sqlite_proto/loading_predictor_key_value_data.h"
+#include "components/sqlite_proto/key_value_data.h"
 #include "content/public/common/resource_type.h"
 #include "net/base/network_isolation_key.h"
 #include "url/gurl.h"
@@ -121,10 +121,9 @@
   };
 
   using RedirectDataMap =
-      LoadingPredictorKeyValueData<RedirectData,
-                                   internal::LastVisitTimeCompare>;
+      sqlite_proto::KeyValueData<RedirectData, internal::LastVisitTimeCompare>;
   using OriginDataMap =
-      LoadingPredictorKeyValueData<OriginData, internal::LastVisitTimeCompare>;
+      sqlite_proto::KeyValueData<OriginData, internal::LastVisitTimeCompare>;
   using NavigationMap =
       std::map<NavigationID, std::unique_ptr<PageRequestSummary>>;
 
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_tables.cc b/chrome/browser/predictors/resource_prefetch_predictor_tables.cc
index be452ad..809d2fe 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_tables.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_tables.cc
@@ -84,11 +84,11 @@
 
 ResourcePrefetchPredictorTables::ResourcePrefetchPredictorTables(
     scoped_refptr<base::SequencedTaskRunner> db_task_runner)
-    : PredictorTableBase(db_task_runner) {
+    : sqlite_proto::TableManager(db_task_runner) {
   host_redirect_table_ =
-      std::make_unique<LoadingPredictorKeyValueTable<RedirectData>>(
+      std::make_unique<sqlite_proto::KeyValueTable<RedirectData>>(
           kHostRedirectTableName);
-  origin_table_ = std::make_unique<LoadingPredictorKeyValueTable<OriginData>>(
+  origin_table_ = std::make_unique<sqlite_proto::KeyValueTable<OriginData>>(
       kOriginTableName);
 }
 
@@ -120,11 +120,11 @@
   return score;
 }
 
-LoadingPredictorKeyValueTable<RedirectData>*
+sqlite_proto::KeyValueTable<RedirectData>*
 ResourcePrefetchPredictorTables::host_redirect_table() {
   return host_redirect_table_.get();
 }
-LoadingPredictorKeyValueTable<OriginData>*
+sqlite_proto::KeyValueTable<OriginData>*
 ResourcePrefetchPredictorTables::origin_table() {
   return origin_table_.get();
 }
@@ -198,7 +198,7 @@
   return statement.Run();
 }
 
-void ResourcePrefetchPredictorTables::CreateTableIfNonExistent() {
+void ResourcePrefetchPredictorTables::CreateTablesIfNonExistent() {
   DCHECK(GetTaskRunner()->RunsTasksInCurrentSequence());
   if (CantAccessDatabase())
     return;
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_tables.h b/chrome/browser/predictors/resource_prefetch_predictor_tables.h
index 64089ac..1d43754 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_tables.h
+++ b/chrome/browser/predictors/resource_prefetch_predictor_tables.h
@@ -15,8 +15,8 @@
 #include "base/macros.h"
 #include "base/sequenced_task_runner.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor.pb.h"
-#include "components/sqlite_proto/loading_predictor_key_value_table.h"
-#include "components/sqlite_proto/predictor_table_base.h"
+#include "components/sqlite_proto/key_value_table.h"
+#include "components/sqlite_proto/table_manager.h"
 
 namespace predictors {
 
@@ -27,10 +27,10 @@
 // Currently manages:
 //  - HostRedirectTable - key: host, value: RedirectData
 //  - OriginTable - key: host, value: OriginData
-class ResourcePrefetchPredictorTables : public PredictorTableBase {
+class ResourcePrefetchPredictorTables : public sqlite_proto::TableManager {
  public:
-  virtual LoadingPredictorKeyValueTable<RedirectData>* host_redirect_table();
-  virtual LoadingPredictorKeyValueTable<OriginData>* origin_table();
+  virtual sqlite_proto::KeyValueTable<RedirectData>* host_redirect_table();
+  virtual sqlite_proto::KeyValueTable<OriginData>* origin_table();
 
   // Removes the redirects with more than |max_consecutive_misses| consecutive
   // misses from |data|.
@@ -69,17 +69,17 @@
   // schema (including the .proto).
   static constexpr int kDatabaseVersion = 11;
 
-  // PredictorTableBase:
-  void CreateTableIfNonExistent() override;
+  // sqlite_proto::TableManager:
+  void CreateTablesIfNonExistent() override;
   void LogDatabaseStats() override;
 
   static bool DropTablesIfOutdated(sql::Database* db);
   static int GetDatabaseVersion(sql::Database* db);
   static bool SetDatabaseVersion(sql::Database* db, int version);
 
-  std::unique_ptr<LoadingPredictorKeyValueTable<RedirectData>>
+  std::unique_ptr<sqlite_proto::KeyValueTable<RedirectData>>
       host_redirect_table_;
-  std::unique_ptr<LoadingPredictorKeyValueTable<OriginData>> origin_table_;
+  std::unique_ptr<sqlite_proto::KeyValueTable<OriginData>> origin_table_;
 
   DISALLOW_COPY_AND_ASSIGN(ResourcePrefetchPredictorTables);
 };
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc b/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc
index 35467a1..59d8f28 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/predictors/predictors_features.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor_tables.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/sqlite_proto/key_value_table.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
 #include "net/base/request_priority.h"
@@ -26,6 +27,8 @@
 
 namespace predictors {
 
+using ::sqlite_proto::KeyValueTable;
+
 class ResourcePrefetchPredictorTablesTest : public testing::Test {
  public:
   ResourcePrefetchPredictorTablesTest();
@@ -130,12 +133,12 @@
                                              "http://google.com"};
   std::vector<std::string> hosts_to_delete = {"microsoft.com"};
   tables_->ExecuteDBTaskOnDBSequence(base::BindOnce(
-      &LoadingPredictorKeyValueTable<RedirectData>::DeleteData,
+      &KeyValueTable<RedirectData>::DeleteData,
       base::Unretained(tables_->host_redirect_table()), hosts_to_delete));
 
   hosts_to_delete = {"twitter.com"};
   tables_->ExecuteDBTaskOnDBSequence(base::BindOnce(
-      &LoadingPredictorKeyValueTable<OriginData>::DeleteData,
+      &KeyValueTable<OriginData>::DeleteData,
       base::Unretained(tables_->origin_table()), hosts_to_delete));
 
   RedirectDataMap actual_host_redirect_data;
@@ -161,7 +164,7 @@
                          GURL("https://microsoft.org"), 7, 2, 0);
 
   tables_->ExecuteDBTaskOnDBSequence(
-      base::BindOnce(&LoadingPredictorKeyValueTable<RedirectData>::UpdateData,
+      base::BindOnce(&KeyValueTable<RedirectData>::UpdateData,
                      base::Unretained(tables_->host_redirect_table()),
                      microsoft.primary_key(), microsoft));
 
@@ -169,7 +172,7 @@
   InitializeOriginStat(twitter.add_origins(), "https://dogs.twitter.com", 10, 1,
                        0, 12., false, true);
   tables_->ExecuteDBTaskOnDBSequence(base::BindOnce(
-      &LoadingPredictorKeyValueTable<OriginData>::UpdateData,
+      &KeyValueTable<OriginData>::UpdateData,
       base::Unretained(tables_->origin_table()), twitter.host(), twitter));
 
   RedirectDataMap actual_host_redirect_data;
@@ -310,11 +313,11 @@
 }
 
 void ResourcePrefetchPredictorTablesTest::DeleteAllData() {
-  tables_->ExecuteDBTaskOnDBSequence(base::BindOnce(
-      &LoadingPredictorKeyValueTable<RedirectData>::DeleteAllData,
-      base::Unretained(tables_->host_redirect_table())));
   tables_->ExecuteDBTaskOnDBSequence(
-      base::BindOnce(&LoadingPredictorKeyValueTable<OriginData>::DeleteAllData,
+      base::BindOnce(&KeyValueTable<RedirectData>::DeleteAllData,
+                     base::Unretained(tables_->host_redirect_table())));
+  tables_->ExecuteDBTaskOnDBSequence(
+      base::BindOnce(&KeyValueTable<OriginData>::DeleteAllData,
                      base::Unretained(tables_->origin_table())));
 }
 
@@ -322,10 +325,10 @@
     RedirectDataMap* host_redirect_data,
     OriginDataMap* origin_data) const {
   tables_->ExecuteDBTaskOnDBSequence(base::BindOnce(
-      &LoadingPredictorKeyValueTable<RedirectData>::GetAllData,
+      &KeyValueTable<RedirectData>::GetAllData,
       base::Unretained(tables_->host_redirect_table()), host_redirect_data));
   tables_->ExecuteDBTaskOnDBSequence(
-      base::BindOnce(&LoadingPredictorKeyValueTable<OriginData>::GetAllData,
+      base::BindOnce(&KeyValueTable<OriginData>::GetAllData,
                      base::Unretained(tables_->origin_table()), origin_data));
 }
 
@@ -355,11 +358,11 @@
         std::make_pair(microsoft.primary_key(), microsoft));
 
     tables_->ExecuteDBTaskOnDBSequence(
-        base::BindOnce(&LoadingPredictorKeyValueTable<RedirectData>::UpdateData,
+        base::BindOnce(&KeyValueTable<RedirectData>::UpdateData,
                        base::Unretained(tables_->host_redirect_table()),
                        bbc.primary_key(), bbc));
     tables_->ExecuteDBTaskOnDBSequence(
-        base::BindOnce(&LoadingPredictorKeyValueTable<RedirectData>::UpdateData,
+        base::BindOnce(&KeyValueTable<RedirectData>::UpdateData,
                        base::Unretained(tables_->host_redirect_table()),
                        microsoft.primary_key(), microsoft));
   }
@@ -384,10 +387,10 @@
     test_origin_data_.insert({"abc.xyz", alphabet});
 
     tables_->ExecuteDBTaskOnDBSequence(base::BindOnce(
-        &LoadingPredictorKeyValueTable<OriginData>::UpdateData,
+        &KeyValueTable<OriginData>::UpdateData,
         base::Unretained(tables_->origin_table()), twitter.host(), twitter));
     tables_->ExecuteDBTaskOnDBSequence(base::BindOnce(
-        &LoadingPredictorKeyValueTable<OriginData>::UpdateData,
+        &KeyValueTable<OriginData>::UpdateData,
         base::Unretained(tables_->origin_table()), alphabet.host(), alphabet));
   }
 }
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc b/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
index 7ae28bb..6e3485a 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
@@ -44,9 +44,9 @@
 
 template <typename T>
 class FakeLoadingPredictorKeyValueTable
-    : public LoadingPredictorKeyValueTable<T> {
+    : public sqlite_proto::KeyValueTable<T> {
  public:
-  FakeLoadingPredictorKeyValueTable() : LoadingPredictorKeyValueTable<T>("") {}
+  FakeLoadingPredictorKeyValueTable() : sqlite_proto::KeyValueTable<T>("") {}
   void GetAllData(std::map<std::string, T>* data_map,
                   sql::Database* db) const override {
     *data_map = data_;
@@ -69,7 +69,9 @@
 class MockResourcePrefetchPredictorTables
     : public ResourcePrefetchPredictorTables {
  public:
-  MockResourcePrefetchPredictorTables(
+  using DBTask = base::OnceCallback<void(sql::Database*)>;
+
+  explicit MockResourcePrefetchPredictorTables(
       scoped_refptr<base::SequencedTaskRunner> db_task_runner)
       : ResourcePrefetchPredictorTables(std::move(db_task_runner)) {}
 
@@ -81,11 +83,11 @@
     std::move(task).Run(nullptr);
   }
 
-  LoadingPredictorKeyValueTable<RedirectData>* host_redirect_table() override {
+  sqlite_proto::KeyValueTable<RedirectData>* host_redirect_table() override {
     return &host_redirect_table_;
   }
 
-  LoadingPredictorKeyValueTable<OriginData>* origin_table() override {
+  sqlite_proto::KeyValueTable<OriginData>* origin_table() override {
     return &origin_table_;
   }
 
@@ -182,9 +184,9 @@
 }
 
 void ResourcePrefetchPredictorTest::TearDown() {
-  EXPECT_EQ(*predictor_->host_redirect_data_->DataCacheForTesting(),
+  EXPECT_EQ(predictor_->host_redirect_data_->GetAllCached(),
             mock_tables_->host_redirect_table_.data_);
-  EXPECT_EQ(*predictor_->origin_data_->DataCacheForTesting(),
+  EXPECT_EQ(predictor_->origin_data_->GetAllCached(),
             mock_tables_->origin_table_.data_);
   loading_predictor_->Shutdown();
 }
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 9673886..7d3a9adb5 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
@@ -437,6 +437,16 @@
 
     public static final String NTP_SNIPPETS_IS_SCHEDULED = "ntp_snippets.is_scheduled";
 
+    // Name of an application preference variable used to track whether or not the in-progress
+    // notification is being shown. This is an alternative to
+    // NotificationManager.getActiveNotifications, which isn't available prior to API level 23.
+    public static final String OFFLINE_AUTO_FETCH_SHOWING_IN_PROGRESS =
+            "offline_auto_fetch_showing_in_progress";
+    // The application preference variable which is set to the NotificationAction that triggered the
+    // cancellation, when a cancellation is requested by the user.
+    public static final String OFFLINE_AUTO_FETCH_USER_CANCEL_ACTION_IN_PROGRESS =
+            "offline_auto_fetch_user_cancel_action_in_progress";
+
     /**
      * Key to cache whether offline indicator v2 (persistent offline indicator) is enabled.
      */
@@ -802,6 +812,8 @@
                 NOTIFICATIONS_LAST_SHOWN_NOTIFICATION_TYPE,
                 NOTIFICATIONS_NEXT_TRIGGER,
                 NTP_SNIPPETS_IS_SCHEDULED,
+                OFFLINE_AUTO_FETCH_SHOWING_IN_PROGRESS,
+                OFFLINE_AUTO_FETCH_USER_CANCEL_ACTION_IN_PROGRESS,
                 OFFLINE_INDICATOR_V2_ENABLED,
                 PAYMENTS_PAYMENT_COMPLETE_ONCE,
                 PREFETCH_HAS_NEW_PAGES,
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/injected/api_implementation.js b/chrome/browser/resources/chromeos/accessibility/chromevox/injected/api_implementation.js
index 7f5a724..2392cc1 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/injected/api_implementation.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/injected/api_implementation.js
@@ -53,7 +53,7 @@
  * @return {boolean} True if default event processing should continue.
  */
 ApiImplementation.portSetup = function(event) {
-  if (event.data == 'PortSetup') {
+  if (event.data == 'cvox.PortSetup') {
     ApiImplementation.port = event.ports[0];
     ApiImplementation.port.onmessage = function(event) {
       ApiImplementation.dispatchApiMessage(JSON.parse(event.data));
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager.js
index 0752a1bf..5b17bb1b 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager.js
@@ -261,10 +261,9 @@
    * @private
    */
   onTreeChange_(treeChange) {
-    if (treeChange.type === chrome.automation.TreeChangeType.TEXT_CHANGED) {
-      return;
+    if (treeChange.type === chrome.automation.TreeChangeType.NODE_REMOVED) {
+      this.moveToValidNode();
     }
-    this.moveToValidNode();
   }
 
   // -------------------------------------------------------
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper.js
index f06469ad..7ed5720f 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper.js
@@ -212,6 +212,9 @@
     this.baseNode_ = baseNode;
 
     /** @private {function(chrome.automation.AutomationEvent)} */
+    this.childrenChangedHandler_ = this.refresh_.bind(this);
+
+    /** @private {function(chrome.automation.AutomationEvent)} */
     this.locationChangedHandler_ = SwitchAccess.refreshFocusRings;
   }
 
@@ -260,6 +263,9 @@
   onFocus() {
     super.onFocus();
     this.baseNode_.addEventListener(
+        chrome.automation.EventType.CHILDREN_CHANGED,
+        this.childrenChangedHandler_, false /* is_capture */);
+    this.baseNode_.addEventListener(
         chrome.automation.EventType.LOCATION_CHANGED,
         this.locationChangedHandler_, false /* is_capture */);
   }
@@ -268,10 +274,51 @@
   onUnfocus() {
     super.onUnfocus();
     this.baseNode_.removeEventListener(
+        chrome.automation.EventType.CHILDREN_CHANGED,
+        this.childrenChangedHandler_, false /* is_capture */);
+    this.baseNode_.removeEventListener(
         chrome.automation.EventType.LOCATION_CHANGED,
         this.locationChangedHandler_, false /* is_capture */);
   }
 
+  // ================= Private methods =================
+
+  /**
+   * Refreshes the children of this root node.
+   * @private
+   */
+  refresh_() {
+    // Find the currently focused child.
+    let focusedChild = null;
+    for (const child of this.children) {
+      if (child.isFocused()) {
+        focusedChild = child;
+        break;
+      }
+    }
+
+    // Update this RootNodeWrapper's children.
+    const childConstructor = (node) => new NodeWrapper(node, this);
+    try {
+      RootNodeWrapper.findAndSetChildren(this, childConstructor);
+    } catch (e) {
+      SwitchAccess.moveToValidNode();
+      return;
+    }
+
+    // Set the new instance of that child to be the focused node.
+    for (const child of this.children) {
+      if (child.isEquivalentTo(focusedChild)) {
+        SwitchAccess.forceFocusedNode(child);
+        return;
+      }
+    }
+
+    // If the previously focused node no longer exists, focus the first node in
+    // the group.
+    SwitchAccess.forceFocusedNode(this.children[0]);
+  }
+
   // ================= Static methods =================
 
   /**
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
index 99be7594..12a9269 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -1310,7 +1310,7 @@
       // Reload offline version of the sign-in extension, which will show
       // error itself.
       chrome.send('offlineLogin', [this.email_]);
-    } else if (!this.isLoadingUiShown_) {
+    } else if (!this.loadingFrameContents_) {
       $('bubble').showContentForElement(
           this, cr.ui.Bubble.Attachment.BOTTOM, error,
           BUBBLE_HORIZONTAL_PADDING, BUBBLE_VERTICAL_PADDING);
diff --git a/chrome/browser/safe_browsing/advanced_protection_status_manager.cc b/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
index 527cbaea..187c530 100644
--- a/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
+++ b/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/safe_browsing/advanced_protection_status_manager.h"
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/profiles/profile.h"
@@ -30,6 +31,10 @@
     base::TimeDelta::FromDays(1);
 const base::TimeDelta kRetryDelay = base::TimeDelta::FromMinutes(5);
 const base::TimeDelta kMinimumRefreshDelay = base::TimeDelta::FromMinutes(1);
+
+const char kForceTreatUserAsAdvancedProtection[] =
+    "safe-browsing-treat-user-as-advanced-protection";
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -219,8 +224,10 @@
       last_refreshed_.ToDeltaSinceWindowsEpoch().InMicroseconds());
 }
 
-bool AdvancedProtectionStatusManager::RequestsAdvancedProtectionVerdicts() {
-  return is_under_advanced_protection();
+bool AdvancedProtectionStatusManager::IsUnderAdvancedProtection() const {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+             kForceTreatUserAsAdvancedProtection) ||
+         is_under_advanced_protection_;
 }
 
 bool AdvancedProtectionStatusManager::IsUnconsentedPrimaryAccount(
diff --git a/chrome/browser/safe_browsing/advanced_protection_status_manager.h b/chrome/browser/safe_browsing/advanced_protection_status_manager.h
index 48ecd96ed..52274f3 100644
--- a/chrome/browser/safe_browsing/advanced_protection_status_manager.h
+++ b/chrome/browser/safe_browsing/advanced_protection_status_manager.h
@@ -34,13 +34,9 @@
                                   signin::IdentityManager* identity_manager);
   ~AdvancedProtectionStatusManager() override;
 
-  // If the primary account of associated profile is requesting advanced
-  // protection verdicts.
-  bool RequestsAdvancedProtectionVerdicts();
-
-  bool is_under_advanced_protection() const {
-    return is_under_advanced_protection_;
-  }
+  // Returns whether the unconsented primary account of the associated profile
+  // is under Advanced Protection.
+  bool IsUnderAdvancedProtection() const;
 
   // KeyedService:
   void Shutdown() override;
diff --git a/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc b/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
index 4bdc2ea..2a6b51c 100644
--- a/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
+++ b/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
@@ -96,7 +96,7 @@
   ASSERT_TRUE(aps_manager.GetUnconsentedPrimaryAccountId().empty());
 
   // If user's not signed-in. No refresh is required.
-  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_FALSE(aps_manager.IsRefreshScheduled());
   EXPECT_FALSE(
       pref_service_.HasPrefPath(prefs::kAdvancedProtectionLastRefreshInUs));
@@ -123,7 +123,7 @@
   // protection set.
   MakeOAuthTokenFetchFail(account_id,
                           /* is_transient_error = */ true);
-  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsUnderAdvancedProtection());
 
   EXPECT_THAT(histograms.GetAllSamples(kTokenFetchStatusMetric),
               testing::ElementsAre(base::Bucket(3 /*CONNECTION_FAILED*/, 1)));
@@ -154,7 +154,7 @@
   // protection set.
   MakeOAuthTokenFetchFail(account_id,
                           /* is_transient_error = */ false);
-  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsUnderAdvancedProtection());
 
   EXPECT_THAT(
       histograms.GetAllSamples(kTokenFetchStatusMetric),
@@ -185,7 +185,7 @@
   MakeOAuthTokenFetchSucceed(account_id,
                              /* is_under_advanced_protection = */ false);
 
-  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_FALSE(aps_manager.IsRefreshScheduled());
   EXPECT_TRUE(
       pref_service_.HasPrefPath(prefs::kAdvancedProtectionLastRefreshInUs));
@@ -212,7 +212,7 @@
   MakeOAuthTokenFetchSucceed(account_id,
                              /* is_under_advanced_protection = */ true);
 
-  EXPECT_TRUE(aps_manager.is_under_advanced_protection());
+  EXPECT_TRUE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_TRUE(aps_manager.IsRefreshScheduled());
   EXPECT_TRUE(
       pref_service_.HasPrefPath(prefs::kAdvancedProtectionLastRefreshInUs));
@@ -232,7 +232,7 @@
       &pref_service_, identity_test_env_.identity_manager(),
       base::TimeDelta() /*no min delay*/);
   ASSERT_FALSE(aps_manager.GetUnconsentedPrimaryAccountId().empty());
-  ASSERT_TRUE(aps_manager.is_under_advanced_protection());
+  ASSERT_TRUE(aps_manager.IsUnderAdvancedProtection());
 
   // A refresh is scheduled in the future.
   EXPECT_TRUE(aps_manager.IsRefreshScheduled());
@@ -254,7 +254,7 @@
 
   // Incognito profile should share the advanced protection status with the
   // original profile.
-  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsUnderAdvancedProtection());
   aps_manager.UnsubscribeFromSigninEvents();
 }
 
@@ -270,7 +270,7 @@
       &pref_service_, identity_test_env_.identity_manager(),
       base::TimeDelta() /*no min delay*/);
   ASSERT_FALSE(aps_manager.GetUnconsentedPrimaryAccountId().empty());
-  ASSERT_TRUE(aps_manager.is_under_advanced_protection());
+  ASSERT_TRUE(aps_manager.IsUnderAdvancedProtection());
 
   // Simulate gets refresh token.
   aps_manager.OnGetIDToken(account_id, kIdTokenAdvancedProtectionEnabled);
@@ -288,16 +288,16 @@
   AdvancedProtectionStatusManager aps_manager(
       &pref_service_, identity_test_env_.identity_manager(),
       base::TimeDelta() /*no min delay*/);
-  ASSERT_FALSE(aps_manager.is_under_advanced_protection());
+  ASSERT_FALSE(aps_manager.IsUnderAdvancedProtection());
   ASSERT_TRUE(aps_manager.GetUnconsentedPrimaryAccountId().empty());
 
   SignIn("test@test.com",
          /* is_under_advanced_protection = */ true);
-  EXPECT_TRUE(aps_manager.is_under_advanced_protection());
+  EXPECT_TRUE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_TRUE(aps_manager.IsRefreshScheduled());
 
   identity_test_env_.ClearPrimaryAccount();
-  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_TRUE(
       pref_service_.HasPrefPath(prefs::kAdvancedProtectionLastRefreshInUs));
   EXPECT_FALSE(aps_manager.IsRefreshScheduled());
@@ -309,12 +309,12 @@
   AdvancedProtectionStatusManager aps_manager(
       &pref_service_, identity_test_env_.identity_manager(),
       base::TimeDelta() /*no min delay*/);
-  ASSERT_FALSE(aps_manager.is_under_advanced_protection());
+  ASSERT_FALSE(aps_manager.IsUnderAdvancedProtection());
   ASSERT_TRUE(aps_manager.GetUnconsentedPrimaryAccountId().empty());
 
   CoreAccountId account_id = SignIn("test@test.com",
                                     /* is_under_advanced_protection = */ false);
-  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_FALSE(aps_manager.IsRefreshScheduled());
 
   // Simulates account update.
@@ -323,7 +323,7 @@
       ->UpdateAccountInfo(account_id,
                           /*is_child_account=*/false,
                           /*is_under_advanced_protection=*/true);
-  EXPECT_TRUE(aps_manager.is_under_advanced_protection());
+  EXPECT_TRUE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_TRUE(aps_manager.IsRefreshScheduled());
 
   // This call is necessary to ensure that the account removal is fully
@@ -332,7 +332,7 @@
   identity_test_env_.identity_manager()->GetAccountsMutator()->RemoveAccount(
       account_id,
       signin_metrics::SourceForRefreshTokenOperation::kUserMenu_RemoveAccount);
-  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_TRUE(
       pref_service_.HasPrefPath(prefs::kAdvancedProtectionLastRefreshInUs));
   EXPECT_FALSE(aps_manager.IsRefreshScheduled());
@@ -352,7 +352,7 @@
 
   // Now that we've signed into Advanced Protection, we should have a scheduled
   // refresh.
-  EXPECT_TRUE(aps_manager.is_under_advanced_protection());
+  EXPECT_TRUE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_TRUE(aps_manager.IsRefreshScheduled());
 
   // Skip the 24 hour wait, and try to refresh the token now.
@@ -360,7 +360,7 @@
   MakeOAuthTokenFetchSucceed(account_id,
                              /* is_under_advanced_protection = */ false);
 
-  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_FALSE(aps_manager.IsRefreshScheduled());
 
   aps_manager.UnsubscribeFromSigninEvents();
@@ -382,13 +382,13 @@
       &pref_service_, identity_test_env_.identity_manager(),
       base::TimeDelta() /*no min delay*/);
   ASSERT_FALSE(aps_manager.GetUnconsentedPrimaryAccountId().empty());
-  ASSERT_TRUE(aps_manager.is_under_advanced_protection());
+  ASSERT_TRUE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_TRUE(aps_manager.IsRefreshScheduled());
 
   MakeOAuthTokenFetchSucceed(account_id,
                              /* is_under_advanced_protection = */ false);
 
-  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_FALSE(aps_manager.IsRefreshScheduled());
 
   aps_manager.UnsubscribeFromSigninEvents();
@@ -401,7 +401,7 @@
   AdvancedProtectionStatusManager aps_manager(
       &pref_service_, identity_test_env_.identity_manager(),
       base::TimeDelta() /*no min delay*/);
-  ASSERT_FALSE(aps_manager.is_under_advanced_protection());
+  ASSERT_FALSE(aps_manager.IsUnderAdvancedProtection());
   ASSERT_TRUE(aps_manager.GetUnconsentedPrimaryAccountId().empty());
 
   // Sign in, but don't set this as the primary account.
@@ -412,7 +412,7 @@
       {{account_info.email, account_info.gaia}});
   identity_test_env_.UpdateAccountInfoForAccount(account_info);
 
-  EXPECT_TRUE(aps_manager.is_under_advanced_protection());
+  EXPECT_TRUE(aps_manager.IsUnderAdvancedProtection());
   EXPECT_TRUE(aps_manager.IsRefreshScheduled());
 
   aps_manager.UnsubscribeFromSigninEvents();
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index 88193e1..e0e6f99 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -34,6 +34,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/google/core/common/google_util.h"
 #include "components/password_manager/core/browser/compromised_credentials_table.h"
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -1641,7 +1642,8 @@
   for (const std::string& domain : matching_domains) {
     password_store->AddCompromisedCredentials(
         password_manager::CompromisedCredentials(
-            GURL(domain), base::ASCIIToUTF16(username), base::Time::Now(),
+            password_manager::GetSignonRealm(GURL(domain)),
+            base::ASCIIToUTF16(username), base::Time::Now(),
             password_manager::CompromiseType::kPhished));
   }
 }
@@ -1706,7 +1708,7 @@
 #if BUILDFLAG(FULL_SAFE_BROWSING)
 bool ChromePasswordProtectionService::IsUnderAdvancedProtection() {
   return AdvancedProtectionStatusManagerFactory::GetForProfile(profile_)
-      ->is_under_advanced_protection();
+      ->IsUnderAdvancedProtection();
 }
 
 gfx::Size ChromePasswordProtectionService::GetCurrentContentAreaSize() const {
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
index 877dbab..5bb0128 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
@@ -75,7 +75,7 @@
         std::move(request),
         /*authorized=*/safe_browsing::AdvancedProtectionStatusManagerFactory::
             GetForProfile(profile_)
-                ->is_under_advanced_protection());
+                ->IsUnderAdvancedProtection());
     return;
   }
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
index c1cb5ce..299e017 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
@@ -170,22 +170,6 @@
   return mime_type;
 }
 
-// File types supported for DLP scanning.
-// Keep sorted for efficient access.
-constexpr const std::array<const base::FilePath::CharType*, 21>
-    kSupportedDLPFileTypes = {
-        FILE_PATH_LITERAL(".7z"),   FILE_PATH_LITERAL(".bzip"),
-        FILE_PATH_LITERAL(".cab"),  FILE_PATH_LITERAL(".doc"),
-        FILE_PATH_LITERAL(".docx"), FILE_PATH_LITERAL(".eps"),
-        FILE_PATH_LITERAL(".gzip"), FILE_PATH_LITERAL(".odt"),
-        FILE_PATH_LITERAL(".pdf"),  FILE_PATH_LITERAL(".ppt"),
-        FILE_PATH_LITERAL(".pptx"), FILE_PATH_LITERAL(".ps"),
-        FILE_PATH_LITERAL(".rar"),  FILE_PATH_LITERAL(".rtf"),
-        FILE_PATH_LITERAL(".tar"),  FILE_PATH_LITERAL(".txt"),
-        FILE_PATH_LITERAL(".wpd"),  FILE_PATH_LITERAL(".xls"),
-        FILE_PATH_LITERAL(".xlsx"), FILE_PATH_LITERAL(".xps"),
-        FILE_PATH_LITERAL(".zip")};
-
 }  // namespace
 
 // A BinaryUploadService::Request implementation that gets the data to scan
@@ -268,35 +252,6 @@
   RunCallback();
 }
 
-// static
-bool DeepScanningDialogDelegate::FileTypeSupported(const bool for_malware_scan,
-                                                   const bool for_dlp_scan,
-                                                   const base::FilePath& path) {
-  // At least one of the booleans needs to be true.
-  DCHECK(for_malware_scan || for_dlp_scan);
-
-  // Accept any file type for malware scans.
-  if (for_malware_scan)
-    return true;
-
-  // Accept any file type in the supported list for DLP scans.
-  if (for_dlp_scan) {
-    base::FilePath::StringType extension(path.FinalExtension());
-    std::transform(extension.begin(), extension.end(), extension.begin(),
-                   tolower);
-
-    // TODO: Replace this DCHECK with a static assert once std::is_sorted is
-    // constexpr in C++20.
-    DCHECK(std::is_sorted(
-        kSupportedDLPFileTypes.begin(), kSupportedDLPFileTypes.end(),
-        [](const base::FilePath::StringType& a,
-           const base::FilePath::StringType& b) { return a.compare(b) < 0; }));
-    return std::binary_search(kSupportedDLPFileTypes.begin(),
-                              kSupportedDLPFileTypes.end(), extension);
-  }
-
-  return false;
-}
 
 bool DeepScanningDialogDelegate::ResultShouldAllowDataUse(
     BinaryUploadService::Result result) {
@@ -564,6 +519,7 @@
     } else {
       ++file_result_count_;
       result_.paths_results[i] = true;
+      // TODO(crbug/1013584): Handle unsupported types appropriately.
     }
   }
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
index 728034d7..bb225f2 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
@@ -172,11 +172,6 @@
   // DeepScanningDialogDelegates.
   static void SetFactoryForTesting(Factory factory);
 
-  // Returns true if the given file type is supported for scanning.
-  static bool FileTypeSupported(const bool for_malware_scan,
-                                const bool for_dlp_scan,
-                                const base::FilePath& path);
-
   // Determines if a request result should be used to allow a data use or to
   // block it.
   static bool ResultShouldAllowDataUse(BinaryUploadService::Result result);
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc
index ca6c2587..692ea7d 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc
@@ -15,9 +15,11 @@
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/ui_base_types.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/text_constants.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/image_view.h"
@@ -48,12 +50,14 @@
 constexpr SkColor kScanPendingSideImageColor = gfx::kGoogleBlue400;
 constexpr SkColor kScanDoneSideImageColor = SkColorSetRGB(0xFF, 0xFF, 0xFF);
 
-constexpr int kSideImageSize = 35;
+constexpr int kSideImageSize = 24;
 constexpr int kTopImageSize = 100;
 
 constexpr gfx::Insets kSideImageInsets = gfx::Insets(8, 8, 8, 8);
-constexpr gfx::Insets KSideIconLayoutInsets = gfx::Insets(0, 10);
-constexpr int kSideIconBetweenChildSpacing = 20;
+constexpr gfx::Insets kMessageAndIconRowInsets = gfx::Insets(0, 32, 0, 48);
+constexpr int kSideIconBetweenChildSpacing = 16;
+
+constexpr int kTextLineHeight = 20;
 
 // A simple background class to show a colored circle behind the side icon once
 // the scanning is done.
@@ -243,7 +247,7 @@
   if (!scan_success_.has_value() || !scan_success_.value()) {
     DialogDelegate::set_button_label(ui::DIALOG_BUTTON_CANCEL,
                                      GetCancelButtonText());
-    DialogDelegate::set_default_button(ui::DIALOG_BUTTON_CANCEL);
+    DialogDelegate::set_default_button(ui::DIALOG_BUTTON_NONE);
   }
 
   // TODO(domfc): Add "Learn more" button setup for scan failures.
@@ -302,17 +306,20 @@
                                         kTopImageSize, GetImageColor()));
   image_ = layout->AddView(std::move(image));
 
+  // Add padding to distance the top image from the icon and message.
+  layout->AddPaddingRow(views::GridLayout::kFixedSize, 16);
+
   // Add the side icon and message row.
   layout->StartRow(views::GridLayout::kFixedSize, 0);
   auto icon_and_message_row = std::make_unique<views::View>();
   auto* row_layout =
       icon_and_message_row->SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kHorizontal, KSideIconLayoutInsets,
+          views::BoxLayout::Orientation::kHorizontal, kMessageAndIconRowInsets,
           kSideIconBetweenChildSpacing));
   row_layout->set_main_axis_alignment(
       views::BoxLayout::MainAxisAlignment::kStart);
   row_layout->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kStart);
+      views::BoxLayout::CrossAxisAlignment::kCenter);
 
   // Add the side icon.
   icon_and_message_row->AddChildView(CreateSideIcon());
@@ -320,6 +327,9 @@
   // Add the message.
   auto label = std::make_unique<views::Label>(GetDialogMessage());
   label->SetMultiLine(true);
+  label->SetLineHeight(kTextLineHeight);
+  label->SetVerticalAlignment(gfx::ALIGN_MIDDLE);
+  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   message_ = icon_and_message_row->AddChildView(std::move(label));
 
   layout->AddView(std::move(icon_and_message_row));
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
index d1385dd..9db7d8f 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
@@ -185,4 +185,51 @@
       50);
 }
 
+std::array<const base::FilePath::CharType*, 21> SupportedDlpFileTypes() {
+  // Keep sorted for efficient access.
+  static constexpr const std::array<const base::FilePath::CharType*, 21>
+      kSupportedDLPFileTypes = {
+          FILE_PATH_LITERAL(".7z"),   FILE_PATH_LITERAL(".bzip"),
+          FILE_PATH_LITERAL(".cab"),  FILE_PATH_LITERAL(".doc"),
+          FILE_PATH_LITERAL(".docx"), FILE_PATH_LITERAL(".eps"),
+          FILE_PATH_LITERAL(".gzip"), FILE_PATH_LITERAL(".odt"),
+          FILE_PATH_LITERAL(".pdf"),  FILE_PATH_LITERAL(".ppt"),
+          FILE_PATH_LITERAL(".pptx"), FILE_PATH_LITERAL(".ps"),
+          FILE_PATH_LITERAL(".rar"),  FILE_PATH_LITERAL(".rtf"),
+          FILE_PATH_LITERAL(".tar"),  FILE_PATH_LITERAL(".txt"),
+          FILE_PATH_LITERAL(".wpd"),  FILE_PATH_LITERAL(".xls"),
+          FILE_PATH_LITERAL(".xlsx"), FILE_PATH_LITERAL(".xps"),
+          FILE_PATH_LITERAL(".zip")};
+  // TODO: Replace this DCHECK with a static assert once std::is_sorted is
+  // constexpr in C++20.
+  DCHECK(std::is_sorted(
+      kSupportedDLPFileTypes.begin(), kSupportedDLPFileTypes.end(),
+      [](const base::FilePath::StringType& a,
+         const base::FilePath::StringType& b) { return a.compare(b) < 0; }));
+
+  return kSupportedDLPFileTypes;
+}
+
+bool FileTypeSupported(bool for_malware_scan,
+                       bool for_dlp_scan,
+                       const base::FilePath& path) {
+  // At least one of the booleans needs to be true.
+  DCHECK(for_malware_scan || for_dlp_scan);
+
+  // Accept any file type for malware scans.
+  if (for_malware_scan)
+    return true;
+
+  // Accept any file type in the supported list for DLP scans.
+  if (for_dlp_scan) {
+    base::FilePath::StringType extension(path.FinalExtension());
+    std::transform(extension.begin(), extension.end(), extension.begin(),
+                   tolower);
+
+    auto dlp_types = SupportedDlpFileTypes();
+    return std::binary_search(dlp_types.begin(), dlp_types.end(), extension);
+  }
+
+  return false;
+}
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
index 082d018..4a93e1f 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
@@ -53,6 +53,14 @@
                            const std::string& result,
                            bool success);
 
+// Returns an array of the file types supported for DLP scanning.
+std::array<const base::FilePath::CharType*, 21> SupportedDlpFileTypes();
+
+// Returns true if the given file type is supported for DLP scanning.
+bool FileTypeSupported(bool for_malware_scan,
+                       bool for_dlp_scan,
+                       const base::FilePath& path);
+
 }  // namespace safe_browsing
 
 #endif  // CHROME_BROWSER_SAFE_BROWSING_CLOUD_CONTENT_SCANNING_DEEP_SCANNING_UTILS_H_
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc
index ebc1fa0..76885e9b7 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc
@@ -7,6 +7,7 @@
 #include <string>
 #include <tuple>
 
+#include "base/files/file_path.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -229,4 +230,54 @@
       histograms().GetTotalCountsForPrefix("SafeBrowsing.DeepScan.").size());
 }
 
+class DeepScanningUtilsFileTypeSupportedTest : public testing::Test {
+ protected:
+  std::vector<base::FilePath::StringType> UnsupportedDlpFileTypes() {
+    return {FILE_PATH_LITERAL(".these"), FILE_PATH_LITERAL(".types"),
+            FILE_PATH_LITERAL(".are"), FILE_PATH_LITERAL(".not"),
+            FILE_PATH_LITERAL(".supported")};
+  }
+
+  base::FilePath FilePath(const base::FilePath::StringType& type) {
+    return base::FilePath(FILE_PATH_LITERAL("foo") + type);
+  }
+};
+
+TEST_F(DeepScanningUtilsFileTypeSupportedTest, DLP) {
+  // With a DLP-only scan, only the types returned by SupportedDlpFileTypes()
+  // will be supported, and other types will fail.
+  for (const base::FilePath::StringType type : SupportedDlpFileTypes()) {
+    EXPECT_TRUE(FileTypeSupported(/*for_malware_scan=*/false,
+                                  /*for_dlp_scan=*/true, FilePath(type)));
+  }
+  for (const auto& type : UnsupportedDlpFileTypes()) {
+    EXPECT_FALSE(FileTypeSupported(/*for_malware_scan=*/false,
+                                   /*for_dlp_scan=*/true, FilePath(type)));
+  }
+}
+
+TEST_F(DeepScanningUtilsFileTypeSupportedTest, Malware) {
+  // With a Malware-only scan, every type is supported.
+  for (const base::FilePath::StringType type : SupportedDlpFileTypes()) {
+    EXPECT_TRUE(FileTypeSupported(/*for_malware_scan=*/true,
+                                  /*for_dlp_scan=*/false, FilePath(type)));
+  }
+  for (const auto& type : UnsupportedDlpFileTypes()) {
+    EXPECT_TRUE(FileTypeSupported(/*for_malware_scan=*/true,
+                                  /*for_dlp_scan=*/false, FilePath(type)));
+  }
+}
+
+TEST_F(DeepScanningUtilsFileTypeSupportedTest, MalwareAndDLP) {
+  // With a Malware and DLP scan, every type is supported.
+  for (const base::FilePath::StringType type : SupportedDlpFileTypes()) {
+    EXPECT_TRUE(FileTypeSupported(/*for_malware_scan=*/true,
+                                  /*for_dlp_scan=*/true, FilePath(type)));
+  }
+  for (const auto& type : UnsupportedDlpFileTypes()) {
+    EXPECT_TRUE(FileTypeSupported(/*for_malware_scan=*/true,
+                                  /*for_dlp_scan=*/true, FilePath(type)));
+  }
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
index 02be0ad..f7e3fbd 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -256,6 +256,9 @@
           CheckContentComplianceValues::CHECK_UPLOADS_AND_DOWNLOADS)
     return false;
 
+  // TODO(crbug/1013584): Call FileTypeSupported from DeepScanningUtils around
+  // here and handle both supported and unsupported types appropriately.
+
   Profile* profile = Profile::FromBrowserContext(GetBrowserContext());
   // If there's no valid DM token, the upload will fail, so we can skip
   // uploading now.
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
index 5a65ce3..77c015d8 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
@@ -161,11 +161,7 @@
     is_under_advanced_protection_ =
         profile &&
         AdvancedProtectionStatusManagerFactory::GetForProfile(profile)
-            ->is_under_advanced_protection();
-    requests_ap_verdicts_ =
-        profile &&
-        AdvancedProtectionStatusManagerFactory::GetForProfile(profile)
-            ->RequestsAdvancedProtectionVerdicts();
+            ->IsUnderAdvancedProtection();
 
     int password_protected_allowed_policy =
         g_browser_process->local_state()->GetInteger(
@@ -559,7 +555,7 @@
     request->mutable_archived_binary()->Swap(&archived_binaries_);
   request->set_archive_file_count(file_count_);
   request->set_archive_directory_count(directory_count_);
-  request->set_request_ap_verdicts(requests_ap_verdicts_);
+  request->set_request_ap_verdicts(is_under_advanced_protection_);
 
   if (!request->SerializeToString(&client_download_request_data_)) {
     FinishRequest(DownloadCheckResult::UNKNOWN, REASON_INVALID_REQUEST_PROTO);
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
index ff12d601..7d67d0b 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
@@ -200,11 +200,6 @@
   bool is_extended_reporting_ = false;
   bool is_incognito_ = false;
   bool is_under_advanced_protection_ = false;
-  // Boolean indicating whether the user requests AP verdicts. Note that this is
-  // distinct from |is_under_advanced_protection_| while:
-  //  - The feature is still partially rolled out
-  //  - The feature has been force enabled from chrome://flags
-  bool requests_ap_verdicts_ = false;
   bool password_protected_allowed_ = true;
 
   bool is_password_protected_ = false;
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
index d5b1445..22b000af 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
@@ -326,7 +326,7 @@
       content::DownloadItemUtils::GetBrowserContext(item));
   if (profile &&
       AdvancedProtectionStatusManagerFactory::GetForProfile(profile)
-          ->RequestsAdvancedProtectionVerdicts() &&
+          ->IsUnderAdvancedProtection() &&
       item->GetDangerType() ==
           download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT) {
     learn_more_url = GURL(chrome::kAdvancedProtectionDownloadLearnMoreURL);
diff --git a/chrome/browser/sharing/sharing_metrics.cc b/chrome/browser/sharing/sharing_metrics.cc
index 667b669..a20524d 100644
--- a/chrome/browser/sharing/sharing_metrics.cc
+++ b/chrome/browser/sharing/sharing_metrics.cc
@@ -232,6 +232,7 @@
       base::UmaHistogramMediumTimes(platform_suffixed_name, time);
       break;
     case chrome_browser_sharing::MessageType::SMS_FETCH_REQUEST:
+    case chrome_browser_sharing::MessageType::DISCOVERY_REQUEST:
       base::UmaHistogramCustomTimes(
           suffixed_name, time, /*min=*/base::TimeDelta::FromMilliseconds(1),
           /*max=*/base::TimeDelta::FromMinutes(10), /*buckets=*/50);
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_observer.cc b/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
index d9c93662..8ae6bf2 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
@@ -38,6 +38,22 @@
 
 using content::NavigationEntry;
 
+// static
+void SupervisedUserNavigationObserver::MaybeCreateForWebContents(
+    content::WebContents* web_contents) {
+  DCHECK(web_contents);
+  Profile* user_profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  if (!user_profile->IsSupervised())
+    return;
+
+  if (!FromWebContents(web_contents)) {
+    web_contents->SetUserData(
+        UserDataKey(),
+        base::WrapUnique(new SupervisedUserNavigationObserver(web_contents)));
+  }
+}
+
 SupervisedUserNavigationObserver::~SupervisedUserNavigationObserver() {
   supervised_user_service_->RemoveObserver(this);
 }
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_observer.h b/chrome/browser/supervised_user/supervised_user_navigation_observer.h
index 80c0783..9f36d45 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_observer.h
+++ b/chrome/browser/supervised_user/supervised_user_navigation_observer.h
@@ -35,6 +35,10 @@
       public SupervisedUserServiceObserver,
       public supervised_user::mojom::SupervisedUserCommands {
  public:
+  // Creates SupervisedUserNavigationObserver if the profile is a child user.
+  static void MaybeCreateForWebContents(content::WebContents* web_contents);
+  static void CreateForWebContents(content::WebContents* web_contents) = delete;
+
   ~SupervisedUserNavigationObserver() override;
 
   const std::vector<std::unique_ptr<const sessions::SerializedNavigationEntry>>&
diff --git a/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc b/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
index 852a32a..ce738cd 100644
--- a/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
@@ -188,7 +188,6 @@
                                LOCAL_DELETION, 0);
 }
 
-// Flaky on all platform. See crbug.com/971666
 IN_PROC_BROWSER_TEST_F(TwoClientAutofillProfileSyncTest,
                        AddDuplicateProfiles_OneIsVerified) {
   ASSERT_TRUE(SetupClients());
@@ -210,13 +209,20 @@
   EXPECT_EQ(verified_origin, GetAllAutoFillProfiles(0)[0]->origin());
   EXPECT_EQ(verified_origin, GetAllAutoFillProfiles(1)[0]->origin());
 
-  histograms.ExpectBucketCount("Sync.ModelTypeEntityChange3.AUTOFILL_PROFILE",
-                               LOCAL_DELETION, 0);
+  // Among duplicate profiles, sync prefers the one with largest GUID. If
+  // |profile0| (committed first) has a smaller GUID, client 1 should upload its
+  // deletion to the server. Otherwise, no deletion should occur.
+  if (profile1.guid() > profile0.guid()) {
+    histograms.ExpectBucketCount("Sync.ModelTypeEntityChange3.AUTOFILL_PROFILE",
+                                 LOCAL_DELETION, 1);
+  } else {
+    histograms.ExpectBucketCount("Sync.ModelTypeEntityChange3.AUTOFILL_PROFILE",
+                                 LOCAL_DELETION, 0);
+  }
 }
 
-IN_PROC_BROWSER_TEST_F(
-    TwoClientAutofillProfileSyncTest,
-    AddDuplicateProfiles_OneIsVerified_NonverifiedComesLater) {
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillProfileSyncTest,
+                       AddDuplicateProfiles_OneAtStart_OtherComesLater) {
   ASSERT_TRUE(SetupClients());
   base::HistogramTester histograms;
 
@@ -225,26 +231,23 @@
       autofill::test::GetVerifiedProfile();  // I.e. Full + Verified.
   std::string verified_origin = profile1.origin();
 
-  // We start by having the verified profile.
-  AddProfile(1, profile1);
+  AddProfile(0, profile0);
   ASSERT_TRUE(SetupSync());
   EXPECT_TRUE(AutofillProfileChecker(0, 1).Wait());
 
   EXPECT_EQ(1U, GetAllAutoFillProfiles(0).size());
-  EXPECT_EQ(verified_origin, GetAllAutoFillProfiles(0)[0]->origin());
-  EXPECT_EQ(verified_origin, GetAllAutoFillProfiles(1)[0]->origin());
 
-  // Add the same (but non-verified) profile on the other client, afterwards.
-  AddProfile(0, profile0);
+  // Add the same (but verified) profile on the other client, afterwards.
+  AddProfile(1, profile1);
   EXPECT_TRUE(AutofillProfileChecker(0, 1).Wait());
 
-  // The profiles should de-duplicate via sync on both other client, the
-  // verified one should win.
+  // The latter addition is caught in deduplication logic in PDM to sync. As a
+  // result, both clients end up with the non-verified profile.
   EXPECT_EQ(1U, GetAllAutoFillProfiles(0).size());
   EXPECT_EQ(1U, GetAllAutoFillProfiles(0).size());
 
-  EXPECT_EQ(verified_origin, GetAllAutoFillProfiles(0)[0]->origin());
-  EXPECT_EQ(verified_origin, GetAllAutoFillProfiles(1)[0]->origin());
+  EXPECT_NE(verified_origin, GetAllAutoFillProfiles(0)[0]->origin());
+  EXPECT_NE(verified_origin, GetAllAutoFillProfiles(1)[0]->origin());
 
   histograms.ExpectBucketCount("Sync.ModelTypeEntityChange3.AUTOFILL_PROFILE",
                                LOCAL_DELETION, 0);
diff --git a/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
index 0baac6c..2e46ec1a 100644
--- a/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
@@ -465,7 +465,7 @@
 
 // Flaky. http://crbug.com/917498
 IN_PROC_BROWSER_TEST_F(TwoClientWalletSyncTest,
-                       DISABLED_ServerAddressConvertsToSameLocalAddress) {
+                       ServerAddressConvertsToSameLocalAddress) {
   GetFakeServer()->SetWalletData(
       {CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
        CreateDefaultSyncPaymentsCustomerData()});
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index cb9568c..68eaa55 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3411,6 +3411,13 @@
         "views/ssl_client_certificate_selector_mac.h",
         "views/ssl_client_certificate_selector_mac.mm",
       ]
+
+      # The Views task manager is not used on Mac - a native Cocoa
+      # implementation is used instead.
+      sources -= [
+        "views/task_manager_view.cc",
+        "views/task_manager_view.h",
+      ]
     } else {
       sources += [
         "views/create_application_shortcut_view.cc",
diff --git a/chrome/browser/ui/android/page_info/page_info_controller_android.cc b/chrome/browser/ui/android/page_info/page_info_controller_android.cc
index e00c896a..59360ec 100644
--- a/chrome/browser/ui/android/page_info/page_info_controller_android.cc
+++ b/chrome/browser/ui/android/page_info/page_info_controller_android.cc
@@ -134,6 +134,10 @@
   base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
   if (cmd->HasSwitch(switches::kEnableExperimentalWebPlatformFeatures))
     permissions_to_display.push_back(ContentSettingsType::BLUETOOTH_SCANNING);
+  if (base::FeatureList::IsEnabled(features::kWebXrPermissionsApi)) {
+    permissions_to_display.push_back(ContentSettingsType::VR);
+    permissions_to_display.push_back(ContentSettingsType::AR);
+  }
 
   std::map<ContentSettingsType, ContentSetting>
       user_specified_settings_to_display;
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index d2e5831..3bb58a3 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -828,6 +828,10 @@
       <message name="IDS_ADS_PERMISSION_TITLE" desc="Title for the ads permission [CHAR-LIMIT=32]">
          Ads
       </message>
+      <!-- TODO(crbug.com/1041009): Finalize WebXr Permissions strings -->
+      <message name="IDS_AR_PERMISSION_TITLE" desc="Title of the permission to use Augmented Reality [CHAR-LIMIT=32]">
+        Augmented Reality
+      </message>
       <message name="IDS_COOKIES_TITLE" desc="Title for the Cookies settings screen [CHAR-LIMIT=32]">
         Cookies
       </message>
@@ -861,6 +865,10 @@
       <message name="IDS_CLIPBOARD_PERMISSION_TITLE" desc="Title of the permission to read from clipboard [CHAR-LIMIT=32]">
         Clipboard
       </message>
+      <!-- TODO(crbug.com/1041009): Finalize WebXr Permissions strings -->
+      <message name="IDS_VR_PERMISSION_TITLE" desc="Title of the permission to use Virtual Reality [CHAR-LIMIT=32]">
+        Virtual Reality
+      </message>
       <message name="IDS_ENABLE_NOTIFICATIONS_VIBRATE_TITLE" desc="Title for the preference to enable vibration for notifications">
         Vibrate
       </message>
@@ -996,12 +1004,26 @@
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_PROTECTED_CONTENT_BLOCKED" desc="Summary text explaining that sites are blocked from playing protected content.">
         Block sites from playing protected content
       </message>
+      <!-- TODO(crbug.com/1041009): Finalize WebXr Permissions strings -->
+      <message name="IDS_WEBSITE_SETTINGS_CATEGORY_AR_ASK" desc="Summary text explaining that sites need to ask for permission before using AR and that it is the recommended setting.">
+        Ask before allowing sites to receive camera position or map your room (recommended)
+      </message>
+      <message name="IDS_WEBSITE_SETTINGS_CATEGORY_AR_BLOCKED" desc="Summary text explaining that sites are blocked from using AR.">
+        Block sites from receiving camera position or mapping your room
+      </message>
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_NFC_ASK" desc="Summary text explaining that sites need to ask for permission before using NFC and that it is the recommended setting.">
         Ask before allowing sites to send and receive info when you tap NFC devices (recommended)
       </message>
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_NFC_BLOCKED" desc="Summary text explaining that sites are blocked from using NFC.">
         Block sites from sending and receiving info when you tap NFC devices
       </message>
+      <!-- TODO(crbug.com/1041009): Finalize WebXr Permissions strings -->
+      <message name="IDS_WEBSITE_SETTINGS_CATEGORY_VR_ASK" desc="Summary text explaining that sites need to ask for permission before using VR and that it is the recommended setting.">
+        Ask before allowing sites to access your virtual reality device and data (recommended)
+      </message>
+      <message name="IDS_WEBSITE_SETTINGS_CATEGORY_VR_BLOCKED" desc="Summary text explaining that sites are blocked from using VR.">
+        Block sites from accessing your virtual reality device and data
+      </message>
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_SENSORS_ALLOWED" desc="Summary text explaining that sites are allowed to use device's sensors and that it is the recommended setting.">
         Allow sites to access sensors (recommended)
       </message>
@@ -2048,6 +2070,9 @@
       <message name="IDS_CONTEXTMENU_LOAD_ORIGINAL_IMAGE" desc="Context sensitive menu item for Lite mode low fidelity placeholder images that loads the original version in place. [CHAR-LIMIT=30]">
         Load image
       </message>
+      <message name="IDS_CONTEXTMENU_COPY_IMAGE" desc="Context sensitive menu item for copying the selected image to the system clipboard. [CHAR-LIMIT=30]">
+        Copy image
+      </message>
       <message name="IDS_CONTEXTMENU_SEARCH_WITH_GOOGLE_LENS" desc="Context sensitive menu item for deep linking into google lens. [CHAR-LIMIT=30]">
         Search with Google Lens <ph name="BEGIN_NEW">&lt;new&gt;</ph>New<ph name="END_NEW">&lt;/new&gt;</ph>
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTMENU_COPY_IMAGE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTMENU_COPY_IMAGE.png.sha1
new file mode 100644
index 0000000..39fe2b2
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTMENU_COPY_IMAGE.png.sha1
@@ -0,0 +1 @@
+12e78b822b60d1e3927b1eac956a1bc5c7e26ab3
\ No newline at end of file
diff --git a/chrome/browser/ui/cocoa/task_manager_mac.mm b/chrome/browser/ui/cocoa/task_manager_mac.mm
index 709beec..e5cc935 100644
--- a/chrome/browser/ui/cocoa/task_manager_mac.mm
+++ b/chrome/browser/ui/cocoa/task_manager_mac.mm
@@ -41,10 +41,6 @@
   return [NSString stringWithFormat:@"%d", id];
 }
 
-bool ShouldUseViewsTaskManager() {
-  return base::FeatureList::IsEnabled(features::kViewsTaskManager);
-}
-
 }  // namespace
 
 @interface TaskManagerWindowController (Private)
@@ -743,14 +739,10 @@
 
 // Declared in browser_dialogs.h.
 task_manager::TaskManagerTableModel* ShowTaskManager(Browser* browser) {
-  if (ShouldUseViewsTaskManager())
-    return chrome::ShowTaskManagerViews(browser);
   return task_manager::TaskManagerMac::Show();
 }
 
 void HideTaskManager() {
-  if (ShouldUseViewsTaskManager())
-    return chrome::HideTaskManagerViews();
   task_manager::TaskManagerMac::Hide();
 }
 
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
index cf7d88c..4095d3f4 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
@@ -388,11 +388,6 @@
     delegate_->EnableSync(account, is_default_promo_account);
 }
 
-void ManagePasswordsBubbleModel::OnSkipSignInClicked() {
-  GetProfile()->GetPrefs()->SetBoolean(
-      password_manager::prefs::kWasSignInPasswordPromoClicked, true);
-}
-
 #if defined(PASSWORD_STORE_SELECT_ENABLED)
 void ManagePasswordsBubbleModel::OnToggleAccountStore(bool is_checked) {
   delegate_->GetPasswordFeatureManager()->SetDefaultPasswordStore(
@@ -427,10 +422,6 @@
          IsSyncUser(GetProfile());
 }
 
-const base::string16& ManagePasswordsBubbleModel::GetCurrentUsername() const {
-  return pending_password_.username_value;
-}
-
 int ManagePasswordsBubbleModel::GetTopIllustration(bool dark_mode) const {
   if (state_ == password_manager::ui::PENDING_PASSWORD_UPDATE_STATE ||
       state_ == password_manager::ui::PENDING_PASSWORD_STATE) {
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.h b/chrome/browser/ui/passwords/manage_passwords_bubble_model.h
index ef1b2a89..795ddf7e 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.h
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.h
@@ -86,10 +86,6 @@
   void OnSignInToChromeClicked(const AccountInfo& account,
                                bool is_default_promo_account);
 
-  // Called by the view when the "No thanks" button in the promo bubble is
-  // clicked.
-  void OnSkipSignInClicked();
-
 #if defined(PASSWORD_STORE_SELECT_ENABLED)
   // Called by the view when the account store checkbox is toggled.
   void OnToggleAccountStore(bool is_checked);
@@ -140,9 +136,6 @@
   // to Google account.
   bool ShouldShowFooter() const;
 
-  // Returns the value for the username field when the bubble is opened.
-  const base::string16& GetCurrentUsername() const;
-
   // Returns the ID of the picture to show above the title.
   int GetTopIllustration(bool dark_mode) const;
 
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
index 8583150f..d895a35 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
@@ -41,9 +41,9 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::testing::_;
 using ::testing::Return;
 using ::testing::ReturnRef;
-using ::testing::_;
 
 namespace {
 
@@ -117,9 +117,7 @@
             .get());
   }
 
-  PasswordsModelDelegateMock* controller() {
-    return mock_delegate_.get();
-  }
+  PasswordsModelDelegateMock* controller() { return mock_delegate_.get(); }
 
   ManagePasswordsBubbleModel* model() { return model_.get(); }
 
@@ -160,13 +158,13 @@
   EXPECT_CALL(*controller(), GetOrigin()).WillOnce(ReturnRef(origin));
   EXPECT_CALL(*controller(), GetState()).WillOnce(Return(state));
   EXPECT_CALL(*controller(), OnBubbleShown());
-  EXPECT_CALL(*controller(), GetWebContents()).WillRepeatedly(
-      Return(test_web_contents_.get()));
+  EXPECT_CALL(*controller(), GetWebContents())
+      .WillRepeatedly(Return(test_web_contents_.get()));
   model_.reset(
       new ManagePasswordsBubbleModel(mock_delegate_->AsWeakPtr(), reason));
   ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(controller()));
-  EXPECT_CALL(*controller(), GetWebContents()).WillRepeatedly(
-      Return(test_web_contents_.get()));
+  EXPECT_CALL(*controller(), GetWebContents())
+      .WillRepeatedly(Return(test_web_contents_.get()));
 }
 
 void ManagePasswordsBubbleModelTest::PretendPasswordWaiting(
@@ -379,7 +377,8 @@
 
 TEST_F(ManagePasswordsBubbleModelTest, GetInitialUsername_MatchedUsername) {
   PretendUpdatePasswordWaiting();
-  EXPECT_EQ(base::UTF8ToUTF16(kUsername), model()->GetCurrentUsername());
+  EXPECT_EQ(base::UTF8ToUTF16(kUsername),
+            model()->pending_password().username_value);
 }
 
 TEST_F(ManagePasswordsBubbleModelTest, EditCredential) {
@@ -453,13 +452,10 @@
   model()->OnSaveClicked();
 
   EXPECT_TRUE(model()->ReplaceToShowPromotionIfNeeded());
-  model()->OnSkipSignInClicked();
   DestroyModelAndVerifyControllerExpectations();
   histogram_tester.ExpectUniqueSample(
       kUIDismissalReasonSaveMetric,
       password_manager::metrics_util::CLICKED_SAVE, 1);
-  EXPECT_TRUE(prefs()->GetBoolean(
-      password_manager::prefs::kWasSignInPasswordPromoClicked));
 }
 
 TEST_F(ManagePasswordsBubbleModelTest, SignInPromoDismiss) {
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index a50441d..b5eeee713f 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -372,7 +372,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-  SupervisedUserNavigationObserver::CreateForWebContents(web_contents);
+  SupervisedUserNavigationObserver::MaybeCreateForWebContents(web_contents);
 #endif
 
   if (predictors::LoadingPredictorFactory::GetForProfile(profile))
diff --git a/chrome/browser/ui/views/download/download_danger_prompt_views.cc b/chrome/browser/ui/views/download/download_danger_prompt_views.cc
index 2183806..a1d6becc 100644
--- a/chrome/browser/ui/views/download/download_danger_prompt_views.cc
+++ b/chrome/browser/ui/views/download/download_danger_prompt_views.cc
@@ -220,7 +220,7 @@
       case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
         if (safe_browsing::AdvancedProtectionStatusManagerFactory::
                 GetForProfile(profile_)
-                    ->RequestsAdvancedProtectionVerdicts()) {
+                    ->IsUnderAdvancedProtection()) {
           return l10n_util::GetStringFUTF16(
               IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT_IN_ADVANCED_PROTECTION,
               download_->GetFileNameToReportUser().LossyDisplayName());
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc
index 9e1ed310..e104656 100644
--- a/chrome/browser/ui/views/download/download_item_view.cc
+++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -1076,7 +1076,7 @@
     case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
       if (safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
               model()->profile())
-              ->RequestsAdvancedProtectionVerdicts()) {
+              ->IsUnderAdvancedProtection()) {
         return gfx::CreateVectorIcon(
             vector_icons::kErrorIcon, GetErrorIconSize(),
             GetNativeTheme()->GetSystemColor(
diff --git a/chrome/browser/ui/webui/downloads/downloads_ui.cc b/chrome/browser/ui/webui/downloads/downloads_ui.cc
index 043070c..dde8c8e 100644
--- a/chrome/browser/ui/webui/downloads/downloads_ui.cc
+++ b/chrome/browser/ui/webui/downloads/downloads_ui.cc
@@ -74,7 +74,7 @@
   bool requests_ap_verdicts =
       safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
           profile)
-          ->RequestsAdvancedProtectionVerdicts();
+          ->IsUnderAdvancedProtection();
   source->AddBoolean("requestsApVerdicts", requests_ap_verdicts);
 
   static constexpr webui::LocalizedString kStrings[] = {
diff --git a/chrome/browser/ui/webui/predictors/predictors_handler.cc b/chrome/browser/ui/webui/predictors/predictors_handler.cc
index 50387cd..8eb0fdc 100644
--- a/chrome/browser/ui/webui/predictors/predictors_handler.cc
+++ b/chrome/browser/ui/webui/predictors/predictors_handler.cc
@@ -91,7 +91,7 @@
       // Origin table cache.
       auto db = std::make_unique<base::ListValue>();
       AddOriginDataMapToListValue(
-          *resource_prefetch_predictor->origin_data_->data_cache_, db.get());
+          resource_prefetch_predictor->origin_data_->GetAllCached(), db.get());
       dict.Set("origin_db", std::move(db));
     }
   }
diff --git a/chrome/browser/webauthn/authenticator_extension_browsertest.cc b/chrome/browser/webauthn/authenticator_extension_browsertest.cc
new file mode 100644
index 0000000..5bd0ef4
--- /dev/null
+++ b/chrome/browser/webauthn/authenticator_extension_browsertest.cc
@@ -0,0 +1,100 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/install_verifier.h"
+#include "chrome/browser/extensions/test_extension_system.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 "content/public/browser/authenticator_environment.h"
+#include "content/public/test/browser_test_utils.h"
+#include "device/fido/virtual_fido_device_factory.h"
+#include "extensions/common/extension_builder.h"
+#include "url/gurl.h"
+
+class WebAuthnBrowserTest : public InProcessBrowserTest {
+ public:
+  WebAuthnBrowserTest() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WebAuthnBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(WebAuthnBrowserTest, ChromeExtensions) {
+  // Test that WebAuthn works inside of Chrome extensions. WebAuthn is based on
+  // Relying Party IDs, which are domain names. But Chrome extensions don't have
+  // domain names therefore the origin is used in their case.
+  //
+  // This test creates and installs an extension and then loads an HTML page
+  // from inside that extension. A WebAuthn call is injected into that context
+  // and it should get an assertion from a credential that's injected into the
+  // virtual authenticator, scoped to the origin string.
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  extensions::ScopedInstallVerifierBypassForTest install_verifier_bypass;
+
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  static constexpr char kPageFile[] = "page.html";
+
+  std::vector<base::Value> resources;
+  resources.emplace_back(std::string(kPageFile));
+  static constexpr char kContents[] = R"(
+<html>
+  <head>
+    <title>WebAuthn in extensions test</title>
+  </head>
+  <body>
+  </body>
+</html>
+)";
+  WriteFile(temp_dir.GetPath().AppendASCII(kPageFile), kContents,
+            sizeof(kContents) - 1);
+
+  extensions::ExtensionBuilder builder("test");
+  builder.SetPath(temp_dir.GetPath())
+      .SetVersion("1.0")
+      .SetLocation(extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD)
+      .SetManifestKey("web_accessible_resources", std::move(resources));
+
+  extensions::ExtensionService* service =
+      extensions::ExtensionSystem::Get(browser()->profile())
+          ->extension_service();
+  scoped_refptr<const extensions::Extension> extension = builder.Build();
+  service->OnExtensionInstalled(extension.get(), syncer::StringOrdinal(), 0);
+
+  auto virtual_device_factory =
+      std::make_unique<device::test::VirtualFidoDeviceFactory>();
+  const GURL url = extension->GetResourceURL(kPageFile);
+  auto extension_id = url.host();
+  static const uint8_t kCredentialID[] = {1, 2, 3, 4};
+  virtual_device_factory->mutable_state()->InjectRegistration(
+      kCredentialID, "chrome-extension://" + extension_id);
+
+  content::AuthenticatorEnvironment::GetInstance()
+      ->ReplaceDefaultDiscoveryFactoryForTesting(
+          std::move(virtual_device_factory));
+
+  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+  std::string result;
+  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+      browser()->tab_strip_model()->GetActiveWebContents(), R"((() => {
+  let cred_id = new Uint8Array([1,2,3,4]);
+  navigator.credentials.get({ publicKey: {
+    challenge: cred_id,
+    timeout: 10000,
+    userVerification: 'discouraged',
+    allowCredentials: [{type: 'public-key', id: cred_id}],
+  }}).then(c => window.domAutomationController.send('webauthn: OK'),
+           e => window.domAutomationController.send('error ' + e));
+})())",
+      &result));
+
+  EXPECT_EQ("webauthn: OK", result);
+}
diff --git a/chrome/browser/webauthn/authenticator_request_scheduler.cc b/chrome/browser/webauthn/authenticator_request_scheduler.cc
index 0bee466..fb49996 100644
--- a/chrome/browser/webauthn/authenticator_request_scheduler.cc
+++ b/chrome/browser/webauthn/authenticator_request_scheduler.cc
@@ -44,8 +44,7 @@
 // static
 std::unique_ptr<ChromeAuthenticatorRequestDelegate>
 AuthenticatorRequestScheduler::CreateRequestDelegate(
-    content::RenderFrameHost* render_frame_host,
-    const std::string& relying_party_id) {
+    content::RenderFrameHost* render_frame_host) {
   auto* const web_contents =
       content::WebContents::FromRenderFrameHost(render_frame_host);
   auto* const active_request_holder =
@@ -54,8 +53,8 @@
   if (active_request_holder->request())
     return nullptr;
 
-  auto request = std::make_unique<ChromeAuthenticatorRequestDelegate>(
-      render_frame_host, relying_party_id);
+  auto request =
+      std::make_unique<ChromeAuthenticatorRequestDelegate>(render_frame_host);
   active_request_holder->request() = request->AsWeakPtr();
   return request;
 }
diff --git a/chrome/browser/webauthn/authenticator_request_scheduler.h b/chrome/browser/webauthn/authenticator_request_scheduler.h
index 8b3d1d9..ea3e792 100644
--- a/chrome/browser/webauthn/authenticator_request_scheduler.h
+++ b/chrome/browser/webauthn/authenticator_request_scheduler.h
@@ -31,8 +31,7 @@
   // Returns a nullptr delegate if there is already an ongoing request in the
   // same WebContents.
   static std::unique_ptr<ChromeAuthenticatorRequestDelegate>
-  CreateRequestDelegate(content::RenderFrameHost* render_frame_host,
-                        const std::string& relying_party_id);
+  CreateRequestDelegate(content::RenderFrameHost* render_frame_host);
 
   // Returns the current request delegate associated to the |web_contents| or
   // nullptr if there is none.
diff --git a/chrome/browser/webauthn/authenticator_request_scheduler_unittest.cc b/chrome/browser/webauthn/authenticator_request_scheduler_unittest.cc
index 796a2f1..072ebf98 100644
--- a/chrome/browser/webauthn/authenticator_request_scheduler_unittest.cc
+++ b/chrome/browser/webauthn/authenticator_request_scheduler_unittest.cc
@@ -20,28 +20,26 @@
 
 TEST_F(AuthenticatorRequestSchedulerTest,
        SingleWebContents_AtMostOneSimultaneousRequest) {
-  const std::string rp_id = "example.com";
   auto first_request = AuthenticatorRequestScheduler::CreateRequestDelegate(
-      web_contents()->GetMainFrame(), rp_id);
+      web_contents()->GetMainFrame());
   ASSERT_TRUE(first_request);
 
   ASSERT_FALSE(AuthenticatorRequestScheduler::CreateRequestDelegate(
-      web_contents()->GetMainFrame(), rp_id));
+      web_contents()->GetMainFrame()));
 
   first_request.reset();
   ASSERT_TRUE(AuthenticatorRequestScheduler::CreateRequestDelegate(
-      web_contents()->GetMainFrame(), rp_id));
+      web_contents()->GetMainFrame()));
 }
 
 TEST_F(AuthenticatorRequestSchedulerTest,
        TwoWebContents_TwoSimultaneousRequests) {
-  const std::string rp_id = "example.com";
   auto first_request = AuthenticatorRequestScheduler::CreateRequestDelegate(
-      web_contents()->GetMainFrame(), rp_id);
+      web_contents()->GetMainFrame());
 
   auto second_web_contents = CreateTestWebContents();
   auto second_request = AuthenticatorRequestScheduler::CreateRequestDelegate(
-      second_web_contents->GetMainFrame(), rp_id);
+      second_web_contents->GetMainFrame());
 
   ASSERT_TRUE(first_request);
   ASSERT_TRUE(second_request);
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index 33972ae8..d5697d1 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -125,13 +125,8 @@
 }
 
 ChromeAuthenticatorRequestDelegate::ChromeAuthenticatorRequestDelegate(
-    content::RenderFrameHost* render_frame_host,
-    const std::string& relying_party_id)
-    : render_frame_host_(render_frame_host),
-      relying_party_id_(relying_party_id),
-      transient_dialog_model_holder_(
-          std::make_unique<AuthenticatorRequestDialogModel>(relying_party_id)),
-      weak_dialog_model_(transient_dialog_model_holder_.get()) {}
+    content::RenderFrameHost* render_frame_host)
+    : render_frame_host_(render_frame_host) {}
 
 ChromeAuthenticatorRequestDelegate::~ChromeAuthenticatorRequestDelegate() {
   // Currently, completion of the request is indicated by //content destroying
@@ -163,6 +158,39 @@
       ->GetBrowserContext();
 }
 
+base::Optional<std::string>
+ChromeAuthenticatorRequestDelegate::MaybeGetRelyingPartyIdOverride(
+    const std::string& claimed_relying_party_id,
+    const url::Origin& caller_origin) {
+  // Don't override cryptotoken processing.
+  constexpr char kCryptotokenOrigin[] =
+      "chrome-extension://kmendfapggjehodndflmmgagdbamhnfd";
+  if (caller_origin == url::Origin::Create(GURL(kCryptotokenOrigin))) {
+    return base::nullopt;
+  }
+
+  // Otherwise, allow extensions to use WebAuthn and map their origins directly
+  // to RP IDs.
+  if (caller_origin.scheme() == "chrome-extension") {
+    // The requested RP ID for an extension must simply be the extension
+    // identifier because no flexibility is permitted. If a caller doesn't
+    // specify an RP ID then Blink defaults the value to the origin's host.
+    if (claimed_relying_party_id != caller_origin.host()) {
+      return base::nullopt;
+    }
+    return caller_origin.Serialize();
+  }
+
+  return base::nullopt;
+}
+
+void ChromeAuthenticatorRequestDelegate::SetRelyingPartyId(
+    const std::string& rp_id) {
+  transient_dialog_model_holder_ =
+      std::make_unique<AuthenticatorRequestDialogModel>(rp_id);
+  weak_dialog_model_ = transient_dialog_model_holder_.get();
+}
+
 bool ChromeAuthenticatorRequestDelegate::DoesBlockRequestOnFailure(
     InterestingFailureReason reason) {
   if (!IsWebAuthnUIEnabled())
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
index 62b4f2d..5f92f165 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
@@ -48,8 +48,7 @@
 
   // The |render_frame_host| must outlive this instance.
   explicit ChromeAuthenticatorRequestDelegate(
-      content::RenderFrameHost* render_frame_host,
-      const std::string& relying_party_id);
+      content::RenderFrameHost* render_frame_host);
   ~ChromeAuthenticatorRequestDelegate() override;
 
 #if defined(OS_MACOSX)
@@ -62,6 +61,10 @@
   AuthenticatorRequestDialogModel* WeakDialogModelForTesting() const;
 
   // content::AuthenticatorRequestClientDelegate:
+  base::Optional<std::string> MaybeGetRelyingPartyIdOverride(
+      const std::string& claimed_relying_party_id,
+      const url::Origin& caller_origin) override;
+  void SetRelyingPartyId(const std::string& rp_id) override;
   bool DoesBlockRequestOnFailure(InterestingFailureReason reason) override;
   void RegisterActionCallbacks(
       base::OnceClosure cancel_callback,
@@ -143,7 +146,6 @@
       std::unique_ptr<device::CableDiscoveryData> discovery_data);
 
   content::RenderFrameHost* const render_frame_host_;
-  const std::string relying_party_id_;
   // Holds ownership of AuthenticatorRequestDialogModel until
   // OnTransportAvailabilityEnumerated() is invoked, at which point the
   // ownership of the model is transferred to AuthenticatorRequestDialogView and
@@ -151,7 +153,7 @@
   // |weak_dialog_model_|.
   std::unique_ptr<AuthenticatorRequestDialogModel>
       transient_dialog_model_holder_;
-  AuthenticatorRequestDialogModel* weak_dialog_model_;
+  AuthenticatorRequestDialogModel* weak_dialog_model_ = nullptr;
   base::OnceClosure cancel_callback_;
   base::RepeatingClosure start_over_callback_;
   device::FidoRequestHandlerBase::RequestCallback request_callback_;
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
index fad51ad..9c8e2c1 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
@@ -36,10 +36,8 @@
 #endif  // defined(OS_MACOSX)
 };
 
-static constexpr char kRelyingPartyID[] = "example.com";
-
 TEST_F(ChromeAuthenticatorRequestDelegateTest, TestTransportPrefType) {
-  ChromeAuthenticatorRequestDelegate delegate(main_rfh(), kRelyingPartyID);
+  ChromeAuthenticatorRequestDelegate delegate(main_rfh());
   EXPECT_FALSE(delegate.GetLastTransportUsed());
   delegate.UpdateLastTransportUsed(device::FidoTransportProtocol::kInternal);
   const auto transport = delegate.GetLastTransportUsed();
@@ -52,7 +50,7 @@
   static constexpr char kTestPairedDeviceAddress[] = "paired_device_address";
   static constexpr char kTestPairedDeviceAddress2[] = "paired_device_address2";
 
-  ChromeAuthenticatorRequestDelegate delegate(main_rfh(), kRelyingPartyID);
+  ChromeAuthenticatorRequestDelegate delegate(main_rfh());
 
   auto* const address_list = delegate.GetPreviouslyPairedFidoBleDeviceIds();
   ASSERT_TRUE(address_list);
@@ -95,7 +93,7 @@
 
 TEST_F(ChromeAuthenticatorRequestDelegateTest, TouchIdMetadataSecret) {
   if (__builtin_available(macOS 10.12.2, *)) {
-    ChromeAuthenticatorRequestDelegate delegate(main_rfh(), kRelyingPartyID);
+    ChromeAuthenticatorRequestDelegate delegate(main_rfh());
     std::string secret = TouchIdMetadataSecret(&delegate);
     EXPECT_EQ(secret.size(), 32u);
     EXPECT_EQ(secret, TouchIdMetadataSecret(&delegate));
@@ -107,8 +105,8 @@
   if (__builtin_available(macOS 10.12.2, *)) {
     // Different delegates on the same BrowserContext (Profile) should return
     // the same secret.
-    ChromeAuthenticatorRequestDelegate delegate1(main_rfh(), kRelyingPartyID);
-    ChromeAuthenticatorRequestDelegate delegate2(main_rfh(), kRelyingPartyID);
+    ChromeAuthenticatorRequestDelegate delegate1(main_rfh());
+    ChromeAuthenticatorRequestDelegate delegate2(main_rfh());
     EXPECT_EQ(TouchIdMetadataSecret(&delegate1),
               TouchIdMetadataSecret(&delegate2));
   }
@@ -122,9 +120,8 @@
     auto browser_context = CreateBrowserContext();
     auto web_contents = content::WebContentsTester::CreateTestWebContents(
         browser_context.get(), nullptr);
-    ChromeAuthenticatorRequestDelegate delegate1(main_rfh(), kRelyingPartyID);
-    ChromeAuthenticatorRequestDelegate delegate2(web_contents->GetMainFrame(),
-                                                 kRelyingPartyID);
+    ChromeAuthenticatorRequestDelegate delegate1(main_rfh());
+    ChromeAuthenticatorRequestDelegate delegate2(web_contents->GetMainFrame());
     EXPECT_NE(TouchIdMetadataSecret(&delegate1),
               TouchIdMetadataSecret(&delegate2));
     // Ensure this second secret is actually valid.
@@ -140,8 +137,7 @@
       touch_id_test_environment_.SetTouchIdAvailable(touch_id_available);
 
       std::unique_ptr<content::AuthenticatorRequestClientDelegate> delegate =
-          std::make_unique<ChromeAuthenticatorRequestDelegate>(main_rfh(),
-                                                               kRelyingPartyID);
+          std::make_unique<ChromeAuthenticatorRequestDelegate>(main_rfh());
       EXPECT_EQ(touch_id_available,
                 delegate->IsUserVerifyingPlatformAuthenticatorAvailable());
     }
@@ -151,9 +147,12 @@
 #endif  // defined(OS_MACOSX)
 
 #if defined(OS_WIN)
+
+static constexpr char kRelyingPartyID[] = "example.com";
+
 TEST_F(ChromeAuthenticatorRequestDelegateTest, WinIsUVPAA) {
-  auto delegate = std::make_unique<ChromeAuthenticatorRequestDelegate>(
-      main_rfh(), kRelyingPartyID);
+  auto delegate =
+      std::make_unique<ChromeAuthenticatorRequestDelegate>(main_rfh());
   device::FakeWinWebAuthnApi win_webauthn_api;
   delegate->GetDiscoveryFactory()->set_win_webauthn_api(&win_webauthn_api);
 
@@ -186,10 +185,11 @@
       /*current_window=*/nullptr, &win_webauthn_api);
 
   ::device::test::ValueCallbackReceiver<bool> cb;
-  ChromeAuthenticatorRequestDelegate delegate(main_rfh(), kRelyingPartyID);
+  ChromeAuthenticatorRequestDelegate delegate(main_rfh());
   delegate.ShouldReturnAttestation(kRelyingPartyID, &authenticator,
                                    cb.callback());
   cb.WaitForCallback();
   EXPECT_EQ(cb.value(), true);
 }
+
 #endif  // defined(OS_WIN)
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index d7cefd5..32cedff 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -55,10 +55,6 @@
 // TODO(ellyjones): Remove this after the last 10.9 release.
 const base::Feature kShow10_9ObsoleteInfobar{"Show109ObsoleteInfobar",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Use the Toolkit-Views Task Manager window.
-const base::Feature kViewsTaskManager{"ViewsTaskManager",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // defined(OS_MACOSX)
 
 #if defined(OS_ANDROID)
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index c68577e..334f4a7 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -48,7 +48,6 @@
 extern const base::Feature kAppShimRemoteCocoa;
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kShow10_9ObsoleteInfobar;
-COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kViewsTaskManager;
 #endif  // defined(OS_MACOSX)
 
 #if defined(OS_ANDROID)
diff --git a/chrome/installer/setup/setup_util_unittest.cc b/chrome/installer/setup/setup_util_unittest.cc
index b43216c..eba456f 100644
--- a/chrome/installer/setup/setup_util_unittest.cc
+++ b/chrome/installer/setup/setup_util_unittest.cc
@@ -264,19 +264,22 @@
       std::string(installer::kUnPackNTSTATUSMetricsName) + "_SetupExePatch";
   histogram_tester.ExpectTotalCount(unpack_status_metrics_name, 0);
 
-  RecordUnPackMetrics(UnPackStatus::UNPACK_NO_ERROR, base::nullopt,
-                      base::nullopt,
+  RecordUnPackMetrics(UnPackStatus::UNPACK_NO_ERROR, 0, ERROR_SUCCESS,
                       installer::UnPackConsumer::SETUP_EXE_PATCH);
   histogram_tester.ExpectTotalCount(unpack_status_metrics_name, 1);
   histogram_tester.ExpectBucketCount(unpack_status_metrics_name, 0, 1);
+  histogram_tester.ExpectTotalCount(unpack_result_metrics_name, 1);
+  histogram_tester.ExpectBucketCount(unpack_result_metrics_name, 0, 1);
+  histogram_tester.ExpectTotalCount(ntstatus_metrics_name, 1);
+  histogram_tester.ExpectBucketCount(ntstatus_metrics_name, 0, 1);
 
-  RecordUnPackMetrics(UnPackStatus::UNPACK_EXTRACT_ERROR, 1, 2,
+  RecordUnPackMetrics(UnPackStatus::UNPACK_CLOSE_FILE_ERROR, 1, 2,
                       installer::UnPackConsumer::SETUP_EXE_PATCH);
   histogram_tester.ExpectTotalCount(unpack_status_metrics_name, 2);
-  histogram_tester.ExpectBucketCount(unpack_status_metrics_name, 4, 1);
-  histogram_tester.ExpectTotalCount(unpack_result_metrics_name, 1);
+  histogram_tester.ExpectBucketCount(unpack_status_metrics_name, 10, 1);
+  histogram_tester.ExpectTotalCount(unpack_result_metrics_name, 2);
   histogram_tester.ExpectBucketCount(unpack_result_metrics_name, 2, 1);
-  histogram_tester.ExpectTotalCount(ntstatus_metrics_name, 1);
+  histogram_tester.ExpectTotalCount(ntstatus_metrics_name, 2);
   histogram_tester.ExpectBucketCount(ntstatus_metrics_name, 1, 1);
 }
 
diff --git a/chrome/installer/util/BUILD.gn b/chrome/installer/util/BUILD.gn
index 0f5e97d..c49629b 100644
--- a/chrome/installer/util/BUILD.gn
+++ b/chrome/installer/util/BUILD.gn
@@ -70,6 +70,8 @@
       "html_dialog_impl.cc",
       "logging_installer.cc",
       "logging_installer.h",
+      "lzma_file_allocator.cc",
+      "lzma_file_allocator.h",
       "lzma_util.cc",
       "lzma_util.h",
       "master_preferences.cc",
@@ -297,6 +299,7 @@
       "install_util_unittest.cc",
       "l10n_string_util_unittest.cc",
       "logging_installer_unittest.cc",
+      "lzma_file_allocator_unittest.cc",
       "lzma_util_unittest.cc",
       "master_preferences_unittest.cc",
       "move_tree_work_item_unittest.cc",
diff --git a/chrome/installer/util/lzma_file_allocator.cc b/chrome/installer/util/lzma_file_allocator.cc
new file mode 100644
index 0000000..4c54818d
--- /dev/null
+++ b/chrome/installer/util/lzma_file_allocator.cc
@@ -0,0 +1,105 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/installer/util/lzma_file_allocator.h"
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+
+#include <windows.h>
+
+extern "C" {
+#include "third_party/lzma_sdk/7zAlloc.h"
+}
+
+LzmaFileAllocator::LzmaFileAllocator(const base::FilePath& temp_directory) {
+  DCHECK(base::DirectoryExists(temp_directory));
+
+  // Direct the ISzAlloc functions to the LZMA SDK's default allocator.
+  Alloc = SzAlloc;
+  Free = SzFree;
+
+  if (!base::CreateTemporaryFileInDir(temp_directory, &mapped_file_path_))
+    return;
+
+  mapped_file_.Initialize(mapped_file_path_,
+                          base::File::FLAG_CREATE_ALWAYS |
+                              base::File::FLAG_READ | base::File::FLAG_WRITE |
+                              base::File::FLAG_TEMPORARY |
+                              base::File::FLAG_DELETE_ON_CLOSE);
+  if (!mapped_file_.IsValid()) {
+    DPLOG(ERROR) << "Failed to initialize mapped file";
+    return;
+  }
+
+  // Direct the ISzAlloc functions to this class's allocator.
+  Alloc = SzFileAlloc;
+  Free = SzFileFree;
+}
+
+LzmaFileAllocator::~LzmaFileAllocator() {
+  DCHECK(!file_mapping_handle_.IsValid());
+  Alloc = nullptr;
+  Free = nullptr;
+}
+
+bool LzmaFileAllocator::IsAddressMapped(uintptr_t address) const {
+  return (address >= mapped_start_address_) &&
+         (address < mapped_start_address_ + mapped_size_);
+}
+
+void* LzmaFileAllocator::DoAllocate(size_t size) {
+  DCHECK(!file_mapping_handle_.IsValid());
+
+  if (size == 0)
+    return nullptr;
+
+  if (!mapped_file_.IsValid())
+    return SzAlloc(this, size);
+
+  if (!mapped_file_.SetLength(size)) {
+    DPLOG(ERROR) << "Failed to set length of mapped file";
+    return SzAlloc(this, size);
+  }
+
+  file_mapping_handle_.Set(::CreateFileMapping(
+      mapped_file_.GetPlatformFile(), nullptr, PAGE_READWRITE, 0, 0, nullptr));
+  if (!file_mapping_handle_.IsValid()) {
+    DPLOG(ERROR) << "Failed to create file mapping handle";
+    return SzAlloc(this, size);
+  }
+
+  void* ret =
+      ::MapViewOfFile(file_mapping_handle_.Get(), FILE_MAP_WRITE, 0, 0, 0);
+  if (!ret) {
+    DPLOG(ERROR) << "Failed to map view of file";
+    file_mapping_handle_.Close();
+    return SzAlloc(this, size);
+  }
+  mapped_start_address_ = reinterpret_cast<uintptr_t>(ret);
+  mapped_size_ = size;
+  return ret;
+}
+
+void LzmaFileAllocator::DoFree(void* address) {
+  if (address == nullptr)
+    return;
+  if (!file_mapping_handle_.IsValid()) {
+    SzFree(this, address);
+    return;
+  }
+  int ret = ::UnmapViewOfFile(address);
+  DPCHECK(ret != 0) << "Failed to unmap view of file";
+  mapped_start_address_ = 0;
+  mapped_size_ = 0;
+  file_mapping_handle_.Close();
+}
+
+void* LzmaFileAllocator::SzFileAlloc(void* p, size_t size) {
+  return static_cast<LzmaFileAllocator*>(p)->DoAllocate(size);
+}
+
+void LzmaFileAllocator::SzFileFree(void* p, void* address) {
+  static_cast<LzmaFileAllocator*>(p)->DoFree(address);
+}
diff --git a/chrome/installer/util/lzma_file_allocator.h b/chrome/installer/util/lzma_file_allocator.h
new file mode 100644
index 0000000..6403e2d9
--- /dev/null
+++ b/chrome/installer/util/lzma_file_allocator.h
@@ -0,0 +1,56 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_INSTALLER_UTIL_LZMA_FILE_ALLOCATOR_H_
+#define CHROME_INSTALLER_UTIL_LZMA_FILE_ALLOCATOR_H_
+
+#include <stddef.h>
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/win/scoped_handle.h"
+#include "third_party/lzma_sdk/7zTypes.h"
+
+// File mapping memory management class which supports multiple allocations in
+// series, but not in parallel. It creates a unique temporary file within
+// |temp_directory| to back subsequent allocations and falls back to normal
+// management in case of error.
+// Example:
+//   LzmaFileAllocator allocator(mmp_file_path);
+//   char* s = reinterpret_cast<char*>(IAlloc_Alloc(&allocator, size));
+//   IAlloc_Free(&allocator, s);
+class LzmaFileAllocator : public ISzAlloc {
+ public:
+  explicit LzmaFileAllocator(const base::FilePath& temp_directory);
+  ~LzmaFileAllocator();
+  bool IsAddressMapped(uintptr_t address) const;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(LzmaFileAllocatorTest, ErrorAndFallbackTest);
+  FRIEND_TEST_ALL_PREFIXES(LzmaFileAllocatorTest, DeleteAfterCloseTest);
+
+  // Allocates |size| bytes backed by the instance's temporary file. The heap
+  // is used if file allocation fails for any reason.
+  void* DoAllocate(size_t size);
+
+  // Frees the memory from disk or heap depending on the type of allocation.
+  void DoFree(void* address);
+
+  // ISzAlloc hook functions
+  static void* SzFileAlloc(void* p, size_t size);
+  static void SzFileFree(void* p, void* address);
+
+  base::FilePath mapped_file_path_;
+  base::File mapped_file_;
+  base::win::ScopedHandle file_mapping_handle_;
+
+  uintptr_t mapped_start_address_ = 0;
+  size_t mapped_size_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(LzmaFileAllocator);
+};
+
+#endif  // CHROME_INSTALLER_UTIL_LZMA_FILE_ALLOCATOR_H_
diff --git a/chrome/installer/util/lzma_file_allocator_unittest.cc b/chrome/installer/util/lzma_file_allocator_unittest.cc
new file mode 100644
index 0000000..a1b593ba
--- /dev/null
+++ b/chrome/installer/util/lzma_file_allocator_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/installer/util/lzma_file_allocator.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include <windows.h>
+
+class LzmaFileAllocatorTest : public testing::Test {
+ protected:
+  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+  // Returns the type of the memory page identified by |address|; one of
+  // MEM_IMAGE, MEM_MAPPED or MEM_PRIVATE.
+  static DWORD GetMemoryType(void* address);
+
+  base::ScopedTempDir temp_dir_;
+};
+
+DWORD LzmaFileAllocatorTest::GetMemoryType(void* address) {
+  MEMORY_BASIC_INFORMATION memory_info = {};
+  EXPECT_NE(0U, ::VirtualQuery(address, &memory_info,
+                               sizeof(memory_info)));
+  return memory_info.Type;
+}
+
+TEST_F(LzmaFileAllocatorTest, ReadAndWriteWithMultipleSizeTest) {
+  static const char kSampleExpectedCharacter = 'a';
+  SYSTEM_INFO sysinfo;
+  ::GetSystemInfo(&sysinfo);
+  EXPECT_GT(sysinfo.dwPageSize, 0U);
+
+  size_t size_list[] = {1, 10, sysinfo.dwPageSize - 1, sysinfo.dwPageSize,
+                        sysinfo.dwPageSize + 1};
+
+  for (size_t size : size_list) {
+    LzmaFileAllocator allocator(temp_dir_.GetPath());
+    char* s = reinterpret_cast<char*>(IAlloc_Alloc(&allocator, size));
+    std::fill_n(s, size, kSampleExpectedCharacter);
+    char* ret = std::find_if(s, s + size, [](char c) {
+      return c != kSampleExpectedCharacter;
+    });
+    EXPECT_EQ(s + size, ret);
+    EXPECT_EQ(static_cast<DWORD>(MEM_MAPPED), GetMemoryType(s));
+
+    IAlloc_Free(&allocator, s);
+  }
+}
+
+TEST_F(LzmaFileAllocatorTest, SizeIsZeroTest) {
+  LzmaFileAllocator allocator(temp_dir_.GetPath());
+  char* s = reinterpret_cast<char*>(IAlloc_Alloc(&allocator, 0));
+  EXPECT_EQ(s, nullptr);
+
+  IAlloc_Free(&allocator, s);
+}
+
+TEST_F(LzmaFileAllocatorTest, DeleteAfterCloseTest) {
+  std::unique_ptr<LzmaFileAllocator> allocator =
+      std::make_unique<LzmaFileAllocator>(temp_dir_.GetPath());
+  base::FilePath file_path = allocator->mapped_file_path_;
+  ASSERT_TRUE(base::PathExists(file_path));
+  allocator.reset();
+  ASSERT_FALSE(base::PathExists(file_path));
+}
+
+TEST_F(LzmaFileAllocatorTest, ErrorAndFallbackTest) {
+  LzmaFileAllocator allocator(temp_dir_.GetPath());
+  allocator.mapped_file_.Close();
+  char* s = reinterpret_cast<char*>(IAlloc_Alloc(&allocator, 10));
+  EXPECT_NE(nullptr, s);
+  ASSERT_FALSE(allocator.file_mapping_handle_.IsValid());
+  EXPECT_EQ(static_cast<DWORD>(MEM_PRIVATE), GetMemoryType(s));
+
+  IAlloc_Free(&allocator, s);
+}
+
+TEST_F(LzmaFileAllocatorTest, IsAddressMappedTest) {
+  LzmaFileAllocator allocator(temp_dir_.GetPath());
+  size_t size = 10;
+  uintptr_t address =
+      reinterpret_cast<uintptr_t>(IAlloc_Alloc(&allocator, size));
+  ASSERT_TRUE(allocator.IsAddressMapped(address));
+  ASSERT_TRUE(allocator.IsAddressMapped(address + 1));
+  ASSERT_TRUE(allocator.IsAddressMapped(address + 9));
+  ASSERT_FALSE(allocator.IsAddressMapped(address + 10));
+  ASSERT_FALSE(allocator.IsAddressMapped(address + 11));
+  ASSERT_FALSE(allocator.IsAddressMapped(address - 1));
+  IAlloc_Free(&allocator, reinterpret_cast<void*>(address));
+}
diff --git a/chrome/installer/util/lzma_util.cc b/chrome/installer/util/lzma_util.cc
index e128f8c..d1d28031 100644
--- a/chrome/installer/util/lzma_util.cc
+++ b/chrome/installer/util/lzma_util.cc
@@ -10,13 +10,11 @@
 
 #include <vector>
 
-#include "base/callback_helpers.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
-#include "base/files/memory_mapped_file.h"
 #include "base/logging.h"
-#include "base/optional.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/installer/util/lzma_file_allocator.h"
 
 extern "C" {
 #include "third_party/lzma_sdk/7z.h"
@@ -86,7 +84,7 @@
 // Returns EXCEPTION_EXECUTE_HANDLER and populates |status| with the underlying
 // NTSTATUS code for paging errors encountered while accessing file-backed
 // mapped memory. Otherwise, return EXCEPTION_CONTINUE_SEARCH.
-DWORD FilterPageError(const base::MemoryMappedFile& mapped_file,
+DWORD FilterPageError(const LzmaFileAllocator& file_allocator,
                       DWORD exception_code,
                       const EXCEPTION_POINTERS* info,
                       int32_t* status) {
@@ -94,13 +92,10 @@
     return EXCEPTION_CONTINUE_SEARCH;
 
   const EXCEPTION_RECORD* exception_record = info->ExceptionRecord;
-  const uint8_t* address = reinterpret_cast<const uint8_t*>(
-      exception_record->ExceptionInformation[1]);
-  if (address < mapped_file.data() ||
-      address >= mapped_file.data() + mapped_file.length()) {
+  if (!file_allocator.IsAddressMapped(
+          exception_record->ExceptionInformation[1])) {
     return EXCEPTION_CONTINUE_SEARCH;
   }
-
   // Cast NTSTATUS to int32_t to avoid including winternl.h
   *status = exception_record->ExceptionInformation[2];
 
@@ -158,209 +153,155 @@
   DCHECK(archive_file_.IsValid());
 
   CFileInStream archiveStream;
+  CLookToRead lookStream;
+  CSzArEx db;
+  ISzAlloc allocImp;
+  ISzAlloc allocTempImp;
+  SRes sz_res = SZ_OK;
+
   archiveStream.file.handle = archive_file_.GetPlatformFile();
   archiveStream.s.Read = SzFileReadImp;
   archiveStream.s.Seek = SzFileSeekImp;
-
-  CLookToRead lookStream;
   LookToRead_CreateVTable(&lookStream, false);
-  LookToRead_Init(&lookStream);
   lookStream.realStream = &archiveStream.s;
 
+  allocImp.Alloc = SzAlloc;
+  allocImp.Free = SzFree;
+  allocTempImp.Alloc = SzAllocTemp;
+  allocTempImp.Free = SzFreeTemp;
+
   CrcGenerateTable();
-
-  CSzArEx db;
   SzArEx_Init(&db);
-
-  ISzAlloc allocImp = {SzAlloc, SzFree};
-  ISzAlloc allocTempImp = {SzAllocTemp, SzFreeTemp};
-  SRes sz_res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
-  if (sz_res != SZ_OK) {
-    LOG(ERROR) << "Error returned by SzArchiveOpen: " << sz_res;
+  ::SetLastError(ERROR_SUCCESS);
+  if ((sz_res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp)) !=
+      SZ_OK) {
+    LOG(ERROR) << L"Error returned by SzArchiveOpen: " << sz_res;
     auto error_code = ::GetLastError();
     if (error_code != ERROR_SUCCESS)
       error_code_ = error_code;
     return UNPACK_SZAREX_OPEN_ERROR;
   }
-  base::ScopedClosureRunner db_closer(
-      base::BindOnce(&SzArEx_Free, &db, &allocImp));
 
-  // Tracks the last folder that was uncompressed. The result is reused when
-  // multiple subsequent files in the archive share the same folder.
-  size_t last_folder_index = -1;
-  // A mapping of either the target file (if the file exactly fits within a
-  // folder) or a temporary file into which a folder is decompressed.
-  base::Optional<base::MemoryMappedFile> mapped_file;
-  for (size_t file_index = 0; file_index < db.NumFiles; ++file_index) {
-    size_t file_name_length = SzArEx_GetFileNameUtf16(&db, file_index, nullptr);
+  Byte* outBuffer = 0;  // it must be 0 before first call for each new archive
+  UInt32 blockIndex = 0xFFFFFFFF;  // can have any value if outBuffer = 0
+  size_t outBufferSize = 0;        // can have any value if outBuffer = 0
+
+  LzmaFileAllocator fileAllocator(location);
+
+  UnPackStatus status = UNPACK_NO_ERROR;
+  for (unsigned int i = 0; i < db.NumFiles; i++) {
+    DWORD written;
+    size_t offset = 0;
+    size_t outSizeProcessed = 0;
+
+    int32_t ntstatus = 0;  // STATUS_SUCCESS
+    ::SetLastError(ERROR_SUCCESS);
+    __try {
+      if ((sz_res =
+               SzArEx_Extract(&db, &lookStream.s, i, &blockIndex, &outBuffer,
+                              &outBufferSize, &offset, &outSizeProcessed,
+                              &fileAllocator, &allocTempImp)) != SZ_OK) {
+        LOG(ERROR) << L"Error returned by SzExtract: " << sz_res;
+        auto error_code = ::GetLastError();
+        if (error_code != ERROR_SUCCESS)
+          error_code_ = error_code;
+        status = UNPACK_EXTRACT_ERROR;
+      }
+    } __except(FilterPageError(fileAllocator, GetExceptionCode(),
+                                GetExceptionInformation(), &ntstatus)) {
+      ntstatus_ = ntstatus;
+      status = UNPACK_EXTRACT_EXCEPTION;
+      LOG(ERROR) << L"EXCEPTION_IN_PAGE_ERROR while accessing mapped memory; "
+                    L"NTSTATUS = "
+                 << ntstatus;
+    }
+    if (status != UNPACK_NO_ERROR)
+      break;
+
+    size_t file_name_length = SzArEx_GetFileNameUtf16(&db, i, NULL);
     if (file_name_length < 1) {
-      LOG(ERROR) << "Couldn't get file name";
-      return UNPACK_NO_FILENAME_ERROR;
+      LOG(ERROR) << L"Couldn't get file name";
+      status = UNPACK_NO_FILENAME_ERROR;
+      break;
     }
 
     std::vector<UInt16> file_name(file_name_length);
-    file_name_length =
-        SzArEx_GetFileNameUtf16(&db, file_index, file_name.data());
-    DCHECK_EQ(file_name_length, file_name.size());
-
+    SzArEx_GetFileNameUtf16(&db, i, &file_name[0]);
     // |file_name| is NULL-terminated.
     base::FilePath file_path = location.Append(
-        base::FilePath::StringType(file_name.begin(), --file_name.end()));
+        base::FilePath::StringType(file_name.begin(), file_name.end() - 1));
 
     if (output_file)
       *output_file = file_path;
 
     // If archive entry is directory create it and move on to the next entry.
-    if (SzArEx_IsDir(&db, file_index)) {
+    if (SzArEx_IsDir(&db, i)) {
       if (!CreateDirectory(file_path)) {
         error_code_ = ::GetLastError();
-        return UNPACK_CREATE_FILE_ERROR;
+        status = UNPACK_CREATE_FILE_ERROR;
+        break;
       }
       continue;
     }
 
     CreateDirectory(file_path.DirName());
 
-    base::File target_file(file_path, base::File::FLAG_CREATE_ALWAYS |
-                                          base::File::FLAG_READ |
-                                          base::File::FLAG_WRITE |
-                                          base::File::FLAG_EXCLUSIVE_READ |
-                                          base::File::FLAG_EXCLUSIVE_WRITE |
-                                          base::File::FLAG_CAN_DELETE_ON_CLOSE |
-                                          base::File::FLAG_SHARE_DELETE);
-    if (!target_file.IsValid()) {
-      PLOG(ERROR) << "Invalid file.";
+    HANDLE hFile;
+    hFile = CreateFile(file_path.value().c_str(), GENERIC_WRITE, 0, NULL,
+                       CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE) {
       error_code_ = ::GetLastError();
-      return UNPACK_CREATE_FILE_ERROR;
-    }
-    // The target file is deleted by default unless extracting succeeds.
-    target_file.DeleteOnClose(true);
-
-    uint32_t folder_index = db.FileToFolder[file_index];
-
-    // If |file_index| has no associated data to uncompress. The resulting file
-    // is still written on disk and will be empty.
-    if (folder_index != uint32_t(-1)) {
-      uint64_t file_offset = db.UnpackPositions[file_index];
-
-      uint64_t folder_offset =
-          db.UnpackPositions[db.FolderToFile[folder_index]];
-      CHECK_LE(folder_offset, file_offset);
-      size_t file_offset_in_folder = (size_t)(file_offset - folder_offset);
-
-      // |UnpackPositions| has NumFiles + 1 entries, with an extra entry
-      // for the sentinel.
-      size_t file_unpack_size =
-          (size_t)(db.UnpackPositions[file_index + 1] - file_offset);
-      uint64_t folder_unpack_size =
-          SzAr_GetFolderUnpackSize(&db.db, folder_index);
-      CHECK_LE(file_offset_in_folder + file_unpack_size, folder_unpack_size);
-
-      // A buffer is used iff the folder doesn't match exactly the target file.
-      // Otherwise, the target is written directly as a memory mapped file.
-      // In practice, all folders are single file.
-      bool use_temp_buffer = folder_unpack_size != file_unpack_size;
-      if (last_folder_index != folder_index) {
-        last_folder_index = folder_index;
-        mapped_file.emplace();
-        bool mapped_file_ok = false;
-        if (use_temp_buffer) {
-          base::FilePath temp_file_path;
-          if (!base::CreateTemporaryFileInDir(location, &temp_file_path)) {
-            error_code_ = ::GetLastError();
-            return UNPACK_ALLOCATE_ERROR;
-          }
-
-          base::File temp_file(
-              temp_file_path,
-              base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ |
-                  base::File::FLAG_WRITE | base::File::FLAG_EXCLUSIVE_READ |
-                  base::File::FLAG_EXCLUSIVE_WRITE |
-                  base::File::FLAG_TEMPORARY |
-                  base::File::FLAG_DELETE_ON_CLOSE |
-                  base::File::FLAG_SHARE_DELETE);
-          mapped_file_ok = mapped_file->Initialize(
-              std::move(temp_file), {0, folder_unpack_size},
-              base::MemoryMappedFile::READ_WRITE_EXTEND);
-        } else {
-          mapped_file_ok = mapped_file->Initialize(
-              target_file.Duplicate(), {0, folder_unpack_size},
-              base::MemoryMappedFile::READ_WRITE_EXTEND);
-        }
-        if (!mapped_file_ok) {
-          PLOG(ERROR) << "Can't map file to memory.";
-          error_code_ = ::GetLastError();
-          return UNPACK_ALLOCATE_ERROR;
-        }
-        int32_t ntstatus = 0;  // STATUS_SUCCESS
-        ::SetLastError(ERROR_SUCCESS);
-        __try {
-          SRes sz_res = SzAr_DecodeFolder(&db.db, folder_index, &lookStream.s,
-                                          db.dataPos, mapped_file->data(),
-                                          folder_unpack_size, &allocTempImp);
-          if (sz_res != SZ_OK) {
-            LOG(ERROR) << "Error returned by SzExtract: " << sz_res;
-            auto error_code = ::GetLastError();
-            if (error_code != ERROR_SUCCESS)
-              error_code_ = error_code;
-            return UNPACK_EXTRACT_ERROR;
-          }
-        } __except(FilterPageError(*mapped_file, GetExceptionCode(),
-                                    GetExceptionInformation(), &ntstatus)) {
-          LOG(ERROR)
-              << "EXCEPTION_IN_PAGE_ERROR while accessing mapped memory; "
-                 "NTSTATUS = "
-              << ntstatus;
-          ntstatus_ = ntstatus;
-          return UNPACK_EXTRACT_EXCEPTION;
-        }
-      }
-
-      if (SzBitWithVals_Check(&db.CRCs, file_index)) {
-        if (CrcCalc(mapped_file->data() + file_offset_in_folder,
-                    file_unpack_size) != db.CRCs.Vals[file_index])
-          return UNPACK_CRC_ERROR;
-      }
-
-      if (use_temp_buffer) {
-        // Don't write all of the data at once because this can lead to kernel
-        // address-space exhaustion on 32-bit Windows (see
-        // https://crbug.com/1001022 for details).
-        constexpr size_t kMaxWriteAmount = 8 * 1024 * 1024;
-        for (size_t total_written = 0; total_written < file_unpack_size; /**/) {
-          const size_t write_amount =
-              std::min(kMaxWriteAmount, file_unpack_size - total_written);
-          int written = target_file.WriteAtCurrentPos(
-              reinterpret_cast<char*>(mapped_file->data() +
-                                      file_offset_in_folder + total_written),
-              write_amount);
-          if (static_cast<size_t>(written) != write_amount) {
-            PLOG(ERROR) << "Error returned by WriteFile";
-            error_code_ = ::GetLastError();
-            return UNPACK_WRITE_FILE_ERROR;
-          }
-          total_written += written;
-        }
-      } else {
-        // Unmap the target file from the process's address space.
-        mapped_file.reset();
-        last_folder_index = -1;
-      }
+      status = UNPACK_CREATE_FILE_ERROR;
+      PLOG(ERROR) << L"CreateFile failed";
+      break;
     }
 
-    // On success, |target_file| is kept.
-    target_file.DeleteOnClose(false);
-
-    if (SzBitWithVals_Check(&db.MTime, file_index)) {
-      if (!SetFileTime(target_file.GetPlatformFile(), nullptr, nullptr,
-                       (const FILETIME*)(&db.MTime.Vals[file_index]))) {
-        PLOG(ERROR) << "Error returned by SetFileTime";
+    // Don't write all of the data at once because this can lead to kernel
+    // address-space exhaustion on 32-bit Windows (see https://crbug.com/1001022
+    // for details).
+    constexpr size_t kMaxWriteAmount = 8 * 1024 * 1024;
+    for (size_t total_written = 0; total_written < outSizeProcessed; /**/) {
+      const size_t write_amount =
+          std::min(kMaxWriteAmount, outSizeProcessed - total_written);
+      if ((!WriteFile(hFile, outBuffer + offset + total_written,
+                      static_cast<DWORD>(write_amount), &written, nullptr)) ||
+          (written != write_amount)) {
         error_code_ = ::GetLastError();
-        return UNPACK_SET_FILE_TIME_ERROR;
+        status = UNPACK_WRITE_FILE_ERROR;
+        PLOG(ERROR) << L"Error returned by WriteFile";
+        CloseHandle(hFile);
+        break;
+      }
+      total_written += write_amount;
+    }
+
+    // Break out of the file loop if the write loop failed.
+    if (status != UNPACK_NO_ERROR)
+      break;
+
+    if (SzBitWithVals_Check(&db.MTime, i)) {
+      if (!SetFileTime(hFile, NULL, NULL,
+                       (const FILETIME*)(&db.MTime.Vals[i]))) {
+        error_code_ = ::GetLastError();
+        status = UNPACK_SET_FILE_TIME_ERROR;
+        PLOG(ERROR) << L"Error returned by SetFileTime";
+        CloseHandle(hFile);
+        break;
       }
     }
-  }
-  return UNPACK_NO_ERROR;
+    if (!CloseHandle(hFile)) {
+      error_code_ = ::GetLastError();
+      status = UNPACK_CLOSE_FILE_ERROR;
+      PLOG(ERROR) << L"Error returned by CloseHandle";
+      break;
+    }
+  }  // for loop
+  IAlloc_Free(&fileAllocator, outBuffer);
+  SzArEx_Free(&db, &allocImp);
+  DCHECK(status != UNPACK_NO_ERROR || !error_code_.has_value());
+  DCHECK(status != UNPACK_NO_ERROR || !ntstatus_.has_value());
+
+  return status;
 }
 
 void LzmaUtilImpl::CloseArchive() {
diff --git a/chrome/installer/util/lzma_util.h b/chrome/installer/util/lzma_util.h
index b637545..08c2334 100644
--- a/chrome/installer/util/lzma_util.h
+++ b/chrome/installer/util/lzma_util.h
@@ -26,9 +26,7 @@
   UNPACK_CREATE_FILE_ERROR = 7,
   UNPACK_WRITE_FILE_ERROR = 8,
   UNPACK_SET_FILE_TIME_ERROR = 9,
-  // UNPACK_CLOSE_FILE_ERROR = 10, Deprecated.
-  UNPACK_ALLOCATE_ERROR = 11,
-  UNPACK_CRC_ERROR = 12,
+  UNPACK_CLOSE_FILE_ERROR = 10,
   UNPACK_STATUS_COUNT,
 };
 
diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.cc b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
index bbcc6a60..ed7a8e4 100644
--- a/chrome/renderer/plugins/chrome_plugin_placeholder.cc
+++ b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
@@ -322,13 +322,13 @@
   hide_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_HIDE);
   params.custom_items.push_back(hide_item);
 
-  blink::WebPoint point(event.PositionInWidget().x(),
-                        event.PositionInWidget().y());
+  gfx::Point point =
+      gfx::Point(event.PositionInWidget().x(), event.PositionInWidget().y());
   if (plugin() && plugin()->Container())
     point = plugin()->Container()->LocalToRootFramePoint(point);
 
-  params.x = point.x;
-  params.y = point.y;
+  params.x = point.x();
+  params.y = point.y();
 
   context_menu_request_id_ = render_frame()->ShowContextMenu(this, params);
   g_last_active_menu = this;
diff --git a/chrome/services/wilco_dtc_supportd/public/mojom/BUILD.gn b/chrome/services/wilco_dtc_supportd/public/mojom/BUILD.gn
index 1902d38..24bfd5d 100644
--- a/chrome/services/wilco_dtc_supportd/public/mojom/BUILD.gn
+++ b/chrome/services/wilco_dtc_supportd/public/mojom/BUILD.gn
@@ -9,5 +9,8 @@
 mojom("mojom") {
   sources = [ "wilco_dtc_supportd.mojom" ]
 
-  public_deps = [ "//mojo/public/mojom/base" ]
+  public_deps = [
+    "//chromeos/services/cros_healthd/public/mojom",
+    "//mojo/public/mojom/base",
+  ]
 }
diff --git a/chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom b/chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom
index c1747e9e..e41ce6b 100644
--- a/chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom
+++ b/chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom
@@ -10,6 +10,8 @@
 
 module chromeos.wilco_dtc_supportd.mojom;
 
+import "chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom";
+
 [Extensible]
 enum WilcoDtcSupportdWebRequestHttpMethod {
   kGet,
@@ -151,4 +153,9 @@
   // Handles actionable EC events related to power (i.e., battery,
   // charger) and connected docking solutions.
   HandleEvent@3(WilcoDtcSupportdEvent event);
+
+  // Binds |service| to an implementation of CrosHealthdDiagnsticsService. In
+  // production, this implementation is provided by cros_healthd.
+  GetCrosHealthdDiagnosticsService@4(
+      chromeos.cros_healthd.mojom.CrosHealthdDiagnosticsService& service);
 };
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index e4224da..37fe96b8 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -715,6 +715,7 @@
       "//crypto:platform",
       "//crypto:test_support",
       "//device/bluetooth:mocks",
+      "//device/fido:test_support",
       "//extensions/buildflags",
       "//google_apis:test_support",
       "//media:media_buildflags",
@@ -1375,6 +1376,7 @@
       "../browser/ui/zoom/zoom_controller_browsertest.cc",
       "../browser/unload_browsertest.cc",
       "../browser/usb/usb_browsertest.cc",
+      "../browser/webauthn/authenticator_extension_browsertest.cc",
       "../common/mac/app_mode_chrome_locator_browsertest.mm",
       "../common/mac/mock_launchd.h",
       "../common/mac/mock_launchd.mm",
@@ -2594,6 +2596,9 @@
 
         # ProcessSingletonMac doesn"t do anything.
         "../browser/process_singleton_browsertest.cc",
+
+        # TaskManagerView is not used or built on Mac.
+        "../browser/ui/views/task_manager_view_browsertest.cc",
       ]
 
       if (safe_browsing_mode == 1) {
@@ -3267,6 +3272,7 @@
     "../browser/policy/cloud/user_policy_signin_service_unittest.cc",
     "../browser/policy/developer_tools_policy_handler_unittest.cc",
     "../browser/policy/file_selection_dialogs_policy_handler_unittest.cc",
+    "../browser/policy/homepage_location_policy_handler_unittest.cc",
     "../browser/policy/javascript_policy_handler_unittest.cc",
     "../browser/policy/managed_bookmarks_policy_handler_unittest.cc",
     "../browser/policy/profile_policy_connector_unittest.cc",
@@ -4167,7 +4173,6 @@
       "../browser/media/webrtc/webrtc_log_uploader_unittest.cc",
       "../browser/media/webrtc/webrtc_rtp_dump_handler_unittest.cc",
       "../browser/media/webrtc/webrtc_rtp_dump_writer_unittest.cc",
-      "../browser/policy/homepage_location_policy_handler_unittest.cc",
       "../browser/policy/local_sync_policy_handler_unittest.cc",
       "../browser/renderer_context_menu/mock_render_view_context_menu.cc",
       "../browser/renderer_context_menu/mock_render_view_context_menu.h",
@@ -4447,6 +4452,7 @@
       "../browser/extensions/api/extension_action/extension_action_api_unittest.cc",
       "../browser/extensions/api/file_system/file_system_api_unittest.cc",
       "../browser/extensions/api/identity/extension_token_key_unittest.cc",
+      "../browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc",
       "../browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc",
       "../browser/extensions/api/identity/identity_api_unittest.cc",
       "../browser/extensions/api/identity/identity_mint_queue_unittest.cc",
diff --git a/chrome/test/base/web_ui_browser_test_browsertest.cc b/chrome/test/base/web_ui_browser_test_browsertest.cc
index 954a018..7a99626 100644
--- a/chrome/test/base/web_ui_browser_test_browsertest.cc
+++ b/chrome/test/base/web_ui_browser_test_browsertest.cc
@@ -101,7 +101,7 @@
 
 // Test that bogus javascript fails async test fast as well - no timeout waiting
 // for result.
-IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, TestFailsAsyncFast) {
+IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, MAYBE_TestFailsAsyncFast) {
   AddLibrary(base::FilePath(FILE_PATH_LITERAL("sample_downloads.js")));
   ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIDownloadsURL));
   EXPECT_FATAL_FAILURE(
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index a577035..afec8582 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -83,7 +83,7 @@
   },
 
   "HomepageLocation": {
-    "os": ["win", "linux", "mac", "chromeos"],
+    "os": ["win", "linux", "mac", "chromeos", "android"],
     "can_be_recommended": true,
     "policy_pref_mapping_test": [
       {
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_menu_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_menu_test.js
index fdf617d..501b3377 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_menu_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_menu_test.js
@@ -4,10 +4,23 @@
 
 /** @fileoverview Runs tests for the OS settings menu. */
 
+function setupRouter() {
+  const routes = {
+    BASIC: new settings.Route('/'),
+    ADVANCED: new settings.Route('/advanced'),
+  };
+  routes.BLUETOOTH = routes.BASIC.createSection('/bluetooth', 'bluetooth');
+  routes.RESET = routes.ADVANCED.createSection('/reset', 'reset');
+
+  settings.Router.resetInstanceForTesting(new settings.Router(routes));
+  settings.routes = routes;
+}
+
 suite('OSSettingsMenu', function() {
   let settingsMenu = null;
 
   setup(function() {
+    setupRouter();
     PolymerTest.clearBody();
     settingsMenu = document.createElement('os-settings-menu');
     settingsMenu.pageVisibility = settings.pageVisibility;
@@ -63,6 +76,7 @@
 
 suite('OSSettingsMenuReset', function() {
   setup(function() {
+    setupRouter();
     PolymerTest.clearBody();
     settings.Router.getInstance().navigateTo(settings.routes.RESET, '');
     settingsMenu = document.createElement('os-settings-menu');
diff --git a/chrome/test/data/webui/settings/chromeos/people_page_kerberos_accounts_test.js b/chrome/test/data/webui/settings/chromeos/people_page_kerberos_accounts_test.js
index 25cc640..2e83712 100644
--- a/chrome/test/data/webui/settings/chromeos/people_page_kerberos_accounts_test.js
+++ b/chrome/test/data/webui/settings/chromeos/people_page_kerberos_accounts_test.js
@@ -114,6 +114,15 @@
     };
 
     setup(function() {
+      const routes = {
+        BASIC: new settings.Route('/'),
+      };
+      routes.PEOPLE = routes.BASIC.createSection('/people', 'people');
+      routes.KERBEROS_ACCOUNTS = routes.PEOPLE.createChild('/kerberosAccounts');
+
+      settings.Router.resetInstanceForTesting(new settings.Router(routes));
+      settings.routes = routes;
+
       browserProxy = new TestKerberosAccountsBrowserProxy();
       settings.KerberosAccountsBrowserProxyImpl.instance_ = browserProxy;
       PolymerTest.clearBody();
diff --git a/chromecast/browser/cast_content_window.cc b/chromecast/browser/cast_content_window.cc
index 7e7a570..ebe3ea59 100644
--- a/chromecast/browser/cast_content_window.cc
+++ b/chromecast/browser/cast_content_window.cc
@@ -24,9 +24,6 @@
   observer_list_.RemoveObserver(observer);
 }
 
-void CastContentWindow::SetCanGoBackQuery(
-    base::RepeatingCallback<bool()> can_go_back) {}
-
 mojom::MediaControlUi* CastContentWindow::media_controls() {
   return nullptr;
 }
diff --git a/chromecast/browser/cast_content_window.h b/chromecast/browser/cast_content_window.h
index c034e01..c62b1b5b 100644
--- a/chromecast/browser/cast_content_window.h
+++ b/chromecast/browser/cast_content_window.h
@@ -8,7 +8,6 @@
 #include <memory>
 #include <string>
 
-#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
@@ -205,10 +204,6 @@
   // the window manager.
   virtual void SetHostContext(base::Value host_context) = 0;
 
-  // Set a callback that can be queried for if the owner can handle a "back"
-  // gesture. If not set the default is to assume that gestures are unhandled.
-  virtual void SetCanGoBackQuery(base::RepeatingCallback<bool()> can_go_back);
-
   // Notify the window that its visibility type has changed. This should only
   // ever be called by the window manager.
   // TODO(seantopping): Make this private to the window manager.
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 3dad9b8..f0c6e718 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-12834.0.0
\ No newline at end of file
+12847.0.0
\ No newline at end of file
diff --git a/chromeos/attestation/BUILD.gn b/chromeos/attestation/BUILD.gn
index 9c2426b..40d1388 100644
--- a/chromeos/attestation/BUILD.gn
+++ b/chromeos/attestation/BUILD.gn
@@ -25,9 +25,7 @@
 
 source_set("test_support") {
   testonly = true
-  public_deps = [
-    ":attestation",
-  ]
+  public_deps = [ ":attestation" ]
   deps = [
     "//base/test:test_support",
     "//components/account_id",
@@ -50,7 +48,5 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
-  sources = [
-    "attestation_flow_unittest.cc",
-  ]
+  sources = [ "attestation_flow_unittest.cc" ]
 }
diff --git a/chromeos/components/media_app_ui/BUILD.gn b/chromeos/components/media_app_ui/BUILD.gn
index 3930cae..29e9e2de 100644
--- a/chromeos/components/media_app_ui/BUILD.gn
+++ b/chromeos/components/media_app_ui/BUILD.gn
@@ -90,26 +90,18 @@
 
 js_library("test_driver_api_js") {
   testonly = true
-  sources = [
-    "test/driver_api.js",
-  ]
+  sources = [ "test/driver_api.js" ]
 }
 
 js_library("test_guest_query_receiver_js") {
   testonly = true
-  sources = [
-    "test/guest_query_receiver.js",
-  ]
-  deps = [
-    ":test_driver_api_js",
-  ]
+  sources = [ "test/guest_query_receiver.js" ]
+  deps = [ ":test_driver_api_js" ]
 }
 
 js_library("test_driver_js") {
   testonly = true
-  sources = [
-    "test/driver.js",
-  ]
+  sources = [ "test/driver.js" ]
   deps = [
     ":test_driver_api_js",
     "//ui/webui/resources/js:assert",
diff --git a/chromeos/components/mojo_bootstrap/BUILD.gn b/chromeos/components/mojo_bootstrap/BUILD.gn
index 7efc081b..093a9cfd 100644
--- a/chromeos/components/mojo_bootstrap/BUILD.gn
+++ b/chromeos/components/mojo_bootstrap/BUILD.gn
@@ -9,17 +9,13 @@
     "pending_connection_manager.cc",
     "pending_connection_manager.h",
   ]
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
   defines = [ "IS_MOJO_BOOTSTRAP_IMPL" ]
 }
 
 source_set("unit_tests") {
   testonly = true
-  sources = [
-    "pending_connection_manager_unittest.cc",
-  ]
+  sources = [ "pending_connection_manager_unittest.cc" ]
 
   deps = [
     ":mojo_bootstrap",
diff --git a/chromeos/components/nearby/library/BUILD.gn b/chromeos/components/nearby/library/BUILD.gn
index a4835b2..3579bc5f 100644
--- a/chromeos/components/nearby/library/BUILD.gn
+++ b/chromeos/components/nearby/library/BUILD.gn
@@ -30,7 +30,5 @@
     "thread_utils.h",
   ]
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 }
diff --git a/chromeos/components/power/BUILD.gn b/chromeos/components/power/BUILD.gn
index 672c440..3a5419b 100644
--- a/chromeos/components/power/BUILD.gn
+++ b/chromeos/components/power/BUILD.gn
@@ -23,9 +23,7 @@
 source_set("unit_tests") {
   testonly = true
 
-  sources = [
-    "dark_resume_controller_unittest.cc",
-  ]
+  sources = [ "dark_resume_controller_unittest.cc" ]
 
   deps = [
     ":power",
diff --git a/chromeos/components/sample_system_web_app_ui/BUILD.gn b/chromeos/components/sample_system_web_app_ui/BUILD.gn
index f540fd78..6ff0f38 100644
--- a/chromeos/components/sample_system_web_app_ui/BUILD.gn
+++ b/chromeos/components/sample_system_web_app_ui/BUILD.gn
@@ -25,17 +25,13 @@
 }
 
 group("closure_compile") {
-  deps = [
-    "resources/js:closure_compile",
-  ]
+  deps = [ "resources/js:closure_compile" ]
 }
 
 js2gtest("browser_tests_js") {
   test_type = "mojo_lite_webui"
 
-  sources = [
-    "test/sample_system_web_app_ui_browsertest.js",
-  ]
+  sources = [ "test/sample_system_web_app_ui_browsertest.js" ]
 
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 }
diff --git a/chromeos/components/sample_system_web_app_ui/resources/js/BUILD.gn b/chromeos/components/sample_system_web_app_ui/resources/js/BUILD.gn
index 9d29a97..a45021d 100644
--- a/chromeos/components/sample_system_web_app_ui/resources/js/BUILD.gn
+++ b/chromeos/components/sample_system_web_app_ui/resources/js/BUILD.gn
@@ -5,9 +5,7 @@
 import("//third_party/closure_compiler/compile_js.gni")
 
 js_type_check("closure_compile") {
-  deps = [
-    ":app",
-  ]
+  deps = [ ":app" ]
 }
 
 js_library("app") {
diff --git a/chromeos/components/smbfs/mojom/BUILD.gn b/chromeos/components/smbfs/mojom/BUILD.gn
index 1ddad420..2fa7f690 100644
--- a/chromeos/components/smbfs/mojom/BUILD.gn
+++ b/chromeos/components/smbfs/mojom/BUILD.gn
@@ -5,13 +5,9 @@
 import("//mojo/public/tools/bindings/mojom.gni")
 
 mojom_component("mojom") {
-  sources = [
-    "smbfs.mojom",
-  ]
+  sources = [ "smbfs.mojom" ]
 
-  public_deps = [
-    "//mojo/public/mojom/base",
-  ]
+  public_deps = [ "//mojo/public/mojom/base" ]
 
   output_prefix = "smbfs_mojom"
   macro_prefix = "SMBFS_MOJOM"
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 6a97e1b..32f3a60 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -257,11 +257,6 @@
 const base::Feature kUpdatedCellularActivationUi{
     "UpdatedCellularActivationUi", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Use the messages.google.com domain as part of the "Messages" feature under
-// "Connected Devices" settings.
-const base::Feature kUseMessagesGoogleComDomain{
-    "UseMessagesGoogleComDomain", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Use the staging URL as part of the "Messages" feature under "Connected
 // Devices" settings.
 const base::Feature kUseMessagesStagingUrl{"UseMessagesStagingUrl",
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index a0d4774..c4177fe 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -119,8 +119,6 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kUpdatedCellularActivationUi;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-extern const base::Feature kUseMessagesGoogleComDomain;
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kUseMessagesStagingUrl;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kUserActivityPrediction;
diff --git a/chromeos/cryptohome/cryptohome_util.cc b/chromeos/cryptohome/cryptohome_util.cc
index e6e056a..9f25a21 100644
--- a/chromeos/cryptohome/cryptohome_util.cc
+++ b/chromeos/cryptohome/cryptohome_util.cc
@@ -354,6 +354,8 @@
       return MOUNT_ERROR_PREVIOUS_MIGRATION_INCOMPLETE;
     case CRYPTOHOME_ERROR_REMOVE_FAILED:
       return MOUNT_ERROR_REMOVE_FAILED;
+    case CRYPTOHOME_ERROR_TPM_UPDATE_REQUIRED:
+      return MOUNT_ERROR_TPM_UPDATE_REQUIRED;
     // TODO(crbug.com/797563): Split the error space and/or handle everything.
     case CRYPTOHOME_ERROR_LOCKBOX_SIGNATURE_INVALID:
     case CRYPTOHOME_ERROR_LOCKBOX_CANNOT_SIGN:
@@ -372,7 +374,6 @@
     case CRYPTOHOME_ERROR_FAILED_TO_EXTEND_PCR:
     case CRYPTOHOME_ERROR_FAILED_TO_READ_PCR:
     case CRYPTOHOME_ERROR_PCR_ALREADY_EXTENDED:
-    case CRYPTOHOME_ERROR_TPM_UPDATE_REQUIRED:
       NOTREACHED();
       return MOUNT_ERROR_FATAL;
   }
diff --git a/chromeos/dbus/BUILD.gn b/chromeos/dbus/BUILD.gn
index b1919ab..161da8c 100644
--- a/chromeos/dbus/BUILD.gn
+++ b/chromeos/dbus/BUILD.gn
@@ -161,9 +161,7 @@
   defines = [ "IS_CHROMEOS_DBUS_IMPL" ]
 
   all_dependent_configs = [ ":use_real_dbus_clients_config" ]
-  public_deps = [
-    "//chromeos/dbus/constants",
-  ]
+  public_deps = [ "//chromeos/dbus/constants" ]
 
   deps = [
     "//base",
@@ -247,9 +245,7 @@
 }
 
 proto_library("anomaly_detector_proto") {
-  sources = [
-    "//third_party/cros_system_api/dbus/anomaly_detector/anomaly_detector.proto",
-  ]
+  sources = [ "//third_party/cros_system_api/dbus/anomaly_detector/anomaly_detector.proto" ]
 
   proto_out_dir = "chromeos/dbus/anomaly_detector"
 }
@@ -271,33 +267,28 @@
 }
 
 proto_library("metrics_event_proto") {
-  sources = [
-    "//third_party/cros_system_api/dbus/metrics_event/metrics_event.proto",
-  ]
+  sources =
+      [ "//third_party/cros_system_api/dbus/metrics_event/metrics_event.proto" ]
 
   proto_out_dir = "chromeos/dbus/metrics_event"
 }
 
 proto_library("oobe_config_proto") {
-  sources = [
-    "//third_party/cros_system_api/dbus/oobe_config/oobe_config.proto",
-  ]
+  sources =
+      [ "//third_party/cros_system_api/dbus/oobe_config/oobe_config.proto" ]
 
   proto_out_dir = "chromeos/dbus/oobe_config"
 }
 
 proto_library("plugin_vm_service_proto") {
-  sources = [
-    "//third_party/cros_system_api/dbus/plugin_vm_service/plugin_vm_service.proto",
-  ]
+  sources = [ "//third_party/cros_system_api/dbus/plugin_vm_service/plugin_vm_service.proto" ]
 
   proto_out_dir = "chromeos/dbus/plugin_vm_service"
 }
 
 proto_library("runtime_probe_proto") {
-  sources = [
-    "//third_party/cros_system_api/dbus/runtime_probe/runtime_probe.proto",
-  ]
+  sources =
+      [ "//third_party/cros_system_api/dbus/runtime_probe/runtime_probe.proto" ]
 
   # chromeos side uses full runtime, so we don't specify lite runtime in proto
   # file, instead we specify lite runtime here.
@@ -307,41 +298,34 @@
 }
 
 proto_library("seneschal_proto") {
-  sources = [
-    "//third_party/cros_system_api/dbus/seneschal/seneschal_service.proto",
-  ]
+  sources =
+      [ "//third_party/cros_system_api/dbus/seneschal/seneschal_service.proto" ]
 
   proto_out_dir = "chromeos/dbus/seneschal"
 }
 
 proto_library("smbprovider_proto") {
-  sources = [
-    "//third_party/cros_system_api/dbus/smbprovider/directory_entry.proto",
-  ]
+  sources =
+      [ "//third_party/cros_system_api/dbus/smbprovider/directory_entry.proto" ]
 
   proto_out_dir = "chromeos/dbus/smbprovider"
 }
 
 proto_library("update_engine_proto") {
-  sources = [
-    "//third_party/cros_system_api/dbus/update_engine/update_engine.proto",
-  ]
+  sources =
+      [ "//third_party/cros_system_api/dbus/update_engine/update_engine.proto" ]
 
   proto_out_dir = "chromeos/dbus/update_engine"
 }
 
 proto_library("vm_applications_apps_proto") {
-  sources = [
-    "//third_party/cros_system_api/dbus/vm_applications/apps.proto",
-  ]
+  sources = [ "//third_party/cros_system_api/dbus/vm_applications/apps.proto" ]
 
   proto_out_dir = "chromeos/dbus/vm_applications"
 }
 
 proto_library("vm_plugin_dispatcher_proto") {
-  sources = [
-    "//third_party/cros_system_api/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher.proto",
-  ]
+  sources = [ "//third_party/cros_system_api/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher.proto" ]
 
   proto_out_dir = "chromeos/dbus/vm_plugin_dispatcher"
 }
diff --git a/chromeos/dbus/biod/BUILD.gn b/chromeos/dbus/biod/BUILD.gn
index e8049ff..c0faea2 100644
--- a/chromeos/dbus/biod/BUILD.gn
+++ b/chromeos/dbus/biod/BUILD.gn
@@ -29,9 +29,7 @@
 
 source_set("test_support") {
   testonly = true
-  public_deps = [
-    ":biod",
-  ]
+  public_deps = [ ":biod" ]
   deps = [
     ":biod_proto",
     "//base",
diff --git a/chromeos/dbus/constants/BUILD.gn b/chromeos/dbus/constants/BUILD.gn
index 7f163b4..c61bb40 100644
--- a/chromeos/dbus/constants/BUILD.gn
+++ b/chromeos/dbus/constants/BUILD.gn
@@ -7,9 +7,7 @@
 component("constants") {
   output_name = "chromeos_dbus_constants"
   defines = [ "IS_CHROMEOS_DBUS_CONSTANTS_IMPL" ]
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
   sources = [
     "attestation_constants.cc",
     "attestation_constants.h",
diff --git a/chromeos/dbus/dlcservice/BUILD.gn b/chromeos/dbus/dlcservice/BUILD.gn
index b98070f7..fbd90baf 100644
--- a/chromeos/dbus/dlcservice/BUILD.gn
+++ b/chromeos/dbus/dlcservice/BUILD.gn
@@ -31,9 +31,7 @@
 source_set("test_support") {
   testonly = true
 
-  public_deps = [
-    ":dlcservice",
-  ]
+  public_deps = [ ":dlcservice" ]
   deps = [
     ":dlcservice_proto",
     "//base",
@@ -43,15 +41,11 @@
     "//testing/gtest",
   ]
 
-  sources = [
-    "dlcservice_client_unittest.cc",
-  ]
+  sources = [ "dlcservice_client_unittest.cc" ]
 }
 
 proto_library("dlcservice_proto") {
-  sources = [
-    "//third_party/cros_system_api/dbus/dlcservice/dlcservice.proto",
-  ]
+  sources = [ "//third_party/cros_system_api/dbus/dlcservice/dlcservice.proto" ]
 
   proto_out_dir = "chromeos/dbus/dlcservice"
 }
diff --git a/chromeos/dbus/media_analytics/BUILD.gn b/chromeos/dbus/media_analytics/BUILD.gn
index fe98046..17a0042 100644
--- a/chromeos/dbus/media_analytics/BUILD.gn
+++ b/chromeos/dbus/media_analytics/BUILD.gn
@@ -25,9 +25,7 @@
 }
 
 proto_library("media_perception_proto") {
-  sources = [
-    "media_perception.proto",
-  ]
+  sources = [ "media_perception.proto" ]
 
   proto_out_dir = "chromeos/dbus/media_perception"
 }
diff --git a/chromeos/dbus/services/BUILD.gn b/chromeos/dbus/services/BUILD.gn
index 939b31ee..57830d4 100644
--- a/chromeos/dbus/services/BUILD.gn
+++ b/chromeos/dbus/services/BUILD.gn
@@ -45,7 +45,5 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
-  sources = [
-    "cros_dbus_service_unittest.cc",
-  ]
+  sources = [ "cros_dbus_service_unittest.cc" ]
 }
diff --git a/chromeos/dbus/session_manager/BUILD.gn b/chromeos/dbus/session_manager/BUILD.gn
index 7b42b2d..565cfa8 100644
--- a/chromeos/dbus/session_manager/BUILD.gn
+++ b/chromeos/dbus/session_manager/BUILD.gn
@@ -9,9 +9,7 @@
 component("session_manager") {
   defines = [ "IS_SESSION_MANAGER_IMPL" ]
 
-  public_deps = [
-    ":login_manager_proto",
-  ]
+  public_deps = [ ":login_manager_proto" ]
 
   deps = [
     "//base",
diff --git a/chromeos/login/auth/auth_status_consumer.h b/chromeos/login/auth/auth_status_consumer.h
index 3ddc3ea..13b67bd 100644
--- a/chromeos/login/auth/auth_status_consumer.h
+++ b/chromeos/login/auth/auth_status_consumer.h
@@ -40,6 +40,7 @@
     FAILED_TO_INITIALIZE_TOKEN = 12,  // Could not get OAuth2 Token,
     MISSING_CRYPTOHOME = 13,          // cryptohome missing from disk.
     AUTH_DISABLED = 14,               // Authentication disabled for user.
+    TPM_UPDATE_REQUIRED = 15,         // TPM firmware update is required.
     NUM_FAILURE_REASONS,              // This has to be the last item.
   };
 
@@ -96,6 +97,8 @@
         return "Auth disabled for user.";
       case TPM_ERROR:
         return "Critical TPM error encountered.";
+      case TPM_UPDATE_REQUIRED:
+        return "TPM firmware update required.";
       default:
         NOTREACHED();
         return std::string();
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc
index e99104b..68fb7a0 100644
--- a/chromeos/login/auth/cryptohome_authenticator.cc
+++ b/chromeos/login/auth/cryptohome_authenticator.cc
@@ -131,6 +131,8 @@
       return "FAILED_PREVIOUS_MIGRATION_INCOMPLETE";
     case CryptohomeAuthenticator::OFFLINE_NO_MOUNT:
       return "OFFLINE_NO_MOUNT";
+    case CryptohomeAuthenticator::TPM_UPDATE_REQUIRED:
+      return "TPM_UPDATE_REQUIRED";
   }
   return "UNKNOWN";
 }
@@ -1031,6 +1033,13 @@
           base::BindOnce(&CryptohomeAuthenticator::OnAuthFailure, this,
                          AuthFailure(AuthFailure::MISSING_CRYPTOHOME)));
       break;
+    case TPM_UPDATE_REQUIRED:
+      current_state_->ResetCryptohomeStatus();
+      task_runner_->PostTask(
+          FROM_HERE,
+          base::BindOnce(&CryptohomeAuthenticator::OnAuthFailure, this,
+                         AuthFailure(AuthFailure::TPM_UPDATE_REQUIRED)));
+      break;
     default:
       NOTREACHED();
       break;
@@ -1111,6 +1120,10 @@
       cryptohome::MOUNT_ERROR_PREVIOUS_MIGRATION_INCOMPLETE) {
     return FAILED_PREVIOUS_MIGRATION_INCOMPLETE;
   }
+  if (current_state_->cryptohome_code() ==
+      cryptohome::MOUNT_ERROR_TPM_UPDATE_REQUIRED) {
+    return TPM_UPDATE_REQUIRED;
+  }
 
   // Return intermediate states in the following case:
   // when there is an online result to use;
diff --git a/chromeos/login/auth/cryptohome_authenticator.h b/chromeos/login/auth/cryptohome_authenticator.h
index 5141db6..fed06a0 100644
--- a/chromeos/login/auth/cryptohome_authenticator.h
+++ b/chromeos/login/auth/cryptohome_authenticator.h
@@ -100,6 +100,7 @@
                                                 // partially encrypted in old
                                                 // format.
     OFFLINE_NO_MOUNT = 26,  // Offline login failed due to missing cryptohome.
+    TPM_UPDATE_REQUIRED = 27,  // TPM firmware update is required.
   };
 
   CryptohomeAuthenticator(scoped_refptr<base::SequencedTaskRunner> task_runner,
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index b0a0f76..a474b6d 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -127,9 +127,7 @@
 
     libs = [ "$root_out_dir/libassistant.so" ]
 
-    data = [
-      "$root_out_dir/libassistant.so",
-    ]
+    data = [ "$root_out_dir/libassistant.so" ]
   }
 }
 
diff --git a/chromeos/services/assistant/public/cpp/BUILD.gn b/chromeos/services/assistant/public/cpp/BUILD.gn
index a7f3f0b6..a98a058 100644
--- a/chromeos/services/assistant/public/cpp/BUILD.gn
+++ b/chromeos/services/assistant/public/cpp/BUILD.gn
@@ -8,7 +8,5 @@
     "assistant_prefs.h",
   ]
 
-  deps = [
-    "//components/prefs",
-  ]
+  deps = [ "//components/prefs" ]
 }
diff --git a/chromeos/services/assistant/public/mojom/BUILD.gn b/chromeos/services/assistant/public/mojom/BUILD.gn
index ea5cd6d..32f1002b 100644
--- a/chromeos/services/assistant/public/mojom/BUILD.gn
+++ b/chromeos/services/assistant/public/mojom/BUILD.gn
@@ -34,9 +34,7 @@
 # target and Ash's public mojoms, which also depend on the definition of
 # AssistantNotification.
 mojom("notification") {
-  sources = [
-    "assistant_notification.mojom",
-  ]
+  sources = [ "assistant_notification.mojom" ]
 
   public_deps = [
     "//mojo/public/mojom/base",
diff --git a/chromeos/services/cros_healthd/public/cpp/BUILD.gn b/chromeos/services/cros_healthd/public/cpp/BUILD.gn
index 781f2550..b5f4f7b 100644
--- a/chromeos/services/cros_healthd/public/cpp/BUILD.gn
+++ b/chromeos/services/cros_healthd/public/cpp/BUILD.gn
@@ -18,9 +18,7 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [
-    "service_connection_unittest.cc",
-  ]
+  sources = [ "service_connection_unittest.cc" ]
   deps = [
     ":cpp",
     "//base/test:test_support",
diff --git a/chromeos/services/cros_healthd/public/cpp/service_connection.cc b/chromeos/services/cros_healthd/public/cpp/service_connection.cc
index b2e11e50..4e947277 100644
--- a/chromeos/services/cros_healthd/public/cpp/service_connection.cc
+++ b/chromeos/services/cros_healthd/public/cpp/service_connection.cc
@@ -57,6 +57,8 @@
       const std::vector<mojom::ProbeCategoryEnum>& categories_to_test,
       mojom::CrosHealthdProbeService::ProbeTelemetryInfoCallback callback)
       override;
+  void GetDiagnosticsService(
+      mojom::CrosHealthdDiagnosticsServiceRequest service) override;
 
   // Binds the factory interface |cros_healthd_service_factory_| to an
   // implementation in the cros_healthd daemon, if it is not already bound. The
@@ -157,6 +159,13 @@
                                                   std::move(callback));
 }
 
+void ServiceConnectionImpl::GetDiagnosticsService(
+    mojom::CrosHealthdDiagnosticsServiceRequest service) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  BindCrosHealthdServiceFactoryIfNeeded();
+  cros_healthd_service_factory_->GetDiagnosticsService(std::move(service));
+}
+
 void ServiceConnectionImpl::BindCrosHealthdServiceFactoryIfNeeded() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (cros_healthd_service_factory_.is_bound())
diff --git a/chromeos/services/cros_healthd/public/cpp/service_connection.h b/chromeos/services/cros_healthd/public/cpp/service_connection.h
index 5c81450..154f8d7 100644
--- a/chromeos/services/cros_healthd/public/cpp/service_connection.h
+++ b/chromeos/services/cros_healthd/public/cpp/service_connection.h
@@ -77,6 +77,13 @@
       const std::vector<mojom::ProbeCategoryEnum>& categories_to_test,
       mojom::CrosHealthdProbeService::ProbeTelemetryInfoCallback callback) = 0;
 
+  // Binds |service| to an implementation of CrosHealthdDiagnosticsService. In
+  // production, this implementation is provided by cros_healthd. See
+  // src/chromeos/service/cros_healthd/public/mojom/cros_healthd.mojom for
+  // details.
+  virtual void GetDiagnosticsService(
+      mojom::CrosHealthdDiagnosticsServiceRequest service) = 0;
+
  protected:
   ServiceConnection() = default;
   virtual ~ServiceConnection() = default;
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn
index 24b1c2d..d5ee851 100644
--- a/chromeos/services/device_sync/BUILD.gn
+++ b/chromeos/services/device_sync/BUILD.gn
@@ -5,9 +5,7 @@
 assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
 
 static_library("feature_status_change") {
-  sources = [
-    "feature_status_change.h",
-  ]
+  sources = [ "feature_status_change.h" ]
 }
 
 static_library("device_sync") {
@@ -229,9 +227,7 @@
     "mock_sync_scheduler.h",
   ]
 
-  public_deps = [
-    ":device_sync",
-  ]
+  public_deps = [ ":device_sync" ]
 
   deps = [
     ":device_sync",
diff --git a/chromeos/services/device_sync/proto/BUILD.gn b/chromeos/services/device_sync/proto/BUILD.gn
index 68b728b..e530c72 100644
--- a/chromeos/services/device_sync/proto/BUILD.gn
+++ b/chromeos/services/device_sync/proto/BUILD.gn
@@ -28,9 +28,7 @@
     "enum_util.h",
   ]
 
-  public_deps = [
-    ":proto",
-  ]
+  public_deps = [ ":proto" ]
 
   deps = [
     "//base",
@@ -46,9 +44,7 @@
     "cryptauth_v2_test_util.h",
   ]
 
-  public_deps = [
-    ":proto",
-  ]
+  public_deps = [ ":proto" ]
 
   deps = [
     "//base",
diff --git a/chromeos/services/device_sync/public/cpp/BUILD.gn b/chromeos/services/device_sync/public/cpp/BUILD.gn
index 025ba6a..f129337 100644
--- a/chromeos/services/device_sync/public/cpp/BUILD.gn
+++ b/chromeos/services/device_sync/public/cpp/BUILD.gn
@@ -23,9 +23,7 @@
     "//chromeos/services/device_sync/public/mojom",
   ]
 
-  deps = [
-    "//mojo/public/cpp/bindings",
-  ]
+  deps = [ "//mojo/public/cpp/bindings" ]
 }
 
 static_library("test_support") {
@@ -40,21 +38,15 @@
     "fake_gcm_device_info_provider.h",
   ]
 
-  public_deps = [
-    ":cpp",
-  ]
+  public_deps = [ ":cpp" ]
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 }
 
 source_set("unit_tests") {
   testonly = true
 
-  sources = [
-    "device_sync_client_impl_unittest.cc",
-  ]
+  sources = [ "device_sync_client_impl_unittest.cc" ]
 
   deps = [
     ":cpp",
diff --git a/chromeos/services/device_sync/public/mojom/BUILD.gn b/chromeos/services/device_sync/public/mojom/BUILD.gn
index f1c33c9..1ca02d4 100644
--- a/chromeos/services/device_sync/public/mojom/BUILD.gn
+++ b/chromeos/services/device_sync/public/mojom/BUILD.gn
@@ -5,9 +5,7 @@
 import("//mojo/public/tools/bindings/mojom.gni")
 
 mojom("mojom") {
-  sources = [
-    "device_sync.mojom",
-  ]
+  sources = [ "device_sync.mojom" ]
 
   public_deps = [
     "//chromeos/components/multidevice/mojom",
@@ -18,9 +16,7 @@
 source_set("unit_tests") {
   testonly = true
 
-  sources = [
-    "device_sync_mojom_traits_unittest.cc",
-  ]
+  sources = [ "device_sync_mojom_traits_unittest.cc" ]
 
   deps = [
     ":mojom",
diff --git a/chromeos/services/ime/BUILD.gn b/chromeos/services/ime/BUILD.gn
index 4d40222c..968dd84 100644
--- a/chromeos/services/ime/BUILD.gn
+++ b/chromeos/services/ime/BUILD.gn
@@ -57,9 +57,7 @@
 
 source_set("unit_tests") {
   testonly = true
-  deps = [
-    "//chromeos/services/ime/public/cpp:rulebased_unit_tests",
-  ]
+  deps = [ "//chromeos/services/ime/public/cpp:rulebased_unit_tests" ]
 }
 
 source_set("services_unittests") {
@@ -74,7 +72,5 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
-  sources = [
-    "ime_service_unittest.cc",
-  ]
+  sources = [ "ime_service_unittest.cc" ]
 }
diff --git a/chromeos/services/ime/public/cpp/BUILD.gn b/chromeos/services/ime/public/cpp/BUILD.gn
index 8f1a3a4..acbdc304 100644
--- a/chromeos/services/ime/public/cpp/BUILD.gn
+++ b/chromeos/services/ime/public/cpp/BUILD.gn
@@ -104,22 +104,16 @@
     "//testing/gmock",
     "//testing/gtest:gtest",
   ]
-  sources = [
-    "rulebased/rulebased_unittest.cc",
-  ]
+  sources = [ "rulebased/rulebased_unittest.cc" ]
 }
 
 if (use_libfuzzer) {
   fuzzable_proto_library("rulebased_fuzzer_proto") {
-    sources = [
-      "rulebased/rulebased_fuzzer.proto",
-    ]
+    sources = [ "rulebased/rulebased_fuzzer.proto" ]
   }
 
   fuzzer_test("rulebased_fuzzer") {
-    sources = [
-      "rulebased/rulebased_fuzzer.cc",
-    ]
+    sources = [ "rulebased/rulebased_fuzzer.cc" ]
     seed_corpus = "rulebased/test_data"
     deps = [
       ":rulebased",
diff --git a/chromeos/services/ime/public/cpp/shared_lib/BUILD.gn b/chromeos/services/ime/public/cpp/shared_lib/BUILD.gn
index d84e1d2..51ca7de 100644
--- a/chromeos/services/ime/public/cpp/shared_lib/BUILD.gn
+++ b/chromeos/services/ime/public/cpp/shared_lib/BUILD.gn
@@ -3,7 +3,5 @@
 # found in the LICENSE file.
 
 source_set("interfaces") {
-  sources = [
-    "interfaces.h",
-  ]
+  sources = [ "interfaces.h" ]
 }
diff --git a/chromeos/services/ime/public/mojom/BUILD.gn b/chromeos/services/ime/public/mojom/BUILD.gn
index e87d17b0..0460ad18 100644
--- a/chromeos/services/ime/public/mojom/BUILD.gn
+++ b/chromeos/services/ime/public/mojom/BUILD.gn
@@ -5,9 +5,7 @@
 import("//mojo/public/tools/bindings/mojom.gni")
 
 mojom("mojom") {
-  sources = [
-    "input_engine.mojom",
-  ]
+  sources = [ "input_engine.mojom" ]
 
   public_deps = [
     "//mojo/public/mojom/base",
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 93345fc..c7d89f7 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -138,6 +138,7 @@
     "//components/signin/public/base:unit_tests",
     "//components/signin/public/identity_manager:unit_tests",
     "//components/signin/public/webdata:unit_tests",
+    "//components/sqlite_proto:unit_tests",
     "//components/ssl_errors:unit_tests",
     "//components/subresource_filter/core/browser:unit_tests",
     "//components/subresource_filter/core/common:unit_tests",
diff --git a/components/autofill/core/browser/webdata/OWNERS b/components/autofill/core/browser/webdata/OWNERS
index 0953786..44502b7 100644
--- a/components/autofill/core/browser/webdata/OWNERS
+++ b/components/autofill/core/browser/webdata/OWNERS
@@ -1,8 +1,8 @@
 per-file *sync_bridge*=jkrcal@chromium.org
 per-file *sync_bridge*=file://components/sync/OWNERS
 
-per-file *syncable_service*=jkrcal@chromium.org
-per-file *syncable_service*=file://components/sync/OWNERS
+per-file autofill_profile_sync_difference_tracker*=jkrcal@chromium.org
+per-file autofill_profile_sync_difference_tracker*=file://components/sync/OWNERS
 
 per-file *type_controller*=jkrcal@chromium.org
 per-file *type_controller*=file://components/sync/OWNERS
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
index a7f39a9b..627a0aac 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
@@ -251,13 +251,18 @@
                      base::Unretained(web_data_backend_))));
 
   std::vector<std::unique_ptr<AutofillProfile>> profiles_to_upload_to_sync;
-  RETURN_IF_ERROR(tracker->FlushToSync(&profiles_to_upload_to_sync));
+  std::vector<std::string> profiles_to_delete_from_sync;
+  RETURN_IF_ERROR(tracker->FlushToSync(&profiles_to_upload_to_sync,
+                                       &profiles_to_delete_from_sync));
   for (const std::unique_ptr<AutofillProfile>& entry :
        profiles_to_upload_to_sync) {
     change_processor()->Put(GetStorageKeyFromAutofillProfile(*entry),
                             CreateEntityDataFromAutofillProfile(*entry),
                             metadata_change_list.get());
   }
+  for (const std::string& storage_key : profiles_to_delete_from_sync) {
+    change_processor()->Delete(storage_key, metadata_change_list.get());
+  }
 
   return static_cast<syncer::SyncMetadataStoreChangeList*>(
              metadata_change_list.get())
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
index 8f96bb8..005c0d1 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
@@ -64,6 +64,11 @@
   }
 
   // Check if profile appears under a different storage key to be de-duplicated.
+  // TODO(crbug.com/1043683): Deal with rare cases when an remote update
+  // contains several exact duplicates (with different guids). We should not
+  // only search in local only entries but also in |update_to_local_| and
+  // |add_to_local_|. Likely needs a bit of refactoring to make the resulting
+  // code easy to understand.
   for (const auto& pair : *GetLocalOnlyEntries()) {
     const std::string& local_storage_key = pair.first;
     const AutofillProfile& local = *pair.second;
@@ -71,30 +76,76 @@
     // Look for exact duplicates, compare only profile contents (and
     // ignore origin and language code in comparison).
     if (local.Compare(*remote) == 0) {
-      // We found a duplicate, we keep the new (remote) one and delete the
-      // local one.
+      // A duplicate found: keep the version with the bigger storage key.
       DVLOG(2)
           << "[AUTOFILL SYNC] The profile "
           << base::UTF16ToUTF8(local.GetRawInfo(NAME_FIRST))
           << base::UTF16ToUTF8(local.GetRawInfo(NAME_LAST))
-          << " already exists with a different storage key; keep the remote key"
-          << remote_storage_key << " and delete the local key "
-          << local_storage_key;
-
-      // Ensure that a verified profile can never revert back to an unverified
-      // one. In such a case, take over the local origin for the new (remote)
-      // entry.
-      if (local.IsVerified() && !remote->IsVerified()) {
-        remote->set_origin(local.origin());
-        // Save a copy of the remote profile also to sync.
-        save_to_sync_.push_back(std::make_unique<AutofillProfile>(*remote));
+          << " already exists with a different storage key; keep the bigger "
+          << (remote_storage_key > local_storage_key ? "remote" : "local")
+          << " key " << std::max(remote_storage_key, local_storage_key)
+          << " and delete the smaller key "
+          << std::min(remote_storage_key, local_storage_key);
+      if (remote_storage_key > local_storage_key) {
+        // We keep the remote entity and delete the local one.
+        // Ensure that a verified profile can never revert back to an unverified
+        // one. In such a case, take over the old origin for the new entry.
+        if (local.IsVerified() && !remote->IsVerified()) {
+          remote->set_origin(local.origin());
+          // Save a copy of the remote profile also to sync.
+          save_to_sync_.push_back(std::make_unique<AutofillProfile>(*remote));
+        }
+        add_to_local_.push_back(std::move(remote));
+        // Deleting from sync is a no-op if it is local-only so far.
+        // There are a few ways how a synced local entry A could theoretically
+        // receive a remote duplicate B with a larger GUID:
+        //  1) Remote entity B got uploaded by another client through initial
+        //     sync. That client thus also knew about A and issued a deletion of
+        //     A at the same time. This client (if receiving creation of B
+        //     first) resolves the conflict in the same way and re-issues the
+        //     deletion of A. In most cases the redundant deletion does not even
+        //     get sent as the processor already knows A got deleted remotely.
+        //  2) Remote entity B got uploaded by another client through race
+        //     condition (i.e. not knowing about A, yet). In practice, this only
+        //     happens when two clients simultaneously convert a server profile
+        //     into local profiles. If the other client goes offline before
+        //     receiving A, this client is responsible for deleting A from the
+        //     server and thus must issue a deletion. (In most cases, the other
+        //     client does not go offline and thus both clients issue a deletion
+        //     of A independently).
+        //  3) (a paranoid case) Remote entity B got uploaded by another client
+        //     by an error, i.e. already as a duplicate given their local state.
+        //     Through standard flows, it should be impossible (duplicates are
+        //     cought early in PDM code so such a change attempt does not even
+        //     propagate to the sync bridge). Still, it's good to treat this
+        //     case here for robustness.
+        delete_from_sync_.insert(local_storage_key);
+        DeleteFromLocal(local_storage_key);
+      } else {
+        // We keep the local entity and delete the remote one.
+        // Ensure that a verified profile can never revert back to an unverified
+        // one. In such a case, modify the origin and re-upload. Otherwise,
+        // there's no need to upload it: either is was already uploaded before
+        // (if this is incremental sync) or we'll upload it with all the
+        // remaining data in GetLocalOnlyEntries (if this is an initial sync).
+        if (remote->IsVerified() && !local.IsVerified()) {
+          auto modified_local = std::make_unique<AutofillProfile>(local);
+          modified_local->set_origin(remote->origin());
+          update_to_local_.push_back(
+              std::make_unique<AutofillProfile>(*modified_local));
+          save_to_sync_.push_back(std::move(modified_local));
+          // The local entity is already marked for upload so it is not local
+          // only anymore (we do not want to upload it once again while flushing
+          // if this is initial sync).
+          GetLocalOnlyEntries()->erase(local_storage_key);
+        }
+        delete_from_sync_.insert(remote_storage_key);
       }
-      // Delete the local profile that gets replaced by |remote|.
-      DeleteFromLocal(local_storage_key);
-      break;
+      return base::nullopt;
     }
   }
 
+  // If no duplicate was found, just add the remote profile.
   add_to_local_.push_back(std::move(remote));
   return base::nullopt;
 }
@@ -132,10 +183,14 @@
 }
 
 Optional<ModelError> AutofillProfileSyncDifferenceTracker::FlushToSync(
-    std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync) {
+    std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
+    std::vector<std::string>* profiles_to_delete_from_sync) {
   for (std::unique_ptr<AutofillProfile>& entry : save_to_sync_) {
     profiles_to_upload_to_sync->push_back(std::move(entry));
   }
+  for (const std::string& entry : delete_from_sync_) {
+    profiles_to_delete_from_sync->push_back(std::move(entry));
+  }
   return base::nullopt;
 }
 
@@ -200,9 +255,11 @@
 }
 
 Optional<ModelError> AutofillProfileInitialSyncDifferenceTracker::FlushToSync(
-    std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync) {
+    std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
+    std::vector<std::string>* profiles_to_delete_from_sync) {
   // First, flush standard updates to sync.
-  AutofillProfileSyncDifferenceTracker::FlushToSync(profiles_to_upload_to_sync);
+  AutofillProfileSyncDifferenceTracker::FlushToSync(
+      profiles_to_upload_to_sync, profiles_to_delete_from_sync);
 
   // For initial sync, we additionally need to upload all local only entries.
   if (!GetLocalOnlyEntries()) {
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
index 055de2e..2ef17366 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
@@ -50,11 +50,12 @@
       base::OnceClosure autofill_changes_callback);
 
   // Writes into |profiles_to_upload_to_sync| all autofill profiles to be sent
-  // to the sync server. After flushing, not further remote changes should get
-  // incorporated.
+  // to the sync server, and into |profiles_to_delete_from_sync| the storage
+  // keys of all profiles to be deleted from the server. After flushing, no
+  // further remote changes should get incorporated.
   virtual base::Optional<syncer::ModelError> FlushToSync(
-      std::vector<std::unique_ptr<AutofillProfile>>*
-          profiles_to_upload_to_sync);
+      std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
+      std::vector<std::string>* profiles_to_delete_from_sync);
 
  protected:
   // If the entry is found, |entry| will be return, otherwise base::nullopt is
@@ -89,7 +90,7 @@
 
   // We use unique_ptrs for storing AutofillProfile to avoid unnecessary copies.
 
-  // Local data, mapped by storage key. Use unique_to_local() to access it.
+  // Local data, mapped by storage key. Use GetLocalOnlyEntries() to access it.
   std::map<std::string, std::unique_ptr<AutofillProfile>> local_only_entries_;
 
   // Contain changes (originating from sync) that need to be saved to the local
@@ -98,9 +99,13 @@
   std::vector<std::unique_ptr<AutofillProfile>> add_to_local_;
   std::vector<std::unique_ptr<AutofillProfile>> update_to_local_;
 
-  // Contains merged data for entries that existed on both sync and local sides
+  // Contains data for entries that existed on both sync and local sides
   // and need to be saved back to sync.
   std::vector<std::unique_ptr<AutofillProfile>> save_to_sync_;
+  // Contains storage keys for entries that existed on both sync and local
+  // sides and need to be deleted from sync (because the conflict resolution
+  // preferred the local copies).
+  std::set<std::string> delete_from_sync_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncDifferenceTracker);
@@ -116,8 +121,8 @@
       const std::string& storage_key) override;
 
   base::Optional<syncer::ModelError> FlushToSync(
-      std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync)
-      override;
+      std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
+      std::vector<std::string>* profiles_to_delete_from_sync) override;
 
   // Performs an additional pass through remote entries incorporated from sync
   // to find any similarities with local entries. Should be run after all
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
index 5a30b96..403f270 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
@@ -30,13 +30,18 @@
 using testing::IsEmpty;
 
 // Some guids for testing.
-const char kGuidA[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A";
-const char kGuidB[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44B";
+const char kSmallerGuid[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A";
+const char kBiggerGuid[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44B";
 const char kHttpOrigin[] = "http://www.example.com/";
 const char kHttpsOrigin[] = "https://www.example.com/";
 const char kLocaleString[] = "en-US";
 const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
 
+struct UpdatesToSync {
+  std::vector<AutofillProfile> profiles_to_upload_to_sync;
+  std::vector<std::string> profiles_to_delete_from_sync;
+};
+
 }  // namespace
 
 class AutofillProfileSyncDifferenceTrackerTestBase : public testing::Test {
@@ -65,24 +70,26 @@
                                  std::make_unique<AutofillProfile>(profile)));
   }
 
-  std::vector<AutofillProfile> FlushAndReturnProfilesToUploadToSync() {
+  UpdatesToSync FlushToSync() {
     EXPECT_EQ(base::nullopt,
               tracker()->FlushToLocal(
                   /*autofill_changes_callback=*/base::DoNothing()));
 
+    UpdatesToSync updates;
     std::vector<std::unique_ptr<AutofillProfile>> vector_of_unique_ptrs;
     EXPECT_EQ(base::nullopt,
               tracker()->FlushToSync(
-                  /*profiles_to_upload_to_sync=*/&vector_of_unique_ptrs));
+                  /*profiles_to_upload_to_sync=*/&vector_of_unique_ptrs,
+                  /*profiles_to_delete_from_sync=*/&updates
+                      .profiles_to_delete_from_sync));
 
     // Copy all the elements by value so that we have a vector that is easier to
     // work with in the test.
-    std::vector<AutofillProfile> vector_of_values;
     for (const std::unique_ptr<AutofillProfile>& entry :
          vector_of_unique_ptrs) {
-      vector_of_values.push_back(*entry);
+      updates.profiles_to_upload_to_sync.push_back(*entry);
     }
-    return vector_of_values;
+    return updates;
   }
 
   std::vector<AutofillProfile> GetAllLocalData() {
@@ -130,63 +137,69 @@
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        IncorporateRemoteProfileShouldOverwriteProfileWithSameKey) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile is completely different but it has the same key.
-  AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
 
   IncorporateRemoteProfile(remote);
 
   // Nothing gets uploaded to sync and the remote profile wins.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
 }
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        IncorporateRemoteProfileShouldOverwriteUnverifiedProfileByVerified) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile has the same key but it is not verified.
-  AutofillProfile remote = AutofillProfile(kGuidA, kSettingsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
 
   IncorporateRemoteProfile(remote);
 
   // Nothing gets uploaded to sync and the local profile wins.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
 }
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        IncorporateRemoteProfileShouldNotOverwriteVerifiedProfileByUnverified) {
-  AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile has the same key but it is not verified.
-  AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
 
   IncorporateRemoteProfile(remote);
 
   // Nothing gets uploaded to sync and the local profile wins.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(local));
 }
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        IncorporateRemoteProfileShouldNotOverwriteFullNameByEmptyString) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(NAME_FULL, ASCIIToUTF16("John"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile has the same key.
-  AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2st st"));
 
   AutofillProfile merged(remote);
@@ -196,40 +209,46 @@
 
   // Nothing gets uploaded to sync and the remote profile wins except for the
   // full name.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
 }
 
-TEST_F(AutofillProfileSyncDifferenceTrackerTest,
-       IncorporateRemoteProfileShouldMergeIdenticalProfilesWithDifferentKeys) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+TEST_F(
+    AutofillProfileSyncDifferenceTrackerTest,
+    IncorporateRemoteProfileShouldKeepRemoteKeyWhenMergingDuplicateProfileWithBiggerKey) {
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile is identical to the local one, except that the guids and
   // origins are different.
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpsOrigin);
   remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
 
   IncorporateRemoteProfile(remote);
 
   // Nothing gets uploaded to sync and the remote profile wins.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync,
+              ElementsAre(std::string(kSmallerGuid)));
   EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
 }
 
 TEST_F(
     AutofillProfileSyncDifferenceTrackerTest,
-    IncorporateRemoteProfileShouldMergeIdenticalProfilesWithDifferentKeysButKeepVerifiedOrigin) {
-  AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+    IncorporateRemoteProfileShouldKeepRemoteKeyAndLocalOriginWhenMergingDuplicateProfileWithBiggerKey) {
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile has the same key.
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpsOrigin);
   remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
 
@@ -240,7 +259,61 @@
 
   // Nothing gets uploaded to sync and the remote profile wins except for the
   // full name.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(merged));
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(merged));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync,
+              ElementsAre(std::string(kSmallerGuid)));
+  EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
+}
+
+TEST_F(
+    AutofillProfileSyncDifferenceTrackerTest,
+    IncorporateRemoteProfileShouldKeepLocalKeyWhenMergingDuplicateProfileWithSmallerKey) {
+  AutofillProfile local = AutofillProfile(kBiggerGuid, kHttpOrigin);
+  local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+  local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+  AddAutofillProfilesToTable({local});
+
+  // The remote profile is identical to the local one, except that the guids and
+  // origins are different.
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin);
+  remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+  remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+
+  IncorporateRemoteProfile(remote);
+
+  // Nothing gets uploaded to sync and the remote profile wins.
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync,
+              ElementsAre(std::string(kSmallerGuid)));
+  EXPECT_THAT(GetAllLocalData(), ElementsAre(local));
+}
+
+TEST_F(
+    AutofillProfileSyncDifferenceTrackerTest,
+    IncorporateRemoteProfileShouldKeepLocalKeyAndRemoteOriginWhenMergingDuplicateProfileWithSmallerKey) {
+  AutofillProfile local = AutofillProfile(kBiggerGuid, kHttpsOrigin);
+  local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+  local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+  AddAutofillProfilesToTable({local});
+
+  // The remote profile has the same key.
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kSettingsOrigin);
+  remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+  remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+
+  AutofillProfile merged(local);
+  merged.set_origin(kSettingsOrigin);
+
+  IncorporateRemoteProfile(remote);
+
+  // Nothing gets uploaded to sync and the remote profile wins except for the
+  // full name.
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(merged));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync,
+              ElementsAre(std::string(kSmallerGuid)));
   EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
 }
 
@@ -255,10 +328,10 @@
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        FlushToLocalShouldCallbackWhenProfileDeleted) {
-  AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   AddAutofillProfilesToTable({local});
 
-  tracker()->IncorporateRemoteDelete(kGuidA);
+  tracker()->IncorporateRemoteDelete(kSmallerGuid);
 
   MockCallback<base::OnceClosure> autofill_changes_callback;
   EXPECT_CALL(autofill_changes_callback, Run()).Times(1);
@@ -271,7 +344,7 @@
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        FlushToLocalShouldCallbackWhenProfileAdded) {
-  AutofillProfile remote = AutofillProfile(kGuidA, kSettingsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   IncorporateRemoteProfile(remote);
 
   MockCallback<base::OnceClosure> autofill_changes_callback;
@@ -285,10 +358,10 @@
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        FlushToLocalShouldCallbackWhenProfileUpdated) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   AddAutofillProfilesToTable({local});
 
-  AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   IncorporateRemoteProfile(remote);
 
@@ -297,7 +370,7 @@
   EXPECT_EQ(base::nullopt,
             tracker()->FlushToLocal(autofill_changes_callback.Get()));
 
-  // On top of that, the profile with key kGuidA should also get updated.
+  // On top of that, the profile with key kSmallerGuid should also get updated.
   EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
 }
 
@@ -324,13 +397,13 @@
 
 TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
        MergeSimilarEntriesForInitialSyncShouldSyncUpChanges) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   local.set_use_count(27);
   AddAutofillProfilesToTable({local});
 
   // The remote profile matches the local one (except for origin and use count).
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpsOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
   remote.set_use_count(13);
@@ -346,19 +419,21 @@
   MergeSimilarEntriesForInitialSync();
 
   // The merged profile needs to get uploaded back to sync and stored locally.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(merged));
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(merged));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
 }
 
 TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
        MergeSimilarEntriesForInitialSyncShouldNotSyncUpWhenNotNeeded) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   local.set_use_count(13);
   AddAutofillProfilesToTable({local});
 
   // The remote profile matches the local one and has some additional data.
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
   // Merging two profile takes their max use count, so use count of 27 is taken.
@@ -368,19 +443,21 @@
   MergeSimilarEntriesForInitialSync();
 
   // Nothing gets uploaded to sync and the remote profile wins.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
 }
 
 TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
        MergeSimilarEntriesForInitialSyncNotMatchNonsimilarEntries) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   local.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
   AddAutofillProfilesToTable({local});
 
   // The remote profile has a different street address.
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2st st"));
   remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
 
@@ -389,19 +466,21 @@
 
   // The local profile gets uploaded (due to initial sync) and the remote
   // profile gets stored locally.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local));
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(local));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote));
 }
 
 TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
        MergeSimilarEntriesForInitialSyncDoesNotMatchLocalVerifiedEntry) {
   // The local entry is verified, should not get merged.
-  AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile is similar to the local one.
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
 
@@ -410,19 +489,21 @@
 
   // The local profile gets uploaded (due to initial sync) and the remote
   // profile gets stored locally.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local));
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(local));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote));
 }
 
 TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
        MergeSimilarEntriesForInitialSyncDoesNotMatchRemoteVerifiedEntry) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile is similar to the local one but is verified and thus it
   // should not get merged.
-  AutofillProfile remote = AutofillProfile(kGuidB, kSettingsOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kSettingsOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
 
@@ -431,7 +512,9 @@
 
   // The local profile gets uploaded (due to initial sync) and the remote
   // profile gets stored locally.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local));
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(local));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote));
 }
 
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index 2868f4b..e540548 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -111,7 +111,7 @@
       std::unique_ptr<std::vector<UserAction>> user_actions) = 0;
 
   // Have the UI leave the prompt state and go back to its previous state.
-  virtual void CancelPrompt() = 0;
+  virtual void CleanUpAfterPrompt() = 0;
 
   // Asks the user to provide the requested user data.
   virtual void CollectUserData(
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action.cc b/components/autofill_assistant/browser/actions/collect_user_data_action.cc
index 8f8d3b0..745d1332 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action.cc
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action.cc
@@ -306,6 +306,7 @@
 }
 
 void CollectUserDataAction::EndAction(const ClientStatus& status) {
+  delegate_->CleanUpAfterPrompt();
   action_successful_ = status.ok();
   UpdateProcessedAction(status);
   std::move(callback_).Run(std::move(processed_action_proto_));
@@ -434,6 +435,7 @@
   if (collect_user_data.has_prompt()) {
     delegate_->SetStatusMessage(collect_user_data.prompt());
   }
+  delegate_->Prompt(nullptr);
   delegate_->CollectUserData(collect_user_data_options_.get());
 }
 
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 843d4e8..74e8010 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -69,7 +69,7 @@
 
   MOCK_METHOD1(Prompt,
                void(std::unique_ptr<std::vector<UserAction>> user_actions));
-  MOCK_METHOD0(CancelPrompt, void());
+  MOCK_METHOD0(CleanUpAfterPrompt, void());
 
   void FillAddressForm(
       const autofill::AutofillProfile* profile,
diff --git a/components/autofill_assistant/browser/actions/prompt_action.cc b/components/autofill_assistant/browser/actions/prompt_action.cc
index 465740a..c4a9732 100644
--- a/components/autofill_assistant/browser/actions/prompt_action.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action.cc
@@ -26,13 +26,12 @@
 PromptAction::~PromptAction() {}
 
 void PromptAction::InternalProcessAction(ProcessActionCallback callback) {
+  callback_ = std::move(callback);
   if (proto_.prompt().choices_size() == 0) {
-    UpdateProcessedAction(INVALID_ACTION);
-    std::move(callback).Run(std::move(processed_action_proto_));
+    EndAction(INVALID_ACTION);
     return;
   }
 
-  callback_ = std::move(callback);
   if (proto_.prompt().has_message()) {
     // TODO(b/144468818): Deprecate and remove message from this action and use
     // tell instead.
@@ -199,7 +198,6 @@
 void PromptAction::OnDoneWaitForDom(const ClientStatus& status) {
   // Status doesn't matter; it's just forwarded from AutoSelectDone.
   if (auto_select_choice_index_ >= 0) {
-    delegate_->CancelPrompt();
     OnSuggestionChosen(auto_select_choice_index_);
   }
 }
@@ -212,9 +210,14 @@
   DCHECK(choice_index >= 0 && choice_index <= proto_.prompt().choices_size());
 
   PromptProto::Choice choice;
-  UpdateProcessedAction(ACTION_APPLIED);
   *processed_action_proto_->mutable_prompt_choice() =
       proto_.prompt().choices(choice_index);
+  EndAction(ACTION_APPLIED);
+}
+
+void PromptAction::EndAction(const ProcessedActionStatusProto& status) {
+  delegate_->CleanUpAfterPrompt();
+  UpdateProcessedAction(status);
   std::move(callback_).Run(std::move(processed_action_proto_));
 }
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/prompt_action.h b/components/autofill_assistant/browser/actions/prompt_action.h
index c28e3cd..ae9c7839 100644
--- a/components/autofill_assistant/browser/actions/prompt_action.h
+++ b/components/autofill_assistant/browser/actions/prompt_action.h
@@ -45,6 +45,7 @@
       base::OnceCallback<void(const ClientStatus&)> wait_for_dom_callback);
   void OnDoneWaitForDom(const ClientStatus& status);
   void OnSuggestionChosen(int choice_index);
+  void EndAction(const ProcessedActionStatusProto& status);
 
   ProcessActionCallback callback_;
 
diff --git a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
index 8849f2a8..03fb70f 100644
--- a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
@@ -261,7 +261,7 @@
               OnElementCheck(Eq(Selector({"element"})), _))
       .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
 
-  EXPECT_CALL(mock_action_delegate_, CancelPrompt());
+  EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt());
   EXPECT_CALL(
       callback_,
       Run(Pointee(AllOf(Property(&ProcessedActionProto::status, ACTION_APPLIED),
@@ -291,7 +291,7 @@
   task_env_.FastForwardBy(base::TimeDelta::FromSeconds(1));
 
   // Second round of element checks: element has gone.
-  EXPECT_CALL(mock_action_delegate_, CancelPrompt());
+  EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt());
   EXPECT_CALL(
       callback_,
       Run(Pointee(AllOf(Property(&ProcessedActionProto::status, ACTION_APPLIED),
diff --git a/components/autofill_assistant/browser/actions/show_form_action.cc b/components/autofill_assistant/browser/actions/show_form_action.cc
index 1522bfe..353693e 100644
--- a/components/autofill_assistant/browser/actions/show_form_action.cc
+++ b/components/autofill_assistant/browser/actions/show_form_action.cc
@@ -184,6 +184,7 @@
 }
 
 void ShowFormAction::EndAction(const ClientStatus& status) {
+  delegate_->CleanUpAfterPrompt();
   delegate_->SetForm(nullptr, base::DoNothing(), base::DoNothing());
   UpdateProcessedAction(status);
   std::move(callback_).Run(std::move(processed_action_proto_));
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 1d2450a8..1e1f6a2 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -273,15 +273,6 @@
 
 void ScriptExecutor::Prompt(
     std::unique_ptr<std::vector<UserAction>> user_actions) {
-  // We change the chips callback with a callback that cleans up the state
-  // before calling the initial callback.
-  for (auto& user_action : *user_actions) {
-    if (!user_action.HasCallback())
-      continue;
-
-    user_action.AddInterceptor(base::BindOnce(&ScriptExecutor::OnChosen,
-                                              weak_ptr_factory_.GetWeakPtr()));
-  }
 
   if (delegate_->EnterState(AutofillAssistantState::PROMPT) &&
       touchable_element_area_) {
@@ -291,18 +282,24 @@
     // set.
     delegate_->SetTouchableElementArea(*touchable_element_area_);
 
-    // The touchable element and overlays are cleared in
+    // The touchable element and overlays are cleared by calling
     // ScriptExecutor::CleanUpAfterPrompt
   }
-  delegate_->SetUserActions(std::move(user_actions));
-}
 
-void ScriptExecutor::CancelPrompt() {
-  delegate_->SetUserActions(nullptr);
-  CleanUpAfterPrompt();
+  if (user_actions != nullptr) {
+    for (auto& user_action : *user_actions) {
+      if (!user_action.HasCallback())
+        continue;
+
+      user_action.AddInterceptor(base::BindOnce(
+          &ScriptExecutor::OnChosen, weak_ptr_factory_.GetWeakPtr()));
+    }
+    delegate_->SetUserActions(std::move(user_actions));
+  }
 }
 
 void ScriptExecutor::CleanUpAfterPrompt() {
+  delegate_->SetUserActions(nullptr);
   // Mark touchable_elements_ as consumed, so that it won't affect the next
   // prompt or the end of the script.
   touchable_element_area_.reset();
@@ -313,7 +310,6 @@
 
 void ScriptExecutor::OnChosen(UserAction::Callback callback,
                               std::unique_ptr<TriggerContext> context) {
-  CleanUpAfterPrompt();
   if (context->is_direct_action()) {
     current_action_data_.direct_action = true;
   }
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index f41f34a..ea70dd4 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -127,7 +127,7 @@
       base::OnceCallback<void(UserData*, UserData::FieldChange*)>) override;
   void GetFullCard(GetFullCardCallback callback) override;
   void Prompt(std::unique_ptr<std::vector<UserAction>> user_actions) override;
-  void CancelPrompt() override;
+  void CleanUpAfterPrompt() override;
   void FillAddressForm(
       const autofill::AutofillProfile* profile,
       const Selector& selector,
@@ -361,7 +361,6 @@
   void OnGetFullCard(GetFullCardCallback callback,
                      std::unique_ptr<autofill::CreditCard> card,
                      const base::string16& cvc);
-  void CleanUpAfterPrompt();
   void OnChosen(UserAction::Callback callback,
                 std::unique_ptr<TriggerContext> context);
 
diff --git a/components/download/internal/background_service/controller_impl.cc b/components/download/internal/background_service/controller_impl.cc
index d810612..5ae92cb 100644
--- a/components/download/internal/background_service/controller_impl.cc
+++ b/components/download/internal/background_service/controller_impl.cc
@@ -368,7 +368,6 @@
     DCHECK(client);
     bool client_ok =
         client->CanServiceRemoveDownloadedFile(entry->guid, mandatory_cleanup);
-    entry->cleanup_attempt_count++;
 
     if (client_ok || mandatory_cleanup) {
       entries_to_remove.push_back(entry);
diff --git a/components/download/internal/background_service/entry.cc b/components/download/internal/background_service/entry.cc
index 53caa48c..f893f9d 100644
--- a/components/download/internal/background_service/entry.cc
+++ b/components/download/internal/background_service/entry.cc
@@ -24,7 +24,6 @@
       bytes_uploaded(0u),
       attempt_count(0),
       resumption_count(0),
-      cleanup_attempt_count(0),
       has_upload_data(false),
       did_received_response(false),
       require_response_headers(true) {}
@@ -40,7 +39,6 @@
       bytes_uploaded(0u),
       attempt_count(0),
       resumption_count(0),
-      cleanup_attempt_count(0),
       has_upload_data(false),
       traffic_annotation(params.traffic_annotation),
       did_received_response(false),
@@ -68,7 +66,6 @@
          bytes_uploaded == other.bytes_uploaded &&
          attempt_count == other.attempt_count &&
          resumption_count == other.resumption_count &&
-         cleanup_attempt_count == other.cleanup_attempt_count &&
          has_upload_data == other.has_upload_data &&
          traffic_annotation == other.traffic_annotation &&
          url_chain == other.url_chain &&
diff --git a/components/download/internal/background_service/entry.h b/components/download/internal/background_service/entry.h
index dbe8406..409b110 100644
--- a/components/download/internal/background_service/entry.h
+++ b/components/download/internal/background_service/entry.h
@@ -102,9 +102,6 @@
   // Stores the number of resumptions for this download.
   uint32_t resumption_count;
 
-  // Stores the number of times the service tried to delete the download file.
-  uint32_t cleanup_attempt_count;
-
   // Stores whether this request has some data to be uploaded. This is set to
   // true only when the client has provided with the upload data and is not
   // cleared afterwards. Retry and resumption logic are impacted by this.
diff --git a/components/download/internal/background_service/file_monitor_impl.cc b/components/download/internal/background_service/file_monitor_impl.cc
index 7d976e1f..4cc40e2 100644
--- a/components/download/internal/background_service/file_monitor_impl.cc
+++ b/components/download/internal/background_service/file_monitor_impl.cc
@@ -185,8 +185,7 @@
 
     // TODO(xingliu): Consider logs life time after the file being deleted on
     // the file thread.
-    stats::LogFileLifeTime(base::Time::Now() - entry->completion_time,
-                           entry->cleanup_attempt_count);
+    stats::LogFileLifeTime(base::Time::Now() - entry->completion_time);
   }
 
   file_thread_task_runner_->PostTaskAndReply(
diff --git a/components/download/internal/background_service/proto/entry.proto b/components/download/internal/background_service/proto/entry.proto
index 6fd65c38..081b68a9 100644
--- a/components/download/internal/background_service/proto/entry.proto
+++ b/components/download/internal/background_service/proto/entry.proto
@@ -65,7 +65,6 @@
 
   // Uses internal time representation.
   optional int64 last_cleanup_check_time = 12;
-  optional uint32 cleanup_attempt_count = 13;
 
   optional uint32 resumption_count = 14;
   optional bool has_upload_data = 15;
diff --git a/components/download/internal/background_service/proto_conversions.cc b/components/download/internal/background_service/proto_conversions.cc
index 630abdf..4970848c 100644
--- a/components/download/internal/background_service/proto_conversions.cc
+++ b/components/download/internal/background_service/proto_conversions.cc
@@ -290,7 +290,6 @@
       base::Time::FromInternalValue(proto.last_cleanup_check_time());
   entry.attempt_count = proto.attempt_count();
   entry.resumption_count = proto.resumption_count();
-  entry.cleanup_attempt_count = proto.cleanup_attempt_count();
   entry.has_upload_data = proto.has_upload_data();
   entry.traffic_annotation =
       net::CreateMutableNetworkTrafficAnnotationTag(proto.traffic_annotation());
@@ -324,7 +323,6 @@
       entry.last_cleanup_check_time.ToInternalValue());
   proto.set_attempt_count(entry.attempt_count);
   proto.set_resumption_count(entry.resumption_count);
-  proto.set_cleanup_attempt_count(entry.cleanup_attempt_count);
   proto.set_has_upload_data(entry.has_upload_data);
   proto.set_traffic_annotation(entry.traffic_annotation.unique_id_hash_code);
   proto.set_bytes_downloaded(entry.bytes_downloaded);
diff --git a/components/download/internal/background_service/proto_conversions_unittest.cc b/components/download/internal/background_service/proto_conversions_unittest.cc
index 692389c..4307d0c 100644
--- a/components/download/internal/background_service/proto_conversions_unittest.cc
+++ b/components/download/internal/background_service/proto_conversions_unittest.cc
@@ -131,7 +131,7 @@
       SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE,
       SchedulingParams::Priority::HIGH, GURL(TEST_URL), "GET",
       Entry::State::ACTIVE, base::FilePath(FILE_PATH_LITERAL("/test/xyz")),
-      base::Time::Now(), base::Time::Now(), base::Time::Now(), 1024u, 3, 8, 5);
+      base::Time::Now(), base::Time::Now(), base::Time::Now(), 1024u, 3, 8);
   actual = EntryFromProto(EntryToProto(expected));
   EXPECT_TRUE(test::CompareEntry(&expected, &actual));
 }
@@ -148,7 +148,7 @@
       SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE,
       SchedulingParams::Priority::HIGH, GURL(TEST_URL), "GET",
       Entry::State::ACTIVE, base::FilePath(FILE_PATH_LITERAL("/test/xyz")),
-      base::Time::Now(), base::Time::Now(), base::Time::Now(), 1024u, 2, 8, 5));
+      base::Time::Now(), base::Time::Now(), base::Time::Now(), 1024u, 2, 8));
 
   auto actual = EntryVectorFromProto(
       EntryVectorToProto(std::make_unique<std::vector<Entry>>(expected)));
diff --git a/components/download/internal/background_service/stats.cc b/components/download/internal/background_service/stats.cc
index 3fd3716..2b901ac4 100644
--- a/components/download/internal/background_service/stats.cc
+++ b/components/download/internal/background_service/stats.cc
@@ -284,13 +284,10 @@
   base::UmaHistogramCounts100(name, external_cleanups);
 }
 
-void LogFileLifeTime(const base::TimeDelta& file_life_time,
-                     int num_cleanup_attempts) {
+void LogFileLifeTime(const base::TimeDelta& file_life_time) {
   UMA_HISTOGRAM_CUSTOM_TIMES("Download.Service.Files.LifeTime", file_life_time,
                              base::TimeDelta::FromSeconds(1),
                              base::TimeDelta::FromDays(8), 100);
-  base::UmaHistogramSparse("Download.Service.Files.Cleanup.Attempts",
-                           num_cleanup_attempts);
 }
 
 void LogFileDirDiskUtilization(int64_t total_disk_space,
diff --git a/components/download/internal/background_service/stats.h b/components/download/internal/background_service/stats.h
index 07cc041..97b2cb0 100644
--- a/components/download/internal/background_service/stats.h
+++ b/components/download/internal/background_service/stats.h
@@ -194,8 +194,7 @@
                           int external_cleanups);
 
 // Logs the file life time for successfully completed download.
-void LogFileLifeTime(const base::TimeDelta& file_life_time,
-                     int num_cleanup_attempts);
+void LogFileLifeTime(const base::TimeDelta& file_life_time);
 
 // Logs the total disk space utilized by download files.
 // This includes the total size of all the files in |file_dir|.
diff --git a/components/download/internal/background_service/test/entry_utils.cc b/components/download/internal/background_service/test/entry_utils.cc
index 2bfecd7..47b99ce 100644
--- a/components/download/internal/background_service/test/entry_utils.cc
+++ b/components/download/internal/background_service/test/entry_utils.cc
@@ -83,8 +83,7 @@
                  base::Time last_cleanup_check_time,
                  uint64_t bytes_downloaded,
                  int attempt_count,
-                 int resumption_count,
-                 int cleanup_attempt_count) {
+                 int resumption_count) {
   Entry entry = BuildEntry(client, guid);
   entry.scheduling_params.cancel_time = cancel_time;
   entry.scheduling_params.network_requirements = network_requirements;
@@ -100,7 +99,6 @@
   entry.bytes_downloaded = bytes_downloaded;
   entry.attempt_count = attempt_count;
   entry.resumption_count = resumption_count;
-  entry.cleanup_attempt_count = cleanup_attempt_count;
   entry.url_chain = {url, url};
   entry.response_headers = base::MakeRefCounted<const net::HttpResponseHeaders>(
       "HTTP/1.1 200 OK\nContent-type: text/html\n\n");
diff --git a/components/download/internal/background_service/test/entry_utils.h b/components/download/internal/background_service/test/entry_utils.h
index 48610ff..1f0cf494 100644
--- a/components/download/internal/background_service/test/entry_utils.h
+++ b/components/download/internal/background_service/test/entry_utils.h
@@ -48,8 +48,7 @@
                  base::Time last_cleanup_check_time,
                  uint64_t bytes_downloaded,
                  int attempt_count,
-                 int resumption_count,
-                 int cleanup_attempt_count);
+                 int resumption_count);
 
 }  // namespace test
 }  // namespace download
diff --git a/components/feed/core/proto/ui/action/ui_feed_action.proto b/components/feed/core/proto/ui/action/ui_feed_action.proto
index 9045a5f..681fbc2 100644
--- a/components/feed/core/proto/ui/action/ui_feed_action.proto
+++ b/components/feed/core/proto/ui/action/ui_feed_action.proto
@@ -30,7 +30,7 @@
 }
 
 // Metadata needed by the host to handle the action.
-// Next Id: 18
+// Next Id: 19
 message FeedActionMetadata {
   // The type of action, used by the host to perform any custom logic needed for
   // a specific type of action.
@@ -59,6 +59,7 @@
     NOT_INTERESTED_IN = 15;
     SEE_SUGGESTED_SITES = 16;
     SEND_FEEDBACK = 17;
+    MANAGE_INTERESTS = 18;
     reserved 9, 10;  // Deprecated
   }
   optional Type type = 1;
diff --git a/components/games/core/games_service_impl.cc b/components/games/core/games_service_impl.cc
index eb8a83da..6c8fe875 100644
--- a/components/games/core/games_service_impl.cc
+++ b/components/games/core/games_service_impl.cc
@@ -41,15 +41,14 @@
     return;
   }
 
-  auto cached_game = highlighted_games_store_->TryGetFromCache();
-  if (cached_game) {
-    std::move(callback).Run(ResponseCode::kSuccess,
-                            std::move(cached_game.value()));
+  highlighted_games_store_->SetPendingCallback(std::move(callback));
+
+  if (highlighted_games_store_->TryRespondFromCache()) {
+    // TODO(crbug.com/1018201): Remove return when we have other stores that
+    // don't have caching support; this is a temporary optimization.
     return;
   }
 
-  highlighted_games_store_->SetPendingCallback(std::move(callback));
-
   UpdateStores();
 }
 
diff --git a/components/games/core/games_service_impl_unittest.cc b/components/games/core/games_service_impl_unittest.cc
index 12563d5..6e0ccebc 100644
--- a/components/games/core/games_service_impl_unittest.cc
+++ b/components/games/core/games_service_impl_unittest.cc
@@ -10,7 +10,6 @@
 #include "base/bind.h"
 #include "base/files/file_path.h"
 #include "base/optional.h"
-#include "base/run_loop.h"
 #include "base/strings/string_piece.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/task_environment.h"
@@ -26,6 +25,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
+using ::testing::Return;
 
 namespace games {
 
@@ -53,14 +53,9 @@
     prefs::SetInstallDirPath(test_pref_service_.get(), fake_install_dir_);
   }
 
-  void SetHighlightedGamesStoreCacheWith(const Game& game) {
-    EXPECT_CALL(*mock_highlighted_games_store_, TryGetFromCache())
-        .WillOnce([&game]() { return base::Optional<Game>(game); });
-  }
-
-  void SetHighlightedGamesStoreCacheEmpty() {
-    EXPECT_CALL(*mock_highlighted_games_store_, TryGetFromCache())
-        .WillOnce([]() { return base::nullopt; });
+  void SetTryRespondFromCacheResponse(bool succeeds) {
+    EXPECT_CALL(*mock_highlighted_games_store_, TryRespondFromCache())
+        .WillOnce(Return(succeeds));
   }
 
   // TaskEnvironment is used instead of SingleThreadTaskEnvironment since we
@@ -77,38 +72,39 @@
 };
 
 TEST_F(GamesServiceImplTest, GetHighlightedGame_NotInstalled) {
-  base::RunLoop run_loop;
+  bool callback_invoked = false;
   games_service_->GetHighlightedGame(base::BindLambdaForTesting(
-      [&run_loop](ResponseCode code, const Game game) {
+      [&callback_invoked](ResponseCode code, const Game game) {
         EXPECT_EQ(ResponseCode::kFileNotFound, code);
         test::ExpectProtosEqual(Game(), game);
-        run_loop.Quit();
+        callback_invoked = true;
       }));
 
-  run_loop.Run();
+  EXPECT_TRUE(callback_invoked);
 }
 
 TEST_F(GamesServiceImplTest, GetHighlightedGame_RetrievesFromCache) {
   // Mock component to be installed.
   SetInstallDirPref();
 
-  Game fake_game = test::CreateGame();
-  SetHighlightedGamesStoreCacheWith(fake_game);
+  // Expect the UI callback to have been given to the highlighted games store.
+  EXPECT_CALL(*mock_highlighted_games_store_, SetPendingCallback(_)).Times(1);
 
-  base::RunLoop run_loop;
-  games_service_->GetHighlightedGame(base::BindLambdaForTesting(
-      [&fake_game, &run_loop](ResponseCode code, const Game game) {
-        EXPECT_EQ(ResponseCode::kSuccess, code);
-        test::ExpectProtosEqual(fake_game, game);
-        run_loop.Quit();
+  // Don't expect processing to be invoked as we'll have returned from cache.
+  EXPECT_CALL(*mock_catalog_store_, UpdateCatalogAsync(_, _)).Times(0);
+  EXPECT_CALL(*mock_highlighted_games_store_, ProcessAsync(_, _, _)).Times(0);
+
+  SetTryRespondFromCacheResponse(true);
+
+  games_service_->GetHighlightedGame(
+      base::BindLambdaForTesting([](ResponseCode code, const Game game) {
+        // No-op.
       }));
-
-  run_loop.Run();
 }
 
 TEST_F(GamesServiceImplTest, GetHighlightedGame_Success) {
   SetInstallDirPref();
-  SetHighlightedGamesStoreCacheEmpty();
+  SetTryRespondFromCacheResponse(false);
 
   // Expect the UI callback to have been given to the highlighted games store.
   EXPECT_CALL(*mock_highlighted_games_store_, SetPendingCallback(_)).Times(1);
@@ -143,25 +139,19 @@
         std::move(done_callback).Run();
       });
 
-  base::RunLoop run_loop;
-  // Upon full success, the cached catalog will get deleted.
-  EXPECT_CALL(*mock_catalog_store_, ClearCache()).WillOnce([&run_loop]() {
-    run_loop.Quit();
-  });
+  EXPECT_CALL(*mock_catalog_store_, ClearCache()).Times(1);
 
   games_service_->GetHighlightedGame(
       base::BindLambdaForTesting([](ResponseCode code, const Game game) {
         // No-op.
       }));
 
-  run_loop.Run();
-
   EXPECT_FALSE(games_service_->is_updating());
 }
 
 TEST_F(GamesServiceImplTest, GetHighlightedGame_CatalogFileNotFound) {
   SetInstallDirPref();
-  SetHighlightedGamesStoreCacheEmpty();
+  SetTryRespondFromCacheResponse(false);
 
   EXPECT_CALL(*mock_catalog_store_, UpdateCatalogAsync(fake_install_dir_, _))
       .WillOnce([](const base::FilePath& install_dir,
@@ -173,17 +163,14 @@
               HandleCatalogFailure(ResponseCode::kFileNotFound))
       .Times(1);
 
-  base::RunLoop run_loop;
-  EXPECT_CALL(*mock_catalog_store_, ClearCache()).WillOnce([&run_loop]() {
-    run_loop.Quit();
-  });
+  EXPECT_CALL(*mock_highlighted_games_store_, SetPendingCallback(_)).Times(1);
+
+  EXPECT_CALL(*mock_catalog_store_, ClearCache()).Times(1);
 
   games_service_->GetHighlightedGame(
       base::BindLambdaForTesting([](ResponseCode code, const Game game) {
         // No-op.
       }));
-
-  run_loop.Run();
 }
 
 }  // namespace games
diff --git a/components/games/core/highlighted_games_store.cc b/components/games/core/highlighted_games_store.cc
index f3c9f4a..c778b03 100644
--- a/components/games/core/highlighted_games_store.cc
+++ b/components/games/core/highlighted_games_store.cc
@@ -29,7 +29,7 @@
     : data_files_parser_(std::move(data_files_parser)),
       task_runner_(
           base::CreateSequencedTaskRunner({base::ThreadPool(), base::MayBlock(),
-                                           base::TaskPriority::USER_VISIBLE})),
+                                           base::TaskPriority::USER_BLOCKING})),
       clock_(clock) {}
 
 HighlightedGamesStore::~HighlightedGamesStore() = default;
@@ -37,6 +37,14 @@
 void HighlightedGamesStore::ProcessAsync(const base::FilePath& install_dir,
                                          const GamesCatalog& catalog,
                                          base::OnceClosure done_callback) {
+  // If cache is valid, we don't need to do extra processing.
+  auto cached_game = TryGetFromCache();
+  if (cached_game) {
+    RespondAndInvoke(ResponseCode::kSuccess, cached_game.value(),
+                     std::move(done_callback));
+    return;
+  }
+
   base::PostTaskAndReplyWithResult(
       task_runner_.get(), FROM_HERE,
       base::BindOnce(&HighlightedGamesStore::GetHighlightedGamesResponse,
@@ -46,6 +54,20 @@
                      catalog));
 }
 
+bool HighlightedGamesStore::TryRespondFromCache() {
+  if (!pending_callback_) {
+    return false;
+  }
+
+  auto cached_game = TryGetFromCache();
+  if (!cached_game) {
+    return false;
+  }
+
+  Respond(ResponseCode::kSuccess, cached_game.value());
+  return true;
+}
+
 base::Optional<Game> HighlightedGamesStore::TryGetFromCache() {
   base::Optional<Game> optional_game;
 
diff --git a/components/games/core/highlighted_games_store.h b/components/games/core/highlighted_games_store.h
index f266aeb..eec2f2d 100644
--- a/components/games/core/highlighted_games_store.h
+++ b/components/games/core/highlighted_games_store.h
@@ -42,6 +42,10 @@
                             const GamesCatalog& catalog,
                             base::OnceClosure done_callback);
 
+  // Allows a caller to make the store reply to the pending callback if it's
+  // already caching a valid highlighted game.
+  virtual bool TryRespondFromCache();
+
   // Allows a callee to verify if the store already has the currently
   // highlighted game cache.
   virtual base::Optional<Game> TryGetFromCache();
diff --git a/components/games/core/highlighted_games_store_unittest.cc b/components/games/core/highlighted_games_store_unittest.cc
index 7e4e1fd..b9d8164 100644
--- a/components/games/core/highlighted_games_store_unittest.cc
+++ b/components/games/core/highlighted_games_store_unittest.cc
@@ -90,6 +90,28 @@
     AssertCacheEmpty();
   }
 
+  Game PopulateCache() {
+    // Get a game to be cached.
+    GamesCatalog fake_catalog = test::CreateCatalogWithTwoGames();
+    Game fake_selected_game = fake_catalog.games().at(1);
+
+    HighlightedGamesResponse fake_response;
+    AddValidHighlightedGame(&fake_response, fake_selected_game.id());
+
+    EXPECT_CALL(*mock_parser_, TryParseHighlightedGames(fake_install_dir_))
+        .WillOnce([&fake_response](const base::FilePath& install_dir) {
+          return base::Optional<HighlightedGamesResponse>(fake_response);
+        });
+
+    base::RunLoop run_loop;
+    highlighted_games_store_->ProcessAsync(
+        fake_install_dir_, fake_catalog,
+        base::BindLambdaForTesting([&run_loop]() { run_loop.Quit(); }));
+
+    run_loop.Run();
+    return fake_selected_game;
+  }
+
   // TaskEnvironment is used instead of SingleThreadTaskEnvironment since we
   // post a task to the thread pool.
   base::test::TaskEnvironment task_environment_{
@@ -148,7 +170,7 @@
   AssertCacheEmpty();
 }
 
-TEST_F(HighlightedGamesStoreTest, ProcessAsync_InvalidData) {
+TEST_F(HighlightedGamesStoreTest, ProcessAsync_EmptyCatalog) {
   GamesCatalog empty_catalog;
   base::RunLoop run_loop;
 
@@ -157,6 +179,12 @@
   auto barrier_closure = base::BarrierClosure(
       2, base::BindLambdaForTesting([&run_loop]() { run_loop.Quit(); }));
 
+  EXPECT_CALL(*mock_parser_, TryParseHighlightedGames(fake_install_dir_))
+      .WillOnce([](const base::FilePath& install_dir) {
+        return base::Optional<HighlightedGamesResponse>(
+            HighlightedGamesResponse());
+      });
+
   highlighted_games_store_->SetPendingCallback(base::BindLambdaForTesting(
       [&barrier_closure](ResponseCode code, const Game game) {
         test::ExpectProtosEqual(Game(), game);
@@ -267,4 +295,38 @@
   highlighted_games_store_->HandleCatalogFailure(ResponseCode::kMissingCatalog);
 }
 
+TEST_F(HighlightedGamesStoreTest, TryRespondFromCache_NoCallbackNoCache) {
+  EXPECT_FALSE(highlighted_games_store_->TryRespondFromCache());
+}
+
+TEST_F(HighlightedGamesStoreTest, TryRespondFromCache_NoCache) {
+  highlighted_games_store_->SetPendingCallback(
+      base::BindLambdaForTesting([](ResponseCode code, const Game game) {
+        // Callback should not be invoked where there's no cached game.
+        FAIL();
+      }));
+  EXPECT_FALSE(highlighted_games_store_->TryRespondFromCache());
+}
+
+TEST_F(HighlightedGamesStoreTest, TryRespondFromCache_NoCallback) {
+  PopulateCache();
+  EXPECT_FALSE(highlighted_games_store_->TryRespondFromCache());
+}
+
+TEST_F(HighlightedGamesStoreTest,
+       TryRespondFromCache_CallbackAndCache_Success) {
+  Game expected_game = PopulateCache();
+
+  base::RunLoop run_loop;
+  highlighted_games_store_->SetPendingCallback(base::BindLambdaForTesting(
+      [&expected_game, &run_loop](ResponseCode code, const Game game) {
+        test::ExpectProtosEqual(expected_game, game);
+        EXPECT_EQ(ResponseCode::kSuccess, code);
+        run_loop.Quit();
+      }));
+
+  EXPECT_TRUE(highlighted_games_store_->TryRespondFromCache());
+  run_loop.Run();
+}
+
 }  // namespace games
diff --git a/components/games/core/test/mocks.h b/components/games/core/test/mocks.h
index b4e2070e..9aafd62 100644
--- a/components/games/core/test/mocks.h
+++ b/components/games/core/test/mocks.h
@@ -55,6 +55,7 @@
                     const GamesCatalog&,
                     base::OnceClosure));
   MOCK_METHOD0(TryGetFromCache, base::Optional<Game>());
+  MOCK_METHOD0(TryRespondFromCache, bool());
   MOCK_METHOD1(SetPendingCallback, void(HighlightedGameCallback));
   MOCK_METHOD1(HandleCatalogFailure, void(ResponseCode));
 };
diff --git a/components/optimization_guide/optimization_guide_store.cc b/components/optimization_guide/optimization_guide_store.cc
index 85dccb6..8399f84 100644
--- a/components/optimization_guide/optimization_guide_store.cc
+++ b/components/optimization_guide/optimization_guide_store.cc
@@ -769,9 +769,8 @@
   // there's an in-flight component data update, which means the keys are
   // about to be invalidated, then the loaded keys should not be considered
   // valid. Reset the keys so that they are cleared.
-  if (!IsAvailable() || data_update_in_flight_) {
+  if (!IsAvailable() || data_update_in_flight_)
     hint_entry_keys.reset();
-  }
 
   entry_keys_ = std::move(hint_entry_keys);
   std::move(callback).Run();
diff --git a/components/optimization_guide/proto/hints.proto b/components/optimization_guide/proto/hints.proto
index 4636fda..63d64e3 100644
--- a/components/optimization_guide/proto/hints.proto
+++ b/components/optimization_guide/proto/hints.proto
@@ -73,7 +73,7 @@
 
   // The maximum duration in which the hints provided in this response should
   // be retained in the client cache.
-  optional Duration max_cache_duration = 2;
+  optional Duration max_cache_duration = 2 [deprecated = true];
 
   // A set of hint keys to remove from the client cache.
   //
diff --git a/components/password_manager/core/browser/compromised_credentials_observer.cc b/components/password_manager/core/browser/compromised_credentials_observer.cc
index 13d1c43..663c0c9 100644
--- a/components/password_manager/core/browser/compromised_credentials_observer.cc
+++ b/components/password_manager/core/browser/compromised_credentials_observer.cc
@@ -49,9 +49,8 @@
       changes.size() != 1 || changes[0].type() == PasswordStoreChange::UPDATE
           ? RemoveCompromisedCredentialsReason::kUpdate
           : RemoveCompromisedCredentialsReason::kRemove;
-  store_->RemoveCompromisedCredentials(GURL(changes[0].form().signon_realm),
-                                       changes[0].form().username_value,
-                                       reason);
+  store_->RemoveCompromisedCredentials(
+      changes[0].form().signon_realm, changes[0].form().username_value, reason);
   UMA_HISTOGRAM_ENUMERATION("PasswordManager.RemoveCompromisedCredentials",
                             changes[0].type());
 }
diff --git a/components/password_manager/core/browser/compromised_credentials_table.cc b/components/password_manager/core/browser/compromised_credentials_table.cc
index e0432a41..8a5b0b60 100644
--- a/components/password_manager/core/browser/compromised_credentials_table.cc
+++ b/components/password_manager/core/browser/compromised_credentials_table.cc
@@ -19,7 +19,7 @@
 // Represents columns of the compromised credentials table. Used with SQL
 // queries that use all the columns.
 enum class CompromisedCredentialsTableColumn {
-  kUrl,
+  kSignonRealm,
   kUsername,
   kCreateTime,
   kCompromiseType,
@@ -48,8 +48,8 @@
     sql::Statement* s) {
   std::vector<CompromisedCredentials> results;
   while (s->Step()) {
-    GURL url(s->ColumnString(
-        GetColumnNumber(CompromisedCredentialsTableColumn::kUrl)));
+    std::string signon_realm(s->ColumnString(
+        GetColumnNumber(CompromisedCredentialsTableColumn::kSignonRealm)));
     base::string16 username = s->ColumnString16(
         GetColumnNumber(CompromisedCredentialsTableColumn::kUsername));
     base::Time create_time = base::Time::FromDeltaSinceWindowsEpoch(
@@ -57,8 +57,9 @@
             GetColumnNumber(CompromisedCredentialsTableColumn::kCreateTime)))));
     CompromiseType compromise_type = static_cast<CompromiseType>(s->ColumnInt64(
         GetColumnNumber(CompromisedCredentialsTableColumn::kCompromiseType)));
-    results.push_back(CompromisedCredentials(
-        std::move(url), std::move(username), create_time, compromise_type));
+    results.push_back(CompromisedCredentials(std::move(signon_realm),
+                                             std::move(username), create_time,
+                                             compromise_type));
   }
   return results;
 }
@@ -67,7 +68,7 @@
 
 bool operator==(const CompromisedCredentials& lhs,
                 const CompromisedCredentials& rhs) {
-  return lhs.url == rhs.url && lhs.username == rhs.username &&
+  return lhs.signon_realm == rhs.signon_realm && lhs.username == rhs.username &&
          lhs.create_time == rhs.create_time &&
          lhs.compromise_type == rhs.compromise_type;
 }
@@ -95,7 +96,7 @@
 
 bool CompromisedCredentialsTable::AddRow(
     const CompromisedCredentials& compromised_credentials) {
-  if (!db_ || !compromised_credentials.url.is_valid())
+  if (!db_ || compromised_credentials.signon_realm.empty())
     return false;
 
   DCHECK(db_->DoesTableExist(kCompromisedCredentialsTableName));
@@ -108,8 +109,8 @@
                               "INSERT OR IGNORE INTO compromised_credentials "
                               "(url, username, create_time, compromise_type) "
                               "VALUES (?, ?, ?, ?)"));
-  s.BindString(GetColumnNumber(CompromisedCredentialsTableColumn::kUrl),
-               compromised_credentials.url.spec());
+  s.BindString(GetColumnNumber(CompromisedCredentialsTableColumn::kSignonRealm),
+               compromised_credentials.signon_realm);
   s.BindString16(GetColumnNumber(CompromisedCredentialsTableColumn::kUsername),
                  compromised_credentials.username);
   s.BindInt64(GetColumnNumber(CompromisedCredentialsTableColumn::kCreateTime),
@@ -122,9 +123,9 @@
 }
 
 std::vector<CompromisedCredentials> CompromisedCredentialsTable::GetRows(
-    const GURL& url,
+    const std::string& signon_realm,
     const base::string16& username) const {
-  if (!db_ || !url.is_valid())
+  if (!db_ || signon_realm.empty())
     return std::vector<CompromisedCredentials>{};
 
   DCHECK(db_->DoesTableExist(kCompromisedCredentialsTableName));
@@ -133,24 +134,24 @@
       SQL_FROM_HERE,
       "SELECT url, username, create_time, compromise_type FROM "
       "compromised_credentials WHERE url = ? AND username = ? "));
-  s.BindString(0, url.spec());
+  s.BindString(0, signon_realm);
   s.BindString16(1, username);
   return StatementToCompromisedCredentials(&s);
 }
 
 bool CompromisedCredentialsTable::UpdateRow(
-    const GURL& new_url,
+    const std::string& new_signon_realm,
     const base::string16& new_username,
-    const GURL& old_url,
+    const std::string& old_signon_realm,
     const base::string16& old_username) const {
-  if (!db_ || !new_url.is_valid() || !old_url.is_valid())
+  if (!db_ || new_signon_realm.empty() || old_signon_realm.empty())
     return false;
 
   DCHECK(db_->DoesTableExist(kCompromisedCredentialsTableName));
 
   // Retrieve the rows that are to be updated to log.
   const std::vector<CompromisedCredentials> compromised_credentials =
-      GetRows(old_url, old_username);
+      GetRows(old_signon_realm, old_username);
   if (compromised_credentials.empty())
     return false;
   for (const auto& compromised_credential : compromised_credentials) {
@@ -162,25 +163,25 @@
                                            "UPDATE compromised_credentials "
                                            "SET url = ?, username = ? "
                                            "WHERE url = ? and username = ?"));
-  s.BindString(0, new_url.spec());
+  s.BindString(0, new_signon_realm);
   s.BindString16(1, new_username);
-  s.BindString(2, old_url.spec());
+  s.BindString(2, old_signon_realm);
   s.BindString16(3, old_username);
   return s.Run();
 }
 
 bool CompromisedCredentialsTable::RemoveRow(
-    const GURL& url,
+    const std::string& signon_realm,
     const base::string16& username,
     RemoveCompromisedCredentialsReason reason) {
-  if (!db_ || !url.is_valid())
+  if (!db_ || signon_realm.empty())
     return false;
 
   DCHECK(db_->DoesTableExist(kCompromisedCredentialsTableName));
 
   // Retrieve the rows that are to be removed to log.
   const std::vector<CompromisedCredentials> compromised_credentials =
-      GetRows(url, username);
+      GetRows(signon_realm, username);
   if (compromised_credentials.empty())
     return false;
   for (const auto& compromised_credential : compromised_credentials) {
@@ -190,10 +191,11 @@
         "PasswordManager.RemoveCompromisedCredentials.RemoveReason", reason);
   }
 
-  sql::Statement s(db_->GetCachedStatement(
-      SQL_FROM_HERE,
-      "DELETE FROM compromised_credentials WHERE url = ? AND username = ? "));
-  s.BindString(0, url.spec());
+  sql::Statement s(
+      db_->GetCachedStatement(SQL_FROM_HERE,
+                              "DELETE FROM compromised_credentials WHERE "
+                              "url = ? AND username = ? "));
+  s.BindString(0, signon_realm);
   s.BindString16(1, username);
   return s.Run();
 }
@@ -223,7 +225,7 @@
     return s.Run();
   }
 
-  // Otherwise, filter urls.
+  // Otherwise, filter signon_realms.
   sql::Statement s(db_->GetCachedStatement(
       SQL_FROM_HERE,
       "SELECT DISTINCT url FROM compromised_credentials WHERE "
@@ -231,21 +233,21 @@
   s.BindInt64(0, remove_begin_us);
   s.BindInt64(1, remove_end_us);
 
-  std::vector<std::string> urls;
+  std::vector<std::string> signon_realms;
   while (s.Step()) {
-    std::string url = s.ColumnString(0);
-    if (url_filter.Run(GURL(url))) {
-      urls.push_back(std::move(url));
+    std::string signon_realm = s.ColumnString(0);
+    if (url_filter.Run(GURL(signon_realm))) {
+      signon_realms.push_back(std::move(signon_realm));
     }
   }
 
   bool success = true;
-  for (const std::string& url : urls) {
+  for (const std::string& signon_realm : signon_realms) {
     sql::Statement s(db_->GetCachedStatement(
         SQL_FROM_HERE,
         "DELETE FROM compromised_credentials WHERE url = ? "
         "AND create_time >= ? AND create_time < ?"));
-    s.BindString(0, url);
+    s.BindString(0, signon_realm);
     s.BindInt64(1, remove_begin_us);
     s.BindInt64(2, remove_end_us);
     success = success && s.Run();
diff --git a/components/password_manager/core/browser/compromised_credentials_table.h b/components/password_manager/core/browser/compromised_credentials_table.h
index 5b176362..411a7ba 100644
--- a/components/password_manager/core/browser/compromised_credentials_table.h
+++ b/components/password_manager/core/browser/compromised_credentials_table.h
@@ -33,17 +33,17 @@
 
 // Represents information about the particular compromised credentials.
 struct CompromisedCredentials {
-  CompromisedCredentials(GURL url,
+  CompromisedCredentials(std::string signon_realm,
                          base::string16 username,
                          base::Time create_time,
                          CompromiseType compromise_type)
-      : url(std::move(url)),
+      : signon_realm(std::move(signon_realm)),
         username(std::move(username)),
         create_time(create_time),
         compromise_type(compromise_type) {}
 
-  // The url of the website where the credentials were compromised.
-  GURL url;
+  // The signon_realm of the website where the credentials were compromised.
+  std::string signon_realm;
   // The value of the compromised username.
   base::string16 username;
   // The date when the record was created.
@@ -71,31 +71,32 @@
   // completed successfully.
   bool AddRow(const CompromisedCredentials& compromised_credentials);
 
-  // Updates the row that has |old_url| and |old_username| with |new_url| and
-  // |new_username|. If the row does not exist, the method will not do anything.
-  // Returns true if the SQL completed successfully.
-  bool UpdateRow(const GURL& new_url,
+  // Updates the row that has |old_signon_realm| and |old_username| with
+  // |new_signon_realm| and |new_username|. If the row does not exist, the
+  // method will not do anything. Returns true if the SQL completed
+  // successfully.
+  bool UpdateRow(const std::string& new_signon_realm,
                  const base::string16& new_username,
-                 const GURL& old_url,
+                 const std::string& old_signon_realm,
                  const base::string16& old_username) const;
 
   // Removes information about the credentials compromised for |username| on
-  // |url|. |reason| is the reason why the credentials is removed from
+  // |signon_realm|. |reason| is the reason why the credentials is removed from
   // the table. Returns true if the SQL completed successfully.
   // Also logs the compromise type in UMA.
-  bool RemoveRow(const GURL& url,
+  bool RemoveRow(const std::string& signon_realm,
                  const base::string16& username,
                  RemoveCompromisedCredentialsReason reason);
 
-  // Gets all the rows in the database for the |username| and |url|.
+  // Gets all the rows in the database for the |username| and |signon_realm|.
   std::vector<CompromisedCredentials> GetRows(
-      const GURL& url,
+      const std::string& signon_realm,
       const base::string16& username) const;
 
   // Removes all compromised credentials created between |remove_begin|
   // inclusive and |remove_end| exclusive. If |url_filter| is not null, only
-  // compromised credentials for matching urls are removed. Returns true if the
-  // SQL completed successfully.
+  // compromised credentials for matching signon_realms are removed. Returns
+  // true if the SQL completed successfully.
   bool RemoveRowsByUrlAndTime(
       const base::RepeatingCallback<bool(const GURL&)>& url_filter,
       base::Time remove_begin,
diff --git a/components/password_manager/core/browser/compromised_credentials_table_unittest.cc b/components/password_manager/core/browser/compromised_credentials_table_unittest.cc
index 9ac49403..12fdc1c 100644
--- a/components/password_manager/core/browser/compromised_credentials_table_unittest.cc
+++ b/components/password_manager/core/browser/compromised_credentials_table_unittest.cc
@@ -20,9 +20,9 @@
 namespace password_manager {
 namespace {
 
-const char kTestDomain[] = "http://example.com";
-const char kTestDomain2[] = "http://test.com";
-const char kTestDomain3[] = "http://google.com";
+const char kTestDomain[] = "http://example.com/";
+const char kTestDomain2[] = "http://test.com/";
+const char kTestDomain3[] = "http://google.com/";
 const char kUsername[] = "user";
 const char kUsername2[] = "user2";
 const char kUsername3[] = "user3";
@@ -52,12 +52,12 @@
   void CheckDatabaseAccessibility() {
     EXPECT_TRUE(db()->AddRow(test_data()));
     EXPECT_THAT(db()->GetAllRows(), ElementsAre(test_data()));
-    EXPECT_THAT(db()->GetRows(test_data().url, test_data().username),
+    EXPECT_THAT(db()->GetRows(test_data().signon_realm, test_data().username),
                 ElementsAre(test_data()));
-    EXPECT_TRUE(db()->RemoveRow(test_data().url, test_data().username,
+    EXPECT_TRUE(db()->RemoveRow(test_data().signon_realm, test_data().username,
                                 RemoveCompromisedCredentialsReason::kRemove));
     EXPECT_THAT(db()->GetAllRows(), IsEmpty());
-    EXPECT_THAT(db()->GetRows(test_data().url, test_data().username),
+    EXPECT_THAT(db()->GetRows(test_data().signon_realm, test_data().username),
                 IsEmpty());
   }
 
@@ -70,9 +70,9 @@
   base::ScopedTempDir temp_dir_;
   std::unique_ptr<sql::Database> connection_;
   std::unique_ptr<CompromisedCredentialsTable> db_;
-  CompromisedCredentials test_data_{
-      GURL(kTestDomain), base::ASCIIToUTF16(kUsername),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked};
+  CompromisedCredentials test_data_{kTestDomain, base::ASCIIToUTF16(kUsername),
+                                    base::Time::FromTimeT(1),
+                                    CompromiseType::kLeaked};
 };
 
 TEST_F(CompromisedCredentialsTableTest, Reload) {
@@ -104,14 +104,14 @@
   feature_list_.InitWithFeatures({},
                                  {password_manager::features::kLeakHistory});
   EXPECT_THAT(db()->GetAllRows(), IsEmpty());
-  EXPECT_THAT(db()->GetRows(test_data().url, test_data().username),
+  EXPECT_THAT(db()->GetRows(test_data().signon_realm, test_data().username),
               ElementsAre());
-  EXPECT_FALSE(db()->RemoveRow(test_data().url, test_data().username,
+  EXPECT_FALSE(db()->RemoveRow(test_data().signon_realm, test_data().username,
                                RemoveCompromisedCredentialsReason::kRemove));
   EXPECT_TRUE(db()->AddRow(test_data()));
 }
 
-TEST_F(CompromisedCredentialsTableTest, SameUrlDifferentUsername) {
+TEST_F(CompromisedCredentialsTableTest, SameSignonRealmDifferentUsername) {
   CompromisedCredentials compromised_credentials1 = test_data();
   CompromisedCredentials compromised_credentials2 = test_data();
   compromised_credentials2.username = base::ASCIIToUTF16(kUsername2);
@@ -122,10 +122,10 @@
               ElementsAre(compromised_credentials1, compromised_credentials2));
 }
 
-TEST_F(CompromisedCredentialsTableTest, SameUsernameDifferentUrl) {
+TEST_F(CompromisedCredentialsTableTest, SameUsernameDifferentSignonRealm) {
   CompromisedCredentials compromised_credentials1 = test_data();
   CompromisedCredentials compromised_credentials2 = test_data();
-  compromised_credentials2.url = GURL(kTestDomain2);
+  compromised_credentials2.signon_realm = kTestDomain2;
 
   EXPECT_TRUE(db()->AddRow(compromised_credentials1));
   EXPECT_TRUE(db()->AddRow(compromised_credentials2));
@@ -133,7 +133,8 @@
               ElementsAre(compromised_credentials1, compromised_credentials2));
 }
 
-TEST_F(CompromisedCredentialsTableTest, SameUrlAndUsernameDifferentTime) {
+TEST_F(CompromisedCredentialsTableTest,
+       SameSignonRealmAndUsernameDifferentTime) {
   CompromisedCredentials compromised_credentials1 = test_data();
   CompromisedCredentials compromised_credentials2 = test_data();
   compromised_credentials2.create_time = base::Time::FromTimeT(2);
@@ -147,7 +148,7 @@
 }
 
 TEST_F(CompromisedCredentialsTableTest,
-       SameUrlAndUsernameAndDifferentCompromiseType) {
+       SameSignonRealmAndUsernameAndDifferentCompromiseType) {
   CompromisedCredentials compromised_credentials1 = test_data();
   CompromisedCredentials compromised_credentials2 = test_data();
   compromised_credentials2.compromise_type = CompromiseType::kPhished;
@@ -159,7 +160,7 @@
 }
 
 TEST_F(CompromisedCredentialsTableTest,
-       SameUsernameAndUrlAndDifferentCompromiseType) {
+       SameUsernameAndSignonRealmAndDifferentCompromiseType) {
   CompromisedCredentials compromised_credentials1 = test_data();
   CompromisedCredentials compromised_credentials2 = test_data();
   compromised_credentials2.compromise_type = CompromiseType::kPhished;
@@ -173,13 +174,13 @@
 TEST_F(CompromisedCredentialsTableTest, UpdateRow) {
   CompromisedCredentials compromised_credentials1 = test_data();
   CompromisedCredentials compromised_credentials2{
-      GURL(kTestDomain2), base::ASCIIToUTF16(kUsername2),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked};
+      kTestDomain2, base::ASCIIToUTF16(kUsername2), base::Time::FromTimeT(1),
+      CompromiseType::kLeaked};
 
   EXPECT_TRUE(db()->AddRow(compromised_credentials1));
-  EXPECT_TRUE(db()->UpdateRow(
-      GURL(kTestDomain2), base::ASCIIToUTF16(kUsername2),
-      compromised_credentials1.url, compromised_credentials1.username));
+  EXPECT_TRUE(db()->UpdateRow(kTestDomain2, base::ASCIIToUTF16(kUsername2),
+                              compromised_credentials1.signon_realm,
+                              compromised_credentials1.username));
   EXPECT_THAT(db()->GetAllRows(), ElementsAre(compromised_credentials2));
 }
 
@@ -262,8 +263,8 @@
   CompromisedCredentials compromised_credentials3 = test_data();
   CompromisedCredentials compromised_credentials4 = test_data();
   compromised_credentials2.username = base::ASCIIToUTF16(kUsername2);
-  compromised_credentials3.url = GURL(kTestDomain2);
-  compromised_credentials4.url = GURL(kTestDomain3);
+  compromised_credentials3.signon_realm = kTestDomain2;
+  compromised_credentials4.signon_realm = kTestDomain3;
 
   EXPECT_TRUE(db()->AddRow(compromised_credentials1));
   EXPECT_TRUE(db()->AddRow(compromised_credentials2));
@@ -276,27 +277,19 @@
 
   EXPECT_TRUE(db()->RemoveRowsByUrlAndTime(
       base::BindRepeating(std::not_equal_to<GURL>(),
-                          compromised_credentials1.url),
+                          GURL(compromised_credentials1.signon_realm)),
       base::Time(), base::Time::Max()));
   // With unbounded time range and given url filter all rows that are not
-  // matching the |compromised_credentials1.url| should be removed.
+  // matching the |compromised_credentials1.signon_realm| should be removed.
   EXPECT_THAT(db()->GetAllRows(),
               ElementsAre(compromised_credentials1, compromised_credentials2));
 }
 
-TEST_F(CompromisedCredentialsTableTest, BadURL) {
-  test_data().url = GURL("bad");
+TEST_F(CompromisedCredentialsTableTest, EmptySignonRealm) {
+  test_data().signon_realm = "";
   EXPECT_FALSE(db()->AddRow(test_data()));
   EXPECT_THAT(db()->GetAllRows(), IsEmpty());
-  EXPECT_FALSE(db()->RemoveRow(test_data().url, test_data().username,
-                               RemoveCompromisedCredentialsReason::kRemove));
-}
-
-TEST_F(CompromisedCredentialsTableTest, EmptyURL) {
-  test_data().url = GURL();
-  EXPECT_FALSE(db()->AddRow(test_data()));
-  EXPECT_THAT(db()->GetAllRows(), IsEmpty());
-  EXPECT_FALSE(db()->RemoveRow(test_data().url, test_data().username,
+  EXPECT_FALSE(db()->RemoveRow(test_data().signon_realm, test_data().username,
                                RemoveCompromisedCredentialsReason::kRemove));
 }
 
diff --git a/components/password_manager/core/browser/leak_detection_delegate.cc b/components/password_manager/core/browser/leak_detection_delegate.cc
index 0e71e4d6..bda411a 100644
--- a/components/password_manager/core/browser/leak_detection_delegate.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate.cc
@@ -9,6 +9,7 @@
 #include "components/autofill/core/common/password_form.h"
 #include "components/autofill/core/common/save_password_progress_logger.h"
 #include "components/password_manager/core/browser/compromised_credentials_table.h"
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
 #include "components/password_manager/core/browser/leak_detection/leak_detection_check.h"
 #include "components/password_manager/core/browser/leak_detection/leak_detection_check_factory_impl.h"
 #include "components/password_manager/core/browser/leak_detection_delegate_helper.h"
@@ -81,11 +82,11 @@
 
   password_manager::PasswordStore* password_store =
       client_->GetProfilePasswordStore();
-  if (base::FeatureList::IsEnabled(password_manager::features::kLeakHistory)) {
-    if (is_leaked) {
-      password_store->AddCompromisedCredentials(CompromisedCredentials(
-          url, username, base::Time::Now(), CompromiseType::kLeaked));
-    }
+  if (base::FeatureList::IsEnabled(password_manager::features::kLeakHistory) &&
+      is_leaked) {
+    password_store->AddCompromisedCredentials(
+        CompromisedCredentials(GetSignonRealm(url), username, base::Time::Now(),
+                               CompromiseType::kLeaked));
   }
 
   if (is_leaked) {
diff --git a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
index c692812..9854a3d 100644
--- a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
 #include "components/password_manager/core/browser/leak_detection/leak_detection_check.h"
 #include "components/password_manager/core/browser/leak_detection_delegate.h"
 #include "components/password_manager/core/browser/mock_password_store.h"
@@ -218,7 +219,7 @@
       form.password_value);
 
   const CompromisedCredentials compromised_credentials(
-      form.origin, form.username_value, base::Time::Now(),
+      GetSignonRealm(form.origin), form.username_value, base::Time::Now(),
       CompromiseType::kLeaked);
   EXPECT_CALL(*store(), AddCompromisedCredentialsImpl(compromised_credentials));
   WaitForPasswordStore();
diff --git a/components/password_manager/core/browser/mock_password_store.h b/components/password_manager/core/browser/mock_password_store.h
index b22ace0..165ea34a 100644
--- a/components/password_manager/core/browser/mock_password_store.h
+++ b/components/password_manager/core/browser/mock_password_store.h
@@ -76,7 +76,7 @@
   MOCK_METHOD1(AddCompromisedCredentialsImpl,
                void(const CompromisedCredentials&));
   MOCK_METHOD3(RemoveCompromisedCredentialsImpl,
-               void(const GURL&,
+               void(const std::string&,
                     const base::string16&,
                     RemoveCompromisedCredentialsReason));
   MOCK_METHOD0(GetAllCompromisedCredentialsImpl,
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 96163927..8e62456c 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -378,12 +378,12 @@
 }
 
 void PasswordStore::RemoveCompromisedCredentials(
-    const GURL& url,
+    const std::string& signon_realm,
     const base::string16& username,
     RemoveCompromisedCredentialsReason reason) {
   DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
   ScheduleTask(base::BindOnce(&PasswordStore::RemoveCompromisedCredentialsImpl,
-                              this, url, username, reason));
+                              this, signon_realm, username, reason));
 }
 
 void PasswordStore::GetAllCompromisedCredentials(
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index 1021091..e1bb4a6 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -251,8 +251,9 @@
   void AddCompromisedCredentials(
       const CompromisedCredentials& compromised_credentials);
 
-  // Removes information about credentials compromised on |url| for |username|.
-  void RemoveCompromisedCredentials(const GURL& url,
+  // Removes information about credentials compromised on |signon_realm| for
+  // |username|.
+  void RemoveCompromisedCredentials(const std::string& signon_realm,
                                     const base::string16& username,
                                     RemoveCompromisedCredentialsReason reason);
 
@@ -488,9 +489,8 @@
   // compromised credentials.
   virtual void AddCompromisedCredentialsImpl(
       const CompromisedCredentials& compromised_credentials) = 0;
-  // TODO(bdea): Add CompromiseType as a filter.
   virtual void RemoveCompromisedCredentialsImpl(
-      const GURL& url,
+      const std::string& signon_realm,
       const base::string16& username,
       RemoveCompromisedCredentialsReason reason) = 0;
   virtual std::vector<CompromisedCredentials>
diff --git a/components/password_manager/core/browser/password_store_default.cc b/components/password_manager/core/browser/password_store_default.cc
index d605be1..70186a45 100644
--- a/components/password_manager/core/browser/password_store_default.cc
+++ b/components/password_manager/core/browser/password_store_default.cc
@@ -230,12 +230,13 @@
 }
 
 void PasswordStoreDefault::RemoveCompromisedCredentialsImpl(
-    const GURL& url,
+    const std::string& signon_realm,
     const base::string16& username,
     RemoveCompromisedCredentialsReason reason) {
   DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
   if (login_db_) {
-    login_db_->compromised_credentials_table().RemoveRow(url, username, reason);
+    login_db_->compromised_credentials_table().RemoveRow(signon_realm, username,
+                                                         reason);
   }
 }
 
diff --git a/components/password_manager/core/browser/password_store_default.h b/components/password_manager/core/browser/password_store_default.h
index 61157dc4..ac880e2 100644
--- a/components/password_manager/core/browser/password_store_default.h
+++ b/components/password_manager/core/browser/password_store_default.h
@@ -76,7 +76,7 @@
   void AddCompromisedCredentialsImpl(
       const CompromisedCredentials& compromised_credentials) override;
   void RemoveCompromisedCredentialsImpl(
-      const GURL& url,
+      const std::string& signon_realm,
       const base::string16& username,
       RemoveCompromisedCredentialsReason reason) override;
   std::vector<CompromisedCredentials> GetAllCompromisedCredentialsImpl()
diff --git a/components/password_manager/core/browser/password_store_unittest.cc b/components/password_manager/core/browser/password_store_unittest.cc
index a2dc541..f31d7a6 100644
--- a/components/password_manager/core/browser/password_store_unittest.cc
+++ b/components/password_manager/core/browser/password_store_unittest.cc
@@ -22,6 +22,7 @@
 #include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
 #include "components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h"
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
 #include "components/password_manager/core/browser/password_leak_history_consumer.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/password_manager/core/browser/password_reuse_detector.h"
@@ -377,7 +378,7 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(password_manager::features::kLeakHistory);
   CompromisedCredentials compromised_credentials(
-      GURL(kTestWebRealm1), base::ASCIIToUTF16("username_value_1"),
+      kTestWebRealm1, base::ASCIIToUTF16("username_value_1"),
       base::Time::FromTimeT(1), CompromiseType::kLeaked);
 
   scoped_refptr<PasswordStoreDefault> store = CreatePasswordStore();
@@ -420,7 +421,7 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(password_manager::features::kLeakHistory);
   CompromisedCredentials compromised_credentials(
-      GURL(kTestWebRealm1), base::ASCIIToUTF16("username_value_1"),
+      kTestWebRealm1, base::ASCIIToUTF16("username_value_1"),
       base::Time::FromTimeT(1), CompromiseType::kLeaked);
   scoped_refptr<PasswordStoreDefault> store = CreatePasswordStore();
   store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
@@ -462,7 +463,7 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(password_manager::features::kLeakHistory);
   CompromisedCredentials compromised_credentials(
-      GURL(kTestWebRealm1), base::ASCIIToUTF16("username_value_1"),
+      kTestWebRealm1, base::ASCIIToUTF16("username_value_1"),
       base::Time::FromTimeT(1), CompromiseType::kLeaked);
   scoped_refptr<PasswordStoreDefault> store = CreatePasswordStore();
   store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
@@ -1388,10 +1389,10 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(password_manager::features::kLeakHistory);
   CompromisedCredentials compromised_credentials(
-      GURL("https://example.com"), base::ASCIIToUTF16("username"),
+      "https://example.com/", base::ASCIIToUTF16("username"),
       base::Time::FromTimeT(1), CompromiseType::kLeaked);
   CompromisedCredentials compromised_credentials2(
-      GURL("https://2.example.com"), base::ASCIIToUTF16("username2"),
+      "https://2.example.com/", base::ASCIIToUTF16("username2"),
       base::Time::FromTimeT(2), CompromiseType::kLeaked);
 
   scoped_refptr<PasswordStoreDefault> store = CreatePasswordStore();
@@ -1408,7 +1409,7 @@
   testing::Mock::VerifyAndClearExpectations(&consumer);
 
   store->RemoveCompromisedCredentials(
-      compromised_credentials.url, compromised_credentials.username,
+      compromised_credentials.signon_realm, compromised_credentials.username,
       RemoveCompromisedCredentialsReason::kRemove);
   EXPECT_CALL(consumer, OnGetCompromisedCredentials(
                             UnorderedElementsAre(compromised_credentials2)));
@@ -1422,13 +1423,13 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(password_manager::features::kLeakHistory);
   CompromisedCredentials compromised_credentials1(
-      GURL("https://example1.com"), base::ASCIIToUTF16("username1"),
+      "https://example1.com/", base::ASCIIToUTF16("username1"),
       base::Time::FromTimeT(100), CompromiseType::kLeaked);
   CompromisedCredentials compromised_credentials2(
-      GURL("https://2.example.com"), base::ASCIIToUTF16("username2"),
+      "https://2.example.com/", base::ASCIIToUTF16("username2"),
       base::Time::FromTimeT(200), CompromiseType::kLeaked);
   CompromisedCredentials compromised_credentials3(
-      GURL("https://example3.com"), base::ASCIIToUTF16("username3"),
+      "https://example3.com/", base::ASCIIToUTF16("username3"),
       base::Time::FromTimeT(300), CompromiseType::kLeaked);
 
   scoped_refptr<PasswordStoreDefault> store = CreatePasswordStore();
@@ -1448,7 +1449,7 @@
 
   store->RemoveCompromisedCredentialsByUrlAndTime(
       base::BindRepeating(std::not_equal_to<GURL>(),
-                          compromised_credentials3.url),
+                          GURL(compromised_credentials3.signon_realm)),
       base::Time::FromTimeT(150), base::Time::FromTimeT(350), base::Closure());
 
   EXPECT_CALL(consumer,
diff --git a/components/password_manager/core/browser/test_password_store.cc b/components/password_manager/core/browser/test_password_store.cc
index d1e11da2..922b6c3 100644
--- a/components/password_manager/core/browser/test_password_store.cc
+++ b/components/password_manager/core/browser/test_password_store.cc
@@ -231,7 +231,7 @@
 }
 
 void TestPasswordStore::RemoveCompromisedCredentialsImpl(
-    const GURL& url,
+    const std::string& signon_realm,
     const base::string16& username,
     RemoveCompromisedCredentialsReason reason) {
   NOTIMPLEMENTED();
diff --git a/components/password_manager/core/browser/test_password_store.h b/components/password_manager/core/browser/test_password_store.h
index 238aef31..be358af 100644
--- a/components/password_manager/core/browser/test_password_store.h
+++ b/components/password_manager/core/browser/test_password_store.h
@@ -89,7 +89,7 @@
   void AddCompromisedCredentialsImpl(
       const CompromisedCredentials& compromised_credentials) override;
   void RemoveCompromisedCredentialsImpl(
-      const GURL& url,
+      const std::string& signon_realm,
       const base::string16& username,
       RemoveCompromisedCredentialsReason reason) override;
   std::vector<CompromisedCredentials> GetAllCompromisedCredentialsImpl()
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 9d49147..285d1c8 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -1086,7 +1086,7 @@
       'owners': ['file://components/policy/resources/OWNERS'],
       'type': 'string',
       'schema': { 'type': 'string' },
-      'supported_on': ['chrome.*:8-', 'chrome_os:11-'],
+      'supported_on': ['chrome.*:8-', 'chrome_os:11-', 'android:81-'],
       'features': {
         'can_be_recommended': True,
         'dynamic_refresh': True,
@@ -1098,7 +1098,7 @@
       'tags': [],
       'desc': '''Configures the default home page URL in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> and prevents users from changing it.
 
-          The home page is the page opened by the Home button. The pages that open on startup are controlled by the RestoreOnStartup policies.
+          The home page is the page opened by the Home button. On desktop, the pages that open on startup are controlled by the RestoreOnStartup policies.
 
           The home page type can either be set to a URL you specify here or set to the New Tab Page. If you select the New Tab Page, then this policy does not take effect.
 
diff --git a/components/sqlite_proto/BUILD.gn b/components/sqlite_proto/BUILD.gn
index fdd39ab..cea6951 100644
--- a/components/sqlite_proto/BUILD.gn
+++ b/components/sqlite_proto/BUILD.gn
@@ -2,13 +2,20 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("test_proto") {
+  testonly = true
+  sources = [ "test_proto.proto" ]
+}
+
 static_library("sqlite_proto") {
   sources = [
-    "loading_predictor_key_value_data.h",
-    "loading_predictor_key_value_table.cc",
-    "loading_predictor_key_value_table.h",
-    "predictor_table_base.cc",
-    "predictor_table_base.h",
+    "key_value_data.h",
+    "key_value_table.cc",
+    "key_value_table.h",
+    "table_manager.cc",
+    "table_manager.h",
   ]
   deps = [
     "//base",
@@ -16,3 +23,20 @@
     "//third_party/protobuf:protobuf_lite",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "key_value_data_unittest.cc",
+    "key_value_table_unittest.cc",
+  ]
+  deps = [
+    ":sqlite_proto",
+    ":test_proto",
+    "//base",
+    "//base/test:test_support",
+    "//sql",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/sqlite_proto/key_value_data.h b/components/sqlite_proto/key_value_data.h
new file mode 100644
index 0000000..126eeda
--- /dev/null
+++ b/components/sqlite_proto/key_value_data.h
@@ -0,0 +1,262 @@
+// 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 COMPONENTS_SQLITE_PROTO_KEY_VALUE_DATA_H_
+#define COMPONENTS_SQLITE_PROTO_KEY_VALUE_DATA_H_
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "base/sequence_checker.h"
+#include "base/timer/timer.h"
+#include "components/sqlite_proto/key_value_table.h"
+#include "components/sqlite_proto/table_manager.h"
+
+namespace sqlite_proto {
+
+namespace internal {
+// FakeCompare is a dummy comparator provided so that clients using
+// KeyValueData<T> objects with unbounded-size caches need not
+// specify the Compare template parameter, which is used exclusively
+// for pruning the cache when it would exceed its size bound.
+template <typename T>
+struct FakeCompare {
+  bool operator()(const T& unused_lhs, const T& unused_rhs) { return true; }
+};
+}  // namespace internal
+
+// The class provides a synchronous access to the data backed by
+// KeyValueTable<T>. The current implementation caches all the
+// data in the memory. The cache size is limited by the |max_num_entries|
+// parameter, using the Compare function to decide which entries should
+// be evicted.
+//
+// NOTE: If the data store is larger than the maximum cache size, it
+// will be pruned on construction to satisfy the size invariant specified
+// by |max_num_entries|. If this is undesirable, set a sufficiently high
+// |max_num_entries| (or pass |max_num_entries| = base::nullopt for
+// unbounded size).
+//
+// InitializeOnDBSequence() must be called on the DB sequence of the
+// TableManager. All other methods must be called on UI thread.
+template <typename T, typename Compare = internal::FakeCompare<T>>
+class KeyValueData {
+ public:
+  // Constructor. Parameters:
+  // - |manager| provides an interface for scheduling database tasks for
+  // execution on the database thread.
+  // - |backend| provides the operations for updating and querying the
+  // backing database (to be scheduled and executed using |manager|).
+  // - |max_num_entries|, if given, caps the size of the in-memory cache;
+  // the Compare template parameter requires a meaningful (in particular,
+  // non-default) value iff max_num_entries is non-nullopt.
+  // - |flush_delay| is the interval for which to gather writes and deletes
+  // passing them through to the backing store; a value of zero will
+  // pass writes and deletes through immediately.
+  KeyValueData(scoped_refptr<TableManager> manager,
+               KeyValueTable<T>* backend,
+               base::Optional<size_t> max_num_entries,
+               base::TimeDelta flush_delay);
+
+  KeyValueData(const KeyValueData&) = delete;
+  KeyValueData& operator=(const KeyValueData&) = delete;
+
+  // Must be called on the provided TableManager's DB sequence
+  // before calling all other methods.
+  void InitializeOnDBSequence();
+
+  // Assigns data associated with the |key| to |data|. Returns true iff the
+  // |key| exists, false otherwise. |data| pointer may be nullptr to get the
+  // return value only.
+  bool TryGetData(const std::string& key, T* data) const;
+
+  // Returns a view of all the cached data. (The next write or delete may
+  // invalidate this reference.)
+  const std::map<std::string, T>& GetAllCached() { return *data_cache_; }
+
+  // Assigns data associated with the |key| to |data|.
+  void UpdateData(const std::string& key, const T& data);
+
+  // Deletes data associated with the |keys| from the database.
+  void DeleteData(const std::vector<std::string>& keys);
+
+  // Deletes all entries from the database.
+  void DeleteAllData();
+
+ private:
+  struct EntryCompare : private Compare {
+    bool operator()(const std::pair<std::string, T>& lhs,
+                    const std::pair<std::string, T>& rhs) {
+      return Compare::operator()(lhs.second, rhs.second);
+    }
+  };
+
+  enum class DeferredOperation { kUpdate, kDelete };
+
+  void FlushDataToDisk();
+
+  scoped_refptr<TableManager> manager_;
+  KeyValueTable<T>* backend_table_;
+  std::unique_ptr<std::map<std::string, T>> data_cache_;
+  std::unordered_map<std::string, DeferredOperation> deferred_updates_;
+  base::RepeatingTimer flush_timer_;
+  const base::TimeDelta flush_delay_;
+  const base::Optional<size_t> max_num_entries_;
+  EntryCompare entry_compare_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+template <typename T, typename Compare>
+KeyValueData<T, Compare>::KeyValueData(scoped_refptr<TableManager> manager,
+                                       KeyValueTable<T>* backend,
+                                       base::Optional<size_t> max_num_entries,
+                                       base::TimeDelta flush_delay)
+    : manager_(manager),
+      backend_table_(backend),
+      flush_delay_(flush_delay),
+      max_num_entries_(max_num_entries) {}
+
+template <typename T, typename Compare>
+void KeyValueData<T, Compare>::InitializeOnDBSequence() {
+  DCHECK(manager_->GetTaskRunner()->RunsTasksInCurrentSequence());
+  auto data_map = std::make_unique<std::map<std::string, T>>();
+  manager_->ExecuteDBTaskOnDBSequence(
+      base::BindOnce(&KeyValueTable<T>::GetAllData,
+                     base::Unretained(backend_table_), data_map.get()));
+
+  // To ensure invariant that data_cache_.size() <= max_num_entries_.
+  std::vector<std::string> keys_to_delete;
+  while (max_num_entries_.has_value() && data_map->size() > *max_num_entries_) {
+    auto entry_to_delete =
+        std::min_element(data_map->begin(), data_map->end(), entry_compare_);
+    keys_to_delete.emplace_back(entry_to_delete->first);
+    data_map->erase(entry_to_delete);
+  }
+  if (!keys_to_delete.empty()) {
+    manager_->ExecuteDBTaskOnDBSequence(base::BindOnce(
+        &KeyValueTable<T>::DeleteData, base::Unretained(backend_table_),
+        std::vector<std::string>(keys_to_delete)));
+  }
+
+  data_cache_ = std::move(data_map);
+}
+
+template <typename T, typename Compare>
+bool KeyValueData<T, Compare>::TryGetData(const std::string& key,
+                                          T* data) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(data_cache_);
+  auto it = data_cache_->find(key);
+  if (it == data_cache_->end())
+    return false;
+
+  if (data)
+    *data = it->second;
+  return true;
+}
+
+template <typename T, typename Compare>
+void KeyValueData<T, Compare>::UpdateData(const std::string& key,
+                                          const T& data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(data_cache_);
+  auto it = data_cache_->find(key);
+  if (it == data_cache_->end()) {
+    if (data_cache_->size() == max_num_entries_) {
+      auto entry_to_delete = std::min_element(
+          data_cache_->begin(), data_cache_->end(), entry_compare_);
+      deferred_updates_[entry_to_delete->first] = DeferredOperation::kDelete;
+      data_cache_->erase(entry_to_delete);
+    }
+    data_cache_->emplace(key, data);
+  } else {
+    it->second = data;
+  }
+  deferred_updates_[key] = DeferredOperation::kUpdate;
+
+  if (flush_delay_.is_zero()) {
+    // Flush immediately, only for tests.
+    FlushDataToDisk();
+  } else if (!flush_timer_.IsRunning()) {
+    flush_timer_.Start(FROM_HERE, flush_delay_, this,
+                       &KeyValueData::FlushDataToDisk);
+  }
+}
+
+template <typename T, typename Compare>
+void KeyValueData<T, Compare>::DeleteData(
+    const std::vector<std::string>& keys) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(data_cache_);
+  for (const std::string& key : keys) {
+    if (data_cache_->erase(key))
+      deferred_updates_[key] = DeferredOperation::kDelete;
+  }
+
+  // Run all deferred updates immediately because it was requested by user.
+  if (!deferred_updates_.empty())
+    FlushDataToDisk();
+}
+
+template <typename T, typename Compare>
+void KeyValueData<T, Compare>::DeleteAllData() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(data_cache_);
+  data_cache_->clear();
+  deferred_updates_.clear();
+  // Delete all the content of the database immediately because it was requested
+  // by user.
+  manager_->ScheduleDBTask(FROM_HERE,
+                           base::BindOnce(&KeyValueTable<T>::DeleteAllData,
+                                          base::Unretained(backend_table_)));
+}
+
+template <typename T, typename Compare>
+void KeyValueData<T, Compare>::FlushDataToDisk() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (deferred_updates_.empty())
+    return;
+
+  std::vector<std::string> keys_to_delete;
+  for (const auto& entry : deferred_updates_) {
+    const std::string& key = entry.first;
+    switch (entry.second) {
+      case DeferredOperation::kUpdate: {
+        auto it = data_cache_->find(key);
+        if (it != data_cache_->end()) {
+          manager_->ScheduleDBTask(
+              FROM_HERE, base::BindOnce(&KeyValueTable<T>::UpdateData,
+                                        base::Unretained(backend_table_), key,
+                                        it->second));
+        }
+        break;
+      }
+      case DeferredOperation::kDelete:
+        keys_to_delete.push_back(key);
+    }
+  }
+
+  if (!keys_to_delete.empty()) {
+    manager_->ScheduleDBTask(
+        FROM_HERE,
+        base::BindOnce(&KeyValueTable<T>::DeleteData,
+                       base::Unretained(backend_table_), keys_to_delete));
+  }
+
+  deferred_updates_.clear();
+}
+
+}  // namespace sqlite_proto
+
+#endif  // COMPONENTS_SQLITE_PROTO_KEY_VALUE_DATA_H_
diff --git a/components/sqlite_proto/key_value_data_unittest.cc b/components/sqlite_proto/key_value_data_unittest.cc
new file mode 100644
index 0000000..cfc21e6d
--- /dev/null
+++ b/components/sqlite_proto/key_value_data_unittest.cc
@@ -0,0 +1,234 @@
+// 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/sqlite_proto/key_value_data.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "base/strings/stringprintf.h"
+#include "base/task/post_task.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/sqlite_proto/table_manager.h"
+#include "components/sqlite_proto/test_proto.pb.h"
+#include "sql/database.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sqlite_proto {
+
+namespace {
+
+template <typename T>
+class FakeKeyValueTable : public KeyValueTable<T> {
+ public:
+  FakeKeyValueTable() : sqlite_proto::KeyValueTable<T>("") {}
+  void GetAllData(std::map<std::string, T>* data_map,
+                  sql::Database* db) const override {
+    *data_map = data_;
+  }
+  void UpdateData(const std::string& key,
+                  const T& data,
+                  sql::Database* db) override {
+    data_[key] = data;
+  }
+  void DeleteData(const std::vector<std::string>& keys,
+                  sql::Database* db) override {
+    for (const auto& key : keys)
+      data_.erase(key);
+  }
+  void DeleteAllData(sql::Database* db) override { data_.clear(); }
+
+  std::map<std::string, T> data_;
+};
+
+class FakeTableManager : public TableManager {
+ public:
+  FakeTableManager() : TableManager(base::ThreadTaskRunnerHandle::Get()) {}
+  void ScheduleDBTask(const base::Location& from_here,
+                      base::OnceCallback<void(sql::Database*)> task) override {
+    GetTaskRunner()->PostTask(
+        from_here, base::BindOnce(&TableManager::ExecuteDBTaskOnDBSequence,
+                                  this, std::move(task)));
+  }
+  void ExecuteDBTaskOnDBSequence(
+      base::OnceCallback<void(sql::Database*)> task) override {
+    ASSERT_TRUE(GetTaskRunner()->RunsTasksInCurrentSequence());
+    std::move(task).Run(DB());
+  }
+
+ protected:
+  ~FakeTableManager() override = default;
+
+  // TableManager
+  void CreateTablesIfNonExistent() override {}
+  void LogDatabaseStats() override {}
+};
+
+MATCHER_P(EqualsProto,
+          message,
+          "Match a proto Message equal to the matcher's argument.") {
+  std::string expected_serialized, actual_serialized;
+  message.SerializeToString(&expected_serialized);
+  arg.SerializeToString(&actual_serialized);
+  return expected_serialized == actual_serialized;
+}
+
+struct TestProtoCompare {
+  bool operator()(const TestProto& lhs, const TestProto& rhs) {
+    return lhs.value() < rhs.value();
+  }
+};
+
+}  // namespace
+
+class KeyValueDataTest : public ::testing::Test {
+ public:
+  KeyValueDataTest()
+      : manager_(base::MakeRefCounted<FakeTableManager>()),
+        data_(manager_, &table_, base::nullopt, base::TimeDelta()) {
+    // In these tests, we're using the current thread as the DB sequence.
+    data_.InitializeOnDBSequence();
+  }
+
+  ~KeyValueDataTest() override = default;
+
+ protected:
+  base::test::TaskEnvironment env_;
+  FakeKeyValueTable<TestProto> table_;
+  scoped_refptr<TableManager> manager_ =
+      base::MakeRefCounted<FakeTableManager>();
+  KeyValueData<TestProto, TestProtoCompare> data_;
+};
+
+TEST_F(KeyValueDataTest, GetWhenEmpty) {
+  TestProto result;
+  EXPECT_FALSE(data_.TryGetData("nonexistent_key", &result));
+}
+
+TEST_F(KeyValueDataTest, PutAndGet) {
+  TestProto first_entry, second_entry;
+  first_entry.set_value(1);
+  second_entry.set_value(1);
+
+  data_.UpdateData("a", first_entry);
+  data_.UpdateData("b", second_entry);
+
+  TestProto result;
+  ASSERT_TRUE(data_.TryGetData("a", &result));
+  EXPECT_THAT(result, EqualsProto(first_entry));
+
+  ASSERT_TRUE(data_.TryGetData("b", &result));
+  EXPECT_THAT(result, EqualsProto(second_entry));
+}
+
+// Test that deleting one entry:
+// - makes that entry inaccessible, but
+// - does not affect the remaining entry.
+TEST_F(KeyValueDataTest, Delete) {
+  TestProto first_entry, second_entry;
+  first_entry.set_value(1);
+  second_entry.set_value(1);
+
+  data_.UpdateData("a", first_entry);
+  data_.UpdateData("b", second_entry);
+
+  TestProto result;
+  data_.DeleteData(std::vector<std::string>{"b"});
+  EXPECT_FALSE(data_.TryGetData("b", &result));
+
+  ASSERT_TRUE(data_.TryGetData("a", &result));
+  EXPECT_THAT(result, EqualsProto(first_entry));
+}
+
+TEST_F(KeyValueDataTest, DeleteAll) {
+  TestProto first_entry, second_entry;
+  first_entry.set_value(1);
+  second_entry.set_value(1);
+
+  data_.UpdateData("a", first_entry);
+  data_.UpdateData("b", second_entry);
+
+  data_.DeleteAllData();
+
+  EXPECT_TRUE(data_.GetAllCached().empty());
+}
+
+TEST(KeyValueDataTestSize, CantAddToFullTable) {
+  FakeKeyValueTable<TestProto> table;
+  base::test::TaskEnvironment env;
+
+  auto manager = base::MakeRefCounted<FakeTableManager>();
+  KeyValueData<TestProto, TestProtoCompare> data(
+      manager, &table, /*max_num_entries=*/2,
+      /*flush_delay=*/base::TimeDelta());
+  // In these tests, we're using the current thread as the DB sequence.
+  data.InitializeOnDBSequence();
+
+  TestProto one_entry, two_entry, three_entry;
+  one_entry.set_value(1);
+  two_entry.set_value(2);
+  three_entry.set_value(3);
+
+  data.UpdateData("a", one_entry);
+  data.UpdateData("b", two_entry);
+  data.UpdateData("c", three_entry);
+
+  EXPECT_EQ(data.GetAllCached().size(), 2u);
+}
+
+// Test that building a KeyValueData on top of a backend table
+// with more than |max_num_entries| many entries leads to the table
+// being pruned down to a number of entries equal to the KeyValueData's
+// capacity.
+TEST(KeyValueDataTestSize, PrunesOverlargeTable) {
+  FakeKeyValueTable<TestProto> table;
+  base::test::TaskEnvironment env;
+
+  auto manager = base::MakeRefCounted<FakeTableManager>();
+
+  // Initialization: write a table of size 2 to |manager|'s backend.
+  {
+    KeyValueData<TestProto, TestProtoCompare> data(
+        manager, &table, /*max_num_entries=*/base::nullopt,
+        /*flush_delay=*/base::TimeDelta());
+    // In these tests, we're using the current thread as the DB sequence.
+    data.InitializeOnDBSequence();
+
+    TestProto one_entry, two_entry;
+    one_entry.set_value(1);
+    two_entry.set_value(2);
+
+    data.UpdateData("a", one_entry);
+    data.UpdateData("b", two_entry);
+
+    // Write changes through to the "disk."
+    env.RunUntilIdle();
+  }
+
+  {
+    KeyValueData<TestProto, TestProtoCompare> data(
+        manager, &table, /*max_num_entries=*/1,
+        /*flush_delay=*/base::TimeDelta());
+    // In these tests, we're using the current thread as the DB sequence.
+    data.InitializeOnDBSequence();
+
+    // A cache with size limit less than the size of the database
+    // should load items up to its capacity (evicting the rest).
+    EXPECT_EQ(data.GetAllCached().size(), 1u);
+  }
+
+  {
+    KeyValueData<TestProto, TestProtoCompare> data(
+        manager, &table, /*max_num_entries=*/base::nullopt,
+        /*flush_delay=*/base::TimeDelta());
+    // In these tests, we're using the current thread as the DB sequence.
+    data.InitializeOnDBSequence();
+
+    // The second, max_num_elements=1, cache should have pruned
+    // the database to a single element upon initialization.
+    EXPECT_EQ(data.GetAllCached().size(), 1u);
+  }
+}
+
+}  // namespace sqlite_proto
diff --git a/components/sqlite_proto/loading_predictor_key_value_table.cc b/components/sqlite_proto/key_value_table.cc
similarity index 91%
rename from components/sqlite_proto/loading_predictor_key_value_table.cc
rename to components/sqlite_proto/key_value_table.cc
index 284be6a..4ec4b50 100644
--- a/components/sqlite_proto/loading_predictor_key_value_table.cc
+++ b/components/sqlite_proto/key_value_table.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/sqlite_proto/loading_predictor_key_value_table.h"
+#include "components/sqlite_proto/key_value_table.h"
 
 #include "base/strings/stringprintf.h"
 #include "third_party/protobuf/src/google/protobuf/message_lite.h"
 
-namespace predictors {
+namespace sqlite_proto {
 
 namespace internal {
 
@@ -38,4 +38,4 @@
 }
 
 }  // namespace internal
-}  // namespace predictors
+}  // namespace sqlite_proto
diff --git a/components/sqlite_proto/loading_predictor_key_value_table.h b/components/sqlite_proto/key_value_table.h
similarity index 60%
rename from components/sqlite_proto/loading_predictor_key_value_table.h
rename to components/sqlite_proto/key_value_table.h
index 8eff3e8..e23e49ea 100644
--- a/components/sqlite_proto/loading_predictor_key_value_table.h
+++ b/components/sqlite_proto/key_value_table.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_SQLITE_PROTO_LOADING_PREDICTOR_KEY_VALUE_TABLE_H_
-#define COMPONENTS_SQLITE_PROTO_LOADING_PREDICTOR_KEY_VALUE_TABLE_H_
+#ifndef COMPONENTS_SQLITE_PROTO_KEY_VALUE_TABLE_H_
+#define COMPONENTS_SQLITE_PROTO_KEY_VALUE_TABLE_H_
 
 #include <map>
 #include <string>
@@ -17,7 +17,7 @@
 }
 }  // namespace google
 
-namespace predictors {
+namespace sqlite_proto {
 
 namespace internal {
 
@@ -38,21 +38,24 @@
 // class doesn't manage the creation and the deletion of the table.
 //
 // All the functions except of the constructor must be called on a DB sequence
-// of the PredictorTableBase.
-// The preferred way to call the methods of this class is passing the method to
-// PredictorTableBase::ScheduleDBTask().
+// of the corresponding TableManager. The preferred way to call the methods of
+// this class is passing the method to TableManager::ScheduleDBTask().
 //
 // Example:
-// tables_->ScheduleDBTask(
+// manager_->ScheduleDBTask(
 //     FROM_HERE,
-//     base::BindOnce(&LoadingPredictorKeyValueTable<PrefetchData>::UpdateData,
+//     base::BindOnce(&KeyValueTable<PrefetchData>::UpdateData,
 //                    base::Unretained(table_), key, data));
 template <typename T>
-class LoadingPredictorKeyValueTable {
+class KeyValueTable {
  public:
-  explicit LoadingPredictorKeyValueTable(const std::string& table_name);
+  explicit KeyValueTable(const std::string& table_name);
   // Virtual for testing.
-  virtual ~LoadingPredictorKeyValueTable() {}
+  virtual ~KeyValueTable() = default;
+
+  KeyValueTable(const KeyValueTable&) = delete;
+  KeyValueTable& operator=(const KeyValueTable&) = delete;
+
   virtual void GetAllData(std::map<std::string, T>* data_map,
                           sql::Database* db) const;
   virtual void UpdateData(const std::string& key,
@@ -64,21 +67,17 @@
 
  private:
   const std::string table_name_;
-
-  DISALLOW_COPY_AND_ASSIGN(LoadingPredictorKeyValueTable);
 };
 
 template <typename T>
-LoadingPredictorKeyValueTable<T>::LoadingPredictorKeyValueTable(
-    const std::string& table_name)
+KeyValueTable<T>::KeyValueTable(const std::string& table_name)
     : table_name_(table_name) {}
 
 template <typename T>
-void LoadingPredictorKeyValueTable<T>::GetAllData(
-    std::map<std::string, T>* data_map,
-    sql::Database* db) const {
+void KeyValueTable<T>::GetAllData(std::map<std::string, T>* data_map,
+                                  sql::Database* db) const {
   sql::Statement reader(db->GetUniqueStatement(
-      ::predictors::internal::GetSelectAllSql(table_name_).c_str()));
+      ::sqlite_proto::internal::GetSelectAllSql(table_name_).c_str()));
   while (reader.Step()) {
     auto it = data_map->emplace(reader.ColumnString(0), T()).first;
     int size = reader.ColumnByteLength(1);
@@ -89,21 +88,20 @@
 }
 
 template <typename T>
-void LoadingPredictorKeyValueTable<T>::UpdateData(const std::string& key,
-                                                  const T& data,
-                                                  sql::Database* db) {
+void KeyValueTable<T>::UpdateData(const std::string& key,
+                                  const T& data,
+                                  sql::Database* db) {
   sql::Statement inserter(db->GetUniqueStatement(
-      ::predictors::internal::GetReplaceSql(table_name_).c_str()));
-  ::predictors::internal::BindDataToStatement(key, data, &inserter);
+      ::sqlite_proto::internal::GetReplaceSql(table_name_).c_str()));
+  ::sqlite_proto::internal::BindDataToStatement(key, data, &inserter);
   inserter.Run();
 }
 
 template <typename T>
-void LoadingPredictorKeyValueTable<T>::DeleteData(
-    const std::vector<std::string>& keys,
-    sql::Database* db) {
+void KeyValueTable<T>::DeleteData(const std::vector<std::string>& keys,
+                                  sql::Database* db) {
   sql::Statement deleter(db->GetUniqueStatement(
-      ::predictors::internal::GetDeleteSql(table_name_).c_str()));
+      ::sqlite_proto::internal::GetDeleteSql(table_name_).c_str()));
   for (const auto& key : keys) {
     deleter.BindString(0, key);
     deleter.Run();
@@ -112,12 +110,12 @@
 }
 
 template <typename T>
-void LoadingPredictorKeyValueTable<T>::DeleteAllData(sql::Database* db) {
+void KeyValueTable<T>::DeleteAllData(sql::Database* db) {
   sql::Statement deleter(db->GetUniqueStatement(
-      ::predictors::internal::GetDeleteAllSql(table_name_).c_str()));
+      ::sqlite_proto::internal::GetDeleteAllSql(table_name_).c_str()));
   deleter.Run();
 }
 
-}  // namespace predictors
+}  // namespace sqlite_proto
 
-#endif  // COMPONENTS_SQLITE_PROTO_LOADING_PREDICTOR_KEY_VALUE_TABLE_H_
+#endif  // COMPONENTS_SQLITE_PROTO_KEY_VALUE_TABLE_H_
diff --git a/components/sqlite_proto/key_value_table_unittest.cc b/components/sqlite_proto/key_value_table_unittest.cc
new file mode 100644
index 0000000..d97488e9
--- /dev/null
+++ b/components/sqlite_proto/key_value_table_unittest.cc
@@ -0,0 +1,131 @@
+// 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/sqlite_proto/key_value_table.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "components/sqlite_proto/test_proto.pb.h"
+#include "sql/database.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ElementsAre;
+using ::testing::Pair;
+using ::testing::UnorderedElementsAre;
+
+namespace sqlite_proto {
+
+namespace {
+
+MATCHER_P(EqualsProto,
+          message,
+          "Match a proto Message equal to the matcher's argument.") {
+  std::string expected_serialized, actual_serialized;
+  message.SerializeToString(&expected_serialized);
+  arg.SerializeToString(&actual_serialized);
+  return expected_serialized == actual_serialized;
+}
+
+bool CreateProtoTable(sql::Database* db) {
+  const char kCreateProtoTableStatementTemplate[] =
+      "CREATE TABLE my_table ( "
+      "key TEXT, "
+      "proto BLOB, "
+      "PRIMARY KEY(key))";
+  return db->Execute(kCreateProtoTableStatementTemplate);
+}
+
+}  // namespace
+
+class KeyValueTableTest : public ::testing::Test {
+ public:
+  KeyValueTableTest() {
+    CHECK(db_.OpenInMemory());
+    CHECK(CreateProtoTable(&db_));
+  }
+  ~KeyValueTableTest() override = default;
+
+ protected:
+  sql::Database db_;
+  KeyValueTable<TestProto> table_{"my_table"};
+};
+
+TEST_F(KeyValueTableTest, Empty) {
+  std::map<std::string, TestProto> my_data;
+  table_.GetAllData(&my_data, &db_);
+  EXPECT_TRUE(my_data.empty());
+}
+
+TEST_F(KeyValueTableTest, PutAndGet) {
+  TestProto element;
+  element.set_value(1);
+  table_.UpdateData("a", element, &db_);
+
+  TestProto second_element;
+  second_element.set_value(2);
+  table_.UpdateData("b", second_element, &db_);
+
+  std::map<std::string, TestProto> my_data;
+  table_.GetAllData(&my_data, &db_);
+  EXPECT_THAT(my_data,
+              UnorderedElementsAre(Pair("a", EqualsProto(element)),
+                                   Pair("b", EqualsProto(second_element))));
+}
+
+TEST_F(KeyValueTableTest, Update) {
+  TestProto element;
+  element.set_value(1);
+
+  TestProto superseding_element;
+  superseding_element.set_value(2);
+
+  table_.UpdateData("a", element, &db_);
+  table_.UpdateData("a", superseding_element, &db_);
+
+  std::map<std::string, TestProto> my_data;
+  table_.GetAllData(&my_data, &db_);
+  EXPECT_THAT(my_data,
+              ElementsAre(Pair("a", EqualsProto(superseding_element))));
+}
+
+TEST_F(KeyValueTableTest, DeleteNonexistent) {
+  TestProto element;
+  element.set_value(1);
+  table_.UpdateData("a", element, &db_);
+
+  // Deleting a nonexistent key should no-op.
+  table_.DeleteData(std::vector<std::string>{"b"}, &db_);
+
+  std::map<std::string, TestProto> my_data;
+  table_.GetAllData(&my_data, &db_);
+  EXPECT_THAT(my_data, ElementsAre(Pair("a", EqualsProto(element))));
+}
+
+TEST_F(KeyValueTableTest, Delete) {
+  TestProto element;
+  element.set_value(1);
+  table_.UpdateData("a", element, &db_);
+
+  table_.DeleteData(std::vector<std::string>{"a"}, &db_);
+
+  std::map<std::string, TestProto> my_data;
+  table_.GetAllData(&my_data, &db_);
+  EXPECT_TRUE(my_data.empty());
+}
+
+TEST_F(KeyValueTableTest, DeleteAll) {
+  TestProto element;
+  element.set_value(1);
+
+  table_.UpdateData("a", element, &db_);
+  table_.UpdateData("b", element, &db_);
+
+  table_.DeleteAllData(&db_);
+
+  std::map<std::string, TestProto> my_data;
+  table_.GetAllData(&my_data, &db_);
+  EXPECT_TRUE(my_data.empty());
+}
+
+}  // namespace sqlite_proto
diff --git a/components/sqlite_proto/loading_predictor_key_value_data.h b/components/sqlite_proto/loading_predictor_key_value_data.h
deleted file mode 100644
index ae5fb9e6..0000000
--- a/components/sqlite_proto/loading_predictor_key_value_data.h
+++ /dev/null
@@ -1,239 +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 COMPONENTS_SQLITE_PROTO_LOADING_PREDICTOR_KEY_VALUE_DATA_H_
-#define COMPONENTS_SQLITE_PROTO_LOADING_PREDICTOR_KEY_VALUE_DATA_H_
-
-#include <algorithm>
-#include <map>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequence_checker.h"
-#include "base/timer/timer.h"
-#include "components/sqlite_proto/loading_predictor_key_value_table.h"
-#include "components/sqlite_proto/predictor_table_base.h"
-
-class PredictorsHandler;
-
-namespace predictors {
-
-// The class provides a synchronous access to the data backed by
-// LoadingPredictorKeyValueTable<T>. The current implementation caches all the
-// data in the memory. The cache size is limited by max_size parameter using
-// Compare function to decide which entry should be evicted.
-//
-// InitializeOnDBSequence() must be called on the DB sequence of the
-// PredictorTableBase. All other methods must be called on UI thread.
-template <typename T, typename Compare>
-class LoadingPredictorKeyValueData {
- public:
-  LoadingPredictorKeyValueData(scoped_refptr<PredictorTableBase> tables,
-                               LoadingPredictorKeyValueTable<T>* backend,
-                               size_t max_size,
-                               base::TimeDelta flush_delay);
-
-  // Must be called on the DB sequence of the ResourcePrefetchPredictorTables
-  // before calling all other methods.
-  void InitializeOnDBSequence();
-
-  // Assigns data associated with the |key| to |data|. Returns true iff the
-  // |key| exists, false otherwise. |data| pointer may be nullptr to get the
-  // return value only.
-  bool TryGetData(const std::string& key, T* data) const;
-
-  // Assigns data associated with the |key| to |data|.
-  void UpdateData(const std::string& key, const T& data);
-
-  // Deletes data associated with the |keys| from the database.
-  void DeleteData(const std::vector<std::string>& keys);
-
-  // Deletes all entries from the database.
-  void DeleteAllData();
-
-  std::map<std::string, T>* DataCacheForTesting() { return data_cache_.get(); }
-
- private:
-  friend class ::PredictorsHandler;
-
-  struct EntryCompare : private Compare {
-    bool operator()(const std::pair<std::string, T>& lhs,
-                    const std::pair<std::string, T>& rhs) {
-      return Compare::operator()(lhs.second, rhs.second);
-    }
-  };
-
-  enum class DeferredOperation { kUpdate, kDelete };
-
-  void FlushDataToDisk();
-
-  scoped_refptr<PredictorTableBase> tables_;
-  LoadingPredictorKeyValueTable<T>* backend_table_;
-  std::unique_ptr<std::map<std::string, T>> data_cache_;
-  std::unordered_map<std::string, DeferredOperation> deferred_updates_;
-  base::RepeatingTimer flush_timer_;
-  const base::TimeDelta flush_delay_;
-  const size_t max_size_;
-  EntryCompare entry_compare_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(LoadingPredictorKeyValueData);
-};
-
-template <typename T, typename Compare>
-LoadingPredictorKeyValueData<T, Compare>::LoadingPredictorKeyValueData(
-    scoped_refptr<PredictorTableBase> tables,
-    LoadingPredictorKeyValueTable<T>* backend,
-    size_t max_size,
-    base::TimeDelta flush_delay)
-    : tables_(tables),
-      backend_table_(backend),
-      flush_delay_(flush_delay),
-      max_size_(max_size) {}
-
-template <typename T, typename Compare>
-void LoadingPredictorKeyValueData<T, Compare>::InitializeOnDBSequence() {
-  DCHECK(tables_->GetTaskRunner()->RunsTasksInCurrentSequence());
-  auto data_map = std::make_unique<std::map<std::string, T>>();
-  tables_->ExecuteDBTaskOnDBSequence(
-      base::BindOnce(&LoadingPredictorKeyValueTable<T>::GetAllData,
-                     base::Unretained(backend_table_), data_map.get()));
-
-  // To ensure invariant that data_cache_.size() <= max_size_.
-  std::vector<std::string> keys_to_delete;
-  while (data_map->size() > max_size_) {
-    auto entry_to_delete =
-        std::min_element(data_map->begin(), data_map->end(), entry_compare_);
-    keys_to_delete.emplace_back(entry_to_delete->first);
-    data_map->erase(entry_to_delete);
-  }
-  if (!keys_to_delete.empty()) {
-    tables_->ExecuteDBTaskOnDBSequence(
-        base::BindOnce(&LoadingPredictorKeyValueTable<T>::DeleteData,
-                       base::Unretained(backend_table_),
-                       std::vector<std::string>(keys_to_delete)));
-  }
-
-  data_cache_ = std::move(data_map);
-}
-
-template <typename T, typename Compare>
-bool LoadingPredictorKeyValueData<T, Compare>::TryGetData(
-    const std::string& key,
-    T* data) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(data_cache_);
-  auto it = data_cache_->find(key);
-  if (it == data_cache_->end())
-    return false;
-
-  if (data)
-    *data = it->second;
-  return true;
-}
-
-template <typename T, typename Compare>
-void LoadingPredictorKeyValueData<T, Compare>::UpdateData(
-    const std::string& key,
-    const T& data) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(data_cache_);
-  auto it = data_cache_->find(key);
-  if (it == data_cache_->end()) {
-    if (data_cache_->size() == max_size_) {
-      auto entry_to_delete = std::min_element(
-          data_cache_->begin(), data_cache_->end(), entry_compare_);
-      deferred_updates_[entry_to_delete->first] = DeferredOperation::kDelete;
-      data_cache_->erase(entry_to_delete);
-    }
-    data_cache_->emplace(key, data);
-  } else {
-    it->second = data;
-  }
-  deferred_updates_[key] = DeferredOperation::kUpdate;
-
-  if (flush_delay_.is_zero()) {
-    // Flush immediately, only for tests.
-    FlushDataToDisk();
-  } else if (!flush_timer_.IsRunning()) {
-    flush_timer_.Start(FROM_HERE, flush_delay_, this,
-                       &LoadingPredictorKeyValueData::FlushDataToDisk);
-  }
-}
-
-template <typename T, typename Compare>
-void LoadingPredictorKeyValueData<T, Compare>::DeleteData(
-    const std::vector<std::string>& keys) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(data_cache_);
-  for (const std::string& key : keys) {
-    if (data_cache_->erase(key))
-      deferred_updates_[key] = DeferredOperation::kDelete;
-  }
-
-  // Run all deferred updates immediately because it was requested by user.
-  if (!deferred_updates_.empty())
-    FlushDataToDisk();
-}
-
-template <typename T, typename Compare>
-void LoadingPredictorKeyValueData<T, Compare>::DeleteAllData() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(data_cache_);
-  data_cache_->clear();
-  deferred_updates_.clear();
-  // Delete all the content of the database immediately because it was requested
-  // by user.
-  tables_->ScheduleDBTask(
-      FROM_HERE,
-      base::BindOnce(&LoadingPredictorKeyValueTable<T>::DeleteAllData,
-                     base::Unretained(backend_table_)));
-}
-
-template <typename T, typename Compare>
-void LoadingPredictorKeyValueData<T, Compare>::FlushDataToDisk() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (deferred_updates_.empty())
-    return;
-
-  std::vector<std::string> keys_to_delete;
-  for (const auto& entry : deferred_updates_) {
-    const std::string& key = entry.first;
-    switch (entry.second) {
-      case DeferredOperation::kUpdate: {
-        auto it = data_cache_->find(key);
-        if (it != data_cache_->end()) {
-          tables_->ScheduleDBTask(
-              FROM_HERE,
-              base::BindOnce(&LoadingPredictorKeyValueTable<T>::UpdateData,
-                             base::Unretained(backend_table_), key,
-                             it->second));
-        }
-        break;
-      }
-      case DeferredOperation::kDelete:
-        keys_to_delete.push_back(key);
-    }
-  }
-
-  if (!keys_to_delete.empty()) {
-    tables_->ScheduleDBTask(
-        FROM_HERE,
-        base::BindOnce(&LoadingPredictorKeyValueTable<T>::DeleteData,
-                       base::Unretained(backend_table_), keys_to_delete));
-  }
-
-  deferred_updates_.clear();
-}
-
-}  // namespace predictors
-
-#endif  // COMPONENTS_SQLITE_PROTO_LOADING_PREDICTOR_KEY_VALUE_DATA_H_
diff --git a/components/sqlite_proto/predictor_table_base.cc b/components/sqlite_proto/predictor_table_base.cc
deleted file mode 100644
index 5c60926..0000000
--- a/components/sqlite_proto/predictor_table_base.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sqlite_proto/predictor_table_base.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/sequenced_task_runner.h"
-#include "sql/database.h"
-
-namespace predictors {
-
-base::SequencedTaskRunner* PredictorTableBase::GetTaskRunner() {
-  return db_task_runner_.get();
-}
-
-void PredictorTableBase::ScheduleDBTask(const base::Location& from_here,
-                                        DBTask task) {
-  GetTaskRunner()->PostTask(
-      from_here, base::BindOnce(&PredictorTableBase::ExecuteDBTaskOnDBSequence,
-                                this, std::move(task)));
-}
-
-void PredictorTableBase::ExecuteDBTaskOnDBSequence(DBTask task) {
-  DCHECK(GetTaskRunner()->RunsTasksInCurrentSequence());
-  if (CantAccessDatabase())
-    return;
-
-  std::move(task).Run(DB());
-}
-
-PredictorTableBase::PredictorTableBase(
-    scoped_refptr<base::SequencedTaskRunner> db_task_runner)
-    : db_task_runner_(std::move(db_task_runner)), db_(nullptr) {}
-
-PredictorTableBase::~PredictorTableBase() = default;
-
-void PredictorTableBase::Initialize(sql::Database* db) {
-  DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
-  db_ = db;
-  CreateTableIfNonExistent();
-}
-
-void PredictorTableBase::SetCancelled() {
-  cancelled_.Set();
-}
-
-bool PredictorTableBase::IsCancelled() {
-  return cancelled_.IsSet();
-}
-
-sql::Database* PredictorTableBase::DB() {
-  DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
-  return db_;
-}
-
-void PredictorTableBase::ResetDB() {
-  DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
-  db_ = nullptr;
-}
-
-bool PredictorTableBase::CantAccessDatabase() {
-  DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
-  return cancelled_.IsSet() || !db_;
-}
-
-}  // namespace predictors
diff --git a/components/sqlite_proto/predictor_table_base.h b/components/sqlite_proto/predictor_table_base.h
deleted file mode 100644
index fc931007..0000000
--- a/components/sqlite_proto/predictor_table_base.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SQLITE_PROTO_PREDICTOR_TABLE_BASE_H_
-#define COMPONENTS_SQLITE_PROTO_PREDICTOR_TABLE_BASE_H_
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/atomic_flag.h"
-
-namespace base {
-class Location;
-class SequencedTaskRunner;
-}  // namespace base
-
-namespace sql {
-class Database;
-}
-
-namespace predictors {
-
-// Base class for all tables in the PredictorDatabase.
-//
-// Refcounted as it is created and destroyed in the UI thread but all database
-// related functions need to happen in the database sequence. The task runner
-// for this sequence is provided by the client to the constructor of this class.
-class PredictorTableBase
-    : public base::RefCountedThreadSafe<PredictorTableBase> {
- public:
-  // Returns a SequencedTaskRunner that is used to run tasks on the DB sequence.
-  base::SequencedTaskRunner* GetTaskRunner();
-
-  typedef base::OnceCallback<void(sql::Database*)> DBTask;
-
-  virtual void ScheduleDBTask(const base::Location& from_here, DBTask task);
-
-  virtual void ExecuteDBTaskOnDBSequence(DBTask task);
-
- protected:
-  explicit PredictorTableBase(
-      scoped_refptr<base::SequencedTaskRunner> db_task_runner);
-  virtual ~PredictorTableBase();
-
-  // DB sequence functions.
-  virtual void CreateTableIfNonExistent() = 0;
-  virtual void LogDatabaseStats() = 0;
-  void Initialize(sql::Database* db);
-  void SetCancelled();
-  bool IsCancelled();
-  sql::Database* DB();
-  void ResetDB();
-
-  bool CantAccessDatabase();
-
- private:
-  base::AtomicFlag cancelled_;
-
-  friend class base::RefCountedThreadSafe<PredictorTableBase>;
-
-  scoped_refptr<base::SequencedTaskRunner> db_task_runner_;
-  sql::Database* db_;
-
-  DISALLOW_COPY_AND_ASSIGN(PredictorTableBase);
-};
-
-}  // namespace predictors
-
-#endif  // COMPONENTS_SQLITE_PROTO_PREDICTOR_TABLE_BASE_H_
diff --git a/components/sqlite_proto/table_manager.cc b/components/sqlite_proto/table_manager.cc
new file mode 100644
index 0000000..9e56c88
--- /dev/null
+++ b/components/sqlite_proto/table_manager.cc
@@ -0,0 +1,72 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sqlite_proto/table_manager.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/sequenced_task_runner.h"
+#include "sql/database.h"
+
+namespace sqlite_proto {
+
+using DBTask = base::OnceCallback<void(sql::Database*)>;
+
+base::SequencedTaskRunner* TableManager::GetTaskRunner() {
+  return db_task_runner_.get();
+}
+
+void TableManager::ScheduleDBTask(const base::Location& from_here,
+                                  DBTask task) {
+  GetTaskRunner()->PostTask(
+      from_here, base::BindOnce(&TableManager::ExecuteDBTaskOnDBSequence, this,
+                                std::move(task)));
+}
+
+void TableManager::ExecuteDBTaskOnDBSequence(DBTask task) {
+  DCHECK(GetTaskRunner()->RunsTasksInCurrentSequence());
+  if (CantAccessDatabase())
+    return;
+
+  std::move(task).Run(DB());
+}
+
+TableManager::TableManager(
+    scoped_refptr<base::SequencedTaskRunner> db_task_runner)
+    : db_task_runner_(std::move(db_task_runner)), db_(nullptr) {}
+
+TableManager::~TableManager() = default;
+
+void TableManager::Initialize(sql::Database* db) {
+  DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
+  db_ = db;
+  CreateTablesIfNonExistent();
+}
+
+void TableManager::SetCancelled() {
+  cancelled_.Set();
+}
+
+bool TableManager::IsCancelled() {
+  return cancelled_.IsSet();
+}
+
+sql::Database* TableManager::DB() {
+  DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
+  return db_;
+}
+
+void TableManager::ResetDB() {
+  DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
+  db_ = nullptr;
+}
+
+bool TableManager::CantAccessDatabase() {
+  DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
+  return cancelled_.IsSet() || !db_;
+}
+
+}  // namespace sqlite_proto
diff --git a/components/sqlite_proto/table_manager.h b/components/sqlite_proto/table_manager.h
new file mode 100644
index 0000000..311f37ea
--- /dev/null
+++ b/components/sqlite_proto/table_manager.h
@@ -0,0 +1,73 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SQLITE_PROTO_TABLE_MANAGER_H_
+#define COMPONENTS_SQLITE_PROTO_TABLE_MANAGER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/atomic_flag.h"
+
+namespace base {
+class Location;
+class SequencedTaskRunner;
+}  // namespace base
+
+namespace sql {
+class Database;
+}
+
+namespace sqlite_proto {
+
+// Base class encapsulating database operation scheduling and management, scoped
+// to a collection of tables (possibly, but not necessarily, all of the
+// database's tables).
+//
+// Refcounted as it is created and destroyed in the main thread (e.g., the UI
+// thread in the browser process) but all database related functions need to
+// happen in the database sequence. The task runner for this sequence is
+// provided by the client to the constructor of this class.
+class TableManager : public base::RefCountedThreadSafe<TableManager> {
+ public:
+  // Returns a SequencedTaskRunner that is used to run tasks on the DB sequence.
+  base::SequencedTaskRunner* GetTaskRunner();
+
+  TableManager(const TableManager&) = delete;
+  TableManager& operator=(const TableManager&) = delete;
+
+  virtual void ScheduleDBTask(const base::Location& from_here,
+                              base::OnceCallback<void(sql::Database*)> task);
+
+  virtual void ExecuteDBTaskOnDBSequence(
+      base::OnceCallback<void(sql::Database*)> task);
+
+ protected:
+  explicit TableManager(
+      scoped_refptr<base::SequencedTaskRunner> db_task_runner);
+  virtual ~TableManager();
+
+  // DB sequence functions.
+  virtual void CreateTablesIfNonExistent() = 0;
+  virtual void LogDatabaseStats() = 0;
+  void Initialize(sql::Database* db);
+  void SetCancelled();
+  bool IsCancelled();
+  sql::Database* DB();
+  void ResetDB();
+
+  bool CantAccessDatabase();
+
+ private:
+  base::AtomicFlag cancelled_;
+
+  friend class base::RefCountedThreadSafe<TableManager>;
+
+  scoped_refptr<base::SequencedTaskRunner> db_task_runner_;
+  sql::Database* db_;
+};
+
+}  // namespace sqlite_proto
+
+#endif  // COMPONENTS_SQLITE_PROTO_TABLE_MANAGER_H_
diff --git a/components/sqlite_proto/test_proto.proto b/components/sqlite_proto/test_proto.proto
new file mode 100644
index 0000000..c088bbb8
--- /dev/null
+++ b/components/sqlite_proto/test_proto.proto
@@ -0,0 +1,15 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package sqlite_proto;
+
+// TestProto is a simple protocol buffer type to provide to
+// KeyValueTable and KeyValueData in their tests.
+message TestProto {
+  optional int32 value = 1;
+}
diff --git a/components/sync/engine/non_blocking_sync_common.h b/components/sync/engine/non_blocking_sync_common.h
index 2c2825c0..aeca027 100644
--- a/components/sync/engine/non_blocking_sync_common.h
+++ b/components/sync/engine/non_blocking_sync_common.h
@@ -76,6 +76,10 @@
   ClientTagHash client_tag_hash;
   sync_pb::CommitResponse::ResponseType response_type =
       sync_pb::CommitResponse::TRANSIENT_ERROR;
+
+  // Datatype specific errors (populated only if committed item is of that
+  // datatype).
+  sync_pb::SharingMessageCommitError sharing_message_error;
 };
 
 struct UpdateResponseData {
diff --git a/components/sync/engine_impl/non_blocking_type_commit_contribution.cc b/components/sync/engine_impl/non_blocking_type_commit_contribution.cc
index 3d92226..07c394d 100644
--- a/components/sync/engine_impl/non_blocking_type_commit_contribution.cc
+++ b/components/sync/engine_impl/non_blocking_type_commit_contribution.cc
@@ -17,6 +17,17 @@
 
 namespace syncer {
 
+namespace {
+
+void SetDatatypeError(const sync_pb::CommitResponse_EntryResponse& response,
+                      FailedCommitResponseData* data) {
+  if (response.has_sharing_message_error()) {
+    data->sharing_message_error = response.sharing_message_error();
+  }
+}
+
+}  // namespace
+
 NonBlockingTypeCommitContribution::NonBlockingTypeCommitContribution(
     ModelType type,
     const sync_pb::DataTypeContext& context,
@@ -129,6 +140,7 @@
       response_data.client_tag_hash = commit_request.entity->client_tag_hash;
 
       response_data.response_type = entry_response.response_type();
+      SetDatatypeError(entry_response, &response_data);
       error_response_list.push_back(response_data);
 
       switch (entry_response.response_type()) {
diff --git a/components/sync/engine_impl/non_blocking_type_commit_contribution_unittest.cc b/components/sync/engine_impl/non_blocking_type_commit_contribution_unittest.cc
index 72510e1..3843ba6 100644
--- a/components/sync/engine_impl/non_blocking_type_commit_contribution_unittest.cc
+++ b/components/sync/engine_impl/non_blocking_type_commit_contribution_unittest.cc
@@ -23,6 +23,7 @@
 
 using sync_pb::CommitResponse;
 using sync_pb::EntitySpecifics;
+using sync_pb::SharingMessageCommitError;
 using sync_pb::SyncEntity;
 
 const ClientTagHash kTag = ClientTagHash::FromHashed("tag");
@@ -303,6 +304,10 @@
     sync_pb::CommitResponse_EntryResponse* entry =
         commit_response->add_entryresponse();
     entry->set_response_type(CommitResponse::TRANSIENT_ERROR);
+    SharingMessageCommitError* sharing_message_error =
+        entry->mutable_sharing_message_error();
+    sharing_message_error->set_error_code(
+        SharingMessageCommitError::INVALID_ARGUMENT);
   }
 
   StatusController status;
@@ -313,6 +318,8 @@
   FailedCommitResponseData failed_item = actual_error_response_list[0];
   EXPECT_EQ(ClientTagHash::FromHashed("hash"), failed_item.client_tag_hash);
   EXPECT_EQ(CommitResponse::TRANSIENT_ERROR, failed_item.response_type);
+  EXPECT_EQ(SharingMessageCommitError::INVALID_ARGUMENT,
+            failed_item.sharing_message_error.error_code());
 }
 
 }  // namespace
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
index cb41a95..ce3e11f9 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
@@ -2451,6 +2451,8 @@
   FailedCommitResponseData response_data;
   response_data.client_tag_hash = GetHash("dummy tag");
   response_data.response_type = sync_pb::CommitResponse::TRANSIENT_ERROR;
+  response_data.sharing_message_error.set_error_code(
+      sync_pb::SharingMessageCommitError::INVALID_ARGUMENT);
 
   FailedCommitResponseDataList failed_list;
   failed_list.push_back(response_data);
@@ -2478,6 +2480,8 @@
             actual_error_response_list[0].client_tag_hash);
   EXPECT_EQ(response_data.response_type,
             actual_error_response_list[0].response_type);
+  EXPECT_EQ(response_data.sharing_message_error.error_code(),
+            actual_error_response_list[0].sharing_message_error.error_code());
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
diff --git a/components/sync/protocol/sharing_message_specifics.proto b/components/sync/protocol/sharing_message_specifics.proto
index d4113a9..3da5368 100644
--- a/components/sync/protocol/sharing_message_specifics.proto
+++ b/components/sync/protocol/sharing_message_specifics.proto
@@ -51,3 +51,19 @@
   // SerializeToString.
   optional bytes payload = 3;
 }
+
+// Used for the server to return fine grained commit errors back to the client.
+message SharingMessageCommitError {
+  enum ErrorCode {
+    NONE = 0;
+    INVALID_ARGUMENT = 1;
+    NOT_FOUND = 2;
+    INTERNAL = 3;
+    UNAVAILABLE = 4;
+    RESOURCE_EXHAUSTED = 5;
+    UNAUTHENTICATED = 6;
+    PERMISSION_DENIED = 7;
+  }
+
+  optional ErrorCode error_code = 1;
+}
diff --git a/components/sync/protocol/sync.proto b/components/sync/protocol/sync.proto
index e33722f7..ca34c06 100644
--- a/components/sync/protocol/sync.proto
+++ b/components/sync/protocol/sync.proto
@@ -867,6 +867,11 @@
     // Last modification time (in java time milliseconds).  Allows the server
     // to override the client-supplied mtime during a commit operation.
     optional int64 mtime = 10;
+
+    // Datatype specific error (if any).
+    oneof datatype_error {
+      SharingMessageCommitError sharing_message_error = 11;
+    }
   }
 };
 
diff --git a/components/test/data/autofill_assistant/html/form_target_website.html b/components/test/data/autofill_assistant/html/form_target_website.html
index bc47618..8b0b8ba 100644
--- a/components/test/data/autofill_assistant/html/form_target_website.html
+++ b/components/test/data/autofill_assistant/html/form_target_website.html
@@ -5,9 +5,31 @@
     <meta name="robots" content="noindex, nofollow">
     <title>Form tests.</title>
   </head>
+  <style>
+      .terms {
+        background-color: yellow;
+        height: 5em;
+      }
+  </style>
+  <script>
+    function onClick() {
+      var on = document.getElementById("toggle_on");
+      if (on.style.display === "none") {
+        on.style.display = "block";
+      } else {
+        on.style.display = "none";
+      }
+    }
+  </script>
   <body>
     <h3>Form filling</h3>
 
+    <h4>Terms and conditions</h4>
+    <div id="terms" class="terms">
+      <button id="button" onclick="onClick()"> Button </button>
+      <div id="toggle_on" style="display: none"> Toggle on </div>
+  </div>
+
     <h4>Credit card</h4>
     <div>
       <form id="form1" action="https://example.com/" method="post">
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index a57b8fe0..908200e 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -414,8 +414,14 @@
     current_frame()->output_surface_plane->gpu_fence_id =
         output_surface_->UpdateGpuFence();
 
+  if (overlay_processor_)
+    overlay_processor_->TakeOverlayCandidates(&current_frame()->overlay_list);
+
   FinishDrawingFrame();
 
+  if (overlay_processor_)
+    overlay_processor_->ScheduleOverlays(resource_provider_);
+
   render_passes_in_draw_order->clear();
   render_pass_filters_.clear();
   render_pass_backdrop_filters_.clear();
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index beb7a79d..5587497f 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -721,6 +721,7 @@
   if (no_pending_swaps_callback_ && pending_swaps_ == 0)
     std::move(no_pending_swaps_callback_).Run();
 
+  overlay_processor_->OverlayPresentationComplete();
   if (renderer_)
     renderer_->SwapBuffersComplete();
 
diff --git a/components/viz/service/display/overlay_processor_android.cc b/components/viz/service/display/overlay_processor_android.cc
index d434838..ee7c8455 100644
--- a/components/viz/service/display/overlay_processor_android.cc
+++ b/components/viz/service/display/overlay_processor_android.cc
@@ -6,7 +6,6 @@
 
 #include "base/synchronization/waitable_event.h"
 #include "components/viz/common/quads/stream_video_draw_quad.h"
-#include "components/viz/service/display/display_resource_provider.h"
 #include "components/viz/service/display/overlay_processor_on_gpu.h"
 #include "components/viz/service/display/overlay_strategy_underlay.h"
 #include "components/viz/service/display/skia_output_surface.h"
@@ -89,18 +88,15 @@
   if (!gpu_task_scheduler_)
     return;
 
-  std::vector<
-      std::unique_ptr<DisplayResourceProvider::ScopedReadLockSharedImage>>
-      locks;
+  pending_overlay_locks_.emplace_back();
+  auto& locks = pending_overlay_locks_.back();
   for (auto& candidate : overlay_candidates_) {
-    locks.emplace_back(
-        std::make_unique<DisplayResourceProvider::ScopedReadLockSharedImage>(
-            resource_provider, candidate.resource_id));
+    locks.emplace_back(resource_provider, candidate.resource_id);
   }
 
   std::vector<gpu::SyncToken> locks_sync_tokens;
   for (auto& read_lock : locks)
-    locks_sync_tokens.push_back(read_lock->sync_token());
+    locks_sync_tokens.push_back(read_lock.sync_token());
 
   auto task = base::BindOnce(&OverlayProcessorOnGpu::ScheduleOverlays,
                              base::Unretained(processor_on_gpu_.get()),
@@ -109,6 +105,14 @@
   overlay_candidates_.clear();
 }
 
+void OverlayProcessorAndroid::OverlayPresentationComplete() {
+  // This is a signal from Display::DidReceiveSwapBuffersAck. We use this to
+  // help clear locks on resources from old frame.
+  committed_overlay_locks_.clear();
+  std::swap(committed_overlay_locks_, pending_overlay_locks_.front());
+  pending_overlay_locks_.pop_front();
+}
+
 void OverlayProcessorAndroid::CheckOverlaySupport(
     const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
     OverlayCandidateList* candidates) {
@@ -154,6 +158,12 @@
   return ToEnclosedRect(overlay.display_rect);
 }
 
+void OverlayProcessorAndroid::TakeOverlayCandidates(
+    OverlayCandidateList* candidate_list) {
+  overlay_candidates_.swap(*candidate_list);
+  candidate_list->clear();
+}
+
 void OverlayProcessorAndroid::NotifyOverlayPromotion(
     DisplayResourceProvider* resource_provider,
     const CandidateList& candidates,
diff --git a/components/viz/service/display/overlay_processor_android.h b/components/viz/service/display/overlay_processor_android.h
index b0f53d5c..1063040 100644
--- a/components/viz/service/display/overlay_processor_android.h
+++ b/components/viz/service/display/overlay_processor_android.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_OVERLAY_PROCESSOR_ANDROID_H_
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_OVERLAY_PROCESSOR_ANDROID_H_
 
+#include "base/containers/circular_deque.h"
+#include "components/viz/service/display/display_resource_provider.h"
 #include "components/viz/service/display/overlay_processor_using_strategy.h"
 
 namespace base {
@@ -38,6 +40,8 @@
 
   void ScheduleOverlays(
       DisplayResourceProvider* display_resource_provider) override;
+  void OverlayPresentationComplete() override;
+
   // Override OverlayProcessorUsingStrategy.
   void SetDisplayTransformHint(gfx::OverlayTransform transform) override {}
   void SetViewportSize(const gfx::Size& size) override {}
@@ -55,6 +59,7 @@
   void InitializeOverlayProcessorOnGpu(
       gpu::SharedImageManager* shared_image_manager);
   void DestroyOverlayProcessorOnGpu(base::WaitableEvent* event);
+  void TakeOverlayCandidates(CandidateList* candidate_list) override;
   void NotifyOverlayPromotion(
       DisplayResourceProvider* display_resource_provider,
       const OverlayCandidateList& candidate_list,
@@ -78,6 +83,15 @@
   std::unique_ptr<OverlayProcessorOnGpu> processor_on_gpu_;
 
   OverlayCandidateList overlay_candidates_;
+
+  using OverlayResourceLock =
+      DisplayResourceProvider::ScopedReadLockSharedImage;
+
+  // Locks for overlays are pending for OverlayPresentationComplete.
+  base::circular_deque<std::vector<OverlayResourceLock>> pending_overlay_locks_;
+  // Locks for overlays have been committed. |pending_overlay_locks_| will
+  // be moved to |committed_overlay_locks_| after OverlayPresentationComplete.
+  std::vector<OverlayResourceLock> committed_overlay_locks_;
 };
 
 }  // namespace viz
diff --git a/components/viz/service/display/overlay_processor_interface.cc b/components/viz/service/display/overlay_processor_interface.cc
index 94592d5..e33c591 100644
--- a/components/viz/service/display/overlay_processor_interface.cc
+++ b/components/viz/service/display/overlay_processor_interface.cc
@@ -163,4 +163,6 @@
 void OverlayProcessorInterface::ScheduleOverlays(
     DisplayResourceProvider* display_resource_provider) {}
 
+void OverlayProcessorInterface::OverlayPresentationComplete() {}
+
 }  // namespace viz
diff --git a/components/viz/service/display/overlay_processor_interface.h b/components/viz/service/display/overlay_processor_interface.h
index 74d3376..80f538d 100644
--- a/components/viz/service/display/overlay_processor_interface.h
+++ b/components/viz/service/display/overlay_processor_interface.h
@@ -129,10 +129,21 @@
   virtual void AdjustOutputSurfaceOverlay(
       base::Optional<OutputSurfaceOverlayPlane>* output_surface_plane) = 0;
 
+  // Before the overlay refactor to use OverlayProcessorOnGpu, overlay
+  // candidates are stored inside DirectRenderer. Those overlay candidates are
+  // later sent over to the GPU thread by GLRenderer or SkiaRenderer. This is a
+  // helper function to take these overlay candidates inside overlay processor
+  // to avoid sending over DirectRenderer implementation. This is overridden by
+  // each platform that is ready to send overlay candidates inside overlay
+  // processo. Must be called before ScheduleOverlays().
+  virtual void TakeOverlayCandidates(CandidateList* candidate_list) {}
+
   // TODO(weiliangc): Make it pure virtual after it is implemented by every
   // subclass.
   virtual void ScheduleOverlays(
       DisplayResourceProvider* display_resource_provider);
+  // This is a signal from Display::DidReceiveSwapBuffersAck.
+  virtual void OverlayPresentationComplete();
 
   // These two functions are used by Android SurfaceControl.
   virtual void SetDisplayTransformHint(gfx::OverlayTransform transform) {}
diff --git a/components/viz/service/display/overlay_processor_on_gpu.cc b/components/viz/service/display/overlay_processor_on_gpu.cc
index 89a7494b..a7b28f0 100644
--- a/components/viz/service/display/overlay_processor_on_gpu.cc
+++ b/components/viz/service/display/overlay_processor_on_gpu.cc
@@ -5,6 +5,7 @@
 #include "components/viz/service/display/overlay_processor_on_gpu.h"
 #include "gpu/command_buffer/service/shared_image_factory.h"
 #include "gpu/command_buffer/service/shared_image_manager.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 
 namespace viz {
 
@@ -24,7 +25,28 @@
 void OverlayProcessorOnGpu::ScheduleOverlays(
     CandidateList&& overlay_candidates) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  // TODO(weiliangc): Use shared image to schedule overlays.
+#if defined(OS_ANDROID)
+  // TODO(weiliangc): Currently only implemented for Android Classic code path.
+  for (auto& overlay : overlay_candidates) {
+    auto shared_image_overlay =
+        shared_image_representation_factory_->ProduceOverlay(overlay.mailbox);
+    // When display is re-opened, the first few frames might not have video
+    // resource ready. Possible investigation crbug.com/1023971.
+    if (!shared_image_overlay)
+      continue;
+    // In current implementation, the BeginReadAccess will ends up calling
+    // CodecImage::RenderToOverlay. Currently this code path is only used for
+    // Android Classic video overlay, where update of the overlay plane is
+    // within media code. Since we are not actually passing an overlay plane to
+    // the display controller here, we are able to call EndReadAccess directly
+    // after BeginReadAccess.
+    shared_image_overlay->NotifyOverlayPromotion(
+        true, ToNearestRect(overlay.display_rect));
+    std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess>
+        scoped_access = shared_image_overlay->BeginScopedReadAccess(
+            false /* needs_gl_image */);
+  }
+#endif
 }
 
 #if defined(OS_ANDROID)
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 5e77abac..c55ade3 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -861,7 +861,8 @@
 }
 
 base::ScopedClosureRunner SkiaOutputSurfaceImpl::GetCacheBackBufferCb() {
-  DCHECK(impl_on_gpu_->gl_surface());
+  if (!impl_on_gpu_->gl_surface())
+    return base::ScopedClosureRunner();
   return dependency_->CacheGLSurface(impl_on_gpu_->gl_surface());
 }
 
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index 7cee616..4c5d253f 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -658,7 +658,6 @@
   auto it = cached_back_buffers_.find(cache_id);
   DCHECK(it != cached_back_buffers_.end());
 
-  it->second.RunAndReset();
   cached_back_buffers_.erase(it);
   std::move(callback).Run();
 }
diff --git a/components/viz/test/test_gpu_service_holder.h b/components/viz/test/test_gpu_service_holder.h
index 7a64a82..d69abf2 100644
--- a/components/viz/test/test_gpu_service_holder.h
+++ b/components/viz/test/test_gpu_service_holder.h
@@ -34,12 +34,16 @@
 // implementation and should only be used in tests.
 class TestGpuServiceHolder {
  public:
+  class ScopedResetter {
+   public:
+    ~ScopedResetter() { TestGpuServiceHolder::ResetInstance(); }
+  };
   // Exposes a singleton to allow easy sharing of the GpuServiceImpl by
   // different clients (e.g. to share SharedImages via a common
   // SharedImageManager).
   //
   // The instance will parse GpuPreferences from the command line when it is
-  // first created (e.g. to allow entire test suite with --enable-vulkan).
+  // first created (e.g. to allow entire test suite with --use-vulkan).
   //
   // If specific feature flags or GpuPreferences are needed for a specific test,
   // a separate instance of this class can be created.
diff --git a/components/zucchini/disassembler_elf.cc b/components/zucchini/disassembler_elf.cc
index ad2cafd1..4727d1c 100644
--- a/components/zucchini/disassembler_elf.cc
+++ b/components/zucchini/disassembler_elf.cc
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/numerics/checked_math.h"
 #include "base/numerics/safe_conversions.h"
 #include "components/zucchini/abs32_utils.h"
 #include "components/zucchini/algorithm.h"
@@ -275,10 +276,16 @@
   // Visits |segments_| to get estimate on |offset_bound|.
   for (const typename Traits::Elf_Phdr* segment = segments_;
        segment != segments_ + segments_count_; ++segment) {
-    if (!image_.covers({segment->p_offset, segment->p_filesz}))
+    // |image_.covers()| is a sufficient check except when size_t is 32 bit and
+    // parsing ELF64. In such cases a value-in-range check is needed on the
+    // segment. This fixes crbug/1035603.
+    offset_t segment_end;
+    base::CheckedNumeric<offset_t> checked_segment_end = segment->p_offset;
+    checked_segment_end += segment->p_filesz;
+    if (!checked_segment_end.AssignIfValid(&segment_end) ||
+        !image_.covers({segment->p_offset, segment->p_filesz})) {
       return false;
-    offset_t segment_end =
-        base::checked_cast<offset_t>(segment->p_offset + segment->p_filesz);
+    }
     offset_bound = std::max(offset_bound, segment_end);
   }
 
diff --git a/content/browser/android/content_feature_list.cc b/content/browser/android/content_feature_list.cc
index 7fba4a3..d5feae1 100644
--- a/content/browser/android/content_feature_list.cc
+++ b/content/browser/android/content_feature_list.cc
@@ -24,6 +24,7 @@
 const base::Feature* kFeaturesExposedToJava[] = {
     &features::kBackgroundMediaRendererHasModerateBinding,
     &features::kWebNfc,
+    &features::kWebXrPermissionsApi,
     &kServiceGroupImportance,
 };
 
diff --git a/content/browser/devtools/protocol/emulation_handler.cc b/content/browser/devtools/protocol/emulation_handler.cc
index 8af406e..f7b9bdf 100644
--- a/content/browser/devtools/protocol/emulation_handler.cc
+++ b/content/browser/devtools/protocol/emulation_handler.cc
@@ -229,7 +229,7 @@
       blink::WebSize(screen_width.fromMaybe(0), screen_height.fromMaybe(0));
   if (position_x.isJust() && position_y.isJust()) {
     params.view_position =
-        blink::WebPoint(position_x.fromMaybe(0), position_y.fromMaybe(0));
+        gfx::Point(position_x.fromMaybe(0), position_y.fromMaybe(0));
   }
   params.device_scale_factor = device_scale_factor;
   params.view_size = blink::WebSize(width, height);
diff --git a/content/browser/devtools/protocol/service_worker_handler.cc b/content/browser/devtools/protocol/service_worker_handler.cc
index ab29fea..be5b54a 100644
--- a/content/browser/devtools/protocol/service_worker_handler.cc
+++ b/content/browser/devtools/protocol/service_worker_handler.cc
@@ -32,8 +32,8 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -438,7 +438,7 @@
 
     for (const auto& client : version.clients) {
       if (client.second.type ==
-          blink::mojom::ServiceWorkerProviderType::kForWindow) {
+          blink::mojom::ServiceWorkerContainerType::kForWindow) {
         // A navigation may not yet be associated with a RenderFrameHost. Use
         // the |web_contents_getter| instead.
         WebContents* web_contents =
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index 44d3b89..892a8ce9 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -10264,4 +10264,163 @@
   EXPECT_FALSE(abort_observer.has_committed());
 }
 
+namespace {
+
+// A request handler that returns a 301 or 307 response to /redirect-301 or
+// /redirect-307 respectively, redirecting to the URL provided in the query
+// string. The error codes 301 and 307 are used as examples of codes that switch
+// the method to GET across the redirect (301) versus those that preserve the
+// method (307).
+std::unique_ptr<net::test_server::HttpResponse> HandleRedirect(
+    const net::test_server::HttpRequest& request) {
+  GURL url = request.base_url.Resolve(request.relative_url);
+  std::unique_ptr<net::test_server::BasicHttpResponse> response =
+      std::make_unique<net::test_server::BasicHttpResponse>();
+  if (url.path() == "/redirect-301") {
+    response->set_code(net::HTTP_MOVED_PERMANENTLY);
+  } else if (url.path() == "/redirect-307") {
+    response->set_code(net::HTTP_TEMPORARY_REDIRECT);
+  } else {
+    return nullptr;
+  }
+  response->AddCustomHeader("Location", url.query());
+  return std::move(response);
+}
+
+// A request handler that returns a simple page for requests using |method| to
+// /handle-method-only, and closes the socket for any other method.
+std::unique_ptr<net::test_server::HttpResponse> HandleMethodOnly(
+    net::test_server::HttpMethod method,
+    const net::test_server::HttpRequest& request) {
+  if (request.relative_url != "/handle-method-only")
+    return nullptr;
+
+  if (request.method != method) {
+    return std::make_unique<net::test_server::RawHttpResponse>("", "");
+  }
+  std::unique_ptr<net::test_server::BasicHttpResponse> response =
+      std::make_unique<net::test_server::BasicHttpResponse>();
+  response->set_content_type("text/html");
+  response->set_content("Success!");
+  return response;
+}
+
+}  // namespace
+
+// Tests that the navigation entry's method is updated to GET when following a
+// 301 redirect that encounters an error page. See https://crbug.com/1041597.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTestNoServer,
+                       UpdateMethodOn301RedirectError) {
+  // Install a request handler to serve 301 or 307 redirects. In this test,
+  // which uses a 301 redirect, the default handler for "/server-redirect?..."
+  // would work. But this test still installs a custom request handler anyway
+  // just for symmetry with the PreserveMethodOn307RedirectError test below,
+  // which requires a custom handler to serve 307 responses.
+  embedded_test_server()->RegisterRequestHandler(
+      base::BindRepeating(&HandleRedirect));
+  // HandleMethodOnly serves the final endpoint that the test ends up at. It
+  // lets the test distinguish a GET from a POST by serving a response only for
+  // POST requests.
+  embedded_test_server()->RegisterRequestHandler(
+      base::BindRepeating(&HandleMethodOnly, net::test_server::METHOD_POST));
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  GURL start_url(embedded_test_server()->GetURL(
+      "/navigation_controller/simple_page_1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), start_url));
+
+  GURL post_only_url = embedded_test_server()->GetURL("/handle-method-only");
+  GURL form_action_url(
+      embedded_test_server()->GetURL("/redirect-301?" + post_only_url.spec()));
+
+  // Inject a form into the page and submit it, to create a POST request to
+  // |form_action_url|. This POST request will redirect to |post_only_url|. The
+  // request's method should change to GET while following the redirect,
+  // resulting in an error page since |post_only_url| closes the connection on
+  // GETs.
+  TestNavigationObserver form_nav_observer(shell()->web_contents(), 1);
+  EXPECT_TRUE(ExecJs(shell()->web_contents(),
+                     JsReplace("var form = document.createElement('form');"
+                               "form.method = 'POST';"
+                               "form.action = $1;"
+                               "document.body.appendChild(form);"
+                               "form.submit();",
+                               form_action_url.spec())));
+  form_nav_observer.Wait();
+  NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
+      shell()->web_contents()->GetController());
+  NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
+  EXPECT_EQ(post_only_url, entry->GetURL());
+  EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
+  EXPECT_FALSE(entry->GetHasPostData());
+
+  // When the error page is reloaded, the method should still be GET, resulting
+  // in an error page. If https://crbug.com/1041597 regresses, the
+  // NavigationEntry's method would be POST and this test would fail, seeing a
+  // successful response instead of an error page from |post_only_url|.
+  TestNavigationObserver reload_observer(shell()->web_contents(), 1);
+  // Set |check_for_repost| to false to avoid hanging the test if the method is
+  // improperly set to POST.
+  controller.Reload(ReloadType::NORMAL, false);
+  reload_observer.Wait();
+  entry = controller.GetLastCommittedEntry();
+  EXPECT_EQ(post_only_url, entry->GetURL());
+  EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
+}
+
+// Tests that the navigation entry's method is preserved as POST when following
+// a 307 redirect that encounters an error page. This test is similar to the
+// above UpdateMethodOn301RedirectError, but reversed: in this test, the method
+// should be preserved as POST.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTestNoServer,
+                       UpdateMethodOn307RedirectError) {
+  embedded_test_server()->RegisterRequestHandler(
+      base::BindRepeating(&HandleRedirect));
+  // HandleMethodOnly serves the final endpoint that the test ends up at. It
+  // lets the test distinguish a GET from a POST by serving a response only for
+  // GET requests.
+  embedded_test_server()->RegisterRequestHandler(
+      base::BindRepeating(&HandleMethodOnly, net::test_server::METHOD_GET));
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  GURL start_url(embedded_test_server()->GetURL(
+      "/navigation_controller/simple_page_1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), start_url));
+
+  GURL get_only_url = embedded_test_server()->GetURL("/handle-method-only");
+  GURL form_action_url(
+      embedded_test_server()->GetURL("/redirect-307?" + get_only_url.spec()));
+
+  // Inject a form into the page and submit it, to create a POST request to
+  // |form_action_url|. This POST request will redirect to |get_only_url|. The
+  // request's method should stay as POST while following the redirect,
+  // resulting in an error page since |get_only_url| closes the connection on
+  // POSTs.
+  TestNavigationObserver form_nav_observer(shell()->web_contents(), 1);
+  EXPECT_TRUE(ExecJs(shell()->web_contents(),
+                     JsReplace("var form = document.createElement('form');"
+                               "form.method = 'POST';"
+                               "form.action = $1;"
+                               "document.body.appendChild(form);"
+                               "form.submit();",
+                               form_action_url.spec())));
+  form_nav_observer.Wait();
+  NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
+      shell()->web_contents()->GetController());
+  NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
+  EXPECT_EQ(get_only_url, entry->GetURL());
+  EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
+  EXPECT_TRUE(entry->GetHasPostData());
+
+  // When the error page is reloaded, the method should still be POST, resulting
+  // in an error page.
+  TestNavigationObserver reload_observer(shell()->web_contents(), 1);
+  // Set |check_for_repost| to false to avoid hanging the test with the prompt.
+  controller.Reload(ReloadType::NORMAL, false);
+  reload_observer.Wait();
+  entry = controller.GetLastCommittedEntry();
+  EXPECT_EQ(get_only_url, entry->GetURL());
+  EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_proxy_host.cc b/content/browser/frame_host/render_frame_proxy_host.cc
index 453bee4..ad7999e 100644
--- a/content/browser/frame_host/render_frame_proxy_host.cc
+++ b/content/browser/frame_host/render_frame_proxy_host.cc
@@ -389,6 +389,15 @@
       frame_tree_node_->navigator()->GetController()->GetWebContents(),
       current_rfh, params.user_gesture, &download_policy);
 
+  if ((frame_tree_node_->pending_frame_policy().sandbox_flags &
+       blink::WebSandboxFlags::kDownloads) != blink::WebSandboxFlags::kNone) {
+    if (download_policy.blocking_downloads_in_sandbox_enabled) {
+      download_policy.SetDisallowed(content::NavigationDownloadType::kSandbox);
+    } else {
+      download_policy.SetAllowed(content::NavigationDownloadType::kSandbox);
+    }
+  }
+
   // TODO(lfg, lukasza): Remove |extra_headers| parameter from
   // RequestTransferURL method once both RenderFrameProxyHost and
   // RenderFrameHostImpl call RequestOpenURL from their OnOpenURL handlers.
diff --git a/content/browser/payments/payment_app_provider_impl.cc b/content/browser/payments/payment_app_provider_impl.cc
index 3b813cb..dd962f5 100644
--- a/content/browser/payments/payment_app_provider_impl.cc
+++ b/content/browser/payments/payment_app_provider_impl.cc
@@ -34,7 +34,7 @@
 #include "mojo/public/mojom/base/time.mojom.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/image/image.h"
 
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 380d518..77072992 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -888,4 +888,9 @@
   cached_back_buffer_.reset();
 }
 
+void CompositorImpl::RequestPresentationTimeForNextFrame(
+    PresentationTimeCallback callback) {
+  host_->RequestPresentationTimeForNextFrame(std::move(callback));
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index 510ca6b..16c4b28 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -104,6 +104,8 @@
   ui::ResourceManager& GetResourceManager() override;
   void CacheBackBufferForCurrentSurface() override;
   void EvictCachedBackBuffer() override;
+  void RequestPresentationTimeForNextFrame(
+      PresentationTimeCallback callback) override;
 
   // LayerTreeHostClient implementation.
   void WillBeginMainFrame() override {}
diff --git a/content/browser/renderer_host/compositor_impl_android_browsertest.cc b/content/browser/renderer_host/compositor_impl_android_browsertest.cc
index a125fa3..74fe27f 100644
--- a/content/browser/renderer_host/compositor_impl_android_browsertest.cc
+++ b/content/browser/renderer_host/compositor_impl_android_browsertest.cc
@@ -227,6 +227,32 @@
   CompositorSwapRunLoop(compositor_impl()).RunUntilSwap();
 }
 
+// This test waits for a presentation feedback token to arrive from the GPU. If
+// this test is timing out then it demonstrates a bug.
+IN_PROC_BROWSER_TEST_P(CompositorImplBrowserTest,
+                       CompositorImplReceivesPresentationTimeCallbacks) {
+  // OOP-R is required for this test to succeed with SkDDL, but is disabled on
+  // Android L and lower.
+  if (GetParam() == CompositorImplMode::kSkiaRenderer &&
+      base::android::BuildInfo::GetInstance()->sdk_int() <
+          base::android::SDK_VERSION_MARSHMALLOW) {
+    return;
+  }
+
+  // Presentation feedback occurs after the GPU has presented content to the
+  // display. This is later than the buffers swap.
+  base::RunLoop loop;
+  // The callback will cancel the loop used to wait.
+  static_cast<content::Compositor*>(compositor_impl())
+      ->RequestPresentationTimeForNextFrame(base::BindOnce(
+          [](base::OnceClosure quit,
+             const gfx::PresentationFeedback& feedback) {
+            std::move(quit).Run();
+          },
+          loop.QuitClosure()));
+  loop.Run();
+}
+
 class CompositorImplBrowserTestRefreshRate
     : public CompositorImplBrowserTest,
       public ui::WindowAndroid::TestHooks {
diff --git a/content/browser/renderer_host/media/media_devices_manager.cc b/content/browser/renderer_host/media/media_devices_manager.cc
index 98db846..6748993 100644
--- a/content/browser/renderer_host/media/media_devices_manager.cc
+++ b/content/browser/renderer_host/media/media_devices_manager.cc
@@ -60,6 +60,10 @@
 // Frame rates for sources with no support for capability enumeration.
 const uint16_t kFallbackVideoFrameRates[] = {30, 60};
 
+void SendLogMessage(const std::string& message) {
+  MediaStreamManager::SendMessageToNativeLog("MDM::" + message);
+}
+
 const char* DeviceTypeToString(blink::MediaDeviceType device_type) {
   switch (device_type) {
     case blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT:
@@ -74,18 +78,19 @@
   return "UNKNOWN";
 }
 
-// Private helper method to generate a string for the log message that lists the
-// human readable names of |devices|.
-std::string GetLogMessageString(
+std::string GetDevicesEnumeratedLogString(
     blink::MediaDeviceType device_type,
     const blink::WebMediaDeviceInfoArray& device_infos) {
-  std::string output_string = base::StringPrintf(
-      "Getting devices of type %s:\n", DeviceTypeToString(device_type));
-  if (device_infos.empty())
-    return output_string + "No devices found.";
+  std::string str = base::StringPrintf("DevicesEnumerated({type=%s}, ",
+                                       DeviceTypeToString(device_type));
+  base::StringAppendF(&str, "{labels=[");
   for (const auto& device_info : device_infos)
-    output_string += "  " + device_info.label + "\n";
-  return output_string;
+    base::StringAppendF(&str, "%s, ", device_info.label.c_str());
+  if (!str.empty()) {
+    str.erase(str.end() - 2, str.end());
+  }
+  str += "])";
+  return str;
 }
 
 blink::WebMediaDeviceInfoArray GetFakeAudioDevices(bool is_input) {
@@ -386,6 +391,7 @@
   DCHECK(video_capture_manager_.get());
   DCHECK(!stop_removed_input_device_cb_.is_null());
   DCHECK(!ui_input_device_change_cb_.is_null());
+  SendLogMessage("MediaDevicesManager()");
   cache_policies_.fill(CachePolicy::NO_CACHE);
   has_seen_result_.fill(false);
 }
@@ -427,6 +433,12 @@
   DCHECK(request_audio_input_capabilities &&
              requested_types[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] ||
          !request_audio_input_capabilities);
+  SendLogMessage(base::StringPrintf(
+      "EnumerateDevices({render_process_id=%d}, {render_frame_id=%d}, "
+      "{request_audio=%s}, {request_video=%s})",
+      render_process_id, render_frame_id,
+      request_audio_input_capabilities ? "true" : "false",
+      request_video_input_capabilities ? "true" : "false"));
 
   base::PostTaskAndReplyWithResult(
       base::CreateSingleThreadTaskRunner({BrowserThread::UI}).get(), FROM_HERE,
@@ -502,6 +514,7 @@
         std::make_unique<AudioServiceDeviceListener>();
   }
 #endif
+  SendLogMessage("StartMonitoring()");
   monitoring_started_ = true;
   base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
 
@@ -534,6 +547,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!monitoring_started_)
     return;
+  SendLogMessage(base::StringPrintf("StopMonitoring([this=%p])", this));
   base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
   audio_service_device_listener_.reset();
   monitoring_started_ = false;
@@ -838,6 +852,8 @@
   CacheInfo& cache_info = cache_infos_[type];
   if (cache_info.is_update_ongoing())
     return;
+  SendLogMessage(base::StringPrintf("DoEnumerateDevices({type=%s})",
+                                    DeviceTypeToString(type)));
 
   cache_info.UpdateStarted();
   switch (type) {
@@ -907,10 +923,7 @@
   UpdateSnapshot(type, snapshot);
   cache_infos_[type].UpdateCompleted();
   has_seen_result_[type] = true;
-
-  std::string log_message =
-      "New device enumeration result:\n" + GetLogMessageString(type, snapshot);
-  MediaStreamManager::SendMessageToNativeLog(log_message);
+  SendLogMessage(GetDevicesEnumeratedLogString(type, snapshot));
 
   if (cache_policies_[type] == CachePolicy::NO_CACHE) {
     for (auto& request : requests_)
@@ -1028,6 +1041,10 @@
 void MediaDevicesManager::HandleDevicesChanged(blink::MediaDeviceType type) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(IsValidMediaDeviceType(type));
+  if (!cache_infos_[type].is_update_ongoing()) {
+    SendLogMessage(base::StringPrintf("HandleDevicesChanged({type=%s}",
+                                      DeviceTypeToString(type)));
+  }
   cache_infos_[type].InvalidateCache();
   DoEnumerateDevices(type);
 }
@@ -1129,6 +1146,9 @@
   auto it = subscriptions_.find(subscription_id);
   if (it == subscriptions_.end())
     return;
+  SendLogMessage(
+      base::StringPrintf("NotifyDeviceChange({subscription_id=%u}, {type=%s}",
+                         subscription_id, DeviceTypeToString(type)));
 
   const SubscriptionRequest& request = it->second;
   request.listener_->OnDevicesChanged(
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index e28ed56..fb1ae0a 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -188,6 +188,169 @@
   return blink::NUM_MEDIA_DEVICE_TYPES;
 }
 
+const char* DeviceTypeToString(blink::MediaDeviceType type) {
+  switch (type) {
+    case blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT:
+      return "DEVICE_AUDIO_INPUT";
+    case blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT:
+      return "DEVICE_AUDIO_OUTPUT";
+    case blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT:
+      return "DEVICE_VIDEO_INPUT";
+    default:
+      NOTREACHED();
+  }
+  return "INVALID";
+}
+
+const char* RequestTypeToString(blink::MediaStreamRequestType type) {
+  switch (type) {
+    case blink::MEDIA_DEVICE_ACCESS:
+      return "MEDIA_DEVICE_ACCESS";
+    case blink::MEDIA_DEVICE_UPDATE:
+      return "MEDIA_DEVICE_UPDATE";
+    case blink::MEDIA_GENERATE_STREAM:
+      return "MEDIA_GENERATE_STREAM";
+    case blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY:
+      return "MEDIA_OPEN_DEVICE_PEPPER_ONLY";
+    default:
+      NOTREACHED();
+  }
+  return "INVALID";
+}
+
+const char* StreamTypeToString(blink::mojom::MediaStreamType type) {
+  switch (type) {
+    case blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE:
+      return "DEVICE_AUDIO_CAPTURE";
+    case blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE:
+      return "DEVICE_VIDEO_CAPTURE";
+    case blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE:
+      return "GUM_TAB_AUDIO_CAPTURE";
+    case blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE:
+      return "GUM_TAB_VIDEO_CAPTURE";
+    case blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE:
+      return "GUM_DESKTOP_AUDIO_CAPTURE";
+    case blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE:
+      return "GUM_DESKTOP_VIDEO_CAPTURE";
+    case blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE:
+      return "DISPLAY_AUDIO_CAPTURE";
+    case blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE:
+      return "DISPLAY_VIDEO_CAPTURE";
+    case blink::mojom::MediaStreamType::NO_SERVICE:
+      return "NO_SERVICE";
+    case blink::mojom::MediaStreamType::NUM_MEDIA_TYPES:
+      return "NUM_MEDIA_TYPES";
+    default:
+      NOTREACHED();
+  }
+  return "INVALID";
+}
+
+const char* RequestStateToString(MediaRequestState state) {
+  switch (state) {
+    case MEDIA_REQUEST_STATE_NOT_REQUESTED:
+      return "STATE_NOT_REQUESTED";
+    case MEDIA_REQUEST_STATE_REQUESTED:
+      return "STATE_REQUESTED";
+    case MEDIA_REQUEST_STATE_PENDING_APPROVAL:
+      return "STATE_PENDING_APPROVAL";
+    case MEDIA_REQUEST_STATE_OPENING:
+      return "STATE_OPENING";
+    case MEDIA_REQUEST_STATE_DONE:
+      return "STATE_DONE";
+    case MEDIA_REQUEST_STATE_CLOSING:
+      return "STATE_CLOSING";
+    case MEDIA_REQUEST_STATE_ERROR:
+      return "STATE_ERROR";
+    default:
+      NOTREACHED();
+  }
+  return "INVALID";
+}
+
+const char* RequestResultToString(
+    blink::mojom::MediaStreamRequestResult result) {
+  switch (result) {
+    case blink::mojom::MediaStreamRequestResult::OK:
+      return "OK";
+    case blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED:
+      return "PERMISSION_DENIED";
+    case blink::mojom::MediaStreamRequestResult::PERMISSION_DISMISSED:
+      return "PERMISSION_DISMISSED";
+    case blink::mojom::MediaStreamRequestResult::INVALID_STATE:
+      return "INVALID_STATE";
+    case blink::mojom::MediaStreamRequestResult::NO_HARDWARE:
+      return "NO_HARDWARE";
+    case blink::mojom::MediaStreamRequestResult::INVALID_SECURITY_ORIGIN:
+      return "INVALID_SECURITY_ORIGIN";
+    case blink::mojom::MediaStreamRequestResult::TAB_CAPTURE_FAILURE:
+      return "INVALID_STATE";
+    case blink::mojom::MediaStreamRequestResult::SCREEN_CAPTURE_FAILURE:
+      return "TAB_CAPTURE_FAILURE";
+    case blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE:
+      return "CAPTURE_FAILURE";
+    case blink::mojom::MediaStreamRequestResult::CONSTRAINT_NOT_SATISFIED:
+      return "CONSTRAINT_NOT_SATISFIED";
+    case blink::mojom::MediaStreamRequestResult::TRACK_START_FAILURE_AUDIO:
+      return "TRACK_START_FAILURE_AUDIO";
+    case blink::mojom::MediaStreamRequestResult::TRACK_START_FAILURE_VIDEO:
+      return "TRACK_START_FAILURE_VIDEO";
+    case blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED:
+      return "NOT_SUPPORTED";
+    case blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN:
+      return "FAILED_DUE_TO_SHUTDOWN";
+    case blink::mojom::MediaStreamRequestResult::KILL_SWITCH_ON:
+      return "KILL_SWITCH_ON";
+    case blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED:
+      return "SYSTEM_PERMISSION_DENIED";
+    case blink::mojom::MediaStreamRequestResult::NUM_MEDIA_REQUEST_RESULTS:
+      return "NUM_MEDIA_REQUEST_RESULTS";
+    default:
+      NOTREACHED();
+  }
+  return "INVALID";
+}
+
+std::string GetGenerateStreamLogString(int render_process_id,
+                                       int render_frame_id,
+                                       int requester_id,
+                                       int page_request_id) {
+  return base::StringPrintf(
+      "GenerateStream({render_process_id=%d}, {render_frame_id=%d}, "
+      "{requester_id=%d}, {page_request_id=%d})",
+      render_process_id, render_frame_id, requester_id, page_request_id);
+}
+
+std::string GetOpenDeviceLogString(int render_process_id,
+                                   int render_frame_id,
+                                   int requester_id,
+                                   int page_request_id,
+                                   const std::string& device_id,
+                                   MediaStreamType type) {
+  return base::StringPrintf(
+      "OpenDevice({render_process_id=%d}, {render_frame_id=%d}, "
+      "{requester_id=%d}, {page_request_id=%d}, {device_id=%s}, {type=%s})",
+      render_process_id, render_frame_id, requester_id, page_request_id,
+      device_id.c_str(), StreamTypeToString(type));
+}
+
+std::string GetStopStreamDeviceLogString(
+    int render_process_id,
+    int render_frame_id,
+    int requester_id,
+    const std::string& device_id,
+    const base::UnguessableToken& session_id) {
+  return base::StringPrintf(
+      "StopStreamDevice({render_process_id=%d}, {render_frame_id=%d}, "
+      "{requester_id=%d}, {device_id=%s}, {session_id=%s})",
+      render_process_id, render_frame_id, requester_id, device_id.c_str(),
+      session_id.ToString().c_str());
+}
+
+void SendLogMessage(const std::string& message) {
+  MediaStreamManager::SendMessageToNativeLog("MSM::" + message);
+}
+
 void SendVideoCaptureLogMessage(const std::string& message) {
   MediaStreamManager::SendMessageToNativeLog("video capture: " + message);
 }
@@ -307,7 +470,13 @@
         audio_type_(MediaStreamType::NO_SERVICE),
         video_type_(MediaStreamType::NO_SERVICE),
         target_process_id_(-1),
-        target_frame_id_(-1) {}
+        target_frame_id_(-1) {
+    SendLogMessage(base::StringPrintf(
+        "DR::DeviceRequest({requesting_process_id=%d}, "
+        "{requesting_frame_id=%d}, {requester_id=%d}, {request_type=%s})",
+        requesting_process_id, requesting_frame_id, requester_id,
+        RequestTypeToString(request_type)));
+  }
 
   ~DeviceRequest() { RunMojoCallbacks(); }
 
@@ -317,6 +486,9 @@
   void SetAudioType(MediaStreamType audio_type) {
     DCHECK(blink::IsAudioInputMediaType(audio_type) ||
            audio_type == MediaStreamType::NO_SERVICE);
+    SendLogMessage(base::StringPrintf(
+        "DR::SetAudioType([requester_id=%d] {audio_type=%s})", requester_id,
+        StreamTypeToString(audio_type)));
     audio_type_ = audio_type;
   }
 
@@ -335,6 +507,11 @@
   void CreateUIRequest(const std::string& requested_audio_device_id,
                        const std::string& requested_video_device_id) {
     DCHECK(!ui_request_);
+    SendLogMessage(base::StringPrintf(
+        "DR::CreateUIRequest([requester_id=%d] {requested_audio_device_id=%s}, "
+        "{requested_video_device_id=%s})",
+        requester_id, requested_audio_device_id.c_str(),
+        requested_video_device_id.c_str()));
     target_process_id_ = requesting_process_id;
     target_frame_id_ = requesting_frame_id;
     ui_request_.reset(new MediaStreamRequest(
@@ -364,6 +541,11 @@
 
   // Update the request state and notify observers.
   void SetState(MediaStreamType stream_type, MediaRequestState new_state) {
+    SendLogMessage(base::StringPrintf(
+        "DR::SetState([requester_id=%d] {stream_type=%s}, {new_state=%s})",
+        requester_id, StreamTypeToString(stream_type),
+        RequestStateToString(new_state)));
+
     if (stream_type == MediaStreamType::NUM_MEDIA_TYPES) {
       for (int i = static_cast<int>(MediaStreamType::NO_SERVICE) + 1;
            i < static_cast<int>(MediaStreamType::NUM_MEDIA_TYPES); ++i) {
@@ -505,7 +687,9 @@
 MediaStreamManager::MediaStreamManager(
     media::AudioSystem* audio_system,
     scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner)
-    : MediaStreamManager(audio_system, std::move(audio_task_runner), nullptr) {}
+    : MediaStreamManager(audio_system, std::move(audio_task_runner), nullptr) {
+  SendLogMessage(base::StringPrintf("MediaStreamManager([this=%p]))", this));
+}
 
 MediaStreamManager::MediaStreamManager(
     media::AudioSystem* audio_system,
@@ -586,7 +770,6 @@
 
 MediaStreamManager::~MediaStreamManager() {
   DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::IO));
-  DVLOG(1) << "~MediaStreamManager";
   DCHECK(requests_.empty());
 
   base::PowerMonitor::RemoveObserver(this);
@@ -684,7 +867,8 @@
     DeviceStoppedCallback device_stopped_cb,
     DeviceChangedCallback device_changed_cb) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DVLOG(1) << "GenerateStream()";
+  SendLogMessage(GetGenerateStreamLogString(render_process_id, render_frame_id,
+                                            requester_id, page_request_id));
 
   DeviceRequest* request = new DeviceRequest(
       render_process_id, render_frame_id, requester_id, page_request_id,
@@ -739,7 +923,8 @@
 
 void MediaStreamManager::CancelRequest(const std::string& label) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DVLOG(1) << "CancelRequest({label = " << label << "})";
+  SendLogMessage(
+      base::StringPrintf("CancelRequest({label=%s})", label.c_str()));
   DeviceRequest* request = FindRequest(label);
   if (!request) {
     // The request does not exist.
@@ -790,9 +975,9 @@
     const std::string& device_id,
     const base::UnguessableToken& session_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DVLOG(1) << "StopStreamDevice({render_frame_id = " << render_frame_id << "} "
-           << ", {device_id = " << device_id << "}, session_id = " << session_id
-           << "})";
+  SendLogMessage(GetStopStreamDeviceLogString(
+      render_process_id, render_frame_id, requester_id, device_id, session_id));
+
   // Find the first request for this |render_process_id| and |render_frame_id|
   // of type MEDIA_GENERATE_STREAM that has requested to use |device_id| and
   // stop it.
@@ -833,9 +1018,9 @@
 void MediaStreamManager::StopDevice(MediaStreamType type,
                                     const base::UnguessableToken& session_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DVLOG(1) << "StopDevice"
-           << "{type = " << type << "}"
-           << "{session_id = " << session_id << "}";
+  SendLogMessage(base::StringPrintf("StopDevice({type=%s}, {session_id=%s})",
+                                    StreamTypeToString(type),
+                                    session_id.ToString().c_str()));
   auto request_it = requests_.begin();
   while (request_it != requests_.end()) {
     DeviceRequest* request = request_it->second.get();
@@ -873,9 +1058,9 @@
 void MediaStreamManager::CloseDevice(MediaStreamType type,
                                      const base::UnguessableToken& session_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DVLOG(1) << "CloseDevice("
-           << "{type = " << type << "} "
-           << "{session_id = " << session_id << "})";
+  SendLogMessage(base::StringPrintf("CloseDevice({type=%s}, {session_id=%s})",
+                                    StreamTypeToString(type),
+                                    session_id.ToString().c_str()));
   GetDeviceManager(type)->Close(session_id);
 
   for (const LabeledDeviceRequest& labeled_request : requests_) {
@@ -902,7 +1087,9 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(type == MediaStreamType::DEVICE_AUDIO_CAPTURE ||
          type == MediaStreamType::DEVICE_VIDEO_CAPTURE);
-  DVLOG(1) << "OpenDevice ({page_request_id = " << page_request_id << "})";
+  SendLogMessage(GetOpenDeviceLogString(render_process_id, render_frame_id,
+                                        requester_id, page_request_id,
+                                        device_id, type));
   StreamControls controls;
   if (blink::IsAudioInputMediaType(type)) {
     controls.audio.requested = true;
@@ -972,6 +1159,12 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(type == blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT ||
          type == blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT);
+  SendLogMessage(base::StringPrintf(
+                     "StopRemovedDevice({type=%s}, {device=[id: %s, name: %s]}",
+                     DeviceTypeToString(type),
+                     media_device_info.device_id.c_str(),
+                     media_device_info.label.c_str())
+                     .c_str());
 
   MediaStreamType stream_type = ConvertToMediaStreamType(type);
   std::vector<base::UnguessableToken> session_ids;
@@ -991,14 +1184,6 @@
   }
   for (const auto& session_id : session_ids)
     StopDevice(stream_type, session_id);
-
-  AddLogMessageOnIOThread(
-      base::StringPrintf(
-          "Media input device removed: type=%s, id=%s, name=%s ",
-          (stream_type == MediaStreamType::DEVICE_AUDIO_CAPTURE ? "audio"
-                                                                : "video"),
-          media_device_info.device_id.c_str(), media_device_info.label.c_str())
-          .c_str());
 }
 
 bool MediaStreamManager::PickDeviceId(
@@ -1054,6 +1239,9 @@
 void MediaStreamManager::StartEnumeration(DeviceRequest* request,
                                           const std::string& label) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  SendLogMessage(
+      base::StringPrintf("StartEnumeration({requester_id=%d}, {label=%s})",
+                         request->requester_id, label.c_str()));
 
   // Start monitoring the devices when doing the first enumeration.
   media_devices_manager_->StartMonitoring();
@@ -1094,6 +1282,9 @@
     unique_label = RandomLabel();
   } while (FindRequest(unique_label) != nullptr);
 
+  SendLogMessage(
+      base::StringPrintf("AddRequest([requester_id=%d]) => (label=%s)",
+                         request->requester_id, unique_label.c_str()));
   requests_.push_back(std::make_pair(unique_label, std::move(request)));
 
   return unique_label;
@@ -1111,7 +1302,8 @@
 
 void MediaStreamManager::DeleteRequest(const std::string& label) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DVLOG(1) << "DeleteRequest({label= " << label << "})";
+  SendLogMessage(
+      base::StringPrintf("DeleteRequest([label=%s])", label.c_str()));
   for (auto request_it = requests_.begin(); request_it != requests_.end();
        ++request_it) {
     if (request_it->first == label) {
@@ -1152,12 +1344,12 @@
     const base::Optional<media::AudioParameters>& output_parameters) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!output_parameters || output_parameters->IsValid());
-  DVLOG(1) << "PostRequestToUI({label= " << label << "})";
-
   DeviceRequest* request = FindRequest(label);
   if (!request)
     return;
   DCHECK(request->HasUIRequest());
+  SendLogMessage(
+      base::StringPrintf("PostRequestToUI({label=%s}, ", label.c_str()));
 
   const MediaStreamType audio_type = request->audio_type();
   const MediaStreamType video_type = request->video_type();
@@ -1224,6 +1416,9 @@
     DVLOG(1) << "SetUpRequest label " << label << " doesn't exist!!";
     return;  // This can happen if the request has been canceled.
   }
+  SendLogMessage(
+      base::StringPrintf("SetUpRequest([requester_id=%d] {label=%s})",
+                         request->requester_id, label.c_str()));
 
   request->SetAudioType(request->controls.audio.stream_type);
   request->SetVideoType(request->controls.video.stream_type);
@@ -1300,6 +1495,8 @@
           request->audio_type() == MediaStreamType::NO_SERVICE) &&
          (request->video_type() == MediaStreamType::DEVICE_VIDEO_CAPTURE ||
           request->video_type() == MediaStreamType::NO_SERVICE));
+  SendLogMessage(base::StringPrintf(
+      "SetUpDeviceCaptureRequest([requester_id=%d])", request->requester_id));
   std::string audio_device_id;
   if (request->controls.audio.requested &&
       !GetRequestedDeviceCaptureId(
@@ -1535,9 +1732,13 @@
 
 void MediaStreamManager::FinalizeGenerateStream(const std::string& label,
                                                 DeviceRequest* request) {
-  DVLOG(1) << "FinalizeGenerateStream label " << label;
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(request->generate_stream_cb);
+  SendLogMessage(
+      base::StringPrintf("FinalizeGenerateStream({label=%s}, {requester_id="
+                         "%d}, {request_type=%s})",
+                         label.c_str(), request->requester_id,
+                         RequestTypeToString(request->request_type())));
 
   // Partition the array of devices into audio vs video.
   MediaStreamDevices audio_devices, video_devices;
@@ -1559,6 +1760,9 @@
     DeviceRequest* request,
     MediaStreamRequestResult result) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  SendLogMessage(base::StringPrintf(
+      "FinalizeRequestFailed({label=%s}, {requester_id=%d}, {result=%s})",
+      label.c_str(), request->requester_id, RequestResultToString(result)));
 
   switch (request->request_type()) {
     case blink::MEDIA_GENERATE_STREAM: {
@@ -1608,6 +1812,11 @@
 void MediaStreamManager::FinalizeOpenDevice(const std::string& label,
                                             DeviceRequest* request) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  SendLogMessage(
+      base::StringPrintf("FinalizeOpenDevice({label=%s}, {requester_id="
+                         "%d}, {request_type=%s})",
+                         label.c_str(), request->requester_id,
+                         RequestTypeToString(request->request_type())));
   if (request->open_device_cb) {
     std::move(request->open_device_cb)
         .Run(true /* success */, label, request->devices.front());
@@ -1618,6 +1827,11 @@
                                               DeviceRequest* request) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(request->device_changed_cb);
+  SendLogMessage(
+      base::StringPrintf("FinalizeChangeDevice({label=%s}, {requester_id="
+                         "%d}, {request_type=%s})",
+                         label.c_str(), request->requester_id,
+                         RequestTypeToString(request->request_type())));
 
   std::vector<std::vector<MediaStreamDevice>> old_devices_by_type(
       static_cast<size_t>(MediaStreamType::NUM_MEDIA_TYPES));
@@ -1647,6 +1861,11 @@
     const MediaStreamDevices& devices) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(request->media_access_request_cb);
+  SendLogMessage(
+      base::StringPrintf("FinalizeMediaAccessRequest({label=%s}, {requester_id="
+                         "%d}, {request_type=%s})",
+                         label.c_str(), request->requester_id,
+                         RequestTypeToString(request->request_type())));
   std::move(request->media_access_request_cb)
       .Run(devices, std::move(request->ui_proxy));
 
@@ -1666,12 +1885,14 @@
                                   std::move(video_capture_provider)));
     return;
   }
+  SendLogMessage(base::StringPrintf("InitializeMaybeAsync([this=%p])", this));
 
-  // Store a pointer to |this| on the IO thread to avoid having to jump to the
-  // UI thread to fetch a pointer to the MSM. In particular on Android, it can
-  // be problematic to post to a UI thread from arbitrary worker threads since
-  // attaching to the VM is required and we may have to access the MSM from
-  // callback threads that we don't own and don't want to attach.
+  // Store a pointer to |this| on the IO thread to avoid having to jump to
+  // the UI thread to fetch a pointer to the MSM. In particular on Android,
+  // it can be problematic to post to a UI thread from arbitrary worker
+  // threads since attaching to the VM is required and we may have to access
+  // the MSM from callback threads that we don't own and don't want to
+  // attach.
   g_media_stream_manager_tls_ptr.Pointer()->Set(this);
 
   audio_input_device_manager_ = new AudioInputDeviceManager(audio_system_);
@@ -1701,8 +1922,9 @@
     MediaStreamType stream_type,
     const base::UnguessableToken& capture_session_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DVLOG(1) << "Opened({stream_type = " << stream_type << "} "
-           << "{capture_session_id = " << capture_session_id << "})";
+  SendLogMessage(base::StringPrintf("Opened({stream_type=%s}, {session_id=%s})",
+                                    StreamTypeToString(stream_type),
+                                    capture_session_id.ToString().c_str()));
 
   // Find the request(s) containing this device and mark it as used.
   // It can be used in several requests since the same device can be
@@ -1748,8 +1970,6 @@
 void MediaStreamManager::HandleRequestDone(const std::string& label,
                                            DeviceRequest* request) {
   DCHECK(RequestDone(*request));
-  DVLOG(1) << "HandleRequestDone("
-           << ", {label = " << label << "})";
 
   switch (request->request_type()) {
     case blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY:
@@ -1774,6 +1994,9 @@
     MediaStreamType stream_type,
     const base::UnguessableToken& capture_session_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  SendLogMessage(base::StringPrintf("Closed({stream_type=%s}, {session_id=%s})",
+                                    StreamTypeToString(stream_type),
+                                    capture_session_id.ToString().c_str()));
 }
 
 void MediaStreamManager::DevicesEnumerated(
@@ -1787,6 +2010,11 @@
   if (!request)
     return;
 
+  SendLogMessage(base::StringPrintf(
+      "DevicesEnumerated({label=%s}, {requester_id=%d}, {request_type=%s})",
+      label.c_str(), request->requester_id,
+      RequestTypeToString(request->request_type())));
+
   bool requested[] = {requested_audio_input, requested_video_input};
   MediaStreamType stream_types[] = {MediaStreamType::DEVICE_AUDIO_CAPTURE,
                                     MediaStreamType::DEVICE_VIDEO_CAPTURE};
@@ -1812,17 +2040,18 @@
     MediaStreamType stream_type,
     const base::UnguessableToken& capture_session_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DVLOG(1) << "Aborted({stream_type = " << stream_type << "} "
-           << "{capture_session_id = " << capture_session_id << "})";
+  SendLogMessage(base::StringPrintf(
+      "Aborted({stream_type=%s}, {session_id=%s})",
+      StreamTypeToString(stream_type), capture_session_id.ToString().c_str()));
   StopDevice(stream_type, capture_session_id);
 }
 
 void MediaStreamManager::OnSuspend() {
-  SendMessageToNativeLog("Power state suspended.");
+  SendLogMessage(base::StringPrintf("OnSuspend([this=%p])", this));
 }
 
 void MediaStreamManager::OnResume() {
-  SendMessageToNativeLog("Power state resumed.");
+  SendLogMessage(base::StringPrintf("OnResume([this=%p])", this));
 }
 
 void MediaStreamManager::UseFakeUIFactoryForTests(
@@ -1870,14 +2099,15 @@
     const MediaStreamDevices& devices,
     MediaStreamRequestResult result) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DVLOG(1) << "HandleAccessRequestResponse("
-           << ", {label = " << label << "})";
-
   DeviceRequest* request = FindRequest(label);
   if (!request) {
     // The request has been canceled before the UI returned.
     return;
   }
+  SendLogMessage(base::StringPrintf(
+      "HandleAccessRequestResponse({label=%s}, {request=%s}, {result=%s})",
+      label.c_str(), RequestTypeToString(request->request_type()),
+      RequestResultToString(result)));
 
   if (request->request_type() == blink::MEDIA_DEVICE_ACCESS) {
     FinalizeMediaAccessRequest(label, request, devices);
@@ -1941,21 +2171,23 @@
       if (FindExistingRequestedDevice(*request, device, &device, &state)) {
         request->devices.push_back(device);
         request->SetState(device.type, state);
-        DVLOG(1) << "HandleAccessRequestResponse - device already opened "
-                 << ", {label = " << label << "}"
-                 << ", device_id = " << device.id << "}";
+        SendLogMessage(base::StringPrintf(
+            "HandleAccessRequestResponse([label=%s]) => "
+            "(already opened device: [id: %s, session_id: %s])",
+            label.c_str(), device.id.c_str(),
+            device.session_id().ToString().c_str()));
         continue;
       }
     }
     device.set_session_id(GetDeviceManager(device.type)->Open(device));
     TranslateDeviceIdToSourceId(request, &device);
     request->devices.push_back(device);
-
     request->SetState(device.type, MEDIA_REQUEST_STATE_OPENING);
-    DVLOG(1) << "HandleAccessRequestResponse - opening device "
-             << ", {label = " << label << "}"
-             << ", {device_id = " << device.id << "}"
-             << ", {session_id = " << device.session_id() << "}";
+    SendLogMessage(
+        base::StringPrintf("HandleAccessRequestResponse([label=%s]) => "
+                           "(opening device: [id: %s, session_id: %s])",
+                           label.c_str(), device.id.c_str(),
+                           device.session_id().ToString().c_str()));
   }
 
   // Check whether we've received all stream types requested.
@@ -2004,6 +2236,9 @@
   if (!request)
     return;
 
+  SendLogMessage(base::StringPrintf("StopMediaStreamFromBrowser({label=%s})",
+                                    label.c_str()));
+
   // Notify renderers that the devices in the stream will be stopped.
   if (request->device_stopped_cb) {
     for (const MediaStreamDevice& device : request->devices) {
@@ -2024,6 +2259,9 @@
   if (!request)
     return;
 
+  SendLogMessage(base::StringPrintf(
+      "ChangeMediaStreamSourceFromBrowser({label=%s})", label.c_str()));
+
   SetUpDesktopCaptureChangeSourceRequest(request, label, media_id);
   IncrementDesktopCaptureCounter(DESKTOP_CAPTURE_NOTIFICATION_CHANGE_SOURCE);
 }
@@ -2050,6 +2288,9 @@
     blink::MediaDeviceType device_type,
     const blink::WebMediaDeviceInfoArray& devices) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  SendLogMessage(base::StringPrintf("NotifyDevicesChanged({device_type=%s})",
+                                    DeviceTypeToString(device_type)));
+
   MediaObserver* media_observer =
       GetContentClient()->browser()->GetMediaObserver();
 
@@ -2074,6 +2315,9 @@
 
 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  SendLogMessage(base::StringPrintf(
+      "RequestDone({requester_id=%d}, {request_type=%s})", request.requester_id,
+      RequestTypeToString(request.request_type())));
 
   const bool requested_audio =
       blink::IsAudioInputMediaType(request.audio_type());
@@ -2272,6 +2516,10 @@
   DeviceRequest* const request = FindRequest(label);
   if (!request)
     return;
+  SendLogMessage(base::StringPrintf(
+      "OnStreamStarted({label=%s}, {requester_id=%d}, {request_type=%s})",
+      label.c_str(), request->requester_id,
+      RequestTypeToString(request->request_type())));
 
   // Show "Change source" button on notification bar only for tab sharing by
   // desktopCapture API.
diff --git a/content/browser/service_worker/service_worker_client_info.cc b/content/browser/service_worker/service_worker_client_info.cc
index cff6cf1..7e8b9f14 100644
--- a/content/browser/service_worker/service_worker_client_info.cc
+++ b/content/browser/service_worker/service_worker_client_info.cc
@@ -14,13 +14,13 @@
           ChildProcessHost::kInvalidUniqueID,
           MSG_ROUTING_NONE,
           base::RepeatingCallback<WebContents*(void)>(),
-          blink::mojom::ServiceWorkerProviderType::kUnknown) {}
+          blink::mojom::ServiceWorkerContainerType::kUnknown) {}
 
 ServiceWorkerClientInfo::ServiceWorkerClientInfo(
     int process_id,
     int route_id,
     const base::RepeatingCallback<WebContents*(void)>& web_contents_getter,
-    blink::mojom::ServiceWorkerProviderType type)
+    blink::mojom::ServiceWorkerContainerType type)
     : process_id(process_id),
       route_id(route_id),
       web_contents_getter(web_contents_getter),
diff --git a/content/browser/service_worker/service_worker_client_info.h b/content/browser/service_worker/service_worker_client_info.h
index 245c198..d310446 100644
--- a/content/browser/service_worker/service_worker_client_info.h
+++ b/content/browser/service_worker/service_worker_client_info.h
@@ -8,7 +8,7 @@
 #include "base/callback.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/web_contents.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 
 namespace content {
 
@@ -20,7 +20,7 @@
       int process_id,
       int route_id,
       const base::RepeatingCallback<WebContents*(void)>& web_contents_getter,
-      blink::mojom::ServiceWorkerProviderType type);
+      blink::mojom::ServiceWorkerContainerType type);
   ServiceWorkerClientInfo(const ServiceWorkerClientInfo& other);
   ~ServiceWorkerClientInfo();
 
@@ -35,7 +35,7 @@
   // occurred in.
   base::RepeatingCallback<WebContents*(void)> web_contents_getter;
   // The client type.
-  blink::mojom::ServiceWorkerProviderType type;
+  blink::mojom::ServiceWorkerContainerType type;
 };
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc
index 0d979c6..139a8c4 100644
--- a/content/browser/service_worker/service_worker_container_host.cc
+++ b/content/browser/service_worker/service_worker_container_host.cc
@@ -88,9 +88,9 @@
         container_remote) {
   DCHECK(context);
   auto container_host = std::make_unique<ServiceWorkerContainerHost>(
-      blink::mojom::ServiceWorkerProviderType::kForWindow, are_ancestors_secure,
-      frame_tree_node_id, std::move(host_receiver), std::move(container_remote),
-      context);
+      blink::mojom::ServiceWorkerContainerType::kForWindow,
+      are_ancestors_secure, frame_tree_node_id, std::move(host_receiver),
+      std::move(container_remote), context);
 
   std::string client_uuid = container_host->client_uuid();
   base::WeakPtr<ServiceWorkerContainerHost> weak_ptr =
@@ -109,18 +109,18 @@
 ServiceWorkerContainerHost::CreateForWebWorker(
     base::WeakPtr<ServiceWorkerContextCore> context,
     int process_id,
-    blink::mojom::ServiceWorkerProviderType provider_type,
+    blink::mojom::ServiceWorkerContainerType container_type,
     mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
         host_receiver,
     mojo::PendingAssociatedRemote<blink::mojom::ServiceWorkerContainer>
         container_remote) {
   DCHECK(context);
-  using ServiceWorkerProviderType = blink::mojom::ServiceWorkerProviderType;
+  using ServiceWorkerContainerType = blink::mojom::ServiceWorkerContainerType;
   DCHECK((base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker) &&
-          provider_type == ServiceWorkerProviderType::kForDedicatedWorker) ||
-         provider_type == ServiceWorkerProviderType::kForSharedWorker);
+          container_type == ServiceWorkerContainerType::kForDedicatedWorker) ||
+         container_type == ServiceWorkerContainerType::kForSharedWorker);
   auto container_host = std::make_unique<ServiceWorkerContainerHost>(
-      provider_type, /*is_parent_frame_secure=*/true,
+      container_type, /*is_parent_frame_secure=*/true,
       FrameTreeNode::kFrameTreeNodeInvalidId, std::move(host_receiver),
       std::move(container_remote), context);
   container_host->SetContainerProcessId(process_id);
@@ -138,7 +138,7 @@
 }
 
 ServiceWorkerContainerHost::ServiceWorkerContainerHost(
-    blink::mojom::ServiceWorkerProviderType type,
+    blink::mojom::ServiceWorkerContainerType type,
     bool is_parent_frame_secure,
     int frame_tree_node_id,
     mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
@@ -513,7 +513,7 @@
 
   // This is only called for windows now, but it should be called for all
   // clients someday.
-  DCHECK_EQ(type(), blink::mojom::ServiceWorkerProviderType::kForWindow);
+  DCHECK_EQ(type(), blink::mojom::ServiceWorkerContainerType::kForWindow);
 
   versions_to_update_.emplace(std::move(version));
 }
@@ -685,19 +685,19 @@
 
 bool ServiceWorkerContainerHost::IsContainerForServiceWorker() const {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  return type_ == blink::mojom::ServiceWorkerProviderType::kForServiceWorker;
+  return type_ == blink::mojom::ServiceWorkerContainerType::kForServiceWorker;
 }
 
 bool ServiceWorkerContainerHost::IsContainerForClient() const {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
   switch (type_) {
-    case blink::mojom::ServiceWorkerProviderType::kForWindow:
-    case blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker:
-    case blink::mojom::ServiceWorkerProviderType::kForSharedWorker:
+    case blink::mojom::ServiceWorkerContainerType::kForWindow:
+    case blink::mojom::ServiceWorkerContainerType::kForDedicatedWorker:
+    case blink::mojom::ServiceWorkerContainerType::kForSharedWorker:
       return true;
-    case blink::mojom::ServiceWorkerProviderType::kForServiceWorker:
+    case blink::mojom::ServiceWorkerContainerType::kForServiceWorker:
       return false;
-    case blink::mojom::ServiceWorkerProviderType::kUnknown:
+    case blink::mojom::ServiceWorkerContainerType::kUnknown:
       break;
   }
   NOTREACHED() << type_;
@@ -708,14 +708,14 @@
     const {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
   switch (type_) {
-    case blink::mojom::ServiceWorkerProviderType::kForWindow:
+    case blink::mojom::ServiceWorkerContainerType::kForWindow:
       return blink::mojom::ServiceWorkerClientType::kWindow;
-    case blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker:
+    case blink::mojom::ServiceWorkerContainerType::kForDedicatedWorker:
       return blink::mojom::ServiceWorkerClientType::kDedicatedWorker;
-    case blink::mojom::ServiceWorkerProviderType::kForSharedWorker:
+    case blink::mojom::ServiceWorkerContainerType::kForSharedWorker:
       return blink::mojom::ServiceWorkerClientType::kSharedWorker;
-    case blink::mojom::ServiceWorkerProviderType::kForServiceWorker:
-    case blink::mojom::ServiceWorkerProviderType::kUnknown:
+    case blink::mojom::ServiceWorkerContainerType::kForServiceWorker:
+    case blink::mojom::ServiceWorkerContainerType::kUnknown:
       break;
   }
   NOTREACHED() << type_;
@@ -727,7 +727,7 @@
     int container_frame_id,
     network::mojom::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  DCHECK_EQ(blink::mojom::ServiceWorkerProviderType::kForWindow, type());
+  DCHECK_EQ(blink::mojom::ServiceWorkerContainerType::kForWindow, type());
 
   SetContainerProcessId(container_process_id);
 
@@ -765,9 +765,9 @@
 void ServiceWorkerContainerHost::CompleteWebWorkerPreparation(
     network::mojom::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  using ServiceWorkerProviderType = blink::mojom::ServiceWorkerProviderType;
-  DCHECK(type_ == ServiceWorkerProviderType::kForDedicatedWorker ||
-         type_ == ServiceWorkerProviderType::kForSharedWorker);
+  using ServiceWorkerContainerType = blink::mojom::ServiceWorkerContainerType;
+  DCHECK(type_ == ServiceWorkerContainerType::kForDedicatedWorker ||
+         type_ == ServiceWorkerContainerType::kForSharedWorker);
 
   DCHECK(!cross_origin_embedder_policy_.has_value());
   cross_origin_embedder_policy_ = cross_origin_embedder_policy;
@@ -803,7 +803,7 @@
     // Revoke the token on URL change since any service worker holding the token
     // may no longer be the potential controller of this frame and shouldn't
     // have the power to display SSL dialogs for it.
-    if (type_ == blink::mojom::ServiceWorkerProviderType::kForWindow) {
+    if (type_ == blink::mojom::ServiceWorkerContainerType::kForWindow) {
       auto* registry = FrameTreeNodeIdRegistry::GetInstance();
       registry->Remove(fetch_request_window_id_);
       fetch_request_window_id_ = base::UnguessableToken::Create();
@@ -1037,7 +1037,7 @@
     BackForwardCacheMetrics::NotRestoredReason reason) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
   DCHECK(IsBackForwardCacheEnabled());
-  DCHECK_EQ(type_, blink::mojom::ServiceWorkerProviderType::kForWindow);
+  DCHECK_EQ(type_, blink::mojom::ServiceWorkerContainerType::kForWindow);
   is_in_back_forward_cache_ = false;
   RunOrPostTaskOnThread(
       FROM_HERE, BrowserThread::UI,
@@ -1056,7 +1056,7 @@
 void ServiceWorkerContainerHost::OnEnterBackForwardCache() {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
   DCHECK(IsBackForwardCacheEnabled());
-  DCHECK_EQ(type_, blink::mojom::ServiceWorkerProviderType::kForWindow);
+  DCHECK_EQ(type_, blink::mojom::ServiceWorkerContainerType::kForWindow);
   if (controller_)
     controller_->MoveControlleeToBackForwardCacheMap(client_uuid());
   is_in_back_forward_cache_ = true;
@@ -1065,7 +1065,7 @@
 void ServiceWorkerContainerHost::OnRestoreFromBackForwardCache() {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
   DCHECK(IsBackForwardCacheEnabled());
-  DCHECK_EQ(type_, blink::mojom::ServiceWorkerProviderType::kForWindow);
+  DCHECK_EQ(type_, blink::mojom::ServiceWorkerContainerType::kForWindow);
   if (controller_)
     controller_->RestoreControlleeFromBackForwardCacheMap(client_uuid());
   is_in_back_forward_cache_ = false;
diff --git a/content/browser/service_worker/service_worker_container_host.h b/content/browser/service_worker/service_worker_container_host.h
index 8d6ff05..c7bd4a3 100644
--- a/content/browser/service_worker/service_worker_container_host.h
+++ b/content/browser/service_worker/service_worker_container_host.h
@@ -22,7 +22,7 @@
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_client.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_container.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
 
 namespace content {
@@ -105,16 +105,14 @@
   static base::WeakPtr<ServiceWorkerContainerHost> CreateForWebWorker(
       base::WeakPtr<ServiceWorkerContextCore> context,
       int process_id,
-      blink::mojom::ServiceWorkerProviderType provider_type,
+      blink::mojom::ServiceWorkerContainerType container_type,
       mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
           host_receiver,
       mojo::PendingAssociatedRemote<blink::mojom::ServiceWorkerContainer>
           container_remote);
 
-  // TODO(https://crbug.com/931087): Rename ServiceWorkerProviderType to
-  // ServiceWorkerContainerType.
   ServiceWorkerContainerHost(
-      blink::mojom::ServiceWorkerProviderType type,
+      blink::mojom::ServiceWorkerContainerType type,
       bool is_parent_frame_secure,
       int frame_tree_node_id,
       mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
@@ -251,7 +249,7 @@
   bool IsContainerForServiceWorker() const;
   bool IsContainerForClient() const;
 
-  blink::mojom::ServiceWorkerProviderType type() const { return type_; }
+  blink::mojom::ServiceWorkerContainerType type() const { return type_; }
 
   // Can only be called when IsContainerForClient() is true.
   blink::mojom::ServiceWorkerClientType client_type() const;
@@ -540,7 +538,7 @@
                                     const char* error_prefix,
                                     Args... args);
 
-  const blink::mojom::ServiceWorkerProviderType type_;
+  const blink::mojom::ServiceWorkerContainerType type_;
 
   // See comments for the getter functions.
   GURL url_;
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index a966204..c10c267 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -47,7 +47,7 @@
 #include "net/http/http_response_info.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
 #include "url/gurl.h"
 
@@ -156,7 +156,7 @@
       return false;
   }
   return container_host->type() ==
-             blink::mojom::ServiceWorkerProviderType::kForWindow &&
+             blink::mojom::ServiceWorkerContainerType::kForWindow &&
          container_host->url().GetOrigin() == origin &&
          (allow_reserved_client || container_host->is_execution_ready());
 }
@@ -372,7 +372,7 @@
     ServiceWorkerContainerHost* container_host =
         container_host_iterator->GetContainerHost();
     DCHECK_EQ(container_host->type(),
-              blink::mojom::ServiceWorkerProviderType::kForWindow);
+              blink::mojom::ServiceWorkerContainerType::kForWindow);
     render_frames->push_back(std::make_pair(container_host->process_id(),
                                             container_host->frame_id()));
     container_host_iterator->Advance();
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index ca2fb6b..244b97e 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -177,7 +177,7 @@
       bool include_reserved_clients);
 
   // Runs the callback with true if there is a ContainerHost for |origin| of
-  // type blink::mojom::ServiceWorkerProviderType::kForWindow which is a main
+  // type blink::mojom::ServiceWorkerContainerType::kForWindow which is a main
   // (top-level) frame. Reserved clients are ignored.
   // TODO(crbug.com/824858): Make this synchronously return bool when the core
   // thread is UI.
diff --git a/content/browser/service_worker/service_worker_context_core_observer.h b/content/browser/service_worker/service_worker_context_core_observer.h
index e5e8bd9..75126bab 100644
--- a/content/browser/service_worker/service_worker_context_core_observer.h
+++ b/content/browser/service_worker/service_worker_context_core_observer.h
@@ -13,7 +13,7 @@
 #include "base/time/time.h"
 #include "content/browser/service_worker/service_worker_info.h"
 #include "content/browser/service_worker/service_worker_version.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/content/browser/service_worker/service_worker_context_watcher.h b/content/browser/service_worker/service_worker_context_watcher.h
index bb05c08..5c2b717 100644
--- a/content/browser/service_worker/service_worker_context_watcher.h
+++ b/content/browser/service_worker/service_worker_context_watcher.h
@@ -16,7 +16,7 @@
 #include "content/browser/service_worker/service_worker_context_core_observer.h"
 #include "content/browser/service_worker/service_worker_info.h"
 #include "content/common/content_export.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index d6bfdb0..6bdbe1e1d 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -919,7 +919,7 @@
        !it->IsAtEnd(); it->Advance()) {
     ServiceWorkerContainerHost* container_host = it->GetContainerHost();
     DCHECK_EQ(container_host->type(),
-              blink::mojom::ServiceWorkerProviderType::kForWindow);
+              blink::mojom::ServiceWorkerContainerType::kForWindow);
     frame_routing_ids->push_back(GlobalFrameRoutingId(
         container_host->process_id(), container_host->frame_id()));
   }
diff --git a/content/browser/service_worker/service_worker_info.h b/content/browser/service_worker/service_worker_info.h
index 21f54cf2..76b2e6514 100644
--- a/content/browser/service_worker/service_worker_info.h
+++ b/content/browser/service_worker/service_worker_info.h
@@ -14,7 +14,7 @@
 #include "base/time/time.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/common/content_export.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
diff --git a/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc b/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc
index 6655dd6..e045776 100644
--- a/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc
@@ -123,8 +123,8 @@
              params.resource_type == ResourceType::kSharedWorker);
       auto container_type =
           params.resource_type == ResourceType::kWorker
-              ? blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker
-              : blink::mojom::ServiceWorkerProviderType::kForSharedWorker;
+              ? blink::mojom::ServiceWorkerContainerType::kForDedicatedWorker
+              : blink::mojom::ServiceWorkerContainerType::kForSharedWorker;
       container_host = ServiceWorkerContainerHost::CreateForWebWorker(
           context_core->AsWeakPtr(), params.process_id, container_type,
           std::move(host_receiver), std::move(client_remote));
diff --git a/content/browser/service_worker/service_worker_object_host.cc b/content/browser/service_worker/service_worker_object_host.cc
index 1bd8311..71a6e2c 100644
--- a/content/browser/service_worker/service_worker_object_host.cc
+++ b/content/browser/service_worker/service_worker_object_host.cc
@@ -286,14 +286,14 @@
   }
   DCHECK_EQ(container_origin_, url::Origin::Create(container_host_->url()));
   switch (container_host_->type()) {
-    case blink::mojom::ServiceWorkerProviderType::kForWindow:
+    case blink::mojom::ServiceWorkerContainerType::kForWindow:
       service_worker_client_utils::GetClient(
           container_host_,
           base::BindOnce(&DispatchExtendableMessageEventFromClient, context_,
                          version_, std::move(message), container_origin_,
                          std::move(callback)));
       return;
-    case blink::mojom::ServiceWorkerProviderType::kForServiceWorker: {
+    case blink::mojom::ServiceWorkerContainerType::kForServiceWorker: {
       // Clamp timeout to the sending worker's remaining timeout, to prevent
       // postMessage from keeping workers alive forever.
       base::TimeDelta timeout = container_host_->service_worker_host()
@@ -308,11 +308,11 @@
                          container_host_->GetWeakPtr()));
       return;
     }
-    case blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker:
-    case blink::mojom::ServiceWorkerProviderType::kForSharedWorker:
+    case blink::mojom::ServiceWorkerContainerType::kForDedicatedWorker:
+    case blink::mojom::ServiceWorkerContainerType::kForSharedWorker:
     // Web workers don't yet have access to ServiceWorker objects, so they
     // can't postMessage to one (https://crbug.com/371690).
-    case blink::mojom::ServiceWorkerProviderType::kUnknown:
+    case blink::mojom::ServiceWorkerContainerType::kUnknown:
       break;
   }
   NOTREACHED() << container_host_->type();
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 187f062..dca405d 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -61,7 +61,7 @@
     : provider_id_(NextProviderId()),
       running_hosted_version_(std::move(running_hosted_version)),
       container_host_(std::make_unique<content::ServiceWorkerContainerHost>(
-          blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
+          blink::mojom::ServiceWorkerContainerType::kForServiceWorker,
           /*is_parent_frame_secure=*/true,
           FrameTreeNode::kFrameTreeNodeInvalidId,
           std::move(host_receiver),
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 47a69738..3745334 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -26,8 +26,8 @@
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_container.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
 #include "third_party/blink/public/mojom/webtransport/quic_transport_connector.mojom.h"
 #include "url/origin.h"
diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc
index 57a9988d..ca5ddd1 100644
--- a/content/browser/service_worker/service_worker_provider_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -302,14 +302,14 @@
   }
 
   void TestReservedClientsAreNotExposed(
-      blink::mojom::ServiceWorkerProviderType provider_type,
+      blink::mojom::ServiceWorkerContainerType provider_type,
       const GURL& url);
   void TestClientPhaseTransition(
-      blink::mojom::ServiceWorkerProviderType provider_type,
+      blink::mojom::ServiceWorkerContainerType provider_type,
       const GURL& url);
 
   void TestBackForwardCachedClientsAreNotExposed(
-      blink::mojom::ServiceWorkerProviderType provider_type,
+      blink::mojom::ServiceWorkerContainerType provider_type,
       const GURL& url);
 
   BrowserTaskEnvironment task_environment_;
@@ -976,7 +976,7 @@
 // when iterating over client container hosts. If it were, it'd be undesirably
 // exposed via the Clients API.
 void ServiceWorkerProviderHostTest::TestReservedClientsAreNotExposed(
-    blink::mojom::ServiceWorkerProviderType provider_type,
+    blink::mojom::ServiceWorkerContainerType provider_type,
     const GURL& url) {
   {
     mojo::PendingAssociatedRemote<blink::mojom::ServiceWorkerContainer>
@@ -1026,14 +1026,14 @@
   ASSERT_TRUE(
       base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker));
   TestReservedClientsAreNotExposed(
-      blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker,
+      blink::mojom::ServiceWorkerContainerType::kForDedicatedWorker,
       GURL("https://www.example.com/dedicated_worker.js"));
 }
 
 TEST_F(ServiceWorkerProviderHostTest,
        ReservedClientsAreNotExposedToClientsApiForSharedWorker) {
   TestReservedClientsAreNotExposed(
-      blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
+      blink::mojom::ServiceWorkerContainerType::kForSharedWorker,
       GURL("https://www.example.com/shared_worker.js"));
 }
 
@@ -1063,7 +1063,7 @@
 
 // Tests the client phase transitions for workers.
 void ServiceWorkerProviderHostTest::TestClientPhaseTransition(
-    blink::mojom::ServiceWorkerProviderType provider_type,
+    blink::mojom::ServiceWorkerContainerType provider_type,
     const GURL& url) {
   mojo::PendingAssociatedRemote<blink::mojom::ServiceWorkerContainer>
       client_remote;
@@ -1095,13 +1095,13 @@
   ASSERT_TRUE(
       base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker));
   TestClientPhaseTransition(
-      blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker,
+      blink::mojom::ServiceWorkerContainerType::kForDedicatedWorker,
       GURL("https://www.example.com/dedicated_worker.js"));
 }
 
 TEST_F(ServiceWorkerProviderHostTest, ClientPhaseForSharedWorker) {
   TestClientPhaseTransition(
-      blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
+      blink::mojom::ServiceWorkerContainerType::kForSharedWorker,
       GURL("https://www.example.com/shared_worker.js"));
 }
 
@@ -1130,7 +1130,7 @@
 // when iterating over client container hosts. If it were, it'd be undesirably
 // exposed via the Clients API.
 void ServiceWorkerProviderHostTest::TestBackForwardCachedClientsAreNotExposed(
-    blink::mojom::ServiceWorkerProviderType provider_type,
+    blink::mojom::ServiceWorkerContainerType provider_type,
     const GURL& url) {
   std::unique_ptr<ServiceWorkerProviderHost> provider_host;
   {
@@ -1177,7 +1177,7 @@
   ASSERT_TRUE(ServiceWorkerContext::IsServiceWorkerOnUIEnabled());
 
   TestBackForwardCachedClientsAreNotExposed(
-      blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
+      blink::mojom::ServiceWorkerContainerType::kForServiceWorker,
       GURL("https://www.example.com/sw.js"));
 }
 
diff --git a/content/browser/service_worker/service_worker_registration_object_host.cc b/content/browser/service_worker/service_worker_registration_object_host.cc
index fb38b89..9dc53e6a 100644
--- a/content/browser/service_worker/service_worker_registration_object_host.cc
+++ b/content/browser/service_worker/service_worker_registration_object_host.cc
@@ -211,14 +211,14 @@
 }
 
 void ServiceWorkerRegistrationObjectHost::DelayUpdate(
-    blink::mojom::ServiceWorkerProviderType provider_type,
+    blink::mojom::ServiceWorkerContainerType container_type,
     ServiceWorkerRegistration* registration,
     ServiceWorkerVersion* version,
     StatusCallback update_function) {
   DCHECK(registration);
 
-  if (provider_type !=
-          blink::mojom::ServiceWorkerProviderType::kForServiceWorker ||
+  if (container_type !=
+          blink::mojom::ServiceWorkerContainerType::kForServiceWorker ||
       (version && version->HasControllee())) {
     // Don't delay update() if called by non-workers or by workers with
     // controllees.
diff --git a/content/browser/service_worker/service_worker_registration_object_host.h b/content/browser/service_worker/service_worker_registration_object_host.h
index 5279d74..e4fd908 100644
--- a/content/browser/service_worker/service_worker_registration_object_host.h
+++ b/content/browser/service_worker/service_worker_registration_object_host.h
@@ -83,10 +83,11 @@
   //
   // TODO(falken): See if tests can call |Update| directly, then this separate
   // function isn't needed.
-  static void DelayUpdate(blink::mojom::ServiceWorkerProviderType provider_type,
-                          ServiceWorkerRegistration* registration,
-                          ServiceWorkerVersion* version,
-                          StatusCallback update_function);
+  static void DelayUpdate(
+      blink::mojom::ServiceWorkerContainerType container_type,
+      ServiceWorkerRegistration* registration,
+      ServiceWorkerVersion* version,
+      StatusCallback update_function);
   // Called back from ServiceWorkerContextCore when an update is complete.
   void UpdateComplete(UpdateCallback callback,
                       blink::ServiceWorkerStatusCode status,
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index e37df62b..da91e9f 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -827,13 +827,13 @@
   }
 
   blink::ServiceWorkerStatusCode CallDelayUpdate(
-      blink::mojom::ServiceWorkerProviderType provider_type,
+      blink::mojom::ServiceWorkerContainerType provider_type,
       ServiceWorkerRegistration* registration,
       ServiceWorkerVersion* version) {
     base::Optional<blink::ServiceWorkerStatusCode> status;
     base::RunLoop run_loop;
     ServiceWorkerRegistrationObjectHost::DelayUpdate(
-        blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
+        blink::mojom::ServiceWorkerContainerType::kForServiceWorker,
         registration, version,
         base::BindOnce(
             [](base::Optional<blink::ServiceWorkerStatusCode>* out_status,
@@ -1120,7 +1120,7 @@
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
             CallDelayUpdate(
-                blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
+                blink::mojom::ServiceWorkerContainerType::kForServiceWorker,
                 registration.get(), version.get()));
   EXPECT_LT(base::TimeDelta(), registration->self_update_delay());
 
@@ -1131,7 +1131,7 @@
   registration->set_self_update_delay(base::TimeDelta::FromMinutes(5));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout,
             CallDelayUpdate(
-                blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
+                blink::mojom::ServiceWorkerContainerType::kForServiceWorker,
                 registration.get(), version.get()));
   EXPECT_LE(base::TimeDelta::FromMinutes(5), registration->self_update_delay());
 }
@@ -1160,7 +1160,7 @@
   registration->set_self_update_delay(base::TimeDelta());
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
             CallDelayUpdate(
-                blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
+                blink::mojom::ServiceWorkerContainerType::kForServiceWorker,
                 registration.get(), version.get()));
   EXPECT_EQ(base::TimeDelta(), registration->self_update_delay());
 
@@ -1169,7 +1169,7 @@
   registration->set_self_update_delay(base::TimeDelta::FromMinutes(5));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
             CallDelayUpdate(
-                blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
+                blink::mojom::ServiceWorkerContainerType::kForServiceWorker,
                 registration.get(), version.get()));
   EXPECT_EQ(base::TimeDelta::FromMinutes(5), registration->self_update_delay());
 }
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index f719f1fda..38a5e38e 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -12229,9 +12229,9 @@
       &screen_info);
   // Convert from CSS to physical pixels
   expected.Scale(screen_info.device_scale_factor);
-  blink::WebPoint actual = filter->GetIntersectionState().viewport_offset;
-  EXPECT_NEAR(expected.x(), actual.x, 2.0);
-  EXPECT_NEAR(expected.y(), actual.y, 2.0);
+  gfx::Point actual = filter->GetIntersectionState().viewport_offset;
+  EXPECT_NEAR(expected.x(), actual.x(), 2.0);
+  EXPECT_NEAR(expected.y(), actual.y(), 2.0);
 }
 
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc
index d08e9e88..bd098cbd 100644
--- a/content/browser/webauth/authenticator_common.cc
+++ b/content/browser/webauth/authenticator_common.cc
@@ -151,37 +151,46 @@
   return blink::mojom::AuthenticatorStatus::SUCCESS;
 }
 
-// Ensure the relying party ID is a registrable domain suffix of or equal
-// to the origin's effective domain. Reference:
+// Return the relying party ID to use for a request given the requested RP ID
+// and the origin of the caller. It's valid for the requested RP ID to be a
+// registrable domain suffix of, or be equal to, the origin's effective domain.
+// Reference:
 // https://html.spec.whatwg.org/multipage/origin.html#is-a-registrable-domain-suffix-of-or-is-equal-to.
-bool IsRelyingPartyIdValid(const std::string& relying_party_id,
-                           url::Origin caller_origin) {
+base::Optional<std::string> GetRelyingPartyId(
+    std::string claimed_relying_party_id,
+    const url::Origin& caller_origin) {
   if (OriginIsCryptoTokenExtension(caller_origin)) {
-    return true;
+    // This code trusts cryptotoken to handle the validation itself.
+    return claimed_relying_party_id;
   }
 
-  if (relying_party_id.empty())
-    return false;
+  if (claimed_relying_party_id.empty()) {
+    return base::nullopt;
+  }
 
-  if (caller_origin.host() == relying_party_id)
-    return true;
+  if (caller_origin.host() == claimed_relying_party_id) {
+    return claimed_relying_party_id;
+  }
 
-  if (!caller_origin.DomainIs(relying_party_id))
-    return false;
+  if (!caller_origin.DomainIs(claimed_relying_party_id)) {
+    return base::nullopt;
+  }
+
   if (!net::registry_controlled_domains::HostHasRegistryControlledDomain(
           caller_origin.host(),
           net::registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES,
-          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES))
-    return false;
-  if (!net::registry_controlled_domains::HostHasRegistryControlledDomain(
-          relying_party_id,
+          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) ||
+      !net::registry_controlled_domains::HostHasRegistryControlledDomain(
+          claimed_relying_party_id,
           net::registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES,
-          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES))
+          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
     // TODO(crbug.com/803414): Accept corner-case situations like the following
     // origin: "https://login.awesomecompany",
     // relying_party_id: "awesomecompany".
-    return false;
-  return true;
+    return base::nullopt;
+  }
+
+  return claimed_relying_party_id;
 }
 
 // Checks if the icon URL is an a-priori authenticated URL.
@@ -560,8 +569,7 @@
 }
 
 std::unique_ptr<AuthenticatorRequestClientDelegate>
-AuthenticatorCommon::CreateRequestDelegate(std::string relying_party_id) {
-  DCHECK(!relying_party_id.empty());
+AuthenticatorCommon::CreateRequestDelegate() {
   auto* frame_tree_node =
       static_cast<RenderFrameHostImpl*>(render_frame_host_)->frame_tree_node();
   if (AuthenticatorEnvironmentImpl::GetInstance()->GetVirtualFactoryFor(
@@ -570,7 +578,7 @@
         frame_tree_node);
   }
   return GetContentClient()->browser()->GetWebAuthenticationRequestDelegate(
-      render_frame_host_, relying_party_id_);
+      render_frame_host_);
 }
 
 void AuthenticatorCommon::StartMakeCredentialRequest(
@@ -765,26 +773,49 @@
     return;
   }
 
-  blink::mojom::AuthenticatorStatus domain_validation =
-      ValidateEffectiveDomain(caller_origin);
-  if (domain_validation != blink::mojom::AuthenticatorStatus::SUCCESS) {
-    ReportSecurityCheckFailure(
-        RelyingPartySecurityCheckFailure::kOpaqueOrNonSecureOrigin);
-    InvokeCallbackAndCleanup(std::move(callback), domain_validation, nullptr,
-                             Focus::kDontCheck);
+  request_delegate_ = CreateRequestDelegate();
+  if (!request_delegate_) {
+    InvokeCallbackAndCleanup(std::move(callback),
+                             blink::mojom::AuthenticatorStatus::PENDING_REQUEST,
+                             nullptr, Focus::kDontCheck);
     return;
   }
 
-  if (!IsRelyingPartyIdValid(options->relying_party.id, caller_origin)) {
-    ReportSecurityCheckFailure(
-        RelyingPartySecurityCheckFailure::kRelyingPartyIdInvalid);
-    InvokeCallbackAndCleanup(
-        std::move(callback),
-        blink::mojom::AuthenticatorStatus::BAD_RELYING_PARTY_ID, nullptr,
-        Focus::kDontCheck);
-    return;
+  base::Optional<std::string> rp_id =
+      request_delegate_->MaybeGetRelyingPartyIdOverride(
+          options->relying_party.id, caller_origin);
+
+  if (!rp_id) {
+    // If the delegate didn't override RP ID selection then apply standard
+    // rules.
+    blink::mojom::AuthenticatorStatus domain_validation =
+        ValidateEffectiveDomain(caller_origin);
+    if (domain_validation != blink::mojom::AuthenticatorStatus::SUCCESS) {
+      ReportSecurityCheckFailure(
+          RelyingPartySecurityCheckFailure::kOpaqueOrNonSecureOrigin);
+      InvokeCallbackAndCleanup(std::move(callback), domain_validation, nullptr,
+                               Focus::kDontCheck);
+      return;
+    }
+
+    rp_id =
+        GetRelyingPartyId(std::move(options->relying_party.id), caller_origin);
+    if (!rp_id) {
+      ReportSecurityCheckFailure(
+          RelyingPartySecurityCheckFailure::kRelyingPartyIdInvalid);
+      InvokeCallbackAndCleanup(
+          std::move(callback),
+          blink::mojom::AuthenticatorStatus::BAD_RELYING_PARTY_ID, nullptr,
+          Focus::kDontCheck);
+      return;
+    }
   }
 
+  caller_origin_ = caller_origin;
+  relying_party_id_ = *rp_id;
+  options->relying_party.id = std::move(*rp_id);
+  request_delegate_->SetRelyingPartyId(relying_party_id_);
+
   base::Optional<std::string> appid_exclude;
   if (options->appid_exclude) {
     appid_exclude =
@@ -811,17 +842,6 @@
     return;
   }
 
-  caller_origin_ = caller_origin;
-  relying_party_id_ = options->relying_party.id;
-
-  request_delegate_ = CreateRequestDelegate(relying_party_id_);
-  if (!request_delegate_) {
-    InvokeCallbackAndCleanup(std::move(callback),
-                             blink::mojom::AuthenticatorStatus::PENDING_REQUEST,
-                             nullptr, Focus::kDontCheck);
-    return;
-  }
-
   if (!IsFocused()) {
     InvokeCallbackAndCleanup(std::move(callback),
                              blink::mojom::AuthenticatorStatus::NOT_FOCUSED);
@@ -982,28 +1002,7 @@
     return;
   }
 
-  blink::mojom::AuthenticatorStatus domain_validation =
-      ValidateEffectiveDomain(caller_origin);
-  if (domain_validation != blink::mojom::AuthenticatorStatus::SUCCESS) {
-    ReportSecurityCheckFailure(
-        RelyingPartySecurityCheckFailure::kOpaqueOrNonSecureOrigin);
-    InvokeCallbackAndCleanup(std::move(callback), domain_validation, nullptr);
-    return;
-  }
-
-  if (!IsRelyingPartyIdValid(options->relying_party_id, caller_origin)) {
-    ReportSecurityCheckFailure(
-        RelyingPartySecurityCheckFailure::kRelyingPartyIdInvalid);
-    InvokeCallbackAndCleanup(
-        std::move(callback),
-        blink::mojom::AuthenticatorStatus::BAD_RELYING_PARTY_ID, nullptr);
-    return;
-  }
-
-  caller_origin_ = caller_origin;
-  relying_party_id_ = options->relying_party_id;
-
-  request_delegate_ = CreateRequestDelegate(relying_party_id_);
+  request_delegate_ = CreateRequestDelegate();
   if (!request_delegate_) {
     InvokeCallbackAndCleanup(std::move(callback),
                              blink::mojom::AuthenticatorStatus::PENDING_REQUEST,
@@ -1011,9 +1010,40 @@
     return;
   }
 
+  base::Optional<std::string> rp_id =
+      request_delegate_->MaybeGetRelyingPartyIdOverride(
+          options->relying_party_id, caller_origin);
+
+  if (!rp_id) {
+    // If the delegate didn't override RP ID selection then apply standard
+    // rules.
+    blink::mojom::AuthenticatorStatus domain_validation =
+        ValidateEffectiveDomain(caller_origin);
+    if (domain_validation != blink::mojom::AuthenticatorStatus::SUCCESS) {
+      ReportSecurityCheckFailure(
+          RelyingPartySecurityCheckFailure::kOpaqueOrNonSecureOrigin);
+      InvokeCallbackAndCleanup(std::move(callback), domain_validation, nullptr);
+      return;
+    }
+
+    rp_id =
+        GetRelyingPartyId(std::move(options->relying_party_id), caller_origin);
+    if (!rp_id) {
+      ReportSecurityCheckFailure(
+          RelyingPartySecurityCheckFailure::kRelyingPartyIdInvalid);
+      InvokeCallbackAndCleanup(
+          std::move(callback),
+          blink::mojom::AuthenticatorStatus::BAD_RELYING_PARTY_ID, nullptr);
+      return;
+    }
+  }
+
+  caller_origin_ = caller_origin;
+  relying_party_id_ = *rp_id;
+  options->relying_party_id = std::move(*rp_id);
+  request_delegate_->SetRelyingPartyId(relying_party_id_);
+
   // Save client data to return with the authenticator response.
-  // TODO(kpaulhamus): Fetch and add the Channel ID/Token Binding ID public key
-  // used to communicate with the origin.
   if (OriginIsCryptoTokenExtension(caller_origin)) {
     request_delegate_->DisableUI();
 
@@ -1071,15 +1101,13 @@
 void AuthenticatorCommon::IsUserVerifyingPlatformAuthenticatorAvailable(
     blink::mojom::Authenticator::
         IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) {
-  const std::string relying_party_id =
-      render_frame_host_->GetLastCommittedOrigin().host();
   // Use |request_delegate_| if a request is currently in progress; or create a
   // temporary request delegate otherwise.
   //
   // Note that |CreateRequestDelegate| may return nullptr if there is an active
   // |request_delegate_| already.
   std::unique_ptr<AuthenticatorRequestClientDelegate> maybe_request_delegate =
-      request_delegate_ ? nullptr : CreateRequestDelegate(relying_party_id);
+      request_delegate_ ? nullptr : CreateRequestDelegate();
   AuthenticatorRequestClientDelegate* request_delegate_ptr =
       request_delegate_ ? request_delegate_.get()
                         : maybe_request_delegate.get();
diff --git a/content/browser/webauth/authenticator_common.h b/content/browser/webauth/authenticator_common.h
index 954b3b6..0c06d75 100644
--- a/content/browser/webauth/authenticator_common.h
+++ b/content/browser/webauth/authenticator_common.h
@@ -97,7 +97,7 @@
 
  protected:
   virtual std::unique_ptr<AuthenticatorRequestClientDelegate>
-  CreateRequestDelegate(std::string relying_party_id);
+  CreateRequestDelegate();
 
   std::unique_ptr<AuthenticatorRequestClientDelegate> request_delegate_;
 
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index e8b431a..ad63d52 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -1645,6 +1645,92 @@
   EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status());
 }
 
+class OverrideRPIDAuthenticatorRequestDelegate
+    : public AuthenticatorRequestClientDelegate {
+ public:
+  OverrideRPIDAuthenticatorRequestDelegate() = default;
+  ~OverrideRPIDAuthenticatorRequestDelegate() override = default;
+
+  base::Optional<std::string> MaybeGetRelyingPartyIdOverride(
+      const std::string& claimed_rp_id,
+      const url::Origin& caller_origin) override {
+    CHECK_EQ(caller_origin.scheme(), "chrome-extension");
+    return caller_origin.Serialize();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OverrideRPIDAuthenticatorRequestDelegate);
+};
+
+class OverrideRPIDAuthenticatorContentBrowserClient
+    : public ContentBrowserClient {
+ public:
+  std::unique_ptr<AuthenticatorRequestClientDelegate>
+  GetWebAuthenticationRequestDelegate(
+      RenderFrameHost* render_frame_host) override {
+    return std::make_unique<OverrideRPIDAuthenticatorRequestDelegate>();
+  }
+};
+
+class OverrideRPIDAuthenticatorTest : public AuthenticatorImplTest {
+ public:
+  void SetUp() override {
+    AuthenticatorImplTest::SetUp();
+    old_client_ = SetBrowserClientForTesting(&test_client_);
+  }
+
+  void TearDown() override {
+    SetBrowserClientForTesting(old_client_);
+    AuthenticatorImplTest::TearDown();
+  }
+
+ private:
+  OverrideRPIDAuthenticatorContentBrowserClient test_client_;
+  ContentBrowserClient* old_client_ = nullptr;
+};
+
+TEST_F(OverrideRPIDAuthenticatorTest, ChromeExtensions) {
+  // Test that credentials can be created and used from an extension origin when
+  // permitted by the delegate.
+  url::AddStandardScheme("chrome-extension", url::SCHEME_WITH_HOST);
+  constexpr char kExtensionId[] = "abcdefg";
+  const std::string extension_origin =
+      std::string("chrome-extension://") + kExtensionId;
+  const std::string extension_page = extension_origin + "/test.html";
+  SimulateNavigation(GURL(extension_page));
+
+  mojo::Remote<blink::mojom::Authenticator> authenticator =
+      ConnectToAuthenticator();
+
+  std::vector<uint8_t> credential_id;
+  {
+    PublicKeyCredentialCreationOptionsPtr options =
+        GetTestPublicKeyCredentialCreationOptions();
+    options->relying_party.id = kExtensionId;
+
+    TestMakeCredentialCallback callback_receiver;
+    authenticator->MakeCredential(std::move(options),
+                                  callback_receiver.callback());
+    callback_receiver.WaitForCallback();
+    ASSERT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status());
+    credential_id = callback_receiver.value()->info->raw_id;
+  }
+
+  {
+    PublicKeyCredentialRequestOptionsPtr options =
+        GetTestPublicKeyCredentialRequestOptions();
+    options->relying_party_id = kExtensionId;
+    options->allow_credentials[0] = device::PublicKeyCredentialDescriptor(
+        device::CredentialType::kPublicKey, std::move(credential_id));
+
+    TestGetAssertionCallback callback_receiver;
+    authenticator->GetAssertion(std::move(options),
+                                callback_receiver.callback());
+    callback_receiver.WaitForCallback();
+    ASSERT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status());
+  }
+}
+
 enum class IndividualAttestation {
   REQUESTED,
   NOT_REQUESTED,
@@ -1756,8 +1842,7 @@
  public:
   std::unique_ptr<AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
-      RenderFrameHost* render_frame_host,
-      const std::string& relying_party_id) override {
+      RenderFrameHost* render_frame_host) override {
     if (return_null_delegate)
       return nullptr;
     return std::make_unique<TestAuthenticatorRequestDelegate>(
@@ -2538,8 +2623,8 @@
         mock_delegate_(std::move(mock_delegate)) {}
   ~FakeAuthenticatorCommon() override = default;
 
-  std::unique_ptr<AuthenticatorRequestClientDelegate> CreateRequestDelegate(
-      std::string relying_party_id) override {
+  std::unique_ptr<AuthenticatorRequestClientDelegate> CreateRequestDelegate()
+      override {
     DCHECK(mock_delegate_);
     return std::move(mock_delegate_);
   }
@@ -3181,8 +3266,7 @@
  public:
   std::unique_ptr<AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
-      RenderFrameHost* render_frame_host,
-      const std::string& relying_party_id) override {
+      RenderFrameHost* render_frame_host) override {
     return std::make_unique<PINTestAuthenticatorRequestDelegate>(
         supports_pin, expected, &failure_reason);
   }
@@ -3712,8 +3796,7 @@
  public:
   std::unique_ptr<AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
-      RenderFrameHost* render_frame_host,
-      const std::string& relying_party_id) override {
+      RenderFrameHost* render_frame_host) override {
     return std::make_unique<UVTokenTestAuthenticatorClientDelegate>();
   }
 };
@@ -3900,8 +3983,7 @@
  public:
   std::unique_ptr<AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
-      RenderFrameHost* render_frame_host,
-      const std::string& relying_party_id) override {
+      RenderFrameHost* render_frame_host) override {
     return std::make_unique<ResidentKeyTestAuthenticatorRequestDelegate>(
         expected_accounts, selected_user_id, &might_create_resident_credential,
         &failure_reason);
diff --git a/content/browser/webauth/webauth_browsertest.cc b/content/browser/webauth/webauth_browsertest.cc
index 3e2e8de..e1a75d8 100644
--- a/content/browser/webauth/webauth_browsertest.cc
+++ b/content/browser/webauth/webauth_browsertest.cc
@@ -368,8 +368,7 @@
 
   std::unique_ptr<AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
-      RenderFrameHost* render_frame_host,
-      const std::string& relying_party_id) override {
+      RenderFrameHost* render_frame_host) override {
     test_state_->delegate_create_count++;
     return std::make_unique<WebAuthBrowserTestClientDelegate>(test_state_);
   }
diff --git a/content/browser/worker_host/shared_worker_host_unittest.cc b/content/browser/worker_host/shared_worker_host_unittest.cc
index 143b262..3028e64 100644
--- a/content/browser/worker_host/shared_worker_host_unittest.cc
+++ b/content/browser/worker_host/shared_worker_host_unittest.cc
@@ -109,7 +109,7 @@
     base::WeakPtr<ServiceWorkerContainerHost> service_worker_container_host =
         ServiceWorkerContainerHost::CreateForWebWorker(
             helper_->context()->AsWeakPtr(), mock_render_process_host_.GetID(),
-            blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
+            blink::mojom::ServiceWorkerContainerType::kForSharedWorker,
             std::move(host_receiver), std::move(client_remote));
     service_worker_handle->OnCreatedProviderHost(std::move(provider_info));
     host->SetServiceWorkerHandle(std::move(service_worker_handle));
diff --git a/content/browser/worker_host/worker_script_loader_factory.cc b/content/browser/worker_host/worker_script_loader_factory.cc
index 7d5e5a1..85e13a72 100644
--- a/content/browser/worker_host/worker_script_loader_factory.cc
+++ b/content/browser/worker_host/worker_script_loader_factory.cc
@@ -16,7 +16,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 
 namespace content {
 
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index e120990..16726ba 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -151,6 +151,7 @@
 IPC_STRUCT_TRAITS_BEGIN(content::NavigationDownloadPolicy)
   IPC_STRUCT_TRAITS_MEMBER(observed_types)
   IPC_STRUCT_TRAITS_MEMBER(disallowed_types)
+  IPC_STRUCT_TRAITS_MEMBER(blocking_downloads_in_sandbox_enabled)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(blink::WebFloatSize)
diff --git a/content/gpu/gpu_sandbox_hook_linux.cc b/content/gpu/gpu_sandbox_hook_linux.cc
index 6fac07f1..992a56c0 100644
--- a/content/gpu/gpu_sandbox_hook_linux.cc
+++ b/content/gpu/gpu_sandbox_hook_linux.cc
@@ -6,6 +6,7 @@
 
 #include <dlfcn.h>
 #include <errno.h>
+#include <sys/stat.h>
 
 #include <memory>
 #include <sstream>
@@ -197,10 +198,26 @@
 }
 
 void AddIntelGpuWhitelist(std::vector<BrokerFilePermission>* permissions) {
+  static const char* const kReadOnlyList[] = {
+      "/dev/dri",
+      "/usr/share/vulkan/icd.d",
+      "/usr/share/vulkan/icd.d/intel_icd.x86_64.json"};
+  for (const char* item : kReadOnlyList)
+    permissions->push_back(BrokerFilePermission::ReadOnly(item));
+
   // TODO(hob): Whitelist all valid render node paths.
-  static const char* const kReadWriteList[] = {"/dev/dri/renderD128"};
-  for (const char* item : kReadWriteList)
-    permissions->push_back(BrokerFilePermission::ReadWrite(item));
+  static const char kRenderNodePath[] = "/dev/dri/renderD128";
+  struct stat st;
+  if (stat(kRenderNodePath, &st) == 0) {
+    permissions->push_back(BrokerFilePermission::ReadWrite(kRenderNodePath));
+
+    uint32_t major = (static_cast<uint32_t>(st.st_rdev) >> 8) & 0xff;
+    uint32_t minor = static_cast<uint32_t>(st.st_rdev) & 0xff;
+    std::string char_device_path =
+        base::StringPrintf("/sys/dev/char/%u:%u/", major, minor);
+    permissions->push_back(
+        BrokerFilePermission::ReadOnlyRecursive(char_device_path));
+  }
 }
 
 void AddArmGpuWhitelist(std::vector<BrokerFilePermission>* permissions) {
@@ -387,7 +404,8 @@
   command_set.set(sandbox::syscall_broker::COMMAND_ACCESS);
   command_set.set(sandbox::syscall_broker::COMMAND_OPEN);
   command_set.set(sandbox::syscall_broker::COMMAND_STAT);
-  if (IsChromeOS() && options.use_amd_specific_policies) {
+  if (IsChromeOS() && (options.use_amd_specific_policies ||
+                       options.use_intel_specific_policies)) {
     command_set.set(sandbox::syscall_broker::COMMAND_READLINK);
   }
   return command_set;
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
index 39293ff6d..b4e7957 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
@@ -29,4 +29,6 @@
     public static final String SERVICE_GROUP_IMPORTANCE = "ServiceGroupImportance";
 
     public static final String WEB_NFC = "WebNFC";
+
+    public static final String WEBXR_PERMISSIONS_API = "WebXrPermissionsApi";
 }
diff --git a/content/public/browser/android/compositor.h b/content/public/browser/android/compositor.h
index 2bef4f3..fd0018b 100644
--- a/content/public/browser/android/compositor.h
+++ b/content/public/browser/android/compositor.h
@@ -7,6 +7,7 @@
 
 #include "base/callback.h"
 #include "cc/resources/ui_resource_bitmap.h"
+#include "cc/trees/layer_tree_host_client.h"
 #include "content/common/content_export.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "ui/android/resources/ui_resource_provider.h"
@@ -100,6 +101,14 @@
   // Evicts the cache entry created from the cached call above.
   virtual void EvictCachedBackBuffer() = 0;
 
+  // Registers a callback that is run when the next frame successfully makes it
+  // to the screen (it's entirely possible some frames may be dropped between
+  // the time this is called and the callback is run).
+  using PresentationTimeCallback =
+      base::OnceCallback<void(const gfx::PresentationFeedback&)>;
+  virtual void RequestPresentationTimeForNextFrame(
+      PresentationTimeCallback callback) = 0;
+
  protected:
   Compositor() {}
 };
diff --git a/content/public/browser/authenticator_request_client_delegate.cc b/content/public/browser/authenticator_request_client_delegate.cc
index 1778053..ff6b1552 100644
--- a/content/public/browser/authenticator_request_client_delegate.cc
+++ b/content/public/browser/authenticator_request_client_delegate.cc
@@ -23,6 +23,16 @@
 AuthenticatorRequestClientDelegate::~AuthenticatorRequestClientDelegate() =
     default;
 
+base::Optional<std::string>
+AuthenticatorRequestClientDelegate::MaybeGetRelyingPartyIdOverride(
+    const std::string& claimed_relying_party_id,
+    const url::Origin& caller_origin) {
+  return base::nullopt;
+}
+
+void AuthenticatorRequestClientDelegate::SetRelyingPartyId(const std::string&) {
+}
+
 bool AuthenticatorRequestClientDelegate::DoesBlockRequestOnFailure(
     InterestingFailureReason reason) {
   return false;
diff --git a/content/public/browser/authenticator_request_client_delegate.h b/content/public/browser/authenticator_request_client_delegate.h
index 7afae3e7..1be14d6e 100644
--- a/content/public/browser/authenticator_request_client_delegate.h
+++ b/content/public/browser/authenticator_request_client_delegate.h
@@ -59,6 +59,25 @@
   AuthenticatorRequestClientDelegate();
   ~AuthenticatorRequestClientDelegate() override;
 
+  // Permits the embedder to override normal relying party ID processing. Is
+  // given the untrusted, claimed relying party ID from the WebAuthn call, as
+  // well as the origin of the caller, and may return a relying party ID to
+  // override normal validation.
+  //
+  // This is an access-control decision: RP IDs are used to control access to
+  // credentials so thought is required before allowing an origin to assert an
+  // RP ID. RP ID strings may be stored on authenticators and may later appear
+  // in management UI.
+  virtual base::Optional<std::string> MaybeGetRelyingPartyIdOverride(
+      const std::string& claimed_relying_party_id,
+      const url::Origin& caller_origin);
+
+  // SetRelyingPartyId sets the RP ID for this request. This is called after
+  // |MaybeGetRelyingPartyIdOverride| is given the opportunity to affect this
+  // value. For typical origins, the RP ID is just a domain name, but
+  // |MaybeGetRelyingPartyIdOverride| may return other forms of strings.
+  virtual void SetRelyingPartyId(const std::string& rp_id);
+
   // Called when the request fails for the given |reason|.
   //
   // Embedders may return true if they want AuthenticatorImpl to hold off from
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index a6042e5b..bcdc73f 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -874,8 +874,7 @@
 
 std::unique_ptr<AuthenticatorRequestClientDelegate>
 ContentBrowserClient::GetWebAuthenticationRequestDelegate(
-    RenderFrameHost* render_frame_host,
-    const std::string& relying_party_id) {
+    RenderFrameHost* render_frame_host) {
   return std::make_unique<AuthenticatorRequestClientDelegate>();
 }
 
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 8256cd8..5807d1d7 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1547,11 +1547,9 @@
   // being serviced in a given RenderFrame. The instance is guaranteed to be
   // destroyed before the RenderFrame goes out of scope. The embedder may choose
   // to return nullptr to indicate that the request cannot be serviced right
-  // now. |relying_party_id| is the RP ID from Webauthn, essentially a domain
-  // name.
+  // now.
   virtual std::unique_ptr<AuthenticatorRequestClientDelegate>
-  GetWebAuthenticationRequestDelegate(RenderFrameHost* render_frame_host,
-                                      const std::string& relying_party_id);
+  GetWebAuthenticationRequestDelegate(RenderFrameHost* render_frame_host);
 
   // Get platform ClientCertStore. May return nullptr. Called on the UI thread.
   virtual std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
diff --git a/content/public/common/common_param_traits_macros.h b/content/public/common/common_param_traits_macros.h
index f3f854e..fe4ca0fa 100644
--- a/content/public/common/common_param_traits_macros.h
+++ b/content/public/common/common_param_traits_macros.h
@@ -24,7 +24,6 @@
 #include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
 #include "third_party/blink/public/platform/web_drag_operation.h"
 #include "third_party/blink/public/platform/web_history_scroll_restoration_type.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -82,11 +81,6 @@
     content::AutoplayPolicy::kNoUserGestureRequired,
     content::AutoplayPolicy::kDocumentUserActivationRequired)
 
-IPC_STRUCT_TRAITS_BEGIN(blink::WebPoint)
-  IPC_STRUCT_TRAITS_MEMBER(x)
-  IPC_STRUCT_TRAITS_MEMBER(y)
-IPC_STRUCT_TRAITS_END()
-
 IPC_STRUCT_TRAITS_BEGIN(blink::WebRect)
   IPC_STRUCT_TRAITS_MEMBER(x)
   IPC_STRUCT_TRAITS_MEMBER(y)
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index f40d4c2..5ce51cb 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -468,12 +468,6 @@
     "SendBeaconThrowForBlobWithNonSimpleType",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables long running message dispatch for service workers.
-// This is a temporary addition only to be used for the Android Messages
-// integration with ChromeOS (http://crbug.com/823256).
-const base::Feature kServiceWorkerLongRunningMessage{
-    "ServiceWorkerLongRunningMessage", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // If enabled, ServiceWorkerContextCore lives on the UI thread rather than the
 // IO thread.
 // https://crbug.com/824858
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index bf8c1c19..d57de74 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -105,7 +105,6 @@
 CONTENT_EXPORT extern const base::Feature kSavePageAsWebBundle;
 CONTENT_EXPORT extern const base::Feature
     kSendBeaconThrowForBlobWithNonSimpleType;
-CONTENT_EXPORT extern const base::Feature kServiceWorkerLongRunningMessage;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerOnUI;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerPaymentApps;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerPrefersUnusedProcess;
diff --git a/content/public/common/navigation_policy.h b/content/public/common/navigation_policy.h
index 930eb0f..363b7c1 100644
--- a/content/public/common/navigation_policy.h
+++ b/content/public/common/navigation_policy.h
@@ -88,6 +88,8 @@
   // a download, the download should be dropped.
   std::bitset<static_cast<size_t>(NavigationDownloadType::kMaxValue) + 1>
       disallowed_types;
+
+  bool blocking_downloads_in_sandbox_enabled = false;
 };
 
 }  // namespace content
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index 1f1186c..43b47f5f 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -257,18 +257,6 @@
   if (use_software_compositing_) {
     command_line->AppendSwitch(switches::kDisableGpu);
     command_line->RemoveSwitch(switches::kDisableSoftwareCompositingFallback);
-#if defined(USE_X11)
-    // If Vulkan is enabled, make sure it uses SwiftShader instead of native,
-    // though only on platforms where it is supported.
-    // TODO(samans): Support Swiftshader on more platforms.
-    // https://crbug.com/963988
-    if (command_line->HasSwitch(switches::kUseVulkan)) {
-      command_line->AppendSwitchASCII(
-          switches::kUseVulkan, switches::kVulkanImplementationNameSwiftshader);
-      command_line->AppendSwitchASCII(
-          switches::kGrContextType, switches::kGrContextTypeVulkan);
-    }
-#endif
   }
 
   // The layout of windows on screen is unpredictable during tests, so disable
diff --git a/content/renderer/accessibility/blink_ax_action_target.cc b/content/renderer/accessibility/blink_ax_action_target.cc
index 110568b..5d97840 100644
--- a/content/renderer/accessibility/blink_ax_action_target.cc
+++ b/content/renderer/accessibility/blink_ax_action_target.cc
@@ -10,7 +10,6 @@
 
 using blink::WebAXObject;
 using blink::WebFloatRect;
-using blink::WebPoint;
 using blink::WebRect;
 
 namespace content {
@@ -70,18 +69,15 @@
 }
 
 gfx::Point BlinkAXActionTarget::GetScrollOffset() const {
-  WebPoint offset = web_ax_object_.GetScrollOffset();
-  return gfx::Point(offset.x, offset.y);
+  return web_ax_object_.GetScrollOffset();
 }
 
 gfx::Point BlinkAXActionTarget::MinimumScrollOffset() const {
-  WebPoint offset = web_ax_object_.MinimumScrollOffset();
-  return gfx::Point(offset.x, offset.y);
+  return web_ax_object_.MinimumScrollOffset();
 }
 
 gfx::Point BlinkAXActionTarget::MaximumScrollOffset() const {
-  WebPoint offset = web_ax_object_.MaximumScrollOffset();
-  return gfx::Point(offset.x, offset.y);
+  return web_ax_object_.MaximumScrollOffset();
 }
 
 bool BlinkAXActionTarget::SetAccessibilityFocus() const {
@@ -89,7 +85,7 @@
 }
 
 void BlinkAXActionTarget::SetScrollOffset(const gfx::Point& point) const {
-  web_ax_object_.SetScrollOffset(WebPoint(point.x(), point.y()));
+  web_ax_object_.SetScrollOffset(point);
 }
 
 bool BlinkAXActionTarget::SetSelected(bool selected) const {
@@ -139,7 +135,7 @@
 }
 
 bool BlinkAXActionTarget::ScrollToGlobalPoint(const gfx::Point& point) const {
-  return web_ax_object_.ScrollToGlobalPoint(WebPoint(point.x(), point.y()));
+  return web_ax_object_.ScrollToGlobalPoint(point);
 }
 
 }  // namespace content
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index cf7679c..9ef02d5 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -50,7 +50,6 @@
 using blink::WebFloatRect;
 using blink::WebLocalFrame;
 using blink::WebNode;
-using blink::WebPoint;
 using blink::WebRect;
 using blink::WebSettings;
 using blink::WebView;
@@ -800,8 +799,7 @@
       target->SetAccessibilityFocus();
       break;
     case ax::mojom::Action::kSetScrollOffset:
-      target->SetScrollOffset(
-          WebPoint(data.target_point.x(), data.target_point.y()));
+      target->SetScrollOffset(data.target_point);
       break;
     case ax::mojom::Action::kSetSelection:
       anchor->SetSelection(anchor.get(), data.anchor_offset, focus.get(),
diff --git a/content/renderer/context_menu_params_builder.cc b/content/renderer/context_menu_params_builder.cc
index 4b13917..747077b 100644
--- a/content/renderer/context_menu_params_builder.cc
+++ b/content/renderer/context_menu_params_builder.cc
@@ -18,8 +18,8 @@
     const blink::WebContextMenuData& data) {
   ContextMenuParams params;
   params.media_type = data.media_type;
-  params.x = data.mouse_position.x;
-  params.y = data.mouse_position.y;
+  params.x = data.mouse_position.x();
+  params.y = data.mouse_position.y();
   params.link_url = data.link_url;
   params.unfiltered_link_url = data.link_url;
   params.src_url = data.src_url;
diff --git a/content/renderer/history_serialization.cc b/content/renderer/history_serialization.cc
index 25a60d0..249f6f1 100644
--- a/content/renderer/history_serialization.cc
+++ b/content/renderer/history_serialization.cc
@@ -14,7 +14,6 @@
 #include "content/renderer/loader/web_url_request_util.h"
 #include "third_party/blink/public/platform/web_data.h"
 #include "third_party/blink/public/platform/web_http_body.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/web_history_item.h"
diff --git a/content/renderer/input/render_widget_input_handler.cc b/content/renderer/input/render_widget_input_handler.cc
index 0c4527a..6534468 100644
--- a/content/renderer/input/render_widget_input_handler.cc
+++ b/content/renderer/input/render_widget_input_handler.cc
@@ -267,8 +267,8 @@
     point_in_pixel = gfx::ConvertPointToPixel(
         widget_->GetOriginalScreenInfo().device_scale_factor, point_in_pixel);
   }
-  blink::WebHitTestResult result = widget_->GetWebWidget()->HitTestResultAt(
-      blink::WebPoint(ToRoundedPoint(point_in_pixel)));
+  blink::WebHitTestResult result =
+      widget_->GetWebWidget()->HitTestResultAt(ToRoundedPoint(point_in_pixel));
 
   blink::WebNode result_node = result.GetNode();
   *local_point = gfx::PointF(point);
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 33dbeb24..7b9393c4 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -2732,8 +2732,7 @@
     return PP_FALSE;
 
   auto custom_cursor = std::make_unique<WebCursorInfo>(ui::CursorType::kCustom);
-  custom_cursor->hot_spot.x = hot_spot->x;
-  custom_cursor->hot_spot.y = hot_spot->y;
+  custom_cursor->hot_spot.SetPoint(hot_spot->x, hot_spot->y);
 
   SkBitmap bitmap(image_data->GetMappedBitmap());
   // Make a deep copy, so that the cursor remains valid even after the original
diff --git a/content/renderer/pepper/pepper_webplugin_impl.cc b/content/renderer/pepper/pepper_webplugin_impl.cc
index 2c78c48..9f2e57b 100644
--- a/content/renderer/pepper/pepper_webplugin_impl.cc
+++ b/content/renderer/pepper/pepper_webplugin_impl.cc
@@ -26,7 +26,6 @@
 #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/public/web/web_associated_url_loader_client.h"
@@ -44,7 +43,6 @@
 using blink::WebPlugin;
 using blink::WebPluginContainer;
 using blink::WebPluginParams;
-using blink::WebPoint;
 using blink::WebPrintParams;
 using blink::WebRect;
 using blink::WebSize;
@@ -384,7 +382,7 @@
   return false;
 }
 
-WebURL PepperWebPluginImpl::LinkAtPosition(const WebPoint& position) const {
+WebURL PepperWebPluginImpl::LinkAtPosition(const gfx::Point& position) const {
   // Re-entrancy may cause JS to try to execute script on the plugin before it
   // is fully initialized. See: crbug.com/715747.
   if (!instance_)
diff --git a/content/renderer/pepper/pepper_webplugin_impl.h b/content/renderer/pepper/pepper_webplugin_impl.h
index 7c1f1c6..d6a5e5b 100644
--- a/content/renderer/pepper/pepper_webplugin_impl.h
+++ b/content/renderer/pepper/pepper_webplugin_impl.h
@@ -70,7 +70,7 @@
   bool ExecuteEditCommand(const blink::WebString& name) override;
   bool ExecuteEditCommand(const blink::WebString& name,
                           const blink::WebString& value) override;
-  blink::WebURL LinkAtPosition(const blink::WebPoint& position) const override;
+  blink::WebURL LinkAtPosition(const gfx::Point& position) const override;
   bool GetPrintPresetOptionsFromDocument(
       blink::WebPrintPresetOptions* preset_options) override;
   bool IsPdfPlugin() override;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 1b98fc24..eb9eb19 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -182,7 +182,6 @@
 #include "third_party/blink/public/platform/web_http_body.h"
 #include "third_party/blink/public/platform/web_media_player.h"
 #include "third_party/blink/public/platform/web_media_player_source.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/public/platform/web_scroll_into_view_params.h"
 #include "third_party/blink/public/platform/web_string.h"
@@ -274,7 +273,6 @@
 using blink::WebNode;
 using blink::WebPluginDocument;
 using blink::WebPluginParams;
-using blink::WebPoint;
 using blink::WebPopupMenuInfo;
 using blink::WebRange;
 using blink::WebRect;
@@ -1755,6 +1753,9 @@
       }
     }
   }
+
+  download_policy->blocking_downloads_in_sandbox_enabled =
+      blocking_downloads_in_sandbox_enabled;
 }
 
 blink::WebURL RenderFrameImpl::OverrideFlashEmbedWithHTML(
@@ -3540,6 +3541,13 @@
   FillNavigationParamsRequest(*common_params, *commit_params,
                               navigation_params.get());
   navigation_params->url = GURL(kUnreachableWebDataURL);
+  // FillNavigationParamsRequest() sets the |navigation_params->http_method| to
+  // the original method of the request. In successful page loads,
+  // |navigation_params->redirects| also gets populated and the redirects are
+  // later replayed to update the method. However, in the case of an error page
+  // load, the redirects are neither populated nor replayed. Hence |http_method|
+  // needs to be manually set to the final method.
+  navigation_params->http_method = WebString::FromASCII(common_params->method);
   navigation_params->error_code = error_code;
 
   if (!ShouldDisplayErrorPageForFailedLoad(error_code, common_params->url)) {
@@ -6210,7 +6218,7 @@
   blink::WebFloatRect viewport_position(location.x(), location.y(), 0, 0);
   GetLocalRootRenderWidget()->ConvertWindowToViewport(&viewport_position);
   frame_->PerformMediaPlayerAction(
-      WebPoint(viewport_position.x, viewport_position.y), action);
+      gfx::Point(viewport_position.x, viewport_position.y), action);
 }
 
 #if BUILDFLAG(USE_EXTERNAL_POPUP_MENU)
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 19ae6123..bfcc75d9 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -706,7 +706,7 @@
 void RenderFrameProxy::Navigate(const blink::WebURLRequest& request,
                                 bool should_replace_current_entry,
                                 bool is_opener_navigation,
-                                bool has_download_sandbox_flag,
+                                bool initiator_frame_has_download_sandbox_flag,
                                 bool blocking_downloads_in_sandbox_enabled,
                                 bool initiator_frame_is_ad,
                                 mojo::ScopedMessagePipeHandle blob_url_token) {
@@ -727,13 +727,15 @@
   params.triggering_event_info = blink::TriggeringEventInfo::kUnknown;
   params.blob_url_token = blob_url_token.release();
 
-  // Note: For the AdFrame download policy here it only covers the case where
-  // the navigation initiator frame is ad. The download_policy may be further
-  // augmented in RenderFrameProxyHost::OnOpenURL if the navigating frame is ad.
+  // Note: For the AdFrame/Sandbox download policy here it only covers the case
+  // where the navigation initiator frame is ad. The download_policy may be
+  // further augmented in RenderFrameProxyHost::OnOpenURL if the navigating
+  // frame is ad or sandboxed.
   RenderFrameImpl::MaybeSetDownloadFramePolicy(
       is_opener_navigation, request, web_frame_->GetSecurityOrigin(),
-      has_download_sandbox_flag, blocking_downloads_in_sandbox_enabled,
-      initiator_frame_is_ad, &params.download_policy);
+      initiator_frame_has_download_sandbox_flag,
+      blocking_downloads_in_sandbox_enabled, initiator_frame_is_ad,
+      &params.download_policy);
 
   Send(new FrameHostMsg_OpenURL(routing_id_, params));
 }
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index b34f5f27..7526117 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -181,7 +181,7 @@
   void Navigate(const blink::WebURLRequest& request,
                 bool should_replace_current_entry,
                 bool is_opener_navigation,
-                bool has_download_sandbox_flag,
+                bool initiator_frame_has_download_sandbox_flag,
                 bool blocking_downloads_in_sandbox_enabled,
                 bool initiator_frame_is_ad,
                 mojo::ScopedMessagePipeHandle blob_url_token) override;
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 596e71de..8b6362f 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -106,7 +106,6 @@
 #include "third_party/blink/public/platform/web_http_body.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/public/platform/web_network_state_notifier.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/public/platform/web_size.h"
@@ -199,7 +198,6 @@
 using blink::WebNavigationPolicy;
 using blink::WebNavigationType;
 using blink::WebNode;
-using blink::WebPoint;
 using blink::WebRect;
 using blink::WebRuntimeFeatures;
 using blink::WebSandboxFlags;
@@ -1979,7 +1977,7 @@
 }
 
 void RenderViewImpl::OnAnimateDoubleTapZoomInMainFrame(
-    const blink::WebPoint& point,
+    const gfx::Point& point,
     const blink::WebRect& bound) {
   webview()->AnimateDoubleTapZoom(point, bound);
 }
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 7c9264c..edefd46 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -431,7 +431,7 @@
   void OnEnablePreferredSizeChangedMode();
   void OnPluginActionAt(const gfx::Point& location,
                         const blink::PluginAction& action);
-  void OnAnimateDoubleTapZoomInMainFrame(const blink::WebPoint& point,
+  void OnAnimateDoubleTapZoomInMainFrame(const gfx::Point& point,
                                          const blink::WebRect& rect_to_zoom);
   void OnZoomToFindInPageRect(const blink::WebRect& rect_to_zoom);
   void OnMoveOrResizeStarted();
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index b7343c4..5ec4bce 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -88,7 +88,6 @@
 #include "third_party/blink/public/platform/web_cursor_info.h"
 #include "third_party/blink/public/platform/web_drag_data.h"
 #include "third_party/blink/public/platform/web_drag_operation.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/public/platform/web_size.h"
@@ -3326,7 +3325,7 @@
 }
 
 void RenderWidget::AnimateDoubleTapZoomInMainFrame(
-    const blink::WebPoint& point,
+    const gfx::Point& point,
     const blink::WebRect& rect_to_zoom) {
   // Only oopif subframes should be sending this message.
   DCHECK(!delegate());
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 946c2254..5a5416a 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -422,7 +422,7 @@
   void SetHaveScrollEventHandlers(bool have_handlers) override;
   void SetNeedsLowLatencyInput(bool) override;
   void SetNeedsUnbufferedInputForDebugger(bool) override;
-  void AnimateDoubleTapZoomInMainFrame(const blink::WebPoint& point,
+  void AnimateDoubleTapZoomInMainFrame(const gfx::Point& point,
                                        const blink::WebRect& bounds) override;
   void ZoomToFindInPageRectInMainFrame(
       const blink::WebRect& rect_to_zoom) override;
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index 76b80d1..dc8951eb 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -37,7 +37,6 @@
 using blink::WebInputEventResult;
 using blink::WebMouseEvent;
 using blink::WebMouseWheelEvent;
-using blink::WebPoint;
 using blink::WebRect;
 using blink::WebSize;
 using blink::WebString;
diff --git a/content/renderer/service_worker/service_worker_network_provider_for_frame.cc b/content/renderer/service_worker/service_worker_network_provider_for_frame.cc
index a1ed89d..29100e0 100644
--- a/content/renderer/service_worker/service_worker_network_provider_for_frame.cc
+++ b/content/renderer/service_worker/service_worker_network_provider_for_frame.cc
@@ -69,7 +69,7 @@
   auto provider =
       base::WrapUnique(new ServiceWorkerNetworkProviderForFrame(frame));
   provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>(
-      blink::mojom::ServiceWorkerProviderType::kForWindow,
+      blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(provider_info->client_receiver),
       std::move(provider_info->host_remote), std::move(controller_info),
       std::move(fallback_loader_factory));
diff --git a/content/renderer/service_worker/service_worker_provider_context.cc b/content/renderer/service_worker/service_worker_provider_context.cc
index 676e8fe..43c3f10 100644
--- a/content/renderer/service_worker/service_worker_provider_context.cc
+++ b/content/renderer/service_worker/service_worker_provider_context.cc
@@ -62,14 +62,14 @@
 }  // namespace
 
 ServiceWorkerProviderContext::ServiceWorkerProviderContext(
-    blink::mojom::ServiceWorkerProviderType provider_type,
+    blink::mojom::ServiceWorkerContainerType container_type,
     mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainer>
         receiver,
     mojo::PendingAssociatedRemote<blink::mojom::ServiceWorkerContainerHost>
         host_remote,
     blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
     scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory)
-    : provider_type_(provider_type),
+    : container_type_(container_type),
       main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       receiver_(this, std::move(receiver)),
       fallback_loader_factory_(std::move(fallback_loader_factory)) {
@@ -168,8 +168,8 @@
 
 blink::mojom::ServiceWorkerContainerHost*
 ServiceWorkerProviderContext::container_host() const {
-  DCHECK_EQ(blink::mojom::ServiceWorkerProviderType::kForWindow,
-            provider_type_);
+  DCHECK_EQ(blink::mojom::ServiceWorkerContainerType::kForWindow,
+            container_type_);
   return container_host_ ? container_host_.get() : nullptr;
 }
 
@@ -243,8 +243,8 @@
 
 void ServiceWorkerProviderContext::NotifyExecutionReady() {
   DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
-  DCHECK_EQ(provider_type(),
-            blink::mojom::ServiceWorkerProviderType::kForWindow)
+  DCHECK_EQ(container_type(),
+            blink::mojom::ServiceWorkerContainerType::kForWindow)
       << "only windows need to send this message; shared workers have "
          "execution ready set on the browser-side when the response is "
          "committed";
diff --git a/content/renderer/service_worker/service_worker_provider_context.h b/content/renderer/service_worker/service_worker_provider_context.h
index 83527ee..b0bac60 100644
--- a/content/renderer/service_worker/service_worker_provider_context.h
+++ b/content/renderer/service_worker/service_worker_provider_context.h
@@ -26,9 +26,9 @@
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_container.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom-forward.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom-forward.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom-forward.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-forward.h"
 #include "third_party/blink/public/mojom/timing/worker_timing_container.mojom-forward.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-forward.h"
@@ -91,7 +91,7 @@
   // This is non-null only if the provider is created for controllees, and if
   // the loading context, e.g. a frame, provides it.
   ServiceWorkerProviderContext(
-      blink::mojom::ServiceWorkerProviderType provider_type,
+      blink::mojom::ServiceWorkerContainerType container_type,
       mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainer>
           receiver,
       mojo::PendingAssociatedRemote<blink::mojom::ServiceWorkerContainerHost>
@@ -99,8 +99,8 @@
       blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
       scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory);
 
-  blink::mojom::ServiceWorkerProviderType provider_type() const {
-    return provider_type_;
+  blink::mojom::ServiceWorkerContainerType container_type() const {
+    return container_type_;
   }
 
   // Returns version id of the controller service worker object
@@ -234,7 +234,7 @@
   // ServiceWorker, or nullptr if no controller is attached.
   network::mojom::URLLoaderFactory* GetSubresourceLoaderFactoryInternal();
 
-  const blink::mojom::ServiceWorkerProviderType provider_type_;
+  const blink::mojom::ServiceWorkerContainerType container_type_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
 
   // This keeps the connection to the content::ServiceWorkerProviderHost in the
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 e5233c0..294ce18 100644
--- a/content/renderer/service_worker/service_worker_provider_context_unittest.cc
+++ b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
@@ -30,9 +30,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_container.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_error_type.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h"
@@ -337,7 +337,7 @@
     auto container_receiver =
         container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
     auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
-        blink::mojom::ServiceWorkerProviderType::kForWindow,
+        blink::mojom::ServiceWorkerContainerType::kForWindow,
         std::move(container_receiver), host_remote.Unbind(),
         nullptr /* controller_info */, nullptr /* loader_factory*/);
 
@@ -379,7 +379,7 @@
     auto container_receiver =
         container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
     auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
-        blink::mojom::ServiceWorkerProviderType::kForWindow,
+        blink::mojom::ServiceWorkerContainerType::kForWindow,
         std::move(container_receiver), host_remote.Unbind(),
         nullptr /* controller_info */, nullptr /* loader_factory*/);
     auto provider_impl =
@@ -412,7 +412,7 @@
   auto container_receiver =
       container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
-      blink::mojom::ServiceWorkerProviderType::kForWindow,
+      blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(container_receiver), host_remote.Unbind(),
       nullptr /* controller_info */, nullptr /* loader_factory*/);
   auto provider_impl =
@@ -463,10 +463,10 @@
   controller_info1->object_info = std::move(object_info1);
   controller_info1->remote_controller = remote_controller1.Unbind();
 
-  // Make the ServiceWorkerProviderContext, pasing it the controller, container,
-  // and container host.
+  // Make the ServiceWorkerProviderContext, passing it the controller,
+  // container, and container host.
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
-      blink::mojom::ServiceWorkerProviderType::kForWindow,
+      blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(container_receiver), host_remote.Unbind(),
       std::move(controller_info1), loader_factory_);
 
@@ -652,7 +652,7 @@
   auto container_receiver =
       container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
-      blink::mojom::ServiceWorkerProviderType::kForWindow,
+      blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(container_receiver),
       mojo::NullAssociatedRemote() /* host_remote */,
       std::move(controller_info), loader_factory_);
@@ -680,7 +680,7 @@
   auto container_receiver =
       container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
-      blink::mojom::ServiceWorkerProviderType::kForWindow,
+      blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(container_receiver), host_remote.Unbind(),
       nullptr /* controller_info */, nullptr /* loader_factory*/);
   auto provider_impl =
@@ -709,7 +709,7 @@
   auto container_receiver =
       container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
-      blink::mojom::ServiceWorkerProviderType::kForWindow,
+      blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(container_receiver), host_remote.Unbind(),
       nullptr /* controller_info */, nullptr /* loader_factory*/);
   auto provider_impl =
@@ -762,7 +762,7 @@
 
   // Make the provider context.
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
-      blink::mojom::ServiceWorkerProviderType::kForWindow,
+      blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(container_receiver), host_remote.Unbind(),
       std::move(controller_info), loader_factory_);
 
@@ -808,10 +808,10 @@
   auto container_receiver =
       container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
 
-  // Make the ServiceWorkerProviderContext, pasing it the controller, container,
-  // and container host.
+  // Make the ServiceWorkerProviderContext, passing it the controller,
+  // container, and container host.
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
-      blink::mojom::ServiceWorkerProviderType::kForWindow,
+      blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(container_receiver), host_remote.Unbind(),
       std::move(controller_info), loader_factory_);
 
diff --git a/content/renderer/service_worker/web_service_worker_provider_impl.cc b/content/renderer/service_worker/web_service_worker_provider_impl.cc
index c09b795..c62ec2e 100644
--- a/content/renderer/service_worker/web_service_worker_provider_impl.cc
+++ b/content/renderer/service_worker/web_service_worker_provider_impl.cc
@@ -15,7 +15,7 @@
 #include "content/renderer/service_worker/service_worker_type_converters.h"
 #include "content/renderer/worker/fetch_client_settings_object_helpers.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h"
@@ -36,8 +36,8 @@
     ServiceWorkerProviderContext* context)
     : context_(context), provider_client_(nullptr) {
   DCHECK(context_);
-  DCHECK_EQ(context_->provider_type(),
-            blink::mojom::ServiceWorkerProviderType::kForWindow);
+  DCHECK_EQ(context_->container_type(),
+            blink::mojom::ServiceWorkerContainerType::kForWindow);
   context_->SetWebServiceWorkerProvider(weak_factory_.GetWeakPtr());
 }
 
diff --git a/content/renderer/text_input_client_observer.cc b/content/renderer/text_input_client_observer.cc
index 9308dff9..49a0dfe 100644
--- a/content/renderer/text_input_client_observer.cc
+++ b/content/renderer/text_input_client_observer.cc
@@ -17,7 +17,6 @@
 #include "content/renderer/render_widget.h"
 #include "ipc/ipc_message.h"
 #include "ppapi/buildflags/buildflags.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/mac/web_substring_util.h"
@@ -93,7 +92,7 @@
 #endif
 
 void TextInputClientObserver::OnStringAtPoint(gfx::Point point) {
-  blink::WebPoint baseline_point;
+  gfx::Point baseline_point;
   NSAttributedString* string = nil;
 
   if (auto* frame_widget = GetWebFrameWidget()) {
@@ -108,10 +107,9 @@
 }
 
 void TextInputClientObserver::OnCharacterIndexForPoint(gfx::Point point) {
-  blink::WebPoint web_point(point);
   uint32_t index = 0U;
   if (auto* frame = GetFocusedFrame())
-    index = static_cast<uint32_t>(frame->CharacterIndexForPoint(web_point));
+    index = static_cast<uint32_t>(frame->CharacterIndexForPoint(point));
 
   Send(new TextInputClientReplyMsg_GotCharacterIndexForPoint(MSG_ROUTING_NONE,
                                                              index));
@@ -144,7 +142,7 @@
 }
 
 void TextInputClientObserver::OnStringForRange(gfx::Range range) {
-  blink::WebPoint baseline_point;
+  gfx::Point baseline_point;
   NSAttributedString* string = nil;
   blink::WebLocalFrame* frame = GetFocusedFrame();
   // TODO(yabinh): Null check should not be necessary.
diff --git a/content/renderer/worker/dedicated_worker_host_factory_client.cc b/content/renderer/worker/dedicated_worker_host_factory_client.cc
index ae618bb..2d39b94a 100644
--- a/content/renderer/worker/dedicated_worker_host_factory_client.cc
+++ b/content/renderer/worker/dedicated_worker_host_factory_client.cc
@@ -144,7 +144,7 @@
   if (service_worker_provider_info) {
     service_worker_provider_context_ =
         base::MakeRefCounted<ServiceWorkerProviderContext>(
-            blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker,
+            blink::mojom::ServiceWorkerContainerType::kForDedicatedWorker,
             std::move(service_worker_provider_info->client_receiver),
             std::move(service_worker_provider_info->host_remote),
             std::move(controller_info), subresource_loader_factory_bundle_);
diff --git a/content/renderer/worker/embedded_shared_worker_stub.cc b/content/renderer/worker/embedded_shared_worker_stub.cc
index 7200d59..9ceba95b 100644
--- a/content/renderer/worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/worker/embedded_shared_worker_stub.cc
@@ -91,7 +91,7 @@
   if (service_worker_provider_info) {
     service_worker_provider_context_ =
         base::MakeRefCounted<ServiceWorkerProviderContext>(
-            blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker,
+            blink::mojom::ServiceWorkerContainerType::kForDedicatedWorker,
             std::move(service_worker_provider_info->client_receiver),
             std::move(service_worker_provider_info->host_remote),
             std::move(controller_info), subresource_loader_factory_bundle_);
diff --git a/content/shell/renderer/web_test/blink_test_runner.cc b/content/shell/renderer/web_test/blink_test_runner.cc
index 82772ec4..2e88791 100644
--- a/content/shell/renderer/web_test/blink_test_runner.cc
+++ b/content/shell/renderer/web_test/blink_test_runner.cc
@@ -66,7 +66,6 @@
 #include "third_party/blink/public/platform/file_path_conversion.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/public/platform/web_string.h"
@@ -95,7 +94,6 @@
 using blink::WebFrame;
 using blink::WebHistoryItem;
 using blink::WebLocalFrame;
-using blink::WebPoint;
 using blink::WebRect;
 using blink::WebScriptSource;
 using blink::WebSize;
diff --git a/content/shell/test_runner/event_sender.cc b/content/shell/test_runner/event_sender.cc
index abd3ec0..08faef6 100644
--- a/content/shell/test_runner/event_sender.cc
+++ b/content/shell/test_runner/event_sender.cc
@@ -52,6 +52,7 @@
 #include "ui/events/blink/blink_event_util.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 #include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/geometry/point_conversions.h"
 #include "v8/include/v8.h"
 
 using blink::WebContextMenuData;
@@ -66,7 +67,6 @@
 using blink::WebMouseEvent;
 using blink::WebMouseWheelEvent;
 using blink::WebPagePopup;
-using blink::WebPoint;
 using blink::WebPointerEvent;
 using blink::WebPointerProperties;
 using blink::WebString;
@@ -240,7 +240,7 @@
 
 void InitMouseEventGeneric(WebMouseEvent::Button b,
                            int current_buttons,
-                           const WebPoint& pos,
+                           const gfx::Point& pos,
                            int click_count,
                            WebPointerProperties::PointerType pointerType,
                            int pointerId,
@@ -249,8 +249,8 @@
                            int tiltY,
                            WebMouseEvent* e) {
   e->button = b;
-  e->SetPositionInWidget(pos.x, pos.y);
-  e->SetPositionInScreen(pos.x, pos.y);
+  e->SetPositionInWidget(gfx::PointF(pos));
+  e->SetPositionInScreen(gfx::PointF(pos));
   e->pointer_type = pointerType;
   e->id = pointerId;
   e->force = pressure;
@@ -261,7 +261,7 @@
 
 void InitMouseEvent(WebMouseEvent::Button b,
                     int current_buttons,
-                    const WebPoint& pos,
+                    const gfx::Point& pos,
                     int click_count,
                     WebMouseEvent* e) {
   InitMouseEventGeneric(b, current_buttons, pos, click_count,
@@ -404,8 +404,9 @@
 const char kDisabledIdentifier[] = "#";
 const char kCheckedIdentifier[] = "*";
 
-bool OutsideMultiClickRadius(const WebPoint& a, const WebPoint& b) {
-  return ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)) >
+bool OutsideMultiClickRadius(const gfx::Point& a, const gfx::Point& b) {
+  return ((a.x() - b.x()) * (a.x() - b.x()) +
+          (a.y() - b.y()) * (a.y() - b.y())) >
          kMultipleClickRadiusPixels * kMultipleClickRadiusPixels;
 }
 
@@ -1330,7 +1331,7 @@
 #endif
 
   last_click_time_ = base::TimeTicks();
-  last_click_pos_ = WebPoint(0, 0);
+  last_click_pos_ = gfx::Point();
   last_button_type_ = WebMouseEvent::Button::kNoButton;
   touch_points_.clear();
   last_context_menu_data_.reset();
@@ -2010,7 +2011,7 @@
   }
   current_drag_effects_allowed_ = blink::kWebDragOperationCopy;
 
-  const WebPoint& last_pos =
+  const gfx::Point& last_pos =
       current_pointer_state_[kRawMousePointerId].last_pos_;
 
   // Compute the scale from window (dsf-independent) to blink (dsf-dependent
@@ -2019,8 +2020,8 @@
   web_widget_test_proxy_->ConvertWindowToViewport(&rect);
   float scale_to_blink_coords = rect.width;
 
-  gfx::PointF last_pos_for_blink(last_pos.x * scale_to_blink_coords,
-                                 last_pos.y * scale_to_blink_coords);
+  gfx::PointF last_pos_for_blink(last_pos.x() * scale_to_blink_coords,
+                                 last_pos.y() * scale_to_blink_coords);
 
   // Provide a drag source.
   mainFrameWidget()->DragTargetDragEnter(current_drag_data_, last_pos_for_blink,
@@ -2156,7 +2157,7 @@
     args->ThrowError();
     return;
   }
-  WebPoint mouse_pos(static_cast<int>(x), static_cast<int>(y));
+  gfx::Point mouse_pos(x, y);
 
   int modifiers = 0;
   if (!args->PeekNext().IsEmpty()) {
@@ -2504,7 +2505,7 @@
 
     InitMouseEvent(current_pointer_state_[kRawMousePointerId].pressed_button_,
                    current_pointer_state_[kRawMousePointerId].current_buttons_,
-                   WebPoint(x, y), click_count_, &mouse_event);
+                   gfx::Point(x, y), click_count_, &mouse_event);
 
     FinishDragAndDrop(mouse_event, blink::kWebDragOperationNone);
   }
@@ -2729,8 +2730,8 @@
             current_pointer_state_[kRawMousePointerId].pressed_button_,
             current_pointer_state_[kRawMousePointerId].current_buttons_, e.pos,
             click_count_, &event);
-        current_pointer_state_[kRawMousePointerId].last_pos_ = blink::WebPoint(
-            event.PositionInWidget().x(), event.PositionInWidget().y());
+        current_pointer_state_[kRawMousePointerId].last_pos_ =
+            gfx::ToFlooredPoint(event.PositionInWidget());
         HandleInputEventOnViewOrPopup(event);
         DoDragAfterMouseMove(event);
         break;
diff --git a/content/shell/test_runner/event_sender.h b/content/shell/test_runner/event_sender.h
index 0cc7f712..566b9ca 100644
--- a/content/shell/test_runner/event_sender.h
+++ b/content/shell/test_runner/event_sender.h
@@ -24,7 +24,7 @@
 #include "third_party/blink/public/platform/web_drag_data.h"
 #include "third_party/blink/public/platform/web_drag_operation.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
-#include "third_party/blink/public/platform/web_point.h"
+#include "ui/gfx/geometry/point.h"
 
 namespace blink {
 class WebFrameWidget;
@@ -102,7 +102,7 @@
 
     SavedEventType type;
     blink::WebMouseEvent::Button button_type;  // For MouseUp.
-    blink::WebPoint pos;                       // For MouseMove.
+    gfx::Point pos;                            // For MouseMove.
     int milliseconds;                          // For LeapForward.
     int modifiers;
   };
@@ -290,14 +290,13 @@
     int current_buttons_;
 
     // Location of last mouseMoveTo event of this pointer.
-    blink::WebPoint last_pos_;
+    gfx::Point last_pos_;
 
     int modifiers_;
 
     PointerState()
         : pressed_button_(blink::WebMouseEvent::Button::kNoButton),
           current_buttons_(0),
-          last_pos_(blink::WebPoint(0, 0)),
           modifiers_(0) {}
   };
   typedef std::unordered_map<int, PointerState> PointerStateMap;
@@ -311,7 +310,7 @@
 
   // Time and place of the last mouse up event.
   base::TimeTicks last_click_time_;
-  blink::WebPoint last_click_pos_;
+  gfx::Point last_click_pos_;
 
   // The last button number passed to mouseDown and mouseUp.
   // Used to determine whether the click count continues to increment or not.
diff --git a/content/shell/test_runner/pixel_dump.cc b/content/shell/test_runner/pixel_dump.cc
index 6695eee4..4d8d8a741 100644
--- a/content/shell/test_runner/pixel_dump.cc
+++ b/content/shell/test_runner/pixel_dump.cc
@@ -30,7 +30,6 @@
 #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/clipboard/clipboard.mojom.h"
 #include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/web/web_frame.h"
 #include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_local_frame.h"
@@ -103,8 +102,7 @@
   uint64_t sequence_number_before = 0;
   clipboard->GetSequenceNumber(ui::ClipboardBuffer::kCopyPaste,
                                &sequence_number_before);
-
-  web_frame->CopyImageAtForTesting(blink::WebPoint(x, y));
+  web_frame->CopyImageAtForTesting(gfx::Point(x, y));
   uint64_t sequence_number_after = 0;
   while (sequence_number_before == sequence_number_after) {
     clipboard->GetSequenceNumber(ui::ClipboardBuffer::kCopyPaste,
diff --git a/content/shell/test_runner/test_runner.cc b/content/shell/test_runner/test_runner.cc
index 9b36bb4..84cc5ec3 100644
--- a/content/shell/test_runner/test_runner.cc
+++ b/content/shell/test_runner/test_runner.cc
@@ -40,7 +40,6 @@
 #include "gin/wrappable.h"
 #include "services/network/public/mojom/cors.mojom.h"
 #include "third_party/blink/public/platform/web_data.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/public/web/blink.h"
 #include "third_party/blink/public/web/web_array_buffer.h"
diff --git a/content/shell/test_runner/test_runner_for_specific_view.cc b/content/shell/test_runner/test_runner_for_specific_view.cc
index fff9365..2e98bbfe 100644
--- a/content/shell/test_runner/test_runner_for_specific_view.cc
+++ b/content/shell/test_runner/test_runner_for_specific_view.cc
@@ -39,7 +39,6 @@
 #include "third_party/blink/public/platform/web_data.h"
 #include "third_party/blink/public/platform/web_isolated_world_ids.h"
 #include "third_party/blink/public/platform/web_isolated_world_info.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/public/web/blink.h"
diff --git a/content/shell/test_runner/web_ax_object_proxy.cc b/content/shell/test_runner/web_ax_object_proxy.cc
index 677c292..e9d5b67 100644
--- a/content/shell/test_runner/web_ax_object_proxy.cc
+++ b/content/shell/test_runner/web_ax_object_proxy.cc
@@ -10,7 +10,6 @@
 #include "base/strings/stringprintf.h"
 #include "gin/handle.h"
 #include "third_party/blink/public/platform/web_float_rect.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/blink.h"
@@ -432,8 +431,8 @@
   gfx::RectF computedBounds(0, 0, bounds.width, bounds.height);
   while (!container.IsDetached()) {
     computedBounds.Offset(bounds.x, bounds.y);
-    computedBounds.Offset(-container.GetScrollOffset().x,
-                          -container.GetScrollOffset().y);
+    computedBounds.Offset(-container.GetScrollOffset().x(),
+                          -container.GetScrollOffset().y());
     if (!matrix.isIdentity()) {
       gfx::Transform transform(matrix);
       transform.TransformRect(&computedBounds);
@@ -1643,7 +1642,7 @@
 
 v8::Local<v8::Object> WebAXObjectProxy::ElementAtPoint(int x, int y) {
   accessibility_object_.UpdateLayoutAndCheckValidity();
-  blink::WebPoint point(x, y);
+  gfx::Point point(x, y);
   blink::WebAXObject obj = accessibility_object_.HitTest(point);
   if (obj.IsNull())
     return v8::Local<v8::Object>();
@@ -1824,17 +1823,17 @@
 
 void WebAXObjectProxy::ScrollToGlobalPoint(int x, int y) {
   accessibility_object_.UpdateLayoutAndCheckValidity();
-  accessibility_object_.ScrollToGlobalPoint(blink::WebPoint(x, y));
+  accessibility_object_.ScrollToGlobalPoint(gfx::Point(x, y));
 }
 
 int WebAXObjectProxy::ScrollX() {
   accessibility_object_.UpdateLayoutAndCheckValidity();
-  return accessibility_object_.GetScrollOffset().x;
+  return accessibility_object_.GetScrollOffset().x();
 }
 
 int WebAXObjectProxy::ScrollY() {
   accessibility_object_.UpdateLayoutAndCheckValidity();
-  return accessibility_object_.GetScrollOffset().y;
+  return accessibility_object_.GetScrollOffset().y();
 }
 
 std::string WebAXObjectProxy::ToString() {
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 428b8be4..a2480a20 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -186,11 +186,6 @@
 # Skia Renderer Vulkan (and GL) producing incorrect negative result
 crbug.com/1033113 [ android android-chromium skia-renderer ] Pixel_Video_Context_Loss_VP9 [ Skip ]
 
-# Skia Renderer Vulkan issue with tombstone symbolization
-crbug.com/1042693 [ android android-chromium skia-renderer ] Pixel_WebGLGreenTriangle_AA_NoAlpha [ Skip ]
-crbug.com/1042693 [ android android-chromium skia-renderer ] Pixel_WebGLTransparentGreenTriangle_NoAlpha_ImplicitClear [ Skip ]
-crbug.com/1042693 [ android android-chromium skia-renderer ] Pixel_WebGLGreenTriangle_NoAA_NoAlpha [ Skip ]
-
 # Skia Renderer EGL timeout, fails to load minidump
 crbug.com/1033121 [ android skia-renderer use-gl no-use-vulkan ] Pixel_Video_MP4_FourColors_Aspect_4x3 [ Skip ]
 crbug.com/1033121 [ android skia-renderer use-gl no-use-vulkan ] Pixel_Video_MP4_FourColors_Rot_180 [ Skip ]
diff --git a/docs/clang_tidy.md b/docs/clang_tidy.md
index b872670..6b1ca7d0 100644
--- a/docs/clang_tidy.md
+++ b/docs/clang_tidy.md
@@ -37,6 +37,77 @@
 comments. If feedback is overwhelmingly "users don't find this useful," the
 check may be removed.
 
+### Ignoring a check
+
+If a check is invalid on a particular piece of code, clang-tidy supports `//
+NOLINT` and `// NOLINTNEXTLINE` for ignoring all lint checks in the current and
+next lines, respectively. To suppress a specific lint, you can put it in
+parenthesis, e.g., `// NOLINTNEXTLINE(modernize-use-nullptr)`. For more, please
+see [the documentation](
+https://clang.llvm.org/extra/clang-tidy/#suppressing-undesired-diagnostics).
+
+**Please note** that adding comments that exist only to silence clang-tidy is
+actively discouraged. These comments clutter code, can easily get
+out-of-date, and don't provide much value to readers. Moreover, clang-tidy only
+complains on Gerrit when lines are touched, and making Chromium clang-tidy clean
+is an explicit non-goal; making code less readable in order to silence a
+rarely-surfaced complaint isn't a good trade-off.
+
+If clang-tidy emits a diagnostic that's incorrect due to a subtlety in the code,
+adding an explanantion of what the code is doing with a trailing `NOLINT` may be
+fine. Put differently, the comment should be able to stand on its own even if we
+removed the `NOLINT`. The fact that the comment also silences clang-tidy is a
+convenient side-effect.
+
+For example:
+
+Not OK; comment exists just to silence clang-tidy:
+
+```
+// NOLINTNEXTLINE
+for (int i = 0; i < arr.size(); i++) {
+  // ...
+}
+```
+
+Not OK; comment exists just to verbosely silence clang-tidy:
+
+```
+// Clang-tidy doesn't get that we can't range-for-ize this loop. NOLINTNEXTLINE
+for (int i = 0; i < arr.size(); i++) {
+  // ...
+}
+```
+
+Not OK; it's obvious that this loop modifies `arr`, so the comment doesn't
+actually clarify anything:
+
+```
+// It'd be invalid to make this into a range-for loop, since the body might add
+// elements to `arr`. NOLINTNEXTLINE
+for (int i = 0; i < arr.size(); i++) {
+  if (i % 4) {
+    arr.push_back(4);
+    arr.push_back(2);
+  }
+}
+```
+
+OK; comment calls out a non-obvious property of this loop's body. As an
+afterthought, it silences clang-tidy:
+
+```
+// It'd be invalid to make this into a range-for loop, since the call to `foo`
+// here might add elements to `arr`. NOLINTNEXTLINE
+for (int i = 0; i < arr.size(); i++) {
+  foo();
+  bar();
+}
+```
+
+In the end, as always, what is and isn't obvious at some point is highly
+context-dependent. Please use your best judgement.
+
 ## But I want to run it locally
 
 If you want to sync the officially-supported `clang-tidy` to your workstation,
diff --git a/docs/infra/new_builder.md b/docs/infra/new_builder.md
index d486e53..571bea86 100644
--- a/docs/infra/new_builder.md
+++ b/docs/infra/new_builder.md
@@ -165,6 +165,13 @@
 
 LUCI services used by chromium are configured in [//infra/config][6].
 
+The configuration is written in Starlark and used to generate Protobuf files
+which are also checked in to the repo.
+
+Generating all of the LUCI services configuration files for the production
+builders is done by executing [main.star][22] or running
+`lucicfg generate main.star`.
+
 #### Buildbucket
 
 Buildbucket is responsible for taking a build scheduled by a user or
@@ -175,81 +182,101 @@
   * Swarming dimensions
   * Recipe name and properties
 
+Chromium's buildbucket Starlark configuration is [here][23].
+Chromium's generated buildbucket configuration is [here][8].
 Buildbucket's configuration schema is [here][7].
-Chromium's buildbucket configuration is [here][8].
 
-A typical chromium builder won't need to configure much. Adding a
-`builders` entry to the appropriate bucket
-(`luci.chromium.ci` for CI / waterfall, `luci.chromium.try` for try)
-with the new builder's name, the mixin containing the appropriate
-master name, and perhaps one or two dimensions should be sufficient,
-e.g.:
+Each bucket has a corresponding `.star` file where the builders for the bucket
+are defined.
 
-``` sh
-buckets {
-  name: "luci.chromium.ci"
-  ...
+Most builders are defined using the builder function from [builders.star][24]
+(or some function that wraps it), which simplifies setting the most common
+dimensions and properties and provides a unified interface for setting
+module-level defaults.
 
-  swarming {
-    ...
+A typical chromium builder won't need to configure much; module-level defaults
+apply values that are widely used for the bucket (e.g. bucket and executable).
 
-    builders {
-      name: "your-new-builder"
+Each master has a function (sometimes multiple) defined that can be used to
+define a builder that runs with that mastername and sets master-specific
+defaults. Find the block of builders defined using the appropriate function and
+add a new definition, which may be as simple as:
 
-      # To determine what you should include here, look for an
-      # existing mixin containing
-      #
-      #   recipe {
-      #     properties: "mastername:$MASTER_NAME"
-      #   }
-      #
-      mixins: "$MASTER_NAME_MIXIN"
-
-      # If you're running a bunch of bots on GCE, you probably don't
-      # want those bots to be keyed by buildername. Rather, they should
-      # share the large pool with all the other bots using similar hardware.
-      # To enable this, use the builderless mixin:
-      mixins: builderless
-
-      # Add other mixins and dimensions as necessary. You will
-      # usually at least want an os dimension configured, so if
-      # none of your included mixins have one, consider adding one.
-    }
-  }
-}
+```starlark
+linux_builder(
+    name = '$BUILDER_NAME',
+)
 ```
 
+You can generate the configuration files and then view the added entry in
+[cr-buildbucket.cfg][8] to make sure all properties and dimensions are set as
+expected and adjust your definition as necessary.
+
 #### Milo
 
 Milo is responsible for displaying builders and build histories on a
 set of consoles. Its configuration includes the definitions of those
 consoles.
 
+Chromium's milo Starlark configuration is [here][25].
+Chromium's generated milo configuration is [here][10].
 Milo's configuration schema is [here][9].
-Chromium's milo configuration is [here][10].
+
+Each console has a corresponding `.star` file that defines the console.
 
 A typical chromium builder should be added to one or two consoles
 at most: one corresponding to its master, and possibly the main
-console, e.g.
+console.
 
-``` sh
-consoles {
-  ...
-  name: "$MASTER_NAME"
-  ...
-  builders {
-    name: "buildbucket/$BUCKET_NAME/$BUILDER_NAME"
+##### CI builders
 
-    # A builder's category is a pipe-delimited list of strings
-    # that determines how a builder is grouped on a console page.
-    category: "$LARGE_GROUP|$MEDIUM_GROUP|$SMALL_GROUP"
+The sequence of CI builds for a builder corresponds to a linear history of
+revisions in the repository, and the console takes advantage of that, allowing
+you to compare what revisions are in what builds for different builders in the
+console.
 
-    # A builder's short name is a string up to three characters
-    # long that lets someone uniquely identify it among builders
-    # in the same category.
-    short_name: "$ID"
-  }
-}
+```starlark
+luci.console_view(
+    name = '$MASTER_NAME',
+    ...
+    entries = [
+        ...
+        luci.console_view(
+            builder = '$BUCKET_NAME/$BUILDER_NAME',
+
+            # A builder's category is a pipe-delimited list of strings
+            # that determines how a builder is grouped on a console page.
+            # N>=0
+            category = '$GROUP1|$GROUP2|...|$GROUPN',
+
+           # A builder's short name is a string up to three characters
+           # long that lets someone uniquely identify it among builders
+           # in the same category.
+           short_name = '$SHORT_NAME',
+       ),
+   ...
+   ],
+),
+```
+
+Both category and short_name can be omitted, but is strongly recommended that
+all entries include short name.
+
+##### Try builders
+
+The sequence of try builders for a builder does not correspond to a linear
+history of revisions. Consequently, the interface for the consoles is different,
+as is the method of defining the console.
+
+```starlark
+luci.list_view(
+    name = '$MASTER_NAME',
+    entries = [
+        ...
+        '$BUCKET_NAME/$BUILDER_NAME',
+        ...
+    ],
+),
 ```
 
 #### Scheduler (CI / waterfall builders only)
@@ -258,6 +285,10 @@
 
 Scheduler's configuration schema is [here][11].
 Chromium's scheduler configuration is [here][12].
+The scheduler configuration is not written in Starlark at this time, but is
+still part of the generation process: the handwritten proto file gets copied to
+the `generated` directory where the other configuration files are generated to
+and a presubmit check ensures that the files remain in sync.
 
 A typical chromium builder will need a job configuration. A chromium
 builder that's triggering on new commits or on a regular schedule
@@ -336,3 +367,7 @@
 [19]: https://g.co/bugatrooper
 [20]: https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/swarming/proto/bots.proto
 [21]: https://chromium.googlesource.com/chromium/tools/build/+/master/scripts/slave/recipe_modules/chromium_tests/trybots.py
+[22]: /infra/config/main.star
+[23]: /infra/config/buckets
+[24]: /infra/config/lib/builders.star
+[25]: /infra/config/consoles
diff --git a/extensions/browser/api/activity_log/BUILD.gn b/extensions/browser/api/activity_log/BUILD.gn
index 7f0db86a..c376a7b8 100644
--- a/extensions/browser/api/activity_log/BUILD.gn
+++ b/extensions/browser/api/activity_log/BUILD.gn
@@ -13,11 +13,7 @@
     "web_request_constants.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/alarms/BUILD.gn b/extensions/browser/api/alarms/BUILD.gn
index 002f8975..e6c7666c9 100644
--- a/extensions/browser/api/alarms/BUILD.gn
+++ b/extensions/browser/api/alarms/BUILD.gn
@@ -17,11 +17,7 @@
     "alarms_api_constants.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/app_current_window_internal/BUILD.gn b/extensions/browser/api/app_current_window_internal/BUILD.gn
index 7caa7ca..c6896e2 100644
--- a/extensions/browser/api/app_current_window_internal/BUILD.gn
+++ b/extensions/browser/api/app_current_window_internal/BUILD.gn
@@ -18,7 +18,5 @@
     "//extensions/common/api",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/app_runtime/BUILD.gn b/extensions/browser/api/app_runtime/BUILD.gn
index 041b5ff..b2e292ac 100644
--- a/extensions/browser/api/app_runtime/BUILD.gn
+++ b/extensions/browser/api/app_runtime/BUILD.gn
@@ -13,11 +13,7 @@
     "app_runtime_api.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/audio/BUILD.gn b/extensions/browser/api/audio/BUILD.gn
index da4d4f8c..17f4723 100644
--- a/extensions/browser/api/audio/BUILD.gn
+++ b/extensions/browser/api/audio/BUILD.gn
@@ -18,9 +18,7 @@
     "pref_names.h",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 
   deps = [
     "//components/prefs",
diff --git a/extensions/browser/api/bluetooth/BUILD.gn b/extensions/browser/api/bluetooth/BUILD.gn
index c0d270bb..1e5683d 100644
--- a/extensions/browser/api/bluetooth/BUILD.gn
+++ b/extensions/browser/api/bluetooth/BUILD.gn
@@ -29,7 +29,5 @@
     "//extensions/common/api",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/bluetooth_low_energy/BUILD.gn b/extensions/browser/api/bluetooth_low_energy/BUILD.gn
index 3f392b0f..c7767b07 100644
--- a/extensions/browser/api/bluetooth_low_energy/BUILD.gn
+++ b/extensions/browser/api/bluetooth_low_energy/BUILD.gn
@@ -34,7 +34,5 @@
     "//extensions/common/api",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/bluetooth_socket/BUILD.gn b/extensions/browser/api/bluetooth_socket/BUILD.gn
index 3b02b08..953002f 100644
--- a/extensions/browser/api/bluetooth_socket/BUILD.gn
+++ b/extensions/browser/api/bluetooth_socket/BUILD.gn
@@ -22,11 +22,7 @@
     "//build/config/compiler:no_size_t_to_int_warning",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/cec_private/BUILD.gn b/extensions/browser/api/cec_private/BUILD.gn
index eea3c34..7b549fb 100644
--- a/extensions/browser/api/cec_private/BUILD.gn
+++ b/extensions/browser/api/cec_private/BUILD.gn
@@ -13,11 +13,7 @@
     "cec_private_api.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/clipboard/BUILD.gn b/extensions/browser/api/clipboard/BUILD.gn
index fe8b639e..f4d1009 100644
--- a/extensions/browser/api/clipboard/BUILD.gn
+++ b/extensions/browser/api/clipboard/BUILD.gn
@@ -13,11 +13,7 @@
     "clipboard_api.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/declarative/BUILD.gn b/extensions/browser/api/declarative/BUILD.gn
index 7a9e5ac..26f1b29 100644
--- a/extensions/browser/api/declarative/BUILD.gn
+++ b/extensions/browser/api/declarative/BUILD.gn
@@ -26,7 +26,5 @@
     "//extensions/common/api",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/declarative_content/BUILD.gn b/extensions/browser/api/declarative_content/BUILD.gn
index ba1247af0..2c66667 100644
--- a/extensions/browser/api/declarative_content/BUILD.gn
+++ b/extensions/browser/api/declarative_content/BUILD.gn
@@ -8,15 +8,9 @@
        "Cannot depend on extensions because enable_extensions=false.")
 
 source_set("declarative_content") {
-  sources = [
-    "content_rules_registry.h",
-  ]
+  sources = [ "content_rules_registry.h" ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/declarative_net_request/filter_list_converter/BUILD.gn b/extensions/browser/api/declarative_net_request/filter_list_converter/BUILD.gn
index 77037ea..808b976 100644
--- a/extensions/browser/api/declarative_net_request/filter_list_converter/BUILD.gn
+++ b/extensions/browser/api/declarative_net_request/filter_list_converter/BUILD.gn
@@ -24,9 +24,7 @@
 
 executable("filter_list_converter") {
   testonly = true
-  sources = [
-    "main.cc",
-  ]
+  sources = [ "main.cc" ]
   deps = [
     ":support",
     "//base",
@@ -35,9 +33,7 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [
-    "converter_unittest.cc",
-  ]
+  sources = [ "converter_unittest.cc" ]
   deps = [
     ":support",
     "//base",
diff --git a/extensions/browser/api/declarative_net_request/flat/BUILD.gn b/extensions/browser/api/declarative_net_request/flat/BUILD.gn
index fd8e641..16e780d 100644
--- a/extensions/browser/api/declarative_net_request/flat/BUILD.gn
+++ b/extensions/browser/api/declarative_net_request/flat/BUILD.gn
@@ -5,10 +5,6 @@
 import("//third_party/flatbuffers/flatbuffer.gni")
 
 flatbuffer("extension_ruleset") {
-  sources = [
-    "extension_ruleset.fbs",
-  ]
-  public_deps = [
-    "//components/url_pattern_index/flat:url_pattern_index",
-  ]
+  sources = [ "extension_ruleset.fbs" ]
+  public_deps = [ "//components/url_pattern_index/flat:url_pattern_index" ]
 }
diff --git a/extensions/browser/api/declarative_webrequest/BUILD.gn b/extensions/browser/api/declarative_webrequest/BUILD.gn
index bb7dec5b..803b4f2 100644
--- a/extensions/browser/api/declarative_webrequest/BUILD.gn
+++ b/extensions/browser/api/declarative_webrequest/BUILD.gn
@@ -29,7 +29,5 @@
     "//third_party/re2",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/diagnostics/BUILD.gn b/extensions/browser/api/diagnostics/BUILD.gn
index cfa34d7..9fab6bf 100644
--- a/extensions/browser/api/diagnostics/BUILD.gn
+++ b/extensions/browser/api/diagnostics/BUILD.gn
@@ -15,11 +15,7 @@
     "diagnostics_api.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/display_source/BUILD.gn b/extensions/browser/api/display_source/BUILD.gn
index e938d20..8ab7c4b 100644
--- a/extensions/browser/api/display_source/BUILD.gn
+++ b/extensions/browser/api/display_source/BUILD.gn
@@ -33,11 +33,7 @@
     }
   }
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/dns/BUILD.gn b/extensions/browser/api/dns/BUILD.gn
index 7e492383..71de8de 100644
--- a/extensions/browser/api/dns/BUILD.gn
+++ b/extensions/browser/api/dns/BUILD.gn
@@ -13,11 +13,7 @@
     "dns_api.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/document_scan/BUILD.gn b/extensions/browser/api/document_scan/BUILD.gn
index 4be146a2..7ac8b406 100644
--- a/extensions/browser/api/document_scan/BUILD.gn
+++ b/extensions/browser/api/document_scan/BUILD.gn
@@ -21,11 +21,7 @@
     sources += [ "document_scan_interface_nonchromeos.cc" ]
   }
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/idle/BUILD.gn b/extensions/browser/api/idle/BUILD.gn
index a635a4f0..986d88e 100644
--- a/extensions/browser/api/idle/BUILD.gn
+++ b/extensions/browser/api/idle/BUILD.gn
@@ -24,7 +24,5 @@
     "//ui/base/idle",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/management/BUILD.gn b/extensions/browser/api/management/BUILD.gn
index 579c50d..7670df7 100644
--- a/extensions/browser/api/management/BUILD.gn
+++ b/extensions/browser/api/management/BUILD.gn
@@ -16,9 +16,7 @@
     "management_api_delegate.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
   public_deps = [
     "//extensions/browser:browser_sources",
diff --git a/extensions/browser/api/metrics_private/BUILD.gn b/extensions/browser/api/metrics_private/BUILD.gn
index 08a3dfb..139df2ef 100644
--- a/extensions/browser/api/metrics_private/BUILD.gn
+++ b/extensions/browser/api/metrics_private/BUILD.gn
@@ -19,11 +19,7 @@
     "//build/config/compiler:no_size_t_to_int_warning",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/power/BUILD.gn b/extensions/browser/api/power/BUILD.gn
index 1b75846..6c1e1895 100644
--- a/extensions/browser/api/power/BUILD.gn
+++ b/extensions/browser/api/power/BUILD.gn
@@ -20,7 +20,5 @@
     "//services/device/public/mojom",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/runtime/BUILD.gn b/extensions/browser/api/runtime/BUILD.gn
index 7220ebba..f351e339 100644
--- a/extensions/browser/api/runtime/BUILD.gn
+++ b/extensions/browser/api/runtime/BUILD.gn
@@ -20,7 +20,5 @@
     "//extensions/common/api",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/serial/BUILD.gn b/extensions/browser/api/serial/BUILD.gn
index 40be6ddc..bb37bb9 100644
--- a/extensions/browser/api/serial/BUILD.gn
+++ b/extensions/browser/api/serial/BUILD.gn
@@ -25,7 +25,5 @@
     "//services/service_manager/public/cpp",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/socket/BUILD.gn b/extensions/browser/api/socket/BUILD.gn
index e20046a05..32ba77a 100644
--- a/extensions/browser/api/socket/BUILD.gn
+++ b/extensions/browser/api/socket/BUILD.gn
@@ -41,7 +41,5 @@
     "//extensions/common/api",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/system_cpu/BUILD.gn b/extensions/browser/api/system_cpu/BUILD.gn
index 16c1602..487742c 100644
--- a/extensions/browser/api/system_cpu/BUILD.gn
+++ b/extensions/browser/api/system_cpu/BUILD.gn
@@ -18,15 +18,11 @@
     "system_cpu_api.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
   if (is_chromeos) {
     deps += [ "//chromeos/system" ]
   }
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/system_power_source/BUILD.gn b/extensions/browser/api/system_power_source/BUILD.gn
index 4fca008..f014563 100644
--- a/extensions/browser/api/system_power_source/BUILD.gn
+++ b/extensions/browser/api/system_power_source/BUILD.gn
@@ -13,11 +13,7 @@
     "system_power_source_api.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/system_storage/BUILD.gn b/extensions/browser/api/system_storage/BUILD.gn
index 6b8889ba..03957f7 100644
--- a/extensions/browser/api/system_storage/BUILD.gn
+++ b/extensions/browser/api/system_storage/BUILD.gn
@@ -15,11 +15,7 @@
     "system_storage_api.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/test/BUILD.gn b/extensions/browser/api/test/BUILD.gn
index c1242fb7..c1eeb8f1 100644
--- a/extensions/browser/api/test/BUILD.gn
+++ b/extensions/browser/api/test/BUILD.gn
@@ -18,7 +18,5 @@
     "//extensions/common/api",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/usb/BUILD.gn b/extensions/browser/api/usb/BUILD.gn
index 513b30c..c08ea3d 100644
--- a/extensions/browser/api/usb/BUILD.gn
+++ b/extensions/browser/api/usb/BUILD.gn
@@ -30,7 +30,5 @@
     "//services/device/public/mojom:usb",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/virtual_keyboard/BUILD.gn b/extensions/browser/api/virtual_keyboard/BUILD.gn
index 51a58df1..df8f416 100644
--- a/extensions/browser/api/virtual_keyboard/BUILD.gn
+++ b/extensions/browser/api/virtual_keyboard/BUILD.gn
@@ -18,7 +18,5 @@
     "//extensions/common/api",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/virtual_keyboard_private/BUILD.gn b/extensions/browser/api/virtual_keyboard_private/BUILD.gn
index 2d50bf8a..cc073a1 100644
--- a/extensions/browser/api/virtual_keyboard_private/BUILD.gn
+++ b/extensions/browser/api/virtual_keyboard_private/BUILD.gn
@@ -14,11 +14,7 @@
     "virtual_keyboard_private_api.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/vpn_provider/BUILD.gn b/extensions/browser/api/vpn_provider/BUILD.gn
index ce57f5d..9456bdc6 100644
--- a/extensions/browser/api/vpn_provider/BUILD.gn
+++ b/extensions/browser/api/vpn_provider/BUILD.gn
@@ -18,11 +18,7 @@
     "vpn_service_factory.h",
   ]
 
-  deps = [
-    "//extensions/common/api",
-  ]
+  deps = [ "//extensions/common/api" ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/webcam_private/BUILD.gn b/extensions/browser/api/webcam_private/BUILD.gn
index 2a1c04b..a36e0d9a 100644
--- a/extensions/browser/api/webcam_private/BUILD.gn
+++ b/extensions/browser/api/webcam_private/BUILD.gn
@@ -27,7 +27,5 @@
     "//services/service_manager/public/cpp",
   ]
 
-  public_deps = [
-    "//extensions/browser:browser_sources",
-  ]
+  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/kiosk/BUILD.gn b/extensions/browser/kiosk/BUILD.gn
index 37e73dc0..3c15f71 100644
--- a/extensions/browser/kiosk/BUILD.gn
+++ b/extensions/browser/kiosk/BUILD.gn
@@ -13,7 +13,5 @@
     "kiosk_delegate.h",
   ]
 
-  deps = [
-    "//extensions/common",
-  ]
+  deps = [ "//extensions/common" ]
 }
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index ee0bfa0..82804deb7b 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -488,9 +488,7 @@
   }
 
   fuzzer_test("extension_fuzzer") {
-    sources = [
-      "extension_fuzzer.cc",
-    ]
+    sources = [ "extension_fuzzer.cc" ]
     deps = [
       ":common",
       "//base",
diff --git a/extensions/common/api/BUILD.gn b/extensions/common/api/BUILD.gn
index cd20fc1..721e545 100644
--- a/extensions/common/api/BUILD.gn
+++ b/extensions/common/api/BUILD.gn
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
-import("//extensions/common/api/schema.gni")
 import("//extensions/buildflags/buildflags.gni")
+import("//extensions/common/api/schema.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//tools/json_schema_compiler/json_features.gni")
 import("//tools/json_schema_compiler/json_schema_api.gni")
@@ -33,9 +33,7 @@
 }
 
 mojom("mojom") {
-  sources = [
-    "mime_handler.mojom",
-  ]
+  sources = [ "mime_handler.mojom" ]
 }
 
 ################################################################################
@@ -66,35 +64,27 @@
 json_features("api_features") {
   feature_type = "APIFeature"
   method_name = "AddCoreAPIFeatures"
-  sources = [
-    "_api_features.json",
-  ]
+  sources = [ "_api_features.json" ]
   visibility = [ ":extensions_features" ]
 }
 
 json_features("permission_features") {
   feature_type = "PermissionFeature"
   method_name = "AddCorePermissionFeatures"
-  sources = [
-    "_permission_features.json",
-  ]
+  sources = [ "_permission_features.json" ]
   visibility = [ ":extensions_features" ]
 }
 
 json_features("manifest_features") {
   feature_type = "ManifestFeature"
   method_name = "AddCoreManifestFeatures"
-  sources = [
-    "_manifest_features.json",
-  ]
+  sources = [ "_manifest_features.json" ]
   visibility = [ ":extensions_features" ]
 }
 
 json_features("behavior_features") {
   feature_type = "BehaviorFeature"
   method_name = "AddCoreBehaviorFeatures"
-  sources = [
-    "_behavior_features.json",
-  ]
+  sources = [ "_behavior_features.json" ]
   visibility = [ ":extensions_features" ]
 }
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index b247dd9e..72b359ec 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -256,9 +256,7 @@
 executable("app_shell") {
   # testonly because :app_shell_lib is testonly. See :app_shell_lib comment.
   testonly = true
-  sources = [
-    "app/shell_main.cc",
-  ]
+  sources = [ "app/shell_main.cc" ]
 
   deps = [
     ":app_shell_lib",
@@ -430,8 +428,6 @@
 
     binary = "$root_out_dir/app_shell"
     symbol_file = "$root_out_dir/app_shell.breakpad.$current_cpu"
-    deps = [
-      ":app_shell",
-    ]
+    deps = [ ":app_shell" ]
   }
 }
diff --git a/extensions/shell/common/api/BUILD.gn b/extensions/shell/common/api/BUILD.gn
index 70d335ef..d6952ca 100644
--- a/extensions/shell/common/api/BUILD.gn
+++ b/extensions/shell/common/api/BUILD.gn
@@ -19,9 +19,7 @@
   impl_dir = "//extensions/shell/browser/api"
   bundle_name = "Shell"
 
-  deps = [
-    "//extensions/common",
-  ]
+  deps = [ "//extensions/common" ]
 
   visibility = [ ":api" ]
 }
@@ -41,9 +39,7 @@
 json_features("shell_api_features") {
   feature_type = "APIFeature"
   method_name = "AddShellAPIFeatures"
-  sources = [
-    "_api_features.json",
-  ]
+  sources = [ "_api_features.json" ]
 }
 
 # Public Targets
diff --git a/fuchsia/engine/DEPS b/fuchsia/engine/DEPS
index c3f48a1..7314b929 100644
--- a/fuchsia/engine/DEPS
+++ b/fuchsia/engine/DEPS
@@ -3,6 +3,7 @@
   "+components/viz/common",
   "+content/public/app",
   "+gpu/command_buffer/service",
+  "+gpu/config/gpu_finch_features.h",
   "+media/base",
   "+mojo/public",
   "+services/service_manager",
diff --git a/fuchsia/engine/context_provider_impl.cc b/fuchsia/engine/context_provider_impl.cc
index ea6825f5..402d9d6 100644
--- a/fuchsia/engine/context_provider_impl.cc
+++ b/fuchsia/engine/context_provider_impl.cc
@@ -46,6 +46,7 @@
 #include "fuchsia/engine/common/web_engine_content_client.h"
 #include "fuchsia/engine/switches.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
+#include "gpu/config/gpu_finch_features.h"
 #include "media/base/key_system_names.h"
 #include "media/base/media_switches.h"
 #include "net/http/http_util.h"
@@ -329,10 +330,10 @@
     DLOG(ERROR) << "Enabling Vulkan GPU acceleration.";
     // Vulkan requires use of SkiaRenderer, configured to a use Vulkan context.
     launch_command.AppendSwitch(switches::kUseVulkan);
+    const std::vector<base::StringPiece> enabled_features = {
+        features::kUseSkiaRenderer.name, features::kVulkan.name};
     launch_command.AppendSwitchASCII(switches::kEnableFeatures,
-                                     features::kUseSkiaRenderer.name);
-    launch_command.AppendSwitchASCII(switches::kGrContextType,
-                                     switches::kGrContextTypeVulkan);
+                                     base::JoinString(enabled_features, ","));
 
     // SkiaRenderer requires out-of-process rasterization be enabled.
     launch_command.AppendSwitch(switches::kEnableOopRasterization);
diff --git a/fuchsia/engine/test/web_engine_shell.cc b/fuchsia/engine/test/web_engine_shell.cc
index 5f5b2be..c43aa3a 100644
--- a/fuchsia/engine/test/web_engine_shell.cc
+++ b/fuchsia/engine/test/web_engine_shell.cc
@@ -25,13 +25,17 @@
 
 constexpr char kRemoteDebuggingPortSwitch[] = "remote-debugging-port";
 constexpr char kEnableLoggingSwitch[] = "enable-logging";
+constexpr char kHeadlessSwitch[] = "headless";
 
 void PrintUsage() {
   std::cerr << "Usage: "
             << base::CommandLine::ForCurrentProcess()->GetProgram().BaseName()
-            << " [--" << kRemoteDebuggingPortSwitch << "] URL." << std::endl
+            << " [--" << kRemoteDebuggingPortSwitch << "] [--"
+            << kHeadlessSwitch << "] URL." << std::endl
             << "Setting " << kRemoteDebuggingPortSwitch << " to 0 will "
-            << "automatically choose an available port.";
+            << "automatically choose an available port." << std::endl
+            << "Setting " << kHeadlessSwitch << " will prevent creation of "
+            << "a view." << std::endl;
 }
 
 int main(int argc, char** argv) {
@@ -80,6 +84,8 @@
                                   ->svc()
                                   ->Connect<fuchsia::web::ContextProvider>();
 
+  bool is_headless = command_line->HasSwitch(kHeadlessSwitch);
+
   // Set up the content directory fuchsia-pkg://shell-data/, which will host
   // the files stored under //fuchsia/engine/test/shell_data.
   fuchsia::web::CreateContextParams create_context_params;
@@ -102,11 +108,16 @@
       base::FilePath(base::fuchsia::kServiceDirectoryPath)));
 
   // Enable other WebEngine features.
-  create_context_params.set_features(
+  fuchsia::web::ContextFeatureFlags features =
       fuchsia::web::ContextFeatureFlags::AUDIO |
-      fuchsia::web::ContextFeatureFlags::VULKAN |
       fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER |
-      fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM);
+      fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM;
+  if (is_headless)
+    features |= fuchsia::web::ContextFeatureFlags::HEADLESS;
+  else
+    features |= fuchsia::web::ContextFeatureFlags::VULKAN;
+
+  create_context_params.set_features(features);
   if (remote_debugging_port)
     create_context_params.set_remote_debugging_port(*remote_debugging_port);
 
@@ -165,15 +176,19 @@
         }
       });
 
-  // Present a fullscreen view of |frame|.
-  fuchsia::ui::views::ViewToken view_token;
-  fuchsia::ui::views::ViewHolderToken view_holder_token;
-  std::tie(view_token, view_holder_token) = scenic::NewViewTokenPair();
-  frame->CreateView(std::move(view_token));
-  auto presenter = base::fuchsia::ComponentContextForCurrentProcess()
-                       ->svc()
-                       ->Connect<::fuchsia::ui::policy::Presenter>();
-  presenter->PresentView(std::move(view_holder_token), nullptr);
+  if (is_headless)
+    frame->EnableHeadlessRendering();
+  else {
+    // Present a fullscreen view of |frame|.
+    fuchsia::ui::views::ViewToken view_token;
+    fuchsia::ui::views::ViewHolderToken view_holder_token;
+    std::tie(view_token, view_holder_token) = scenic::NewViewTokenPair();
+    frame->CreateView(std::move(view_token));
+    auto presenter = base::fuchsia::ComponentContextForCurrentProcess()
+                         ->svc()
+                         ->Connect<::fuchsia::ui::policy::Presenter>();
+    presenter->PresentView(std::move(view_holder_token), nullptr);
+  }
 
   LOG(INFO) << "Launched browser at URL " << url.spec();
 
diff --git a/gpu/command_buffer/service/gpu_switches.cc b/gpu/command_buffer/service/gpu_switches.cc
index 0a878ad..1b6c73b 100644
--- a/gpu/command_buffer/service/gpu_switches.cc
+++ b/gpu/command_buffer/service/gpu_switches.cc
@@ -66,14 +66,10 @@
 // round intermediate values in ANGLE.
 const char kEmulateShaderPrecision[] = "emulate-shader-precision";
 
-// Selects the type of the GrContext.
-const char kGrContextType[] = "gr-context-type";
-const char kGrContextTypeGL[] = "gl";
-const char kGrContextTypeVulkan[] = "vulkan";
-const char kGrContextTypeMetal[] = "metal";
-const char kGrContextTypeDawn[] = "dawn";
 // Enable Vulkan support and select Vulkan implementation, must also have
-// ENABLE_VULKAN defined.
+// ENABLE_VULKAN defined. This only initializes Vulkan, the flag
+// --enable-features=Vulkan must also be used to select Vulkan for compositing
+// and rasterization.
 const char kUseVulkan[] = "use-vulkan";
 const char kVulkanImplementationNameNative[] = "native";
 const char kVulkanImplementationNameSwiftshader[] = "swiftshader";
diff --git a/gpu/command_buffer/service/gpu_switches.h b/gpu/command_buffer/service/gpu_switches.h
index ed9eda57..5bc1ec35 100644
--- a/gpu/command_buffer/service/gpu_switches.h
+++ b/gpu/command_buffer/service/gpu_switches.h
@@ -29,12 +29,6 @@
 GPU_EXPORT extern const char kEnableThreadedTextureMailboxes[];
 GPU_EXPORT extern const char kGLShaderIntermOutput[];
 GPU_EXPORT extern const char kEmulateShaderPrecision[];
-GPU_EXPORT extern const char kGrContextType[];
-GPU_EXPORT extern const char kGrContextTypeGL[];
-GPU_EXPORT extern const char kGrContextTypeVulkan[];
-GPU_EXPORT extern const char kGrContextTypeMetal[];
-GPU_EXPORT extern const char kGrContextTypeDawn[];
-GPU_EXPORT extern const char kVulkanImplementationNameNative[];
 GPU_EXPORT extern const char kUseVulkan[];
 GPU_EXPORT extern const char kVulkanImplementationNameNative[];
 GPU_EXPORT extern const char kVulkanImplementationNameSwiftshader[];
diff --git a/gpu/command_buffer/service/service_utils.cc b/gpu/command_buffer/service/service_utils.cc
index 5eaa199..f6da770 100644
--- a/gpu/command_buffer/service/service_utils.cc
+++ b/gpu/command_buffer/service/service_utils.cc
@@ -171,40 +171,19 @@
   }
   gpu_preferences.disable_vulkan_surface =
       command_line->HasSwitch(switches::kDisableVulkanSurface);
-  if (command_line->HasSwitch(switches::kGrContextType)) {
-    auto value = command_line->GetSwitchValueASCII(switches::kGrContextType);
-    if (value == switches::kGrContextTypeGL) {
-      gpu_preferences.gr_context_type = GrContextType::kGL;
-    } else if (value == switches::kGrContextTypeVulkan) {
-      gpu_preferences.gr_context_type = GrContextType::kVulkan;
-    } else if (value == switches::kGrContextTypeMetal) {
 #if defined(OS_MACOSX)
-      DCHECK(base::FeatureList::IsEnabled(features::kMetal))
-          << "GrContextType is Metal, but Metal is not enabled.";
-      gpu_preferences.gr_context_type = GrContextType::kMetal;
+  gpu_preferences.gr_context_type =
+      base::FeatureList::IsEnabled(features::kMetal) ? GrContextType::kMetal
+                                                     : GrContextType::kGL;
+#else
+  gpu_preferences.gr_context_type =
+      base::FeatureList::IsEnabled(features::kVulkan) ? GrContextType::kVulkan
+                                                      : GrContextType::kGL;
 #endif
 #if BUILDFLAG(SKIA_USE_DAWN)
-    } else if (value == switches::kGrContextTypeDawn) {
-      gpu_preferences.gr_context_type = GrContextType::kDawn;
+  if (base::FeatureList::IsEnabled(features::kSkiaDawn))
+    gpu_preferences.gr_context_type = GrContextType::kDawn;
 #endif
-    } else {
-      NOTREACHED() << "Invalid GrContextType.";
-      gpu_preferences.gr_context_type = GrContextType::kGL;
-    }
-  } else {
-#if defined(OS_MACOSX)
-    gpu_preferences.gr_context_type =
-        base::FeatureList::IsEnabled(features::kMetal) ?
-            GrContextType::kMetal :
-            GrContextType::kGL;
-#else
-    if (base::FeatureList::IsEnabled(features::kVulkan)) {
-      gpu_preferences.gr_context_type = GrContextType::kVulkan;
-    } else {
-      gpu_preferences.gr_context_type = GrContextType::kGL;
-    }
-#endif
-  }
   if (gpu_preferences.gr_context_type == GrContextType::kVulkan &&
       gpu_preferences.use_vulkan == gpu::VulkanImplementationName::kNone) {
     // If gpu_preferences.use_vulkan is not set from --use-vulkan, the native
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc
index 5531305..cef3b34d 100644
--- a/gpu/config/gpu_finch_features.cc
+++ b/gpu/config/gpu_finch_features.cc
@@ -105,10 +105,15 @@
 const base::Feature kVaapiWebPImageDecodeAcceleration{
     "VaapiWebPImageDecodeAcceleration", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enable Vulkan graphics backend if --use-vulkan flag is not used. Otherwise
+// Enable Vulkan graphics backend for compositing and rasterization. Defaults to
+// native implementation if --use-vulkan flag is not used. Otherwise
 // --use-vulkan will be followed.
 const base::Feature kVulkan{"Vulkan", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable SkiaRenderer Dawn graphics backend. On Windows this will use D3D12,
+// and on Linux this will use Vulkan.
+const base::Feature kSkiaDawn{"SkiaDawn", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Used to enable shared image mailbox and disable legacy texture mailbox on
 // webview.
 const base::Feature kEnableSharedImageForWebview{
diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h
index dc3a36a..d2da35b72 100644
--- a/gpu/config/gpu_finch_features.h
+++ b/gpu/config/gpu_finch_features.h
@@ -51,6 +51,8 @@
 
 GPU_EXPORT extern const base::Feature kVulkan;
 
+GPU_EXPORT extern const base::Feature kSkiaDawn;
+
 GPU_EXPORT extern const base::Feature kEnableSharedImageForWebview;
 
 #if defined(OS_ANDROID)
diff --git a/gpu/ipc/service/gpu_channel_manager.h b/gpu/ipc/service/gpu_channel_manager.h
index 217adb6..9a4ab313 100644
--- a/gpu/ipc/service/gpu_channel_manager.h
+++ b/gpu/ipc/service/gpu_channel_manager.h
@@ -290,14 +290,14 @@
 
   // With --enable-vulkan, |vulkan_context_provider_| will be set from
   // viz::GpuServiceImpl. The raster decoders will use it for rasterization if
-  // --gr-context-type is also set to Vulkan.
+  // features::Vulkan is used.
   viz::VulkanContextProvider* vulkan_context_provider_ = nullptr;
 
-  // If features::SkiaOnMetad, |metal_context_provider_| will be set from
+  // If features::Metal, |metal_context_provider_| will be set from
   // viz::GpuServiceImpl. The raster decoders will use it for rasterization.
   viz::MetalContextProvider* metal_context_provider_ = nullptr;
 
-  // With --gr-context-type=dawn, |dawn_context_provider_| will be set from
+  // With features::SkiaDawn, |dawn_context_provider_| will be set from
   // viz::GpuServiceImpl. The raster decoders will use it for rasterization.
   viz::DawnContextProvider* dawn_context_provider_ = nullptr;
 
diff --git a/ios/build/bots/scripts/xcodebuild_runner.py b/ios/build/bots/scripts/xcodebuild_runner.py
index 23d1d32..2439d65 100644
--- a/ios/build/bots/scripts/xcodebuild_runner.py
+++ b/ios/build/bots/scripts/xcodebuild_runner.py
@@ -395,7 +395,6 @@
           or (len(running_tests) - len(self.egtests_app.excluded_tests)
               <= MAXIMUM_TESTS_PER_SHARD_FOR_RERUN)):
         shards = 1
-    self.test_results['end_run'] = int(time.time())
     self.summary_log()
 
     return {
@@ -526,9 +525,7 @@
       self.host_app_path = os.path.abspath(host_app_path)
     self._init_sharding_data()
     self.logs = collections.OrderedDict()
-    self.test_results = collections.OrderedDict()
-    self.test_results['start_run'] = int(time.time())
-    self.test_results['end_run'] = None
+    self.test_results['path_delimiter'] = '/'
 
   def _init_sharding_data(self):
     """Initialize sharding data.
@@ -582,7 +579,6 @@
     attempts_results = []
     for result in pool.imap_unordered(LaunchCommand.launch, launch_commands):
       attempts_results.append(result['test_results']['attempts'])
-    self.test_results['end_run'] = int(time.time())
 
     # Gets passed tests
     self.logs['passed tests'] = []
@@ -618,6 +614,16 @@
     aborted_tests.sort()
     self.logs['aborted tests'] = aborted_tests
 
+    self.test_results['interrupted'] = bool(aborted_tests)
+    self.test_results['num_failures_by_type'] = {
+        'FAIL': len(self.logs['failed tests'] + self.logs['aborted tests']),
+        'PASS': len(self.logs['passed tests']),
+    }
+    self.test_results['tests'] = collections.OrderedDict()
+    for test in self.logs['passed tests']:
+      self.test_results['tests'][test] = {'expected': 'PASS', 'actual': 'PASS'}
+    for test in self.logs['failed tests'] + self.logs['aborted tests']:
+      self.test_results['tests'][test] = {'expected': 'PASS', 'actual': 'FAIL'}
     # Test is failed if there are failures for the last run.
     return not self.logs['failed tests']
 
@@ -686,12 +692,8 @@
     # Destination is required to run tests via xcodebuild on real devices
     # and it looks like id=%ID%
     self.sharding_data[0]['destination'] = 'id=%s' % self.udid
-
-    self.logs = collections.OrderedDict()
-    self.test_results = collections.OrderedDict()
-    self.test_results['start_run'] = int(time.time())
-    self.test_results['end_run'] = None
     self.start_time = time.strftime('%Y-%m-%d-%H%M%S', time.localtime())
+    self.test_results['path_delimiter'] = '/'
 
   def set_up(self):
     """Performs setup actions which must occur prior to every test launch."""
diff --git a/ios/chrome/app/main_controller_guts.h b/ios/chrome/app/main_controller_guts.h
index 352c7c1..6ab4a665 100644
--- a/ios/chrome/app/main_controller_guts.h
+++ b/ios/chrome/app/main_controller_guts.h
@@ -10,6 +10,7 @@
 #import "base/ios/block_types.h"
 #include "components/browsing_data/core/browsing_data_utils.h"
 #include "ios/chrome/app/startup/chrome_app_startup_parameters.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_remove_mask.h"
 #import "ios/chrome/browser/crash_report/crash_restore_helper.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
@@ -23,10 +24,6 @@
 @protocol TabSwitcher;
 class AppUrlLoadingService;
 
-namespace ios {
-class ChromeBrowserState;
-}  // namespace ios
-
 // Used to update the current BVC mode if a new tab is added while the tab
 // switcher view is being dismissed.  This is different than ApplicationMode in
 // that it can be set to |NONE| when not in use.
diff --git a/ios/chrome/app/startup/content_suggestions_scheduler_notifications.h b/ios/chrome/app/startup/content_suggestions_scheduler_notifications.h
index a6e5eb2..74209cc 100644
--- a/ios/chrome/app/startup/content_suggestions_scheduler_notifications.h
+++ b/ios/chrome/app/startup/content_suggestions_scheduler_notifications.h
@@ -7,9 +7,7 @@
 
 #import <Foundation/Foundation.h>
 
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 // Notify the scheduler of the Content Suggestions services of the app lifecycle
 // events.
diff --git a/ios/chrome/app/startup_tasks.h b/ios/chrome/app/startup_tasks.h
index f39c875..07cea43 100644
--- a/ios/chrome/app/startup_tasks.h
+++ b/ios/chrome/app/startup_tasks.h
@@ -7,9 +7,7 @@
 
 #import <Foundation/Foundation.h>
 
-namespace ios {
-class ChromeBrowserState;
-}  // namespace ios.
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 // Class handling all startup tasks.
 @interface StartupTasks : NSObject
diff --git a/ios/chrome/browser/autofill/autocomplete_history_manager_factory.h b/ios/chrome/browser/autofill/autocomplete_history_manager_factory.h
index fbb6ef49..bc48f68b 100644
--- a/ios/chrome/browser/autofill/autocomplete_history_manager_factory.h
+++ b/ios/chrome/browser/autofill/autocomplete_history_manager_factory.h
@@ -10,10 +10,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace autofill {
 
diff --git a/ios/chrome/browser/autofill/autofill_log_router_factory.h b/ios/chrome/browser/autofill/autofill_log_router_factory.h
index 917cdfd..b398ad2 100644
--- a/ios/chrome/browser/autofill/autofill_log_router_factory.h
+++ b/ios/chrome/browser/autofill/autofill_log_router_factory.h
@@ -8,10 +8,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace autofill {
 
diff --git a/ios/chrome/browser/autofill/autofill_tab_helper.h b/ios/chrome/browser/autofill/autofill_tab_helper.h
index f9ae9fd8..185aa478 100644
--- a/ios/chrome/browser/autofill/autofill_tab_helper.h
+++ b/ios/chrome/browser/autofill/autofill_tab_helper.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #include "ios/web/public/web_state_observer.h"
 #import "ios/web/public/web_state_user_data.h"
 
@@ -23,10 +24,6 @@
 class PasswordManager;
 }
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // Class binding an instance of AutofillAgent to a WebState.
 class AutofillTabHelper : public web::WebStateObserver,
                           public web::WebStateUserData<AutofillTabHelper> {
diff --git a/ios/chrome/browser/autofill/personal_data_manager_factory.h b/ios/chrome/browser/autofill/personal_data_manager_factory.h
index fc08227..dc22043 100644
--- a/ios/chrome/browser/autofill/personal_data_manager_factory.h
+++ b/ios/chrome/browser/autofill/personal_data_manager_factory.h
@@ -10,10 +10,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace autofill {
 
diff --git a/ios/chrome/browser/autofill/strike_database_factory.h b/ios/chrome/browser/autofill/strike_database_factory.h
index 8562185..4732a60 100644
--- a/ios/chrome/browser/autofill/strike_database_factory.h
+++ b/ios/chrome/browser/autofill/strike_database_factory.h
@@ -10,10 +10,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace autofill {
 
diff --git a/ios/chrome/browser/favicon/favicon_service_factory.h b/ios/chrome/browser/favicon/favicon_service_factory.h
index 4d7f4ec..b9a2f39c 100644
--- a/ios/chrome/browser/favicon/favicon_service_factory.h
+++ b/ios/chrome/browser/favicon/favicon_service_factory.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 enum class ServiceAccessType;
 
@@ -18,9 +19,6 @@
 }
 
 namespace ios {
-
-class ChromeBrowserState;
-
 // Singleton that owns all FaviconServices and associates them with
 // ios::ChromeBrowserState.
 class FaviconServiceFactory : public BrowserStateKeyedServiceFactory {
diff --git a/ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h b/ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h
index 801815a..09a38f67 100644
--- a/ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h
+++ b/ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h
@@ -10,13 +10,10 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 class FaviconLoader;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // Singleton that owns all FaviconLoaders and associates them with
 // ios::ChromeBrowserState.
 class IOSChromeFaviconLoaderFactory : public BrowserStateKeyedServiceFactory {
diff --git a/ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h b/ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h
index d0eee8b2..42c4641 100644
--- a/ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h
+++ b/ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h
@@ -10,14 +10,11 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 class KeyedService;
 class LargeIconCache;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // Singleton that owns all LargeIconCaches and associates them with
 // ChromeBrowserState.
 class IOSChromeLargeIconCacheFactory : public BrowserStateKeyedServiceFactory {
diff --git a/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h b/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h
index bfd6d17..b03605aa 100644
--- a/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h
+++ b/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 class KeyedService;
 
@@ -17,10 +18,6 @@
 class LargeIconService;
 }
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // Singleton that owns all LargeIconService and associates them with
 // ChromeBrowserState.
 class IOSChromeLargeIconServiceFactory
diff --git a/ios/chrome/browser/feature_engagement/tracker_factory.h b/ios/chrome/browser/feature_engagement/tracker_factory.h
index 00f6096..d059862 100644
--- a/ios/chrome/browser/feature_engagement/tracker_factory.h
+++ b/ios/chrome/browser/feature_engagement/tracker_factory.h
@@ -8,10 +8,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}  // namespace ios
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace feature_engagement {
 class Tracker;
diff --git a/ios/chrome/browser/feature_engagement/tracker_util.h b/ios/chrome/browser/feature_engagement/tracker_util.h
index d2c728e..bde764e 100644
--- a/ios/chrome/browser/feature_engagement/tracker_util.h
+++ b/ios/chrome/browser/feature_engagement/tracker_util.h
@@ -5,9 +5,8 @@
 #ifndef IOS_CHROME_BROWSER_FEATURE_ENGAGEMENT_TRACKER_UTIL_H_
 #define IOS_CHROME_BROWSER_FEATURE_ENGAGEMENT_TRACKER_UTIL_H_
 
-namespace ios {
-class ChromeBrowserState;
-}  // namespace ios
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
+
 @class OpenNewTabCommand;
 
 namespace feature_engagement {
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 097e314..6ec7371 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -460,14 +460,6 @@
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(
          autofill::features::kAutofillNoLocalSaveOnUnmaskSuccess)},
-    {"new-omnibox-popup-layout", flag_descriptions::kNewOmniboxPopupLayoutName,
-     flag_descriptions::kNewOmniboxPopupLayoutDescription, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(kNewOmniboxPopupLayout)},
-    {"omnibox-use-default-search-engine-favicon",
-     flag_descriptions::kOmniboxUseDefaultSearchEngineFaviconName,
-     flag_descriptions::kOmniboxUseDefaultSearchEngineFaviconDescription,
-     flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(kOmniboxUseDefaultSearchEngineFavicon)},
     {"omnibox-preserve-default-match-against-async-update",
      flag_descriptions::kOmniboxPreserveDefaultMatchAgainstAsyncUpdateName,
      flag_descriptions::
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 89305763..ac4cf54 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -272,11 +272,6 @@
     "Request the Mobile version of Google SRP by default when the desktop mode "
     "is requested by default.";
 
-const char kNewOmniboxPopupLayoutName[] = "New omnibox popup";
-const char kNewOmniboxPopupLayoutDescription[] =
-    "Switches the omnibox suggestions and omnibox itself to display the new "
-    "design with favicons, new suggestion layout, rich entity support.";
-
 const char kNonModalDialogsName[] = "Use non-modal JavaScript dialogs";
 const char kNonModalDialogsDescription[] =
     "Presents JavaScript dialogs non-modally so that the user can change tabs "
@@ -297,11 +292,6 @@
     "Changes the maximum number of autocomplete matches displayed in the "
     "Omnibox UI.";
 
-const char kOmniboxUseDefaultSearchEngineFaviconName[] =
-    "Default search engine favicon in the omnibox";
-const char kOmniboxUseDefaultSearchEngineFaviconDescription[] =
-    "Shows default search engine favicon in the omnibox";
-
 const char kOmniboxOnDeviceHeadSuggestionsName[] =
     "Omnibox on device head suggestions";
 const char kOmniboxOnDeviceHeadSuggestionsDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 511b7d3..a5f3da0 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -237,10 +237,6 @@
 extern const char kMobileGoogleSRPName[];
 extern const char kMobileGoogleSRPDescription[];
 
-// Title and description for the flag to display new omnibox popup.
-extern const char kNewOmniboxPopupLayoutName[];
-extern const char kNewOmniboxPopupLayoutDescription[];
-
 // Title and description for the flag to enable non-modal JavaScript dialogs.
 extern const char kNonModalDialogsName[];
 extern const char kNonModalDialogsDescription[];
@@ -255,11 +251,6 @@
 extern const char kOmniboxUIMaxAutocompleteMatchesName[];
 extern const char kOmniboxUIMaxAutocompleteMatchesDescription[];
 
-// Title and description for the flag to show default search engine favicon in
-// the omnibox
-extern const char kOmniboxUseDefaultSearchEngineFaviconName[];
-extern const char kOmniboxUseDefaultSearchEngineFaviconDescription[];
-
 // Title and description for the flag to enable Omnibox On Device Head
 // suggestions.
 extern const char kOmniboxOnDeviceHeadSuggestionsName[];
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_modal_overlay_request_callback_installer.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_modal_overlay_request_callback_installer.h
index a8acd47..0a536d1 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_modal_overlay_request_callback_installer.h
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_modal_overlay_request_callback_installer.h
@@ -8,7 +8,6 @@
 #include "base/memory/weak_ptr.h"
 #include "ios/chrome/browser/overlays/public/overlay_request_callback_installer.h"
 
-class OverlayRequestSupport;
 class InfobarModalInteractionHandler;
 
 // Callback installer, intended to be subclassed, for infobar modal interaction
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/BUILD.gn
index 25249927..a249a4a9 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/BUILD.gn
@@ -9,14 +9,21 @@
     "password_infobar_banner_interaction_handler.mm",
     "password_infobar_interaction_handler.h",
     "password_infobar_interaction_handler.mm",
+    "password_infobar_modal_interaction_handler.h",
+    "password_infobar_modal_interaction_handler.mm",
+    "password_infobar_modal_overlay_request_callback_installer.h",
+    "password_infobar_modal_overlay_request_callback_installer.mm",
   ]
   deps = [
     "//base",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/infobars:public",
+    "//ios/chrome/browser/infobars/overlays:util",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common",
+    "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/overlays/public/infobar_banner",
+    "//ios/chrome/browser/overlays/public/infobar_modal",
     "//ios/chrome/browser/passwords:infobar_delegates",
   ]
 }
@@ -24,19 +31,27 @@
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
-  sources = [ "password_infobar_banner_interaction_handler_unittest.mm" ]
+  sources = [
+    "password_infobar_banner_interaction_handler_unittest.mm",
+    "password_infobar_modal_interaction_handler_unittest.mm",
+    "password_infobar_modal_overlay_request_callback_installer_unittest.mm",
+  ]
   deps = [
     ":passwords",
     "//base/test:test_support",
+    "//components/infobars/core:feature_flags",
     "//components/password_manager/core/browser:test_support",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/infobars:public",
+    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test",
     "//ios/chrome/browser/infobars/test",
     "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/common/infobars",
+    "//ios/chrome/browser/overlays/public/infobar_modal",
+    "//ios/chrome/browser/overlays/test",
     "//ios/chrome/browser/passwords:infobar_delegates",
     "//ios/chrome/browser/passwords/test",
-    "//ios/chrome/browser/ui/infobars:feature_flags",
     "//ios/chrome/browser/ui/infobars/test",
     "//ios/chrome/test:test_support",
     "//ios/web/public/test/fakes",
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_interaction_handler.mm
index 9cb2ff91..22a901c 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_interaction_handler.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_interaction_handler.mm
@@ -6,6 +6,7 @@
 
 #import "ios/chrome/browser/infobars/infobar_type.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_banner_interaction_handler.h"
+#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/save_password_infobar_banner_overlay.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -17,10 +18,7 @@
           InfobarType::kInfobarTypePasswordSave,
           std::make_unique<PasswordInfobarBannerInteractionHandler>(),
           /*sheet_handler=*/nullptr,
-          /*modal_handler=*/nullptr) {
-  // TODO(crbug.com/1033154): Create interaction handlers for detail sheet and
-  // modal.
-}
+          std::make_unique<PasswordInfobarModalInteractionHandler>()) {}
 
 PasswordInfobarInteractionHandler::~PasswordInfobarInteractionHandler() =
     default;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.h
new file mode 100644
index 0000000..50c58649
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.h
@@ -0,0 +1,55 @@
+// 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_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_PASSWORDS_PASSWORD_INFOBAR_MODAL_INTERACTION_HANDLER_H_
+#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_PASSWORDS_PASSWORD_INFOBAR_MODAL_INTERACTION_HANDLER_H_
+
+#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_modal_interaction_handler.h"
+
+class IOSChromeSavePasswordInfoBarDelegate;
+
+class PasswordInfobarModalInteractionHandler
+    : public InfobarModalInteractionHandler {
+ public:
+  PasswordInfobarModalInteractionHandler();
+  ~PasswordInfobarModalInteractionHandler() override;
+
+  // Instructs the handler to update the credentials with |username| and
+  // |password| for interaction with |infobar|'s modal UI.
+  // TODO(crbug.com/1040653): This function is only virtual so it can be mocked
+  // for testing purposes.  It should become non-virtual once the password
+  // infobar delegate is refactored for testability.
+  virtual void UpdateCredentials(InfoBarIOS* infobar,
+                                 NSString* username,
+                                 NSString* password);
+  // Instructs the handler that the user has used |infobar|'s modal UI to
+  // request that credentials are never saved for the current site.
+  // TODO(crbug.com/1040653): This function is only virtual so it can be mocked
+  // for testing purposes.  It should become non-virtual once the password
+  // infobar delegate is refactored for testability.
+  virtual void NeverSaveCredentials(InfoBarIOS* infobar);
+  // Instructs the handler that the user has requested the passwords settings
+  // page through |infobar|'s modal UI.  The settings will be presented after
+  // the dismissal of |infobar|'s modal UI.
+  // TODO(crbug.com/1040653): This function is only virtual so it can be mocked
+  // for testing purposes.  It should become non-virtual once the password
+  // infobar delegate is refactored for testability.
+  virtual void PresentPasswordsSettings(InfoBarIOS* infobar);
+
+  // InfobarModalInteractionHandler:
+  void PerformMainAction(InfoBarIOS* infobar) override;
+
+  // InfobarInteractionHandler::Handler:
+  void InfobarVisibilityChanged(InfoBarIOS* infobar, bool visible) override;
+
+ private:
+  // InfobarModalInteractionHandler:
+  std::unique_ptr<InfobarModalOverlayRequestCallbackInstaller>
+  CreateModalInstaller() override;
+
+  // Returns the password delegate from |infobar|.
+  IOSChromeSavePasswordInfoBarDelegate* GetDelegate(InfoBarIOS* infobar);
+};
+
+#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_PASSWORDS_PASSWORD_INFOBAR_MODAL_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.mm
new file mode 100644
index 0000000..5ef4334
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.mm
@@ -0,0 +1,70 @@
+// 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/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.h"
+
+#include "ios/chrome/browser/infobars/infobar_ios.h"
+#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer.h"
+#import "ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+PasswordInfobarModalInteractionHandler::
+    PasswordInfobarModalInteractionHandler() = default;
+
+PasswordInfobarModalInteractionHandler::
+    ~PasswordInfobarModalInteractionHandler() = default;
+
+#pragma mark - Public
+
+void PasswordInfobarModalInteractionHandler::UpdateCredentials(
+    InfoBarIOS* infobar,
+    NSString* username,
+    NSString* password) {
+  GetDelegate(infobar)->UpdateCredentials(username, password);
+}
+
+void PasswordInfobarModalInteractionHandler::NeverSaveCredentials(
+    InfoBarIOS* infobar) {
+  GetDelegate(infobar)->Cancel();
+}
+
+void PasswordInfobarModalInteractionHandler::PresentPasswordsSettings(
+    InfoBarIOS* infobar) {
+  // TODO(crbug.com/1033154): Show the passwords settings.
+}
+
+void PasswordInfobarModalInteractionHandler::PerformMainAction(
+    InfoBarIOS* infobar) {
+  infobar->set_accepted(GetDelegate(infobar)->Accept());
+}
+
+void PasswordInfobarModalInteractionHandler::InfobarVisibilityChanged(
+    InfoBarIOS* infobar,
+    bool visible) {
+  if (visible) {
+    GetDelegate(infobar)->InfobarPresenting(/*automatic=*/NO);
+  } else {
+    GetDelegate(infobar)->InfobarDismissed();
+  }
+}
+
+#pragma mark - Private
+
+std::unique_ptr<InfobarModalOverlayRequestCallbackInstaller>
+PasswordInfobarModalInteractionHandler::CreateModalInstaller() {
+  return std::make_unique<PasswordInfobarModalOverlayRequestCallbackInstaller>(
+      this);
+}
+
+IOSChromeSavePasswordInfoBarDelegate*
+PasswordInfobarModalInteractionHandler::GetDelegate(InfoBarIOS* infobar) {
+  IOSChromeSavePasswordInfoBarDelegate* delegate =
+      IOSChromeSavePasswordInfoBarDelegate::FromInfobarDelegate(
+          infobar->delegate());
+  DCHECK(delegate);
+  return delegate;
+}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler_unittest.mm
new file mode 100644
index 0000000..e86bab7
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler_unittest.mm
@@ -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.
+
+#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "components/infobars/core/infobar_feature.h"
+#import "ios/chrome/browser/infobars/test/fake_infobar_ios.h"
+#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
+#import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
+#import "ios/chrome/browser/ui/infobars/test/fake_infobar_ui_delegate.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test fixture for PasswordInfobarModalInteractionHandler.
+class PasswordInfobarModalInteractionHandlerTest : public PlatformTest {
+ public:
+  PasswordInfobarModalInteractionHandlerTest()
+      : infobar_(
+            [[FakeInfobarUIDelegate alloc] init],
+            MockIOSChromeSavePasswordInfoBarDelegate::Create(@"username",
+                                                             @"password")) {
+    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot}, {});
+  }
+
+  MockIOSChromeSavePasswordInfoBarDelegate& mock_delegate() {
+    return *static_cast<MockIOSChromeSavePasswordInfoBarDelegate*>(
+        infobar_.delegate());
+  }
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  InfoBarIOS infobar_;
+  PasswordInfobarModalInteractionHandler handler_;
+};
+
+// Tests that UpdateCredentials() forwards the call to the mock delegate.
+TEST_F(PasswordInfobarModalInteractionHandlerTest, UpdateCredentials) {
+  NSString* username = @"username";
+  NSString* password = @"password";
+  EXPECT_CALL(mock_delegate(), UpdateCredentials(username, password));
+  handler_.UpdateCredentials(&infobar_, username, password);
+}
+
+// Tests that NeverSaveCredentials() forwards the call to the mock delegate.
+TEST_F(PasswordInfobarModalInteractionHandlerTest, NeverSaveCredentials) {
+  EXPECT_CALL(mock_delegate(), Cancel());
+  handler_.NeverSaveCredentials(&infobar_);
+}
+
+// Tests PerformMainAction() calls Accept() on the mock delegate and resets
+// the infobar to be accepted.
+TEST_F(PasswordInfobarModalInteractionHandlerTest, MainAction) {
+  ASSERT_FALSE(infobar_.accepted());
+  EXPECT_CALL(mock_delegate(), Accept()).WillOnce(testing::Return(true));
+  handler_.PerformMainAction(&infobar_);
+  EXPECT_TRUE(infobar_.accepted());
+}
+
+// Tests that InfobarVisibilityChanged() calls InfobarPresenting() and
+// InfobarDismissed() on the mock delegate.
+TEST_F(PasswordInfobarModalInteractionHandlerTest, InfobarVisibilityChanged) {
+  EXPECT_CALL(mock_delegate(), InfobarPresenting(/*automatic=*/false));
+  handler_.InfobarVisibilityChanged(&infobar_, true);
+  EXPECT_CALL(mock_delegate(), InfobarDismissed());
+  handler_.InfobarVisibilityChanged(&infobar_, false);
+}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer.h
new file mode 100644
index 0000000..19a27bf
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer.h
@@ -0,0 +1,64 @@
+// 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_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_PASSWORDS_PASSWORD_INFOBAR_MODAL_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
+#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_PASSWORDS_PASSWORD_INFOBAR_MODAL_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
+
+#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_modal_overlay_request_callback_installer.h"
+
+#include "base/memory/weak_ptr.h"
+
+class PasswordInfobarModalInteractionHandler;
+
+// Callback installer, intended to be subclassed, for infobar modal interaction
+// events.
+class PasswordInfobarModalOverlayRequestCallbackInstaller
+    : public InfobarModalOverlayRequestCallbackInstaller {
+ public:
+  // Constructor for an instance that installs callbacks that forward
+  // interaction events to |interaction_handler|.
+  explicit PasswordInfobarModalOverlayRequestCallbackInstaller(
+      PasswordInfobarModalInteractionHandler* interaction_handler);
+  ~PasswordInfobarModalOverlayRequestCallbackInstaller() override;
+
+ private:
+  // Used as a callback for OverlayResponses dispatched through |request|'s
+  // callback manager.  The OverlayDispatchCallback is created with an
+  // OverlayResponseSupport that guarantees that |response| is created with an
+  // password_infobar_modal_responses::UpdateCredentialsInfo.
+  void UpdateCredentialsCallback(OverlayRequest* request,
+                                 OverlayResponse* response);
+  // Used as a callback for OverlayResponses dispatched through |request|'s
+  // callback manager.  The OverlayDispatchCallback is created with an
+  // OverlayResponseSupport that guarantees that |response| is created with a
+  // password_infobar_modal_responses::NeverSaveCredentials.
+  void NeverSaveCredentialsCallback(OverlayRequest* request,
+                                    OverlayResponse* response);
+  // Used as a callback for OverlayResponses dispatched through |request|'s
+  // callback manager.  The OverlayDispatchCallback is created with an
+  // OverlayResponseSupport that guarantees that |response| is created with a
+  // password_infobar_modal_responses::PresentPasswordSettings.
+  void PresentPasswordsSettingsCallback(OverlayRequest* request,
+                                        OverlayResponse* response);
+
+  // Used as an optional completion callback for |request|.  Removes the
+  // request's infobar from its manager upon completion.
+  void RemoveInfobarCompletionCallback(OverlayRequest* request,
+                                       OverlayResponse* response);
+  // Used as an optional completion callback for |request|.  Presents the
+  // password settings.
+  void PresentPasswordSettingsCompletionCallback(OverlayRequest* request,
+                                                 OverlayResponse* response);
+
+  // OverlayRequestCallbackInstaller:
+  void InstallCallbacksInternal(OverlayRequest* request) override;
+
+  // The handler for received responses.
+  PasswordInfobarModalInteractionHandler* interaction_handler_ = nullptr;
+
+  base::WeakPtrFactory<PasswordInfobarModalOverlayRequestCallbackInstaller>
+      weak_factory_{this};
+};
+
+#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_PASSWORDS_PASSWORD_INFOBAR_MODAL_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer.mm
new file mode 100644
index 0000000..a49c377
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer.mm
@@ -0,0 +1,108 @@
+// 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/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer.h"
+
+#include "base/logging.h"
+#include "ios/chrome/browser/infobars/infobar_ios.h"
+#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
+#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.h"
+#include "ios/chrome/browser/infobars/overlays/overlay_request_infobar_util.h"
+#import "ios/chrome/browser/overlays/public/infobar_modal/password_infobar_modal_overlay_request_config.h"
+#import "ios/chrome/browser/overlays/public/infobar_modal/password_infobar_modal_overlay_responses.h"
+#include "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
+#import "ios/chrome/browser/overlays/public/overlay_response.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using password_infobar_modal_responses::UpdateCredentialsInfo;
+using password_infobar_modal_responses::NeverSaveCredentials;
+using password_infobar_modal_responses::PresentPasswordSettings;
+
+PasswordInfobarModalOverlayRequestCallbackInstaller::
+    PasswordInfobarModalOverlayRequestCallbackInstaller(
+        PasswordInfobarModalInteractionHandler* interaction_handler)
+    : InfobarModalOverlayRequestCallbackInstaller(
+          PasswordInfobarModalOverlayRequestConfig::RequestSupport(),
+          interaction_handler),
+      interaction_handler_(interaction_handler) {
+  DCHECK(interaction_handler_);
+}
+
+PasswordInfobarModalOverlayRequestCallbackInstaller::
+    ~PasswordInfobarModalOverlayRequestCallbackInstaller() = default;
+
+#pragma mark - Private
+
+void PasswordInfobarModalOverlayRequestCallbackInstaller::
+    UpdateCredentialsCallback(OverlayRequest* request,
+                              OverlayResponse* response) {
+  UpdateCredentialsInfo* info = response->GetInfo<UpdateCredentialsInfo>();
+  interaction_handler_->UpdateCredentials(GetOverlayRequestInfobar(request),
+                                          info->username(), info->password());
+}
+
+void PasswordInfobarModalOverlayRequestCallbackInstaller::
+    NeverSaveCredentialsCallback(OverlayRequest* request,
+                                 OverlayResponse* response) {
+  // Inform the interaction handler to never save credentials, then add the
+  // infobar removal callback as a completion.  This causes the infobar and its
+  // badge to be removed once the infobar modal's dismissal finishes.
+  interaction_handler_->NeverSaveCredentials(GetOverlayRequestInfobar(request));
+  request->GetCallbackManager()->AddCompletionCallback(
+      base::BindOnce(&PasswordInfobarModalOverlayRequestCallbackInstaller::
+                         RemoveInfobarCompletionCallback,
+                     weak_factory_.GetWeakPtr(), request));
+}
+
+void PasswordInfobarModalOverlayRequestCallbackInstaller::
+    PresentPasswordsSettingsCallback(OverlayRequest* request,
+                                     OverlayResponse* response) {
+  // Add a completion callback to trigger the presentation of the password
+  // settings view once the infobar modal's dismissal finishes.
+  request->GetCallbackManager()->AddCompletionCallback(
+      base::BindOnce(&PasswordInfobarModalOverlayRequestCallbackInstaller::
+                         PresentPasswordSettingsCompletionCallback,
+                     weak_factory_.GetWeakPtr(), request));
+}
+
+void PasswordInfobarModalOverlayRequestCallbackInstaller::
+    RemoveInfobarCompletionCallback(OverlayRequest* request,
+                                    OverlayResponse* response) {
+  InfoBarManagerImpl::FromWebState(request->GetQueueWebState())
+      ->RemoveInfoBar(GetOverlayRequestInfobar(request));
+}
+
+void PasswordInfobarModalOverlayRequestCallbackInstaller::
+    PresentPasswordSettingsCompletionCallback(OverlayRequest* request,
+                                              OverlayResponse* response) {
+  interaction_handler_->PresentPasswordsSettings(
+      GetOverlayRequestInfobar(request));
+}
+
+#pragma mark - OverlayRequestCallbackInstaller
+
+void PasswordInfobarModalOverlayRequestCallbackInstaller::
+    InstallCallbacksInternal(OverlayRequest* request) {
+  InfobarModalOverlayRequestCallbackInstaller::InstallCallbacksInternal(
+      request);
+  OverlayCallbackManager* manager = request->GetCallbackManager();
+  manager->AddDispatchCallback(OverlayDispatchCallback(
+      base::BindRepeating(&PasswordInfobarModalOverlayRequestCallbackInstaller::
+                              UpdateCredentialsCallback,
+                          weak_factory_.GetWeakPtr(), request),
+      UpdateCredentialsInfo::ResponseSupport()));
+  manager->AddDispatchCallback(OverlayDispatchCallback(
+      base::BindRepeating(&PasswordInfobarModalOverlayRequestCallbackInstaller::
+                              NeverSaveCredentialsCallback,
+                          weak_factory_.GetWeakPtr(), request),
+      NeverSaveCredentials::ResponseSupport()));
+  manager->AddDispatchCallback(OverlayDispatchCallback(
+      base::BindRepeating(&PasswordInfobarModalOverlayRequestCallbackInstaller::
+                              PresentPasswordsSettingsCallback,
+                          weak_factory_.GetWeakPtr(), request),
+      PresentPasswordSettings::ResponseSupport()));
+}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer_unittest.mm
new file mode 100644
index 0000000..e568e2c
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer_unittest.mm
@@ -0,0 +1,132 @@
+// 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/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "components/infobars/core/infobar_feature.h"
+#include "ios/chrome/browser/infobars/infobar_ios.h"
+#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
+#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test/mock_password_infobar_modal_interaction_handler.h"
+#import "ios/chrome/browser/overlays/public/common/infobars/infobar_overlay_request_config.h"
+#include "ios/chrome/browser/overlays/public/infobar_modal/infobar_modal_overlay_responses.h"
+#import "ios/chrome/browser/overlays/public/infobar_modal/password_infobar_modal_overlay_request_config.h"
+#import "ios/chrome/browser/overlays/public/infobar_modal/password_infobar_modal_overlay_responses.h"
+#include "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
+#include "ios/chrome/browser/overlays/public/overlay_request.h"
+#include "ios/chrome/browser/overlays/public/overlay_request_queue.h"
+#include "ios/chrome/browser/overlays/public/overlay_response.h"
+#include "ios/chrome/browser/overlays/test/overlay_test_macros.h"
+#import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
+#import "ios/chrome/browser/ui/infobars/test/fake_infobar_ui_delegate.h"
+#import "ios/web/public/test/fakes/test_navigation_manager.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using password_infobar_modal_responses::UpdateCredentialsInfo;
+using password_infobar_modal_responses::NeverSaveCredentials;
+using password_infobar_modal_responses::PresentPasswordSettings;
+
+// Test fixture for PasswordInfobarModalOverlayRequestCallbackInstaller.
+class PasswordInfobarModalOverlayRequestCallbackInstallerTest
+    : public PlatformTest {
+ public:
+  PasswordInfobarModalOverlayRequestCallbackInstallerTest()
+      : installer_(&mock_handler_) {
+    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot}, {});
+    // Create the infobar and add it to the WebState's manager.
+    web_state_.SetNavigationManager(
+        std::make_unique<web::TestNavigationManager>());
+    InfoBarManagerImpl::CreateForWebState(&web_state_);
+    std::unique_ptr<InfoBarIOS> added_infobar = std::make_unique<InfoBarIOS>(
+        [[FakeInfobarUIDelegate alloc] init],
+        MockIOSChromeSavePasswordInfoBarDelegate::Create(@"username",
+                                                         @"password"));
+    infobar_ = added_infobar.get();
+    manager()->AddInfoBar(std::move(added_infobar));
+    // Create the request and add it to the WebState's queue.
+    std::unique_ptr<OverlayRequest> added_request =
+        OverlayRequest::CreateWithConfig<
+            PasswordInfobarModalOverlayRequestConfig>(infobar_);
+    request_ = added_request.get();
+    queue()->AddRequest(std::move(added_request));
+    // Install the callbacks on the added request.
+    installer_.InstallCallbacks(request_);
+  }
+
+  InfoBarManagerImpl* manager() {
+    return InfoBarManagerImpl::FromWebState(&web_state_);
+  }
+  OverlayRequestQueue* queue() {
+    return OverlayRequestQueue::FromWebState(&web_state_,
+                                             OverlayModality::kInfobarModal);
+  }
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  web::TestWebState web_state_;
+  InfoBarIOS* infobar_ = nullptr;
+  OverlayRequest* request_ = nullptr;
+  MockPasswordInfobarModalInteractionHandler mock_handler_;
+  PasswordInfobarModalOverlayRequestCallbackInstaller installer_;
+};
+
+// Tests that a dispatched InfobarBannerMainActionResponse calls
+// PasswordInfobarModalInteractionHandler::MainButtonTapped().
+TEST_F(PasswordInfobarModalOverlayRequestCallbackInstallerTest, MainAction) {
+  EXPECT_CALL(mock_handler_, PerformMainAction(infobar_));
+  request_->GetCallbackManager()->DispatchResponse(
+      OverlayResponse::CreateWithInfo<InfobarModalMainActionResponse>());
+}
+
+// Tests that a dispatched UpdateCredentialsInfo responses calls
+// PasswordInfobarModalInteractionHandler::UpdateCredentials().
+TEST_F(PasswordInfobarModalOverlayRequestCallbackInstallerTest,
+       UpdateCredentials) {
+  NSString* username = @"update_username";
+  NSString* password = @"update_password";
+  EXPECT_CALL(mock_handler_, UpdateCredentials(infobar_, username, password));
+  request_->GetCallbackManager()->DispatchResponse(
+      OverlayResponse::CreateWithInfo<UpdateCredentialsInfo>(username,
+                                                             password));
+}
+
+// Tests the flow for the NeverSaveCredentials response.
+TEST_F(PasswordInfobarModalOverlayRequestCallbackInstallerTest,
+       NeverSaveCredentials) {
+  // Send an NeverSaveCredentials response, expecting the interaction handler
+  // update to be called.
+  EXPECT_CALL(mock_handler_, NeverSaveCredentials(infobar_));
+  request_->GetCallbackManager()->DispatchResponse(
+      OverlayResponse::CreateWithInfo<NeverSaveCredentials>());
+
+  // When the installer handles the NeverSaveCredentials response, it adds a
+  // completion callback to the request that removes its infobar from the
+  // manager so that the badge/infobar is no longer shown for this page.  Cancel
+  // the request to trigger this completion callback and verify that the infobar
+  // is successfully removed from its manager.
+  queue()->CancelAllRequests();
+  EXPECT_EQ(0U, manager()->infobar_count());
+}
+
+// Tests the flow for the PresentPasswordSettings response.
+TEST_F(PasswordInfobarModalOverlayRequestCallbackInstallerTest,
+       PresentPasswordSettings) {
+  // Dispatch a PresentPasswordSettings response.
+  request_->GetCallbackManager()->DispatchResponse(
+      OverlayResponse::CreateWithInfo<PresentPasswordSettings>());
+
+  // When the installer handles the PresentPasswordSettings response, it adds a
+  // completion callback to the request that instructs the interaction handler
+  // to present settings when the dismissal finishes.  Cancel the request to
+  // trigger this completion callback and verify that the interaction handler's
+  // PresentPasswordSettings() was called.
+  EXPECT_CALL(mock_handler_, PresentPasswordsSettings(infobar_));
+  queue()->CancelAllRequests();
+}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test/BUILD.gn
new file mode 100644
index 0000000..46309e07
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test/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.
+
+source_set("test") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "mock_password_infobar_modal_interaction_handler.h",
+    "mock_password_infobar_modal_interaction_handler.mm",
+  ]
+  deps = [
+    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords",
+    "//ios/chrome/browser/overlays",
+    "//testing/gmock",
+  ]
+}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test/mock_password_infobar_modal_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test/mock_password_infobar_modal_interaction_handler.h
new file mode 100644
index 0000000..27a32c88
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test/mock_password_infobar_modal_interaction_handler.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_PASSWORDS_TEST_MOCK_PASSWORD_INFOBAR_MODAL_INTERACTION_HANDLER_H_
+#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_PASSWORDS_TEST_MOCK_PASSWORD_INFOBAR_MODAL_INTERACTION_HANDLER_H_
+
+#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+// Mock version of PasswordInfobarModalInteractionHandler for use in tests.
+class MockPasswordInfobarModalInteractionHandler
+    : public PasswordInfobarModalInteractionHandler {
+ public:
+  MockPasswordInfobarModalInteractionHandler();
+  ~MockPasswordInfobarModalInteractionHandler();
+
+  MOCK_METHOD3(UpdateCredentials,
+               void(InfoBarIOS* infobar,
+                    NSString* username,
+                    NSString* password));
+  MOCK_METHOD1(NeverSaveCredentials, void(InfoBarIOS* infobar));
+  MOCK_METHOD1(PresentPasswordsSettings, void(InfoBarIOS* infobar));
+  MOCK_METHOD1(PerformMainAction, void(InfoBarIOS* infobar));
+  MOCK_METHOD2(InfobarVisibilityChanged,
+               void(InfoBarIOS* infobar, bool visible));
+};
+
+#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_PASSWORDS_TEST_MOCK_PASSWORD_INFOBAR_MODAL_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test/mock_password_infobar_modal_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test/mock_password_infobar_modal_interaction_handler.mm
new file mode 100644
index 0000000..97b89c3
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test/mock_password_infobar_modal_interaction_handler.mm
@@ -0,0 +1,15 @@
+// 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/infobars/overlays/browser_agent/interaction_handlers/passwords/test/mock_password_infobar_modal_interaction_handler.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+MockPasswordInfobarModalInteractionHandler::
+    MockPasswordInfobarModalInteractionHandler() = default;
+
+MockPasswordInfobarModalInteractionHandler::
+    ~MockPasswordInfobarModalInteractionHandler() = default;
diff --git a/ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.h b/ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.h
index 35e466c2..546c3567 100644
--- a/ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.h
+++ b/ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.h
@@ -7,9 +7,7 @@
 
 #include <memory>
 
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace ntp_tiles {
 class MostVisitedSites;
diff --git a/ios/chrome/browser/ntp_tiles/ios_popular_sites_factory.h b/ios/chrome/browser/ntp_tiles/ios_popular_sites_factory.h
index 7faadd7..b30c79b0 100644
--- a/ios/chrome/browser/ntp_tiles/ios_popular_sites_factory.h
+++ b/ios/chrome/browser/ntp_tiles/ios_popular_sites_factory.h
@@ -7,9 +7,7 @@
 
 #include <memory>
 
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace ntp_tiles {
 class PopularSites;
diff --git a/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h b/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h
index 1a4db77c..611bfaf1 100644
--- a/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h
+++ b/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h
@@ -49,7 +49,10 @@
   void InfoBarDismissed() override;
 
   // Updates the credentials being saved with |username| and |password|.
-  void UpdateCredentials(NSString* username, NSString* password);
+  // TODO(crbug.com/1040653): This function is only virtual so it can be mocked
+  // for testing purposes.  It should become non-virtual once this test is
+  // refactored for testability.
+  virtual void UpdateCredentials(NSString* username, NSString* password);
 
   // Informs the delegate that the Infobar has been presented. If |automatic|
   // YES the Infobar was presented automatically (e.g. The banner was
diff --git a/ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h b/ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h
index 781e04ee..9d995971 100644
--- a/ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h
+++ b/ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h
@@ -31,7 +31,9 @@
       const GURL& url = GURL::EmptyGURL());
 
   MOCK_METHOD0(InfoBarDismissed, void());
+  MOCK_METHOD2(UpdateCredentials, void(NSString* username, NSString* password));
   MOCK_METHOD0(Accept, bool());
+  MOCK_METHOD0(Cancel, bool());
   MOCK_METHOD1(InfobarPresenting, void(bool automatic));
   MOCK_METHOD0(InfobarDismissed, void());
 
diff --git a/ios/chrome/browser/payments/ios_can_make_payment_query_factory.h b/ios/chrome/browser/payments/ios_can_make_payment_query_factory.h
index 7e921a69..a6b99d16 100644
--- a/ios/chrome/browser/payments/ios_can_make_payment_query_factory.h
+++ b/ios/chrome/browser/payments/ios_can_make_payment_query_factory.h
@@ -10,10 +10,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}  // namespace ios
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace payments {
 class CanMakePaymentQuery;
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.h b/ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.h
index 8536902..8ec12b78 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.h
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.h
@@ -10,10 +10,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}  // namespace ios
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace payments {
 class IOSPaymentInstrumentLauncher;
diff --git a/ios/chrome/browser/payments/ios_payment_request_cache_factory.h b/ios/chrome/browser/payments/ios_payment_request_cache_factory.h
index 38dda67..605d30a 100644
--- a/ios/chrome/browser/payments/ios_payment_request_cache_factory.h
+++ b/ios/chrome/browser/payments/ios_payment_request_cache_factory.h
@@ -10,10 +10,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}  // namespace ios
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace payments {
 class PaymentRequestCache;
diff --git a/ios/chrome/browser/payments/payment_request.h b/ios/chrome/browser/payments/payment_request.h
index 0dbdcaa..b7902110 100644
--- a/ios/chrome/browser/payments/payment_request.h
+++ b/ios/chrome/browser/payments/payment_request.h
@@ -22,6 +22,7 @@
 #include "components/payments/core/payment_request_base_delegate.h"
 #include "components/payments/core/payments_profile_comparator.h"
 #include "components/payments/core/web_payment_request.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #import "ios/chrome/browser/payments/ios_payment_instrument_finder.h"
 #import "ios/chrome/browser/payments/payment_response_helper.h"
 #include "url/gurl.h"
@@ -42,10 +43,6 @@
 class PaymentShippingOption;
 }  // namespace payments
 
-namespace ios {
-class ChromeBrowserState;
-}  // namepsace ios
-
 namespace web {
 class WebState;
 }  // namespace web
diff --git a/ios/chrome/browser/payments/test_payment_request.h b/ios/chrome/browser/payments/test_payment_request.h
index 542dda5..212e14d 100644
--- a/ios/chrome/browser/payments/test_payment_request.h
+++ b/ios/chrome/browser/payments/test_payment_request.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "components/autofill/core/browser/address_normalization_manager.h"
 #include "components/autofill/core/browser/test_address_normalizer.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 
 namespace autofill {
@@ -15,10 +16,6 @@
 class RegionDataLoader;
 }  // namespace autofill
 
-namespace ios {
-class ChromeBrowserState;
-}  // namespace ios
-
 namespace payments {
 class PaymentShippingOption;
 class PaymentsProfileComparator;
diff --git a/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_client.h b/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_client.h
index 923ffe5..9b75a8a 100644
--- a/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_client.h
+++ b/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_client.h
@@ -7,10 +7,7 @@
 
 #include "base/macros.h"
 #include "components/sessions/core/tab_restore_service_client.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 // IOSChromeTabRestoreServiceClient provides an implementation of
 // TabRestoreServiceClient that depends on ios/chrome/.
diff --git a/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h b/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h
index 99b57ba..b650002 100644
--- a/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h
+++ b/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h
@@ -10,15 +10,12 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace sessions {
 class TabRestoreService;
 }
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // Singleton that owns all TabRestoreServices and associates them with
 // ChromeBrowserStates.
 class IOSChromeTabRestoreServiceFactory
diff --git a/ios/chrome/browser/sessions/session_restoration_browser_agent.h b/ios/chrome/browser/sessions/session_restoration_browser_agent.h
index fb5b3800..0978cc80 100644
--- a/ios/chrome/browser/sessions/session_restoration_browser_agent.h
+++ b/ios/chrome/browser/sessions/session_restoration_browser_agent.h
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "base/observer_list.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #include "ios/chrome/browser/main/browser_user_data.h"
 
 @class SessionWindowIOS;
@@ -19,10 +20,6 @@
 class WebStateList;
 @class SessionServiceIOS;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // This class is responsible for handling requests of session restoration. It
 // can be observed via SeassonRestorationObserver which it uses to notify
 // observers of session restoration events.
diff --git a/ios/chrome/browser/sessions/session_util.h b/ios/chrome/browser/sessions/session_util.h
index 44b9108..7914c88 100644
--- a/ios/chrome/browser/sessions/session_util.h
+++ b/ios/chrome/browser/sessions/session_util.h
@@ -9,10 +9,7 @@
 #include <vector>
 
 #include "base/callback.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 namespace sessions {
 class SerializedNavigationEntry;
diff --git a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h
index 8801a4dc..087bfdf 100644
--- a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h
+++ b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h
@@ -13,13 +13,10 @@
 #include "components/sessions/core/live_tab_context.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 class WebStateList;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // Implementation of sessions::LiveTabContext which uses an instance
 // of TabModel in order to fulfil its duties.
 class TabRestoreServiceDelegateImplIOS : public sessions::LiveTabContext,
diff --git a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h
index 96c6227..15184c2b 100644
--- a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h
+++ b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h
@@ -10,10 +10,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 class TabRestoreServiceDelegateImplIOS;
 
diff --git a/ios/chrome/browser/snapshots/snapshot_cache_factory.h b/ios/chrome/browser/snapshots/snapshot_cache_factory.h
index 2ed1dd2..f790a6e 100644
--- a/ios/chrome/browser/snapshots/snapshot_cache_factory.h
+++ b/ios/chrome/browser/snapshots/snapshot_cache_factory.h
@@ -10,13 +10,10 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 @class SnapshotCache;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // Singleton that owns all SnapshotCaches and associates them with
 // ios::ChromeBrowserState.
 class SnapshotCacheFactory : public BrowserStateKeyedServiceFactory {
diff --git a/ios/chrome/browser/snapshots/snapshot_cache_tab_model_list_observer.h b/ios/chrome/browser/snapshots/snapshot_cache_tab_model_list_observer.h
index 32a0e6f7b2..0500358 100644
--- a/ios/chrome/browser/snapshots/snapshot_cache_tab_model_list_observer.h
+++ b/ios/chrome/browser/snapshots/snapshot_cache_tab_model_list_observer.h
@@ -9,16 +9,13 @@
 
 #include "base/macros.h"
 #include "base/scoped_observer.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #import "ios/chrome/browser/tabs/tab_model_list_observer.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/chrome/browser/web_state_list/web_state_list_observer.h"
 
 @class TabModel;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // SnapshotCacheTabModelListObserver tracks when TabModels are created and
 // destroyed for a given ChromeBrowserState.  Whenever the TabModelList changes,
 // SnapshotCacheTabModelListObserver registers a provided observer as a
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn
index 6914ef4..a31ef04 100644
--- a/ios/chrome/browser/tabs/BUILD.gn
+++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -18,6 +18,7 @@
   deps = [
     "//components/sessions",
     "//components/sync_sessions",
+    "//ios/chrome/browser/browser_state:forward",
     "//ios/chrome/browser/sessions:serialisation",
     "//ios/chrome/browser/sessions:session_service",
     "//ios/chrome/browser/web_state_list",
diff --git a/ios/chrome/browser/tabs/tab_model.h b/ios/chrome/browser/tabs/tab_model.h
index f97b3f98..aeeb38db 100644
--- a/ios/chrome/browser/tabs/tab_model.h
+++ b/ios/chrome/browser/tabs/tab_model.h
@@ -8,6 +8,7 @@
 #import <Foundation/Foundation.h>
 #import <UIKit/UIKit.h>
 
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #import "ios/chrome/browser/sessions/session_window_restoring.h"
 
 @class SessionServiceIOS;
@@ -16,10 +17,6 @@
 class WebStateList;
 class Browser;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // A model of a tab "strip". Although the UI representation may not be a
 // traditional strip at all, tabs are still accessed via an integral index.
 // The model knows about the currently selected tab in order to maintain
diff --git a/ios/chrome/browser/tabs/tab_model_list.h b/ios/chrome/browser/tabs/tab_model_list.h
index 3c2b049..896b249 100644
--- a/ios/chrome/browser/tabs/tab_model_list.h
+++ b/ios/chrome/browser/tabs/tab_model_list.h
@@ -8,15 +8,12 @@
 #import <Foundation/Foundation.h>
 
 #include "base/macros.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 @class TabModel;
 
 class TabModelListObserver;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // A class containing static functions to help maintain a 1:N relationship
 // between an ios::ChromeBrowserState and multiple TabModels.
 class TabModelList {
diff --git a/ios/chrome/browser/tabs/tab_model_list_observer.h b/ios/chrome/browser/tabs/tab_model_list_observer.h
index b63f8cc..c7eca06 100644
--- a/ios/chrome/browser/tabs/tab_model_list_observer.h
+++ b/ios/chrome/browser/tabs/tab_model_list_observer.h
@@ -6,13 +6,10 @@
 #define IOS_CHROME_BROWSER_TABS_TAB_MODEL_LIST_OBSERVER_H_
 
 #include "base/macros.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 @class TabModel;
 
-namespace ios {
-class ChromeBrowserState;
-}  // namespace ios
-
 // Interface for getting notified when TabModels get associated/dissociated
 // to/from browser states.
 class TabModelListObserver {
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.h b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
index ae7904ca..ebccb55 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.h
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
@@ -8,6 +8,7 @@
 #import <UIKit/UIKit.h>
 
 #import "base/ios/block_types.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #import "ios/chrome/browser/ui/page_info/requirements/page_info_presentation.h"
 #import "ios/chrome/browser/ui/settings/sync/utils/sync_presenter.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h"
@@ -28,10 +29,6 @@
 @class TabModel;
 @protocol ToolbarCommands;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // The top-level view controller for the browser UI. Manages other controllers
 // which implement the interface.
 @interface BrowserViewController
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller_dependency_factory.h b/ios/chrome/browser/ui/browser_view/browser_view_controller_dependency_factory.h
index ac163668..9b48281 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller_dependency_factory.h
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller_dependency_factory.h
@@ -7,15 +7,13 @@
 
 #import <UIKit/UIKit.h>
 
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
+
 @class AlertCoordinator;
 @class BrowserViewControllerHelper;
 @class KeyCommandsProvider;
 class WebStateList;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // Creates helper objects needed by BrowserViewController.
 @interface BrowserViewControllerDependencyFactory : NSObject
 
diff --git a/ios/chrome/browser/ui/coordinators/BUILD.gn b/ios/chrome/browser/ui/coordinators/BUILD.gn
index 6969e04d..245e7d3 100644
--- a/ios/chrome/browser/ui/coordinators/BUILD.gn
+++ b/ios/chrome/browser/ui/coordinators/BUILD.gn
@@ -12,6 +12,7 @@
 
   deps = [
     "//base",
+    "//ios/chrome/browser/browser_state:forward",
     "//ios/chrome/browser/main:public",
   ]
 }
diff --git a/ios/chrome/browser/ui/coordinators/chrome_coordinator.h b/ios/chrome/browser/ui/coordinators/chrome_coordinator.h
index 1a745f2..046b0f9 100644
--- a/ios/chrome/browser/ui/coordinators/chrome_coordinator.h
+++ b/ios/chrome/browser/ui/coordinators/chrome_coordinator.h
@@ -7,11 +7,11 @@
 
 #import <UIKit/UIKit.h>
 
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
+
 @class ChromeCoordinator;
 class Browser;
-namespace ios {
-class ChromeBrowserState;
-}
+
 typedef NSMutableArray<ChromeCoordinator*> MutableCoordinatorArray;
 
 // A coordinator object that manages view controllers and other coordinators.
diff --git a/ios/chrome/browser/ui/fullscreen/BUILD.gn b/ios/chrome/browser/ui/fullscreen/BUILD.gn
index a0d6094..3ed81fb 100644
--- a/ios/chrome/browser/ui/fullscreen/BUILD.gn
+++ b/ios/chrome/browser/ui/fullscreen/BUILD.gn
@@ -23,6 +23,7 @@
     "//base",
     "//components/keyed_service/core",
     "//components/keyed_service/ios",
+    "//ios/chrome/browser/browser_state:forward",
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/common",
   ]
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_factory.h b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_factory.h
index 890d26de..1a65874c 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_factory.h
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_factory.h
@@ -8,10 +8,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 class FullscreenController;
 
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_generic_coordinator.h b/ios/chrome/browser/ui/location_bar/location_bar_generic_coordinator.h
index fde69bf..dd9693ed 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_generic_coordinator.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_generic_coordinator.h
@@ -7,13 +7,11 @@
 
 #import <UIKit/UIKit.h>
 
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_url_loader.h"
 #import "ios/chrome/browser/ui/omnibox/location_bar_delegate.h"
 #import "ios/chrome/browser/ui/toolbar/public/omnibox_focuser.h"
 
-namespace ios {
-class ChromeBrowserState;
-}
 class WebStateList;
 @class CommandDispatcher;
 @protocol ApplicationCommands;
diff --git a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h
index d6295b5..f45db247 100644
--- a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h
+++ b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h
@@ -10,13 +10,10 @@
 #include "base/compiler_specific.h"
 #include "components/omnibox/browser/omnibox_client.h"
 #include "ios/chrome/browser/autocomplete/autocomplete_scheme_classifier_impl.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 class WebOmniboxEditController;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 class ChromeOmniboxClientIOS : public OmniboxClient {
  public:
   ChromeOmniboxClientIOS(WebOmniboxEditController* controller,
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_container_view.mm b/ios/chrome/browser/ui/omnibox/omnibox_container_view.mm
index 3f57e1d4..bfff5e5e 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_container_view.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_container_view.mm
@@ -5,7 +5,6 @@
 #import "ios/chrome/browser/ui/omnibox/omnibox_container_view.h"
 
 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
-#include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/ui/util/animation_util.h"
 #import "ios/chrome/browser/ui/util/named_guide.h"
 #include "ios/chrome/browser/ui/util/rtl_geometry.h"
@@ -157,43 +156,12 @@
   _leadingImageView.translatesAutoresizingMaskIntoConstraints = NO;
   _leadingImageView.contentMode = UIViewContentModeCenter;
 
-  // When the flag is enabled, the image view is always shown. Its width should
-  // also be constant.
-  if (base::FeatureList::IsEnabled(kNewOmniboxPopupLayout)) {
-    [NSLayoutConstraint activateConstraints:@[
-      [_leadingImageView.widthAnchor
-          constraintEqualToConstant:kLeadingImageSize],
-      [_leadingImageView.heightAnchor
-          constraintEqualToAnchor:_leadingImageView.widthAnchor],
-    ]];
-  } else {
-    [_leadingImageView
-        setContentCompressionResistancePriority:UILayoutPriorityRequired
-                                        forAxis:
-                                            UILayoutConstraintAxisHorizontal];
-    [_leadingImageView
-        setContentCompressionResistancePriority:UILayoutPriorityRequired
-                                        forAxis:UILayoutConstraintAxisVertical];
-    [_leadingImageView
-        setContentHuggingPriority:UILayoutPriorityDefaultLow
-                          forAxis:UILayoutConstraintAxisHorizontal];
-    [_leadingImageView
-        setContentHuggingPriority:UILayoutPriorityRequired
-                          forAxis:UILayoutConstraintAxisVertical];
-
-    // Sometimes the image view is not hidden and has no image. Then it doesn't
-    // have an intrinsic size. In this case the omnibox should appear the same
-    // as with hidden image view. Add a placeholder width constraint.
-    CGFloat placeholderSize = kTextFieldLeadingOffsetNoImage -
-                              kleadingImageViewEdgeOffset -
-                              kTextFieldLeadingOffsetImage;
-    NSLayoutConstraint* placeholderWidthConstraint =
-        [_leadingImageView.widthAnchor
-            constraintEqualToConstant:placeholderSize];
-    // The priority must be higher than content hugging.
-    placeholderWidthConstraint.priority = UILayoutPriorityDefaultLow + 1;
-    placeholderWidthConstraint.active = YES;
-  }
+  // The image view is always shown. Its width should be constant.
+  [NSLayoutConstraint activateConstraints:@[
+    [_leadingImageView.widthAnchor constraintEqualToConstant:kLeadingImageSize],
+    [_leadingImageView.heightAnchor
+        constraintEqualToAnchor:_leadingImageView.widthAnchor],
+  ]];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.h b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.h
index e0609a5e..469e14d 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.h
@@ -7,9 +7,8 @@
 
 #import <UIKit/UIKit.h>
 
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
+
 class WebOmniboxEditController;
 @class CommandDispatcher;
 @protocol EditViewAnimatee;
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
index d7bed07..44793dde 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
@@ -65,8 +65,8 @@
   self.viewController =
       [[OmniboxViewController alloc] initWithIncognito:isIncognito];
 
-  self.viewController.defaultLeadingImage = GetOmniboxSuggestionIcon(
-      DEFAULT_FAVICON, base::FeatureList::IsEnabled(kNewOmniboxPopupLayout));
+  self.viewController.defaultLeadingImage =
+      GetOmniboxSuggestionIcon(DEFAULT_FAVICON);
   self.viewController.dispatcher =
       static_cast<id<BrowserCommands, LoadQueryCommands, OmniboxFocuser>>(
           self.dispatcher);
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm b/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
index 283cc7f..3c76943 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
@@ -101,23 +101,19 @@
       matchType, /* is_starred */ false);
   [self.consumer updateAutocompleteIcon:image];
 
-  if (base::FeatureList::IsEnabled(kNewOmniboxPopupLayout)) {
-    __weak OmniboxMediator* weakSelf = self;
+  __weak OmniboxMediator* weakSelf = self;
 
-    if (AutocompleteMatch::IsSearchType(matchType)) {
-      if (base::FeatureList::IsEnabled(kOmniboxUseDefaultSearchEngineFavicon)) {
-        // Show Default Search Engine favicon.
-        [self loadDefaultSearchEngineFaviconWithCompletion:^(UIImage* image) {
-          [weakSelf.consumer updateAutocompleteIcon:image];
-        }];
-      }
-    } else {
-      // Show favicon.
-      [self loadFaviconByPageURL:faviconURL
-                      completion:^(UIImage* image) {
-                        [weakSelf.consumer updateAutocompleteIcon:image];
-                      }];
-    }
+  if (AutocompleteMatch::IsSearchType(matchType)) {
+    // Show Default Search Engine favicon.
+    [self loadDefaultSearchEngineFaviconWithCompletion:^(UIImage* image) {
+      [weakSelf.consumer updateAutocompleteIcon:image];
+    }];
+  } else {
+    // Show favicon.
+    [self loadFaviconByPageURL:faviconURL
+                    completion:^(UIImage* image) {
+                      [weakSelf.consumer updateAutocompleteIcon:image];
+                    }];
   }
 }
 
@@ -127,12 +123,10 @@
   [self.consumer updateAutocompleteIcon:image];
 
   __weak OmniboxMediator* weakSelf = self;
-  if (base::FeatureList::IsEnabled(kOmniboxUseDefaultSearchEngineFavicon)) {
-    // Show Default Search Engine favicon.
-    [self loadDefaultSearchEngineFaviconWithCompletion:^(UIImage* image) {
-      [weakSelf.consumer updateAutocompleteIcon:image];
-    }];
-  }
+  // Show Default Search Engine favicon.
+  [self loadDefaultSearchEngineFaviconWithCompletion:^(UIImage* image) {
+    [weakSelf.consumer updateAutocompleteIcon:image];
+  }];
 }
 
 // Loads a favicon for a given page URL.
@@ -201,7 +195,6 @@
 
   // Can't load favicons without a favicon loader.
   DCHECK(self.faviconLoader);
-  DCHECK(base::FeatureList::IsEnabled(kOmniboxUseDefaultSearchEngineFavicon));
 
   const TemplateURL* defaultProvider =
       self.templateURLService->GetDefaultSearchProvider();
@@ -247,17 +240,14 @@
   [_consumer
       updateSearchByImageSupported:self.searchEngineSupportsSearchByImage];
 
-  if (base::FeatureList::IsEnabled(kNewOmniboxPopupLayout) &&
-      base::FeatureList::IsEnabled(kOmniboxUseDefaultSearchEngineFavicon)) {
-    // Show Default Search Engine favicon.
-    // Remember what is the Default Search Engine provider that the icon is
-    // for, in case the user changes Default Search Engine while this is being
-    // loaded.
-    __weak __typeof(self) weakSelf = self;
-    [self loadDefaultSearchEngineFaviconWithCompletion:^(UIImage* image) {
-      [weakSelf.consumer setEmptyTextLeadingImage:image];
-    }];
-  }
+  // Show Default Search Engine favicon.
+  // Remember what is the Default Search Engine provider that the icon is
+  // for, in case the user changes Default Search Engine while this is being
+  // loaded.
+  __weak __typeof(self) weakSelf = self;
+  [self loadDefaultSearchEngineFaviconWithCompletion:^(UIImage* image) {
+    [weakSelf.consumer setEmptyTextLeadingImage:image];
+  }];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.h b/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.h
index 2f693108..5c15e45 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.h
@@ -31,13 +31,7 @@
 // Returns the asset name (to be used in -[UIImage imageNamed:]).
 NSString* GetOmniboxSuggestionIconTypeAssetName(OmniboxSuggestionIconType icon);
 
-// Returns the new asset name (to be used in -[UIImage imageNamed:]).
-NSString* GetOmniboxNewSuggestionIconTypeAssetName(
-    OmniboxSuggestionIconType icon);
-
-// Returns the asset with "always template" rendering mode. |useNewPopupLayout|
-// is temporary.
-UIImage* GetOmniboxSuggestionIcon(OmniboxSuggestionIconType icon,
-                                  bool useNewPopupLayout);
+// Returns the asset with "always template" rendering mode.
+UIImage* GetOmniboxSuggestionIcon(OmniboxSuggestionIconType icon);
 
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_SUGGESTION_ICON_UTIL_H_
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.mm b/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.mm
index 1d737bd..3aeee6a 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.mm
@@ -16,52 +16,6 @@
     case BOOKMARK:
       return @"omnibox_completion_bookmark";
     case CALCULATOR:
-      return @"omnibox_completion_calculator";
-    case DEFAULT_FAVICON:
-      return @"omnibox_completion_default_favicon";
-    case HISTORY:
-      return @"omnibox_completion_history";
-    case SEARCH:
-      return @"omnibox_completion_search";
-    // These icons should only be used with new omnibox design through
-    // GetOmniboxNewSuggestionIconTypeAssetName()
-    case CONVERSION:
-      NOTREACHED();
-      return @"omnibox_completion_default_favicon";
-    case DICTIONARY:
-      NOTREACHED();
-      return @"omnibox_completion_default_favicon";
-    case STOCK:
-      NOTREACHED();
-      return @"omnibox_completion_default_favicon";
-    case SUNRISE:
-      NOTREACHED();
-      return @"omnibox_completion_default_favicon";
-    case LOCAL_TIME:
-      NOTREACHED();
-      return @"omnibox_completion_default_favicon";
-    case WHEN_IS:
-      NOTREACHED();
-      return @"omnibox_completion_default_favicon";
-    case TRANSLATION:
-      NOTREACHED();
-      return @"omnibox_completion_default_favicon";
-    case FALLBACK_ANSWER:
-      NOTREACHED();
-      return @"omnibox_completion_default_favicon";
-    case SEARCH_HISTORY:
-    case OMNIBOX_SUGGESTION_ICON_TYPE_COUNT:
-      NOTREACHED();
-      return @"omnibox_completion_default_favicon";
-  }
-}
-
-NSString* GetOmniboxNewSuggestionIconTypeAssetName(
-    OmniboxSuggestionIconType iconType) {
-  switch (iconType) {
-    case BOOKMARK:
-      return @"omnibox_completion_bookmark";
-    case CALCULATOR:
       return @"answer_calculator";
     case DEFAULT_FAVICON:
       return @"favicon_fallback";
@@ -93,14 +47,8 @@
   }
 }
 
-UIImage* GetOmniboxSuggestionIcon(OmniboxSuggestionIconType iconType,
-                                  bool useNewPopupLayout) {
-  NSString* imageName = nil;
-  if (useNewPopupLayout) {
-    imageName = GetOmniboxNewSuggestionIconTypeAssetName(iconType);
-  } else {
-    imageName = GetOmniboxSuggestionIconTypeAssetName(iconType);
-  }
+UIImage* GetOmniboxSuggestionIcon(OmniboxSuggestionIconType iconType) {
+  NSString* imageName = GetOmniboxSuggestionIconTypeAssetName(iconType);
   return [[UIImage imageNamed:imageName]
       imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
 }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
index 1f98e083..5e7ffb9 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
@@ -100,9 +100,7 @@
 #pragma mark - Public methods
 // Overload to allow for code-based initialization.
 - (instancetype)initWithFrame:(CGRect)frame {
-  return [self initWithFrame:frame
-                   textColor:TextColor()
-                   tintColor:nil];
+  return [self initWithFrame:frame textColor:TextColor() tintColor:nil];
 }
 
 - (instancetype)initWithFrame:(CGRect)frame
@@ -211,8 +209,8 @@
   DCHECK([self isFirstResponder]);
   UITextPosition* beginning = [self beginningOfDocument];
   UITextRange* selectedRange = [self selectedTextRange];
-  NSInteger start =
-      [self offsetFromPosition:beginning toPosition:[selectedRange start]];
+  NSInteger start = [self offsetFromPosition:beginning
+                                  toPosition:[selectedRange start]];
   NSInteger length = [self offsetFromPosition:[selectedRange start]
                                    toPosition:[selectedRange end]];
   return NSMakeRange(start, length);
@@ -263,23 +261,20 @@
 // but there are numerous edge case issues with it, so it's simpler to just
 // manually update the text alignment and writing direction of the UITextField.
 - (void)updateTextDirection {
-  // If the flag is enabled, we want to use the default text alignment.
-  if (base::FeatureList::IsEnabled(kNewOmniboxPopupLayout)) {
-    // If the keyboard language direction does not match the device
-    // language direction, the alignment of the placeholder text will be off.
-    if (self.text.length == 0) {
-      NSLocaleLanguageDirection direction = [NSLocale
-          characterDirectionForLanguage:self.textInputMode.primaryLanguage];
-      if (direction == NSLocaleLanguageDirectionRightToLeft) {
-        [self setTextAlignment:NSTextAlignmentRight];
-      } else {
-        [self setTextAlignment:NSTextAlignmentLeft];
-      }
+  // If the keyboard language direction does not match the device
+  // language direction, the alignment of the placeholder text will be off.
+  if (self.text.length == 0) {
+    NSLocaleLanguageDirection direction = [NSLocale
+        characterDirectionForLanguage:self.textInputMode.primaryLanguage];
+    if (direction == NSLocaleLanguageDirectionRightToLeft) {
+      [self setTextAlignment:NSTextAlignmentRight];
     } else {
-      [self setTextAlignment:NSTextAlignmentNatural];
+      [self setTextAlignment:NSTextAlignmentLeft];
     }
-    return;
+  } else {
+    [self setTextAlignment:NSTextAlignmentNatural];
   }
+  return;
   // Setting the empty field to Natural seems to let iOS update the cursor
   // position when the keyboard language is changed.
   if (![self text].length) {
@@ -779,10 +774,10 @@
   }
 
   UITextPosition* beginning = self.beginningOfDocument;
-  UITextPosition* cursorPosition =
-      [self positionFromPosition:beginning offset:offset];
-  UITextRange* textRange =
-      [self textRangeFromPosition:cursorPosition toPosition:cursorPosition];
+  UITextPosition* cursorPosition = [self positionFromPosition:beginning
+                                                       offset:offset];
+  UITextRange* textRange = [self textRangeFromPosition:cursorPosition
+                                            toPosition:cursorPosition];
   self.selectedTextRange = textRange;
 }
 
@@ -928,7 +923,7 @@
 }
 
 - (UIColor*)selectedTextBackgroundColor {
-    return [_displayedTintColor colorWithAlphaComponent:0.2];
+  return [_displayedTintColor colorWithAlphaComponent:0.2];
 }
 
 - (BOOL)isColorHidden:(UIColor*)color {
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_util.mm b/ios/chrome/browser/ui/omnibox/omnibox_util.mm
index a49584e..4a4225f0 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_util.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_util.mm
@@ -37,11 +37,7 @@
     case AutocompleteMatchType::HISTORY_TITLE:
     case AutocompleteMatchType::HISTORY_URL:
     case AutocompleteMatchType::TAB_SEARCH_DEPRECATED:
-      return base::FeatureList::IsEnabled(kNewOmniboxPopupLayout) &&
-                     base::FeatureList::IsEnabled(
-                         kOmniboxUseDefaultSearchEngineFavicon)
-                 ? DEFAULT_FAVICON
-                 : HISTORY;
+      return DEFAULT_FAVICON;
     case AutocompleteMatchType::CONTACT_DEPRECATED:
     case AutocompleteMatchType::SEARCH_OTHER_ENGINE:
     case AutocompleteMatchType::SEARCH_SUGGEST:
@@ -55,9 +51,7 @@
     case AutocompleteMatchType::CLIPBOARD_IMAGE:
       return SEARCH;
     case AutocompleteMatchType::SEARCH_HISTORY:
-      return base::FeatureList::IsEnabled(kNewOmniboxPopupLayout)
-                 ? SEARCH_HISTORY
-                 : HISTORY;
+      return SEARCH_HISTORY;
     case AutocompleteMatchType::CALCULATOR:
       return CALCULATOR;
     case AutocompleteMatchType::EXTENSION_APP_DEPRECATED:
@@ -72,8 +66,7 @@
     bool is_starred) {
   OmniboxSuggestionIconType iconType =
       GetOmniboxSuggestionIconTypeForAutocompleteMatchType(type, is_starred);
-  return GetOmniboxSuggestionIcon(
-      iconType, base::FeatureList::IsEnabled(kNewOmniboxPopupLayout));
+  return GetOmniboxSuggestionIcon(iconType);
 }
 
 #pragma mark - Security icons.
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
index 4951101..1dea2095c 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
@@ -91,21 +91,14 @@
       [UIColor colorNamed:kBlueColor], self.incognito,
       [UIColor colorNamed:kBlueDarkColor]);
   UIColor* iconTintColor;
-  if (base::FeatureList::IsEnabled(kNewOmniboxPopupLayout)) {
-    iconTintColor = color::DarkModeDynamicColor(
-        [UIColor colorNamed:kToolbarButtonColor], self.incognito,
-        [UIColor colorNamed:kToolbarButtonDarkColor]);
-  } else {
-    iconTintColor = color::DarkModeDynamicColor(
-        [UIColor colorNamed:kToolbarButtonColor], self.incognito,
-        [UIColor colorNamed:kToolbarButtonDarkColor]);
-  }
+  iconTintColor = color::DarkModeDynamicColor(
+      [UIColor colorNamed:kToolbarButtonColor], self.incognito,
+      [UIColor colorNamed:kToolbarButtonDarkColor]);
 
-  self.view = [[OmniboxContainerView alloc]
-      initWithFrame:CGRectZero
-          textColor:textColor
-      textFieldTint:textFieldTintColor
-           iconTint:iconTintColor];
+  self.view = [[OmniboxContainerView alloc] initWithFrame:CGRectZero
+                                                textColor:textColor
+                                            textFieldTint:textFieldTintColor
+                                                 iconTint:iconTintColor];
   self.view.incognito = self.incognito;
 
   self.textField.delegate = self;
@@ -298,10 +291,7 @@
 #pragma mark - private
 
 - (void)updateLeadingImageVisibility {
-  BOOL newOmniboxPopupLayout =
-      base::FeatureList::IsEnabled(kNewOmniboxPopupLayout);
-  [self.view setLeadingImageHidden:!newOmniboxPopupLayout &&
-                                   !IsRegularXRegularSizeClass(self)];
+  [self.view setLeadingImageHidden:NO];
 }
 
 // Tint color for the textfield placeholder and the clear button.
@@ -390,10 +380,6 @@
     (UISemanticContentAttribute)semanticContentAttribute {
   _semanticContentAttribute = semanticContentAttribute;
 
-  if (!base::FeatureList::IsEnabled(kNewOmniboxPopupLayout)) {
-    return;
-  }
-
   self.view.semanticContentAttribute = self.semanticContentAttribute;
   self.textField.semanticContentAttribute = self.semanticContentAttribute;
 }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
index c4e595e..7e863d1 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
@@ -10,6 +10,7 @@
 #include <memory>
 #include "components/omnibox/browser/location_bar_model.h"
 #include "components/omnibox/browser/omnibox_view.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_left_image_consumer.h"
 #include "ios/chrome/browser/ui/omnibox/omnibox_text_change_delegate.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
@@ -24,10 +25,6 @@
 @class OmniboxTextFieldPasteDelegate;
 @protocol OmniboxFocuser;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 // iOS implementation of OmniBoxView.  Wraps a UITextField and
 // interfaces with the rest of the autocomplete system.
 class OmniboxViewIOS : public OmniboxView,
diff --git a/ios/chrome/browser/ui/omnibox/popup/autocomplete_match_formatter.mm b/ios/chrome/browser/ui/omnibox/popup/autocomplete_match_formatter.mm
index c3387387..53fba95 100644
--- a/ios/chrome/browser/ui/omnibox/popup/autocomplete_match_formatter.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/autocomplete_match_formatter.mm
@@ -44,11 +44,6 @@
   return UIColor.whiteColor;
 }
 
-// Temporary convenience accessor for this flag.
-// Cleanup along with feature: crbug.com/945334.
-bool ShouldUseNewFormatting() {
-  return base::FeatureList::IsEnabled(kNewOmniboxPopupLayout);
-}
 }  // namespace
 
 @implementation AutocompleteMatchFormatter {
@@ -87,8 +82,7 @@
 - (BOOL)hasImage {
   BOOL hasAnswerImage =
       self.hasAnswer && _match.answer->second_line().image_url().is_valid();
-  BOOL hasRichEntityImage =
-      ShouldUseNewFormatting() && !_match.image_url.empty();
+  BOOL hasRichEntityImage = !_match.image_url.empty();
   return hasAnswerImage || hasRichEntityImage;
 }
 
@@ -98,8 +92,7 @@
 
 - (NSAttributedString*)detailText {
   if (self.hasAnswer) {
-    if (ShouldUseNewFormatting() &&
-        !_match.answer->IsExceptedFromLineReversal()) {
+    if (!_match.answer->IsExceptedFromLineReversal()) {
       NSAttributedString* detailBaseText = [self
           attributedStringWithString:base::SysUTF16ToNSString(_match.contents)
                      classifications:&_match.contents_class
@@ -165,8 +158,7 @@
 
 - (NSAttributedString*)text {
   if (self.hasAnswer) {
-    if (ShouldUseNewFormatting() &&
-        !_match.answer->IsExceptedFromLineReversal()) {
+    if (!_match.answer->IsExceptedFromLineReversal()) {
       return [self attributedStringWithAnswerLine:_match.answer->second_line()
                            useDeemphasizedStyling:NO];
     } else {
@@ -315,72 +307,13 @@
       base::SysUTF16ToNSString(net::UnescapeForHTML(string));
 
   NSDictionary* attributes =
-      ShouldUseNewFormatting()
-          ? [self formattingAttributesForSuggestionStyle:field->style()
-                                  useDeemphasizedStyling:useDeemphasizedStyling]
-          : [self attributesForSuggestionType:field->type()];
+      [self formattingAttributesForSuggestionStyle:field->style()
+                            useDeemphasizedStyling:useDeemphasizedStyling];
 
   return [[NSAttributedString alloc] initWithString:unescapedString
                                          attributes:attributes];
 }
 
-- (NSDictionary<NSAttributedStringKey, id>*)attributesForSuggestionType:
-    (int)type {
-  DCHECK(!ShouldUseNewFormatting());
-  // Answer types, sizes and colors specified at http://goto.google.com/ais_api.
-  UIColor* detailTextColor = SuggestionDetailTextColor(self.incognito);
-  switch (type) {
-    case SuggestionAnswer::TOP_ALIGNED:
-      return @{
-        NSFontAttributeName : [UIFont systemFontOfSize:12],
-        NSBaselineOffsetAttributeName : @10.0f,
-        NSForegroundColorAttributeName : detailTextColor,
-      };
-    case SuggestionAnswer::DESCRIPTION_POSITIVE:
-      return @{
-        NSFontAttributeName : [UIFont systemFontOfSize:16],
-        NSForegroundColorAttributeName : [UIColor colorNamed:kGreenColor],
-      };
-    case SuggestionAnswer::DESCRIPTION_NEGATIVE:
-      return @{
-        NSFontAttributeName : [UIFont systemFontOfSize:16],
-        NSForegroundColorAttributeName : [UIColor colorNamed:kRedColor],
-      };
-    case SuggestionAnswer::PERSONALIZED_SUGGESTION:
-      return @{
-        NSFontAttributeName : [UIFont systemFontOfSize:16],
-      };
-    case SuggestionAnswer::ANSWER_TEXT_MEDIUM:
-      return @{
-        NSFontAttributeName : [UIFont systemFontOfSize:20],
-
-        NSForegroundColorAttributeName : detailTextColor,
-      };
-    case SuggestionAnswer::ANSWER_TEXT_LARGE:
-      return @{
-        NSFontAttributeName : [UIFont systemFontOfSize:24],
-        NSForegroundColorAttributeName : detailTextColor,
-      };
-    case SuggestionAnswer::SUGGESTION_SECONDARY_TEXT_SMALL:
-      return @{
-        NSFontAttributeName : [UIFont systemFontOfSize:12],
-        NSForegroundColorAttributeName : detailTextColor,
-      };
-    case SuggestionAnswer::SUGGESTION_SECONDARY_TEXT_MEDIUM:
-      return @{
-        NSFontAttributeName : [UIFont systemFontOfSize:14],
-        NSForegroundColorAttributeName : detailTextColor,
-      };
-    case SuggestionAnswer::SUGGESTION:
-      // Fall through.
-    default:
-      return @{
-        NSFontAttributeName : [UIFont systemFontOfSize:16],
-        NSForegroundColorAttributeName : SuggestionTextColor(self.incognito),
-      };
-  }
-}
-
 // Return correct formatting attributes for the given style.
 // |useDeemphasizedStyling| is necessary because some styles (e.g. SUPERIOR)
 // should take their color from the surrounding line; they don't have a fixed
@@ -388,7 +321,6 @@
 - (NSDictionary<NSAttributedStringKey, id>*)
     formattingAttributesForSuggestionStyle:(SuggestionAnswer::TextStyle)style
                     useDeemphasizedStyling:(BOOL)useDeemphasizedStyling {
-  DCHECK(ShouldUseNewFormatting());
   UIFontDescriptor* defaultFontDescriptor =
       useDeemphasizedStyling
           ? [[UIFontDescriptor
@@ -479,15 +411,9 @@
     return nil;
 
   UIFont* fontRef;
-  if (ShouldUseNewFormatting()) {
-    fontRef =
-        smallFont
-            ? [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]
-            : [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
-  } else {
-    fontRef =
-        smallFont ? [UIFont systemFontOfSize:15] : [UIFont systemFontOfSize:17];
-  }
+  fontRef = smallFont
+                ? [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]
+                : [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
 
   NSMutableAttributedString* styledText =
       [[NSMutableAttributedString alloc] initWithString:text];
@@ -501,16 +427,10 @@
 
   if (classifications != NULL) {
     UIFont* boldFontRef;
-    if (ShouldUseNewFormatting()) {
-      UIFontDescriptor* fontDescriptor = fontRef.fontDescriptor;
-      UIFontDescriptor* boldFontDescriptor = [fontDescriptor
-          fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
-      boldFontRef = [UIFont fontWithDescriptor:boldFontDescriptor size:0];
-    } else {
-      UIFontWeight boldFontWeight = UIFontWeightMedium;
-      boldFontRef = [UIFont systemFontOfSize:fontRef.pointSize
-                                      weight:boldFontWeight];
-    }
+    UIFontDescriptor* fontDescriptor = fontRef.fontDescriptor;
+    UIFontDescriptor* boldFontDescriptor = [fontDescriptor
+        fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
+    boldFontRef = [UIFont fontWithDescriptor:boldFontDescriptor size:0];
 
     for (ACMatchClassifications::const_iterator i = classifications->begin();
          i != classifications->end(); ++i) {
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.h b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.h
index ee0559f..0bfc5c2 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.h
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.h
@@ -9,14 +9,12 @@
 
 #include <memory>
 
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
+
 @class CommandDispatcher;
 @protocol OmniboxPopupPresenterDelegate;
 @protocol OmniboxFocuser;
 class OmniboxPopupViewIOS;
-
-namespace ios {
-class ChromeBrowserState;
-}
 class WebStateList;
 
 // Coordinator for the Omnibox Popup.
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
index 183d18a..a64bd1f 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
@@ -75,11 +75,7 @@
       templateURLService->GetDefaultSearchProvider()->GetEngineType(
           templateURLService->search_terms_data()) == SEARCH_ENGINE_GOOGLE;
 
-  if (base::FeatureList::IsEnabled(kNewOmniboxPopupLayout)) {
-    self.popupViewController = [[OmniboxPopupViewController alloc] init];
-  } else {
-    self.popupViewController = [[OmniboxPopupLegacyViewController alloc] init];
-  }
+  self.popupViewController = [[OmniboxPopupViewController alloc] init];
   self.popupViewController.incognito = self.browserState->IsOffTheRecord();
 
   BOOL isIncognito = self.browserState->IsOffTheRecord();
diff --git a/ios/chrome/browser/ui/omnibox/popup/simple_omnibox_icon.mm b/ios/chrome/browser/ui/omnibox/popup/simple_omnibox_icon.mm
index b696e9a0..5c397f9 100644
--- a/ios/chrome/browser/ui/omnibox/popup/simple_omnibox_icon.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/simple_omnibox_icon.mm
@@ -55,7 +55,7 @@
     return [[self fallbackAnswerBrandedIcon]
         imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
   }
-  return GetOmniboxSuggestionIcon(self.suggestionIconType, true);
+  return GetOmniboxSuggestionIcon(self.suggestionIconType);
 }
 
 - (BOOL)hasCustomAnswerIcon {
diff --git a/ios/chrome/browser/ui/overscroll_actions/BUILD.gn b/ios/chrome/browser/ui/overscroll_actions/BUILD.gn
index c11d32be..4464e34 100644
--- a/ios/chrome/browser/ui/overscroll_actions/BUILD.gn
+++ b/ios/chrome/browser/ui/overscroll_actions/BUILD.gn
@@ -23,6 +23,7 @@
     "//ios/chrome/app/strings:ios_chromium_strings_grit",
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/app/theme",
+    "//ios/chrome/browser/browser_state:forward",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
     "//ios/chrome/browser/ui/fullscreen",
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h
index aa82191..a84300e9 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h
@@ -7,14 +7,12 @@
 
 #import <UIKit/UIKit.h>
 
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.h"
 #import "ios/chrome/browser/ui/util/relaxed_bounds_constraints_hittest.h"
 #import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
 
 @protocol CRWWebViewProxy;
-namespace ios {
-class ChromeBrowserState;
-}
 @class OverscrollActionsController;
 
 // Describe the current state of the overscroll action controller.
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm
index e08e9a99..82298e8 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm
@@ -164,7 +164,7 @@
 }
 
 // Test that Clear Browsing Data can be successfully done from tab grid.
-- (void)testClearBrowsingData {
+- (void)DISABLED_testClearBrowsingData {
   // Load history
   [self loadTestURLs];
 
diff --git a/ios/chrome/browser/ui/tabs/tab_strip_legacy_coordinator.h b/ios/chrome/browser/ui/tabs/tab_strip_legacy_coordinator.h
index 02c7797..af5d6c5e 100644
--- a/ios/chrome/browser/ui/tabs/tab_strip_legacy_coordinator.h
+++ b/ios/chrome/browser/ui/tabs/tab_strip_legacy_coordinator.h
@@ -7,6 +7,7 @@
 
 #import <UIKit/UIKit.h>
 
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
 #import "ios/chrome/browser/ui/tabs/requirements/tab_strip_highlighting.h"
 
@@ -16,10 +17,6 @@
 @class TabModel;
 @protocol TabStripPresentation;
 
-namespace ios {
-class ChromeBrowserState;
-}  // namespace
-
 // A legacy coordinator that presents the public interface for the tablet tab
 // strip feature.
 @interface TabStripLegacyCoordinator : ChromeCoordinator<TabStripHighlighting>
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index 9395084c..790ced9 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -16,12 +16,6 @@
 const base::Feature kSettingsRefresh{"SettingsRefresh",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kNewOmniboxPopupLayout{"NewOmniboxPopupLayout",
-                                           base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kOmniboxUseDefaultSearchEngineFavicon{
-    "OmniboxUseDefaultSearchEngineFavicon", base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kEmbedderBlockRestoreUrl{"EmbedderBlockRestoreUrl",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -37,5 +31,7 @@
 const base::Feature kContainedBVC{"ContainedBVC",
                                   base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kTestFeature{"ContainedBVC",
+                                 base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kClearSyncedData{"ClearSyncedData",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h
index 77de00c..1b1a083 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -16,14 +16,6 @@
 // Feature to apply UI Refresh theme to the settings.
 extern const base::Feature kSettingsRefresh;
 
-// Feature to display the new omnibox popup design with favicons, search engine
-// favicon in the omnibox, rich entities support, new layout.
-extern const base::Feature kNewOmniboxPopupLayout;
-
-// Feature to display the omnibox with default search engine favicon
-// in the omnibox.
-extern const base::Feature kOmniboxUseDefaultSearchEngineFavicon;
-
 // Feature flag for embedders to block restore urls.
 extern const base::Feature kEmbedderBlockRestoreUrl;
 
@@ -40,6 +32,11 @@
 // presented.
 extern const base::Feature kContainedBVC;
 
+// Test-only: Feature flag used to verify that EG2 can trigger flags. Must be
+// always disabled by default, because it is used to verify that enabling
+// features in tests works.
+extern const base::Feature kTestFeature;
+
 // Feature flag to display a new option that wipes synced data on a local
 // device when signing out from a non-managed account.
 extern const base::Feature kClearSyncedData;
diff --git a/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.h b/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.h
index d300c5b..7d3b665 100644
--- a/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.h
+++ b/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.h
@@ -8,10 +8,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 
 class TextToSpeechPlaybackController;
 
diff --git a/ios/chrome/browser/web/blocked_popup_tab_helper.h b/ios/chrome/browser/web/blocked_popup_tab_helper.h
index 715daa3..457ed5c4 100644
--- a/ios/chrome/browser/web/blocked_popup_tab_helper.h
+++ b/ios/chrome/browser/web/blocked_popup_tab_helper.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 #include "components/infobars/core/infobar_manager.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #include "ios/web/public/navigation/referrer.h"
 #import "ios/web/public/web_state_user_data.h"
 #include "url/gurl.h"
@@ -18,10 +19,6 @@
 class InfoBar;
 }  // namespace infobars
 
-namespace ios {
-class ChromeBrowserState;
-}  // namespace ios
-
 namespace web {
 class WebState;
 }  // namespace web
diff --git a/ios/chrome/test/app/chrome_test_util.h b/ios/chrome/test/app/chrome_test_util.h
index 218cb6a1..e7eaa80 100644
--- a/ios/chrome/test/app/chrome_test_util.h
+++ b/ios/chrome/test/app/chrome_test_util.h
@@ -8,12 +8,9 @@
 
 #include "base/compiler_specific.h"
 #import "base/ios/block_types.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_forward.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 
-namespace ios {
-class ChromeBrowserState;
-}
-
 @protocol ApplicationCommands;
 @class DeviceSharingManager;
 @class MainController;
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 22b98d6..680ac928 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -441,9 +441,6 @@
 // Returns YES if BlockNewTabPagePendingLoad feature is enabled.
 - (BOOL)isBlockNewTabPagePendingLoadEnabled WARN_UNUSED_RESULT;
 
-// Returns YES if NewOmniboxPopupLayout feature is enabled.
-- (BOOL)isNewOmniboxPopupLayoutEnabled WARN_UNUSED_RESULT;
-
 // Returns YES if |variationID| is enabled.
 - (BOOL)isVariationEnabled:(int)variationID;
 
@@ -456,6 +453,9 @@
 // Returns YES if UKM feature is enabled.
 - (BOOL)isUKMEnabled WARN_UNUSED_RESULT;
 
+// Returns YES if kTestFeature is enabled.
+- (BOOL)isTestFeatureEnabled;
+
 // Returns YES if CreditCardScanner feature is enabled.
 - (BOOL)isCreditCardScannerEnabled WARN_UNUSED_RESULT;
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 667adf1..77e16a0 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -776,10 +776,6 @@
   return [ChromeEarlGreyAppInterface isBlockNewTabPagePendingLoadEnabled];
 }
 
-- (BOOL)isNewOmniboxPopupLayoutEnabled {
-  return [ChromeEarlGreyAppInterface isNewOmniboxPopupLayoutEnabled];
-}
-
 - (BOOL)isVariationEnabled:(int)variationID {
   return [ChromeEarlGreyAppInterface isVariationEnabled:variationID];
 }
@@ -796,6 +792,10 @@
   return [ChromeEarlGreyAppInterface isUKMEnabled];
 }
 
+- (BOOL)isTestFeatureEnabled {
+  return [ChromeEarlGreyAppInterface isTestFeatureEnabled];
+}
+
 - (BOOL)isCreditCardScannerEnabled {
   return [ChromeEarlGreyAppInterface isCreditCardScannerEnabled];
 }
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
index 46f0e6c..9fdf0ce 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -345,9 +345,6 @@
 // Returns YES if BlockNewTabPagePendingLoad feature is enabled.
 + (BOOL)isBlockNewTabPagePendingLoadEnabled WARN_UNUSED_RESULT;
 
-// Returns YES if NewOmniboxPopupLayout feature is enabled.
-+ (BOOL)isNewOmniboxPopupLayoutEnabled WARN_UNUSED_RESULT;
-
 // Returns YES if |variationID| is enabled.
 + (BOOL)isVariationEnabled:(int)variationID;
 
@@ -360,6 +357,9 @@
 // Returns YES if UKM feature is enabled.
 + (BOOL)isUKMEnabled WARN_UNUSED_RESULT;
 
+// Returns YES if kTestFeature is enabled.
++ (BOOL)isTestFeatureEnabled;
+
 // Returns YES if CreditCardScanner feature is enabled.
 + (BOOL)isCreditCardScannerEnabled WARN_UNUSED_RESULT;
 
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 d40de92..daf7676 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
@@ -610,10 +610,6 @@
   return base::FeatureList::IsEnabled(kBlockNewTabPagePendingLoad);
 }
 
-+ (BOOL)isNewOmniboxPopupLayoutEnabled {
-  return base::FeatureList::IsEnabled(kNewOmniboxPopupLayout);
-}
-
 + (BOOL)isVariationEnabled:(int)variationID {
   variations::VariationsHttpHeaderProvider* provider =
       variations::VariationsHttpHeaderProvider::GetInstance();
@@ -638,6 +634,10 @@
   return base::FeatureList::IsEnabled(ukm::kUkmFeature);
 }
 
++ (BOOL)isTestFeatureEnabled {
+  return base::FeatureList::IsEnabled(kTestFeature);
+}
+
 + (BOOL)isCreditCardScannerEnabled {
   return base::FeatureList::IsEnabled(kCreditCardScanner);
 }
diff --git a/ios/chrome/test/earl_grey2/smoke_egtest.mm b/ios/chrome/test/earl_grey2/smoke_egtest.mm
index 4dcec436..f2eccb3 100644
--- a/ios/chrome/test/earl_grey2/smoke_egtest.mm
+++ b/ios/chrome/test/earl_grey2/smoke_egtest.mm
@@ -196,12 +196,12 @@
 // ensureAppLaunchedWithFeaturesEnabled]
 - (void)testAppLaunchManagerLaunchWithFeatures {
   [[AppLaunchManager sharedManager]
-      ensureAppLaunchedWithFeaturesEnabled:{kNewOmniboxPopupLayout}
+      ensureAppLaunchedWithFeaturesEnabled:{kTestFeature}
                                   disabled:{}
                             relaunchPolicy:NoForceRelaunchAndResetState];
 
-  GREYAssertTrue([ChromeEarlGrey isNewOmniboxPopupLayoutEnabled],
-                 @"NewOmniboxPopupLayout should be enabled");
+  GREYAssertTrue([ChromeEarlGrey isTestFeatureEnabled],
+                 @"kTestFeature should be enabled");
 
   GREYAssertEqual([ChromeEarlGrey mainTabCount], 1U,
                   @"Exactly one new tab should be opened.");
@@ -256,7 +256,7 @@
   [self disableMockAuthentication];
   [ChromeEarlGrey openNewTab];
   [[AppLaunchManager sharedManager]
-      ensureAppLaunchedWithFeaturesEnabled:{kNewOmniboxPopupLayout}
+      ensureAppLaunchedWithFeaturesEnabled:{kTestFeature}
                                   disabled:{}
                             relaunchPolicy:NoForceRelaunchAndResetState];
   [ChromeEarlGrey waitForMainTabCount:1];
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 27a34e8..ff232a0 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -398,6 +398,7 @@
     "web_state/context_menu_params_utils_unittest.mm",
     "web_state/page_display_state_unittest.mm",
     "web_state/page_viewport_state_unittest.mm",
+    "web_state/web_state_context_menu_bridge_unittest.mm",
     "web_state/web_state_delegate_bridge_unittest.mm",
     "web_state/web_state_impl_unittest.mm",
     "web_state/web_state_observer_bridge_unittest.mm",
diff --git a/ios/web/public/web_client.h b/ios/web/public/web_client.h
index 4751e1d..846bfc4 100644
--- a/ios/web/public/web_client.h
+++ b/ios/web/public/web_client.h
@@ -183,6 +183,11 @@
   // Instructs the embedder to return a container that is attached to a window.
   virtual UIView* GetWindowedContainer();
 
+  // Enables the logic to handle long press and force
+  // touch. Should return false to use the context menu API.
+  // Defaults to return true.
+  virtual bool EnableLongPressAndForceTouchHandling() const;
+
   // This method is used when the user didn't express any preference for the
   // version of |url|. Returning true allows to make sure that for |url|, the
   // mobile version will be used, unless the user explicitly requested the
diff --git a/ios/web/public/web_state_delegate.h b/ios/web/public/web_state_delegate.h
index 5743b69..d0efa84d 100644
--- a/ios/web/public/web_state_delegate.h
+++ b/ios/web/public/web_state_delegate.h
@@ -8,6 +8,7 @@
 #include <set>
 
 #import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
 
 #include "base/callback.h"
 #import "ios/web/public/web_state.h"
@@ -89,6 +90,27 @@
       WebState* source,
       UIViewController* previewing_view_controller);
 
+  // Called when iOS13+ context menu is triggered and now it is required to
+  // provide a UIContextMenuConfiguration to |completion_handler| to generate
+  // the context menu.
+  virtual void ContextMenuConfiguration(
+      WebState* source,
+      const GURL& link_url,
+      void (^completion_handler)(UIContextMenuConfiguration*))
+      API_AVAILABLE(ios(13.0));
+  // Called when iOS13+ context menu is ready to be showed.
+  virtual void ContextMenuDidEnd(WebState* source, const GURL& link_url)
+      API_AVAILABLE(ios(13.0));
+  // Called when iOS13+ context menu will commit with animator.
+  virtual void ContextMenuWillCommitWithAnimator(
+      WebState* source,
+      const GURL& link_url,
+      id<UIContextMenuInteractionCommitAnimating> animator)
+      API_AVAILABLE(ios(13.0));
+  // Called when iOS13+ context menu will present.
+  virtual void ContextMenuWillPresent(WebState* source, const GURL& link_url)
+      API_AVAILABLE(ios(13.0));
+
  protected:
   virtual ~WebStateDelegate();
 
diff --git a/ios/web/public/web_state_delegate_bridge.h b/ios/web/public/web_state_delegate_bridge.h
index 57b079e3..02679ed1 100644
--- a/ios/web/public/web_state_delegate_bridge.h
+++ b/ios/web/public/web_state_delegate_bridge.h
@@ -6,6 +6,7 @@
 #define IOS_WEB_PUBLIC_WEB_STATE_DELEGATE_BRIDGE_H_
 
 #import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
 
 #import "ios/web/public/web_state_delegate.h"
 
@@ -78,6 +79,32 @@
 - (void)webState:(web::WebState*)webState
     commitPreviewingViewController:(UIViewController*)previewingViewController;
 
+// Called when iOS13+ context menu is triggered and now it is required to
+// provide a UIContextMenuConfiguration to |completion_handler| to generate the
+// context menu.
+- (void)webState:(web::WebState*)webState
+    contextMenuConfigurationForLinkWithURL:(const GURL&)linkURL
+                         completionHandler:
+                             (void (^)(UIContextMenuConfiguration*))
+                                 completionHandler API_AVAILABLE(ios(13.0));
+
+// Called when iOS13+ context menu is ready to be showed.
+- (void)webState:(web::WebState*)webState
+    contextMenuWillPresentForLinkWithURL:(const GURL&)linkURL
+    API_AVAILABLE(ios(13.0));
+
+// Called when iOS13+ context menu will commit with animator.
+- (void)webState:(web::WebState*)webState
+    contextMenuForLinkWithURL:(const GURL&)linkURL
+       willCommitWithAnimator:
+           (id<UIContextMenuInteractionCommitAnimating>)animator
+    API_AVAILABLE(ios(13.0));
+
+// Called when iOS13+ context menu will present.
+- (void)webState:(web::WebState*)webState
+    contextMenuDidEndForLinkWithURL:(const GURL&)linkURL
+    API_AVAILABLE(ios(13.0));
+
 @end
 
 namespace web {
@@ -113,6 +140,20 @@
   void CommitPreviewingViewController(
       WebState* source,
       UIViewController* previewing_view_controller) override;
+  void ContextMenuConfiguration(
+      WebState* source,
+      const GURL& link_url,
+      void (^completion_handler)(UIContextMenuConfiguration*))
+      API_AVAILABLE(ios(13.0)) override;
+  void ContextMenuDidEnd(WebState* source, const GURL& link_url)
+      API_AVAILABLE(ios(13.0)) override;
+  void ContextMenuWillCommitWithAnimator(
+      WebState* source,
+      const GURL& link_url,
+      id<UIContextMenuInteractionCommitAnimating> animator)
+      API_AVAILABLE(ios(13.0)) override;
+  void ContextMenuWillPresent(WebState* source, const GURL& link_url)
+      API_AVAILABLE(ios(13.0)) override;
 
  private:
   // CRWWebStateDelegate which receives forwarded calls.
diff --git a/ios/web/public/web_view_only/wk_web_view_configuration_util.h b/ios/web/public/web_view_only/wk_web_view_configuration_util.h
index 9cee0295..7855e37 100644
--- a/ios/web/public/web_view_only/wk_web_view_configuration_util.h
+++ b/ios/web/public/web_view_only/wk_web_view_configuration_util.h
@@ -14,6 +14,8 @@
 
 // Creates the web view of |web_state| with given |configuration|.
 // Returns the created web view.
+// If |configuration| is nil, a new WKWebViewConfiguration object will be
+// created and used to create the web view.
 // This must be called immediately after |web_state| is created
 // e.g., with web::WebState::Create().
 //
diff --git a/ios/web/web_client.mm b/ios/web/web_client.mm
index c869459..ebfd2e7a 100644
--- a/ios/web/web_client.mm
+++ b/ios/web/web_client.mm
@@ -112,6 +112,10 @@
   return nullptr;
 }
 
+bool WebClient::EnableLongPressAndForceTouchHandling() const {
+  return true;
+}
+
 bool WebClient::ForceMobileVersionByDefault(const GURL&) {
   return false;
 }
diff --git a/ios/web/web_state/ui/crw_context_menu_controller.mm b/ios/web/web_state/ui/crw_context_menu_controller.mm
index 2b5645c..3b2322e9 100644
--- a/ios/web/web_state/ui/crw_context_menu_controller.mm
+++ b/ios/web/web_state/ui/crw_context_menu_controller.mm
@@ -17,6 +17,7 @@
 #import "ios/web/public/deprecated/crw_context_menu_delegate.h"
 #import "ios/web/public/navigation/navigation_context.h"
 #import "ios/web/public/ui/context_menu_params.h"
+#include "ios/web/public/web_client.h"
 #import "ios/web/public/web_state.h"
 #import "ios/web/public/web_state_observer_bridge.h"
 #import "ios/web/web_state/context_menu_constants.h"
@@ -216,6 +217,9 @@
   // Precalculation is necessary because retreiving DOM element relies on async
   // API so element info can not be built on demand.
   ContextMenuInfo _contextMenuInfoForLastTouch;
+  // Whether or not the system cotext menu should be displayed. If not, custom
+  // context menu should be displayed.
+  BOOL _systemContextMenuEnabled;
   // Whether or not the cotext menu should be displayed as soon as the DOM
   // element details are returned. Since fetching the details from the |webView|
   // of the element the user long pressed is asyncrounous, it may not be
@@ -243,6 +247,11 @@
     _delegate = delegate;
     _pendingElementFetchRequests = [[NSMutableDictionary alloc] init];
 
+    // If system context menu is enabled, the recognizer below will not be
+    // triggered.
+    _systemContextMenuEnabled =
+        !web::GetWebClient()->EnableLongPressAndForceTouchHandling();
+
     // The system context menu triggers after 0.55 second. Add a gesture
     // recognizer with a shorter delay to be able to cancel the system menu if
     // needed.
@@ -479,6 +488,12 @@
   // Expect only _contextMenuRecognizer.
   DCHECK([gestureRecognizer isEqual:_contextMenuRecognizer]);
 
+  // If the system context menu is enabled, it disables this long press
+  // gesture recognizer to prevent custom context menu from being displayed.
+  if (_systemContextMenuEnabled) {
+    return NO;
+  }
+
   // This is custom long press gesture recognizer. By the time the gesture is
   // recognized the web controller needs to know if there is a link under the
   // touch. If there a link, the web controller will reject system's context
@@ -499,6 +514,12 @@
   // Expect only _contextMenuRecognizer.
   DCHECK([gestureRecognizer isEqual:_contextMenuRecognizer]);
 
+  // If the system context menu is enabled, it disables this long press
+  // gesture recognizer to prevent custom context menu from being displayed.
+  if (_systemContextMenuEnabled) {
+    return NO;
+  }
+
   // Context menu should not be triggered while scrolling, as some users tend to
   // stop scrolling by resting the finger on the screen instead of touching the
   // screen. For more info, please refer to crbug.com/642375.
diff --git a/ios/web/web_state/ui/crw_wk_ui_handler.mm b/ios/web/web_state/ui/crw_wk_ui_handler.mm
index e13f73d4..45abc63 100644
--- a/ios/web/web_state/ui/crw_wk_ui_handler.mm
+++ b/ios/web/web_state/ui/crw_wk_ui_handler.mm
@@ -191,6 +191,59 @@
       previewingViewController);
 }
 
+- (void)webView:(WKWebView*)webView
+    contextMenuConfigurationForElement:(WKContextMenuElementInfo*)elementInfo
+                     completionHandler:
+                         (void (^)(UIContextMenuConfiguration* _Nullable))
+                             completionHandler API_AVAILABLE(ios(13.0)) {
+  web::WebStateDelegate* delegate = self.webStateImpl->GetDelegate();
+  if (!delegate) {
+    return;
+  }
+
+  delegate->ContextMenuConfiguration(self.webStateImpl,
+                                     net::GURLWithNSURL(elementInfo.linkURL),
+                                     completionHandler);
+}
+
+- (void)webView:(WKWebView*)webView
+    contextMenuDidEndForElement:(WKContextMenuElementInfo*)elementInfo
+    API_AVAILABLE(ios(13.0)) {
+  web::WebStateDelegate* delegate = self.webStateImpl->GetDelegate();
+  if (!delegate) {
+    return;
+  }
+
+  delegate->ContextMenuDidEnd(self.webStateImpl,
+                              net::GURLWithNSURL(elementInfo.linkURL));
+}
+
+- (void)webView:(WKWebView*)webView
+     contextMenuForElement:(nonnull WKContextMenuElementInfo*)elementInfo
+    willCommitWithAnimator:
+        (nonnull id<UIContextMenuInteractionCommitAnimating>)animator
+    API_AVAILABLE(ios(13.0)) {
+  web::WebStateDelegate* delegate = self.webStateImpl->GetDelegate();
+  if (!delegate) {
+    return;
+  }
+
+  delegate->ContextMenuWillCommitWithAnimator(
+      self.webStateImpl, net::GURLWithNSURL(elementInfo.linkURL), animator);
+}
+
+- (void)webView:(WKWebView*)webView
+    contextMenuWillPresentForElement:(WKContextMenuElementInfo*)elementInfo
+    API_AVAILABLE(ios(13.0)) {
+  web::WebStateDelegate* delegate = self.webStateImpl->GetDelegate();
+  if (!delegate) {
+    return;
+  }
+
+  delegate->ContextMenuWillPresent(self.webStateImpl,
+                                   net::GURLWithNSURL(elementInfo.linkURL));
+}
+
 #pragma mark - Helper
 
 // Helper to respond to |webView:runJavaScript...| delegate methods.
diff --git a/ios/web/web_state/ui/wk_web_view_configuration_provider.mm b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
index 0fa3b0b..49074e3 100644
--- a/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
+++ b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
@@ -101,6 +101,9 @@
   } else {
     configuration = [configuration copy];
   }
+  if (configuration_) {
+    Purge();
+  }
   configuration_ = configuration;
 
   if (browser_state_->IsOffTheRecord()) {
@@ -117,9 +120,13 @@
     @try {
       // Disable system context menu on iOS 13 and later. Disabling
       // "longPressActions" prevents the WKWebView ContextMenu from being
-      // displayed.
+      // displayed and also prevents the iOS 13 ContextMenu delegate methods
+      // from being called.
       // https://github.com/WebKit/webkit/blob/1233effdb7826a5f03b3cdc0f67d713741e70976/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfiguration.mm#L307
-      [configuration_ setValue:@NO forKey:@"longPressActionsEnabled"];
+      BOOL enable_long_press_action =
+          !web::GetWebClient()->EnableLongPressAndForceTouchHandling();
+      [configuration_ setValue:@(enable_long_press_action)
+                        forKey:@"longPressActionsEnabled"];
     } @catch (NSException* exception) {
       NOTREACHED() << "Error setting value for longPressActionsEnabled";
     }
diff --git a/ios/web/web_state/web_state_context_menu_bridge_unittest.mm b/ios/web/web_state/web_state_context_menu_bridge_unittest.mm
new file mode 100644
index 0000000..6e6dcfc
--- /dev/null
+++ b/ios/web/web_state/web_state_context_menu_bridge_unittest.mm
@@ -0,0 +1,147 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web/public/web_state_delegate_bridge.h"
+
+#include "ios/web/public/test/fakes/test_browser_state.h"
+#include "ios/web/public/test/scoped_testing_web_client.h"
+#import "ios/web/test/web_test_with_web_controller.h"
+#import "net/base/mac/url_conversions.h"
+#import "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// A fake of CRWWebStateDelegate to test if the methods in the delegate
+// bridge are called normally. Uses BOOLs below to record the calls.
+@interface FakeCRWWebStateDelegate : NSObject <CRWWebStateDelegate>
+@property(nonatomic) web::WebState* webState;
+@property(nonatomic) NSURL* URL;
+@property(nonatomic) BOOL contextMenuConfigurationNeeded;
+@property(nonatomic) BOOL contextMenuWillPresent;
+@property(nonatomic) BOOL contextMenuWillCommitWithAnimator;
+@property(nonatomic) BOOL contextMenuDidEnd;
+@end
+
+@implementation FakeCRWWebStateDelegate
+
+- (void)webState:(web::WebState*)webState
+    contextMenuConfigurationForLinkWithURL:(const GURL&)linkURL
+                         completionHandler:
+                             (void (^)(UIContextMenuConfiguration*))
+                                 completionHandler API_AVAILABLE(ios(13.0)) {
+  self.webState = webState;
+  self.URL = net::NSURLWithGURL(linkURL);
+  self.contextMenuConfigurationNeeded = YES;
+}
+
+- (void)webState:(web::WebState*)webState
+    contextMenuWillPresentForLinkWithURL:(const GURL&)linkURL
+    API_AVAILABLE(ios(13.0)) {
+  self.webState = webState;
+  self.URL = net::NSURLWithGURL(linkURL);
+  self.contextMenuWillPresent = YES;
+}
+
+- (void)webState:(web::WebState*)webState
+    contextMenuForLinkWithURL:(const GURL&)linkURL
+       willCommitWithAnimator:
+           (id<UIContextMenuInteractionCommitAnimating>)animator
+    API_AVAILABLE(ios(13.0)) {
+  self.webState = webState;
+  self.URL = net::NSURLWithGURL(linkURL);
+  self.contextMenuWillCommitWithAnimator = YES;
+}
+
+- (void)webState:(web::WebState*)webState
+    contextMenuDidEndForLinkWithURL:(const GURL&)linkURL
+    API_AVAILABLE(ios(13.0)) {
+  self.webState = webState;
+  self.URL = net::NSURLWithGURL(linkURL);
+  self.contextMenuDidEnd = YES;
+}
+
+@end
+
+namespace web {
+
+// Tests if the iOS 13 context menu delegate methods are correctly called
+// via the web state delegate bridge.
+class WebStateContextMenuBridgeTest : public web::WebTestWithWebController {
+ public:
+  WebStateContextMenuBridgeTest() : web::WebTestWithWebController() {}
+
+  FakeCRWWebStateDelegate* MakeFakeCRWWebStateDelegate() {
+    FakeCRWWebStateDelegate* web_state_delegate =
+        [[FakeCRWWebStateDelegate alloc] init];
+    web_state_delegate_bridge_ =
+        std::make_unique<web::WebStateDelegateBridge>(web_state_delegate);
+    web_state()->SetDelegate(web_state_delegate_bridge_.get());
+    return web_state_delegate;
+  }
+
+ private:
+  std::unique_ptr<web::WebStateDelegateBridge> web_state_delegate_bridge_ =
+      nullptr;
+};
+
+TEST_F(WebStateContextMenuBridgeTest, IOS13ContextMenuDelegateBridgeTest) {
+  if (@available(iOS 13, *)) {
+    WKWebView* web_view = [web_controller() ensureWebViewCreated];
+    id<WKUIDelegate> ui_delegate = web_view.UIDelegate;
+
+    NSURL* url = [NSURL URLWithString:@"https://google.com/"];
+    id element_info = OCMClassMock([WKContextMenuElementInfo class]);
+    [[[element_info stub] andReturn:url] linkURL];
+
+    FakeCRWWebStateDelegate* web_state_delegate = MakeFakeCRWWebStateDelegate();
+    [ui_delegate webView:web_view
+        contextMenuConfigurationForElement:element_info
+                         completionHandler:^(id){
+                         }];
+    EXPECT_EQ(web_state(), web_state_delegate.webState);
+    EXPECT_NSEQ(url, web_state_delegate.URL);
+    EXPECT_TRUE(web_state_delegate.contextMenuConfigurationNeeded);
+    EXPECT_FALSE(web_state_delegate.contextMenuDidEnd);
+    EXPECT_FALSE(web_state_delegate.contextMenuWillCommitWithAnimator);
+    EXPECT_FALSE(web_state_delegate.contextMenuWillPresent);
+
+    web_state_delegate = MakeFakeCRWWebStateDelegate();
+    [ui_delegate webView:web_view contextMenuDidEndForElement:element_info];
+    EXPECT_EQ(web_state(), web_state_delegate.webState);
+    EXPECT_NSEQ(url, web_state_delegate.URL);
+    EXPECT_FALSE(web_state_delegate.contextMenuConfigurationNeeded);
+    EXPECT_TRUE(web_state_delegate.contextMenuDidEnd);
+    EXPECT_FALSE(web_state_delegate.contextMenuWillCommitWithAnimator);
+    EXPECT_FALSE(web_state_delegate.contextMenuWillPresent);
+
+    web_state_delegate = MakeFakeCRWWebStateDelegate();
+    [ui_delegate webView:web_view
+         contextMenuForElement:element_info
+        willCommitWithAnimator:
+            [OCMockObject
+                mockForProtocol:@protocol(UIContextMenuInteractionDelegate)]];
+    EXPECT_EQ(web_state(), web_state_delegate.webState);
+    EXPECT_NSEQ(url, web_state_delegate.URL);
+    EXPECT_FALSE(web_state_delegate.contextMenuConfigurationNeeded);
+    EXPECT_FALSE(web_state_delegate.contextMenuDidEnd);
+    EXPECT_TRUE(web_state_delegate.contextMenuWillCommitWithAnimator);
+    EXPECT_FALSE(web_state_delegate.contextMenuWillPresent);
+
+    web_state_delegate = MakeFakeCRWWebStateDelegate();
+    [ui_delegate webView:web_view
+        contextMenuWillPresentForElement:element_info];
+    EXPECT_EQ(web_state(), web_state_delegate.webState);
+    EXPECT_NSEQ(url, web_state_delegate.URL);
+    EXPECT_FALSE(web_state_delegate.contextMenuConfigurationNeeded);
+    EXPECT_FALSE(web_state_delegate.contextMenuDidEnd);
+    EXPECT_FALSE(web_state_delegate.contextMenuWillCommitWithAnimator);
+    EXPECT_TRUE(web_state_delegate.contextMenuWillPresent);
+  }
+}
+
+}  // namespace web
diff --git a/ios/web/web_state/web_state_delegate.mm b/ios/web/web_state/web_state_delegate.mm
index 04cdb5c..5af374d7 100644
--- a/ios/web/web_state/web_state_delegate.mm
+++ b/ios/web/web_state/web_state_delegate.mm
@@ -80,4 +80,23 @@
   attached_states_.erase(source);
 }
 
+void WebStateDelegate::ContextMenuConfiguration(
+    WebState* source,
+    const GURL& link_url,
+    void (^completion_handler)(UIContextMenuConfiguration*))
+    API_AVAILABLE(ios(13.0)) {}
+
+void WebStateDelegate::ContextMenuDidEnd(WebState* source, const GURL& link_url)
+    API_AVAILABLE(ios(13.0)) {}
+
+void WebStateDelegate::ContextMenuWillCommitWithAnimator(
+    WebState* source,
+    const GURL& link_url,
+    id<UIContextMenuInteractionCommitAnimating> animator)
+    API_AVAILABLE(ios(13.0)) {}
+
+void WebStateDelegate::ContextMenuWillPresent(WebState* source,
+                                              const GURL& link_url)
+    API_AVAILABLE(ios(13.0)) {}
+
 }  // web
diff --git a/ios/web/web_state/web_state_delegate_bridge.mm b/ios/web/web_state/web_state_delegate_bridge.mm
index 720c48c..5634d9d 100644
--- a/ios/web/web_state/web_state_delegate_bridge.mm
+++ b/ios/web/web_state/web_state_delegate_bridge.mm
@@ -132,4 +132,51 @@
   }
 }
 
+void WebStateDelegateBridge::ContextMenuConfiguration(
+    WebState* source,
+    const GURL& link_url,
+    void (^completion_handler)(UIContextMenuConfiguration*))
+    API_AVAILABLE(ios(13.0)) {
+  if ([delegate_
+          respondsToSelector:@selector
+          (webState:
+              contextMenuConfigurationForLinkWithURL:completionHandler:)]) {
+    [delegate_ webState:source
+        contextMenuConfigurationForLinkWithURL:link_url
+                             completionHandler:completion_handler];
+  }
+}
+
+void WebStateDelegateBridge::ContextMenuDidEnd(WebState* source,
+                                               const GURL& link_url)
+    API_AVAILABLE(ios(13.0)) {
+  if ([delegate_ respondsToSelector:@selector(webState:
+                                        contextMenuDidEndForLinkWithURL:)]) {
+    [delegate_ webState:source contextMenuDidEndForLinkWithURL:link_url];
+  }
+}
+
+void WebStateDelegateBridge::ContextMenuWillCommitWithAnimator(
+    WebState* source,
+    const GURL& link_url,
+    id<UIContextMenuInteractionCommitAnimating> animator)
+    API_AVAILABLE(ios(13.0)) {
+  if ([delegate_ respondsToSelector:@selector
+                 (webState:
+                     contextMenuForLinkWithURL:willCommitWithAnimator:)]) {
+    [delegate_ webState:source
+        contextMenuForLinkWithURL:link_url
+           willCommitWithAnimator:animator];
+  }
+}
+
+void WebStateDelegateBridge::ContextMenuWillPresent(WebState* source,
+                                                    const GURL& link_url)
+    API_AVAILABLE(ios(13.0)) {
+  if ([delegate_ respondsToSelector:@selector
+                 (webState:contextMenuWillPresentForLinkWithURL:)]) {
+    [delegate_ webState:source contextMenuWillPresentForLinkWithURL:link_url];
+  }
+}
+
 }  // web
diff --git a/ios/web_view/internal/cwv_web_view.mm b/ios/web_view/internal/cwv_web_view.mm
index 16fcd1f..59a60c5 100644
--- a/ios/web_view/internal/cwv_web_view.mm
+++ b/ios/web_view/internal/cwv_web_view.mm
@@ -158,7 +158,10 @@
 
 @end
 
-static NSString* gUserAgentProduct = nil;
+namespace {
+NSString* gUserAgentProduct = nil;
+BOOL gChromeLongPressAndForceTouchHandlingEnabled = YES;
+}  // namespace
 
 @implementation CWVWebView
 
@@ -188,6 +191,14 @@
   ios_web_view::InitializeGlobalState();
 }
 
++ (BOOL)chromeLongPressAndForceTouchHandlingEnabled {
+  return gChromeLongPressAndForceTouchHandlingEnabled;
+}
+
++ (void)setChromeLongPressAndForceTouchHandlingEnabled:(BOOL)newValue {
+  gChromeLongPressAndForceTouchHandlingEnabled = newValue;
+}
+
 + (NSString*)userAgentProduct {
   return gUserAgentProduct;
 }
@@ -419,8 +430,8 @@
 
 - (void)webState:(web::WebState*)webState
     handleContextMenu:(const web::ContextMenuParams&)params {
-  SEL selector = @selector(webView:runContextMenuWithTitle:forHTMLElement:inView
-                                  :userGestureLocation:);
+  SEL selector = @selector(webView:
+           runContextMenuWithTitle:forHTMLElement:inView:userGestureLocation:);
   if (![_UIDelegate respondsToSelector:selector]) {
     return;
   }
@@ -437,6 +448,8 @@
           userGestureLocation:params.location];
 }
 
+#pragma mark - CRWWebStateDelegate
+
 - (web::WebState*)webState:(web::WebState*)webState
     createNewWebStateForURL:(const GURL&)URL
                   openerURL:(const GURL&)openerURL
@@ -508,6 +521,54 @@
 }
 
 - (void)webState:(web::WebState*)webState
+    contextMenuConfigurationForLinkWithURL:(const GURL&)linkURL
+                         completionHandler:
+                             (void (^)(UIContextMenuConfiguration*))
+                                 completionHandler API_AVAILABLE(ios(13.0)) {
+  SEL selector = @selector(webView:
+      contextMenuConfigurationForLinkWithURL:completionHandler:);
+  if ([_UIDelegate respondsToSelector:selector]) {
+    [_UIDelegate webView:self
+        contextMenuConfigurationForLinkWithURL:net::NSURLWithGURL(linkURL)
+                             completionHandler:completionHandler];
+  }
+}
+
+- (void)webState:(web::WebState*)webState
+    contextMenuWillPresentForLinkWithURL:(const GURL&)linkURL
+    API_AVAILABLE(ios(13.0)) {
+  SEL selector = @selector(webView:contextMenuWillPresentForLinkWithURL:);
+  if ([_UIDelegate respondsToSelector:selector]) {
+    [_UIDelegate webView:self
+        contextMenuWillPresentForLinkWithURL:net::NSURLWithGURL(linkURL)];
+  }
+}
+
+- (void)webState:(web::WebState*)webState
+    contextMenuForLinkWithURL:(const GURL&)linkURL
+       willCommitWithAnimator:
+           (id<UIContextMenuInteractionCommitAnimating>)animator
+    API_AVAILABLE(ios(13.0)) {
+  SEL selector = @selector(webView:
+         contextMenuForLinkWithURL:willCommitWithAnimator:);
+  if ([_UIDelegate respondsToSelector:selector]) {
+    [_UIDelegate webView:self
+        contextMenuForLinkWithURL:net::NSURLWithGURL(linkURL)
+           willCommitWithAnimator:animator];
+  }
+}
+
+- (void)webState:(web::WebState*)webState
+    contextMenuDidEndForLinkWithURL:(const GURL&)linkURL
+    API_AVAILABLE(ios(13.0)) {
+  SEL selector = @selector(webView:contextMenuDidEndForLinkWithURL:);
+  if ([_UIDelegate respondsToSelector:selector]) {
+    [_UIDelegate webView:self
+        contextMenuDidEndForLinkWithURL:net::NSURLWithGURL(linkURL)];
+  }
+}
+
+- (void)webState:(web::WebState*)webState
     didUpdateFaviconURLCandidates:
         (const std::vector<web::FaviconURL>&)candidates {
   if ([_UIDelegate respondsToSelector:@selector(webView:didLoadFavicons:)]) {
@@ -678,12 +739,14 @@
   // |web::EnsureWebViewCreatedWithConfiguration()|, as this is the requirement
   // of |web::EnsureWebViewCreatedWithConfiguration()|
 
-  WKWebView* webView = nil;
-  if (wkConfiguration) {
-    webView = web::EnsureWebViewCreatedWithConfiguration(_webState.get(),
-                                                         wkConfiguration);
-  }
+  // Creates a WKWebView immediately to assure the class property
+  // |chromeLongPressAndForceTouchHandlingEnabled| is consumed when a
+  // CWVWebView is initializing instead of some time later. Then
+  // "longPressActionsEnabled" will be set in WKWebViewConfigurationProvider.
+  WKWebView* webView = web::EnsureWebViewCreatedWithConfiguration(
+      _webState.get(), wkConfiguration);
   if (createdWebView) {
+    // If the created webView is needed, returns it by the out variable way.
     *createdWebView = webView;
   }
 
diff --git a/ios/web_view/internal/cwv_web_view_configuration.mm b/ios/web_view/internal/cwv_web_view_configuration.mm
index 63f0455..d1c708e4 100644
--- a/ios/web_view/internal/cwv_web_view_configuration.mm
+++ b/ios/web_view/internal/cwv_web_view_configuration.mm
@@ -85,7 +85,7 @@
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
     auto browserState = std::make_unique<ios_web_view::WebViewBrowserState>(
-        /*off_the_record = */ false);
+        /* off_the_record = */ false);
     gDefaultConfiguration = [[CWVWebViewConfiguration alloc]
         initWithBrowserState:std::move(browserState)];
   });
@@ -97,7 +97,7 @@
   dispatch_once(&onceToken, ^{
     CWVWebViewConfiguration* defaultConfiguration = [self defaultConfiguration];
     auto browserState = std::make_unique<ios_web_view::WebViewBrowserState>(
-        /*off_the_record = */ true, defaultConfiguration.browserState);
+        /* off_the_record = */ true, defaultConfiguration.browserState);
     gIncognitoConfiguration = [[CWVWebViewConfiguration alloc]
         initWithBrowserState:std::move(browserState)];
   });
diff --git a/ios/web_view/internal/web_view_web_client.h b/ios/web_view/internal/web_view_web_client.h
index e122d11..09ba5f0b 100644
--- a/ios/web_view/internal/web_view_web_client.h
+++ b/ios/web_view/internal/web_view_web_client.h
@@ -38,6 +38,7 @@
       bool overridable,
       int64_t navigation_id,
       const base::RepeatingCallback<void(bool)>& callback) override;
+  bool EnableLongPressAndForceTouchHandling() const override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(WebViewWebClient);
diff --git a/ios/web_view/internal/web_view_web_client.mm b/ios/web_view/internal/web_view_web_client.mm
index 155b0b0..d513ede 100644
--- a/ios/web_view/internal/web_view_web_client.mm
+++ b/ios/web_view/internal/web_view_web_client.mm
@@ -154,4 +154,8 @@
   }
 }
 
+bool WebViewWebClient::EnableLongPressAndForceTouchHandling() const {
+  return CWVWebView.chromeLongPressAndForceTouchHandlingEnabled;
+}
+
 }  // namespace ios_web_view
diff --git a/ios/web_view/public/cwv_ui_delegate.h b/ios/web_view/public/cwv_ui_delegate.h
index 8037844..809fa07 100644
--- a/ios/web_view/public/cwv_ui_delegate.h
+++ b/ios/web_view/public/cwv_ui_delegate.h
@@ -90,6 +90,45 @@
 - (void)webView:(CWVWebView*)webView
     didLoadFavicons:(NSArray<CWVFavicon*>*)favIcons;
 
+// Equivalent of -[WKUIDelegate
+// webView:contextMenuConfigurationForElement:completionHandler:].
+// Must set |CWVWebView.chromeLongPressAndForceTouchHandlingEnabled| to NO
+// before the |webView| is initialized to use this delegate method, otherwise it
+// won't be called.
+- (void)webView:(CWVWebView*)webView
+    contextMenuConfigurationForLinkWithURL:(NSURL*)linkURL
+                         completionHandler:
+                             (void (^)(UIContextMenuConfiguration*))
+                                 completionHandler API_AVAILABLE(ios(13.0));
+
+// Equivalent of -[WKUIDelegate
+// webView:contextMenuWillPresentForElement:].
+// Must set |CWVWebView.chromeLongPressAndForceTouchHandlingEnabled| to NO
+// before the |webView| is initialized to use this delegate method, otherwise it
+// won't be called.
+- (void)webView:(CWVWebView*)webView
+    contextMenuWillPresentForLinkWithURL:(NSURL*)linkURL
+    API_AVAILABLE(ios(13.0));
+
+// Equivalent of -[WKUIDelegate
+// webView:contextMenuForElement:willCommitWithAnimator:].
+// Must set |CWVWebView.chromeLongPressAndForceTouchHandlingEnabled| to NO
+// before the |webView| is initialized to use this delegate method, otherwise it
+// won't be called.
+- (void)webView:(CWVWebView*)webView
+    contextMenuForLinkWithURL:(NSURL*)linkURL
+       willCommitWithAnimator:
+           (id<UIContextMenuInteractionCommitAnimating>)animator
+    API_AVAILABLE(ios(13.0));
+
+// Equivalent of -[WKUIDelegate
+// webView:contextMenuDidEndForElement:].
+// Must set |CWVWebView.chromeLongPressAndForceTouchHandlingEnabled| to NO
+// before the |webView| is initialized to use this delegate method, otherwise it
+// won't be called.
+- (void)webView:(CWVWebView*)webView
+    contextMenuDidEndForLinkWithURL:(NSURL*)linkURL API_AVAILABLE(ios(13.0));
+
 @end
 
 NS_ASSUME_NONNULL_END
diff --git a/ios/web_view/public/cwv_web_view.h b/ios/web_view/public/cwv_web_view.h
index ee6caa6..a28e6da3 100644
--- a/ios/web_view/public/cwv_web_view.h
+++ b/ios/web_view/public/cwv_web_view.h
@@ -119,6 +119,11 @@
 // https://developer.apple.com/documentation/webkit/wkwebview/1414977-backforwardlist
 @property(nonatomic, readonly, nonnull) CWVBackForwardList* backForwardList;
 
+// Enables Chrome's custom logic to handle long press and force touch. Defaults
+// to YES. This CLASS PROPERTY setting will only be applied to all CWVWebView
+// instances INITIALIZED AFTERWARD.
+@property(nonatomic, class) BOOL chromeLongPressAndForceTouchHandlingEnabled;
+
 // The User Agent product string used to build the full User Agent.
 + (NSString*)userAgentProduct;
 
diff --git a/ios/web_view/public/cwv_web_view_configuration.h b/ios/web_view/public/cwv_web_view_configuration.h
index 185548fc..d91b290c 100644
--- a/ios/web_view/public/cwv_web_view_configuration.h
+++ b/ios/web_view/public/cwv_web_view_configuration.h
@@ -22,7 +22,7 @@
 // Configuration with persistent data store which stores all data on disk.
 + (instancetype)defaultConfiguration;
 
-// Configuration with ephemeral data store that neven stores data on disk.
+// Configuration with ephemeral data store that never stores data on disk.
 + (instancetype)incognitoConfiguration;
 
 - (instancetype)init NS_UNAVAILABLE;
diff --git a/ios/web_view/shell/shell_view_controller.m b/ios/web_view/shell/shell_view_controller.m
index 7be2e0c..8d975ed 100644
--- a/ios/web_view/shell/shell_view_controller.m
+++ b/ios/web_view/shell/shell_view_controller.m
@@ -251,6 +251,7 @@
   ]];
 
   [CWVWebView setUserAgentProduct:@"Dummy/1.0"];
+  CWVWebView.chromeLongPressAndForceTouchHandlingEnabled = NO;
 
   _authService = [[ShellAuthService alloc] init];
   CWVSyncController.dataSource = _authService;
@@ -593,6 +594,30 @@
                                   createWebViewWithConfiguration:configuration];
                             }]];
 
+  // Developers can choose to use system or Chrome context menu in the shell
+  // app. This will also recreate the web view.
+  BOOL chromeContextMenuEnabled =
+      CWVWebView.chromeLongPressAndForceTouchHandlingEnabled;
+  NSString* contextMenuSwitchActionTitle = [NSString
+      stringWithFormat:@"Use %@ context menu",
+                       chromeContextMenuEnabled ? @"system" : @"Chrome"];
+  [alertController
+      addAction:[UIAlertAction
+                    actionWithTitle:contextMenuSwitchActionTitle
+                              style:UIAlertActionStyleDefault
+                            handler:^(UIAlertAction* action) {
+                              CWVWebView
+                                  .chromeLongPressAndForceTouchHandlingEnabled =
+                                  !chromeContextMenuEnabled;
+                              NSLog(@"Chrome context menu is %@ now.",
+                                    !chromeContextMenuEnabled ? @"OFF" : @"ON");
+                              CWVWebViewConfiguration* configuration =
+                                  weakSelf.webView.configuration;
+                              [weakSelf removeWebView];
+                              [weakSelf
+                                  createWebViewWithConfiguration:configuration];
+                            }]];
+
   // Resets all translation settings to default values.
   [alertController
       addAction:[UIAlertAction actionWithTitle:@"Reset translate settings"
@@ -792,6 +817,71 @@
 }
 
 - (void)webView:(CWVWebView*)webView
+    contextMenuConfigurationForLinkWithURL:(NSURL*)linkURL
+                         completionHandler:
+                             (void (^)(UIContextMenuConfiguration*))
+                                 completionHandler API_AVAILABLE(ios(13.0)) {
+  void (^copyHandler)(UIAction*) = ^(UIAction* action) {
+    NSDictionary* item = @{
+      (NSString*)(kUTTypeURL) : linkURL.absoluteString,
+      (NSString*)(kUTTypeUTF8PlainText) :
+          [linkURL.absoluteString dataUsingEncoding:NSUTF8StringEncoding],
+    };
+    [[UIPasteboard generalPasteboard] setItems:@[ item ]];
+  };
+
+  UIContextMenuConfiguration* configuration = [UIContextMenuConfiguration
+      configurationWithIdentifier:nil
+      previewProvider:^{
+        UIViewController* controller = [[UIViewController alloc] init];
+        CGRect frame = CGRectMake(10, 200, 200, 21);
+        UILabel* label = [[UILabel alloc] initWithFrame:frame];
+        label.text = @"iOS13 Preview Page";
+        [controller.view addSubview:label];
+        return controller;
+      }
+      actionProvider:^(id _) {
+        NSArray* actions = @[
+          [UIAction actionWithTitle:@"Copy Link"
+                              image:nil
+                         identifier:nil
+                            handler:copyHandler],
+          [UIAction actionWithTitle:@"Cancel"
+                              image:nil
+                         identifier:nil
+                            handler:^(id _){
+                            }]
+        ];
+        NSString* menuTitle = [NSString
+            stringWithFormat:@"iOS13 Context Menu: %@", linkURL.absoluteString];
+        return [UIMenu menuWithTitle:menuTitle children:actions];
+      }];
+
+  completionHandler(configuration);
+}
+
+- (void)webView:(CWVWebView*)webView
+    contextMenuWillPresentForLinkWithURL:(NSURL*)linkURL
+    API_AVAILABLE(ios(13.0)) {
+  NSLog(@"webView:contextMenuWillPresentForLinkWithURL: %@",
+        linkURL.absoluteString);
+}
+
+- (void)webView:(CWVWebView*)webView
+    contextMenuForLinkWithURL:(NSURL*)linkURL
+       willCommitWithAnimator:
+           (id<UIContextMenuInteractionCommitAnimating>)animator
+    API_AVAILABLE(ios(13.0)) {
+  NSLog(@"webView:contextMenuForLinkWithURL:willCommitWithAnimator: %@",
+        linkURL.absoluteString);
+}
+
+- (void)webView:(CWVWebView*)webView
+    contextMenuDidEndForLinkWithURL:(NSURL*)linkURL API_AVAILABLE(ios(13.0)) {
+  NSLog(@"webView:contextMenuDidEndForLinkWithURL: %@", linkURL.absoluteString);
+}
+
+- (void)webView:(CWVWebView*)webView
     runJavaScriptAlertPanelWithMessage:(NSString*)message
                                pageURL:(NSURL*)URL
                      completionHandler:(void (^)(void))handler {
diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc
index 88cb98d..eabf979 100644
--- a/media/base/key_systems.cc
+++ b/media/base/key_systems.cc
@@ -10,6 +10,7 @@
 #include <unordered_map>
 
 #include "base/logging.h"
+#include "base/no_destructor.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_checker.h"
@@ -232,8 +233,6 @@
  public:
   static KeySystemsImpl* GetInstance();
 
-  void UpdateIfNeeded();
-
   // These two functions are for testing purpose only.
   void AddCodecMaskForTesting(EmeMediaType media_type,
                               const std::string& codec,
@@ -242,6 +241,8 @@
                                       uint32_t mask);
 
   // Implementation of KeySystems interface.
+  void UpdateIfNeeded() override;
+
   bool IsSupportedKeySystem(const std::string& key_system) const override;
 
   bool CanUseAesDecryptor(const std::string& key_system) const override;
@@ -277,6 +278,8 @@
       const std::string& key_system) const override;
 
  private:
+  friend class base::NoDestructor<KeySystemsImpl>;
+
   KeySystemsImpl();
   ~KeySystemsImpl() override;
 
@@ -328,9 +331,9 @@
 };
 
 KeySystemsImpl* KeySystemsImpl::GetInstance() {
-  static KeySystemsImpl* key_systems = new KeySystemsImpl();
+  static base::NoDestructor<KeySystemsImpl> key_systems;
   key_systems->UpdateIfNeeded();
-  return key_systems;
+  return key_systems.get();
 }
 
 // Because we use a thread-safe static, the key systems info must be populated
diff --git a/media/base/key_systems.h b/media/base/key_systems.h
index c912c1bb..d57aa8e 100644
--- a/media/base/key_systems.h
+++ b/media/base/key_systems.h
@@ -28,6 +28,9 @@
  public:
   static KeySystems* GetInstance();
 
+  // Refreshes the list of available key systems if it may be out of date.
+  virtual void UpdateIfNeeded() = 0;
+
   // Returns whether |key_system| is a supported key system.
   virtual bool IsSupportedKeySystem(const std::string& key_system) const = 0;
 
diff --git a/media/blink/key_system_config_selector.cc b/media/blink/key_system_config_selector.cc
index e4cd8d1..7d2951d1 100644
--- a/media/blink/key_system_config_selector.cc
+++ b/media/blink/key_system_config_selector.cc
@@ -302,7 +302,7 @@
 };
 
 KeySystemConfigSelector::KeySystemConfigSelector(
-    const KeySystems* key_systems,
+    KeySystems* key_systems,
     MediaPermission* media_permission)
     : key_systems_(key_systems),
       media_permission_(media_permission),
@@ -909,6 +909,8 @@
     return;
   }
 
+  key_systems_->UpdateIfNeeded();
+
   std::string key_system_ascii = key_system.Ascii();
   if (!key_systems_->IsSupportedKeySystem(key_system_ascii)) {
     not_supported_cb.Run();
diff --git a/media/blink/key_system_config_selector.h b/media/blink/key_system_config_selector.h
index d7a621b..8a0b30c 100644
--- a/media/blink/key_system_config_selector.h
+++ b/media/blink/key_system_config_selector.h
@@ -33,7 +33,7 @@
 
 class MEDIA_BLINK_EXPORT KeySystemConfigSelector {
  public:
-  KeySystemConfigSelector(const KeySystems* key_systems,
+  KeySystemConfigSelector(KeySystems* key_systems,
                           MediaPermission* media_permission);
 
   ~KeySystemConfigSelector();
@@ -96,7 +96,7 @@
       const blink::WebMediaKeySystemMediaCapability::EncryptionScheme
           encryption_scheme);
 
-  const KeySystems* key_systems_;
+  KeySystems* const key_systems_;
   MediaPermission* media_permission_;
 
   // A callback used to check whether a media type is supported. Only set in
diff --git a/media/blink/key_system_config_selector_unittest.cc b/media/blink/key_system_config_selector_unittest.cc
index 8fbf9160..ca9bd8a 100644
--- a/media/blink/key_system_config_selector_unittest.cc
+++ b/media/blink/key_system_config_selector_unittest.cc
@@ -191,6 +191,8 @@
  public:
   ~FakeKeySystems() override = default;
 
+  void UpdateIfNeeded() override { ++update_count; }
+
   bool IsSupportedKeySystem(const std::string& key_system) const override {
     // Based on EME spec, Clear Key key system is always supported.
     return key_system == kSupportedKeySystem ||
@@ -333,6 +335,8 @@
   // the default values may be changed.
   EmeFeatureSupport persistent_state = EmeFeatureSupport::NOT_SUPPORTED;
   EmeFeatureSupport distinctive_identifier = EmeFeatureSupport::REQUESTABLE;
+
+  int update_count = 0;
 };
 
 class FakeMediaPermission : public MediaPermission {
@@ -504,6 +508,17 @@
   EXPECT_FALSE(cdm_config_.use_hw_secure_codecs);
 }
 
+// KeySystemConfigSelector should make sure it uses up-to-date KeySystems.
+TEST_F(KeySystemConfigSelectorTest, UpdateKeySystems) {
+  configs_.push_back(UsableConfiguration());
+
+  ASSERT_EQ(key_systems_->update_count, 0);
+  SelectConfig();
+  EXPECT_EQ(key_systems_->update_count, 1);
+  SelectConfig();
+  EXPECT_EQ(key_systems_->update_count, 2);
+}
+
 TEST_F(KeySystemConfigSelectorTest, Label) {
   auto config = UsableConfiguration();
   config.label = "foo";
diff --git a/net/disk_cache/disk_cache_test_base.cc b/net/disk_cache/disk_cache_test_base.cc
index 35c36db..e6970469 100644
--- a/net/disk_cache/disk_cache_test_base.cc
+++ b/net/disk_cache/disk_cache_test_base.cc
@@ -234,9 +234,15 @@
 }
 
 void DiskCacheTestWithCache::FlushQueueForTest() {
-  if (memory_only_ || !cache_impl_)
+  if (memory_only_)
     return;
 
+  if (simple_cache_impl_) {
+    simple_cache_impl_->FlushWorkerPoolForTesting();
+    return;
+  }
+
+  DCHECK(cache_impl_);
   net::TestCompletionCallback cb;
   int rv = cache_impl_->FlushQueueForTest(cb.callback());
   EXPECT_THAT(cb.GetResult(rv), IsOk());
diff --git a/services/BUILD.gn b/services/BUILD.gn
index 00ae3d4..ada6565 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -152,8 +152,6 @@
       "//third_party/junit",
       "//ui/gfx/geometry/mojom:mojom_java",
     ]
-    data = [
-      "test/data/",
-    ]
+    data = [ "test/data/" ]
   }
 }
diff --git a/services/data_decoder/BUILD.gn b/services/data_decoder/BUILD.gn
index c59d28d1..45ff0f1 100644
--- a/services/data_decoder/BUILD.gn
+++ b/services/data_decoder/BUILD.gn
@@ -38,9 +38,7 @@
     "//ui/gfx/geometry",
   ]
 
-  public_deps = [
-    "//services/data_decoder/public/mojom",
-  ]
+  public_deps = [ "//services/data_decoder/public/mojom" ]
 
   if (!is_ios) {
     sources += [
@@ -88,9 +86,7 @@
       "//gin:gin_test",
       "//third_party/blink/public:blink",
     ]
-    data_deps = [
-      "//tools/v8_context_snapshot",
-    ]
+    data_deps = [ "//tools/v8_context_snapshot" ]
     configs += [
       "//tools/v8_context_snapshot:use_v8_context_snapshot",
       "//v8:external_startup_data",
@@ -99,9 +95,7 @@
 }
 
 fuzzer_test("web_bundle_parser_fuzzer") {
-  sources = [
-    "web_bundle_parser_fuzzer.cc",
-  ]
+  sources = [ "web_bundle_parser_fuzzer.cc" ]
   deps = [
     ":lib",
     "//base",
@@ -111,9 +105,7 @@
 }
 
 fuzzer_test("xml_parser_fuzzer") {
-  sources = [
-    "xml_parser_fuzzer.cc",
-  ]
+  sources = [ "xml_parser_fuzzer.cc" ]
   deps = [
     ":lib",
     "//base",
@@ -126,11 +118,7 @@
 
 if (is_chromeos) {
   fuzzer_test("ble_scan_parser_fuzzer") {
-    sources = [
-      "ble_scan_parser_impl_fuzzer.cc",
-    ]
-    deps = [
-      ":lib",
-    ]
+    sources = [ "ble_scan_parser_impl_fuzzer.cc" ]
+    deps = [ ":lib" ]
   }
 }
diff --git a/services/data_decoder/public/cpp/BUILD.gn b/services/data_decoder/public/cpp/BUILD.gn
index 69f3a06..4dd8989 100644
--- a/services/data_decoder/public/cpp/BUILD.gn
+++ b/services/data_decoder/public/cpp/BUILD.gn
@@ -8,13 +8,9 @@
 # converted to a component target. A component target is necessary for
 # ServiceProvider because it exposes global storage.
 component("service_provider") {
-  public = [
-    "service_provider.h",
-  ]
+  public = [ "service_provider.h" ]
 
-  sources = [
-    "service_provider.cc",
-  ]
+  sources = [ "service_provider.cc" ]
 
   public_deps = [
     "//base",
@@ -51,9 +47,8 @@
   if (is_android) {
     sources += [ "json_sanitizer_android.cc" ]
 
-    deps = [
-      "//services/data_decoder/public/cpp/android:safe_json_jni_headers",
-    ]
+    deps =
+        [ "//services/data_decoder/public/cpp/android:safe_json_jni_headers" ]
   } else {
     sources += [ "json_sanitizer_non_android.cc" ]
   }
@@ -62,9 +57,7 @@
     # NOTE: We depend on this target here for iOS only, to support in-process
     # use of the service. Non-test targets in this directory should otherwise
     # NEVER depend on this target.
-    deps = [
-      "//services/data_decoder:lib",
-    ]
+    deps = [ "//services/data_decoder:lib" ]
   } else {
     public += [
       "decode_image.h",
@@ -88,9 +81,7 @@
     "test_support/web_bundle_builder.h",
   ]
 
-  deps = [
-    "//components/cbor",
-  ]
+  deps = [ "//components/cbor" ]
 
   public_deps = [
     ":cpp",
diff --git a/services/device/bluetooth/BUILD.gn b/services/device/bluetooth/BUILD.gn
index b5fe1f1e..7b27ba9 100644
--- a/services/device/bluetooth/BUILD.gn
+++ b/services/device/bluetooth/BUILD.gn
@@ -17,9 +17,7 @@
     "bluetooth_system_factory.h",
   ]
 
-  public_deps = [
-    "//services/device/public/mojom",
-  ]
+  public_deps = [ "//services/device/public/mojom" ]
 
   deps = [
     "//base",
@@ -31,9 +29,7 @@
 source_set("bluetooth_system_tests") {
   testonly = true
 
-  sources = [
-    "bluetooth_system_unittest.cc",
-  ]
+  sources = [ "bluetooth_system_unittest.cc" ]
 
   deps = [
     ":bluetooth_system",
diff --git a/services/device/fingerprint/BUILD.gn b/services/device/fingerprint/BUILD.gn
index e8079a9..5a39e5bd 100644
--- a/services/device/fingerprint/BUILD.gn
+++ b/services/device/fingerprint/BUILD.gn
@@ -33,7 +33,5 @@
     sources += [ "fingerprint_default.cc" ]
   }
 
-  public_deps = [
-    "//services/device/public/mojom",
-  ]
+  public_deps = [ "//services/device/public/mojom" ]
 }
diff --git a/services/device/wake_lock/BUILD.gn b/services/device/wake_lock/BUILD.gn
index 1dec49d..23fe2f9b 100644
--- a/services/device/wake_lock/BUILD.gn
+++ b/services/device/wake_lock/BUILD.gn
@@ -21,7 +21,5 @@
     "//ui/gfx",
   ]
 
-  deps = [
-    "//services/device/wake_lock/power_save_blocker",
-  ]
+  deps = [ "//services/device/wake_lock/power_save_blocker" ]
 }
diff --git a/services/device/wake_lock/power_save_blocker/BUILD.gn b/services/device/wake_lock/power_save_blocker/BUILD.gn
index 310bbcd..af81c2e 100644
--- a/services/device/wake_lock/power_save_blocker/BUILD.gn
+++ b/services/device/wake_lock/power_save_blocker/BUILD.gn
@@ -20,17 +20,11 @@
     "//services/device/wake_lock:*",
   ]
 
-  sources = [
-    "power_save_blocker.h",
-  ]
+  sources = [ "power_save_blocker.h" ]
 
-  public_deps = [
-    "//services/device/public/mojom",
-  ]
+  public_deps = [ "//services/device/public/mojom" ]
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 
   if (use_dbus) {
     deps += [ "//dbus" ]
@@ -90,8 +84,6 @@
       "//services/device:*",
     ]
     sources = java_sources_needing_jni
-    deps = [
-      "//base:base_java",
-    ]
+    deps = [ "//base:base_java" ]
   }
 }
diff --git a/services/image_annotation/BUILD.gn b/services/image_annotation/BUILD.gn
index 436d1f67..24a69d5 100644
--- a/services/image_annotation/BUILD.gn
+++ b/services/image_annotation/BUILD.gn
@@ -45,9 +45,7 @@
 source_set("tests") {
   testonly = true
 
-  sources = [
-    "annotator_unittest.cc",
-  ]
+  sources = [ "annotator_unittest.cc" ]
 
   deps = [
     ":lib",
diff --git a/services/image_annotation/public/cpp/BUILD.gn b/services/image_annotation/public/cpp/BUILD.gn
index ca63856..8e864e9 100644
--- a/services/image_annotation/public/cpp/BUILD.gn
+++ b/services/image_annotation/public/cpp/BUILD.gn
@@ -20,9 +20,7 @@
 source_set("tests") {
   testonly = true
 
-  sources = [
-    "image_processor_unittest.cc",
-  ]
+  sources = [ "image_processor_unittest.cc" ]
 
   deps = [
     ":cpp",
diff --git a/services/image_annotation/public/mojom/BUILD.gn b/services/image_annotation/public/mojom/BUILD.gn
index 4e6bd52..4eb0e6f 100644
--- a/services/image_annotation/public/mojom/BUILD.gn
+++ b/services/image_annotation/public/mojom/BUILD.gn
@@ -5,7 +5,5 @@
 import("//mojo/public/tools/bindings/mojom.gni")
 
 mojom("mojom") {
-  sources = [
-    "image_annotation.mojom",
-  ]
+  sources = [ "image_annotation.mojom" ]
 }
diff --git a/services/metrics/public/cpp/BUILD.gn b/services/metrics/public/cpp/BUILD.gn
index 6eff85e..68bfc8ee 100644
--- a/services/metrics/public/cpp/BUILD.gn
+++ b/services/metrics/public/cpp/BUILD.gn
@@ -66,9 +66,7 @@
     "//tools/metrics/ukm/codegen.py",
     "//tools/metrics/ukm/gen_builders.py",
   ]
-  sources = [
-    "//tools/metrics/ukm/ukm.xml",
-  ]
+  sources = [ "//tools/metrics/ukm/ukm.xml" ]
 
   outdir = "$target_gen_dir"
 
diff --git a/services/metrics/public/mojom/BUILD.gn b/services/metrics/public/mojom/BUILD.gn
index 4713382e..ace0b430 100644
--- a/services/metrics/public/mojom/BUILD.gn
+++ b/services/metrics/public/mojom/BUILD.gn
@@ -5,11 +5,7 @@
 import("//mojo/public/tools/bindings/mojom.gni")
 
 mojom("mojom") {
-  sources = [
-    "ukm_interface.mojom",
-  ]
+  sources = [ "ukm_interface.mojom" ]
 
-  public_deps = [
-    "//url/mojom:url_mojom_gurl",
-  ]
+  public_deps = [ "//url/mojom:url_mojom_gurl" ]
 }
diff --git a/services/network/BUILD.gn b/services/network/BUILD.gn
index 49115e0f..f535093 100644
--- a/services/network/BUILD.gn
+++ b/services/network/BUILD.gn
@@ -245,9 +245,7 @@
     "//url",
   ]
 
-  public_deps = [
-    "//services/network/public/cpp:buildflags",
-  ]
+  public_deps = [ "//services/network/public/cpp:buildflags" ]
 
   if (is_ct_supported) {
     sources += [
@@ -473,9 +471,7 @@
 }
 
 fuzzer_test("network_content_security_policy_fuzzer") {
-  sources = [
-    "content_security_policy_fuzzer.cc",
-  ]
+  sources = [ "content_security_policy_fuzzer.cc" ]
   deps = [
     "//base",
     "//net",
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn
index 5546dd8f..e949d87 100644
--- a/services/network/public/cpp/BUILD.gn
+++ b/services/network/public/cpp/BUILD.gn
@@ -185,12 +185,8 @@
 }
 
 mojom("test_interfaces") {
-  sources = [
-    "network_traits_test_service.mojom",
-  ]
-  public_deps = [
-    "//services/network/public/mojom",
-  ]
+  sources = [ "network_traits_test_service.mojom" ]
+  public_deps = [ "//services/network/public/mojom" ]
 }
 
 source_set("tests") {
@@ -246,16 +242,10 @@
     "//testing/gtest",
   ]
 
-  public_deps = [
-    ":buildflags",
-  ]
+  public_deps = [ ":buildflags" ]
 }
 
 fuzzer_test("cors_fuzzer") {
-  sources = [
-    "cors/cors_fuzzer.cc",
-  ]
-  deps = [
-    ":cpp",
-  ]
+  sources = [ "cors/cors_fuzzer.cc" ]
+  deps = [ ":cpp" ]
 }
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index 74e526c8..9ec95487 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -18,9 +18,7 @@
     "ip_endpoint.mojom",
   ]
 
-  public_deps = [
-    "//url/mojom:url_mojom_gurl",
-  ]
+  public_deps = [ "//url/mojom:url_mojom_gurl" ]
 }
 
 # As with mojom_ip_address, this is a separate target to avoid a circular
@@ -30,13 +28,9 @@
 # depending on that and don't need this.
 mojom("mojom_network_isolation_key") {
   generate_java = true
-  sources = [
-    "network_isolation_key.mojom",
-  ]
+  sources = [ "network_isolation_key.mojom" ]
 
-  public_deps = [
-    "//url/mojom:url_mojom_origin",
-  ]
+  public_deps = [ "//url/mojom:url_mojom_origin" ]
 
   if (!is_ios) {
     export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/BUILD.gn b/services/resource_coordinator/public/cpp/memory_instrumentation/BUILD.gn
index 7775874..603f9215 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/BUILD.gn
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/BUILD.gn
@@ -50,7 +50,5 @@
     "browser_metrics.h",
   ]
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 }
diff --git a/services/service_manager/sandbox/BUILD.gn b/services/service_manager/sandbox/BUILD.gn
index 13340bd..1ee6a624 100644
--- a/services/service_manager/sandbox/BUILD.gn
+++ b/services/service_manager/sandbox/BUILD.gn
@@ -20,9 +20,7 @@
     "switches.h",
   ]
   defines = [ "SERVICE_MANAGER_SANDBOX_IMPL" ]
-  public_deps = [
-    "//services/service_manager/embedder:embedder_switches",
-  ]
+  public_deps = [ "//services/service_manager/embedder:embedder_switches" ]
   deps = [
     ":sanitizer_buildflags",
     "//base",
diff --git a/services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.cc b/services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.cc
index 9c0bc37..321ea4a 100644
--- a/services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.cc
+++ b/services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.cc
@@ -36,7 +36,6 @@
     case __NR_sysinfo:
     case __NR_uname:
 #if !defined(__aarch64__)
-    case __NR_getdents:
     case __NR_readlink:
     case __NR_stat:
 #endif
diff --git a/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.cc b/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.cc
index c4daa506..6621433 100644
--- a/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.cc
+++ b/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.cc
@@ -48,6 +48,10 @@
     (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS))
     case __NR_ftruncate64:
 #endif
+#if !defined(__aarch64__)
+    case __NR_getdents:
+#endif
+    case __NR_getdents64:
     case __NR_ioctl:
       return Allow();
 #if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
diff --git a/services/test/BUILD.gn b/services/test/BUILD.gn
index 76aa1a4c..9f50070 100644
--- a/services/test/BUILD.gn
+++ b/services/test/BUILD.gn
@@ -11,9 +11,7 @@
 
   testonly = true
 
-  sources = [
-    "run_all_unittests.cc",
-  ]
+  sources = [ "run_all_unittests.cc" ]
 
   deps = [
     "//base",
@@ -25,9 +23,7 @@
   if (is_ios) {
     deps += [ ":tests_bundle_data" ]
   } else {
-    data = [
-      "data/",
-    ]
+    data = [ "data/" ]
   }
 
   data_deps = [
@@ -65,8 +61,6 @@
     "//services/test/data/simple_page.html",
     "//services/test/data/title1.html",
   ]
-  outputs = [
-    "{{bundle_resources_dir}}/" +
-        "{{source_root_relative_dir}}/{{source_file_part}}",
-  ]
+  outputs = [ "{{bundle_resources_dir}}/" +
+              "{{source_root_relative_dir}}/{{source_file_part}}" ]
 }
diff --git a/services/test/echo/public/mojom/BUILD.gn b/services/test/echo/public/mojom/BUILD.gn
index f0d5d4c..3d10b85 100644
--- a/services/test/echo/public/mojom/BUILD.gn
+++ b/services/test/echo/public/mojom/BUILD.gn
@@ -6,7 +6,5 @@
 
 mojom("mojom") {
   generate_java = true
-  sources = [
-    "echo.mojom",
-  ]
+  sources = [ "echo.mojom" ]
 }
diff --git a/services/test/user_id/public/mojom/BUILD.gn b/services/test/user_id/public/mojom/BUILD.gn
index c6e8f231..615c838e 100644
--- a/services/test/user_id/public/mojom/BUILD.gn
+++ b/services/test/user_id/public/mojom/BUILD.gn
@@ -5,10 +5,6 @@
 import("//mojo/public/tools/bindings/mojom.gni")
 
 mojom("mojom") {
-  sources = [
-    "user_id.mojom",
-  ]
-  public_deps = [
-    "//mojo/public/mojom/base",
-  ]
+  sources = [ "user_id.mojom" ]
+  public_deps = [ "//mojo/public/mojom/base" ]
 }
diff --git a/services/video_capture/BUILD.gn b/services/video_capture/BUILD.gn
index 5cda90b65..b61adf35 100644
--- a/services/video_capture/BUILD.gn
+++ b/services/video_capture/BUILD.gn
@@ -50,9 +50,7 @@
     "//services/viz/public/cpp/gpu",
   ]
 
-  deps = [
-    "//media/capture:capture_switches",
-  ]
+  deps = [ "//media/capture:capture_switches" ]
 
   if (is_chromeos) {
     public_deps += [ "//media/capture/video/chromeos/mojom:cros_camera" ]
diff --git a/services/video_capture/public/cpp/BUILD.gn b/services/video_capture/public/cpp/BUILD.gn
index 83f37e5..8cead26 100644
--- a/services/video_capture/public/cpp/BUILD.gn
+++ b/services/video_capture/public/cpp/BUILD.gn
@@ -17,9 +17,7 @@
     "//services/video_capture/public/mojom",
   ]
 
-  deps = [
-    "//mojo/public/cpp/bindings:bindings",
-  ]
+  deps = [ "//mojo/public/cpp/bindings:bindings" ]
 }
 
 source_set("mocks") {
diff --git a/services/video_capture/public/mojom/BUILD.gn b/services/video_capture/public/mojom/BUILD.gn
index 4807a00..a31b425 100644
--- a/services/video_capture/public/mojom/BUILD.gn
+++ b/services/video_capture/public/mojom/BUILD.gn
@@ -35,7 +35,5 @@
 }
 
 mojom("constants") {
-  sources = [
-    "constants.mojom",
-  ]
+  sources = [ "constants.mojom" ]
 }
diff --git a/services/video_capture/public/uma/BUILD.gn b/services/video_capture/public/uma/BUILD.gn
index 838e7c1..4baaff6 100644
--- a/services/video_capture/public/uma/BUILD.gn
+++ b/services/video_capture/public/uma/BUILD.gn
@@ -8,7 +8,5 @@
     "video_capture_service_event.h",
   ]
 
-  public_deps = [
-    "//base",
-  ]
+  public_deps = [ "//base" ]
 }
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index a4f2f4d..d8478b6 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -93,8 +93,7 @@
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
           "--test-launcher-filter-file=../../testing/buildbot/filters/vulkan.content_browsertests.filter",
-          "--enable-features=UseSkiaRenderer,UiGpuRasterization",
-          "--gr-context-type=vulkan",
+          "--enable-features=UseSkiaRenderer,UiGpuRasterization,Vulkan",
           "--use-vulkan=swiftshader",
           "--enable-oop-rasterization",
           "--enable-gpu-rasterization",
@@ -152,10 +151,9 @@
         "args": [
           "--num-retries=3",
           "--additional-driver-flag=--enable-gpu-rasterization",
-          "--additional-driver-flag=--enable-features=UseSkiaRenderer",
+          "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan",
           "--additional-driver-flag=--force-gpu-rasterization",
           "--additional-driver-flag=--enable-oop-rasterization",
-          "--additional-driver-flag=--gr-context-type=vulkan",
           "--additional-driver-flag=--use-vulkan=swiftshader",
           "--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing",
           "--fuzzy-diff",
@@ -20486,10 +20484,9 @@
         "args": [
           "--num-retries=3",
           "--additional-driver-flag=--enable-gpu-rasterization",
-          "--additional-driver-flag=--enable-features=UseSkiaRenderer",
+          "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan",
           "--additional-driver-flag=--force-gpu-rasterization",
           "--additional-driver-flag=--enable-oop-rasterization",
-          "--additional-driver-flag=--gr-context-type=vulkan",
           "--additional-driver-flag=--use-vulkan=swiftshader",
           "--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing",
           "--fuzzy-diff",
@@ -22338,10 +22335,9 @@
         "args": [
           "--num-retries=3",
           "--additional-driver-flag=--enable-gpu-rasterization",
-          "--additional-driver-flag=--enable-features=UseSkiaRenderer",
+          "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan",
           "--additional-driver-flag=--force-gpu-rasterization",
           "--additional-driver-flag=--enable-oop-rasterization",
-          "--additional-driver-flag=--gr-context-type=vulkan",
           "--additional-driver-flag=--use-vulkan=swiftshader",
           "--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing",
           "--fuzzy-diff",
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index b90e511..38b2c1e9 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -6732,9 +6732,8 @@
       {
         "args": [
           "--use-vulkan=native",
-          "--gr-context-type=vulkan",
           "--disable-vulkan-fallback-to-gl-for-testing",
-          "--enable-features=UseSkiaRenderer",
+          "--enable-features=UseSkiaRenderer,Vulkan",
           "--test-launcher-filter-file=../../testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
@@ -6787,7 +6786,7 @@
           "--browser=android-chromium",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --gr-context-type=vulkan --use-vulkan=native --disable-vulkan-fallback-to-gl-for-testing --enable-features=UseSkiaRenderer --use-cmd-decoder=validating",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-vulkan=native --disable-vulkan-fallback-to-gl-for-testing --enable-features=UseSkiaRenderer,Vulkan --use-cmd-decoder=validating",
           "--dont-restore-color-profile-after-test",
           "--build-revision",
           "${got_revision}",
@@ -10115,8 +10114,7 @@
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
           "--test-launcher-filter-file=../../testing/buildbot/filters/vulkan.content_browsertests.filter",
-          "--enable-features=UseSkiaRenderer,UiGpuRasterization",
-          "--gr-context-type=vulkan",
+          "--enable-features=UseSkiaRenderer,UiGpuRasterization,Vulkan",
           "--use-vulkan=native",
           "--enable-oop-rasterization",
           "--enable-gpu-rasterization",
@@ -10423,12 +10421,11 @@
         "args": [
           "--num-retries=3",
           "--additional-driver-flag=--enable-gpu-rasterization",
-          "--additional-driver-flag=--enable-features=UseSkiaRenderer",
+          "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan",
           "--additional-driver-flag=--use-gl=any",
           "--additional-driver-flag=--force-gpu-rasterization",
           "--additional-driver-flag=--enable-oop-rasterization",
           "--additional-driver-flag=--disable-software-compositing-fallback",
-          "--additional-driver-flag=--gr-context-type=vulkan",
           "--additional-driver-flag=--use-vulkan=native",
           "--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing",
           "--additional-driver-flag=--disable-headless-mode",
@@ -10470,7 +10467,7 @@
           "--browser=release",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --gr-context-type=vulkan --use-vulkan=native --disable-vulkan-fallback-to-gl-for-testing --enable-features=UseSkiaRenderer --use-cmd-decoder=validating",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-vulkan=native --disable-vulkan-fallback-to-gl-for-testing --enable-features=UseSkiaRenderer,Vulkan --use-cmd-decoder=validating",
           "--dont-restore-color-profile-after-test",
           "--build-revision",
           "${got_revision}",
@@ -10516,8 +10513,7 @@
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
           "--test-launcher-filter-file=../../testing/buildbot/filters/vulkan.content_browsertests.filter",
-          "--enable-features=UseSkiaRenderer,UiGpuRasterization",
-          "--gr-context-type=vulkan",
+          "--enable-features=UseSkiaRenderer,UiGpuRasterization,Vulkan",
           "--use-vulkan=native",
           "--enable-oop-rasterization",
           "--enable-gpu-rasterization",
@@ -10824,12 +10820,11 @@
         "args": [
           "--num-retries=3",
           "--additional-driver-flag=--enable-gpu-rasterization",
-          "--additional-driver-flag=--enable-features=UseSkiaRenderer",
+          "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan",
           "--additional-driver-flag=--use-gl=any",
           "--additional-driver-flag=--force-gpu-rasterization",
           "--additional-driver-flag=--enable-oop-rasterization",
           "--additional-driver-flag=--disable-software-compositing-fallback",
-          "--additional-driver-flag=--gr-context-type=vulkan",
           "--additional-driver-flag=--use-vulkan=native",
           "--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing",
           "--additional-driver-flag=--disable-headless-mode",
@@ -10871,7 +10866,7 @@
           "--browser=release",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --gr-context-type=vulkan --use-vulkan=native --disable-vulkan-fallback-to-gl-for-testing --enable-features=UseSkiaRenderer --use-cmd-decoder=validating",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-vulkan=native --disable-vulkan-fallback-to-gl-for-testing --enable-features=UseSkiaRenderer,Vulkan --use-cmd-decoder=validating",
           "--dont-restore-color-profile-after-test",
           "--build-revision",
           "${got_revision}",
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index d659262..817c0f77 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -4206,10 +4206,9 @@
         "args": [
           "--num-retries=3",
           "--additional-driver-flag=--enable-gpu-rasterization",
-          "--additional-driver-flag=--enable-features=UseSkiaRenderer",
+          "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan",
           "--additional-driver-flag=--force-gpu-rasterization",
           "--additional-driver-flag=--enable-oop-rasterization",
-          "--additional-driver-flag=--gr-context-type=vulkan",
           "--additional-driver-flag=--use-vulkan=swiftshader",
           "--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing",
           "--fuzzy-diff",
@@ -7791,10 +7790,9 @@
         "args": [
           "--num-retries=3",
           "--additional-driver-flag=--enable-gpu-rasterization",
-          "--additional-driver-flag=--enable-features=UseSkiaRenderer",
+          "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan",
           "--additional-driver-flag=--force-gpu-rasterization",
           "--additional-driver-flag=--enable-oop-rasterization",
-          "--additional-driver-flag=--gr-context-type=vulkan",
           "--additional-driver-flag=--use-vulkan=swiftshader",
           "--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing",
           "--fuzzy-diff",
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 142198e..2a4d8cb 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -11907,8 +11907,7 @@
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
           "--test-launcher-filter-file=../../testing/buildbot/filters/vulkan.content_browsertests.filter",
-          "--enable-features=UseSkiaRenderer,UiGpuRasterization",
-          "--gr-context-type=vulkan",
+          "--enable-features=UseSkiaRenderer,UiGpuRasterization,Vulkan",
           "--use-vulkan=swiftshader",
           "--enable-oop-rasterization",
           "--enable-gpu-rasterization",
diff --git a/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter b/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
index d1769f40..d9588a2 100644
--- a/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
+++ b/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
@@ -8,14 +8,10 @@
 -MSE_*ClearKey/EncryptedMediaTest.*
 -SRC_*ClearKey/EncryptedMediaTest.*
 
-# SkiaOutputSurfaceImplOnGpu::MakeCurrent Fails. https://crbug.com/1012282
+# Non-Existent Mailbox Error During Deletion. https://crbug.com/1043934
 -*CompositorImplLowEndBrowserTest.CompositorImplDropsResourcesOnBackground*
 
 # FeatureList Flakes. https://crbug.com/1012372
 -NavigationBrowserTest.HistoryBackCancelPendingNavigationUserGesture/1
 -SitePerProcessBrowserTest.*
--TextFragmentAnchorBrowserTest.DisabledOnScriptNavigation/0
-
-# crbug.com/1037939
--BackForwardCacheBrowserTest.VideoSuspendAndResume
--P/CompositorImplBrowserTestRefreshRate.VideoPreference/0
+-TextFragmentAnchorBrowserTest.DisabledOnScriptNavigation/0
\ No newline at end of file
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 83b75e2..d454943 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -1756,12 +1756,11 @@
         'args': [
           '--num-retries=3',
           '--additional-driver-flag=--enable-gpu-rasterization',
-          '--additional-driver-flag=--enable-features=UseSkiaRenderer',
+          '--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan',
           '--additional-driver-flag=--use-gl=any',
           '--additional-driver-flag=--force-gpu-rasterization',
           '--additional-driver-flag=--enable-oop-rasterization',
           '--additional-driver-flag=--disable-software-compositing-fallback',
-          '--additional-driver-flag=--gr-context-type=vulkan',
           '--additional-driver-flag=--use-vulkan=native',
           '--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing',
           '--additional-driver-flag=--disable-headless-mode',
@@ -2028,8 +2027,7 @@
           '--test-launcher-bot-mode',
           '--test-launcher-jobs=1',
           '--test-launcher-filter-file=../../testing/buildbot/filters/vulkan.content_browsertests.filter',
-          '--enable-features=UseSkiaRenderer,UiGpuRasterization',
-          '--gr-context-type=vulkan',
+          '--enable-features=UseSkiaRenderer,UiGpuRasterization,Vulkan',
           '--use-vulkan=native',
           '--enable-oop-rasterization',
           '--enable-gpu-rasterization',
@@ -2050,8 +2048,7 @@
           '--test-launcher-bot-mode',
           '--test-launcher-jobs=1',
           '--test-launcher-filter-file=../../testing/buildbot/filters/vulkan.content_browsertests.filter',
-          '--enable-features=UseSkiaRenderer,UiGpuRasterization',
-          '--gr-context-type=vulkan',
+          '--enable-features=UseSkiaRenderer,UiGpuRasterization,Vulkan',
           '--use-vulkan=swiftshader',
           '--enable-oop-rasterization',
           '--enable-gpu-rasterization',
@@ -2222,9 +2219,8 @@
       'vulkan_content_browsertests': {
         'args': [
           '--use-vulkan=native',
-          '--gr-context-type=vulkan',
           '--disable-vulkan-fallback-to-gl-for-testing',
-          '--enable-features=UseSkiaRenderer',
+          '--enable-features=UseSkiaRenderer,Vulkan',
           '--test-launcher-filter-file=../../testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter',
         ],
         'swarming': {
@@ -2246,7 +2242,7 @@
           '${got_revision}',
           '--test-machine-name',
           '${buildername}',
-          '--extra-browser-args=--gr-context-type=vulkan --use-vulkan=native --disable-vulkan-fallback-to-gl-for-testing --enable-features=UseSkiaRenderer --use-cmd-decoder=validating',
+          '--extra-browser-args=--use-vulkan=native --disable-vulkan-fallback-to-gl-for-testing --enable-features=UseSkiaRenderer,Vulkan --use-cmd-decoder=validating',
         ],
         'precommit_args': [
           # Gerrit issue ID
@@ -3163,10 +3159,9 @@
         'args': [
           '--num-retries=3',
           '--additional-driver-flag=--enable-gpu-rasterization',
-          '--additional-driver-flag=--enable-features=UseSkiaRenderer',
+          '--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan',
           '--additional-driver-flag=--force-gpu-rasterization',
           '--additional-driver-flag=--enable-oop-rasterization',
-          '--additional-driver-flag=--gr-context-type=vulkan',
           '--additional-driver-flag=--use-vulkan=swiftshader',
           '--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing',
           '--fuzzy-diff',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a2dd426..18c60337 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3736,6 +3736,22 @@
             ]
         }
     ],
+    "MayBlockWithoutDelay": [
+        {
+            "platforms": [
+                "windows",
+                "mac"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "MayBlockWithoutDelay"
+                    ]
+                }
+            ]
+        }
+    ],
     "MediaCapabilitiesWithParameters": [
         {
             "platforms": [
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
index 9b0699c..c5072a8 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
@@ -24,6 +24,8 @@
     // It is provided here from manual lookups. Note that licenseUrl must provide textual content
     // rather than be an html page.
     final def FALLBACK_PROPERTIES = [
+        'androidx_multidex_multidex': new PropertyOverride(
+            url: 'https://maven.google.com/androidx/multidex/multidex/2.0.0/multidex-2.0.0.aar'),
         'com_github_kevinstern_software_and_algorithms': new PropertyOverride(
             licenseUrl: "https://raw.githubusercontent.com/KevinStern/software-and-algorithms/master/LICENSE"),
         'com_google_auto_auto_common': new PropertyOverride(
diff --git a/third_party/android_deps/libs/androidx_multidex_multidex/README.chromium b/third_party/android_deps/libs/androidx_multidex_multidex/README.chromium
index 3d9cf78..32a80ff 100644
--- a/third_party/android_deps/libs/androidx_multidex_multidex/README.chromium
+++ b/third_party/android_deps/libs/androidx_multidex_multidex/README.chromium
@@ -1,13 +1,14 @@
 Name: Android Multi-Dex Library
 Short Name: multidex
-URL: 
+URL: https://maven.google.com/androidx/multidex/multidex/2.0.0/multidex-2.0.0.aar
 Version: 2.0.0
 License: Apache Version 2.0
 License File: LICENSE
 Security Critical: yes
 
 Description:
-Library for legacy multi-dex support
+Library for legacy multi-dex support.
+https://developer.android.com/jetpack/androidx/releases/multidex
 
 Local Modifications:
 No modifications.
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 86a5e0c..6dfd12d 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -236,7 +236,6 @@
     "platform/web_mixed_content_context_type.h",
     "platform/web_navigation_body_loader.h",
     "platform/web_network_state_notifier.h",
-    "platform/web_point.h",
     "platform/web_prerender.h",
     "platform/web_prerendering_support.h",
     "platform/web_prescient_networking.h",
diff --git a/third_party/blink/public/common/frame/frame_policy.h b/third_party/blink/public/common/frame/frame_policy.h
index 01ed00a..64715d8 100644
--- a/third_party/blink/public/common/frame/frame_policy.h
+++ b/third_party/blink/public/common/frame/frame_policy.h
@@ -44,8 +44,8 @@
   // However, in certain cases where the initiator of the navigation is not the
   // document itself (e.g., a parent document), the FrameOwner element should be
   // checked for "downloads" flag. If this boolean is false then navigations
-  // leading to downloads should be blocked unless they have user gesture. Note:
-  // this flag is currently only set if the frame is sandboxed for downloads.
+  // leading to downloads should be blocked. Note: this flag is currently only
+  // set if the frame is sandboxed for downloads.
   bool allowed_to_download;
 };
 
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 069f2ce..4d65747b 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -115,12 +115,12 @@
     "service_worker/dispatch_fetch_event_params.mojom",
     "service_worker/navigation_preload_state.mojom",
     "service_worker/service_worker_client.mojom",
+    "service_worker/service_worker_container_type.mojom",
     "service_worker/service_worker_database.mojom",
     "service_worker/service_worker_error_type.mojom",
     "service_worker/service_worker_event_status.mojom",
     "service_worker/service_worker_fetch_response_callback.mojom",
     "service_worker/service_worker_installed_scripts_manager.mojom",
-    "service_worker/service_worker_provider_type.mojom",
     "service_worker/service_worker_registration_options.mojom",
     "service_worker/service_worker_state.mojom",
     "service_worker/service_worker_stream_handle.mojom",
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom b/third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom
new file mode 100644
index 0000000..9faffa9
--- /dev/null
+++ b/third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom
@@ -0,0 +1,28 @@
+// 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.
+
+module blink.mojom;
+
+// ServiceWorkerContainer is the spec concept that is exposed to a service
+// worker client or a service worker itself, and ServiceWorkerContainerHost is
+// the host of the container.
+// https://w3c.github.io/ServiceWorker/#serviceworkercontainer-interface
+//
+// This enum identifies which context ServiceWorkerContainer(Host) works for.
+// Currently, only windows support web-exposed ServiceWorkerContainer
+// (see https://crbug.com/371690), but ServiceWorkerContainerHost is created for
+// them all to provide service worker functionalities. Therefore, this enum also
+// defines the types for workers that don't support ServiceWorkerContainer yet.
+// See header comments on ServiceWorkerContainerHost for details.
+enum ServiceWorkerContainerType {
+  kUnknown,
+
+  // For service worker clients.
+  kForWindow,
+  kForDedicatedWorker,
+  kForSharedWorker,
+
+  // For service workers.
+  kForServiceWorker,
+};
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
index bb2b910..ea021fc 100644
--- a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
+++ b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
@@ -9,8 +9,8 @@
 import "third_party/blink/public/mojom/cache_storage/cache_storage.mojom";
 import "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom";
 import "third_party/blink/public/mojom/service_worker/service_worker_container.mojom";
+import "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom";
 import "third_party/blink/public/mojom/service_worker/service_worker_object.mojom";
-import "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom";
 
 // The name of the InterfaceProviderSpec in service manifests used by the
 // frame tree to expose service-worker-specific interfaces between renderer
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom b/third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom
deleted file mode 100644
index d7c94300..0000000
--- a/third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom
+++ /dev/null
@@ -1,22 +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.
-
-module blink.mojom;
-
-// Identifies the type of a service worker provider.
-//
-// A service worker provider attaches to a service worker client or a service
-// worker itself and "provides" it with functionality related to service
-// workers. See the main documentation in content::ServiceWorkerProviderContext.
-enum ServiceWorkerProviderType {
-  kUnknown,
-
-  // For service worker clients.
-  kForWindow,
-  kForDedicatedWorker,
-  kForSharedWorker,
-
-  // For service workers.
-  kForServiceWorker,
-};
diff --git a/third_party/blink/public/platform/viewport_intersection_state.h b/third_party/blink/public/platform/viewport_intersection_state.h
index 93a251ef..59691d0 100644
--- a/third_party/blink/public/platform/viewport_intersection_state.h
+++ b/third_party/blink/public/platform/viewport_intersection_state.h
@@ -6,8 +6,8 @@
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_VIEWPORT_INTERSECTION_STATE_H_
 
 #include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
+#include "ui/gfx/geometry/point.h"
 
 namespace blink {
 
@@ -44,7 +44,7 @@
   }
 
   // Child frame's offset from the root frame.
-  WebPoint viewport_offset;
+  gfx::Point viewport_offset;
   // Portion of the child frame which is within the root frame's scrolling
   // viewport, in the coordinate system of the child frame.
   WebRect viewport_intersection;
diff --git a/third_party/blink/public/platform/web_cursor_info.h b/third_party/blink/public/platform/web_cursor_info.h
index 478f20b..4cb01731 100644
--- a/third_party/blink/public/platform/web_cursor_info.h
+++ b/third_party/blink/public/platform/web_cursor_info.h
@@ -31,9 +31,10 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_CURSOR_INFO_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_CURSOR_INFO_H_
 
-#include "third_party/blink/public/platform/web_point.h"
+#include "third_party/blink/public/platform/web_common.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/cursor/types/cursor_types.h"
+#include "ui/gfx/geometry/point.h"
 
 #ifdef WIN32
 typedef struct HICON__* HICON;
@@ -46,7 +47,7 @@
 
 struct WebCursorInfo {
   ui::CursorType type;
-  WebPoint hot_spot;
+  gfx::Point hot_spot;
   float image_scale_factor;
   SkBitmap custom_image;
 
diff --git a/third_party/blink/public/platform/web_point.h b/third_party/blink/public/platform/web_point.h
deleted file mode 100644
index 43fff47..0000000
--- a/third_party/blink/public/platform/web_point.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_POINT_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_POINT_H_
-
-#include "third_party/blink/public/platform/web_common.h"
-#include "ui/gfx/geometry/point.h"
-
-#if INSIDE_BLINK
-#include "third_party/blink/renderer/platform/geometry/int_point.h"  // nogncheck
-#endif
-
-namespace blink {
-
-struct WebPoint {
-  int x;
-  int y;
-
-  WebPoint() : x(0), y(0) {}
-
-  WebPoint(int x, int y) : x(x), y(y) {}
-
-#if INSIDE_BLINK
-  WebPoint(const IntPoint& p) : x(p.X()), y(p.Y()) {}
-
-  WebPoint& operator=(const IntPoint& p) {
-    x = p.X();
-    y = p.Y();
-    return *this;
-  }
-
-  operator IntPoint() const { return IntPoint(x, y); }
-
-  explicit WebPoint(const gfx::Point& p) : x(p.x()), y(p.y()) {}
-
-  explicit operator gfx::Point() const { return gfx::Point(x, y); }
-#else
-  WebPoint(const gfx::Point& p) : x(p.x()), y(p.y()) {}
-
-  WebPoint& operator=(const gfx::Point& p) {
-    x = p.x();
-    y = p.y();
-    return *this;
-  }
-
-  operator gfx::Point() const { return gfx::Point(x, y); }
-#endif
-};
-
-inline bool operator==(const WebPoint& a, const WebPoint& b) {
-  return a.x == b.x && a.y == b.y;
-}
-
-inline bool operator!=(const WebPoint& a, const WebPoint& b) {
-  return !(a == b);
-}
-
-}  // namespace blink
-
-#endif
diff --git a/third_party/blink/public/web/mac/web_substring_util.h b/third_party/blink/public/web/mac/web_substring_util.h
index d15128d..f6cf37b 100644
--- a/third_party/blink/public/web/mac/web_substring_util.h
+++ b/third_party/blink/public/web/mac/web_substring_util.h
@@ -39,11 +39,14 @@
 class NSAttributedString;
 #endif
 
+namespace gfx {
+class Point;
+}  // namespace gfx
+
 namespace blink {
 
 class WebFrameWidget;
 class WebLocalFrame;
-struct WebPoint;
 
 class WebSubstringUtil {
  public:
@@ -51,8 +54,10 @@
   // the given point inside the given WebFrameWidget or nil on error.
   // Upon return, |baselinePoint| is set to the left baseline point in
   // AppKit coordinates.
-  BLINK_EXPORT static NSAttributedString*
-  AttributedWordAtPoint(WebFrameWidget*, WebPoint, WebPoint& baseline_point);
+  BLINK_EXPORT static NSAttributedString* AttributedWordAtPoint(
+      WebFrameWidget*,
+      gfx::Point,
+      gfx::Point& baseline_point);
 
   // Returns an autoreleased NSAttributedString that is a substring of the
   // Frame at the given range, or nil on error.
@@ -67,7 +72,7 @@
       WebLocalFrame*,
       size_t location,
       size_t length,
-      WebPoint* baseline_point);
+      gfx::Point* baseline_point);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h
index 74e0b59..8e0d182 100644
--- a/third_party/blink/public/web/web_ax_object.h
+++ b/third_party/blink/public/web/web_ax_object.h
@@ -33,7 +33,6 @@
 
 #include <memory>
 #include "third_party/blink/public/platform/web_common.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_private_ptr.h"
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/public/platform/web_vector.h"
@@ -42,6 +41,10 @@
 
 class SkMatrix44;
 
+namespace gfx {
+class Point;
+}
+
 namespace blink {
 
 class AXObject;
@@ -51,7 +54,6 @@
 class WebString;
 class WebURL;
 struct WebFloatRect;
-struct WebPoint;
 struct WebRect;
 struct WebSize;
 
@@ -181,7 +183,7 @@
   BLINK_EXPORT double EstimatedLoadingProgress() const;
   BLINK_EXPORT int HeadingLevel() const;
   BLINK_EXPORT int HierarchicalLevel() const;
-  BLINK_EXPORT WebAXObject HitTest(const WebPoint&) const;
+  BLINK_EXPORT WebAXObject HitTest(const gfx::Point&) const;
   // Get the WebAXObject's bounds in frame-relative coordinates as a WebRect.
   BLINK_EXPORT WebRect GetBoundsInFrameCoordinates() const;
   BLINK_EXPORT WebString KeyboardShortcut() const;
@@ -309,7 +311,7 @@
           ax::mojom::ScrollBehavior::kDoNotScrollIfVisible) const;
   // Scroll this object to a given point in global coordinates of the top-level
   // window.
-  BLINK_EXPORT bool ScrollToGlobalPoint(const WebPoint&) const;
+  BLINK_EXPORT bool ScrollToGlobalPoint(const gfx::Point&) const;
 
   // For a table
   BLINK_EXPORT int AriaColumnCount() const;
@@ -354,10 +356,10 @@
 
   // Scrollable containers.
   BLINK_EXPORT bool IsScrollableContainer() const;
-  BLINK_EXPORT WebPoint GetScrollOffset() const;
-  BLINK_EXPORT WebPoint MinimumScrollOffset() const;
-  BLINK_EXPORT WebPoint MaximumScrollOffset() const;
-  BLINK_EXPORT void SetScrollOffset(const WebPoint&) const;
+  BLINK_EXPORT gfx::Point GetScrollOffset() const;
+  BLINK_EXPORT gfx::Point MinimumScrollOffset() const;
+  BLINK_EXPORT gfx::Point MaximumScrollOffset() const;
+  BLINK_EXPORT void SetScrollOffset(const gfx::Point&) const;
 
   // aria-dropeffect is deprecated in WAI-ARIA 1.1
   BLINK_EXPORT void Dropeffects(
diff --git a/third_party/blink/public/web/web_context_menu_data.h b/third_party/blink/public/web/web_context_menu_data.h
index 8283e8c..94a908d 100644
--- a/third_party/blink/public/web/web_context_menu_data.h
+++ b/third_party/blink/public/web/web_context_menu_data.h
@@ -35,12 +35,12 @@
 #include "third_party/blink/public/common/context_menu_data/input_field_type.h"
 #include "third_party/blink/public/common/context_menu_data/media_type.h"
 #include "third_party/blink/public/common/input/web_menu_source_type.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/web_menu_item_info.h"
+#include "ui/gfx/geometry/point.h"
 
 namespace blink {
 
@@ -51,7 +51,7 @@
   ContextMenuDataMediaType media_type;
 
   // The x and y position of the mouse pointer (relative to the webview).
-  WebPoint mouse_position;
+  gfx::Point mouse_position;
 
   // The absolute URL of the link that is in context.
   WebURL link_url;
diff --git a/third_party/blink/public/web/web_device_emulation_params.h b/third_party/blink/public/web/web_device_emulation_params.h
index 833e6ae..f5f1088b 100644
--- a/third_party/blink/public/web/web_device_emulation_params.h
+++ b/third_party/blink/public/web/web_device_emulation_params.h
@@ -7,9 +7,9 @@
 
 #include "base/optional.h"
 #include "third_party/blink/public/common/screen_orientation/web_screen_orientation_type.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_size.h"
+#include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point_f.h"
 
 namespace blink {
@@ -27,7 +27,7 @@
 
   // Position of view on the screen. Missing position means using default value:
   // original one for kDesktop screen position, (0, 0) for kMobile.
-  base::Optional<WebPoint> view_position;
+  base::Optional<gfx::Point> view_position;
 
   // Emulated view size. A width or height of 0 means no override in that
   // dimension, but the other can still be applied. When both are 0, then the
diff --git a/third_party/blink/public/web/web_history_item.h b/third_party/blink/public/web/web_history_item.h
index 30eb74d..f4c2f0a 100644
--- a/third_party/blink/public/web/web_history_item.h
+++ b/third_party/blink/public/web/web_history_item.h
@@ -48,7 +48,6 @@
 class WebHTTPBody;
 class WebString;
 class WebSerializedScriptValue;
-struct WebPoint;
 template <typename T>
 class WebVector;
 
@@ -88,8 +87,8 @@
   BLINK_EXPORT gfx::PointF VisualViewportScrollOffset() const;
   BLINK_EXPORT void SetVisualViewportScrollOffset(const gfx::PointF&);
 
-  BLINK_EXPORT WebPoint GetScrollOffset() const;
-  BLINK_EXPORT void SetScrollOffset(const WebPoint&);
+  BLINK_EXPORT gfx::Point GetScrollOffset() const;
+  BLINK_EXPORT void SetScrollOffset(const gfx::Point&);
 
   BLINK_EXPORT float PageScaleFactor() const;
   BLINK_EXPORT void SetPageScaleFactor(float);
diff --git a/third_party/blink/public/web/web_hit_test_result.h b/third_party/blink/public/web/web_hit_test_result.h
index bf544204..abb01705 100644
--- a/third_party/blink/public/web/web_hit_test_result.h
+++ b/third_party/blink/public/web/web_hit_test_result.h
@@ -27,6 +27,7 @@
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_HIT_TEST_RESULT_H_
 
 #include "third_party/blink/public/platform/web_private_ptr.h"
+#include "ui/gfx/geometry/point.h"
 
 namespace blink {
 
@@ -35,7 +36,6 @@
 class WebHitTestResultPrivate;
 class WebNode;
 class WebURL;
-struct WebPoint;
 
 // Properties of a hit test result, i.e. properties of the nodes at a given
 // point (the hit point) on the page. Both urls may be populated at the same
@@ -54,11 +54,11 @@
   BLINK_EXPORT WebNode GetNode() const;
 
   // Coordinates of the point that was hit. Relative to the node.
-  BLINK_EXPORT WebPoint LocalPoint() const;
+  BLINK_EXPORT gfx::Point LocalPoint() const;
 
   // Coordinates of the point that was hit. Relative to the node, but with
   // ContentBoxOffset removed if the node has box layout.
-  BLINK_EXPORT WebPoint LocalPointWithoutContentBoxOffset() const;
+  BLINK_EXPORT gfx::Point LocalPointWithoutContentBoxOffset() const;
 
   // Returns whether the content box contains the hit test point.
   BLINK_EXPORT bool ContentBoxContainsPoint() const;
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 18be960c..83d0923 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -36,6 +36,10 @@
 #include "third_party/blink/public/web/web_text_direction.h"
 #include "v8/include/v8.h"
 
+namespace gfx {
+class Point;
+}  // namespace gfx
+
 namespace blink {
 
 class FrameScheduler;
@@ -66,7 +70,6 @@
 struct WebContentSecurityPolicyViolation;
 struct WebIsolatedWorldInfo;
 struct MediaPlayerAction;
-struct WebPoint;
 struct WebPrintParams;
 struct WebPrintPresetOptions;
 struct WebScriptSource;
@@ -416,7 +419,7 @@
   // Returns the index of a character in the Frame's text stream at the given
   // point. The point is in the viewport coordinate space. Will return
   // WTF::notFound if the point is invalid.
-  virtual size_t CharacterIndexForPoint(const WebPoint&) const = 0;
+  virtual size_t CharacterIndexForPoint(const gfx::Point&) const = 0;
 
   // Supports commands like Undo, Redo, Cut, Copy, Paste, SelectAll,
   // Unselect, etc. See EditorCommand.cpp for the full list of supported
@@ -450,7 +453,8 @@
   virtual bool SelectWordAroundCaret() = 0;
 
   // DEPRECATED: Use moveRangeSelection.
-  virtual void SelectRange(const WebPoint& base, const WebPoint& extent) = 0;
+  virtual void SelectRange(const gfx::Point& base,
+                           const gfx::Point& extent) = 0;
 
   enum HandleVisibilityBehavior {
     // Hide handle(s) in the new selection.
@@ -473,10 +477,10 @@
   // |TextGranularity| represents character wrapping granularity. If
   // WordGranularity is set, WebFrame extends selection to wrap word.
   virtual void MoveRangeSelection(
-      const WebPoint& base,
-      const WebPoint& extent,
+      const gfx::Point& base,
+      const gfx::Point& extent,
       WebFrame::TextGranularity = kCharacterGranularity) = 0;
-  virtual void MoveCaretSelection(const WebPoint&) = 0;
+  virtual void MoveCaretSelection(const gfx::Point&) = 0;
 
   virtual bool SetEditableSelectionOffsets(int start, int end) = 0;
   virtual bool SetCompositionFromExistingText(
@@ -490,7 +494,7 @@
   // Moves the selection extent point. This function does not allow the
   // selection to collapse. If the new extent is set to the same position as
   // the current base, this function will do nothing.
-  virtual void MoveRangeSelectionExtent(const WebPoint&) = 0;
+  virtual void MoveRangeSelectionExtent(const gfx::Point&) = 0;
   // Replaces the selection with the input string.
   virtual void ReplaceSelection(const WebString&) = 0;
   // Deletes text before and after the current cursor position, excluding the
@@ -573,7 +577,7 @@
 
   // Copy to the clipboard the image located at a particular point in visual
   // viewport coordinates.
-  virtual void CopyImageAtForTesting(const WebPoint&) = 0;
+  virtual void CopyImageAtForTesting(const gfx::Point&) = 0;
 
   // Events --------------------------------------------------------------
 
@@ -755,7 +759,7 @@
 
   // Performs the specified media player action on the media element at the
   // given location.
-  virtual void PerformMediaPlayerAction(const WebPoint&,
+  virtual void PerformMediaPlayerAction(const gfx::Point&,
                                         const MediaPlayerAction&) = 0;
 
   virtual void SetLifecycleState(mojom::FrameLifecycleState state) = 0;
diff --git a/third_party/blink/public/web/web_page_popup.h b/third_party/blink/public/web/web_page_popup.h
index f6551c6..3d32d4a 100644
--- a/third_party/blink/public/web/web_page_popup.h
+++ b/third_party/blink/public/web/web_page_popup.h
@@ -53,7 +53,7 @@
   // Returns a WebPagePopup which is self-referencing. It's self-reference will
   // be released when the popup is closed via Close().
   BLINK_EXPORT static WebPagePopup* Create(WebPagePopupClient*);
-  virtual WebPoint PositionRelativeToOwner() = 0;
+  virtual gfx::Point PositionRelativeToOwner() = 0;
 
   // The popup's accessibility tree is connected to the main document's
   // accessibility tree. Access to the popup document is needed to ensure the
diff --git a/third_party/blink/public/web/web_plugin.h b/third_party/blink/public/web/web_plugin.h
index 17ee19a..49d08e7c 100644
--- a/third_party/blink/public/web/web_plugin.h
+++ b/third_party/blink/public/web/web_plugin.h
@@ -55,7 +55,6 @@
 struct WebCursorInfo;
 struct WebPrintParams;
 struct WebPrintPresetOptions;
-struct WebPoint;
 struct WebRect;
 struct WebURLError;
 template <typename T>
@@ -216,7 +215,7 @@
   virtual void DeleteSurroundingTextInCodePoints(int before, int after) {}
   // If the given position is over a link, returns the absolute url.
   // Otherwise an empty url is returned.
-  virtual WebURL LinkAtPosition(const WebPoint& position) const {
+  virtual WebURL LinkAtPosition(const gfx::Point& position) const {
     return WebURL();
   }
 
diff --git a/third_party/blink/public/web/web_plugin_container.h b/third_party/blink/public/web/web_plugin_container.h
index 17f39dc6..092fab6 100644
--- a/third_party/blink/public/web/web_plugin_container.h
+++ b/third_party/blink/public/web/web_plugin_container.h
@@ -33,6 +33,7 @@
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_PLUGIN_CONTAINER_H_
 
 #include "third_party/blink/public/platform/web_common.h"
+#include "ui/gfx/geometry/point.h"
 #include "v8/include/v8.h"
 
 namespace cc {
@@ -48,7 +49,6 @@
 class WebURL;
 class WebURLRequest;
 class WebDOMMessageEvent;
-struct WebPoint;
 struct WebRect;
 
 class WebPluginContainer {
@@ -121,10 +121,10 @@
   virtual void SetWantsWheelEvents(bool) = 0;
 
   // Converts root frame's coordinates to plugin's local coordinates.
-  virtual WebPoint RootFrameToLocalPoint(const WebPoint&) = 0;
+  virtual gfx::Point RootFrameToLocalPoint(const gfx::Point&) = 0;
 
   // Converts plugin's local coordinate to root frame's coordinates.
-  virtual WebPoint LocalToRootFramePoint(const WebPoint&) = 0;
+  virtual gfx::Point LocalToRootFramePoint(const gfx::Point&) = 0;
 
   // Returns the plugin this container owns. This plugin will be
   // automatically destroyed when the container is destroyed.
diff --git a/third_party/blink/public/web/web_remote_frame_client.h b/third_party/blink/public/web/web_remote_frame_client.h
index 723f07c..cdb2384 100644
--- a/third_party/blink/public/web/web_remote_frame_client.h
+++ b/third_party/blink/public/web/web_remote_frame_client.h
@@ -37,7 +37,7 @@
   virtual void Navigate(const WebURLRequest& request,
                         bool should_replace_current_entry,
                         bool is_opener_navigation,
-                        bool has_download_sandbox_flag,
+                        bool initiator_frame_has_download_sandbox_flag,
                         bool blocking_downloads_in_sandbox_enabled,
                         bool initiator_frame_is_ad,
                         mojo::ScopedMessagePipeHandle blob_url_token) {}
diff --git a/third_party/blink/public/web/web_widget.h b/third_party/blink/public/web/web_widget.h
index d4aa837..7878ee1 100644
--- a/third_party/blink/public/web/web_widget.h
+++ b/third_party/blink/public/web/web_widget.h
@@ -40,7 +40,6 @@
 #include "third_party/blink/public/common/input/web_menu_source_type.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/public/platform/web_text_input_info.h"
diff --git a/third_party/blink/public/web/web_widget_client.h b/third_party/blink/public/web/web_widget_client.h
index cede75f..41eaae6 100644
--- a/third_party/blink/public/web/web_widget_client.h
+++ b/third_party/blink/public/web/web_widget_client.h
@@ -45,7 +45,6 @@
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_drag_operation.h"
 #include "third_party/blink/public/platform/web_intrinsic_sizing_info.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_screen_info.h"
 #include "third_party/blink/public/platform/web_touch_action.h"
@@ -253,7 +252,7 @@
                              const gfx::Point& drag_image_offset) {}
 
   // Double tap zooms a rect in the main-frame renderer.
-  virtual void AnimateDoubleTapZoomInMainFrame(const blink::WebPoint& point,
+  virtual void AnimateDoubleTapZoomInMainFrame(const gfx::Point& point,
                                                const blink::WebRect& bounds) {}
 
   // Find in page zooms a rect in the main-frame renderer.
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_util.cc b/third_party/blink/renderer/core/animation/scroll_timeline_util.cc
index a9dc5ac0..c694177 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline_util.cc
+++ b/third_party/blink/renderer/core/animation/scroll_timeline_util.cc
@@ -15,7 +15,7 @@
 
 namespace scroll_timeline_util {
 
-std::unique_ptr<CompositorScrollTimeline> ToCompositorScrollTimeline(
+scoped_refptr<CompositorScrollTimeline> ToCompositorScrollTimeline(
     AnimationTimeline* timeline) {
   if (!timeline || IsA<DocumentTimeline>(timeline))
     return nullptr;
@@ -51,7 +51,7 @@
     end_scroll_offset = resolved_end_scroll_offset;
   }
 
-  return std::make_unique<CompositorScrollTimeline>(
+  return base::MakeRefCounted<CompositorScrollTimeline>(
       element_id, orientation, start_scroll_offset, end_scroll_offset,
       time_range.GetAsDouble(), scroll_timeline->GetFillMode());
 }
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_util.h b/third_party/blink/renderer/core/animation/scroll_timeline_util.h
index a111127..497bbea 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline_util.h
+++ b/third_party/blink/renderer/core/animation/scroll_timeline_util.h
@@ -22,7 +22,7 @@
 
 // Converts the input timeline to the compositor representation of a
 // ScrollTimeline. Returns nullptr if the input is not a ScrollTimeline.
-std::unique_ptr<CompositorScrollTimeline> CORE_EXPORT
+scoped_refptr<CompositorScrollTimeline> CORE_EXPORT
 ToCompositorScrollTimeline(AnimationTimeline*);
 
 // Retrieves the 'scroll' compositor element id for the input node, or
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc b/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc
index 6d05f7e..c9789b55 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc
+++ b/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc
@@ -51,7 +51,7 @@
   ScrollTimeline* timeline =
       ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
 
-  std::unique_ptr<CompositorScrollTimeline> compositor_timeline =
+  scoped_refptr<CompositorScrollTimeline> compositor_timeline =
       ToCompositorScrollTimeline(timeline);
   EXPECT_EQ(compositor_timeline->GetActiveIdForTest(), base::nullopt);
   EXPECT_EQ(compositor_timeline->GetPendingIdForTest(), element_id);
@@ -85,7 +85,7 @@
       &GetDocument(), scroll_source, ScrollTimeline::Block, start_scroll_offset,
       end_scroll_offset, 100, Timing::FillMode::NONE);
 
-  std::unique_ptr<CompositorScrollTimeline> compositor_timeline =
+  scoped_refptr<CompositorScrollTimeline> compositor_timeline =
       ToCompositorScrollTimeline(timeline);
   ASSERT_TRUE(compositor_timeline.get());
   EXPECT_EQ(compositor_timeline->GetPendingIdForTest(), base::nullopt);
@@ -103,7 +103,7 @@
   ScrollTimeline* timeline =
       ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
 
-  std::unique_ptr<CompositorScrollTimeline> compositor_timeline =
+  scoped_refptr<CompositorScrollTimeline> compositor_timeline =
       ToCompositorScrollTimeline(timeline);
   EXPECT_TRUE(compositor_timeline.get());
   // Here we just want to test the start/end scroll offset.
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 9713f25..b9ae2a38 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -1874,8 +1874,8 @@
   // expect the result to be in the coordinate system of the local root frame.
   // Either the method should be renamed to something which communicates that,
   // or callers should be updated to expect actual top-level frame coordinates.
-  rect.Move(-PhysicalOffset(
-      GetDocument().GetFrame()->LocalFrameRoot().RemoteViewportOffset()));
+  rect.Move(-PhysicalOffset(IntPoint(
+      GetDocument().GetFrame()->LocalFrameRoot().RemoteViewportOffset())));
 
   IntRect visible_rect = PixelSnappedIntRect(rect);
   // If the rect is in the coordinates of the main frame, then it should
diff --git a/third_party/blink/renderer/core/editing/web_substring_util.mm b/third_party/blink/renderer/core/editing/web_substring_util.mm
index 327b82a..683560a 100644
--- a/third_party/blink/renderer/core/editing/web_substring_util.mm
+++ b/third_party/blink/renderer/core/editing/web_substring_util.mm
@@ -138,9 +138,9 @@
   return [string autorelease];
 }
 
-WebPoint GetBaselinePoint(LocalFrameView* frame_view,
-                          const EphemeralRange& range,
-                          NSAttributedString* string) {
+gfx::Point GetBaselinePoint(LocalFrameView* frame_view,
+                            const EphemeralRange& range,
+                            NSAttributedString* string) {
   IntRect string_rect = frame_view->FrameToViewport(FirstRectForRange(range));
   IntPoint string_point = string_rect.MinXMaxYCorner();
 
@@ -157,8 +157,8 @@
 
 NSAttributedString* WebSubstringUtil::AttributedWordAtPoint(
     WebFrameWidget* frame_widget,
-    WebPoint point,
-    WebPoint& baseline_point) {
+    gfx::Point point,
+    gfx::Point& baseline_point) {
   HitTestResult result = static_cast<WebFrameWidgetBase*>(frame_widget)
                              ->CoreHitTestResultAt(IntPoint(point));
 
@@ -195,7 +195,7 @@
     WebLocalFrame* web_frame,
     size_t location,
     size_t length,
-    WebPoint* baseline_point) {
+    gfx::Point* baseline_point) {
   LocalFrame* frame = To<WebLocalFrameImpl>(web_frame)->GetFrame();
   if (frame->View()->NeedsLayout())
     frame->View()->UpdateLayout();
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
index afa7e435..e6393fca 100644
--- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
@@ -384,7 +384,7 @@
   WebFloatRect rect(point_in_local_root.x(), point_in_local_root.y(), 0, 0);
   web_local_frame_impl_->FrameWidgetImpl()->Client()->ConvertWindowToViewport(
       &rect);
-  WebPoint point(rect.x, rect.y);
+  gfx::PointF point(rect.x, rect.y);
 
   HitTestRequest::HitTestRequestType hit_type =
       HitTestRequest::kMove | HitTestRequest::kReadOnly |
@@ -393,7 +393,7 @@
   WebMouseEvent dummy_event(WebInputEvent::kMouseDown,
                             WebInputEvent::kNoModifiers,
                             base::TimeTicks::Now());
-  dummy_event.SetPositionInWidget(point.x, point.y);
+  dummy_event.SetPositionInWidget(point);
   IntPoint transformed_point = FlooredIntPoint(
       TransformWebMouseEvent(web_local_frame_impl_->GetFrameView(), dummy_event)
           .PositionInRootFrame());
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index 48160011d..cbeb817 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -3151,10 +3151,10 @@
 }
 
 void SetScaleAndScrollAndLayout(WebViewImpl* web_view,
-                                WebPoint scroll,
+                                const gfx::Point& scroll,
                                 float scale) {
   web_view->SetPageScaleFactor(scale);
-  web_view->MainFrameImpl()->SetScrollOffset(WebSize(scroll.x, scroll.y));
+  web_view->MainFrameImpl()->SetScrollOffset(WebSize(scroll.x(), scroll.y()));
   web_view->MainFrameWidget()->UpdateAllLifecyclePhases(
       WebWidget::LifecycleUpdateReason::kTest);
 }
@@ -3238,8 +3238,7 @@
   // FIXME: Looks like we are missing EXPECTs here.
 
   scale = web_view_helper.GetWebView()->MinimumPageScaleFactor();
-  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), WebPoint(0, 0),
-                             scale);
+  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(), scale);
 
   // Test double-tap zooming into tall div.
   WebRect tall_block_bound = ComputeBlockBoundHelper(
@@ -3279,7 +3278,7 @@
   gfx::Point point(div.x + 50, div.y + 50);
   float scale;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
 
@@ -3346,7 +3345,7 @@
   gfx::Point bottom_point(bottom_div.x + 50, bottom_div.y + 50);
   float scale;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
 
@@ -3417,7 +3416,7 @@
       web_view_helper.GetWebView()->MinimumPageScaleFactor() *
       double_tap_zoom_already_legible_ratio;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
   SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
@@ -3439,7 +3438,7 @@
       web_view_helper.GetWebView()->MinimumPageScaleFactor() *
       double_tap_zoom_already_legible_ratio;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
   SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
@@ -3461,7 +3460,7 @@
       web_view_helper.GetWebView()->MinimumPageScaleFactor() *
       double_tap_zoom_already_legible_ratio;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
   SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
@@ -3504,7 +3503,7 @@
   //     maximumLegibleScaleFactor
   float legible_scale = maximum_legible_scale_factor;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
   float double_tap_zoom_already_legible_scale =
@@ -3532,7 +3531,7 @@
       web_view_helper.GetWebView()->MinimumPageScaleFactor() *
       double_tap_zoom_already_legible_ratio;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
   SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
@@ -3555,7 +3554,7 @@
       web_view_helper.GetWebView()->MinimumPageScaleFactor() *
       double_tap_zoom_already_legible_ratio;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
   SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
@@ -3578,7 +3577,7 @@
       web_view_helper.GetWebView()->MinimumPageScaleFactor() *
       double_tap_zoom_already_legible_ratio;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
   SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
@@ -3624,7 +3623,7 @@
   //     accessibilityFontScaleFactor
   float legible_scale = accessibility_font_scale_factor;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
   float double_tap_zoom_already_legible_scale =
@@ -3652,7 +3651,7 @@
       web_view_helper.GetWebView()->MinimumPageScaleFactor() *
       double_tap_zoom_already_legible_ratio;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
   SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
@@ -3675,7 +3674,7 @@
       web_view_helper.GetWebView()->MinimumPageScaleFactor() *
       double_tap_zoom_already_legible_ratio;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
   SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
@@ -3698,7 +3697,7 @@
       web_view_helper.GetWebView()->MinimumPageScaleFactor() *
       double_tap_zoom_already_legible_ratio;
   SetScaleAndScrollAndLayout(
-      web_view_helper.GetWebView(), WebPoint(0, 0),
+      web_view_helper.GetWebView(), gfx::Point(),
       (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
           (1 + double_tap_zoom_already_legible_ratio) / 2);
   SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
@@ -3784,7 +3783,7 @@
       web_view_helper.GetWebView()->FakePageScaleAnimationPageScaleForTesting(),
       0);
 
-  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), WebPoint(0, 0),
+  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
                              initial_scale);
   ASSERT_EQ(initial_scale, web_view_helper.GetWebView()->PageScaleFactor());
 
@@ -3799,7 +3798,7 @@
       web_view_helper.GetWebView()->FakePageScaleAnimationPageScaleForTesting(),
       initial_scale);
 
-  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), WebPoint(0, 0),
+  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
                              initial_scale);
   ASSERT_EQ(initial_scale, web_view_helper.GetWebView()->PageScaleFactor());
 
@@ -3850,13 +3849,13 @@
       .GetElementById("EditBoxWithText")
       .To<WebInputElement>()
       .SetSelectionRange(1000, 1000);
-  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), WebPoint(0, 0), 1);
+  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(), 1);
   WebRect rect, caret;
   web_view_helper.GetWebView()->SelectionBounds(caret, rect);
 
   // Set the page scale to be smaller than the minimal readable scale.
   float initial_scale = min_readable_caret_height / caret.height * 0.5f;
-  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), WebPoint(0, 0),
+  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
                              initial_scale);
 
   float scale;
@@ -3881,7 +3880,7 @@
   viewport_width = 200;
   viewport_height = 150;
   web_view_helper.Resize(WebSize(viewport_width, viewport_height));
-  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), WebPoint(0, 0),
+  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
                              initial_scale);
   GetElementAndCaretBoundsForFocusedEditableElement(
       web_view_helper, element_bounds, caret_bounds);
@@ -3895,7 +3894,7 @@
   EXPECT_NEAR(h_scroll, scroll.X(), 2);
   EXPECT_NEAR(min_readable_caret_height / caret.height, scale, 0.1);
 
-  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), WebPoint(0, 0),
+  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
                              initial_scale);
   // Move focus to edit box with text.
   web_view_helper.GetWebView()->AdvanceFocus(false);
@@ -3958,13 +3957,13 @@
       .GetElementById("EditBoxWithText")
       .To<WebInputElement>()
       .SetSelectionRange(0, 0);
-  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), WebPoint(0, 0), 1);
+  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(), 1);
   WebRect rect, caret;
   web_view_helper.GetWebView()->SelectionBounds(caret, rect);
 
   // Set the page scale to be twice as large as the minimal readable scale.
   float new_scale = kMinReadableCaretHeight / caret.height * 2.0;
-  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), WebPoint(0, 0),
+  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
                              new_scale);
 
   float scale;
@@ -3990,7 +3989,7 @@
   new_scale = 3.0;
   h_scroll = 200;
   SetScaleAndScrollAndLayout(web_view_helper.GetWebView(),
-                             WebPoint(h_scroll, 0), new_scale);
+                             gfx::Point(h_scroll, 0), new_scale);
   GetElementAndCaretBoundsForFocusedEditableElement(
       web_view_helper, element_bounds, caret_bounds);
   web_view_helper.GetWebView()->ComputeScaleAndScrollForEditableElementRects(
@@ -4041,7 +4040,7 @@
 
   // Set the page scale to be smaller than the minimal readable scale.
   float initial_scale = 0.25f;
-  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), WebPoint(0, 0),
+  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
                              initial_scale);
 
   float scale;
@@ -4114,7 +4113,7 @@
 
   // Set the page scale to be smaller than the minimal readable scale.
   float initial_scale = 0.5f;
-  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), WebPoint(0, 0),
+  SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
                              initial_scale);
   ASSERT_EQ(web_view_helper.GetWebView()->PageScaleFactor(), initial_scale);
 
@@ -4155,7 +4154,7 @@
 
   // Since we're zoomed in to 2X, each char of Ahem is 20px wide/tall in
   // viewport space. We expect to hit the fifth char on the first line.
-  size_t ix = main_frame->CharacterIndexForPoint(WebPoint(100, 15));
+  size_t ix = main_frame->CharacterIndexForPoint(gfx::Point(100, 15));
 
   EXPECT_EQ(5ul, ix);
 }
@@ -5393,18 +5392,18 @@
   EXPECT_EQ(2, find_in_page_client.ActiveIndex());
 }
 
-static WebPoint TopLeft(const WebRect& rect) {
-  return WebPoint(rect.x, rect.y);
+static gfx::Point TopLeft(const WebRect& rect) {
+  return gfx::Point(rect.x, rect.y);
 }
 
-static WebPoint BottomRightMinusOne(const WebRect& rect) {
+static gfx::Point BottomRightMinusOne(const WebRect& rect) {
   // FIXME: If we don't subtract 1 from the x- and y-coordinates of the
   // selection bounds, selectRange() will select the *next* element. That's
   // strictly correct, as hit-testing checks the pixel to the lower-right of
   // the input coordinate, but it's a wart on the API.
   if (rect.width > 0)
-    return WebPoint(rect.x + rect.width - 1, rect.y + rect.height - 1);
-  return WebPoint(rect.x + rect.width, rect.y + rect.height - 1);
+    return gfx::Point(rect.x + rect.width - 1, rect.y + rect.height - 1);
+  return gfx::Point(rect.x + rect.width, rect.y + rect.height - 1);
 }
 
 static WebRect ElementBounds(WebLocalFrame* frame, const WebString& id) {
@@ -5573,7 +5572,7 @@
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(
       start_web_rect, end_web_rect);
 
-  frame->SelectRange(BottomRightMinusOne(end_web_rect), WebPoint(0, 0));
+  frame->SelectRange(BottomRightMinusOne(end_web_rect), gfx::Point());
   EXPECT_EQ("16-char header. This text is initially selected.",
             SelectionAsString(frame));
 
@@ -5592,7 +5591,7 @@
 
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(
       start_web_rect, end_web_rect);
-  frame->SelectRange(TopLeft(start_web_rect), WebPoint(640, 480));
+  frame->SelectRange(TopLeft(start_web_rect), gfx::Point(640, 480));
   EXPECT_EQ("This text is initially selected. 16-char footer.",
             SelectionAsString(frame));
 }
@@ -5618,7 +5617,7 @@
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(
       start_web_rect, end_web_rect);
 
-  frame->SelectRange(BottomRightMinusOne(end_web_rect), WebPoint(0, 0));
+  frame->SelectRange(BottomRightMinusOne(end_web_rect), gfx::Point());
   EXPECT_EQ("16-char header. This text is initially selected.",
             SelectionAsString(frame));
 
@@ -5638,7 +5637,7 @@
   EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(
       start_web_rect, end_web_rect);
-  frame->SelectRange(TopLeft(start_web_rect), WebPoint(640, 480));
+  frame->SelectRange(TopLeft(start_web_rect), gfx::Point(640, 480));
   EXPECT_EQ("This text is initially selected. 16-char footer.",
             SelectionAsString(frame));
 }
@@ -5766,11 +5765,11 @@
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(
       start_web_rect, end_web_rect);
 
-  frame->MoveRangeSelectionExtent(WebPoint(640, 480));
+  frame->MoveRangeSelectionExtent(gfx::Point(640, 480));
   EXPECT_EQ("This text is initially selected. 16-char footer.",
             SelectionAsString(frame));
 
-  frame->MoveRangeSelectionExtent(WebPoint(0, 0));
+  frame->MoveRangeSelectionExtent(gfx::Point());
   EXPECT_EQ("16-char header. ", SelectionAsString(frame));
 
   // Reset with swapped base and extent.
@@ -5778,10 +5777,10 @@
                      BottomRightMinusOne(start_web_rect));
   EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
 
-  frame->MoveRangeSelectionExtent(WebPoint(640, 480));
+  frame->MoveRangeSelectionExtent(gfx::Point(640, 480));
   EXPECT_EQ(" 16-char footer.", SelectionAsString(frame));
 
-  frame->MoveRangeSelectionExtent(WebPoint(0, 0));
+  frame->MoveRangeSelectionExtent(gfx::Point());
   EXPECT_EQ("16-char header. This text is initially selected.",
             SelectionAsString(frame));
 
@@ -5838,7 +5837,7 @@
                    .RootEditableElement()
                    ->scrollLeft());
   frame->MoveRangeSelectionExtent(
-      WebPoint(end_web_rect.x + 500, end_web_rect.y));
+      gfx::Point(end_web_rect.x + 500, end_web_rect.y));
   EXPECT_GE(frame->GetFrame()
                 ->Selection()
                 .ComputeVisibleSelectionInDOMTree()
@@ -6012,32 +6011,32 @@
   frame->ExecuteScript(WebScriptSource("selectRange();"));
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(
       initial_start_rect, initial_end_rect);
-  WebPoint moved_start(TopLeft(initial_start_rect));
+  gfx::Point moved_start(TopLeft(initial_start_rect));
 
-  moved_start.y += 40;
+  moved_start.Offset(0, 40);
   frame->SelectRange(moved_start, BottomRightMinusOne(initial_end_rect));
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(start_rect,
                                                                    end_rect);
   EXPECT_EQ(start_rect, initial_start_rect);
   EXPECT_EQ(end_rect, initial_end_rect);
 
-  moved_start.y -= 80;
+  moved_start.Offset(0, -80);
   frame->SelectRange(moved_start, BottomRightMinusOne(initial_end_rect));
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(start_rect,
                                                                    end_rect);
   EXPECT_EQ(start_rect, initial_start_rect);
   EXPECT_EQ(end_rect, initial_end_rect);
 
-  WebPoint moved_end(BottomRightMinusOne(initial_end_rect));
+  gfx::Point moved_end(BottomRightMinusOne(initial_end_rect));
 
-  moved_end.y += 40;
+  moved_end.Offset(0, 40);
   frame->SelectRange(TopLeft(initial_start_rect), moved_end);
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(start_rect,
                                                                    end_rect);
   EXPECT_EQ(start_rect, initial_start_rect);
   EXPECT_EQ(end_rect, initial_end_rect);
 
-  moved_end.y -= 80;
+  moved_end.Offset(0, -80);
   frame->SelectRange(TopLeft(initial_start_rect), moved_end);
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(start_rect,
                                                                    end_rect);
@@ -6062,16 +6061,16 @@
   frame->ExecuteScript(WebScriptSource("selectCaret();"));
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(
       initial_start_rect, initial_end_rect);
-  WebPoint move_to(TopLeft(initial_start_rect));
+  gfx::Point move_to(TopLeft(initial_start_rect));
 
-  move_to.y += 40;
+  move_to.Offset(0, 40);
   frame->MoveCaretSelection(move_to);
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(start_rect,
                                                                    end_rect);
   EXPECT_EQ(start_rect, initial_start_rect);
   EXPECT_EQ(end_rect, initial_end_rect);
 
-  move_to.y -= 80;
+  move_to.Offset(0, -80);
   frame->MoveCaretSelection(move_to);
   web_view_helper.GetWebView()->MainFrameWidget()->SelectionBounds(start_rect,
                                                                    end_rect);
@@ -6382,7 +6381,7 @@
   WebFrame* frame = web_view_helper.GetWebView()->MainFrame();
 
   // This test passes if this doesn't crash.
-  frame->ToWebLocalFrame()->MoveCaretSelection(WebPoint(0, 0));
+  frame->ToWebLocalFrame()->MoveCaretSelection(gfx::Point());
 }
 
 class TextCheckClient : public WebTextCheckClient {
@@ -9503,7 +9502,7 @@
   void Navigate(const WebURLRequest& request,
                 bool should_replace_current_entry,
                 bool is_opener_navigation,
-                bool has_download_sandbox_flag,
+                bool initiator_frame_has_download_sandbox_flag,
                 bool blocking_downloads_in_sandbox_enabled,
                 bool initiator_frame_is_ad,
                 mojo::ScopedMessagePipeHandle) override {
@@ -13009,7 +13008,7 @@
   // element.
   WebFrameWidget* widget = local_frame->FrameWidget();
   ASSERT_TRUE(widget);
-  WebPoint viewport_offset(7, -11);
+  gfx::Point viewport_offset(7, -11);
   WebRect viewport_intersection(0, 11, 200, 89);
   WebRect mainframe_intersection(0, 0, 200, 140);
   FrameOcclusionState occlusion_state = FrameOcclusionState::kUnknown;
diff --git a/third_party/blink/renderer/core/exported/web_history_item.cc b/third_party/blink/renderer/core/exported/web_history_item.cc
index e4fdcd5..f6c3cb34 100644
--- a/third_party/blink/renderer/core/exported/web_history_item.cc
+++ b/third_party/blink/renderer/core/exported/web_history_item.cc
@@ -31,7 +31,6 @@
 #include "third_party/blink/public/web/web_history_item.h"
 
 #include "third_party/blink/public/platform/web_http_body.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/web_serialized_script_value.h"
@@ -40,6 +39,7 @@
 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "ui/gfx/geometry/point.h"
 
 namespace blink {
 
@@ -101,16 +101,16 @@
   private_->SetVisualViewportScrollOffset(ToScrollOffset(scroll_offset));
 }
 
-WebPoint WebHistoryItem::GetScrollOffset() const {
+gfx::Point WebHistoryItem::GetScrollOffset() const {
   const auto& scroll_and_view_state = private_->GetViewState();
   ScrollOffset offset = scroll_and_view_state
                             ? scroll_and_view_state->scroll_offset_
                             : ScrollOffset();
-  return WebPoint(offset.Width(), offset.Height());
+  return gfx::Point(offset.Width(), offset.Height());
 }
 
-void WebHistoryItem::SetScrollOffset(const WebPoint& scroll_offset) {
-  private_->SetScrollOffset(ScrollOffset(scroll_offset.x, scroll_offset.y));
+void WebHistoryItem::SetScrollOffset(const gfx::Point& scroll_offset) {
+  private_->SetScrollOffset(ScrollOffset(scroll_offset.x(), scroll_offset.y()));
 }
 
 float WebHistoryItem::PageScaleFactor() const {
diff --git a/third_party/blink/renderer/core/exported/web_hit_test_result.cc b/third_party/blink/renderer/core/exported/web_hit_test_result.cc
index 6a61243..da6c5a7 100644
--- a/third_party/blink/renderer/core/exported/web_hit_test_result.cc
+++ b/third_party/blink/renderer/core/exported/web_hit_test_result.cc
@@ -25,7 +25,6 @@
 
 #include "third_party/blink/public/web/web_hit_test_result.h"
 
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/web/web_element.h"
 #include "third_party/blink/public/web/web_node.h"
@@ -62,11 +61,11 @@
   return WebNode(private_->Result().InnerNode());
 }
 
-WebPoint WebHitTestResult::LocalPoint() const {
+gfx::Point WebHitTestResult::LocalPoint() const {
   return RoundedIntPoint(private_->Result().LocalPoint());
 }
 
-WebPoint WebHitTestResult::LocalPointWithoutContentBoxOffset() const {
+gfx::Point WebHitTestResult::LocalPointWithoutContentBoxOffset() const {
   IntPoint local_point = RoundedIntPoint(private_->Result().LocalPoint());
   LayoutObject* object = private_->Result().GetLayoutObject();
   if (object->IsBox()) {
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
index bf883e5..cfa81ea 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -610,11 +610,11 @@
   return MainFrame().DomWindow();
 }
 
-WebPoint WebPagePopupImpl::PositionRelativeToOwner() {
+gfx::Point WebPagePopupImpl::PositionRelativeToOwner() {
   WebRect root_window_rect = WindowRectInScreen();
   WebRect window_rect = WindowRectInScreen();
-  return WebPoint(window_rect.x - root_window_rect.x,
-                  window_rect.y - root_window_rect.y);
+  return gfx::Point(window_rect.x - root_window_rect.x,
+                    window_rect.y - root_window_rect.y);
 }
 
 WebDocument WebPagePopupImpl::GetDocument() {
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.h b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
index d85407f..c3e329c 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.h
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
@@ -97,7 +97,7 @@
   WebInputEventResult DispatchBufferedTouchEvents() override;
 
   // WebPagePopup implementation.
-  WebPoint PositionRelativeToOwner() override;
+  gfx::Point PositionRelativeToOwner() override;
   WebDocument GetDocument() override;
   WebPagePopupClient* GetClientForTesting() const override;
 
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
index 325b7e4..c82dd72 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -653,16 +653,16 @@
   }
 }
 
-WebPoint WebPluginContainerImpl::RootFrameToLocalPoint(
-    const WebPoint& point_in_root_frame) {
-  WebPoint point_in_content =
-      ParentFrameView()->ConvertFromRootFrame(point_in_root_frame);
+gfx::Point WebPluginContainerImpl::RootFrameToLocalPoint(
+    const gfx::Point& point_in_root_frame) {
+  gfx::Point point_in_content =
+      ParentFrameView()->ConvertFromRootFrame(IntPoint(point_in_root_frame));
   return RoundedIntPoint(element_->GetLayoutObject()->AbsoluteToLocalPoint(
       PhysicalOffset(point_in_content)));
 }
 
-WebPoint WebPluginContainerImpl::LocalToRootFramePoint(
-    const WebPoint& point_in_local) {
+gfx::Point WebPluginContainerImpl::LocalToRootFramePoint(
+    const gfx::Point& point_in_local) {
   IntPoint absolute_point =
       RoundedIntPoint(element_->GetLayoutObject()->LocalToAbsolutePoint(
           PhysicalOffset(point_in_local)));
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.h b/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
index 8d0e085e..65aa4b8 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
@@ -133,8 +133,8 @@
   bool IsRectTopmost(const WebRect&) override;
   void RequestTouchEventType(TouchEventRequestType) override;
   void SetWantsWheelEvents(bool) override;
-  WebPoint RootFrameToLocalPoint(const WebPoint&) override;
-  WebPoint LocalToRootFramePoint(const WebPoint&) override;
+  gfx::Point RootFrameToLocalPoint(const gfx::Point&) override;
+  gfx::Point LocalToRootFramePoint(const gfx::Point&) override;
 
   // Non-Oilpan, this cannot be null. With Oilpan, it will be
   // null when in a disposed state, pending finalization during the next GC.
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
index dd79e6d..d7749da5 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
@@ -288,7 +288,7 @@
                                const WebString& command_name) {
   auto event = frame_test_helpers::CreateMouseEvent(
       WebMouseEvent::kMouseDown, WebMouseEvent::Button::kRight,
-      WebPoint(30, 30), 0);
+      IntPoint(30, 30), 0);
   event.click_count = 1;
 
   web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
@@ -310,26 +310,26 @@
   WebPluginContainer* plugin_container_one =
       GetWebPluginContainer(web_view, WebString::FromUTF8("translated-plugin"));
   DCHECK(plugin_container_one);
-  WebPoint point1 =
-      plugin_container_one->RootFrameToLocalPoint(WebPoint(10, 10));
-  ASSERT_EQ(0, point1.x);
-  ASSERT_EQ(0, point1.y);
-  WebPoint point2 =
-      plugin_container_one->RootFrameToLocalPoint(WebPoint(100, 100));
-  ASSERT_EQ(90, point2.x);
-  ASSERT_EQ(90, point2.y);
+  gfx::Point point1 =
+      plugin_container_one->RootFrameToLocalPoint(gfx::Point(10, 10));
+  ASSERT_EQ(0, point1.x());
+  ASSERT_EQ(0, point1.y());
+  gfx::Point point2 =
+      plugin_container_one->RootFrameToLocalPoint(gfx::Point(100, 100));
+  ASSERT_EQ(90, point2.x());
+  ASSERT_EQ(90, point2.y());
 
   WebPluginContainer* plugin_container_two =
       GetWebPluginContainer(web_view, WebString::FromUTF8("rotated-plugin"));
   DCHECK(plugin_container_two);
-  WebPoint point3 =
-      plugin_container_two->RootFrameToLocalPoint(WebPoint(0, 10));
-  ASSERT_EQ(10, point3.x);
-  ASSERT_EQ(0, point3.y);
-  WebPoint point4 =
-      plugin_container_two->RootFrameToLocalPoint(WebPoint(-10, 10));
-  ASSERT_EQ(10, point4.x);
-  ASSERT_EQ(10, point4.y);
+  gfx::Point point3 =
+      plugin_container_two->RootFrameToLocalPoint(gfx::Point(0, 10));
+  ASSERT_EQ(10, point3.x());
+  ASSERT_EQ(0, point3.y());
+  gfx::Point point4 =
+      plugin_container_two->RootFrameToLocalPoint(gfx::Point(-10, 10));
+  ASSERT_EQ(10, point4.x());
+  ASSERT_EQ(10, point4.y());
 }
 
 TEST_F(WebPluginContainerTest, LocalToWindowPointTest) {
@@ -344,25 +344,26 @@
   WebPluginContainer* plugin_container_one =
       GetWebPluginContainer(web_view, WebString::FromUTF8("translated-plugin"));
   DCHECK(plugin_container_one);
-  WebPoint point1 = plugin_container_one->LocalToRootFramePoint(WebPoint(0, 0));
-  ASSERT_EQ(10, point1.x);
-  ASSERT_EQ(10, point1.y);
-  WebPoint point2 =
-      plugin_container_one->LocalToRootFramePoint(WebPoint(90, 90));
-  ASSERT_EQ(100, point2.x);
-  ASSERT_EQ(100, point2.y);
+  gfx::Point point1 =
+      plugin_container_one->LocalToRootFramePoint(gfx::Point(0, 0));
+  ASSERT_EQ(10, point1.x());
+  ASSERT_EQ(10, point1.y());
+  gfx::Point point2 =
+      plugin_container_one->LocalToRootFramePoint(gfx::Point(90, 90));
+  ASSERT_EQ(100, point2.x());
+  ASSERT_EQ(100, point2.y());
 
   WebPluginContainer* plugin_container_two =
       GetWebPluginContainer(web_view, WebString::FromUTF8("rotated-plugin"));
   DCHECK(plugin_container_two);
-  WebPoint point3 =
-      plugin_container_two->LocalToRootFramePoint(WebPoint(10, 0));
-  ASSERT_EQ(0, point3.x);
-  ASSERT_EQ(10, point3.y);
-  WebPoint point4 =
-      plugin_container_two->LocalToRootFramePoint(WebPoint(10, 10));
-  ASSERT_EQ(-10, point4.x);
-  ASSERT_EQ(10, point4.y);
+  gfx::Point point3 =
+      plugin_container_two->LocalToRootFramePoint(gfx::Point(10, 0));
+  ASSERT_EQ(0, point3.x());
+  ASSERT_EQ(10, point3.y());
+  gfx::Point point4 =
+      plugin_container_two->LocalToRootFramePoint(gfx::Point(10, 10));
+  ASSERT_EQ(-10, point4.x());
+  ASSERT_EQ(10, point4.y());
 }
 
 // Verifies executing the command 'Copy' results in copying to the clipboard.
@@ -406,7 +407,7 @@
 
   auto event = frame_test_helpers::CreateMouseEvent(
       WebMouseEvent::kMouseDown, WebMouseEvent::Button::kRight,
-      WebPoint(30, 30), 0);
+      IntPoint(30, 30), 0);
   event.click_count = 1;
 
   // Now, let's try a more complex scenario:
@@ -753,7 +754,7 @@
 
   WebMouseEvent event = frame_test_helpers::CreateMouseEvent(
       WebMouseEvent::kMouseMove, WebMouseEvent::Button::kNoButton,
-      WebPoint(30, 30),
+      IntPoint(30, 30),
       WebInputEvent::kMiddleButtonDown | WebInputEvent::kShiftKey);
 
   WebRect rect = plugin_container_one_element.BoundsInViewport();
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index 3154dbd0..f74646d 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -39,7 +39,6 @@
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/public/platform/web_string.h"
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 630a05bd..3729bf3b 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -1814,7 +1814,8 @@
   WebRect anchor;
   WebRect focus;
   web_view->MainFrameWidget()->SelectionBounds(anchor, focus);
-  frame->SelectRange(WebPoint(focus.x, focus.y), WebPoint(anchor.x, anchor.y));
+  frame->SelectRange(gfx::Point(focus.x, focus.y),
+                     gfx::Point(anchor.x, anchor.y));
   EXPECT_FALSE(frame->IsSelectionAnchorFirst());
 }
 
@@ -4603,12 +4604,12 @@
   web_view->MainFrameWidget()->Resize(WebSize(400, 400));
   WebLocalFrameImpl* frame = web_view->MainFrameImpl();
 
-  WebPoint baseline_point;
+  gfx::Point baseline_point;
   NSAttributedString* result = WebSubstringUtil::AttributedSubstringInRange(
       frame, 10, 3, &baseline_point);
   ASSERT_TRUE(!!result);
 
-  WebPoint point(baseline_point.x, baseline_point.y);
+  gfx::Point point(baseline_point);
   result = WebSubstringUtil::AttributedWordAtPoint(frame->FrameWidget(), point,
                                                    baseline_point);
   ASSERT_TRUE(!!result);
@@ -4619,7 +4620,7 @@
                                                         &baseline_point);
   ASSERT_TRUE(!!result);
 
-  point = WebPoint(baseline_point.x, baseline_point.y);
+  point = baseline_point;
   result = WebSubstringUtil::AttributedWordAtPoint(frame->FrameWidget(), point,
                                                    baseline_point);
   ASSERT_TRUE(!!result);
@@ -4633,14 +4634,14 @@
   web_view->MainFrameWidget()->Resize(WebSize(400, 400));
   WebLocalFrameImpl* frame = web_view->MainFrameImpl();
 
-  WebPoint old_point;
+  gfx::Point old_point;
   WebSubstringUtil::AttributedSubstringInRange(frame, 3, 1, &old_point);
 
-  WebPoint new_point;
+  gfx::Point new_point;
   WebSubstringUtil::AttributedSubstringInRange(frame, 3, 20, &new_point);
 
-  EXPECT_EQ(old_point.x, new_point.x);
-  EXPECT_EQ(old_point.y, new_point.y);
+  EXPECT_EQ(old_point.x(), new_point.x());
+  EXPECT_EQ(old_point.y(), new_point.y());
 }
 
 TEST_F(WebViewTest, WebSubstringUtilPinchZoom) {
@@ -4652,22 +4653,22 @@
   WebLocalFrameImpl* frame = web_view->MainFrameImpl();
   NSAttributedString* result = nil;
 
-  WebPoint baseline_point;
+  gfx::Point baseline_point;
   result = WebSubstringUtil::AttributedSubstringInRange(frame, 10, 3,
                                                         &baseline_point);
   ASSERT_TRUE(!!result);
 
   web_view->SetPageScaleFactor(3);
 
-  WebPoint point_after_zoom;
+  gfx::Point point_after_zoom;
   result = WebSubstringUtil::AttributedSubstringInRange(frame, 10, 3,
                                                         &point_after_zoom);
   ASSERT_TRUE(!!result);
 
   // We won't have moved by a full factor of 3 because of the translations, but
   // we should move by a factor of >2.
-  EXPECT_LT(2 * baseline_point.x, point_after_zoom.x);
-  EXPECT_LT(2 * baseline_point.y, point_after_zoom.y);
+  EXPECT_LT(2 * baseline_point.x(), point_after_zoom.x());
+  EXPECT_LT(2 * baseline_point.y(), point_after_zoom.y());
 }
 
 TEST_F(WebViewTest, WebSubstringUtilIframe) {
@@ -4682,28 +4683,28 @@
   WebLocalFrameImpl* child_frame = WebLocalFrameImpl::FromFrame(
       To<LocalFrame>(main_frame->GetFrame()->Tree().FirstChild()));
 
-  WebPoint baseline_point;
+  gfx::Point baseline_point;
   NSAttributedString* result = WebSubstringUtil::AttributedSubstringInRange(
       child_frame, 11, 7, &baseline_point);
   ASSERT_NE(result, nullptr);
 
-  WebPoint point(baseline_point.x, baseline_point.y);
+  gfx::Point point(baseline_point);
   result = WebSubstringUtil::AttributedWordAtPoint(main_frame->FrameWidget(),
                                                    point, baseline_point);
   ASSERT_NE(result, nullptr);
 
-  int y_before_change = baseline_point.y;
+  int y_before_change = baseline_point.y();
 
   // Now move the <iframe> down by 100px.
   main_frame->ExecuteScript(WebScriptSource(
       "document.querySelector('iframe').style.marginTop = '100px';"));
 
-  point = WebPoint(point.x, point.y + 100);
+  point = gfx::Point(point.x(), point.y() + 100);
   result = WebSubstringUtil::AttributedWordAtPoint(main_frame->FrameWidget(),
                                                    point, baseline_point);
   ASSERT_NE(result, nullptr);
 
-  EXPECT_EQ(y_before_change, baseline_point.y - 100);
+  EXPECT_EQ(y_before_change, baseline_point.y() - 100);
 }
 
 #endif
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 9107b04..e398d8d 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -347,7 +347,7 @@
   void SetViewportIntersectionFromParent(const ViewportIntersectionState&);
 
   // See viewport_intersection_state.h for more info on these methods.
-  IntPoint RemoteViewportOffset() const {
+  gfx::Point RemoteViewportOffset() const {
     return intersection_state_.viewport_offset;
   }
   IntRect RemoteViewportIntersection() const {
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index b78e8d0..33ca33c6 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -137,15 +137,10 @@
     }
   }
 
-  bool current_frame_has_download_sandbox_flag =
-      GetSecurityContext() &&
-      GetSecurityContext()->IsSandboxed(WebSandboxFlags::kDownloads);
-  bool has_download_sandbox_flag = initiator_frame_has_download_sandbox_flag ||
-                                   current_frame_has_download_sandbox_flag;
-
   Client()->Navigate(frame_request.GetResourceRequest(),
                      frame_load_type == WebFrameLoadType::kReplaceCurrentItem,
-                     is_opener_navigation, has_download_sandbox_flag,
+                     is_opener_navigation,
+                     initiator_frame_has_download_sandbox_flag,
                      initiator_frame_is_ad, frame_request.GetBlobURLToken());
 }
 
diff --git a/third_party/blink/renderer/core/frame/remote_frame_client.h b/third_party/blink/renderer/core/frame/remote_frame_client.h
index c5a8a32..f14b4633 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_client.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_client.h
@@ -32,7 +32,7 @@
   virtual void Navigate(const ResourceRequest&,
                         bool should_replace_current_entry,
                         bool is_opener_navigation,
-                        bool has_download_sandbox_flag,
+                        bool initiator_frame_has_download_sandbox_flag,
                         bool initiator_frame_is_ad,
                         mojo::PendingRemote<mojom::blink::BlobURLToken>) = 0;
   unsigned BackForwardLength() override = 0;
diff --git a/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc b/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
index 5d904d0..3a10a1f 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
@@ -102,7 +102,7 @@
     const ResourceRequest& request,
     bool should_replace_current_entry,
     bool is_opener_navigation,
-    bool has_download_sandbox_flag,
+    bool initiator_frame_has_download_sandbox_flag,
     bool initiator_frame_is_ad,
     mojo::PendingRemote<mojom::blink::BlobURLToken> blob_url_token) {
   bool blocking_downloads_in_sandbox_enabled =
@@ -110,7 +110,7 @@
   if (web_frame_->Client()) {
     web_frame_->Client()->Navigate(
         WrappedResourceRequest(request), should_replace_current_entry,
-        is_opener_navigation, has_download_sandbox_flag,
+        is_opener_navigation, initiator_frame_has_download_sandbox_flag,
         blocking_downloads_in_sandbox_enabled, initiator_frame_is_ad,
         blob_url_token.PassPipe());
   }
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
index 711144f..48d4805f 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -18,6 +18,7 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
 
 namespace blink {
 
@@ -201,25 +202,31 @@
                             const GlobalPaintFlags flags,
                             const CullRect& rect,
                             const IntSize& paint_offset) const {
-  // Painting remote frames is only for printing.
-  if (!context.Printing())
-    return;
-
   if (!rect.Intersects(FrameRect()))
     return;
 
-  DrawingRecorder recorder(context, *GetFrame().OwnerLayoutObject(),
-                           DisplayItem::kDocumentBackground);
-  context.Save();
-  context.Translate(paint_offset.Width(), paint_offset.Height());
+  if (context.Printing()) {
+    DrawingRecorder recorder(context, *GetFrame().OwnerLayoutObject(),
+                             DisplayItem::kDocumentBackground);
+    context.Save();
+    context.Translate(paint_offset.Width(), paint_offset.Height());
 
-  DCHECK(context.Canvas());
-  // Inform the remote frame to print.
-  uint32_t content_id = Print(FrameRect(), context.Canvas());
+    DCHECK(context.Canvas());
+    // Inform the remote frame to print.
+    uint32_t content_id = Print(FrameRect(), context.Canvas());
 
-  // Record the place holder id on canvas.
-  context.Canvas()->recordCustomData(content_id);
-  context.Restore();
+    // Record the place holder id on canvas.
+    context.Canvas()->recordCustomData(content_id);
+    context.Restore();
+  }
+
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+      GetFrame().GetCcLayer()) {
+    auto offset = GetLayoutEmbeddedContent()->ReplacedContentRect().offset;
+    RecordForeignLayer(context, *GetFrame().OwnerLayoutObject(),
+                       DisplayItem::kForeignLayerRemoteFrame,
+                       GetFrame().GetCcLayer(), FloatPoint(offset));
+  }
 }
 
 void RemoteFrameView::UpdateGeometry() {
diff --git a/third_party/blink/renderer/core/frame/visual_viewport_test.cc b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
index 9705b25..114f522 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -72,8 +72,8 @@
 namespace blink {
 
 ::std::ostream& operator<<(::std::ostream& os, const WebContextMenuData& data) {
-  return os << "Context menu location: [" << data.mouse_position.x << ", "
-            << data.mouse_position.y << "]";
+  return os << "Context menu location: [" << data.mouse_position.x() << ", "
+            << data.mouse_position.y() << "]";
 }
 
 namespace {
@@ -990,7 +990,7 @@
   // (-1, -1) will be used if the HistoryItem is an older version prior to
   // having visual viewport scroll offset.
   item.SetVisualViewportScrollOffset(gfx::PointF(-1, -1));
-  item.SetScrollOffset(WebPoint(120, 180));
+  item.SetScrollOffset(gfx::Point(120, 180));
   item.SetPageScaleFactor(2);
 
   frame_test_helpers::LoadHistoryItem(WebView()->MainFrameImpl(), item,
@@ -1072,8 +1072,8 @@
   EXPECT_EQ("ir", mainFrame->SelectionAsText().Utf8());
 
   WebView()->MainFrameWidget()->SelectionBounds(base_rect, extent_rect);
-  WebPoint initialPoint(base_rect.x, base_rect.y);
-  WebPoint endPoint(extent_rect.x, extent_rect.y);
+  gfx::Point initialPoint(base_rect.x, base_rect.y);
+  gfx::Point endPoint(extent_rect.x, extent_rect.y);
 
   // Move the visual viewport over and make the selection in the same
   // screen-space location. The selection should change to two characters to the
@@ -1115,7 +1115,7 @@
            y,
            std::string(negation ? "is" : "isn't") + " at expected location [" +
                PrintToString(x) + ", " + PrintToString(y) + "]") {
-  return arg.mouse_position.x == x && arg.mouse_position.y == y;
+  return arg.mouse_position.x() == x && arg.mouse_position.y() == y;
 }
 
 // Test that the context menu's location is correct in the presence of visual
@@ -1929,7 +1929,7 @@
   // Because of where the visual viewport is located, this should hit the bottom
   // right target (target 4).
   WebAXObject hitNode =
-      WebAXObject::FromWebDocument(web_doc).HitTest(WebPoint(154, 165));
+      WebAXObject::FromWebDocument(web_doc).HitTest(gfx::Point(154, 165));
   ax::mojom::NameFrom name_from;
   WebVector<WebAXObject> name_objects;
   EXPECT_EQ(std::string("Target4"),
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index b339acd..2bcdf5d9 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -38,7 +38,6 @@
 #include "base/single_thread_task_runner.h"
 #include "base/util/type_safety/pass_key.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/public/web/web_input_method_controller.h"
 #include "third_party/blink/renderer/core/frame/web_frame_widget_base.h"
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index ef5eaa93..d90e127 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -103,7 +103,6 @@
 #include "third_party/blink/public/platform/web_double_size.h"
 #include "third_party/blink/public/platform/web_float_rect.h"
 #include "third_party/blink/public/platform/web_isolated_world_info.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/public/platform/web_size.h"
@@ -1089,12 +1088,12 @@
 }
 
 size_t WebLocalFrameImpl::CharacterIndexForPoint(
-    const WebPoint& point_in_viewport) const {
+    const gfx::Point& point_in_viewport) const {
   if (!GetFrame())
     return kNotFound;
 
   HitTestLocation location(
-      GetFrame()->View()->ViewportToFrame(point_in_viewport));
+      GetFrame()->View()->ViewportToFrame(IntPoint(point_in_viewport)));
   HitTestResult result = GetFrame()->GetEventHandler().HitTestResultAtLocation(
       location, HitTestRequest::kReadOnly | HitTestRequest::kActive);
   return GetFrame()->Selection().CharacterIndexForPoint(
@@ -1304,8 +1303,8 @@
   return GetFrame()->Selection().SelectWordAroundCaret();
 }
 
-void WebLocalFrameImpl::SelectRange(const WebPoint& base_in_viewport,
-                                    const WebPoint& extent_in_viewport) {
+void WebLocalFrameImpl::SelectRange(const gfx::Point& base_in_viewport,
+                                    const gfx::Point& extent_in_viewport) {
   MoveRangeSelection(base_in_viewport, extent_in_viewport);
 }
 
@@ -1360,7 +1359,7 @@
       TextIteratorBehavior::EmitsObjectReplacementCharacterBehavior());
 }
 
-void WebLocalFrameImpl::MoveRangeSelectionExtent(const WebPoint& point) {
+void WebLocalFrameImpl::MoveRangeSelectionExtent(const gfx::Point& point) {
   TRACE_EVENT0("blink", "WebLocalFrameImpl::moveRangeSelectionExtent");
 
   // TODO(editing-dev): The use of UpdateStyleAndLayout
@@ -1368,12 +1367,12 @@
   GetFrame()->GetDocument()->UpdateStyleAndLayout();
 
   GetFrame()->Selection().MoveRangeSelectionExtent(
-      GetFrame()->View()->ViewportToFrame(point));
+      GetFrame()->View()->ViewportToFrame(IntPoint(point)));
 }
 
 void WebLocalFrameImpl::MoveRangeSelection(
-    const WebPoint& base_in_viewport,
-    const WebPoint& extent_in_viewport,
+    const gfx::Point& base_in_viewport,
+    const gfx::Point& extent_in_viewport,
     WebFrame::TextGranularity granularity) {
   TRACE_EVENT0("blink", "WebLocalFrameImpl::moveRangeSelection");
 
@@ -1385,19 +1384,20 @@
   if (granularity == WebFrame::kWordGranularity)
     blink_granularity = blink::TextGranularity::kWord;
   GetFrame()->Selection().MoveRangeSelection(
-      GetFrame()->View()->ViewportToFrame(base_in_viewport),
-      GetFrame()->View()->ViewportToFrame(extent_in_viewport),
+      GetFrame()->View()->ViewportToFrame(IntPoint(base_in_viewport)),
+      GetFrame()->View()->ViewportToFrame(IntPoint(extent_in_viewport)),
       blink_granularity);
 }
 
-void WebLocalFrameImpl::MoveCaretSelection(const WebPoint& point_in_viewport) {
+void WebLocalFrameImpl::MoveCaretSelection(
+    const gfx::Point& point_in_viewport) {
   TRACE_EVENT0("blink", "WebLocalFrameImpl::moveCaretSelection");
 
   // TODO(editing-dev): The use of UpdateStyleAndLayout
   // needs to be audited.  see http://crbug.com/590369 for more details.
   GetFrame()->GetDocument()->UpdateStyleAndLayout();
   const IntPoint point_in_contents =
-      GetFrame()->View()->ViewportToFrame(point_in_viewport);
+      GetFrame()->View()->ViewportToFrame(IntPoint(point_in_viewport));
   GetFrame()->Selection().MoveCaretSelection(point_in_contents);
 }
 
@@ -2351,8 +2351,9 @@
   return frame_widget_;
 }
 
-void WebLocalFrameImpl::CopyImageAtForTesting(const WebPoint& pos_in_viewport) {
-  GetFrame()->CopyImageAtViewportPoint(pos_in_viewport);
+void WebLocalFrameImpl::CopyImageAtForTesting(
+    const gfx::Point& pos_in_viewport) {
+  GetFrame()->CopyImageAtViewportPoint(IntPoint(pos_in_viewport));
 }
 
 WebSandboxFlags WebLocalFrameImpl::EffectiveSandboxFlagsForTesting() const {
@@ -2392,10 +2393,9 @@
     }
     return !GetFrame()->Owner() ||
            GetFrame()->Owner()->GetFramePolicy().allowed_to_download;
-  } else {
-    return (GetFrame()->Loader().EffectiveSandboxFlags() &
-            WebSandboxFlags::kDownloads) == WebSandboxFlags::kNone;
   }
+  return (GetFrame()->Loader().PendingEffectiveSandboxFlags() &
+          WebSandboxFlags::kDownloads) == WebSandboxFlags::kNone;
 }
 
 void WebLocalFrameImpl::UsageCountChromeLoadTimes(const WebString& metric) {
@@ -2456,8 +2456,8 @@
   clip_text = clip_data.ClipData();
   clip_rect = clip_data.RectInViewport();
 
-  WebPoint start_point(rect_in_viewport.x, rect_in_viewport.y);
-  WebPoint end_point(rect_in_viewport.x + rect_in_viewport.width,
+  IntPoint start_point(rect_in_viewport.x, rect_in_viewport.y);
+  IntPoint end_point(rect_in_viewport.x + rect_in_viewport.width,
                      rect_in_viewport.y + rect_in_viewport.height);
   clip_html = CreateMarkupInRect(
       GetFrame(), GetFrame()->View()->ViewportToFrame(start_point),
@@ -2520,9 +2520,9 @@
 }
 
 void WebLocalFrameImpl::PerformMediaPlayerAction(
-    const WebPoint& location,
+    const gfx::Point& location,
     const MediaPlayerAction& action) {
-  HitTestResult result = HitTestResultForVisualViewportPos(location);
+  HitTestResult result = HitTestResultForVisualViewportPos(IntPoint(location));
   Node* node = result.InnerNode();
   if (!IsA<HTMLVideoElement>(*node) && !IsA<HTMLAudioElement>(*node))
     return;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 16797bd6..6ec2209 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -198,7 +198,7 @@
   bool FirstRectForCharacterRange(unsigned location,
                                   unsigned length,
                                   WebRect&) const override;
-  size_t CharacterIndexForPoint(const WebPoint&) const override;
+  size_t CharacterIndexForPoint(const gfx::Point&) const override;
   bool ExecuteCommand(const WebString&) override;
   bool ExecuteCommand(const WebString&, const WebString& value) override;
   bool IsCommandEnabled(const WebString&) const override;
@@ -211,16 +211,16 @@
   WebString SelectionAsText() const override;
   WebString SelectionAsMarkup() const override;
   bool SelectWordAroundCaret() override;
-  void SelectRange(const WebPoint& base, const WebPoint& extent) override;
+  void SelectRange(const gfx::Point& base, const gfx::Point& extent) override;
   void SelectRange(const WebRange&,
                    HandleVisibilityBehavior,
                    blink::mojom::SelectionMenuBehavior) override;
   WebString RangeAsText(const WebRange&) override;
   void MoveRangeSelection(
-      const WebPoint& base,
-      const WebPoint& extent,
+      const gfx::Point& base,
+      const gfx::Point& extent,
       WebFrame::TextGranularity = kCharacterGranularity) override;
-  void MoveCaretSelection(const WebPoint&) override;
+  void MoveCaretSelection(const gfx::Point&) override;
   bool SetEditableSelectionOffsets(int start, int end) override;
   bool SetCompositionFromExistingText(
       int composition_start,
@@ -228,7 +228,7 @@
       const WebVector<WebImeTextSpan>& ime_text_spans) override;
   void ExtendSelectionAndDelete(int before, int after) override;
   void SetCaretVisible(bool) override;
-  void MoveRangeSelectionExtent(const WebPoint&) override;
+  void MoveRangeSelectionExtent(const gfx::Point&) override;
   void ReplaceSelection(const WebString&) override;
   void DeleteSurroundingText(int before, int after) override;
   void DeleteSurroundingTextInCodePoints(int before, int after) override;
@@ -258,7 +258,7 @@
                       bool wrap_within_frame) override;
   void SetTickmarks(const WebVector<WebRect>&) override;
   WebNode ContextMenuNode() const override;
-  void CopyImageAtForTesting(const WebPoint&) override;
+  void CopyImageAtForTesting(const gfx::Point&) override;
   void DispatchMessageEventWithOriginCheck(
       const WebSecurityOrigin& intended_target_origin,
       const WebDOMMessageEvent&) override;
@@ -300,7 +300,7 @@
   void SetIsAdSubframe(blink::mojom::AdFrameType ad_frame_type) override;
   void PrintPagesForTesting(cc::PaintCanvas*, const WebSize&) override;
   WebRect GetSelectionBoundsRectForTesting() const override;
-  void PerformMediaPlayerAction(const WebPoint&,
+  void PerformMediaPlayerAction(const gfx::Point&,
                                 const MediaPlayerAction&) override;
   void SetLifecycleState(mojom::FrameLifecycleState state) override;
   void WasHidden() override;
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
index d1bbcbc..2b5054d3 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
@@ -45,6 +45,10 @@
 #include "third_party/blink/renderer/core/inspector/v8_inspector_string.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/inspector_protocol/crdtp/json.h"
+
+using crdtp::SpanFrom;
+using crdtp::json::ConvertCBORToJSON;
 
 namespace {
 
@@ -539,11 +543,12 @@
   DCHECK(breakpoint_owner_node_id);
   description->setInteger("nodeId", breakpoint_owner_node_id);
   description->setString("type", DomTypeName(breakpoint_type));
-  String json = description->toJSONString();
+  std::vector<uint8_t> json;
+  ConvertCBORToJSON(SpanFrom(std::move(*description).TakeSerialized()), &json);
   v8_session_->breakProgram(
       ToV8InspectorStringView(
           v8_inspector::protocol::Debugger::API::Paused::ReasonEnum::DOM),
-      ToV8InspectorStringView(json));
+      v8_inspector::StringView(json.data(), json.size()));
 }
 
 bool InspectorDOMDebuggerAgent::HasBreakpoint(Node* node, int type) {
@@ -579,17 +584,15 @@
     bool synchronous) {
   if (!event_data)
     return;
-  String json = event_data->toJSONString();
+  std::vector<uint8_t> json;
+  ConvertCBORToJSON(SpanFrom(std::move(*event_data).TakeSerialized()), &json);
+  v8_inspector::StringView json_view(json.data(), json.size());
+  auto listener = ToV8InspectorStringView(
+      v8_inspector::protocol::Debugger::API::Paused::ReasonEnum::EventListener);
   if (synchronous)
-    v8_session_->breakProgram(
-        ToV8InspectorStringView(v8_inspector::protocol::Debugger::API::Paused::
-                                    ReasonEnum::EventListener),
-        ToV8InspectorStringView(json));
+    v8_session_->breakProgram(listener, json_view);
   else
-    v8_session_->schedulePauseOnNextStatement(
-        ToV8InspectorStringView(v8_inspector::protocol::Debugger::API::Paused::
-                                    ReasonEnum::EventListener),
-        ToV8InspectorStringView(json));
+    v8_session_->schedulePauseOnNextStatement(listener, json_view);
 }
 
 std::unique_ptr<protocol::DictionaryValue>
@@ -722,11 +725,12 @@
       protocol::DictionaryValue::create();
   event_data->setString("breakpointURL", breakpoint_url);
   event_data->setString("url", url);
-  String json = event_data->toJSONString();
+  std::vector<uint8_t> json;
+  ConvertCBORToJSON(SpanFrom(std::move(*event_data).TakeSerialized()), &json);
   v8_session_->breakProgram(
       ToV8InspectorStringView(
           v8_inspector::protocol::Debugger::API::Paused::ReasonEnum::XHR),
-      ToV8InspectorStringView(json));
+      v8_inspector::StringView(json.data(), json.size()));
 }
 
 void InspectorDOMDebuggerAgent::DidCreateCanvasContext() {
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index ecd28a2..41c01a9 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -85,6 +85,10 @@
 #include "third_party/blink/renderer/platform/wtf/text/base64.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/inspector_protocol/crdtp/json.h"
+
+using crdtp::SpanFrom;
+using crdtp::json::ConvertCBORToJSON;
 
 namespace blink {
 
@@ -1692,14 +1696,21 @@
     return String();
   auto it =
       frame_navigation_initiator_map_.find(IdentifiersFactory::FrameId(frame));
-  if (it != frame_navigation_initiator_map_.end())
-    return it->value->toJSON();
-  // For navigations, we limit async stack trace to depth 1 to avoid the
-  // base::Value depth limits with Mojo serialization / parsing.
-  // See http://crbug.com/809996.
-  return BuildInitiatorObject(frame->GetDocument(), FetchInitiatorInfo(),
-                              /*max_async_depth=*/1)
-      ->toJSON();
+  std::vector<uint8_t> cbor;
+  if (it != frame_navigation_initiator_map_.end()) {
+    it->value->AppendSerialized(&cbor);
+  } else {
+    // For navigations, we limit async stack trace to depth 1 to avoid the
+    // base::Value depth limits with Mojo serialization / parsing.
+    // See http://crbug.com/809996.
+    cbor = std::move(*BuildInitiatorObject(frame->GetDocument(),
+                                           FetchInitiatorInfo(),
+                                           /*max_async_depth=*/1))
+               .TakeSerialized();
+  }
+  std::vector<uint8_t> json;
+  ConvertCBORToJSON(SpanFrom(cbor), &json);
+  return String(reinterpret_cast<const char*>(json.data()), json.size());
 }
 
 InspectorNetworkAgent::InspectorNetworkAgent(
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 123518dc..7319159 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -83,8 +83,12 @@
 #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/keyboard_codes.h"
+#include "third_party/inspector_protocol/crdtp/json.h"
 #include "v8/include/v8.h"
 
+using crdtp::SpanFrom;
+using crdtp::json::ConvertCBORToJSON;
+
 namespace blink {
 
 using protocol::Maybe;
@@ -972,10 +976,15 @@
   std::unique_ptr<protocol::ListValue> command = protocol::ListValue::create();
   command->pushValue(protocol::StringValue::create(method));
   command->pushValue(protocol::StringValue::create(argument));
+  std::vector<uint8_t> cbor = std::move(*command).TakeSerialized();
+  std::vector<uint8_t> json;
+  ConvertCBORToJSON(SpanFrom(cbor), &json);
   To<LocalFrame>(OverlayMainFrame())
       ->GetScriptController()
       .ExecuteScriptInMainWorld(
-          "dispatch(" + command->toJSONString() + ")",
+          "dispatch(" +
+              String(reinterpret_cast<const char*>(json.data()), json.size()) +
+              ")",
           ScriptSourceLocationType::kInspector,
           ScriptController::kExecuteScriptWhenScriptsDisabled);
 }
@@ -987,10 +996,15 @@
   std::unique_ptr<protocol::ListValue> command = protocol::ListValue::create();
   command->pushValue(protocol::StringValue::create(method));
   command->pushValue(std::move(argument));
+  std::vector<uint8_t> cbor = std::move(*command).TakeSerialized();
+  std::vector<uint8_t> json;
+  ConvertCBORToJSON(SpanFrom(cbor), &json);
   To<LocalFrame>(OverlayMainFrame())
       ->GetScriptController()
       .ExecuteScriptInMainWorld(
-          "dispatch(" + command->toJSONString() + ")",
+          "dispatch(" +
+              String(reinterpret_cast<const char*>(json.data()), json.size()) +
+              ")",
           ScriptSourceLocationType::kInspector,
           ScriptController::kExecuteScriptWhenScriptsDisabled);
 }
diff --git a/third_party/blink/renderer/core/inspector/protocol_parser_test.cc b/third_party/blink/renderer/core/inspector/protocol_parser_test.cc
index 2f6d31b..258d557 100644
--- a/third_party/blink/renderer/core/inspector/protocol_parser_test.cc
+++ b/third_party/blink/renderer/core/inspector/protocol_parser_test.cc
@@ -311,44 +311,40 @@
   ASSERT_TRUE(root.get());
   EXPECT_EQ(Value::TypeObject, root->type());
 
-  root = ParseJSON("{\"number\":9.87654321, \"null\":null , \"S\" : \"str\" }");
-  ASSERT_TRUE(root.get());
-  EXPECT_EQ(Value::TypeObject, root->type());
-  DictionaryValue* object_val = DictionaryValue::cast(root.get());
-  ASSERT_TRUE(object_val);
-  double_val = 0.0;
-  EXPECT_TRUE(object_val->getDouble("number", &double_val));
-  EXPECT_DOUBLE_EQ(9.87654321, double_val);
-  Value* null_val = object_val->get("null");
-  ASSERT_TRUE(null_val);
-  EXPECT_EQ(Value::TypeNull, null_val->type());
-  EXPECT_TRUE(object_val->getString("S", &str_val));
-  EXPECT_EQ("str", str_val);
-
-  // Test newline equivalence.
-  root2 = ParseJSON(
-      "{\n"
-      "  \"number\":9.87654321,\n"
-      "  \"null\":null,\n"
-      "  \"S\":\"str\"\n"
-      "}\n");
-  ASSERT_TRUE(root2.get());
-  EXPECT_EQ(root->toJSONString(), root2->toJSONString());
-
-  root2 = ParseJSON(
-      "{\r\n"
-      "  \"number\":9.87654321,\r\n"
-      "  \"null\":null,\r\n"
-      "  \"S\":\"str\"\r\n"
-      "}\r\n");
-  ASSERT_TRUE(root2.get());
-  EXPECT_EQ(root->toJSONString(), root2->toJSONString());
+  // The three test cases in the loop differ only by their newlines; therefore
+  // the same assertions are valid.
+  for (const char* test :
+       {"{\"number\":9.87654321, \"null\":null , \"S\" : \"str\" }",
+        "{\n"
+        "  \"number\":9.87654321,\n"
+        "  \"null\":null,\n"
+        "  \"S\":\"str\"\n"
+        "}\n",
+        "{\r\n"
+        "  \"number\":9.87654321,\r\n"
+        "  \"null\":null,\r\n"
+        "  \"S\":\"str\"\r\n"
+        "}\r\n"}) {
+    root = ParseJSON(String(test));
+    ASSERT_TRUE(root.get());
+    EXPECT_EQ(Value::TypeObject, root->type());
+    DictionaryValue* object_val = DictionaryValue::cast(root.get());
+    ASSERT_TRUE(object_val);
+    double_val = 0.0;
+    EXPECT_TRUE(object_val->getDouble("number", &double_val));
+    EXPECT_DOUBLE_EQ(9.87654321, double_val);
+    Value* null_val = object_val->get("null");
+    ASSERT_TRUE(null_val);
+    EXPECT_EQ(Value::TypeNull, null_val->type());
+    EXPECT_TRUE(object_val->getString("S", &str_val));
+    EXPECT_EQ("str", str_val);
+  }
 
   // Test nesting
   root = ParseJSON("{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}");
   ASSERT_TRUE(root.get());
   EXPECT_EQ(Value::TypeObject, root->type());
-  object_val = DictionaryValue::cast(root.get());
+  DictionaryValue* object_val = DictionaryValue::cast(root.get());
   ASSERT_TRUE(object_val);
   DictionaryValue* inner_object = object_val->getObject("inner");
   ASSERT_TRUE(inner_object);
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
index c4b2d0a..4e48437 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
@@ -481,7 +481,7 @@
       IntRect clip_rect(local_root_frame->RemoteViewportIntersection());
       // Map clip_rect from the coordinate system of the local root frame to
       // the coordinate system of the remote main frame.
-      clip_rect.MoveBy(local_root_frame->RemoteViewportOffset());
+      clip_rect.MoveBy(IntPoint(local_root_frame->RemoteViewportOffset()));
       does_intersect &=
           intersection_rect.InclusiveIntersect(PhysicalRect(clip_rect));
     }
diff --git a/third_party/blink/renderer/core/layout/geometry/physical_offset.h b/third_party/blink/renderer/core/layout/geometry/physical_offset.h
index f435b7ec..20ddb66 100644
--- a/third_party/blink/renderer/core/layout/geometry/physical_offset.h
+++ b/third_party/blink/renderer/core/layout/geometry/physical_offset.h
@@ -96,6 +96,8 @@
       : left(point.X()), top(point.Y()) {}
   explicit PhysicalOffset(const IntSize& size)
       : left(size.Width()), top(size.Height()) {}
+  explicit PhysicalOffset(const gfx::Point& point)
+      : left(point.x()), top(point.y()) {}
 
   static PhysicalOffset FromFloatPointFloor(const FloatPoint& point) {
     return {LayoutUnit::FromFloatFloor(point.X()),
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 894fc7f..812cded 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -2787,6 +2787,11 @@
       StyleRef().HasVisualOverflowingEffect())
     return false;
 
+  // The HTML element and the LayoutView have a complicated background
+  // painting relationship (see ViewPainter).
+  if (IsDocumentElement())
+    return false;
+
   // Hit tests rects are painted and depend on the size.
   if (HasEffectiveAllowedTouchAction())
     return false;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
index 8e90607..f64d622 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
@@ -356,16 +356,10 @@
     NGBoxStrut border_padding_in_child_writing_mode =
         ComputeBorders(flex_basis_space, child) +
         ComputePadding(flex_basis_space, child_style);
-    NGBoxStrut border_scrollbar_padding_in_child_writing_mode =
-        border_padding_in_child_writing_mode +
-        ComputeScrollbars(flex_basis_space, child);
 
     NGPhysicalBoxStrut physical_border_padding(
         border_padding_in_child_writing_mode.ConvertToPhysical(
             child_style.GetWritingMode(), child_style.Direction()));
-    NGPhysicalBoxStrut physical_border_scrollbar_padding(
-        border_scrollbar_padding_in_child_writing_mode.ConvertToPhysical(
-            child_style.GetWritingMode(), child_style.Direction()));
 
     LayoutUnit main_axis_border_padding =
         is_horizontal_flow_ ? physical_border_padding.HorizontalSum()
@@ -373,9 +367,6 @@
     LayoutUnit cross_axis_border_padding =
         is_horizontal_flow_ ? physical_border_padding.VerticalSum()
                             : physical_border_padding.HorizontalSum();
-    LayoutUnit main_axis_border_scrollbar_padding =
-        is_horizontal_flow_ ? physical_border_scrollbar_padding.HorizontalSum()
-                            : physical_border_scrollbar_padding.VerticalSum();
 
     // TODO(dgrogan): Don't layout every time, just when you need to.
     // Use ChildHasIntrinsicMainAxisSize as a guide.
@@ -558,9 +549,10 @@
           min, layout_result->IntrinsicBlockSize(),
           LengthResolvePhase::kLayout);
     }
-    // TODO(dgrogan): Should this include scrollbar?
-    min_max_sizes_in_main_axis_direction -= main_axis_border_scrollbar_padding;
+    min_max_sizes_in_main_axis_direction -= main_axis_border_padding;
 
+    // TODO(dgrogan): Should min_max_sizes_in_cross_axis_direction include
+    // cross_axis_border_padding?
     NGPhysicalBoxStrut physical_child_margins =
         ComputePhysicalMargins(flex_basis_space, child_style);
     algorithm_
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 338e0192..ca7dab9 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -446,7 +446,7 @@
   void Navigate(const ResourceRequest&,
                 bool should_replace_current_entry,
                 bool is_opener_navigation,
-                bool prevent_sandboxed_download,
+                bool initiator_frame_has_download_sandbox_flag,
                 bool initiator_frame_is_ad,
                 mojo::PendingRemote<mojom::blink::BlobURLToken>) override {}
   unsigned BackForwardLength() override { return 0; }
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 3ca39a9..ecc5038 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -1512,6 +1512,16 @@
   return flags;
 }
 
+SandboxFlags FrameLoader::PendingEffectiveSandboxFlags() const {
+  SandboxFlags flags = forced_sandbox_flags_;
+  if (FrameOwner* frame_owner = frame_->Owner())
+    flags |= frame_owner->GetFramePolicy().sandbox_flags;
+  // Frames need to inherit the sandbox flags of their parent frame.
+  if (Frame* parent_frame = frame_->Tree().Parent())
+    flags |= parent_frame->GetSecurityContext()->GetSandboxFlags();
+  return flags;
+}
+
 void FrameLoader::ModifyRequestForCSP(
     ResourceRequest& resource_request,
     const FetchClientSettingsObject* fetch_client_settings_object,
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h
index 0a168bac..7eb611c 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.h
+++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -155,10 +155,17 @@
   }
 
   // Includes the collection of forced, inherited, and FrameOwner's sandbox
+  // flags, where the FrameOwner's flag is snapshotted from the last committed
+  // navigation. Note: with FeaturePolicyForSandbox the frame owner's sandbox
+  // flags only includes the flags which are *not* implemented as feature
+  // policies already present in the FrameOwner's ContainerPolicy.
+  WebSandboxFlags EffectiveSandboxFlags() const;
+
+  // Includes the collection of forced, inherited, and FrameOwner's sandbox
   // flags. Note: with FeaturePolicyForSandbox the frame owner's sandbox flags
   // only includes the flags which are *not* implemented as feature policies
   // already present in the FrameOwner's ContainerPolicy.
-  WebSandboxFlags EffectiveSandboxFlags() const;
+  WebSandboxFlags PendingEffectiveSandboxFlags() const;
 
   // Modifying itself is done based on |fetch_client_settings_object|.
   // |document_for_logging| is used only for logging, use counters,
diff --git a/third_party/blink/renderer/core/page/context_menu_controller_test.cc b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
index 0d098fb..089b18a 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller_test.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
@@ -577,7 +577,7 @@
   LayoutPoint middle_point((rect->left() + rect->right()) / 2,
                            (rect->top() + rect->bottom()) / 2);
   LocalMainFrame()->MoveRangeSelectionExtent(
-      WebPoint(middle_point.X().ToInt(), middle_point.Y().ToInt()));
+      gfx::Point(middle_point.X().ToInt(), middle_point.Y().ToInt()));
   GetWebView()->MainFrameWidget()->ShowContextMenu(kMenuSourceTouchHandle);
 
   context_menu_data = GetWebFrameClient().GetContextMenuData();
@@ -589,7 +589,7 @@
   // Select all the value of |input| to ensure the start of selection is
   // invisible.
   LocalMainFrame()->MoveRangeSelectionExtent(
-      WebPoint(rect->right(), rect->bottom()));
+      gfx::Point(rect->right(), rect->bottom()));
   GetWebView()->MainFrameWidget()->ShowContextMenu(kMenuSourceTouchHandle);
 
   context_menu_data = GetWebFrameClient().GetContextMenuData();
diff --git a/third_party/blink/renderer/core/page/drag_controller.cc b/third_party/blink/renderer/core/page/drag_controller.cc
index da6fe79..89128af1 100644
--- a/third_party/blink/renderer/core/page/drag_controller.cc
+++ b/third_party/blink/renderer/core/page/drag_controller.cc
@@ -33,7 +33,6 @@
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_drag_data.h"
 #include "third_party/blink/public/platform/web_drag_operation.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_screen_info.h"
 #include "third_party/blink/renderer/core/clipboard/data_object.h"
 #include "third_party/blink/renderer/core/clipboard/data_transfer.h"
diff --git a/third_party/blink/renderer/core/paint/background_image_geometry.cc b/third_party/blink/renderer/core/paint/background_image_geometry.cc
index 15aed13..26d0116e 100644
--- a/third_party/blink/renderer/core/paint/background_image_geometry.cc
+++ b/third_party/blink/renderer/core/paint/background_image_geometry.cc
@@ -729,14 +729,6 @@
     snapped_box_offset =
         LayoutPoint(snapped_box_outset.Left() - snapped_dest_adjust.Left(),
                     snapped_box_outset.Top() - snapped_dest_adjust.Top());
-    // For view backgrounds, the input paint rect is specified in root element
-    // local coordinate (i.e. a transform is applied on the context for
-    // painting), and is expanded to cover the whole canvas. Since left/top is
-    // relative to the paint rect, we need to offset them back.
-    if (painting_view_) {
-      unsnapped_box_offset -= paint_rect.Location();
-      snapped_box_offset -= paint_rect.Location();
-    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/paint/filter_effect_builder.cc b/third_party/blink/renderer/core/paint/filter_effect_builder.cc
index f6fa95b..b86ffe7 100644
--- a/third_party/blink/renderer/core/paint/filter_effect_builder.cc
+++ b/third_party/blink/renderer/core/paint/filter_effect_builder.cc
@@ -27,7 +27,6 @@
 #include "third_party/blink/renderer/core/paint/filter_effect_builder.h"
 
 #include <algorithm>
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/renderer/core/style/filter_operations.h"
 #include "third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h"
 #include "third_party/blink/renderer/core/svg/svg_filter_element.h"
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index e599c53a..50e827e 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -691,8 +691,16 @@
     UpdateStackingNode();
 
     if (old_has_non_isolated_descendant_with_blend_mode !=
-        static_cast<bool>(has_non_isolated_descendant_with_blend_mode_))
+        static_cast<bool>(has_non_isolated_descendant_with_blend_mode_)) {
+      // The LayoutView DisplayItemClient owns painting of the background
+      // of the HTML element. When blending isolation of the HTML element's
+      // descendants change, there will be an addition or removal of an
+      // isolation effect node for the HTML element to add (or remove)
+      // isolated blending, and that case we need to re-paint the LayoutView.
+      if (Parent() && Parent()->IsRootLayer())
+        GetLayoutObject().View()->SetBackgroundNeedsFullPaintInvalidation();
       GetLayoutObject().SetNeedsPaintPropertyUpdate();
+    }
     needs_descendant_dependent_flags_update_ = false;
 
     if (IsSelfPaintingLayer() && needs_visual_overflow_recalc_) {
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index cc1667a..4045cb0b 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -3070,6 +3070,7 @@
 PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::VisualRect()
     const {
   const auto* box = scrollable_area_->GetLayoutBox();
+
   const auto& paint_offset = box->FirstFragment().PaintOffset();
   auto overflow_clip_rect =
       PixelSnappedIntRect(box->OverflowClipRect(paint_offset));
@@ -3083,6 +3084,30 @@
               scrollable_area_->layer_->GraphicsLayerBacking()->VisualRect());
   }
 #endif
+
+  // The HTML element of a document is special, in that it can have a transform,
+  // but the bounds of the painted area of the element still extends beyond
+  // its actual size to encompass the entire viewport canvas. This is
+  // accomplished in ViewPainter by starting with a rect in viewport canvas
+  // space that is equal to the size of the viewport canvas, then mapping it
+  // into the local border box space of the HTML element, and painting a rect
+  // equal to the bounding box of the result. We need to add in that mapped rect
+  // in such cases.
+  const Document& document = box->GetDocument();
+  if (box->IsLayoutView() &&
+      (document.IsXMLDocument() || document.IsHTMLDocument())) {
+    if (const auto* document_element = document.documentElement()) {
+      if (const auto* document_element_object =
+              document_element->GetLayoutObject()) {
+        TransformationMatrix matrix;
+        document_element_object->GetTransformFromContainer(
+            box, PhysicalOffset(), matrix);
+        if (matrix.IsInvertible())
+          result.Unite(matrix.Inverse().MapRect(result));
+      }
+    }
+  }
+
   return result;
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index f0a341c8..0da42d3c 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -3317,9 +3317,8 @@
   DCHECK(!fragment_data);
 
   // We need to update property tree states of paint chunks.
-  if (property_changed >= PaintPropertyChangeType::kNodeAddedOrRemoved) {
+  if (property_changed >= PaintPropertyChangeType::kNodeAddedOrRemoved)
     context_.painting_layer->SetNeedsRepaint();
-  }
 
   return property_changed;
 }
diff --git a/third_party/blink/renderer/core/paint/view_painter.cc b/third_party/blink/renderer/core/paint/view_painter.cc
index 2b8c3cc..6e5571c 100644
--- a/third_party/blink/renderer/core/paint/view_painter.cc
+++ b/third_party/blink/renderer/core/paint/view_painter.cc
@@ -33,6 +33,38 @@
   BlockPainter(layout_view_).Paint(paint_info);
 }
 
+// Behind the root element of the main frame of the page, there is an infinite
+// canvas. This is by default white, but it can be overridden by
+// BaseBackgroundColor on the LocalFrameView.
+// https://drafts.fxtf.org/compositing/#rootgroup
+void ViewPainter::PaintRootGroup(const PaintInfo& paint_info,
+                                 const IntRect& pixel_snapped_background_rect,
+                                 const Document& document,
+                                 const DisplayItemClient& client,
+                                 const PropertyTreeState& state) {
+  if (!document.IsInMainFrame())
+    return;
+  bool should_clear_canvas =
+      document.GetSettings() &&
+      document.GetSettings()->GetShouldClearDocumentBackground();
+
+  Color base_background_color =
+      layout_view_.GetFrameView()->BaseBackgroundColor();
+
+  ScopedPaintChunkProperties frame_view_background_state(
+      paint_info.context.GetPaintController(), state, client,
+      DisplayItem::kDocumentRootBackdrop);
+  GraphicsContext& context = paint_info.context;
+  if (!DrawingRecorder::UseCachedDrawingIfPossible(
+          context, client, DisplayItem::kDocumentRootBackdrop)) {
+    DrawingRecorder recorder(context, client,
+                             DisplayItem::kDocumentRootBackdrop);
+    context.FillRect(
+        pixel_snapped_background_rect, base_background_color,
+        should_clear_canvas ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
+  }
+}
+
 void ViewPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info) {
   if (layout_view_.StyleRef().Visibility() != EVisibility::kVisible)
     return;
@@ -57,7 +89,6 @@
 
   const DisplayItemClient* background_client = &layout_view_;
 
-  base::Optional<ScopedPaintChunkProperties> scoped_scroll_property;
   bool painting_scrolling_background =
       BoxDecorationData::IsPaintingScrollingBackground(paint_info,
                                                        layout_view_);
@@ -71,20 +102,78 @@
     background_rect.Unite(document_rect);
     background_client = &layout_view_.GetScrollableArea()
                              ->GetScrollingBackgroundDisplayItemClient();
-    scoped_scroll_property.emplace(
-        paint_info.context.GetPaintController(),
-        layout_view_.FirstFragment().ContentsProperties(), *background_client,
-        DisplayItem::kDocumentBackground);
   }
 
-  if (layout_view_.HasBoxDecorationBackground()) {
-    PaintBoxDecorationBackgroundInternal(
-        paint_info, PixelSnappedIntRect(background_rect), *background_client);
+  IntRect pixel_snapped_background_rect(PixelSnappedIntRect(background_rect));
+
+  const Document& document = layout_view_.GetDocument();
+
+  PropertyTreeState root_element_background_painting_state =
+      layout_view_.FirstFragment().ContentsProperties();
+
+  base::Optional<ScopedPaintChunkProperties> scoped_properties;
+
+  bool painted_separate_backdrop = false;
+
+  bool should_apply_root_background_behavior =
+      layout_view_.GetDocument().IsHTMLDocument() ||
+      layout_view_.GetDocument().IsXHTMLDocument();
+
+  bool should_paint_background = !paint_info.SkipRootBackground() &&
+                                 layout_view_.HasBoxDecorationBackground();
+
+  LayoutObject* root_object = nullptr;
+  if (layout_view_.GetDocument().documentElement()) {
+    root_object =
+        layout_view_.GetDocument().documentElement()->GetLayoutObject();
+  }
+
+  // For HTML and XHTML documents, the root element may paint in a different
+  // clip, effect or transform state than the LayoutView. For
+  // example, the HTML element may have a clip-path, filter, blend-mode,
+  // opacity or transform.
+  //
+  // In these cases, we should paint the background of the root element in
+  // its LocalBorderBoxProperties() state, as part of the Root Element Group
+  // [1]. In addition, for the main frame of the page, we also need to paint the
+  // default backdrop color in the Root Group [2]. The Root Group paints in
+  // the scrolling space of the LayoutView (i.e. its ContentsProperties()).
+  //
+  // [1] https://drafts.fxtf.org/compositing/#pagebackdrop
+  // [2] https://drafts.fxtf.org/compositing/#rootgroup
+  if (should_paint_background && painting_scrolling_background &&
+      should_apply_root_background_behavior && root_object) {
+    const PropertyTreeState& document_element_state =
+        root_object->FirstFragment().LocalBorderBoxProperties();
+
+    // As an optimization, only paint a separate PaintChunk for the
+    // root group if its property tree state differs from root element
+    // group's. Otherwise we can usually avoid both a separate
+    // PaintChunk and a BeginLayer/EndLayer.
+    if (document_element_state != root_element_background_painting_state) {
+      root_element_background_painting_state = document_element_state;
+      PaintRootGroup(paint_info, pixel_snapped_background_rect, document,
+                     *background_client,
+                     layout_view_.FirstFragment().ContentsProperties());
+      painted_separate_backdrop = true;
+    }
+  }
+
+  if (painting_scrolling_background) {
+    scoped_properties.emplace(paint_info.context.GetPaintController(),
+                              root_element_background_painting_state,
+                              *background_client,
+                              DisplayItem::kDocumentBackground);
+  }
+
+  if (should_paint_background) {
+    PaintRootElementGroup(paint_info, pixel_snapped_background_rect,
+                          *background_client, painted_separate_backdrop);
   }
   if (has_touch_action_rect) {
     BoxPainter(layout_view_)
         .RecordHitTestData(paint_info,
-                           PhysicalRect(PixelSnappedIntRect(background_rect)),
+                           PhysicalRect(pixel_snapped_background_rect),
                            *background_client);
   }
 
@@ -119,22 +208,22 @@
 // View background painting is special in the following ways:
 // 1. The view paints background for the root element, the background
 //    positioning respects the positioning and transformation of the root
-//    element.
+//    element. However, this method assumes that there is already an
+//    PaintChunk being recorded with the LocalBorderBoxProperties of the
+//    root element. Therefore the transform of the root element
+//    are applied via PaintChunksToCcLayer, and not via the display list of the
+//    PaintChunk itself.
 // 2. CSS background-clip is ignored, the background layers always expand to
-//    cover the whole canvas. None of the stacking context effects (except
-//    transformation) on the root element affects the background.
+//    cover the whole canvas.
 // 3. The main frame is also responsible for painting the user-agent-defined
 //    base background color. Conceptually it should be painted by the embedder
 //    but painting it here allows culling and pre-blending optimization when
 //    possible.
-void ViewPainter::PaintBoxDecorationBackgroundInternal(
+void ViewPainter::PaintRootElementGroup(
     const PaintInfo& paint_info,
-    const IntRect& background_rect,
-    const DisplayItemClient& background_client) {
-  // TODO(pdr): Can this check be removed? It is not hit in any test.
-  if (paint_info.SkipRootBackground())
-    return;
-
+    const IntRect& pixel_snapped_background_rect,
+    const DisplayItemClient& background_client,
+    bool painted_separate_backdrop) {
   GraphicsContext& context = paint_info.context;
   if (DrawingRecorder::UseCachedDrawingIfPossible(
           context, background_client, DisplayItem::kDocumentBackground)) {
@@ -149,8 +238,9 @@
                                 (frame_view.BaseBackgroundColor().Alpha() > 0);
   Color base_background_color =
       paints_base_background ? frame_view.BaseBackgroundColor() : Color();
-  Color root_background_color = layout_view_.StyleRef().VisitedDependentColor(
-      GetCSSPropertyBackgroundColor());
+  Color root_element_background_color =
+      layout_view_.StyleRef().VisitedDependentColor(
+          GetCSSPropertyBackgroundColor());
   const LayoutObject* root_object =
       document.documentElement() ? document.documentElement()->GetLayoutObject()
                                  : nullptr;
@@ -162,9 +252,11 @@
   if (force_background_to_white) {
     // If for any reason the view background is not transparent, paint white
     // instead, otherwise keep transparent as is.
-    if (paints_base_background || root_background_color.Alpha() ||
-        layout_view_.StyleRef().BackgroundLayers().AnyLayerHasImage())
-      context.FillRect(background_rect, Color::kWhite, SkBlendMode::kSrc);
+    if (paints_base_background || root_element_background_color.Alpha() ||
+        layout_view_.StyleRef().BackgroundLayers().AnyLayerHasImage()) {
+      context.FillRect(pixel_snapped_background_rect, Color::kWhite,
+                       SkBlendMode::kSrc);
+    }
     return;
   }
 
@@ -177,49 +269,37 @@
   // transform on the document rect to get to the root element space.
   // Local / scroll positioned background images will be painted into scrolling
   // contents layer with root layer scrolling. Therefore we need to switch both
-  // the background_rect and context to documentElement visual space.
+  // the pixel_snapped_background_rect and context to documentElement visual
+  // space.
   bool background_renderable = true;
-  TransformationMatrix transform;
-  IntRect paint_rect = background_rect;
+  IntRect paint_rect = pixel_snapped_background_rect;
   if (!root_object || !root_object->IsBox()) {
     background_renderable = false;
-  } else if (root_object->HasLayer()) {
-    if (BoxDecorationData::IsPaintingScrollingBackground(paint_info,
-                                                         layout_view_)) {
-      transform.Translate(
-          layout_view_.PixelSnappedScrolledContentOffset().Width(),
-          layout_view_.PixelSnappedScrolledContentOffset().Height());
-    }
-    const PaintLayer& root_layer =
-        *ToLayoutBoxModelObject(root_object)->Layer();
-    PhysicalOffset offset;
-    root_layer.ConvertToLayerCoords(nullptr, offset);
-    transform.Translate(offset.left, offset.top);
-    transform.Multiply(
-        root_layer.RenderableTransform(paint_info.GetGlobalPaintFlags()));
-
-    if (!transform.IsInvertible()) {
+  } else {
+    TransformationMatrix transform;
+    root_object->GetTransformFromContainer(root_object->View(),
+                                           PhysicalOffset(), transform);
+    if (!transform.IsInvertible())
       background_renderable = false;
-    } else {
-      bool is_clamped;
-      paint_rect = transform.Inverse()
-                       .ProjectQuad(FloatQuad(background_rect), &is_clamped)
-                       .EnclosingBoundingBox();
-      background_renderable = !is_clamped;
-    }
+    else
+      paint_rect = transform.Inverse().MapRect(pixel_snapped_background_rect);
   }
 
   bool should_clear_canvas =
       paints_base_background &&
       (document.GetSettings() &&
        document.GetSettings()->GetShouldClearDocumentBackground());
+
   if (!background_renderable) {
-    if (base_background_color.Alpha()) {
-      context.FillRect(
-          background_rect, base_background_color,
-          should_clear_canvas ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
-    } else if (should_clear_canvas) {
-      context.FillRect(background_rect, Color(), SkBlendMode::kClear);
+    if (!painted_separate_backdrop) {
+      if (base_background_color.Alpha()) {
+        context.FillRect(
+            pixel_snapped_background_rect, base_background_color,
+            should_clear_canvas ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
+      } else if (should_clear_canvas) {
+        context.FillRect(pixel_snapped_background_rect, Color(),
+                         SkBlendMode::kClear);
+      }
     }
     return;
   }
@@ -231,21 +311,30 @@
               reversed_paint_list, layout_view_.StyleRef().BackgroundLayers());
   DCHECK(reversed_paint_list.size());
 
-  // If the root background color is opaque, isolation group can be skipped
-  // because the canvas
-  // will be cleared by root background color.
-  if (!root_background_color.HasAlpha())
-    should_draw_background_in_separate_buffer = false;
+  if (painted_separate_backdrop) {
+    should_draw_background_in_separate_buffer = true;
+  } else {
+    // If the root background color is opaque, isolation group can be skipped
+    // because the canvas
+    // will be cleared by root background color.
+    if (!root_element_background_color.HasAlpha())
+      should_draw_background_in_separate_buffer = false;
 
-  // We are going to clear the canvas with transparent pixels, isolation group
-  // can be skipped.
-  if (!base_background_color.Alpha() && should_clear_canvas)
-    should_draw_background_in_separate_buffer = false;
+    // We are going to clear the canvas with transparent pixels, isolation group
+    // can be skipped.
+    if (!base_background_color.Alpha() && should_clear_canvas)
+      should_draw_background_in_separate_buffer = false;
+  }
 
-  if (should_draw_background_in_separate_buffer) {
+  // Only use BeginLayer if not only we should draw in a separate buffer, but
+  // we also didn't paint a separate backdrop. Separate backdrops are always
+  // painted when there is any effect on the root element, such as a blend
+  // mode. An extra BeginLayer will result in incorrect blend isolation if
+  // it is added on top of any effect on the root element.
+  if (should_draw_background_in_separate_buffer && !painted_separate_backdrop) {
     if (base_background_color.Alpha()) {
       context.FillRect(
-          background_rect, base_background_color,
+          paint_rect, base_background_color,
           should_clear_canvas ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
     }
     context.BeginLayer();
@@ -253,8 +342,8 @@
 
   Color combined_background_color =
       should_draw_background_in_separate_buffer
-          ? root_background_color
-          : base_background_color.Blend(root_background_color);
+          ? root_element_background_color
+          : base_background_color.Blend(root_element_background_color);
 
   if (combined_background_color != frame_view.BaseBackgroundColor())
     context.GetPaintController().SetFirstPainted();
@@ -264,13 +353,13 @@
         RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
       recorder.SetKnownToBeOpaque();
     context.FillRect(
-        background_rect, combined_background_color,
+        paint_rect, combined_background_color,
         (should_draw_background_in_separate_buffer || should_clear_canvas)
             ? SkBlendMode::kSrc
             : SkBlendMode::kSrcOver);
   } else if (should_clear_canvas &&
              !should_draw_background_in_separate_buffer) {
-    context.FillRect(background_rect, Color(), SkBlendMode::kClear);
+    context.FillRect(paint_rect, Color(), SkBlendMode::kClear);
   }
 
   BackgroundImageGeometry geometry(layout_view_);
@@ -279,22 +368,20 @@
     DCHECK(fill_layer->Clip() == EFillBox::kBorder);
 
     if (BackgroundImageGeometry::ShouldUseFixedAttachment(*fill_layer)) {
-      box_model_painter.PaintFillLayer(paint_info, Color(), *fill_layer,
-                                       PhysicalRect(background_rect),
-                                       kBackgroundBleedNone, geometry);
+      box_model_painter.PaintFillLayer(
+          paint_info, Color(), *fill_layer,
+          PhysicalRect(pixel_snapped_background_rect), kBackgroundBleedNone,
+          geometry);
     } else {
-      context.Save();
-      // TODO(trchen): We should be able to handle 3D-transformed root
-      // background with slimming paint by using transform display items.
-      context.ConcatCTM(transform.ToAffineTransform());
+      PhysicalRect painting_rect(paint_rect);
+      painting_rect.Move(root_object->FirstFragment().PaintOffset());
       box_model_painter.PaintFillLayer(paint_info, Color(), *fill_layer,
-                                       PhysicalRect(paint_rect),
-                                       kBackgroundBleedNone, geometry);
-      context.Restore();
+                                       painting_rect, kBackgroundBleedNone,
+                                       geometry);
     }
   }
 
-  if (should_draw_background_in_separate_buffer)
+  if (should_draw_background_in_separate_buffer && !painted_separate_backdrop)
     context.EndLayer();
 }
 
diff --git a/third_party/blink/renderer/core/paint/view_painter.h b/third_party/blink/renderer/core/paint/view_painter.h
index 652ffd6..89a6d89 100644
--- a/third_party/blink/renderer/core/paint/view_painter.h
+++ b/third_party/blink/renderer/core/paint/view_painter.h
@@ -11,8 +11,10 @@
 
 struct PaintInfo;
 class DisplayItemClient;
+class Document;
 class IntRect;
 class LayoutView;
+class PropertyTreeState;
 
 class ViewPainter {
   STACK_ALLOCATED();
@@ -26,10 +28,16 @@
  private:
   const LayoutView& layout_view_;
 
-  void PaintBoxDecorationBackgroundInternal(
-      const PaintInfo&,
-      const IntRect& background_rect,
-      const DisplayItemClient& background_client);
+  void PaintRootElementGroup(const PaintInfo&,
+                             const IntRect& pixel_snapped_background_rect,
+                             const DisplayItemClient& background_client,
+                             bool painted_separate_backdrop);
+
+  void PaintRootGroup(const PaintInfo& paint_info,
+                      const IntRect& pixel_snapped_background_rect,
+                      const Document&,
+                      const DisplayItemClient& background_client,
+                      const PropertyTreeState& state);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/scroll/scrollbar_theme.cc b/third_party/blink/renderer/core/scroll/scrollbar_theme.cc
index 686cb54..1ec12943 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar_theme.cc
+++ b/third_party/blink/renderer/core/scroll/scrollbar_theme.cc
@@ -30,7 +30,6 @@
 #include "cc/input/scrollbar.h"
 #include "third_party/blink/public/common/input/web_mouse_event.h"
 #include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme_overlay_mock.h"
diff --git a/third_party/blink/renderer/core/svg/animation/smil_time_container.cc b/third_party/blink/renderer/core/svg/animation/smil_time_container.cc
index 97fb0b43..6cd93629 100644
--- a/third_party/blink/renderer/core/svg/animation/smil_time_container.cc
+++ b/third_party/blink/renderer/core/svg/animation/smil_time_container.cc
@@ -35,6 +35,7 @@
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/svg/animation/element_smil_animations.h"
 #include "third_party/blink/renderer/core/svg/animation/svg_smil_element.h"
+#include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
 #include "third_party/blink/renderer/core/svg/svg_svg_element.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 
@@ -64,6 +65,7 @@
     : frame_scheduling_state_(kIdle),
       started_(false),
       paused_(false),
+      should_dispatch_events_(!SVGImage::IsInSVGImage(&owner)),
       document_order_indexes_dirty_(false),
       is_updating_intervals_(false),
       wakeup_timer_(
@@ -457,7 +459,7 @@
     SVGSMILElement* element = priority_queue_.MinElement();
     element->UpdateInterval(document_time);
     auto events_to_dispatch = element->UpdateActiveState(document_time);
-    if (events_to_dispatch)
+    if (should_dispatch_events_ && events_to_dispatch)
       element->DispatchEvents(events_to_dispatch);
     SMILTime next_interval_time =
         element->ComputeNextIntervalTime(document_time);
diff --git a/third_party/blink/renderer/core/svg/animation/smil_time_container.h b/third_party/blink/renderer/core/svg/animation/smil_time_container.h
index 60260b2..3129aca9 100644
--- a/third_party/blink/renderer/core/svg/animation/smil_time_container.h
+++ b/third_party/blink/renderer/core/svg/animation/smil_time_container.h
@@ -72,6 +72,7 @@
 
   // Advance the animation timeline a single frame.
   void AdvanceFrameForTesting();
+  bool EventsDisabled() const { return !should_dispatch_events_; }
 
   void Trace(blink::Visitor*);
 
@@ -133,6 +134,7 @@
   bool started_ : 1;  // The timeline has been started.
   bool paused_ : 1;   // The timeline is paused.
 
+  const bool should_dispatch_events_ : 1;
   bool document_order_indexes_dirty_ : 1;
   bool is_updating_intervals_;
 
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.h b/third_party/blink/renderer/core/svg/graphics/svg_image.h
index 8fce541..babb957 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.h
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.h
@@ -247,6 +247,7 @@
   FRIEND_TEST_ALL_PREFIXES(SVGImageTest, LayoutShiftTrackerDisabled);
   FRIEND_TEST_ALL_PREFIXES(SVGImageTest, SetSizeOnVisualViewport);
   FRIEND_TEST_ALL_PREFIXES(SVGImageTest, IsSizeAvailable);
+  FRIEND_TEST_ALL_PREFIXES(SVGImageTest, DisablesSMILEvents);
 };
 
 DEFINE_IMAGE_TYPE_CASTS(SVGImage);
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc b/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
index 19feaf44..70ff534 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
@@ -15,7 +15,9 @@
 #include "third_party/blink/renderer/core/layout/layout_shift_tracker.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_time_container.h"
 #include "third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h"
+#include "third_party/blink/renderer/core/svg/svg_svg_element.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
@@ -252,6 +254,18 @@
   EXPECT_FALSE(GetImage().IsSizeAvailable());
 }
 
+TEST_F(SVGImageTest, DisablesSMILEvents) {
+  const bool kShouldPause = true;
+  Load(kAnimatedDocument, kShouldPause);
+  LocalFrame* local_frame =
+      To<LocalFrame>(GetImage().GetPageForTesting()->MainFrame());
+  EXPECT_TRUE(local_frame->GetDocument()->IsSVGDocument());
+  SMILTimeContainer* time_container =
+      To<SVGSVGElement>(local_frame->GetDocument()->documentElement())
+          ->TimeContainer();
+  EXPECT_TRUE(time_container->EventsDisabled());
+}
+
 TEST_F(SVGImageTest, DarkModeClassification) {
   DarkModeImageClassifier::Features features;
 
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index 4d06f1f9..0a7b3517 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -31,7 +31,6 @@
 #include "third_party/blink/public/web/web_ax_object.h"
 
 #include "third_party/blink/public/platform/web_float_rect.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
@@ -705,14 +704,14 @@
 // that (0, 0) is the top left of the visual viewport. In other words, the
 // point has the VisualViewport scale applied, but not the VisualViewport
 // offset. crbug.com/459591.
-WebAXObject WebAXObject::HitTest(const WebPoint& point) const {
+WebAXObject WebAXObject::HitTest(const gfx::Point& point) const {
   if (IsDetached())
     return WebAXObject();
 
   ScopedActionAnnotator annotater(private_.Get());
   IntPoint contents_point =
       private_->DocumentFrameView()->SoonToBeRemovedUnscaledViewportToContents(
-          point);
+          IntPoint(point));
   AXObject* hit = private_->AccessibilityHitTest(contents_point);
 
   if (hit)
@@ -1528,32 +1527,32 @@
   return private_->IsScrollableContainer();
 }
 
-WebPoint WebAXObject::GetScrollOffset() const {
+gfx::Point WebAXObject::GetScrollOffset() const {
   if (IsDetached())
-    return WebPoint();
+    return gfx::Point();
 
   return private_->GetScrollOffset();
 }
 
-WebPoint WebAXObject::MinimumScrollOffset() const {
+gfx::Point WebAXObject::MinimumScrollOffset() const {
   if (IsDetached())
-    return WebPoint();
+    return gfx::Point();
 
   return private_->MinimumScrollOffset();
 }
 
-WebPoint WebAXObject::MaximumScrollOffset() const {
+gfx::Point WebAXObject::MaximumScrollOffset() const {
   if (IsDetached())
-    return WebPoint();
+    return gfx::Point();
 
   return private_->MaximumScrollOffset();
 }
 
-void WebAXObject::SetScrollOffset(const WebPoint& offset) const {
+void WebAXObject::SetScrollOffset(const gfx::Point& offset) const {
   if (IsDetached())
     return;
 
-  private_->SetScrollOffset(offset);
+  private_->SetScrollOffset(IntPoint(offset));
 }
 
 void WebAXObject::Dropeffects(
@@ -1630,12 +1629,12 @@
       blink_vertical_scroll_alignment);
 }
 
-bool WebAXObject::ScrollToGlobalPoint(const WebPoint& point) const {
+bool WebAXObject::ScrollToGlobalPoint(const gfx::Point& point) const {
   if (IsDetached())
     return false;
 
   ScopedActionAnnotator annotater(private_.Get());
-  return private_->RequestScrollToGlobalPointAction(point);
+  return private_->RequestScrollToGlobalPointAction(IntPoint(point));
 }
 
 void WebAXObject::Swap(WebAXObject& other) {
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_display_cutout_delegate.cc b/third_party/blink/renderer/modules/media_controls/media_controls_display_cutout_delegate.cc
index f0a8544..201b22de 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_display_cutout_delegate.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_display_cutout_delegate.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/modules/media_controls/media_controls_display_cutout_delegate.h"
 
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/events/touch_event.h"
 #include "third_party/blink/renderer/core/frame/viewport_data.h"
@@ -18,13 +17,13 @@
 
 namespace {
 
-WebPoint ExtractWebPoint(Touch* touch) {
-  return WebPoint(touch->pageX(), touch->pageY());
+gfx::Point ExtractTouchPoint(Touch* touch) {
+  return gfx::Point(touch->pageX(), touch->pageY());
 }
 
-double CalculateDistance(WebPoint first, WebPoint second) {
-  double dx = first.x - second.x;
-  double dy = first.y - second.y;
+double CalculateDistance(gfx::Point first, gfx::Point second) {
+  double dx = first.x() - second.x();
+  double dy = first.y() - second.y();
   return sqrt(dx * dx + dy * dy);
 }
 
@@ -120,8 +119,8 @@
     previous_.reset();
 
   // Extract the two touch points and calculate the distance.
-  WebPoint first = ExtractWebPoint(event->touches()->item(0));
-  WebPoint second = ExtractWebPoint(event->touches()->item(1));
+  gfx::Point first = ExtractTouchPoint(event->touches()->item(0));
+  gfx::Point second = ExtractTouchPoint(event->touches()->item(1));
   double distance = CalculateDistance(first, second);
   Direction direction = Direction::kUnknown;
 
diff --git a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc
index b8ba2bf..8adc6fec 100644
--- a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc
+++ b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc
@@ -338,7 +338,7 @@
   IntPoint bounds = video->BoundsInViewport().Center();
 
   frame->PerformMediaPlayerAction(
-      WebPoint(bounds.X(), bounds.Y()),
+      bounds,
       MediaPlayerAction(MediaPlayerAction::Type::kPictureInPicture, true));
 }
 
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation.cc b/third_party/blink/renderer/platform/animation/compositor_animation.cc
index 1c0230f3..7a16e73d 100644
--- a/third_party/blink/renderer/platform/animation/compositor_animation.cc
+++ b/third_party/blink/renderer/platform/animation/compositor_animation.cc
@@ -22,7 +22,7 @@
     cc::WorkletAnimationId worklet_animation_id,
     const String& name,
     double playback_rate,
-    std::unique_ptr<CompositorScrollTimeline> scroll_timeline,
+    scoped_refptr<CompositorScrollTimeline> scroll_timeline,
     std::unique_ptr<cc::AnimationOptions> options,
     std::unique_ptr<cc::AnimationEffectTimings> effect_timings) {
   return std::make_unique<CompositorAnimation>(cc::WorkletAnimation::Create(
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation.h b/third_party/blink/renderer/platform/animation/compositor_animation.h
index ed663c3..771619c 100644
--- a/third_party/blink/renderer/platform/animation/compositor_animation.h
+++ b/third_party/blink/renderer/platform/animation/compositor_animation.h
@@ -37,7 +37,7 @@
       cc::WorkletAnimationId,
       const String& name,
       double playback_rate,
-      std::unique_ptr<CompositorScrollTimeline>,
+      scoped_refptr<CompositorScrollTimeline>,
       std::unique_ptr<cc::AnimationOptions>,
       std::unique_ptr<cc::AnimationEffectTimings> effect_timings);
 
diff --git a/third_party/blink/renderer/platform/audio/audio_array.h b/third_party/blink/renderer/platform/audio/audio_array.h
index 7ff276e5..0a8c263e 100644
--- a/third_party/blink/renderer/platform/audio/audio_array.h
+++ b/third_party/blink/renderer/platform/audio/audio_array.h
@@ -62,6 +62,8 @@
     CHECK_LE(n, std::numeric_limits<unsigned>::max() / sizeof(T));
     uint32_t initial_size = static_cast<uint32_t>(sizeof(T) * n);
 
+    // Minimmum alignment requirements for arrays so that we can use
+    // SIMD.
 #if defined(ARCH_CPU_X86_FAMILY) || defined(WTF_USE_WEBAUDIO_FFMPEG)
     const unsigned kAlignment = 32;
 #else
@@ -71,32 +73,17 @@
     if (allocation_)
       WTF::Partitions::FastFree(allocation_);
 
-    bool is_allocation_good = false;
+    // Always allocate extra space so that we are guaranteed to get
+    // the desired alignment.  Some memory is wasted, but it should be
+    // small since most arrays are probably at least 128 floats (or
+    // doubles).
+    unsigned total = base::CheckAdd(initial_size, kAlignment).ValueOrDie();
+    allocation_ = static_cast<T*>(WTF::Partitions::FastZeroedMalloc(
+        total, WTF_HEAP_PROFILER_TYPE_NAME(AudioArray<T>)));
+    CHECK(allocation_);
 
-    while (!is_allocation_good) {
-      // Initially we try to allocate the exact size, but if it's not aligned
-      // then we'll have to reallocate and from then on allocate extra.
-      static unsigned extra_allocation_bytes = 0;
-
-      unsigned total =
-          base::CheckAdd(initial_size, extra_allocation_bytes).ValueOrDie();
-      T* allocation = static_cast<T*>(WTF::Partitions::FastZeroedMalloc(
-          total, WTF_HEAP_PROFILER_TYPE_NAME(AudioArray<T>)));
-      CHECK(allocation);
-
-      T* aligned_data = AlignedAddress(allocation, kAlignment);
-
-      if (aligned_data == allocation || extra_allocation_bytes == kAlignment) {
-        allocation_ = allocation;
-        aligned_data_ = aligned_data;
-        size_ = static_cast<uint32_t>(n);
-        is_allocation_good = true;
-      } else {
-        // always allocate extra after the first alignment failure.
-        extra_allocation_bytes = kAlignment;
-        WTF::Partitions::FastFree(allocation);
-      }
-    }
+    aligned_data_ = AlignedAddress(allocation_, kAlignment);
+    size_ = static_cast<uint32_t>(n);
   }
 
   T* Data() { return aligned_data_; }
@@ -140,6 +127,8 @@
   }
 
  private:
+  // Return an address that is aligned to an |alignment| boundary.
+  // |alignment| MUST be a power of two!
   static T* AlignedAddress(T* address, intptr_t alignment) {
     intptr_t value = reinterpret_cast<intptr_t>(address);
     return reinterpret_cast<T*>((value + alignment - 1) & ~(alignment - 1));
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index 2dccdb5b..5efa8c9 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -38,7 +38,6 @@
 #include "cc/paint/display_item_list.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_float_rect.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
 #include "third_party/blink/renderer/platform/geometry/geometry_as_json.h"
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item.cc b/third_party/blink/renderer/platform/graphics/paint/display_item.cc
index bb0ae38..54f285c2 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item.cc
@@ -78,6 +78,7 @@
     DEBUG_STRING_CASE(ClippingMask);
     DEBUG_STRING_CASE(ColumnRules);
     DEBUG_STRING_CASE(DebugDrawing);
+    DEBUG_STRING_CASE(DocumentRootBackdrop);
     DEBUG_STRING_CASE(DocumentBackground);
     DEBUG_STRING_CASE(DragImage);
     DEBUG_STRING_CASE(DragCaret);
@@ -123,6 +124,7 @@
     DEBUG_STRING_CASE(ForeignLayerDevToolsOverlay);
     DEBUG_STRING_CASE(ForeignLayerPlugin);
     DEBUG_STRING_CASE(ForeignLayerVideo);
+    DEBUG_STRING_CASE(ForeignLayerRemoteFrame);
     DEBUG_STRING_CASE(ForeignLayerWrapper);
     DEBUG_STRING_CASE(ForeignLayerContentsWrapper);
     DEBUG_STRING_CASE(ForeignLayerLinkHighlight);
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item.h b/third_party/blink/renderer/platform/graphics/paint/display_item.h
index 320f0d0b..3b4a4e7 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item.h
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item.h
@@ -61,6 +61,7 @@
     kClippingMask,
     kColumnRules,
     kDebugDrawing,
+    kDocumentRootBackdrop,
     kDocumentBackground,
     kDragImage,
     kDragCaret,
@@ -100,6 +101,7 @@
     kForeignLayerDevToolsOverlay,
     kForeignLayerPlugin,
     kForeignLayerVideo,
+    kForeignLayerRemoteFrame,
     kForeignLayerWrapper,
     kForeignLayerContentsWrapper,
     kForeignLayerLinkHighlight,
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc
index fde1aedb..7b06b23f 100644
--- a/third_party/blink/renderer/platform/heap/heap.cc
+++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -166,6 +166,8 @@
   weak_table_worklist_.reset(new WeakTableWorklist);
   backing_store_callback_worklist_.reset(new BackingStoreCallbackWorklist());
   v8_references_worklist_.reset(new V8ReferencesWorklist());
+  not_safe_to_concurrently_trace_worklist_.reset(
+      new NotSafeToConcurrentlyTraceWorklist());
   DCHECK(ephemeron_callbacks_.IsEmpty());
 }
 
@@ -176,6 +178,7 @@
   weak_callback_worklist_.reset(nullptr);
   weak_table_worklist_.reset();
   v8_references_worklist_.reset();
+  not_safe_to_concurrently_trace_worklist_.reset();
   ephemeron_callbacks_.clear();
 
   // The fixed point iteration may have found not-fully-constructed objects.
@@ -334,6 +337,15 @@
         break;
 
       finished = DrainWorklistWithDeadline(
+          deadline, not_safe_to_concurrently_trace_worklist_.get(),
+          [visitor](const MarkingItem& item) {
+            item.callback(visitor, item.base_object_payload);
+          },
+          WorklistTaskId::MutatorThread);
+      if (!finished)
+        break;
+
+      finished = DrainWorklistWithDeadline(
           deadline, marking_worklist_.get(),
           [visitor](const MarkingItem& item) {
             HeapObjectHeader* header =
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index 14913bb..e2432558 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -99,6 +99,8 @@
 using BackingStoreCallbackWorklist =
     Worklist<BackingStoreCallbackItem, 16 /* local entries */>;
 using V8ReferencesWorklist = Worklist<V8Reference, 16 /* local entries */>;
+using NotSafeToConcurrentlyTraceWorklist =
+    Worklist<MarkingItem, 256 /* local entries */>;
 
 class PLATFORM_EXPORT HeapAllocHooks {
   STATIC_ONLY(HeapAllocHooks);
@@ -260,6 +262,10 @@
     return v8_references_worklist_.get();
   }
 
+  NotSafeToConcurrentlyTraceWorklist* GetNotSafeToConcurrentlyTraceWorklist()
+      const {
+    return not_safe_to_concurrently_trace_worklist_.get();
+  }
   // Register an ephemeron table for fixed-point iteration.
   void RegisterWeakTable(void* container_object,
                          EphemeronCallback);
@@ -445,6 +451,9 @@
   // to V8.
   std::unique_ptr<V8ReferencesWorklist> v8_references_worklist_;
 
+  std::unique_ptr<NotSafeToConcurrentlyTraceWorklist>
+      not_safe_to_concurrently_trace_worklist_;
+
   // No duplicates allowed for ephemeron callbacks. Hence, we use a hashmap
   // with the key being the HashTable.
   WTF::HashMap<void*, EphemeronCallback> ephemeron_callbacks_;
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.cc b/third_party/blink/renderer/platform/heap/marking_visitor.cc
index 5ac32c2..865bbf79 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -288,7 +288,10 @@
 ConcurrentMarkingVisitor::ConcurrentMarkingVisitor(ThreadState* state,
                                                    MarkingMode marking_mode,
                                                    int task_id)
-    : MarkingVisitorBase(state, marking_mode, task_id) {
+    : MarkingVisitorBase(state, marking_mode, task_id),
+      not_safe_to_concurrently_trace_worklist_(
+          Heap().GetNotSafeToConcurrentlyTraceWorklist(),
+          task_id) {
   DCHECK(!state->CheckThread());
   DCHECK_NE(WorklistTaskId::MutatorThread, task_id);
 }
@@ -299,6 +302,7 @@
   not_fully_constructed_worklist_.FlushToGlobal();
   weak_callback_worklist_.FlushToGlobal();
   weak_table_worklist_.FlushToGlobal();
+  not_safe_to_concurrently_trace_worklist_.FlushToGlobal();
   // Flush compaction worklists.
   movable_reference_worklist_.FlushToGlobal();
   backing_store_callback_worklist_.FlushToGlobal();
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.h b/third_party/blink/renderer/platform/heap/marking_visitor.h
index 81eb7e1..97544d7 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.h
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.h
@@ -243,6 +243,15 @@
 
   // Concurrent variant of MarkingVisitorCommon::AccountMarkedBytes.
   void AccountMarkedBytesSafe(HeapObjectHeader*);
+
+  bool ConcurrentTracingBailOut(TraceDescriptor desc) override {
+    not_safe_to_concurrently_trace_worklist_.Push(desc);
+    return true;
+  }
+
+ private:
+  NotSafeToConcurrentlyTraceWorklist::View
+      not_safe_to_concurrently_trace_worklist_;
 };
 
 ALWAYS_INLINE void ConcurrentMarkingVisitor::AccountMarkedBytesSafe(
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index 2cc8d8d5..5c1ced9a 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -145,6 +145,11 @@
     ScheduleTask();
   }
 
+  void Restart() {
+    DCHECK(!task_.IsActive());
+    ScheduleTask();
+  }
+
   // Cancels incremental marking task in case there is any pending.
   void Cancel() { task_.Cancel(); }
 
@@ -635,7 +640,8 @@
       DCHECK(CheckThread());
       VERIFY_STATE_TRANSITION(gc_state_ == kNoGCScheduled ||
                               gc_state_ == kIncrementalMarkingStepScheduled ||
-                              gc_state_ == kIncrementalGCScheduled);
+                              gc_state_ == kIncrementalGCScheduled ||
+                              gc_state_ == kIncrementalMarkingStepPaused);
       break;
     case kIncrementalMarkingFinalizeScheduled:
       DCHECK(CheckThread());
@@ -1288,6 +1294,13 @@
   return false;
 }
 
+void ThreadState::RestartIncrementalMarkingIfPaused() {
+  if (GetGCState() != ThreadState::kIncrementalMarkingStepPaused)
+    return;
+  SetGCState(ThreadState::kIncrementalMarkingStepScheduled);
+  incremental_marking_scheduler_->Restart();
+}
+
 void ThreadState::CollectGarbage(BlinkGC::CollectionType collection_type,
                                  BlinkGC::StackState stack_state,
                                  BlinkGC::MarkingType marking_type,
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index c4e7880..a2b8af53 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -299,6 +299,8 @@
   void EnableIncrementalMarkingBarrier();
   void DisableIncrementalMarkingBarrier();
 
+  void RestartIncrementalMarkingIfPaused();
+
   void CompleteSweep();
 
   // Returns whether it is currently allowed to allocate an object. Mainly used
diff --git a/third_party/blink/renderer/platform/heap/trace_traits.h b/third_party/blink/renderer/platform/heap/trace_traits.h
index 583aa7f7..f8b7aad 100644
--- a/third_party/blink/renderer/platform/heap/trace_traits.h
+++ b/third_party/blink/renderer/platform/heap/trace_traits.h
@@ -183,6 +183,9 @@
   }
 
   static void Trace(blink::Visitor* visitor, void* self) {
+    if (visitor->ConcurrentTracingBailOut({self, &Trace}))
+      return;
+
     static_assert(!WTF::IsWeak<T>::value,
                   "Weakness is not supported in HeapVector and HeapDeque");
     if (WTF::IsTraceableInCollectionTrait<Traits>::value) {
@@ -217,6 +220,9 @@
 
   template <WTF::WeakHandlingFlag WeakHandling = WTF::kNoWeakHandling>
   static void Trace(Visitor* visitor, void* self) {
+    if (visitor->ConcurrentTracingBailOut({self, &Trace}))
+      return;
+
     static_assert(WTF::IsTraceableInCollectionTrait<Traits>::value ||
                       WTF::IsWeak<ValueType>::value,
                   "T should not be traced");
diff --git a/third_party/blink/renderer/platform/heap/unified_heap_controller.cc b/third_party/blink/renderer/platform/heap/unified_heap_controller.cc
index 1a43d92..ed11cd4 100644
--- a/third_party/blink/renderer/platform/heap/unified_heap_controller.cc
+++ b/third_party/blink/renderer/platform/heap/unified_heap_controller.cc
@@ -134,6 +134,9 @@
     base::TimeTicks deadline =
         base::TimeTicks() + base::TimeDelta::FromMillisecondsD(deadline_in_ms);
     is_tracing_done_ = thread_state_->MarkPhaseAdvanceMarking(deadline);
+    if (!is_tracing_done_) {
+      thread_state_->RestartIncrementalMarkingIfPaused();
+    }
     if (base::FeatureList::IsEnabled(
             blink::features::kBlinkHeapConcurrentMarking)) {
       is_tracing_done_ =
diff --git a/third_party/blink/renderer/platform/heap/visitor.h b/third_party/blink/renderer/platform/heap/visitor.h
index 67e195fe..e4424e3 100644
--- a/third_party/blink/renderer/platform/heap/visitor.h
+++ b/third_party/blink/renderer/platform/heap/visitor.h
@@ -282,6 +282,13 @@
   //   are aware of custom weakness and won't resize their backings.
   virtual void RegisterWeakCallback(WeakCallback callback, void* parameter) = 0;
 
+  // TODO(crbug/986235): ConcurrentTracingBailOut is part of a temporary
+  // bailout mechanism to avoid tracing collections on concurrent threads.
+  // This method and any usage of it will be removed as soon as making all
+  // collections cuncurrent-safe is finished.
+  // The same also applies to NotSafeToConcurrentlyTraceWorklist in heap.h.
+  virtual bool ConcurrentTracingBailOut(TraceDescriptor desc) { return false; }
+
  protected:
   template <typename T>
   static inline TraceDescriptor TraceDescriptorFor(const T* traceable) {
diff --git a/third_party/blink/renderer/platform/mojo/geometry.typemap b/third_party/blink/renderer/platform/mojo/geometry.typemap
index f247c50..280e8a57 100644
--- a/third_party/blink/renderer/platform/mojo/geometry.typemap
+++ b/third_party/blink/renderer/platform/mojo/geometry.typemap
@@ -7,7 +7,6 @@
   "//ui/gfx/geometry/quaternion.h",
   "//ui/gfx/geometry/vector3d_f.h",
   "//third_party/blink/public/platform/web_float_rect.h",
-  "//third_party/blink/public/platform/web_point.h",
   "//third_party/blink/public/platform/web_rect.h",
   "//third_party/blink/public/platform/web_size.h",
   "//third_party/blink/renderer/platform/geometry/float_point_3d.h",
diff --git a/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.cc b/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.cc
index 3671b3c..d8b812cb 100644
--- a/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.cc
+++ b/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.cc
@@ -33,15 +33,6 @@
 }
 
 // static
-bool StructTraits<gfx::mojom::PointDataView, ::blink::WebPoint>::Read(
-    gfx::mojom::PointDataView data,
-    ::blink::WebPoint* out) {
-  out->x = data.x();
-  out->y = data.y();
-  return true;
-}
-
-// static
 bool StructTraits<gfx::mojom::SizeDataView, ::blink::WebSize>::Read(
     gfx::mojom::SizeDataView data,
     ::blink::WebSize* out) {
diff --git a/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.h b/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.h
index 3e5d389..93b9d83 100644
--- a/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.h
+++ b/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_GEOMETRY_MOJOM_TRAITS_H_
 
 #include "third_party/blink/public/platform/web_float_rect.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_size.h"
 #include "ui/gfx/geometry/mojom/geometry.mojom-shared.h"
@@ -14,13 +13,6 @@
 namespace mojo {
 
 template <>
-struct StructTraits<gfx::mojom::PointDataView, ::blink::WebPoint> {
-  static int x(const ::blink::WebPoint& point) { return point.x; }
-  static int y(const ::blink::WebPoint& point) { return point.y; }
-  static bool Read(gfx::mojom::PointDataView, ::blink::WebPoint* out);
-};
-
-template <>
 struct StructTraits<gfx::mojom::RectFDataView, ::blink::WebFloatRect> {
   static float x(const ::blink::WebFloatRect& rect) { return rect.x; }
   static float y(const ::blink::WebFloatRect& rect) { return rect.y; }
diff --git a/third_party/blink/renderer/platform/wtf/deque.h b/third_party/blink/renderer/platform/wtf/deque.h
index e3e444e..4cc77c9 100644
--- a/third_party/blink/renderer/platform/wtf/deque.h
+++ b/third_party/blink/renderer/platform/wtf/deque.h
@@ -677,6 +677,14 @@
 template <typename VisitorDispatcher, typename A>
 std::enable_if_t<A::kIsGarbageCollected>
 Deque<T, inlineCapacity, Allocator>::Trace(VisitorDispatcher visitor) {
+  // Bail out for concurrent marking.
+  if (visitor->ConcurrentTracingBailOut(
+          {this, [](blink::Visitor* visitor, void* object) {
+             reinterpret_cast<Deque<T, inlineCapacity, Allocator>*>(object)
+                 ->Trace(visitor);
+           }}))
+    return;
+
   static_assert(Allocator::kIsGarbageCollected,
                 "Garbage collector must be enabled.");
   if (buffer_.HasOutOfLineBuffer()) {
diff --git a/third_party/blink/renderer/platform/wtf/hash_table.h b/third_party/blink/renderer/platform/wtf/hash_table.h
index 8ada5a8..354467e 100644
--- a/third_party/blink/renderer/platform/wtf/hash_table.h
+++ b/third_party/blink/renderer/platform/wtf/hash_table.h
@@ -2122,6 +2122,15 @@
 std::enable_if_t<A::kIsGarbageCollected>
 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
     Trace(VisitorDispatcher visitor) {
+  // bail out for concurrent marking
+  if (visitor->ConcurrentTracingBailOut(
+          {this, [](blink::Visitor* visitor, void* object) {
+             reinterpret_cast<HashTable<Key, Value, Extractor, HashFunctions,
+                                        Traits, KeyTraits, Allocator>*>(object)
+                 ->Trace(visitor);
+           }}))
+    return;
+
   static_assert(WTF::IsWeak<ValueType>::value ||
                     IsTraceableInCollectionTrait<Traits>::value,
                 "Value should not be traced");
diff --git a/third_party/blink/renderer/platform/wtf/linked_hash_set.h b/third_party/blink/renderer/platform/wtf/linked_hash_set.h
index 22202c3..e3f4fbd3 100644
--- a/third_party/blink/renderer/platform/wtf/linked_hash_set.h
+++ b/third_party/blink/renderer/platform/wtf/linked_hash_set.h
@@ -313,6 +313,14 @@
 
   template <typename VisitorDispatcher>
   void Trace(VisitorDispatcher visitor) {
+    if (visitor->ConcurrentTracingBailOut(
+            {this, [](blink::Visitor* visitor, void* object) {
+               reinterpret_cast<LinkedHashSet<ValueArg, HashFunctions,
+                                              TraitsArg, Allocator>*>(object)
+                   ->Trace(visitor);
+             }}))
+      return;
+
     impl_.Trace(visitor);
     // Should the underlying table be moved by GC, register a callback
     // that fixes up the interior pointers that the (Heap)LinkedHashSet keeps.
diff --git a/third_party/blink/renderer/platform/wtf/list_hash_set.h b/third_party/blink/renderer/platform/wtf/list_hash_set.h
index 7856b7be..4daae4b 100644
--- a/third_party/blink/renderer/platform/wtf/list_hash_set.h
+++ b/third_party/blink/renderer/platform/wtf/list_hash_set.h
@@ -513,6 +513,13 @@
 
   template <typename VisitorDispatcher, typename A = NodeAllocator>
   std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher visitor) {
+    if (visitor->ConcurrentTracingBailOut(
+            {this, [](blink::Visitor* visitor, void* object) {
+               reinterpret_cast<ListHashSetNode<ValueArg, AllocatorArg>*>(
+                   object)
+                   ->Trace(visitor);
+             }}))
+      return;
     // The conservative stack scan can find nodes that have been removed
     // from the set and destructed. We don't need to trace these, and it
     // would be wrong to do so, because the class will not expect the trace
@@ -1187,6 +1194,13 @@
 template <typename T, size_t inlineCapacity, typename U, typename V>
 template <typename VisitorDispatcher>
 void ListHashSet<T, inlineCapacity, U, V>::Trace(VisitorDispatcher visitor) {
+  if (visitor->ConcurrentTracingBailOut(
+          {this, [](blink::Visitor* visitor, void* object) {
+             reinterpret_cast<ListHashSet<T, inlineCapacity, U, V>*>(object)
+                 ->Trace(visitor);
+           }}))
+    return;
+
   static_assert(!IsWeak<T>::value,
                 "HeapListHashSet does not support weakness, consider using "
                 "HeapLinkedHashSet instead.");
diff --git a/third_party/blink/renderer/platform/wtf/vector.h b/third_party/blink/renderer/platform/wtf/vector.h
index 5ba2d7f..bef1a4f 100644
--- a/third_party/blink/renderer/platform/wtf/vector.h
+++ b/third_party/blink/renderer/platform/wtf/vector.h
@@ -1977,6 +1977,14 @@
 template <typename VisitorDispatcher, typename A>
 std::enable_if_t<A::kIsGarbageCollected>
 Vector<T, inlineCapacity, Allocator>::Trace(VisitorDispatcher visitor) {
+  // Bail out for concurrent marking.
+  if (visitor->ConcurrentTracingBailOut(
+          {this, [](blink::Visitor* visitor, void* object) {
+             reinterpret_cast<Vector<T, inlineCapacity, Allocator>*>(object)
+                 ->Trace(visitor);
+           }}))
+    return;
+
   static_assert(Allocator::kIsGarbageCollected,
                 "Garbage collector must be enabled.");
 
diff --git a/third_party/blink/web_tests/FlagExpectations/composite-after-paint b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
index 5a24022..15b18a8 100644
--- a/third_party/blink/web_tests/FlagExpectations/composite-after-paint
+++ b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
@@ -85,9 +85,6 @@
 fast/sub-pixel/transformed-iframe-copy-on-scroll.html [ Failure ]
 fullscreen/compositor-touch-hit-rects-fullscreen-video-controls.html [ Failure ]
 http/tests/devtools/layers/layers-3d-view-after-update.js [ Failure ]
-http/tests/input/discard-events-to-unstable-iframe.html [ Failure ]
-http/tests/misc/destroy-middle-click-locked-target-crash.html [ Timeout ]
-http/tests/misc/synthetic-gesture-initiated-in-cross-origin-frame.html [ Failure ]
 printing/fixed-positioned-headers-and-footers-absolute-covering-some-pages.html [ Failure ]
 printing/fixed-positioned-headers-and-footers-larger-than-page.html [ Failure ]
 printing/offscreencanvas-2d-printing.html [ Failure ]
@@ -103,9 +100,6 @@
 virtual/threaded-prefer-compositing/fast/scrolling/no-hover-during-scroll.html [ Failure Pass ]
 virtual/threaded-prefer-compositing/fast/scrolling/no-hover-during-smooth-js-scroll.html [ Crash Pass ]
 
-# Loading an image in a subframe.
-crbug.com/959936 http/tests/images/image-decode-in-frame.html [ Failure ]
-
 crbug.com/907601 virtual/threaded-prefer-compositing/fast/scrolling/events/scrollend-event-fired-after-snap.html [ Skip ]
 
 # Less invalidations or different invalidations without pixel failures.
@@ -255,3 +249,5 @@
 crbug.com/1035582 virtual/scalefactor150/fast/hidpi/static/calendar-picker-appearance.html [ Failure ]
 crbug.com/1035582 virtual/scalefactor150/fast/hidpi/static/data-suggestion-picker-appearance.html [ Failure ]
 crbug.com/1035582 virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi.html [ Failure ]
+
+external/wpt/css/compositing/root-element-blend-mode.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 43465f8..ec41473 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -52,6 +52,9 @@
 
 crbug.com/1014421 external/wpt/css/cssom-view/MediaQueryList-addListener-handleEvent.html [ Timeout ]
 
+# Tested by paint/background/root-element-background-transparency.html for now.
+external/wpt/css/compositing/root-element-background-transparency.html [ Failure ]
+
 # ====== Site Isolation failures from here ======
 # See also third_party/blink/web_tests/virtual/not-site-per-process/README.md
 #
@@ -375,6 +378,8 @@
 crbug.com/1042783 external/wpt/css/css-backgrounds/background-size/background-size-near-zero-png.html [ Failure ]
 crbug.com/1042783 external/wpt/css/css-backgrounds/background-size/background-size-near-zero-svg.html [ Failure ]
 
+crbug.com/898293 external/wpt/css/css-transforms/transform-translate-background-001.html [ Failure ]
+
 # ====== Paint team owned tests to here ======
 
 crbug.com/922249 virtual/android/fullscreen/compositor-touch-hit-rects-fullscreen-video-controls.html [ Failure Pass ]
@@ -2800,6 +2805,7 @@
 crbug.com/641245 external/wpt/css/motion/offset-path-ray-contain-005.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac10.10 ] external/wpt/webrtc/RTCPeerConnection-createDataChannel.html [ Failure Crash ]
 crbug.com/626703 [ Mac10.10 ] external/wpt/html/webappapis/scripting/events/event-handler-attributes-body-window.html [ Failure Timeout ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/html/webappapis/scripting/events/event-handler-attributes-body-window.html [ Failure Timeout ]
 crbug.com/626703 [ Mac10.10 ] external/wpt/WebCryptoAPI/derive_bits_keys/hkdf.https.any.worker.html?3001-last [ Failure Timeout ]
@@ -4534,10 +4540,6 @@
 
 crbug.com/683800 [ Win7 Debug ] external/wpt/selection/* [ Failure Pass ]
 
-# Arrow function name inferring
-crbug.com/916975 http/tests/devtools/tracing/timeline-misc/timeline-event-causes.js [ Pass Failure ]
-crbug.com/916975 virtual/threaded/http/tests/devtools/tracing/timeline-misc/timeline-event-causes.js [ Pass Failure ]
-
 # Importing 'fetch' tests from WPT.
 crbug.com/705490 external/wpt/fetch/api/basic/error-after-response.html [ Timeout Pass ]
 crbug.com/820334 external/wpt/fetch/api/request/request-cache-default-conditional.html [ Timeout Pass ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
index caa1cac..4d914979 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
@@ -161479,22 +161479,40 @@
    "html/cross-origin-opener-policy/historical/popup-same-site-with-same-site.https.html.headers": [
     []
    ],
-   "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups.https-expected.txt": [
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https-expected.txt": [
     []
    ],
-   "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups.https.html.headers": [
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html.headers": [
     []
    ],
-   "html/cross-origin-opener-policy/iframe-popup-same-origin.https-expected.txt": [
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https-expected.txt": [
     []
    ],
-   "html/cross-origin-opener-policy/iframe-popup-same-origin.https.html.headers": [
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html.headers": [
     []
    ],
-   "html/cross-origin-opener-policy/iframe-popup-unsafe-none.https-expected.txt": [
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html.headers": [
     []
    ],
-   "html/cross-origin-opener-policy/iframe-popup-unsafe-none.https.html.headers": [
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https-expected.txt": [
+    []
+   ],
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html.headers": [
+    []
+   ],
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https-expected.txt": [
+    []
+   ],
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html.headers": [
+    []
+   ],
+   "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https-expected.txt": [
+    []
+   ],
+   "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html.headers": [
+    []
+   ],
+   "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html.headers": [
     []
    ],
    "html/cross-origin-opener-policy/no-https-expected.txt": [
@@ -168421,9 +168439,6 @@
    "infrastructure/testdriver/send_keys-expected.txt": [
     []
    ],
-   "infrastructure/testdriver/set_permission.https-expected.txt": [
-    []
-   ],
    "infrastructure/webdriver/tests/conftest.py": [
     []
    ],
@@ -251984,22 +251999,60 @@
      }
     ]
    ],
-   "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups.https.html": [
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html": [
     [
-     "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups.https.html",
-     {}
+     "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html",
+     {
+      "timeout": "long"
+     }
     ]
    ],
-   "html/cross-origin-opener-policy/iframe-popup-same-origin.https.html": [
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html": [
     [
-     "html/cross-origin-opener-policy/iframe-popup-same-origin.https.html",
-     {}
+     "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html",
+     {
+      "timeout": "long"
+     }
     ]
    ],
-   "html/cross-origin-opener-policy/iframe-popup-unsafe-none.https.html": [
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html": [
     [
-     "html/cross-origin-opener-policy/iframe-popup-unsafe-none.https.html",
-     {}
+     "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html": [
+    [
+     "html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html": [
+    [
+     "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
+   "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html": [
+    [
+     "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
+   "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html": [
+    [
+     "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html",
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "html/cross-origin-opener-policy/no-https.html": [
@@ -283836,7 +283889,8 @@
     [
      "portals/portals-focus.sub.html",
      {
-      "testdriver": true
+      "testdriver": true,
+      "timeout": "long"
      }
     ]
    ],
@@ -342368,7 +342422,7 @@
    "testharness"
   ],
   "2dcontext/drawing-text-to-the-canvas/2d.text.measure.actualBoundingBox.html": [
-   "76fc17a882ff492b29716fcc0ea4230676abc361",
+   "834c1952c5ad86004873c652de0a79993ed2a57e",
    "testharness"
   ],
   "2dcontext/drawing-text-to-the-canvas/2d.text.measure.advances.html": [
@@ -345308,7 +345362,7 @@
    "support"
   ],
   "2dcontext/tools/tests2dtext.yaml": [
-   "6d5f8f660fa3fbc8e2b8b8710070241ccde392d0",
+   "e1423284bd8622b31da6e4ec2a3caaa5ce71dce2",
    "support"
   ],
   "2dcontext/transformations/2d.transformation.getTransform.html": [
@@ -458419,39 +458473,79 @@
    "34bd099a302f893f92586241ea38aac812bf28d0",
    "support"
   ],
-  "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups.https-expected.txt": [
-   "2a5f69085de9fc47352a4115b2986dbb22d88889",
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https-expected.txt": [
+   "77c908d6a49c86b564190a50000a55a744e2f6ae",
    "support"
   ],
-  "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups.https.html": [
-   "51f4b54991be48c3a7bcfe894fc88f87476064dd",
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html": [
+   "b03949e7a9f9a768b5f8911cf34717bd54f5509e",
    "testharness"
   ],
-  "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups.https.html.headers": [
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html.headers": [
    "d83ed86fb9b5d159b9f380424887402edc96cb75",
    "support"
   ],
-  "html/cross-origin-opener-policy/iframe-popup-same-origin.https-expected.txt": [
-   "fadc943d541061f2bac08de6d8f1d71af530003f",
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https-expected.txt": [
+   "67e7b60eb16c2e7c50cd83608e88dff905120e88",
    "support"
   ],
-  "html/cross-origin-opener-policy/iframe-popup-same-origin.https.html": [
-   "55fb592035bb819a62640e7cc3b19d4477af4924",
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html": [
+   "66b0a4afa7fd487c5047bcb7a8cd43224b2fc430",
    "testharness"
   ],
-  "html/cross-origin-opener-policy/iframe-popup-same-origin.https.html.headers": [
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html.headers": [
+   "d83ed86fb9b5d159b9f380424887402edc96cb75",
+   "support"
+  ],
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html": [
+   "d2f2783606e6cdb021eb0a29bec5d11137af7675",
+   "testharness"
+  ],
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html.headers": [
+   "d83ed86fb9b5d159b9f380424887402edc96cb75",
+   "support"
+  ],
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https-expected.txt": [
+   "a0c0e0c6d0803474302667292771736e95f106c2",
+   "support"
+  ],
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html": [
+   "4c70b1102fd5329c64835203d70a2b5611c54111",
+   "testharness"
+  ],
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html.headers": [
    "46ad58d83bf6e98913ca4c564b7acb8f19fa0093",
    "support"
   ],
-  "html/cross-origin-opener-policy/iframe-popup-unsafe-none.https-expected.txt": [
-   "4e74c996e573c20316a59f47e9635280592ac86d",
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https-expected.txt": [
+   "47c49510e990f73b20c04bd2e464d792ca96c7f3",
    "support"
   ],
-  "html/cross-origin-opener-policy/iframe-popup-unsafe-none.https.html": [
-   "878168441cbda5a20e31a8481ffb4bb267d9b25d",
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html": [
+   "d6521bd9dc4cdf32aae3e252a77af3c7b1bf5b83",
    "testharness"
   ],
-  "html/cross-origin-opener-policy/iframe-popup-unsafe-none.https.html.headers": [
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html.headers": [
+   "46ad58d83bf6e98913ca4c564b7acb8f19fa0093",
+   "support"
+  ],
+  "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https-expected.txt": [
+   "d0eaa17ad531e1ed4190a9677f9e20f9e67de130",
+   "support"
+  ],
+  "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html": [
+   "ad7c0e263ab40957ad6b9584254c00ace6941943",
+   "testharness"
+  ],
+  "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html.headers": [
+   "073ce7adfbd81cb7c0b2f91f96c8349b6677f26c",
+   "support"
+  ],
+  "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html": [
+   "8113189cc86c42b367224246c545b253ee780fa5",
+   "testharness"
+  ],
+  "html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html.headers": [
    "073ce7adfbd81cb7c0b2f91f96c8349b6677f26c",
    "support"
   ],
@@ -477603,10 +477697,6 @@
    "2170347c9729564f7e492009b3d20b3267422c1d",
    "testharness"
   ],
-  "infrastructure/testdriver/set_permission.https-expected.txt": [
-   "6906a8435ac0d6c4e833122c321a4030e62d3dc6",
-   "support"
-  ],
   "infrastructure/testdriver/set_permission.https.html": [
    "af743f638289602ecc512a7dbd4000de6056b796",
    "testharness"
@@ -493868,7 +493958,7 @@
    "testharness"
   ],
   "portals/portals-focus.sub.html": [
-   "1779590779af1f7306925add9dfdd84a6abfa323",
+   "97b7579eb63694310aa4d34b656c7414e68bd7c2",
    "testharness"
   ],
   "portals/portals-host-exposure.sub.html": [
@@ -531356,11 +531446,11 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-createDataChannel-expected.txt": [
-   "485802401f5a1272676a5c137b6179773bbb41ba",
+   "13176da50051bfb8a67a02ca1cd09fbdd2b8c291",
    "support"
   ],
   "webrtc/RTCPeerConnection-createDataChannel.html": [
-   "58e757bce076e9b5ad16c5943ff3065b91305fae",
+   "943e5728775494b8c5be4c521ce9671cd4e1bc4e",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-createOffer-expected.txt": [
diff --git a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.actualBoundingBox.html b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.actualBoundingBox.html
index 76fc17a..834c195 100644
--- a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.actualBoundingBox.html
+++ b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.actualBoundingBox.html
@@ -35,14 +35,15 @@
         ctx.direction = 'ltr';
         ctx.align = 'left'
         ctx.baseline = 'alphabetic'
-        // Some platforms may return '-0'.
-        _assertSame(Math.abs(ctx.measureText('A').actualBoundingBoxLeft), 0, "Math.abs(ctx.measureText('A').actualBoundingBoxLeft)", "0");
         // Different platforms may render text slightly different.
+        // Values that are nominally expected to be zero might actually vary by a pixel or so
+        // if the UA accounts for antialiasing at glyph edges, so we allow a slight deviation.
+        _assert(Math.abs(ctx.measureText('A').actualBoundingBoxLeft) <= 1, "Math.abs(ctx.measureText('A').actualBoundingBoxLeft) <= 1");
         _assert(ctx.measureText('A').actualBoundingBoxRight >= 50, "ctx.measureText('A').actualBoundingBoxRight >= 50");
         _assert(ctx.measureText('A').actualBoundingBoxAscent >= 35, "ctx.measureText('A').actualBoundingBoxAscent >= 35");
-        _assertSame(Math.abs(ctx.measureText('A').actualBoundingBoxDescent), 0, "Math.abs(ctx.measureText('A').actualBoundingBoxDescent)", "0");
+        _assert(Math.abs(ctx.measureText('A').actualBoundingBoxDescent) <= 1, "Math.abs(ctx.measureText('A').actualBoundingBoxDescent) <= 1");
 
-        _assertSame(Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft), 0, "Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft)", "0");
+        _assert(Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft) <= 1, "Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft) <= 1");
         _assert(ctx.measureText('ABCD').actualBoundingBoxRight >= 200, "ctx.measureText('ABCD').actualBoundingBoxRight >= 200");
         _assert(ctx.measureText('ABCD').actualBoundingBoxAscent >= 85, "ctx.measureText('ABCD').actualBoundingBoxAscent >= 85");
         _assert(ctx.measureText('ABCD').actualBoundingBoxDescent >= 37, "ctx.measureText('ABCD').actualBoundingBoxDescent >= 37");
diff --git a/third_party/blink/web_tests/external/wpt/2dcontext/tools/tests2dtext.yaml b/third_party/blink/web_tests/external/wpt/2dcontext/tools/tests2dtext.yaml
index 6d5f8f6..e142328 100644
--- a/third_party/blink/web_tests/external/wpt/2dcontext/tools/tests2dtext.yaml
+++ b/third_party/blink/web_tests/external/wpt/2dcontext/tools/tests2dtext.yaml
@@ -1088,14 +1088,15 @@
             ctx.direction = 'ltr';
             ctx.align = 'left'
             ctx.baseline = 'alphabetic'
-            // Some platforms may return '-0'.
-            @assert Math.abs(ctx.measureText('A').actualBoundingBoxLeft) === 0;
             // Different platforms may render text slightly different.
+            // Values that are nominally expected to be zero might actually vary by a pixel or so
+            // if the UA accounts for antialiasing at glyph edges, so we allow a slight deviation.
+            @assert Math.abs(ctx.measureText('A').actualBoundingBoxLeft) <= 1;
             @assert ctx.measureText('A').actualBoundingBoxRight >= 50;
             @assert ctx.measureText('A').actualBoundingBoxAscent >= 35;
-            @assert Math.abs(ctx.measureText('A').actualBoundingBoxDescent) === 0;
+            @assert Math.abs(ctx.measureText('A').actualBoundingBoxDescent) <= 1;
 
-            @assert Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft) === 0;
+            @assert Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft) <= 1;
             @assert ctx.measureText('ABCD').actualBoundingBoxRight >= 200;
             @assert ctx.measureText('ABCD').actualBoundingBoxAscent >= 85;
             @assert ctx.measureText('ABCD').actualBoundingBoxDescent >= 37;
diff --git a/third_party/blink/web_tests/external/wpt/css/compositing/root-element-background-transparency-ref.html b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-background-transparency-ref.html
new file mode 100644
index 0000000..4671d44
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-background-transparency-ref.html
@@ -0,0 +1,7 @@
+<!doctype HTML>
+<title>Backround color transparency on the root element blends with a white backdrop</title>
+<link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/compositing/#pagebackdrop">
+<html style="background: rgb(150, 150, 150)">
+  <div id=spacer style="width: 100px; height: 3000px"></div>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/compositing/root-element-background-transparency.html b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-background-transparency.html
new file mode 100644
index 0000000..910eb08
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-background-transparency.html
@@ -0,0 +1,8 @@
+<!doctype HTML>
+<title>Backround color transparency on the root element blends with a white backdrop</title>
+<link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/compositing/#pagebackdrop">
+<link rel="match" href="root-element-background-transparency-ref.html">
+<html style="background: rgba(45, 45, 45, 0.5)">
+  <div id=spacer style="width: 100px; height: 3000px"></div>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/compositing/root-element-blend-mode-ref.html b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-blend-mode-ref.html
new file mode 100644
index 0000000..14c99f7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-blend-mode-ref.html
@@ -0,0 +1,6 @@
+<!doctype HTML>
+<title>Blend-mode on the root stacking context blends with the root element's background.</title>
+<html style="background: #000">
+  <div style="width: 50px; height: 50px; background: #000"></div>
+  <div id=spacer style="width: 100px; height: 3000px"></div>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/compositing/root-element-blend-mode.html b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-blend-mode.html
new file mode 100644
index 0000000..2c97d8b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-blend-mode.html
@@ -0,0 +1,9 @@
+<!doctype HTML>
+<title>Blend-mode on the root stacking context blends with the root element's background.</title>
+<link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/compositing/#pagebackdrop">
+<link rel="match" href="root-element-blend-mode-ref.html">
+<html style="background: #000;">
+  <div style="width: 50px; height: 50px; background: #E33;  mix-blend-mode: multiply"></div>
+  <div id=spacer style="width: 100px; height: 3000px"></div>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/compositing/root-element-filter-ref.html b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-filter-ref.html
new file mode 100644
index 0000000..27cfb11b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-filter-ref.html
@@ -0,0 +1,8 @@
+<!doctype HTML>
+<title>A filter on the root element applies to its background</title>
+<link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/compositing/#pagebackdrop">
+<html style="background: #FFF">
+  <div style="width: 50px; height: 50px; background: #1CC"></div>
+  <div id=spacer style="width: 100px; height: 3000px"></div>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/compositing/root-element-filter.html b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-filter.html
new file mode 100644
index 0000000..62bde9df
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-filter.html
@@ -0,0 +1,9 @@
+<!doctype HTML>
+<title>A filter on the root element applies to its background</title>
+<link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/compositing/#pagebackdrop">
+<link rel="match" href="root-element-filter-ref.html">
+<html style="filter: invert(1); background: #000">
+  <div style="width: 50px; height: 50px; background: #E33"></div>
+  <div id=spacer style="width: 100px; height: 3000px"></div>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/compositing/root-element-opacity-ref.html b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-opacity-ref.html
new file mode 100644
index 0000000..be2348a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-opacity-ref.html
@@ -0,0 +1,7 @@
+<!doctype HTML>
+<title>Opacity on the root element blends with a white backdrop</title>
+<link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/compositing/#pagebackdrop">
+<html style="background: #DDD">
+  <div id=spacer style="width: 100px; height: 3000px"></div>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/compositing/root-element-opacity.html b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-opacity.html
new file mode 100644
index 0000000..4885d805
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/compositing/root-element-opacity.html
@@ -0,0 +1,8 @@
+<!doctype HTML>
+<title>Opacity on the root element blends with a white background</title>
+<link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/compositing/#pagebackdrop">
+<link rel="match" href="root-element-opacity-ref.html">
+<html style="background: #BBB; opacity: 0.5">
+  <div id=spacer style="width: 100px; height: 3000px"></div>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/zero-content-size-with-scrollbar-crash.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/zero-content-size-with-scrollbar-crash.html
new file mode 100644
index 0000000..c38659a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/zero-content-size-with-scrollbar-crash.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/">
+<meta name="assert" content="This test passes if the renderer does not crash."/>
+<div style="display: flex;">
+  <div style="max-width: 0px; overflow: scroll;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-translate-background-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-translate-background-001-ref.html
new file mode 100644
index 0000000..f0b53d4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-translate-background-001-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<title>CSS Transforms: Reference Translate gradient background on root element</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<style>
+  html {
+    background: green;
+  }
+</style>
+<div style="height: 400vh;"></div>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-translate-background-001.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-translate-background-001.html
new file mode 100644
index 0000000..72011e37
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-translate-background-001.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<title>CSS Transforms: Translate gradient background on root element</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-transforms/#transform-rendering">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=988446">
+<link rel="match" href="transform-translate-background-001-ref.html">
+<meta name="assert" content="Checks that the linear gradient is modified when you vertically translate the root element. The test passes if you see only green.">
+<style>
+  html {
+    background: linear-gradient(to bottom, red 0%, red 50%, green 50%, green 100%);
+    transform: translate(0, -250vh);
+  }
+</style>
+<div style="height: 400vh;"></div>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-translate-background-002.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-translate-background-002.html
new file mode 100644
index 0000000..b1be5277
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-translate-background-002.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>CSS Transforms: Dynamically translate gradient background on root element</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-transforms/#transform-rendering">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=988446">
+<link rel="match" href="transform-translate-background-001-ref.html">
+<meta name="assert" content="Checks that the linear gradient is modified when you dynamically translate the root element. The test passes if you see only green.">
+<script src="/common/reftest-wait.js"></script>
+<style>
+  html {
+    background: linear-gradient(to bottom, red 0%, red 50%, green 50%, green 100%);
+  }
+</style>
+<div style="height: 400vh;"></div>
+<script>
+  requestAnimationFrame(() => requestAnimationFrame(() => {
+    document.documentElement.style.transform = "translate(0, -250vh)";
+    takeScreenshot();
+  }));
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/native-file-system/native_FileSystemBaseHandle-postMessage-manual.https.tentative.html b/third_party/blink/web_tests/external/wpt/native-file-system/native_FileSystemBaseHandle-postMessage-manual.https.tentative.html
index 266238c..e04be7d 100644
--- a/third_party/blink/web_tests/external/wpt/native-file-system/native_FileSystemBaseHandle-postMessage-manual.https.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/native-file-system/native_FileSystemBaseHandle-postMessage-manual.https.tentative.html
@@ -1,5 +1,6 @@
 <!doctype html>
 <meta charset=utf-8>
+<meta name="timeout" content="long">
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/native-file-system/sandboxed_FileSystemBaseHandle-postMessage.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/native-file-system/sandboxed_FileSystemBaseHandle-postMessage.tentative.https.window.js
index e010f65..f7d5776 100644
--- a/third_party/blink/web_tests/external/wpt/native-file-system/sandboxed_FileSystemBaseHandle-postMessage.tentative.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/native-file-system/sandboxed_FileSystemBaseHandle-postMessage.tentative.https.window.js
@@ -4,4 +4,5 @@
 // META: script=resources/messaging-helpers.js
 // META: script=resources/messaging-blob-helpers.js
 // META: script=resources/messaging-serialize-helpers.js
-// META: script=script-tests/FileSystemBaseHandle-postMessage.js
\ No newline at end of file
+// META: script=script-tests/FileSystemBaseHandle-postMessage.js
+// META: timeout=long
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createDataChannel-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createDataChannel-expected.txt
index 4858024..13176da5 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createDataChannel-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createDataChannel-expected.txt
@@ -1,4 +1,5 @@
 This is a testharness.js-based test.
+Found 50 tests; 43 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS createDataChannel with no argument should throw TypeError
 PASS createDataChannel with closed connection should throw InvalidStateError
 FAIL createDataChannel attribute default values assert_equals: expected (string) "low" but got (undefined) undefined
@@ -17,9 +18,15 @@
 PASS createDataChannel with protocol null should succeed
 PASS createDataChannel with protocol undefined should succeed
 PASS createDataChannel with protocol lone surrogate should succeed
-PASS createDataChannel with id 0 should succeed
-PASS createDataChannel with id 1 should succeed
-FAIL createDataChannel with id 65534 should succeed Failed to execute 'createDataChannel' on 'RTCPeerConnection': RTCDataChannel creation failed
+PASS createDataChannel with id 0 and negotiated not set should succeed, but not set the channel's id
+PASS createDataChannel with id 1 and negotiated not set should succeed, but not set the channel's id
+PASS createDataChannel with id 65534 and negotiated not set should succeed, but not set the channel's id
+FAIL createDataChannel with id 65535 and negotiated not set should succeed, but not set the channel's id Failed to execute 'createDataChannel' on 'RTCPeerConnection': RTCDataChannel cannot have id > 65534
+PASS createDataChannel with id 0 and negotiated true should succeed, and set the channel's id
+PASS createDataChannel with id 1 and negotiated true should succeed, and set the channel's id
+FAIL createDataChannel with id 65534 and negotiated true should succeed, and set the channel's id Failed to execute 'createDataChannel' on 'RTCPeerConnection': RTCDataChannel creation failed
+PASS createDataChannel with id -1 and negotiated not set should throw TypeError
+PASS createDataChannel with id 65536 and negotiated not set should throw TypeError
 PASS createDataChannel with id -1 should throw TypeError
 PASS createDataChannel with id 65535 should throw TypeError
 PASS createDataChannel with id 65536 should throw TypeError
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createDataChannel.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createDataChannel.html
index 58e757b..943e572 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createDataChannel.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createDataChannel.html
@@ -69,7 +69,7 @@
   t.add_cleanup(() => pc.close());
 
   assert_equals(pc.createDataChannel.length, 1);
-  assert_throws(new TypeError(), () => pc.createDataChannel());
+  assert_throws_js(TypeError, () => pc.createDataChannel());
 }, 'createDataChannel with no argument should throw TypeError');
 
 /*
@@ -292,11 +292,11 @@
   const pc = new RTCPeerConnection;
   t.add_cleanup(() => pc.close());
 
-  assert_throws(new TypeError(), () => pc.createDataChannel('', {
+  assert_throws_js(TypeError, () => pc.createDataChannel('', {
     maxPacketLifeTime: 0,
     maxRetransmits: 0
   }));
-  assert_throws(new TypeError(), () => pc.createDataChannel('', {
+  assert_throws_js(TypeError, () => pc.createDataChannel('', {
     maxPacketLifeTime: 42,
     maxRetransmits: 42
   }));
@@ -330,6 +330,15 @@
       20. If [[DataChannelId]] is equal to 65535, which is greater than the maximum allowed
           ID of 65534 but still qualifies as an unsigned short, throw a TypeError.
  */
+for (const id of [0, 1, 65534, 65535]) {
+  test((t) => {
+    const pc = new RTCPeerConnection();
+    t.add_cleanup(() => pc.close());
+    const dc = pc.createDataChannel('', { id });
+    assert_equals(dc.id, null);
+  }, `createDataChannel with id ${id} and negotiated not set should succeed, but not set the channel's id`);
+}
+
 for (const id of [0, 1, 65534]) {
   test(t => {
     const pc = new RTCPeerConnection();
@@ -337,7 +346,15 @@
 
     const dc = pc.createDataChannel('', { 'negotiated': true, 'id': id });
     assert_equals(dc.id, id);
-  }, `createDataChannel with id ${id} should succeed`);
+  }, `createDataChannel with id ${id} and negotiated true should succeed, and set the channel's id`);
+}
+
+for (const id of [-1, 65536]) {
+  test((t) => {
+    const pc = new RTCPeerConnection();
+    t.add_cleanup(() => pc.close());
+    assert_throws_js(TypeError, () => pc.createDataChannel('', { id }));
+  }, `createDataChannel with id ${id} and negotiated not set should throw TypeError`);
 }
 
 for (const id of [-1, 65535, 65536]) {
@@ -345,7 +362,7 @@
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
 
-    assert_throws(new TypeError(), () => pc.createDataChannel('',
+    assert_throws_js(TypeError, () => pc.createDataChannel('',
         { 'negotiated': true, 'id': id }));
   }, `createDataChannel with id ${id} should throw TypeError`);
 }
@@ -380,10 +397,10 @@
   const pc = new RTCPeerConnection();
   t.add_cleanup(() => pc.close());
 
-  assert_throws(new TypeError(), () =>
+  assert_throws_js(TypeError, () =>
     pc.createDataChannel('l'.repeat(65536)));
 
-  assert_throws(new TypeError(), () =>
+  assert_throws_js(TypeError, () =>
     pc.createDataChannel('l'.repeat(65536), {
       negotiated: true,
       id: 42
@@ -394,10 +411,10 @@
   const pc = new RTCPeerConnection();
   t.add_cleanup(() => pc.close());
 
-  assert_throws(new TypeError(), () =>
+  assert_throws_js(TypeError, () =>
     pc.createDataChannel('\u00b5'.repeat(32768)));
 
-  assert_throws(new TypeError(), () =>
+  assert_throws_js(TypeError, () =>
     pc.createDataChannel('\u00b5'.repeat(32768), {
       negotiated: true,
       id: 42
@@ -435,12 +452,12 @@
   const pc = new RTCPeerConnection();
   t.add_cleanup(() => pc.close());
 
-  assert_throws(new TypeError(), () =>
+  assert_throws_js(TypeError, () =>
     pc.createDataChannel('', {
       protocol: 'p'.repeat(65536)
     }));
 
-  assert_throws(new TypeError(), () =>
+  assert_throws_js(TypeError, () =>
     pc.createDataChannel('', {
       protocol: 'p'.repeat(65536),
       negotiated: true,
@@ -452,12 +469,12 @@
   const pc = new RTCPeerConnection();
   t.add_cleanup(() => pc.close());
 
-  assert_throws(new TypeError(), () =>
+  assert_throws_js(TypeError, () =>
     pc.createDataChannel('', {
       protocol: '\u00b6'.repeat(32768)
     }));
 
-  assert_throws(new TypeError(), () =>
+  assert_throws_js(TypeError, () =>
     pc.createDataChannel('', {
       protocol: '\u00b6'.repeat(32768),
       negotiated: true,
@@ -520,7 +537,7 @@
   const pc = new RTCPeerConnection();
   t.add_cleanup(() => pc.close());
 
-  assert_throws(new TypeError(), () =>
+  assert_throws_js(TypeError, () =>
     pc.createDataChannel('test', {
       negotiated: true
     }));
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/css3/blending/mix-blend-mode-composited-layers-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/css3/blending/mix-blend-mode-composited-layers-expected.txt
index fe2c783..a97b600 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/css3/blending/mix-blend-mode-composited-layers-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/css3/blending/mix-blend-mode-composited-layers-expected.txt
@@ -2,15 +2,11 @@
   "layers": [
     {
       "name": "Scrolling background of LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF"
+      "bounds": [800, 600]
     },
     {
-      "name": "LayoutNGBlockFlow HTML",
-      "position": [8, 8],
-      "bounds": [248, 49],
-      "backgroundColor": "#008000"
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600]
     },
     {
       "name": "LayoutNGBlockFlow DIV",
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/fast/backgrounds/size/contain-and-cover-zoomed-expected.png b/third_party/blink/web_tests/flag-specific/composite-after-paint/fast/backgrounds/size/contain-and-cover-zoomed-expected.png
deleted file mode 100644
index 9e309f8c..0000000
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/fast/backgrounds/size/contain-and-cover-zoomed-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/fast/borders/border-inner-bleed-expected.png b/third_party/blink/web_tests/flag-specific/composite-after-paint/fast/borders/border-inner-bleed-expected.png
deleted file mode 100644
index 609f5fe..0000000
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/fast/borders/border-inner-bleed-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt
index 38e3712..cc2ade25 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt
@@ -3,8 +3,6 @@
     {
       "name": "Scrolling background of LayoutView #document",
       "bounds": [785, 10016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
       "transform": 1
     },
     {
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt
index bedd1d8..477ed75 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt
@@ -7,59 +7,9 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV",
-          "rect": [128, 28, 80, 80],
-          "reason": "chunk appeared"
-        },
-        {
-          "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV",
-          "rect": [28, 28, 80, 80],
-          "reason": "chunk disappeared"
-        },
-        {
-          "object": "LayoutNGBlockFlow (floating) DIV",
-          "rect": [348, 48, 60, 60],
-          "reason": "paint property change"
-        },
-        {
-          "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV id='fourth'",
-          "rect": [328, 28, 60, 60],
-          "reason": "chunk appeared"
-        },
-        {
-          "object": "LayoutNGBlockFlow HTML",
-          "rect": [328, 28, 60, 60],
-          "reason": "chunk disappeared"
-        },
-        {
-          "object": "LayoutNGBlockFlow (floating) DIV id='third'",
-          "rect": [248, 48, 60, 60],
-          "reason": "paint property change"
-        },
-        {
-          "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV",
-          "rect": [228, 28, 60, 60],
-          "reason": "paint property change"
-        },
-        {
-          "object": "LayoutNGBlockFlow (floating) DIV id='second'",
-          "rect": [148, 48, 60, 60],
-          "reason": "chunk disappeared"
-        },
-        {
-          "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV",
-          "rect": [128, 28, 60, 60],
-          "reason": "chunk disappeared"
-        },
-        {
-          "object": "LayoutNGBlockFlow (floating) DIV id='first'",
-          "rect": [48, 48, 60, 60],
-          "reason": "chunk appeared"
-        },
-        {
-          "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV",
-          "rect": [28, 28, 60, 60],
-          "reason": "chunk appeared"
+          "object": "Scrolling background of LayoutView #document",
+          "rect": [0, 0, 800, 600],
+          "reason": "full layer"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt
index 2bd133c3..1f5d03d 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt
@@ -7,6 +7,16 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
+          "object": "Scrolling background of LayoutView #document",
+          "rect": [0, 0, 800, 600],
+          "reason": "chunk appeared"
+        },
+        {
+          "object": "Scrolling background of LayoutView #document",
+          "rect": [0, 0, 800, 600],
+          "reason": "chunk disappeared"
+        },
+        {
           "object": "LayoutBlockFlow (relative positioned) (floating) DIV",
           "rect": [128, 28, 80, 80],
           "reason": "chunk appeared"
diff --git a/third_party/blink/web_tests/flag-specific/enable-gpu-rasterization/transforms/transformed-document-element-expected.png b/third_party/blink/web_tests/flag-specific/enable-gpu-rasterization/transforms/transformed-document-element-expected.png
index 93bed0e..2dede645 100644
--- a/third_party/blink/web_tests/flag-specific/enable-gpu-rasterization/transforms/transformed-document-element-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-gpu-rasterization/transforms/transformed-document-element-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-event-causes-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-event-causes-expected.txt
index aec8ae8d..10a83aa 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-event-causes-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-event-causes-expected.txt
@@ -3,11 +3,11 @@
 
 Running: testTimerInstall
 PASS - record contained Timer Installed
-Promise @ setTimeoutFunction.js:
+(anonymous) @ setTimeoutFunction.js:
 
 Running: testRequestAnimationFrame
 PASS - record contained Animation Frame Requested
-Promise @ requestAnimationFrameFunction.js:
+(anonymous) @ requestAnimationFrameFunction.js:
 
 Running: testStyleRecalc
 PASS - record contained First Invalidated
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-event-causes.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-event-causes.js
index 33a87bc4..91955241 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-event-causes.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-event-causes.js
@@ -37,7 +37,7 @@
           event, PerformanceTestRunner.timelineModel().targetByEvent(event), null, contentHelper);
       var causes = contentHelper.element.deepTextContent();
       TestRunner.check(causes, 'Should generate causes');
-      checkStringContains(causes, 'Timer Installed\nPromise @ setTimeoutFunction.js:');
+      checkStringContains(causes, 'Timer Installed\n(anonymous) @ setTimeoutFunction.js:');
       next();
     },
 
@@ -60,7 +60,7 @@
           event, PerformanceTestRunner.timelineModel().targetByEvent(event), null, contentHelper);
       var causes = contentHelper.element.deepTextContent();
       TestRunner.check(causes, 'Should generate causes');
-      checkStringContains(causes, 'Animation Frame Requested\nPromise @ requestAnimationFrameFunction.js:');
+      checkStringContains(causes, 'Animation Frame Requested\n(anonymous) @ requestAnimationFrameFunction.js:');
       next();
     },
 
diff --git a/third_party/blink/web_tests/paint/background/root-element-background-transparency-expected.png b/third_party/blink/web_tests/paint/background/root-element-background-transparency-expected.png
new file mode 100644
index 0000000..892f7ef
--- /dev/null
+++ b/third_party/blink/web_tests/paint/background/root-element-background-transparency-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/paint/background/root-element-background-transparency.html b/third_party/blink/web_tests/paint/background/root-element-background-transparency.html
new file mode 100644
index 0000000..89041b1
--- /dev/null
+++ b/third_party/blink/web_tests/paint/background/root-element-background-transparency.html
@@ -0,0 +1,12 @@
+<!doctype HTML>
+<style>
+html::-webkit-scrollbar {
+    display:none;
+}
+</style>
+<!-- This is the same test as external/wpt/css/compositing/root-element-background-transparency.html.
+It is duplicated here because it's not clear how to make it a reftest successfully and avoid
+alpha-channel diffs, and fuzzy matching doesn't seem to work for that channel. -->
+<html style="background: rgba(45, 45, 45, 0.5);">
+  <div id=spacer style="width: 100px; height: 3000px"></div>
+</html>
diff --git a/third_party/blink/web_tests/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt b/third_party/blink/web_tests/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt
index f1c59295..0259d4d 100644
--- a/third_party/blink/web_tests/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt
@@ -7,6 +7,16 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
+          "object": "Scrolling background of LayoutView #document",
+          "rect": [0, 0, 800, 600],
+          "reason": "chunk appeared"
+        },
+        {
+          "object": "Scrolling background of LayoutView #document",
+          "rect": [0, 0, 800, 600],
+          "reason": "chunk disappeared"
+        },
+        {
           "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV",
           "rect": [128, 28, 80, 80],
           "reason": "chunk appeared"
diff --git a/third_party/blink/web_tests/platform/linux/fast/backgrounds/opacity-on-document-element-expected.png b/third_party/blink/web_tests/platform/linux/fast/backgrounds/opacity-on-document-element-expected.png
index a937ba3..ea63cff 100644
--- a/third_party/blink/web_tests/platform/linux/fast/backgrounds/opacity-on-document-element-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/backgrounds/opacity-on-document-element-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/transforms/transformed-document-element-expected.png b/third_party/blink/web_tests/platform/linux/transforms/transformed-document-element-expected.png
index 6ca3c061..9d98072 100644
--- a/third_party/blink/web_tests/platform/linux/transforms/transformed-document-element-expected.png
+++ b/third_party/blink/web_tests/platform/linux/transforms/transformed-document-element-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/backgrounds/opacity-on-document-element-expected.png b/third_party/blink/web_tests/platform/mac/fast/backgrounds/opacity-on-document-element-expected.png
index f3f4660..c5aca45 100644
--- a/third_party/blink/web_tests/platform/mac/fast/backgrounds/opacity-on-document-element-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/backgrounds/opacity-on-document-element-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/transforms/transformed-document-element-expected.png b/third_party/blink/web_tests/platform/mac/transforms/transformed-document-element-expected.png
index 6ad401a..542a755 100644
--- a/third_party/blink/web_tests/platform/mac/transforms/transformed-document-element-expected.png
+++ b/third_party/blink/web_tests/platform/mac/transforms/transformed-document-element-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/backgrounds/opacity-on-document-element-expected.png b/third_party/blink/web_tests/platform/win/fast/backgrounds/opacity-on-document-element-expected.png
index e147fe1f..9747f57 100644
--- a/third_party/blink/web_tests/platform/win/fast/backgrounds/opacity-on-document-element-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/backgrounds/opacity-on-document-element-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/transforms/transformed-document-element-expected.png b/third_party/blink/web_tests/platform/win/transforms/transformed-document-element-expected.png
index c639d58..1b08666 100644
--- a/third_party/blink/web_tests/platform/win/transforms/transformed-document-element-expected.png
+++ b/third_party/blink/web_tests/platform/win/transforms/transformed-document-element-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createDataChannel-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createDataChannel-expected.txt
index a8c8e4c8..649809fd 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createDataChannel-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createDataChannel-expected.txt
@@ -1,4 +1,5 @@
 This is a testharness.js-based test.
+Found 50 tests; 43 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS createDataChannel with no argument should throw TypeError
 PASS createDataChannel with closed connection should throw InvalidStateError
 FAIL createDataChannel attribute default values assert_equals: expected (string) "low" but got (undefined) undefined
@@ -17,9 +18,15 @@
 PASS createDataChannel with protocol null should succeed
 PASS createDataChannel with protocol undefined should succeed
 PASS createDataChannel with protocol lone surrogate should succeed
-PASS createDataChannel with id 0 should succeed
-PASS createDataChannel with id 1 should succeed
-FAIL createDataChannel with id 65534 should succeed Failed to execute 'createDataChannel' on 'RTCPeerConnection': RTCDataChannel creation failed
+PASS createDataChannel with id 0 and negotiated not set should succeed, but not set the channel's id
+PASS createDataChannel with id 1 and negotiated not set should succeed, but not set the channel's id
+PASS createDataChannel with id 65534 and negotiated not set should succeed, but not set the channel's id
+FAIL createDataChannel with id 65535 and negotiated not set should succeed, but not set the channel's id Failed to execute 'createDataChannel' on 'RTCPeerConnection': RTCDataChannel cannot have id > 65534
+PASS createDataChannel with id 0 and negotiated true should succeed, and set the channel's id
+PASS createDataChannel with id 1 and negotiated true should succeed, and set the channel's id
+FAIL createDataChannel with id 65534 and negotiated true should succeed, and set the channel's id Failed to execute 'createDataChannel' on 'RTCPeerConnection': RTCDataChannel creation failed
+PASS createDataChannel with id -1 and negotiated not set should throw TypeError
+PASS createDataChannel with id 65536 and negotiated not set should throw TypeError
 PASS createDataChannel with id -1 should throw TypeError
 PASS createDataChannel with id 65535 should throw TypeError
 PASS createDataChannel with id 65536 should throw TypeError
diff --git a/third_party/proguard/BUILD.gn b/third_party/proguard/BUILD.gn
index aa11967b..623956f 100644
--- a/third_party/proguard/BUILD.gn
+++ b/third_party/proguard/BUILD.gn
@@ -8,17 +8,11 @@
 # compiling, and is reference directly via "java -jar proguar.jar"
 java_prebuilt("proguard603_java") {
   jar_path = "lib/proguard603.jar"
-  data = [
-    "$root_build_dir/lib.java/third_party/proguard/proguard603.jar",
-  ]
+  data = [ "$root_build_dir/lib.java/third_party/proguard/proguard603.jar" ]
 }
 
 java_prebuilt("retrace_java") {
   jar_path = "lib/retrace603.jar"
-  deps = [
-    ":proguard603_java",
-  ]
-  data = [
-    "$root_build_dir/lib.java/third_party/proguard/retrace603.jar",
-  ]
+  deps = [ ":proguard603_java" ]
+  data = [ "$root_build_dir/lib.java/third_party/proguard/retrace603.jar" ]
 }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 1d632cb..84fce257 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -10732,6 +10732,7 @@
   <int value="26" label="Share Link (DirectShare)"/>
   <int value="27" label="Share Image (DirectShare)"/>
   <int value="28" label="Search with Google Lens"/>
+  <int value="29" label="Copy image"/>
 </enum>
 
 <enum name="ContextMenuOptionDesktop">
@@ -31598,6 +31599,13 @@
   <int value="2" label="Different from baseUrl"/>
 </enum>
 
+<enum name="HomeButtonPreferenceStateType">
+  <int value="0" label="User Disabled"/>
+  <int value="1" label="User Enabled"/>
+  <int value="2" label="Managed Disabled"/>
+  <int value="3" label="Managed Enabled"/>
+</enum>
+
 <enum name="HomedirEncryptionType">
   <int value="1" label="Ecryptfs"/>
   <int value="2" label="Ext4 Dir Encryption"/>
@@ -37015,6 +37023,7 @@
   <int value="-1333946113" label="ContentIndexingDownloadHome:disabled"/>
   <int value="-1332267458" label="RemoveNavigationHistory:enabled"/>
   <int value="-1331831950" label="site-isolation-for-password-sites:enabled"/>
+  <int value="-1330409814" label="ContextMenuCopyImage:enabled"/>
   <int value="-1327676774" label="disable-accelerated-mjpeg-decode"/>
   <int value="-1326463296" label="SSLCommittedInterstitials:disabled"/>
   <int value="-1325887476" label="NewPrintPreview:enabled"/>
@@ -39441,6 +39450,7 @@
   <int value="1705724232" label="use-android-midi-api"/>
   <int value="1707283026" label="SyncPseudoUSSExtensions:disabled"/>
   <int value="1708118086" label="TextFragmentAnchor:disabled"/>
+  <int value="1711286384" label="ContextMenuCopyImage:disabled"/>
   <int value="1713230497" label="ColorCorrectRendering:disabled"/>
   <int value="1714016217" label="EnableHeuristicPalmDetectionFilter:enabled"/>
   <int value="1714922056" label="GlobalMediaControls:disabled"/>
@@ -39829,6 +39839,7 @@
   <int value="12" label="Failed to get OAuth2 token"/>
   <int value="13" label="Cryptohome missing from disk"/>
   <int value="14" label="Authentication disabled for user"/>
+  <int value="15" label="TPM firmware update is required"/>
 </enum>
 
 <enum name="LoginIsKnownUser">
@@ -45761,6 +45772,7 @@
   <int value="10"
       label="Clicked on the Refresh button in the all dismissed state"/>
   <int value="11" label="Opened an explore sites tile"/>
+  <int value="12" label="Opened the manage interests page from the feed"/>
 </enum>
 
 <enum name="NewTabPageActioniOS">
@@ -47642,9 +47654,11 @@
     The optimization type was not allowed to be applied based on information
     from a hint.
   </int>
-  <int value="6" label="No matching page hint for page load">
+  <int value="6" label="DEPRECATED: No matching page hint for page load">
     There was a hint loaded that matched the page load, but no matching page
-    hint for the page load.
+    hint for the page load. Deprecated as of 01/2020. Page loads that used to
+    match this decision are now recorded as &quot;Page load not allowed by
+    hint&quot;.
   </int>
   <int value="7" label="Hint on device but not loaded yet">
     A hint for the page load was on the device but was not loaded in time to
@@ -59280,6 +59294,20 @@
   <int value="3" label="Large Downscale"/>
 </enum>
 
+<enum name="SmartChargingMessages">
+  <int value="0" label="SerializeProtoError"/>
+  <int value="1" label="WriteFileError"/>
+  <int value="2" label="WriteFileSuccess"/>
+  <int value="3" label="ReadFileError"/>
+  <int value="4" label="ParseProtoError"/>
+  <int value="5" label="ReadFileSuccess"/>
+  <int value="6" label="GetPrimaryProfileError"/>
+  <int value="7" label="CreateFolderError"/>
+  <int value="8" label="CreatePathSuccess"/>
+  <int value="9" label="PathDoesntExists"/>
+  <int value="10" label="GetPathSuccess"/>
+</enum>
+
 <enum name="SmartChargingReason">
   <int value="1" label="Charger plugged in"/>
   <int value="2" label="Charger unplugged"/>
@@ -63845,9 +63873,7 @@
   <int value="7" label="Can not create extracted file on disk"/>
   <int value="8" label="Can not write the extracted file on disk"/>
   <int value="9" label="Can not set the extract time to file"/>
-  <int value="10" label="Can not close extracted file (Deprecated)"/>
-  <int value="11" label="Can not create file memory mapping"/>
-  <int value="12" label="File CRC mismatch"/>
+  <int value="10" label="Can not close extracted file"/>
 </enum>
 
 <enum name="UnsupportedContainers">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 895a8808..eece7c6 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -2353,6 +2353,9 @@
 
 <histogram name="Android.DownloadManager.Chips.Enabled" units="chips"
     expires_after="2020-01-30">
+  <obsolete>
+    Deprecated Jan 2020.
+  </obsolete>
   <owner>shaktisahu@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>
@@ -2657,7 +2660,7 @@
 
 <histogram base="true"
     name="Android.DownloadManager.Thumbnail.MaxRequiredStretch" units="%"
-    expires_after="2020-01-30">
+    expires_after="2021-01-30">
 <!-- Name completed by histogram_suffixes
      name="AndroidDownloadTypes" -->
 
@@ -2787,7 +2790,7 @@
 
 <histogram base="true"
     name="Android.FeatureModules.UncachedAwakeInstallDuration" units="ms"
-    expires_after="2020-05-10">
+    expires_after="2020-07-19">
 <!-- Name completed by histogram_suffixes
        name="AndroidFeatureModuleName" -->
 
@@ -4126,7 +4129,7 @@
 </histogram>
 
 <histogram base="true" name="Android.ThumbnailProvider.BitmapCache.Size"
-    units="KB" expires_after="2020-01-30">
+    units="KB" expires_after="2021-01-30">
 <!-- Name completed by histogram_suffixes
      name="ThumbnailProvider.ClientType" -->
 
@@ -4139,7 +4142,7 @@
 </histogram>
 
 <histogram base="true" name="Android.ThumbnailProvider.CachedBitmap.Found"
-    enum="BooleanFound" expires_after="2020-01-30">
+    enum="BooleanFound" expires_after="2021-01-30">
 <!-- Name completed by histogram_suffixes
      name="ThumbnailProvider.ClientType" -->
 
@@ -5982,7 +5985,7 @@
 </histogram>
 
 <histogram name="Apps.AppListRecommendedResponse.Latency" units="ms"
-    expires_after="2020-05-17">
+    expires_after="2020-07-19">
   <owner>napper@chromium.org</owner>
   <owner>robsc@chromium.org</owner>
   <summary>
@@ -7573,7 +7576,7 @@
 </histogram>
 
 <histogram name="Arc.SdkVersionUpgradeType" enum="ArcSdkVersionUpgradeType"
-    expires_after="2020-05-10">
+    expires_after="2020-07-19">
   <owner>niwa@google.com</owner>
   <owner>yusukes@google.com</owner>
   <summary>
@@ -9308,7 +9311,7 @@
 </histogram>
 
 <histogram name="Ash.WindowCycleController.DesksSwitchDistance" units="units"
-    expires_after="2020-04-05">
+    expires_after="2020-07-19">
   <owner>afakhry@chromium.org</owner>
   <owner>tclaiborne@chromium.org</owner>
   <summary>
@@ -11069,7 +11072,7 @@
 </histogram>
 
 <histogram name="AuthPolicy.ErrorTypeOfJoinADDomain" enum="AuthPolicyErrorType"
-    expires_after="2020-04-05">
+    expires_after="2020-07-19">
   <owner>fsandrade@chromium.org</owner>
   <owner>tomdobro@chromium.org</owner>
   <summary>
@@ -11163,7 +11166,7 @@
 </histogram>
 
 <histogram name="AuthPolicy.TimeToJoinADDomain" units="ms"
-    expires_after="2020-04-05">
+    expires_after="2020-07-19">
   <owner>fsandrade@chromium.org</owner>
   <owner>tomdobro@chromium.org</owner>
   <summary>
@@ -14139,7 +14142,7 @@
 
 <histogram
     name="AutoScreenBrightness.AdapterDecisionAtUserChange.BrightnessChange.Cause"
-    enum="AutoScreenBrightnessBrightnessChangeCause" expires_after="2020-03-08">
+    enum="AutoScreenBrightnessBrightnessChangeCause" expires_after="2020-07-19">
   <owner>jiameng@chromium.org</owner>
   <summary>
     When user changes brightness manually, we will ask the model whether it also
@@ -14196,7 +14199,7 @@
 </histogram>
 
 <histogram name="AutoScreenBrightness.BrightnessChange.Cause"
-    enum="AutoScreenBrightnessBrightnessChangeCause" expires_after="2020-05-03">
+    enum="AutoScreenBrightnessBrightnessChangeCause" expires_after="2020-07-19">
   <owner>jiameng@chromium.org</owner>
   <summary>Reason for the model to change brightness. Chrome OS only.</summary>
 </histogram>
@@ -14288,7 +14291,7 @@
 </histogram>
 
 <histogram name="AutoScreenBrightness.DailyUserAdjustment.SupportedAls"
-    units="count" expires_after="2020-05-03">
+    units="count" expires_after="2020-07-19">
   <owner>jiameng@chromium.org</owner>
   <summary>
     Number of times that a user has made brightness adjustments on a device with
@@ -17776,7 +17779,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.Outcome" enum="SMSReceiverOutcome"
-    expires_after="2020-05-17">
+    expires_after="2020-07-19">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
   <owner>ayui@chromium.org</owner>
@@ -22796,7 +22799,7 @@
 </histogram>
 
 <histogram name="ChromeOS.SAML.Scraping.PasswordCountAll" units="passwords"
-    expires_after="2020-04-02">
+    expires_after="2020-07-19">
   <owner>mslus@chromium.org</owner>
   <owner>emaxx@chromium.org</owner>
   <summary>
@@ -39710,8 +39713,9 @@
 
 <histogram name="Download.Service.Files.Cleanup.Attempts" units="attempts"
     expires_after="M81">
-<!-- expires-never: Used for monitoring background download system stability. -->
-
+  <obsolete>
+    Deprecated as of 01/2020.
+  </obsolete>
   <owner>shaktisahu@chromium.org</owner>
   <summary>
     The number of times download service tried to delete the download file
@@ -39966,11 +39970,12 @@
 </histogram>
 
 <histogram base="true" name="Download.Service.Upload.HasUploadData"
-    enum="BooleanUpload" expires_after="M81">
+    enum="BooleanUpload" expires_after="2021-01-31">
 <!-- Name completed by histogram_suffixes
      name="Download.Service.Client" -->
 
   <owner>shaktisahu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
   <summary>
     The download service uses a single code path for both downloading and
     uploading data. This metric records whether a given request includes upload
@@ -40084,8 +40089,9 @@
 </histogram>
 
 <histogram name="Download.Start.ContentType.NormalProfile"
-    enum="DownloadContentType" expires_after="M81">
+    enum="DownloadContentType" expires_after="2021-01-31">
   <owner>shaktisahu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
   <summary>
     Content types of the downloads that are started in non-incognito profile.
   </summary>
@@ -43232,7 +43238,7 @@
 </histogram>
 
 <histogram name="Event.FrameEventRouting.TouchEventAckQueueSize" units="units"
-    expires_after="2020-05-17">
+    expires_after="2020-07-19">
   <owner>wjmaclean@chromium.org</owner>
   <owner>kenrb@chromium.org</owner>
   <summary>
@@ -46891,7 +46897,7 @@
 </histogram>
 
 <histogram name="ExploreSites.SiteTilesClickIndex2" units="units"
-    expires_after="2020-04-26">
+    expires_after="2020-07-19">
   <owner>dewittj@chromium.org</owner>
   <owner>petewil@chromium.org</owner>
   <summary>
@@ -57377,7 +57383,7 @@
 </histogram>
 
 <histogram name="GPU.IOSurface.TexImageTime" units="ms"
-    expires_after="2020-05-03">
+    expires_after="2020-07-19">
   <owner>ccameron@chromium.org</owner>
   <summary>
     The time that it took for a call to CGLTexImageIOSurface2D to complete.
@@ -58032,7 +58038,7 @@
 </histogram>
 
 <histogram name="GPU.WatchdogThread.V1.WaitTime" units="ms"
-    expires_after="2020-05-01">
+    expires_after="2020-07-19">
   <owner>magchen@chromium.org</owner>
   <owner>zmo@chromium.org</owner>
   <summary>
@@ -58342,7 +58348,7 @@
 </histogram>
 
 <histogram name="GridTabSwitcher.ThumbnailFetchingResult"
-    enum="GridTabSwitcherThumbnailFetchingResult" expires_after="2020-05-03">
+    enum="GridTabSwitcherThumbnailFetchingResult" expires_after="2020-07-19">
   <owner>yusufo@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
   <summary>
@@ -64238,14 +64244,16 @@
 </histogram>
 
 <histogram name="Kiosk.Launch.CryptohomeFailure" enum="LoginFailureReason"
-    expires_after="2020-07-13">
+    expires_after="2021-01-15">
   <owner>xiyuan@chromium.org</owner>
+  <owner>apotapchuk@chromium.org</owner>
   <summary>Tracks cryptohome failure during kiosk launch.</summary>
 </histogram>
 
 <histogram name="Kiosk.Launch.Error" enum="KioskLaunchError"
-    expires_after="M81">
+    expires_after="2021-01-15">
   <owner>xiyuan@chromium.org</owner>
+  <owner>apotapchuk@chromium.org</owner>
   <summary>Tracks kiosk launch errors.</summary>
 </histogram>
 
@@ -65470,7 +65478,7 @@
 </histogram>
 
 <histogram name="Login.PasswordChangeFlow" enum="LoginPasswordChangeFlow"
-    expires_after="2020-04-19">
+    expires_after="2020-07-19">
   <owner>xiyuan@chromium.org</owner>
   <owner>omrilio@chromium.org</owner>
   <summary>
@@ -98585,7 +98593,7 @@
 </histogram>
 
 <histogram name="NQE.CellularSignalStrength.LevelAvailable"
-    enum="BooleanAvailable" expires_after="2020-05-17">
+    enum="BooleanAvailable" expires_after="2020-07-19">
   <owner>tbansal@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
   <summary>
@@ -118519,6 +118527,13 @@
   </summary>
 </histogram>
 
+<histogram name="Power.SmartCharging.Messages" enum="SmartChargingMessages"
+    expires_after="2020-12-31">
+  <owner>thanhdng@chromium.org</owner>
+  <owner>jiameng@chromium.org</owner>
+  <summary>Type of messages that are reported by smart charging.</summary>
+</histogram>
+
 <histogram name="Power.SuspendAttempt" enum="SuspendAttempt"
     expires_after="2020-05-24">
   <owner>tbroch@chromium.org</owner>
@@ -132214,7 +132229,7 @@
   </summary>
 </histogram>
 
-<histogram name="SB2.RemoteCall.Elapsed" units="ms" expires_after="2020-04-05">
+<histogram name="SB2.RemoteCall.Elapsed" units="ms" expires_after="2020-07-19">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -141821,7 +141836,7 @@
 </histogram>
 
 <histogram name="Settings.ShowHomeButtonPreferenceState" enum="BooleanEnabled"
-    expires_after="M82">
+    expires_after="M85">
   <owner>twellington@chromium.org</owner>
   <owner>tedchoc@chromium.org</owner>
   <summary>
@@ -141829,11 +141844,14 @@
     force homepage feature flag is enabled or the home button is partner
     provided. Recorded for Android only on deferred startup and when the user
     changes their home button enabled setting.
+
+    This histogram is currently being updated to
+    ShowHomeButtonPreferenceStateManaged and will eventually be removed.
   </summary>
 </histogram>
 
 <histogram name="Settings.ShowHomeButtonPreferenceStateChanged"
-    enum="BooleanEnabled" expires_after="M82">
+    enum="BooleanEnabled" expires_after="M85">
   <owner>twellington@chromium.org</owner>
   <owner>tedchoc@chromium.org</owner>
   <summary>
@@ -141842,6 +141860,19 @@
   </summary>
 </histogram>
 
+<histogram name="Settings.ShowHomeButtonPreferenceStateManaged"
+    enum="HomeButtonPreferenceStateType" expires_after="M85">
+  <owner>twellington@chromium.org</owner>
+  <owner>tedchoc@chromium.org</owner>
+  <summary>
+    If the home button is enabled or disabled, either by user or by policy.
+    Recorded for Android only on deferred startup and when the user changes
+    their home button enabled setting.
+
+    Note: HomeButtonPreferenceStateType#ManagedDisabled is currently not used.
+  </summary>
+</histogram>
+
 <histogram name="Settings.StartSetAsDefault" enum="BooleanHit"
     expires_after="M77">
   <owner>pmonette@chromium.org</owner>
@@ -153732,8 +153763,7 @@
   </summary>
 </histogram>
 
-<histogram name="Sync.Local.Enabled" enum="BooleanEnabled"
-    expires_after="2020-05-03">
+<histogram name="Sync.Local.Enabled" enum="BooleanEnabled" expires_after="M86">
   <owner>pastarmovj@chromium.org</owner>
   <summary>
     Tracks the number of times the local sync backend was enabled by the user.
@@ -153748,7 +153778,7 @@
   <summary>Tracks the size of the local sync backend database file.</summary>
 </histogram>
 
-<histogram name="Sync.Local.FileSizeKB" units="KB" expires_after="M81">
+<histogram name="Sync.Local.FileSizeKB" units="KB" expires_after="M86">
   <owner>pastarmovj@chromium.org</owner>
   <summary>
     Tracks the size of the local sync backend database file. Recorded every time
@@ -153757,7 +153787,7 @@
 </histogram>
 
 <histogram name="Sync.Local.RequestTypeOnError" enum="SyncRequestType"
-    expires_after="M81">
+    expires_after="M86">
   <owner>skym@chromium.org</owner>
   <summary>
     Tracks the types of requests that caused errors inside of the local server.
@@ -153765,7 +153795,7 @@
 </histogram>
 
 <histogram name="Sync.Local.RoamingProfileUnavailable" enum="BooleanError"
-    expires_after="M81">
+    expires_after="M86">
   <owner>pastarmovj@chromium.org</owner>
   <summary>
     Tracks the number of times the Roaming profile cannot be retrieved.
@@ -159716,7 +159746,7 @@
 </histogram>
 
 <histogram name="Translate.CLD3.LanguageDetected" enum="CLD3LanguageCode"
-    expires_after="2020-05-17">
+    expires_after="2020-07-19">
   <owner>frechette@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -159824,7 +159854,7 @@
 </histogram>
 
 <histogram name="Translate.DeclineTranslateCloseInfobar" units="units"
-    expires_after="2020-05-17">
+    expires_after="2020-07-19">
   <owner>kenjibaheux@google.com</owner>
   <summary>
     The number of times the translate infobar was closed by clicking the X
@@ -159833,7 +159863,7 @@
 </histogram>
 
 <histogram name="Translate.DeclineTranslateDismissUI" units="units"
-    expires_after="2020-05-17">
+    expires_after="2020-07-19">
   <owner>kenjibaheux@google.com</owner>
   <summary>
     The number of times the translate UI was closed without translating in the
@@ -160145,7 +160175,7 @@
 </histogram>
 
 <histogram name="Translate.RevertTranslation" units="units"
-    expires_after="2020-05-17">
+    expires_after="2020-07-19">
   <owner>kenjibaheux@google.com</owner>
   <summary>
     The number of times the show original button was clicked in the translate
@@ -161392,7 +161422,7 @@
 </histogram>
 
 <histogram name="UMA.LogUpload.Canceled.CellularConstraint"
-    enum="BooleanCanceled" expires_after="2020-04-30">
+    enum="BooleanCanceled" expires_after="2020-07-19">
   <owner>holte@chromium.org</owner>
   <owner>asvitkine@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
@@ -161803,7 +161833,7 @@
 </histogram>
 
 <histogram name="UMA.TruncatedEvents.UserAction" units="events"
-    expires_after="2020-07-13">
+    expires_after="2020-07-19">
   <owner>rkaplow@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
@@ -164814,7 +164844,7 @@
 </histogram>
 
 <histogram name="V8.WasmModuleCodeSizeMiB" units="MB"
-    expires_after="2020-03-31">
+    expires_after="2020-07-19">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensb@chromium.org</owner>
@@ -164825,7 +164855,7 @@
 </histogram>
 
 <histogram name="V8.WasmModuleCodeSizePercentFreed" units="%"
-    expires_after="2020-03-31">
+    expires_after="2020-07-19">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensb@chromium.org</owner>
@@ -169336,7 +169366,7 @@
 </histogram>
 
 <histogram name="WebCore.WebSocket.SendType" enum="WebSocketSendType"
-    expires_after="2020-05-17">
+    expires_after="2020-07-19">
   <owner>yhirano@chromium.org</owner>
   <owner>ricea@chromium.org</owner>
   <summary>
@@ -169539,7 +169569,7 @@
 </histogram>
 
 <histogram name="WebFont.HadBlankText" enum="BooleanHadBlankText"
-    expires_after="2020-04-19">
+    expires_after="2020-07-19">
   <owner>kenjibaheux@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <summary>
@@ -173194,7 +173224,7 @@
 </histogram>
 
 <histogram name="WebRtcEventLogging.NetError" units="units"
-    expires_after="2020-05-17">
+    expires_after="2020-07-19">
   <owner>eladalon@chromium.org</owner>
   <owner>saeedj@google.com</owner>
   <owner>manj@google.com</owner>
diff --git a/tools/perf/core/tbmv3/metrics/console_error_metric.proto b/tools/perf/core/tbmv3/metrics/console_error_metric.proto
index 7769cd8..25d65eb 100644
--- a/tools/perf/core/tbmv3/metrics/console_error_metric.proto
+++ b/tools/perf/core/tbmv3/metrics/console_error_metric.proto
@@ -2,7 +2,6 @@
 // SPDX-License-Identifier: Apache-2.0
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/tools/perf/core/tbmv3/metrics/dummy_metric.proto b/tools/perf/core/tbmv3/metrics/dummy_metric.proto
index 99c3a2b4..2d37e75 100644
--- a/tools/perf/core/tbmv3/metrics/dummy_metric.proto
+++ b/tools/perf/core/tbmv3/metrics/dummy_metric.proto
@@ -2,7 +2,6 @@
 // SPDX-License-Identifier: Apache-2.0
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/ui/android/java/src/org/chromium/ui/base/Clipboard.java b/ui/android/java/src/org/chromium/ui/base/Clipboard.java
index 58d16506..00c42e3 100644
--- a/ui/android/java/src/org/chromium/ui/base/Clipboard.java
+++ b/ui/android/java/src/org/chromium/ui/base/Clipboard.java
@@ -10,6 +10,8 @@
 import android.content.ClipDescription;
 import android.content.ClipboardManager;
 import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
 import android.os.Build;
 import android.text.Html;
 import android.text.Spanned;
@@ -154,6 +156,24 @@
     }
 
     /**
+     * Setting the clipboard's current primary clip to an image.
+     * @param Uri The {@link Uri} will become the content of the clipboard's primary clip.
+     */
+    public void setImage(final Uri uri) {
+        if (uri == null) {
+            showCopyToClipboardFailureMessage();
+            return;
+        }
+
+        ContextUtils.getApplicationContext().grantUriPermission(
+                ClipboardManager.class.getCanonicalName(), uri,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        ClipData clip = ClipData.newUri(
+                ContextUtils.getApplicationContext().getContentResolver(), "image", uri);
+        setPrimaryClipNoException(clip);
+    }
+
+    /**
      * Writes HTML to the clipboard, together with a plain-text representation
      * of that very data.
      *
@@ -179,11 +199,15 @@
             mClipboardManager.setPrimaryClip(clip);
         } catch (Exception ex) {
             // Ignore any exceptions here as certain devices have bugs and will fail.
-            String text = mContext.getString(R.string.copy_to_clipboard_failure_message);
-            Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();
+            showCopyToClipboardFailureMessage();
         }
     }
 
+    private void showCopyToClipboardFailureMessage() {
+        String text = mContext.getString(R.string.copy_to_clipboard_failure_message);
+        Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();
+    }
+
     @CalledByNative
     private void setNativePtr(long nativeClipboard) {
         mNativeClipboard = nativeClipboard;
diff --git a/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java b/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java
index 30099db7..375df79 100644
--- a/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java
+++ b/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java
@@ -10,6 +10,7 @@
 
 import android.content.ClipData;
 import android.content.Intent;
+import android.net.Uri;
 import android.text.SpannableString;
 import android.text.style.RelativeSizeSpan;
 
@@ -27,6 +28,7 @@
 public class ClipboardTest {
     private static final String PLAIN_TEXT = "plain";
     private static final String HTML_TEXT = "<span style=\"color: red;\">HTML</span>";
+    private static final Uri IMAGE_URI = Uri.parse("content://test.image");
 
     @Test
     public void testClipDataToHtmlText() {
@@ -53,4 +55,18 @@
         ClipData intentClip = ClipData.newIntent("intent", intent);
         assertNull(clipboard.clipDataToHtmlText(intentClip));
     }
+
+    @Test
+    public void testSetImage() {
+        Clipboard clipboard = Clipboard.getInstance();
+
+        // simple set a null, check if there is no crash.
+        clipboard.setImage(null);
+
+        // Set actually data.
+        clipboard.setImage(IMAGE_URI);
+
+        // TODO(gangwu): http://crbug.com/1043490
+        // Add verification here to check if system clipboard get the image.
+    }
 }
diff --git a/ui/events/blink/DEPS b/ui/events/blink/DEPS
index bcd0b16..5684142f 100644
--- a/ui/events/blink/DEPS
+++ b/ui/events/blink/DEPS
@@ -14,7 +14,6 @@
   "+third_party/blink/public/common/input/web_mouse_wheel_event.h",
   "+third_party/blink/public/common/input/web_pointer_event.h",
   "+third_party/blink/public/common/input/web_touch_event.h",
-  "+third_party/blink/public/platform/web_point.h",
 
   "+ui/display/win",
   "+ui/gfx",
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc
index bd6c11e..54e9a079 100644
--- a/ui/events/blink/input_handler_proxy_unittest.cc
+++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -25,7 +25,6 @@
 #include "third_party/blink/public/common/input/web_mouse_event.h"
 #include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
 #include "third_party/blink/public/common/input/web_touch_event.h"
-#include "third_party/blink/public/platform/web_point.h"
 #include "ui/events/blink/blink_event_util.h"
 #include "ui/events/blink/compositor_thread_event_queue.h"
 #include "ui/events/blink/did_overscroll_params.h"
diff --git a/ui/login/screen_container.css b/ui/login/screen_container.css
index 88aef84c..0da5e26 100644
--- a/ui/login/screen_container.css
+++ b/ui/login/screen_container.css
@@ -108,46 +108,6 @@
   transform: translateY(50px) rotateX(-2.5deg);
 }
 
-#account-picker-dot,
-#app-launch-splash-dot,
-#arc-kiosk-splash-dot,
-#auto-enrollment-check-dot,
-#autolaunch-dot,
-#confirm-password-dot,
-#debugging-dot,
-#device-disabled-dot,
-#enrollment-dot,
-#error-message-dot,
-#fatal-error-dot,
-#hid-detection-dot,
-#kiosk-enable-dot,
-#oauth-enrollment-dot,
-#password-changed-dot,
-#reset-dot,
-#supervised-user-creation-dialog-dot,
-#supervised-user-creation-dot,
-#terms-of-service-dot,
-#arc-tos-dot,
-#tpm-error-message-dot,
-#update-required-dot,
-#wrong-hwid-dot,
-#unrecoverable-cryptohome-error-dot {
-  display: none;
-}
-
-#oobe.connect #connect-dot,
-#oobe.enrollment #gaia-signin-dot,
-#oobe.enrollment #signin-dot,
-#oobe.eula #eula-dot,
-#oobe.gaia-signin #gaia-signin-dot,
-#oobe.oauth-enrollment #gaia-signin-dot,
-#oobe.oauth-enrollment #signin-dot,
-#oobe.signin #signin-dot,
-#oobe.update #update-dot,
-#oobe.user-image #user-image-dot {
-  opacity: 1;
-}
-
 body:not(.oobe-display) #inner-container {
   height: 262px;
   padding: 0;
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_controller.cc b/ui/ozone/platform/drm/gpu/hardware_display_controller.cc
index b8b8b42..703f870 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_controller.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_controller.cc
@@ -102,7 +102,7 @@
     controller->Disable();
 
   bool ret = GetDrmDevice()->plane_manager()->DisableOverlayPlanes(
-      &owned_hardware_planes_);
+      GetAllOwnedPlanes());
   LOG_IF(ERROR, !ret) << "Can't disable overlays when disabling HDC.";
 
   is_disabled_ = true;
@@ -162,16 +162,19 @@
             [](const DrmOverlayPlane& l, const DrmOverlayPlane& r) {
               return l.z_order < r.z_order;
             });
-  GetDrmDevice()->plane_manager()->BeginFrame(&owned_hardware_planes_);
+
+  HardwareDisplayPlaneList hardware_planes;
+  hardware_planes.old_plane_list = GetAllOwnedPlanes();
+
+  GetDrmDevice()->plane_manager()->BeginFrame(hardware_planes);
 
   bool status = true;
   for (const auto& controller : crtc_controllers_) {
-    status &= controller->AssignOverlayPlanes(&owned_hardware_planes_,
-                                              pending_planes);
+    status &= controller->AssignOverlayPlanes(&hardware_planes, pending_planes);
   }
 
   status &= GetDrmDevice()->plane_manager()->Commit(
-      &owned_hardware_planes_, page_flip_request, out_fence);
+      hardware_planes, page_flip_request, out_fence);
 
   return status;
 }
@@ -233,18 +236,7 @@
 
 void HardwareDisplayController::AddCrtc(
     std::unique_ptr<CrtcController> controller) {
-  scoped_refptr<DrmDevice> drm = controller->drm();
-  DCHECK(crtc_controllers_.empty() || drm == GetDrmDevice());
-
-  // Check if this controller owns any planes and ensure we keep track of them.
-  const std::vector<std::unique_ptr<HardwareDisplayPlane>>& all_planes =
-      drm->plane_manager()->planes();
-  uint32_t crtc = controller->crtc();
-  for (const auto& plane : all_planes) {
-    if (plane->in_use() && (plane->owning_crtc() == crtc))
-      owned_hardware_planes_.old_plane_list.push_back(plane.get());
-  }
-
+  DCHECK(crtc_controllers_.empty() || controller->drm() == GetDrmDevice());
   crtc_controllers_.push_back(std::move(controller));
 }
 
@@ -262,26 +254,9 @@
   std::unique_ptr<CrtcController> controller(std::move(*controller_it));
   crtc_controllers_.erase(controller_it);
 
-  // Move all the planes that have been committed in the last pageflip for this
-  // CRTC at the end of the collection.
-  auto first_plane_to_disable_it =
-      std::partition(owned_hardware_planes_.old_plane_list.begin(),
-                     owned_hardware_planes_.old_plane_list.end(),
-                     [crtc](const HardwareDisplayPlane* plane) {
-                       return plane->owning_crtc() != crtc;
-                     });
-
-  // Disable the planes enabled with the last commit on |crtc|, otherwise
-  // the planes will be visible if the crtc is reassigned to another connector.
-  HardwareDisplayPlaneList hardware_plane_list;
-  std::copy(first_plane_to_disable_it,
-            owned_hardware_planes_.old_plane_list.end(),
-            std::back_inserter(hardware_plane_list.old_plane_list));
-  drm->plane_manager()->DisableOverlayPlanes(&hardware_plane_list);
-
-  // Remove the planes assigned to |crtc|.
-  owned_hardware_planes_.old_plane_list.erase(
-      first_plane_to_disable_it, owned_hardware_planes_.old_plane_list.end());
+  std::vector<HardwareDisplayPlane*> planes =
+      drm->plane_manager()->GetOwnedPlanesForCrtcs({crtc});
+  drm->plane_manager()->DisableOverlayPlanes(planes);
 
   return controller;
 }
@@ -398,4 +373,12 @@
   UpdateCursorImage();
 }
 
+std::vector<HardwareDisplayPlane*>
+HardwareDisplayController::GetAllOwnedPlanes() {
+  std::vector<uint32_t> crtcs;
+  for (const auto& controller : crtc_controllers_)
+    crtcs.push_back(controller->crtc());
+  return GetDrmDevice()->plane_manager()->GetOwnedPlanesForCrtcs(crtcs);
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_controller.h b/ui/ozone/platform/drm/gpu/hardware_display_controller.h
index 264e250..ef0adba 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_controller.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_controller.h
@@ -179,8 +179,7 @@
   void UpdateCursorLocation();
   void ResetCursor();
   void DisableCursor();
-
-  HardwareDisplayPlaneList owned_hardware_planes_;
+  std::vector<HardwareDisplayPlane*> GetAllOwnedPlanes();
 
   // Stores the CRTC configuration. This is used to identify monitors and
   // configure them.
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
index 5954d0c5..36c87f9d 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
@@ -136,20 +136,16 @@
 }
 
 void HardwareDisplayPlaneManager::ResetCurrentPlaneList(
-    HardwareDisplayPlaneList* plane_list) const {
-  for (auto* hardware_plane : plane_list->plane_list) {
+    const HardwareDisplayPlaneList& plane_list) const {
+  for (auto* hardware_plane : plane_list.plane_list) {
     hardware_plane->set_in_use(false);
     hardware_plane->set_owning_crtc(0);
   }
-
-  plane_list->plane_list.clear();
-  plane_list->legacy_page_flips.clear();
-  plane_list->atomic_property_set.reset(drmModeAtomicAlloc());
 }
 
 void HardwareDisplayPlaneManager::BeginFrame(
-    HardwareDisplayPlaneList* plane_list) {
-  for (auto* plane : plane_list->old_plane_list) {
+    const HardwareDisplayPlaneList& plane_list) {
+  for (auto* plane : plane_list.old_plane_list) {
     plane->set_in_use(false);
   }
 }
@@ -170,7 +166,7 @@
         FindNextUnusedPlane(&plane_idx, crtc_index, plane);
     if (!hw_plane) {
       LOG(ERROR) << "Failed to find a free plane for crtc " << crtc_id;
-      ResetCurrentPlaneList(plane_list);
+      ResetCurrentPlaneList(*plane_list);
       return false;
     }
 
@@ -191,7 +187,7 @@
     }
 
     if (!SetPlaneData(plane_list, hw_plane, plane, crtc_id, fixed_point_rect)) {
-      ResetCurrentPlaneList(plane_list);
+      ResetCurrentPlaneList(*plane_list);
       return false;
     }
 
@@ -222,6 +218,19 @@
   return std::vector<uint64_t>();
 }
 
+std::vector<HardwareDisplayPlane*>
+HardwareDisplayPlaneManager::GetOwnedPlanesForCrtcs(
+    const std::vector<uint32_t>& crtcs) {
+  std::vector<HardwareDisplayPlane*> result;
+  for (const auto& plane : planes()) {
+    if (std::find(crtcs.begin(), crtcs.end(), plane->owning_crtc()) !=
+        crtcs.end()) {
+      result.push_back(plane.get());
+    }
+  }
+  return result;
+}
+
 bool HardwareDisplayPlaneManager::SetColorMatrix(
     uint32_t crtc_id,
     const std::vector<float>& color_matrix) {
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
index 5a839b0..c19c4a46c 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
@@ -65,7 +65,7 @@
 
   // Clears old frame state out. Must be called before any AssignOverlayPlanes
   // calls.
-  void BeginFrame(HardwareDisplayPlaneList* plane_list);
+  void BeginFrame(const HardwareDisplayPlaneList& plane_list);
 
   // Sets the color transform matrix (a 3x3 matrix represented in vector form)
   // on the CRTC with ID |crtc_id|.
@@ -95,13 +95,13 @@
   // out buffers are replaced, and not when the buffers are scheduled with
   // |page_flip_request|. Note that the returned fence may be a nullptr
   // if the system doesn't support out fences.
-  virtual bool Commit(HardwareDisplayPlaneList* plane_list,
+  virtual bool Commit(const HardwareDisplayPlaneList& plane_list,
                       scoped_refptr<PageFlipRequest> page_flip_request,
                       std::unique_ptr<gfx::GpuFence>* out_fence) = 0;
 
-  // Disable all the overlay planes previously submitted and now stored in
-  // plane_list->old_plane_list.
-  virtual bool DisableOverlayPlanes(HardwareDisplayPlaneList* plane_list) = 0;
+  // Disable all the overlay planes in |plane_list|.
+  virtual bool DisableOverlayPlanes(
+      const std::vector<HardwareDisplayPlane*>& plane_list) = 0;
 
   // Set the drm_color_ctm contained in |ctm_blob_data| to all planes' KMS
   // states
@@ -132,6 +132,9 @@
   std::vector<uint64_t> GetFormatModifiers(uint32_t crtc_id,
                                            uint32_t format) const;
 
+  std::vector<HardwareDisplayPlane*> GetOwnedPlanesForCrtcs(
+      const std::vector<uint32_t>& crtcs);
+
  protected:
   struct CrtcProperties {
     // Unique identifier for the CRTC. This must be greater than 0 to be valid.
@@ -191,7 +194,7 @@
                             const DrmOverlayPlane& overlay,
                             uint32_t crtc_index) const;
 
-  void ResetCurrentPlaneList(HardwareDisplayPlaneList* plane_list) const;
+  void ResetCurrentPlaneList(const HardwareDisplayPlaneList& plane_list) const;
 
   // Populates scanout formats supported by all planes.
   void PopulateSupportedFormats();
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
index 75fb9a5..95cc188 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
@@ -61,31 +61,31 @@
 }
 
 bool HardwareDisplayPlaneManagerAtomic::Commit(
-    HardwareDisplayPlaneList* plane_list,
+    const HardwareDisplayPlaneList& plane_list,
     scoped_refptr<PageFlipRequest> page_flip_request,
     std::unique_ptr<gfx::GpuFence>* out_fence) {
   bool test_only = !page_flip_request;
-  for (HardwareDisplayPlane* plane : plane_list->old_plane_list) {
-    if (!base::Contains(plane_list->plane_list, plane)) {
+  for (HardwareDisplayPlane* plane : plane_list.old_plane_list) {
+    if (!base::Contains(plane_list.plane_list, plane)) {
       // This plane is being released, so we need to zero it.
       plane->set_in_use(false);
       HardwareDisplayPlaneAtomic* atomic_plane =
           static_cast<HardwareDisplayPlaneAtomic*>(plane);
       atomic_plane->SetPlaneData(
-          plane_list->atomic_property_set.get(), 0, 0, gfx::Rect(), gfx::Rect(),
+          plane_list.atomic_property_set.get(), 0, 0, gfx::Rect(), gfx::Rect(),
           gfx::OVERLAY_TRANSFORM_NONE, base::kInvalidPlatformFile);
     }
   }
 
   std::vector<uint32_t> crtcs;
-  for (HardwareDisplayPlane* plane : plane_list->plane_list) {
+  for (HardwareDisplayPlane* plane : plane_list.plane_list) {
     HardwareDisplayPlaneAtomic* atomic_plane =
         static_cast<HardwareDisplayPlaneAtomic*>(plane);
     if (crtcs.empty() || crtcs.back() != atomic_plane->crtc_id())
       crtcs.push_back(atomic_plane->crtc_id());
   }
 
-  drmModeAtomicReqPtr request = plane_list->atomic_property_set.get();
+  drmModeAtomicReqPtr request = plane_list.atomic_property_set.get();
   for (uint32_t crtc : crtcs) {
     int idx = LookupCrtcIndex(crtc);
 
@@ -104,11 +104,9 @@
   }
 
   if (test_only) {
-    for (HardwareDisplayPlane* plane : plane_list->plane_list) {
+    for (HardwareDisplayPlane* plane : plane_list.plane_list) {
       plane->set_in_use(false);
     }
-  } else {
-    plane_list->plane_list.swap(plane_list->old_plane_list);
   }
 
   uint32_t flags = 0;
@@ -129,15 +127,14 @@
   {
     std::vector<base::ScopedFD::Receiver> out_fence_fd_receivers;
     if (out_fence) {
-      if (!AddOutFencePtrProperties(plane_list->atomic_property_set.get(),
-                                    crtcs, &out_fence_fds,
-                                    &out_fence_fd_receivers)) {
+      if (!AddOutFencePtrProperties(plane_list.atomic_property_set.get(), crtcs,
+                                    &out_fence_fds, &out_fence_fd_receivers)) {
         ResetCurrentPlaneList(plane_list);
         return false;
       }
     }
 
-    if (!drm_->CommitProperties(plane_list->atomic_property_set.get(), flags,
+    if (!drm_->CommitProperties(plane_list.atomic_property_set.get(), flags,
                                 crtcs.size(), page_flip_request)) {
       if (!test_only) {
         PLOG(ERROR) << "Failed to commit properties for page flip.";
@@ -153,14 +150,13 @@
   if (out_fence)
     *out_fence = CreateMergedGpuFenceFromFDs(std::move(out_fence_fds));
 
-  plane_list->plane_list.clear();
-  plane_list->atomic_property_set.reset(drmModeAtomicAlloc());
   return true;
 }
 
 bool HardwareDisplayPlaneManagerAtomic::DisableOverlayPlanes(
-    HardwareDisplayPlaneList* plane_list) {
-  for (HardwareDisplayPlane* plane : plane_list->old_plane_list) {
+    const std::vector<HardwareDisplayPlane*>& plane_list) {
+  ScopedDrmAtomicReqPtr atomic_property_set(drmModeAtomicAlloc());
+  for (HardwareDisplayPlane* plane : plane_list) {
     if (plane->type() != HardwareDisplayPlane::kOverlay)
       continue;
     plane->set_in_use(false);
@@ -168,15 +164,14 @@
 
     HardwareDisplayPlaneAtomic* atomic_plane =
         static_cast<HardwareDisplayPlaneAtomic*>(plane);
-    atomic_plane->SetPlaneData(
-        plane_list->atomic_property_set.get(), 0, 0, gfx::Rect(), gfx::Rect(),
-        gfx::OVERLAY_TRANSFORM_NONE, base::kInvalidPlatformFile);
+    atomic_plane->SetPlaneData(atomic_property_set.get(), 0, 0, gfx::Rect(),
+                               gfx::Rect(), gfx::OVERLAY_TRANSFORM_NONE,
+                               base::kInvalidPlatformFile);
   }
-  bool ret = drm_->CommitProperties(plane_list->atomic_property_set.get(),
+  bool ret = drm_->CommitProperties(atomic_property_set.get(),
                                     DRM_MODE_ATOMIC_NONBLOCK, 0, nullptr);
   PLOG_IF(ERROR, !ret) << "Failed to commit properties for page flip.";
 
-  plane_list->atomic_property_set.reset(drmModeAtomicAlloc());
   return ret;
 }
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h
index 6c9d69bb..90e19b1d 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h
@@ -19,10 +19,11 @@
   ~HardwareDisplayPlaneManagerAtomic() override;
 
   // HardwareDisplayPlaneManager:
-  bool Commit(HardwareDisplayPlaneList* plane_list,
+  bool Commit(const HardwareDisplayPlaneList& plane_list,
               scoped_refptr<PageFlipRequest> page_flip_request,
               std::unique_ptr<gfx::GpuFence>* out_fence) override;
-  bool DisableOverlayPlanes(HardwareDisplayPlaneList* plane_list) override;
+  bool DisableOverlayPlanes(
+      const std::vector<HardwareDisplayPlane*>& plane_list) override;
 
   bool SetColorCorrectionOnAllCrtcPlanes(
       uint32_t crtc_id,
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
index fe4d34f..fa6ba3b 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
@@ -46,23 +46,21 @@
 }
 
 bool HardwareDisplayPlaneManagerLegacy::Commit(
-    HardwareDisplayPlaneList* plane_list,
+    const HardwareDisplayPlaneList& plane_list,
     scoped_refptr<PageFlipRequest> page_flip_request,
     std::unique_ptr<gfx::GpuFence>* out_fence) {
   bool test_only = !page_flip_request;
   if (test_only) {
-    for (HardwareDisplayPlane* plane : plane_list->plane_list) {
+    for (HardwareDisplayPlane* plane : plane_list.plane_list) {
       plane->set_in_use(false);
     }
-    plane_list->plane_list.clear();
-    plane_list->legacy_page_flips.clear();
     return true;
   }
-  if (plane_list->plane_list.empty())  // No assigned planes, nothing to do.
+  if (plane_list.plane_list.empty())  // No assigned planes, nothing to do.
     return true;
 
   bool ret = true;
-  for (const auto& flip : plane_list->legacy_page_flips) {
+  for (const auto& flip : plane_list.legacy_page_flips) {
     if (!drm_->PageFlip(flip.crtc_id, flip.framebuffer, page_flip_request)) {
       // 1) Permission Denied is a legitimate error.
       // 2) EBUSY or ENODEV are possible if we're page flipping a disconnected
@@ -80,25 +78,19 @@
     }
   }
 
-  if (ret) {
-    plane_list->plane_list.swap(plane_list->old_plane_list);
-    plane_list->plane_list.clear();
-    plane_list->legacy_page_flips.clear();
-  } else {
+  if (!ret)
     ResetCurrentPlaneList(plane_list);
-  }
 
   return ret;
 }
 
 bool HardwareDisplayPlaneManagerLegacy::DisableOverlayPlanes(
-    HardwareDisplayPlaneList* plane_list) {
+    const std::vector<HardwareDisplayPlane*>& plane_list) {
   // We're never going to ship legacy pageflip with overlays enabled.
-  DCHECK(std::find_if(plane_list->old_plane_list.begin(),
-                      plane_list->old_plane_list.end(),
+  DCHECK(std::find_if(plane_list.begin(), plane_list.end(),
                       [](HardwareDisplayPlane* plane) {
                         return plane->type() == HardwareDisplayPlane::kOverlay;
-                      }) == plane_list->old_plane_list.end());
+                      }) == plane_list.end());
   return true;
 }
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
index e1c1592..5c6c0a4 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
@@ -19,10 +19,11 @@
   ~HardwareDisplayPlaneManagerLegacy() override;
 
   // HardwareDisplayPlaneManager:
-  bool Commit(HardwareDisplayPlaneList* plane_list,
+  bool Commit(const HardwareDisplayPlaneList& plane_list,
               scoped_refptr<PageFlipRequest> page_flip_request,
               std::unique_ptr<gfx::GpuFence>* out_fence) override;
-  bool DisableOverlayPlanes(HardwareDisplayPlaneList* plane_list) override;
+  bool DisableOverlayPlanes(
+      const std::vector<HardwareDisplayPlane*>& plane_list) override;
 
   bool SetColorCorrectionOnAllCrtcPlanes(
       uint32_t crtc_id,
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
index 1652541a..6d4da74 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
@@ -61,7 +61,7 @@
   uint64_t GetPlanePropertyValue(uint32_t plane,
                                  const std::string& property_name);
 
-  void PerformPageFlip(size_t crtc_idx, ui::HardwareDisplayPlaneList* state);
+  void PerformPageFlip(size_t crtc_idx);
 
   void SetUp() override;
 
@@ -169,16 +169,17 @@
   property_names_.insert({kBackgroundColorPropId, "BACKGROUND_COLOR"});
 }
 
-void HardwareDisplayPlaneManagerTest::PerformPageFlip(
-    size_t crtc_idx,
-    ui::HardwareDisplayPlaneList* state) {
+void HardwareDisplayPlaneManagerTest::PerformPageFlip(size_t crtc_idx) {
+  ui::HardwareDisplayPlaneList state;
+  state.old_plane_list = fake_drm_->plane_manager()->GetOwnedPlanesForCrtcs(
+      {crtc_properties_[crtc_idx].id});
   ui::DrmOverlayPlaneList assigns;
   scoped_refptr<ui::DrmFramebuffer> xrgb_buffer =
       CreateBuffer(kDefaultBufferSize);
   assigns.push_back(ui::DrmOverlayPlane(xrgb_buffer, nullptr));
   fake_drm_->plane_manager()->BeginFrame(state);
   ASSERT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes(
-      state, assigns, crtc_properties_[crtc_idx].id));
+      &state, assigns, crtc_properties_[crtc_idx].id));
   scoped_refptr<ui::PageFlipRequest> page_flip_request =
       base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta());
   ASSERT_TRUE(
@@ -296,7 +297,6 @@
       &state_, assigns, crtc_properties_[0].id));
   EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes(
       &state_, assigns, crtc_properties_[1].id));
-  EXPECT_EQ(0u, state_.plane_list.size());
 }
 
 TEST_P(HardwareDisplayPlaneManagerLegacyTest, CheckFramebufferFormatMatch) {
@@ -309,7 +309,7 @@
   fake_drm_->InitializeState(crtc_properties_, plane_properties_,
                              property_names_, use_atomic_);
 
-  fake_drm_->plane_manager()->BeginFrame(&state_);
+  fake_drm_->plane_manager()->BeginFrame(state_);
   // This should return false as plane manager creates planes which support
   // DRM_FORMAT_XRGB8888 while buffer returns kDummyFormat as its pixelFormat.
   EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes(
@@ -318,10 +318,10 @@
   scoped_refptr<ui::DrmFramebuffer> xrgb_buffer =
       CreateBuffer(kDefaultBufferSize);
   assigns.push_back(ui::DrmOverlayPlane(xrgb_buffer, nullptr));
-  fake_drm_->plane_manager()->BeginFrame(&state_);
+  fake_drm_->plane_manager()->BeginFrame(state_);
   EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes(
       &state_, assigns, crtc_properties_[0].id));
-  fake_drm_->plane_manager()->BeginFrame(&state_);
+  fake_drm_->plane_manager()->BeginFrame(state_);
   EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes(
       &state_, assigns, crtc_properties_[0].id));
 }
@@ -399,21 +399,24 @@
 
   scoped_refptr<ui::PageFlipRequest> page_flip_request =
       base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta());
-  fake_drm_->plane_manager()->BeginFrame(&hdpl);
+  fake_drm_->plane_manager()->BeginFrame(hdpl);
   EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes(
       &hdpl, assigns, crtc_properties_[0].id));
   EXPECT_TRUE(
-      fake_drm_->plane_manager()->Commit(&hdpl, page_flip_request, nullptr));
+      fake_drm_->plane_manager()->Commit(hdpl, page_flip_request, nullptr));
   assigns.clear();
   assigns.push_back(ui::DrmOverlayPlane(primary_buffer, nullptr));
-  fake_drm_->plane_manager()->BeginFrame(&hdpl);
+
+  ui::HardwareDisplayPlaneList hdpl2;
+  hdpl2.old_plane_list = hdpl.plane_list;
+  fake_drm_->plane_manager()->BeginFrame(hdpl2);
   EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes(
-      &hdpl, assigns, crtc_properties_[0].id));
+      &hdpl2, assigns, crtc_properties_[0].id));
   EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset, "FB_ID"));
   EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset + 1, "FB_ID"));
 
   EXPECT_TRUE(
-      fake_drm_->plane_manager()->Commit(&hdpl, page_flip_request, nullptr));
+      fake_drm_->plane_manager()->Commit(hdpl2, page_flip_request, nullptr));
   EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset, "FB_ID"));
   EXPECT_EQ(0u, GetPlanePropertyValue(kPlaneOffset + 1, "FB_ID"));
 }
@@ -431,7 +434,7 @@
   EXPECT_EQ(1u, state_.plane_list.size());
   // Pretend we committed the frame.
   state_.plane_list.swap(state_.old_plane_list);
-  fake_drm_->plane_manager()->BeginFrame(&state_);
+  fake_drm_->plane_manager()->BeginFrame(state_);
   ui::HardwareDisplayPlane* old_plane = state_.old_plane_list[0];
   // The same plane should be used.
   EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes(
@@ -510,8 +513,7 @@
   EXPECT_TRUE(fake_drm_->plane_manager()->SetColorMatrix(
       crtc_properties_[0].id, std::vector<float>(9)));
   if (use_atomic_) {
-    ui::HardwareDisplayPlaneList state;
-    PerformPageFlip(/*crtc_idx=*/0, &state);
+    PerformPageFlip(/*crtc_idx=*/0);
 #if defined(COMMIT_PROPERTIES_ON_PAGE_FLIP)
     EXPECT_EQ(1, fake_drm_->get_commit_count());
 #else
@@ -533,8 +535,7 @@
   EXPECT_FALSE(
       fake_drm_->plane_manager()->SetColorMatrix(crtc_properties_[0].id, {}));
   if (use_atomic_) {
-    ui::HardwareDisplayPlaneList state;
-    PerformPageFlip(/*crtc_idx=*/0, &state);
+    PerformPageFlip(/*crtc_idx=*/0);
     EXPECT_EQ(1, fake_drm_->get_commit_count());
     EXPECT_EQ(0u, GetCrtcPropertyValue(crtc_properties_[0].id, "CTM"));
   } else {
@@ -552,8 +553,7 @@
   EXPECT_FALSE(fake_drm_->plane_manager()->SetGammaCorrection(
       crtc_properties_[0].id, {{0, 0, 0}}, {}));
   if (use_atomic_) {
-    ui::HardwareDisplayPlaneList state;
-    PerformPageFlip(/*crtc_idx=*/0, &state);
+    PerformPageFlip(/*crtc_idx=*/0);
     // Page flip should succeed even if the properties failed to be updated.
     EXPECT_EQ(1, fake_drm_->get_commit_count());
   } else {
@@ -568,8 +568,7 @@
   EXPECT_FALSE(fake_drm_->plane_manager()->SetGammaCorrection(
       crtc_properties_[0].id, {{0, 0, 0}}, {}));
   if (use_atomic_) {
-    ui::HardwareDisplayPlaneList state;
-    PerformPageFlip(/*crtc_idx=*/0, &state);
+    PerformPageFlip(/*crtc_idx=*/0);
     // Page flip should succeed even if the properties failed to be updated.
     EXPECT_EQ(2, fake_drm_->get_commit_count());
   } else {
@@ -587,8 +586,7 @@
   EXPECT_FALSE(fake_drm_->plane_manager()->SetGammaCorrection(
       crtc_properties_[0].id, {}, {{0, 0, 0}}));
   if (use_atomic_) {
-    ui::HardwareDisplayPlaneList state;
-    PerformPageFlip(/*crtc_idx=*/0, &state);
+    PerformPageFlip(/*crtc_idx=*/0);
     // Page flip should succeed even if the properties failed to be updated.
     EXPECT_EQ(1, fake_drm_->get_commit_count());
   } else {
@@ -603,8 +601,7 @@
   EXPECT_FALSE(fake_drm_->plane_manager()->SetGammaCorrection(
       crtc_properties_[0].id, {}, {{0, 0, 0}}));
   if (use_atomic_) {
-    ui::HardwareDisplayPlaneList state;
-    PerformPageFlip(/*crtc_idx=*/0, &state);
+    PerformPageFlip(/*crtc_idx=*/0);
     // Page flip should succeed even if the properties failed to be updated.
     EXPECT_EQ(2, fake_drm_->get_commit_count());
   } else {
@@ -654,12 +651,11 @@
   fake_drm_->InitializeState(crtc_properties_, plane_properties_,
                              property_names_, use_atomic_);
 
-  ui::HardwareDisplayPlaneList state;
   // Check that we reset the properties correctly.
   EXPECT_TRUE(fake_drm_->plane_manager()->SetGammaCorrection(
       crtc_properties_[0].id, {}, {}));
   if (use_atomic_) {
-    PerformPageFlip(/*crtc_idx=*/0, &state);
+    PerformPageFlip(/*crtc_idx=*/0);
 #if defined(COMMIT_PROPERTIES_ON_PAGE_FLIP)
     EXPECT_EQ(1, fake_drm_->get_commit_count());
 #else
@@ -674,7 +670,7 @@
   EXPECT_TRUE(fake_drm_->plane_manager()->SetGammaCorrection(
       crtc_properties_[0].id, {{0, 0, 0}}, {{0, 0, 0}}));
   if (use_atomic_) {
-    PerformPageFlip(/*crtc_idx=*/0, &state);
+    PerformPageFlip(/*crtc_idx=*/0);
 #if defined(COMMIT_PROPERTIES_ON_PAGE_FLIP)
     EXPECT_EQ(2, fake_drm_->get_commit_count());
 #else
@@ -695,8 +691,7 @@
                              property_names_, use_atomic_);
   fake_drm_->plane_manager()->SetBackgroundColor(crtc_properties_[0].id, 0);
   if (use_atomic_) {
-    ui::HardwareDisplayPlaneList state;
-    PerformPageFlip(/*crtc_idx=*/0, &state);
+    PerformPageFlip(/*crtc_idx=*/0);
     EXPECT_EQ(1, fake_drm_->get_commit_count());
     EXPECT_EQ(0u, GetCrtcPropertyValue(crtc_properties_[0].id,
                                        "BACKGROUND_COLOR"));
@@ -710,8 +705,7 @@
                              property_names_, use_atomic_);
   fake_drm_->plane_manager()->SetBackgroundColor(crtc_properties_[0].id, 1);
   if (use_atomic_) {
-    ui::HardwareDisplayPlaneList state;
-    PerformPageFlip(/*crtc_idx=*/0, &state);
+    PerformPageFlip(/*crtc_idx=*/0);
     EXPECT_EQ(2, fake_drm_->get_commit_count());
     EXPECT_EQ(1u, GetCrtcPropertyValue(crtc_properties_[0].id,
                                        "BACKGROUND_COLOR"));
@@ -734,7 +728,7 @@
   ui::DrmOverlayPlaneList assigns2;
   assigns2.push_back(ui::DrmOverlayPlane(fake_buffer2, nullptr));
 
-  fake_drm_->plane_manager()->BeginFrame(&state_);
+  fake_drm_->plane_manager()->BeginFrame(state_);
   EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes(
       &state_, assigns1, crtc_properties_[0].id));
   EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes(
@@ -744,7 +738,7 @@
       base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta());
 
   std::unique_ptr<gfx::GpuFence> out_fence;
-  EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&state_, page_flip_request,
+  EXPECT_TRUE(fake_drm_->plane_manager()->Commit(state_, page_flip_request,
                                                  &out_fence));
   EXPECT_EQ(nullptr, out_fence);
 }
diff --git a/ui/views/controls/button/checkbox.cc b/ui/views/controls/button/checkbox.cc
index 31898b3..b19356e 100644
--- a/ui/views/controls/button/checkbox.cc
+++ b/ui/views/controls/button/checkbox.cc
@@ -46,6 +46,7 @@
 
 Checkbox::Checkbox(const base::string16& label, ButtonListener* listener)
     : LabelButton(listener, label), checked_(false), label_ax_id_(0) {
+  SetImageCentered(false);
   SetHorizontalAlignment(gfx::ALIGN_LEFT);
   SetFocusForPlatform();
 
diff --git a/ui/views/controls/button/label_button.cc b/ui/views/controls/button/label_button.cc
index ef555fa..12c05efa 100644
--- a/ui/views/controls/button/label_button.cc
+++ b/ui/views/controls/button/label_button.cc
@@ -176,6 +176,17 @@
   OnPropertyChanged(&image_label_spacing_, kPropertyEffectsLayout);
 }
 
+bool LabelButton::GetImageCentered() const {
+  return image_centered_;
+}
+
+void LabelButton::SetImageCentered(bool image_centered) {
+  if (GetImageCentered() == image_centered)
+    return;
+  image_centered_ = image_centered;
+  OnPropertyChanged(&image_centered_, kPropertyEffectsLayout);
+}
+
 std::unique_ptr<LabelButtonBorder> LabelButton::CreateDefaultBorder() const {
   auto border = std::make_unique<LabelButtonBorder>();
   border->set_insets(views::LabelButtonAssetBorder::GetDefaultInsets());
@@ -292,9 +303,7 @@
       label_area.height());
 
   gfx::Point image_origin = child_area.origin();
-  if (label_->GetMultiLine()) {
-    // Right now this code currently only works for CheckBox and RadioButton
-    // descendants that have multi-line enabled for their label.
+  if (label_->GetMultiLine() && !image_centered_) {
     image_origin.Offset(
         0, std::max(
                0, (label_->font_list().GetHeight() - image_size.height()) / 2));
@@ -559,6 +568,7 @@
 ADD_PROPERTY_METADATA(LabelButton, gfx::Size, MaxSize)
 ADD_PROPERTY_METADATA(LabelButton, bool, IsDefault)
 ADD_PROPERTY_METADATA(LabelButton, int, ImageLabelSpacing)
+ADD_PROPERTY_METADATA(LabelButton, bool, ImageCentered)
 END_METADATA()
 
 }  // namespace views
diff --git a/ui/views/controls/button/label_button.h b/ui/views/controls/button/label_button.h
index 1c8d5c2..ba9f5f8 100644
--- a/ui/views/controls/button/label_button.h
+++ b/ui/views/controls/button/label_button.h
@@ -91,6 +91,11 @@
   int GetImageLabelSpacing() const;
   void SetImageLabelSpacing(int spacing);
 
+  // Gets or sets the option to place the image aligned with the center of the
+  // the label. The image is not centered for CheckBox and RadioButton only.
+  bool GetImageCentered() const;
+  void SetImageCentered(bool image_centered);
+
   // Creates the default border for this button. This can be overridden by
   // subclasses.
   virtual std::unique_ptr<LabelButtonBorder> CreateDefaultBorder() const;
@@ -224,6 +229,11 @@
   // True if current border was set by UpdateThemedBorder.
   bool border_is_themed_border_ = true;
 
+  // A flag indicating that this button's image should be aligned with the
+  // center of the label when multiline is enabled. This shouldn't be the case
+  // for a CheckBox or a RadioButton.
+  bool image_centered_ = true;
+
   // Spacing between the image and the text.
   int image_label_spacing_ = LayoutProvider::Get()->GetDistanceMetric(
       DISTANCE_RELATED_LABEL_HORIZONTAL);
diff --git a/ui/views/controls/button/label_button_unittest.cc b/ui/views/controls/button/label_button_unittest.cc
index e6e5313..ef91ea9 100644
--- a/ui/views/controls/button/label_button_unittest.cc
+++ b/ui/views/controls/button/label_button_unittest.cc
@@ -345,6 +345,31 @@
   EXPECT_EQ(button_->GetPreferredSize(), gfx::Size(large_size, large_size));
 }
 
+TEST_F(LabelButtonTest, ImageAlignmentWithMultilineLabel) {
+  const base::string16 text(
+      ASCIIToUTF16("Some long text that would result in multiline label"));
+  button_->SetText(text);
+
+  const int max_label_width = 40;
+  button_->label()->SetMultiLine(true);
+  button_->label()->SetMaximumWidth(max_label_width);
+
+  const int image_size = 16;
+  const gfx::ImageSkia image = CreateTestImage(image_size, image_size);
+  button_->SetImage(Button::STATE_NORMAL, image);
+
+  button_->SetBoundsRect(gfx::Rect(button_->GetPreferredSize()));
+  button_->Layout();
+  int y_origin_centered = button_->image()->origin().y();
+
+  button_->SetBoundsRect(gfx::Rect(button_->GetPreferredSize()));
+  button_->SetImageCentered(false);
+  button_->Layout();
+  int y_origin_not_centered = button_->image()->origin().y();
+
+  EXPECT_LT(y_origin_not_centered, y_origin_centered);
+}
+
 TEST_F(LabelButtonTest, LabelAndImage) {
   const gfx::FontList font_list = button_->label()->font_list();
   const base::string16 text(ASCIIToUTF16("abcdefghijklm"));
@@ -422,6 +447,7 @@
   ASSERT_EQ(font_list.GetHeight(), image.width());
 
   button_->SetImage(Button::STATE_NORMAL, image);
+  button_->SetImageCentered(false);
   button_->SetMaxSize(
       gfx::Size(image.width() + image_spacing + text_wrap_width, 0));
 
@@ -436,7 +462,7 @@
   EXPECT_EQ(preferred_size.height(),
             font_list.GetHeight() * 2 + button_insets.height());
 
-  // The image should be centered on the first line of the multi-line label.
+  // The image should be centered on the first line of the multi-line label
   EXPECT_EQ(button_->image()->y(),
             (font_list.GetHeight() - button_->image()->height()) / 2 +
                 button_insets.top());
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc
index e06eb597..be8d8e8 100644
--- a/ui/views/controls/label.cc
+++ b/ui/views/controls/label.cc
@@ -345,7 +345,7 @@
   if (max_width_ == max_width)
     return;
   max_width_ = max_width;
-  OnPropertyChanged(&max_width_, kPropertyEffectsPreferredSizeChanged);
+  OnPropertyChanged(&max_width_, kPropertyEffectsLayout);
 }
 
 bool Label::GetCollapseWhenHidden() const {