diff --git a/AUTHORS b/AUTHORS
index e8e2daf..2210c66f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -41,6 +41,7 @@
 Alex Scheele <alexscheele@gmail.com>
 Alexander Douglas <agdoug@amazon.com>
 Alexander Guettler <alexander@guettler.io>
+Alexander Rezepkin <etu@vivaldi.net>
 Alexander Shalamov <alexander.shalamov@intel.com>
 Alexander Sulfrian <alexander@sulfrian.net>
 Alexandre Abreu <wiss1976@gmail.com>
diff --git a/DEPS b/DEPS
index 8968cef..6bd42a5 100644
--- a/DEPS
+++ b/DEPS
@@ -167,11 +167,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '7274850f96f267f272d7336bdac5355de03f6a58',
+  'skia_revision': '0df7697235b4a02cd6dd6fa2a783345add40cbad',
   # 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': 'be181e241c6da9baa49a424b7d91613c8ebf76f8',
+  'v8_revision': 'fedca19411717706006c21922ded5bf25b6a0f58',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -179,11 +179,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '14dd70757eb07a841301395f33e0428925648cd9',
+  'angle_revision': 'ae1b7786b89a5cde6bafe93f6a2ccce8f6d9621c',
   # 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': '9097eeb5359bbbe7e116bfa9233863b411a006d7',
+  'swiftshader_revision': '6652f0b6428777b5a4a3d191cc30d8b31366b999',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -222,7 +222,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
-  'harfbuzz_revision': 'c1a585fab0c17fe47b0728cf67920791c2173019',
+  'harfbuzz_revision': 'e637a4b3de2fb8bdbc1b82e822f4a6cc579e794b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Emoji Segmenter
   # and whatever else without interference from each other.
@@ -230,7 +230,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '44806300adbd52cb4193a76c7282a2f6cb7250d0',
+  'catapult_revision': '572eb8c70fdcc6e7a6ae0961841fea7661c51fa2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -238,7 +238,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-node-modules
   # and whatever else without interference from each other.
-  'devtools_node_modules_revision': '300c8063dda8f87348bca1643bc04c603a42453a',
+  'devtools_node_modules_revision': '207c67362bdb7e135dc5735fb46b9c508d4e4c5e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -286,7 +286,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '1b6fd37fa6f9c39be15d2e775948a6708afecb7c',
+  'spv_tools_revision': 'e99b9182214fe7797803d89fa4d5f830d9a57bbb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -302,11 +302,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '1093c4de2cfad715f0da1460aafbf88f47bd7bb1',
+  'dawn_revision': '919812ed1c25b9aba8fcf182de96b91181e4cfb0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': 'abb67699b6e21d4650aea2145ed86a8eeede5187',
+  'quiche_revision': '780eaa6043f26e953f32aff96281b94ffc6ec9ed',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -862,7 +862,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '692ad2ff20beca13272af0b95e939f4b9aabb3b4',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'df1de01ceff385b5cb29bca977de70bc21c89765',
       'condition': 'checkout_linux',
   },
 
@@ -877,7 +877,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '61297bd64bfd78f9d4288ab44e28976b53f369f2',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '1fd712f839e27abae28a412c1b2c07148194a142',
       'condition': 'checkout_linux',
   },
 
@@ -887,7 +887,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ba97f6065ed1e9336585468dd85e680cf09d5166',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '989bc351863dd6cbb7e645a027a3c5e04104e44f',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -956,7 +956,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'ee8e9c15222c2aff93dfea451b1e8a892497ec2d',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'a959deb00750826fb087171d663947df550a3339',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1280,7 +1280,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '0e17af90d4c9b2e6514eaf9c44dcf77a74be8a27',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '3787dee781561934b95519d7b5d16b05166dd882',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1366,7 +1366,7 @@
   },
 
   'src/third_party/re2/src':
-    Var('chromium_git') + '/external/github.com/google/re2.git' + '@' + '67bce690decdb507b13e14050661f8b9ebfcfe6c',
+    Var('chromium_git') + '/external/github.com/google/re2.git' + '@' + 'ab12219ba56a47200385673446b5d371548c25db',
 
   'src/third_party/r8': {
       'packages': [
@@ -1470,7 +1470,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '2701c130839edbeb226735b0775966b6423d9e83',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '2f6e525099a0d5186e6b5b130aa26b78158d5aa7',
+    Var('webrtc_git') + '/src.git' + '@' + 'a6d7b028242c38447d8dd13fe232682aceb10932',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1532,7 +1532,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5f410c266cfad9abcc1a13a6c746ee00ce1556ef',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@42ac78d2d012b030f7994c889f377269f9d51051',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_feature_list_creator.cc b/android_webview/browser/aw_feature_list_creator.cc
index 69816fd7..19b75a6a 100644
--- a/android_webview/browser/aw_feature_list_creator.cc
+++ b/android_webview/browser/aw_feature_list_creator.cc
@@ -57,6 +57,10 @@
     // Current and past country codes, to filter variations studies by country.
     variations::prefs::kVariationsCountry,
     variations::prefs::kVariationsPermanentConsistencyCountry,
+    // Last variations seed fetch date/time, used for histograms and to
+    // determine if the seed is expired.
+    variations::prefs::kVariationsLastFetchTime,
+    variations::prefs::kVariationsSeedDate,
 };
 
 // Shows notifications which correspond to PersistentPrefStore's reading errors.
@@ -119,11 +123,23 @@
   field_trial_list_ = std::make_unique<base::FieldTrialList>(
       metrics_client->CreateLowEntropyProvider());
 
+  std::unique_ptr<variations::SeedResponse> seed = GetAndClearJavaSeed();
+  base::Time null_time;
+  base::Time seed_date =
+      seed ? base::Time::FromJavaTime(seed->date) : null_time;
   variations::UIStringOverrider ui_string_overrider;
   client_ = std::make_unique<AwVariationsServiceClient>();
   auto seed_store = std::make_unique<variations::VariationsSeedStore>(
-      local_state_.get(), /*initial_seed=*/GetAndClearJavaSeed(),
+      local_state_.get(), /*initial_seed=*/std::move(seed),
       /*on_initial_seed_stored=*/base::DoNothing());
+
+  // We set the seed fetch time to when the service downloaded the seed rather
+  // than base::Time::Now() because we want to compute seed freshness based on
+  // the initial download time, which happened in the service at some earlier
+  // point.
+  if (!seed_date.is_null())
+    seed_store->RecordLastFetchTime(seed_date);
+
   variations_field_trial_creator_ =
       std::make_unique<variations::VariationsFieldTrialCreator>(
           local_state_.get(), client_.get(), std::move(seed_store),
diff --git a/android_webview/browser/aw_settings.cc b/android_webview/browser/aw_settings.cc
index dfce248..7e612fb3 100644
--- a/android_webview/browser/aw_settings.cc
+++ b/android_webview/browser/aw_settings.cc
@@ -252,9 +252,8 @@
     update_prefs = true;
   }
 
-  content::RenderViewHost* host = web_contents()->GetRenderViewHost();
-  if (update_prefs && host)
-    host->SyncRendererPrefs();
+  if (update_prefs)
+    web_contents()->SyncRendererPrefs();
 
   if (update_prefs) {
     // make sure to update accept languages when the network service is enabled
diff --git a/android_webview/browser/cookie_manager.cc b/android_webview/browser/cookie_manager.cc
index 5cb14e7..e5757ef3 100644
--- a/android_webview/browser/cookie_manager.cc
+++ b/android_webview/browser/cookie_manager.cc
@@ -25,6 +25,7 @@
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
 #include "base/path_service.h"
 #include "base/single_thread_task_runner.h"
@@ -36,6 +37,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/cookie_store_factory.h"
 #include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/cookies/cookie_options.h"
 #include "net/cookies/cookie_store.h"
@@ -88,8 +90,19 @@
   kMaxValue = kFixedUp,
 };
 
-GURL MaybeFixUpSchemeForSecureCookie(const GURL& host,
-                                     const std::string& value) {
+// Since this function parses the set-cookie line into a ParsedCookie, it is
+// convenient to hook into here to get the SameSite value from the parsed
+// cookie for histogramming.
+GURL MaybeFixUpSchemeForSecureCookieAndGetSameSite(
+    const GURL& host,
+    const std::string& value,
+    net::CookieSameSiteString* samesite_out) {
+  net::ParsedCookie parsed_cookie(value);
+
+  // Grab the SameSite value for histogramming.
+  DCHECK(samesite_out);
+  parsed_cookie.SameSite(samesite_out);
+
   // Log message for catching strict secure cookies related bugs.
   // TODO(ntfschr): try to remove this, based on UMA stats
   // (https://crbug.com/933981)
@@ -103,7 +116,6 @@
                                   SecureCookieAction::kAlreadySecureScheme);
     return host;
   }
-  net::ParsedCookie parsed_cookie(value);
   if (!parsed_cookie.IsValid()) {
     base::UmaHistogramEnumeration(kSecureCookieHistogramName,
                                   SecureCookieAction::kInvalidCookie);
@@ -405,7 +417,12 @@
 void CookieManager::SetCookieHelper(const GURL& host,
                                     const std::string& value,
                                     base::OnceCallback<void(bool)> callback) {
-  const GURL& new_host = MaybeFixUpSchemeForSecureCookie(host, value);
+  net::CookieSameSiteString samesite = net::CookieSameSiteString::kUnspecified;
+  const GURL& new_host =
+      MaybeFixUpSchemeForSecureCookieAndGetSameSite(host, value, &samesite);
+
+  UMA_HISTOGRAM_ENUMERATION(
+      "Android.WebView.CookieManager.SameSiteAttributeValue", samesite);
 
   net::CanonicalCookie::CookieInclusionStatus status;
   std::unique_ptr<net::CanonicalCookie> cc(
@@ -417,6 +434,11 @@
     return;
   }
 
+  if (cc->SameSite() == net::CookieSameSite::NO_RESTRICTION) {
+    UMA_HISTOGRAM_BOOLEAN("Android.WebView.CookieManager.SameSiteNoneIsSecure",
+                          cc->IsSecure());
+  }
+
   // Note: CookieStore and network::CookieManager have different signatures: one
   // accepts a boolean callback while the other (recently) changed to accept a
   // CookieInclusionStatus callback. WebView only cares about boolean success,
diff --git a/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc b/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc
index e9a8cec..4a24dea8 100644
--- a/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc
+++ b/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc
@@ -116,8 +116,7 @@
 
   frame_sink_manager_->BindAndSetClient(
       std::move(params->frame_sink_manager), viz_task_runner_,
-      viz::mojom::FrameSinkManagerClientPtr(
-          std::move(params->frame_sink_manager_client)));
+      std::move(params->frame_sink_manager_client));
 }
 
 #if BUILDFLAG(USE_VIZ_DEVTOOLS)
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
index e572887f..91d9962 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
@@ -958,6 +958,7 @@
     public void insertVisualStateCallback(
             final long requestId, final VisualStateCallback callback) {
         sWebViewApiCallSample.record(ApiCall.INSERT_VISUAL_STATE_CALLBACK);
+        if (callback == null) return;
         mSharedWebViewChromium.insertVisualStateCallback(
                 requestId, new AwContents.VisualStateCallback() {
                     @Override
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 41b2047d..1715ef9e 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -152,9 +152,6 @@
     private static final String SAMSUNG_WORKAROUND_BASE_URL = "email://";
     private static final int SAMSUNG_WORKAROUND_DELAY = 200;
 
-    public static final String DATA_URI_HISTOGRAM_NAME =
-            "Android.WebView.LoadUrl.DataUriHasOctothorpe";
-
     @VisibleForTesting
     public static final String DATA_BASE_URL_SCHEME_HISTOGRAM_NAME =
             "Android.WebView.LoadDataWithBaseUrl.BaseUrl";
@@ -1705,11 +1702,6 @@
             params.setExtraHeaders(new HashMap<String, String>(additionalHttpHeaders));
         }
 
-        final String dataScheme = "data:";
-        if (url.startsWith(dataScheme) && url.contains("#")) {
-            RecordHistogram.recordBooleanHistogram(DATA_URI_HISTOGRAM_NAME, true);
-        }
-
         loadUrl(params);
     }
 
@@ -1781,7 +1773,6 @@
         if (TRACE) Log.i(TAG, "%s loadData", this);
         if (isDestroyed(WARN)) return;
         if (data != null && data.contains("#")) {
-            RecordHistogram.recordBooleanHistogram(DATA_URI_HISTOGRAM_NAME, true);
             if (!BuildInfo.targetsAtLeastQ() && !isBase64Encoded(encoding)) {
                 // As of Chromium M72, data URI parsing strictly enforces encoding of '#'. To
                 // support WebView applications which were not expecting this change, we do it for
@@ -1873,10 +1864,6 @@
         recordBaseUrl(schemeForUrl(baseUrl));
 
         if (baseUrl.startsWith("data:")) {
-            // We record only for this branch, because the other branch assumes unencoded content.
-            if (data != null && data.contains("#")) {
-                RecordHistogram.recordBooleanHistogram(DATA_URI_HISTOGRAM_NAME, true);
-            }
             // For backwards compatibility with WebViewClassic, we use the value of |encoding|
             // as the charset, as long as it's not "base64".
             boolean isBase64 = isBase64Encoded(encoding);
diff --git a/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java b/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
index 805cda7..2e4d31c 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
@@ -26,7 +26,6 @@
 import org.chromium.components.version_info.VersionConstants;
 
 import java.io.IOException;
-import java.text.ParseException;
 import java.util.Date;
 import java.util.concurrent.TimeUnit;
 
@@ -142,7 +141,7 @@
                             newSeed, /*onFinished=*/() -> jobFinished(mParams));
                     shouldFinish = false; // jobFinished will be deferred until updateSeed is done.
                 }
-            } catch (IOException | ParseException e) {
+            } catch (IOException e) {
                 // downloadContent() logs and re-throws these exceptions, so there's no need to log
                 // here. IOException includes SocketTimeoutException and UnknownHostException,
                 // which may happen inside downloadContent().
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
index befc991..ea7c052c 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
@@ -945,117 +945,6 @@
     @Test
     @Feature({"AndroidWebView"})
     @SmallTest
-    public void testLoadDataRecordsOctothorpeHistogram() throws Throwable {
-        AwTestContainerView testView =
-                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
-        final AwContents awContents = testView.getAwContents();
-
-        // AwContents.DATA_URI_HISTOGRAM_NAME is a boolean histogram, but as it only records
-        // positive samples we can just use the total count directly.
-        Assert.assertEquals(0, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // Check a URL with no '#' character.
-        mActivityTestRule.runOnUiThread(
-                () -> { awContents.loadData("<html>test</html>", "text/html", null); });
-        Assert.assertEquals(0, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // Check a URL with a '#' character.
-        mActivityTestRule.runOnUiThread(
-                () -> { awContents.loadData("<html>test#foo</html>", "text/html", null); });
-        Assert.assertEquals(1, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // An encoded '#' should not cause the histogram to increment.
-        mActivityTestRule.runOnUiThread(
-                () -> { awContents.loadData("<html>test%23foo</html>", "text/html", null); });
-        Assert.assertEquals(1, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // Finally, check null values are handled correctly.
-        mActivityTestRule.runOnUiThread(() -> { awContents.loadData(null, "text/html", "utf-8"); });
-        Assert.assertEquals(1, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-    }
-
-    @Test
-    @Feature({"AndroidWebView"})
-    @SmallTest
-    public void testLoadDataWithBaseURLRecordsOctothorpeHistogram() throws Throwable {
-        AwTestContainerView testView =
-                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
-        final AwContents awContents = testView.getAwContents();
-
-        // AwContents.DATA_URI_HISTOGRAM_NAME is a boolean histogram, but as it only records
-        // positive samples we can just use the total count directly.
-        Assert.assertEquals(0, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // Check a URL with no '#' character.
-        mActivityTestRule.runOnUiThread(() -> {
-            awContents.loadDataWithBaseURL(
-                    "http://www.example.com", "<html>test</html>", "text/html", null, null);
-        });
-        Assert.assertEquals(0, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // '#' is legal if the baseUrl is not data scheme, because loadDataWithBaseURL accepts
-        // unencoded content.
-        mActivityTestRule.runOnUiThread(() -> {
-            awContents.loadDataWithBaseURL(
-                    "http://www.example.com", "<html>test#foo</html>", "text/html", null, null);
-        });
-        Assert.assertEquals(0, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // Check a URL with a '#' character, with data-scheme baseUrl.
-        mActivityTestRule.runOnUiThread(() -> {
-            awContents.loadDataWithBaseURL(
-                    "data:text/html", "<html>test#foo</html>", "text/html", null, null);
-        });
-        Assert.assertEquals(1, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // An encoded '#' should not cause the histogram to increment.
-        mActivityTestRule.runOnUiThread(() -> {
-            awContents.loadDataWithBaseURL(
-                    "http://www.example.com", "<html>test%23foo</html>", "text/html", null, null);
-        });
-        Assert.assertEquals(1, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // Finally, check null values are handled correctly.
-        mActivityTestRule.runOnUiThread(() -> {
-            awContents.loadDataWithBaseURL("http://www.example.com", null, "text/html", null, null);
-        });
-        Assert.assertEquals(1, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-    }
-
-    @Test
-    @Feature({"AndroidWebView"})
-    @SmallTest
-    public void testLoadUrlRecordsOctothorpeHistogram() throws Throwable {
-        AwTestContainerView testView =
-                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
-        final AwContents awContents = testView.getAwContents();
-
-        // AwContents.DATA_URI_HISTOGRAM_NAME is a boolean histogram, but as it only records
-        // positive samples we can just use the total count directly.
-        Assert.assertEquals(0, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // Check a URL with no '#' character.
-        mActivityTestRule.runOnUiThread(
-                () -> { awContents.loadUrl("data:text/html,<html>test</html>"); });
-        Assert.assertEquals(0, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // Check a URL with a '#' character.
-        mActivityTestRule.runOnUiThread(
-                () -> { awContents.loadUrl("data:text/html,<html>test#foo</html>"); });
-        Assert.assertEquals(1, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // An encoded '#' should not cause the histogram to increment.
-        mActivityTestRule.runOnUiThread(
-                () -> { awContents.loadUrl("data:text/html,<html>test%23foo</html>"); });
-        Assert.assertEquals(1, getHistogramSampleCount(AwContents.DATA_URI_HISTOGRAM_NAME));
-
-        // |loadUrl| doesn't allow a null url, so it is not necessary to check that for this API.
-        // See http://crbug.com/864708.
-    }
-
-    @Test
-    @Feature({"AndroidWebView"})
-    @SmallTest
     public void testLoadUrlRecordsScheme_http() {
         // No need to spin up a web server, since we don't care if the load ever succeeds.
         final String httpUrlWithNoRealPage = "http://some.origin/some/path.html";
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedHolderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedHolderTest.java
index 012f297..fe23c3c 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedHolderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedHolderTest.java
@@ -24,7 +24,6 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.concurrent.TimeUnit;
@@ -133,8 +132,7 @@
     // proceed. The written seed should match the mock seed.
     @Test
     @MediumTest
-    public void testUpdateAndWriteToStaleSeed()
-            throws IOException, TimeoutException, ParseException {
+    public void testUpdateAndWriteToStaleSeed() throws IOException, TimeoutException {
         try {
             SeedInfo mockSeed = VariationsTestUtils.createMockSeed();
             long mockDateMinusOneDay = mockSeed.date - TimeUnit.DAYS.toMillis(1);
@@ -158,8 +156,7 @@
     // a file. Pretend the file already contains an up-to-date seed, so no write should happen.
     @Test
     @MediumTest
-    public void testUpdateAndWriteToFreshSeed()
-            throws IOException, TimeoutException, ParseException {
+    public void testUpdateAndWriteToFreshSeed() throws IOException, TimeoutException {
         try {
             SeedInfo mockSeed = VariationsTestUtils.createMockSeed();
             TestHolder holder = new TestHolder();
diff --git a/android_webview/proto/aw_variations_seed.proto b/android_webview/proto/aw_variations_seed.proto
index 35d246fd..7c16fe2 100644
--- a/android_webview/proto/aw_variations_seed.proto
+++ b/android_webview/proto/aw_variations_seed.proto
@@ -28,7 +28,7 @@
   optional bool is_gzip_compressed = 4;
   // The download body, itself a serialized VariationsSeed proto.
   optional bytes seed_data = 5;
-  // Date the seed was downloaded according to the Variations server in
+  // Date the seed was downloaded according to the device's clock in
   // milliseconds since UNIX epoch, GMT.
   optional int64 date = 6;
 }
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 4a3fa475..f48d9609 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -3162,9 +3162,12 @@
   generator->MoveTouch(gfx::Point(x, closed_y));
   generator->PressTouch();
   generator->MoveTouch(gfx::Point(x, fullscreen_y));
+  generator->ReleaseTouch();
   GetAppListTestHelper()->CheckVisibility(true);
 
   // Drag to shelf to close app list.
+  generator->MoveTouch(gfx::Point(x, fullscreen_y));
+  generator->PressTouch();
   generator->MoveTouch(gfx::Point(x, closed_y));
   generator->ReleaseTouch();
   GetAppListTestHelper()->WaitUntilIdle();
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index dceb048..aa081391 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -1881,14 +1881,14 @@
   ash::AppListViewState app_list_state = ash::AppListViewState::kPeeking;
   if (event_in_screen.type() == ui::ET_SCROLL_FLING_START &&
       fabs(event_in_screen.AsGestureEvent()->details().velocity_y()) >
-          kDragVelocityThreshold) {
+          kDragVelocityFromShelfThreshold) {
     // If the scroll sequence terminates with a fling, show the fullscreen app
     // list if the fling was fast enough and in the correct direction, otherwise
-    // close it.
+    // go to peeking.
     app_list_state =
         event_in_screen.AsGestureEvent()->details().velocity_y() < 0
             ? ash::AppListViewState::kFullscreenAllApps
-            : ash::AppListViewState::kClosed;
+            : ash::AppListViewState::kPeeking;
   } else {
     // Snap the app list to corresponding state according to the snapping
     // thresholds.
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h
index 7daa7c7..0d0103a 100644
--- a/ash/app_list/views/app_list_view.h
+++ b/ash/app_list/views/app_list_view.h
@@ -108,9 +108,13 @@
 
   // The snapping thresholds for dragging app list from shelf in laptop mode,
   // measured in DIPs.
-  static constexpr int kDragSnapToClosedThreshold = 144;
+  static constexpr int kDragSnapToClosedThreshold = 120;
   static constexpr int kDragSnapToPeekingThreshold = 561;
 
+  // The velocity the app list must be dragged from the shelf in order to
+  // transition to the next state, measured in DIPs/event.
+  static constexpr int kDragVelocityFromShelfThreshold = 120;
+
   // The velocity the app list must be dragged in order to transition to the
   // next state, measured in DIPs/event.
   static constexpr int kDragVelocityThreshold = 6;
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index a1eb5f5..d1ee41b 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -322,12 +322,12 @@
     EXPECT_EQ(gfx::Rect(expected_horizontal_margin, kExpectedGridTop,
                         kExpectedGridWidth, kExpectedGridHeight),
               apps_grid_view()->bounds());
-    EXPECT_EQ(
-        gfx::Rect(kExpectedGridWidth + expected_horizontal_margin +
-                      kPageSwitcherSpacing,
-                  kExpectedGridTop, PageSwitcher::kPreferredButtonStripWidth,
-                  kExpectedGridHeight),
-        page_switcher_view()->bounds());
+    EXPECT_EQ(gfx::Rect(kExpectedGridWidth + expected_horizontal_margin +
+                            kPageSwitcherSpacing,
+                        kExpectedGridTop,
+                        2 * PageSwitcher::kMaxButtonRadiusForRootGrid,
+                        kExpectedGridHeight),
+              page_switcher_view()->bounds());
 
     // Horizontal offset between app list item views.
     const int kHorizontalOffset = GridItemSizeWithMargins(
diff --git a/ash/app_list/views/apps_container_view.cc b/ash/app_list/views/apps_container_view.cc
index fb98588..4fa586a 100644
--- a/ash/app_list/views/apps_container_view.cc
+++ b/ash/app_list/views/apps_container_view.cc
@@ -79,7 +79,7 @@
   // If ScalableAppList feature is enabled, there is no extra horizontal margin
   // between grid view and the page switcher.
   return kAppsGridPageSwitcherSpacing +
-         PageSwitcher::kPreferredButtonStripWidth +
+         PageSwitcher::kMaxButtonRadiusForRootGrid * 2 +
          (app_list_features::IsScalableAppListEnabled()
               ? 0
               : kAppsGridMinimumMargin);
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index ea3f2bf..ed7a4dc2 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -27,6 +27,7 @@
 #include "ash/public/cpp/app_list/app_list_switches.h"
 #include "base/logging.h"
 #include "base/numerics/ranges.h"
+#include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
@@ -56,6 +57,27 @@
            target_view_state == ash::AppListViewState::kFullscreenSearch));
 }
 
+// Notifies assistive technology that all schedules animations have completed on
+// this view and that a location change event has occurred. This should be used
+// for notifying a11y to update view locations after transformation animations.
+// This object will delete itself after running OnImplicitAnimationsCompleted.
+class AccessibilityAnimationObserver : public ui::ImplicitAnimationObserver {
+ public:
+  explicit AccessibilityAnimationObserver(views::View* view) : view_(view) {}
+  ~AccessibilityAnimationObserver() override = default;
+
+  // ui::ImplicitAnimationObserver:
+  void OnImplicitAnimationsCompleted() override {
+    view_->NotifyAccessibilityEvent(ax::mojom::Event::kLocationChanged, true);
+    delete this;
+  }
+
+ private:
+  views::View* const view_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccessibilityAnimationObserver);
+};
+
 }  // namespace
 
 ContentsView::ContentsView(AppListView* app_list_view)
@@ -832,7 +854,8 @@
   // |layer| - The layer to transform.
   // |y_offset| - The initial vertical offset - the layer's vertical offset will
   //              be animated to 0.
-  auto animate_transform = [duration](ui::Layer* layer, int y_offset) {
+  auto animate_transform = [duration](views::View* view, int y_offset) {
+    ui::Layer* layer = view->layer();
     gfx::Transform transform;
     transform.Translate(0, y_offset);
     layer->SetTransform(transform);
@@ -843,6 +866,8 @@
     settings->SetTransitionDuration(duration);
     settings->SetPreemptionStrategy(
         ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET);
+    // Observer will delete itself after animation completes.
+    settings->AddObserver(new AccessibilityAnimationObserver(view));
 
     layer->SetTransform(gfx::Transform());
   };
@@ -894,7 +919,7 @@
   // For search box, animate the search_box view layer instead of the widget
   // layer to avoid conflict with pagination model transitions (which update the
   // search box widget layer transform as the transition progresses).
-  animate_transform(search_box->layer(), y_offset);
+  animate_transform(search_box, y_offset);
 
   // Update app list page bounds to their target values. This assumes that
   // potential in-progress pagination transition does not directly animate page
@@ -915,12 +940,12 @@
   expand_arrow_view()->SchedulePaint();
 
   // Animate contents view to the target bounds.
-  animate_transform(layer(), y_offset);
+  animate_transform(this, y_offset);
 
   // The expand arrow view should stay in the same place relative to the
   // app list bounds, so apply the inverse translation to the one set for
   // the contents view layer.
-  animate_transform(expand_arrow_view_->layer(), -y_offset);
+  animate_transform(expand_arrow_view_, -y_offset);
 }
 
 void ContentsView::SetExpandArrowViewVisibility(bool show) {
diff --git a/ash/app_list/views/page_switcher.cc b/ash/app_list/views/page_switcher.cc
index 3b57618a..6c5d7252 100644
--- a/ash/app_list/views/page_switcher.cc
+++ b/ash/app_list/views/page_switcher.cc
@@ -35,7 +35,8 @@
 
 constexpr int kNormalButtonRadius = 4;
 constexpr int kSelectedButtonRadius = 5;
-constexpr int kInkDropRadius = 16;
+constexpr int kInkDropRadiusForRootGrid = 16;
+constexpr int kInkDropRadiusForFolderGrid = 10;
 constexpr SkScalar kStrokeWidth = SkIntToScalar(2);
 
 // Constants for the button strip that grows vertically.
@@ -54,7 +55,8 @@
 
 // Constants for the button strip that grows horizontally.
 // The padding on left/right side of each button.
-constexpr int kHorizontalButtonPadding = 6;
+constexpr int kHorizontalButtonPadding = 0;
+
 // The normal button color for the page switcher shown in folders (54% black).
 constexpr SkColor kLightNormalColor = SkColorSetA(SK_ColorBLACK, 138);
 constexpr SkColor kLightInkDropBaseColor = SkColorSetARGB(255, 95, 99, 104);
@@ -86,8 +88,10 @@
 
   // Overridden from views::View:
   gfx::Size CalculatePreferredSize() const override {
-    return gfx::Size(PageSwitcher::kMaxButtonRadius * 2,
-                     PageSwitcher::kMaxButtonRadius * 2);
+    const int max_radius = is_root_app_grid_page_switcher_
+                               ? PageSwitcher::kMaxButtonRadiusForRootGrid
+                               : PageSwitcher::kMaxButtonRadiusForFolderGrid;
+    return gfx::Size(max_radius * 2, max_radius * 2);
   }
 
   void PaintButtonContents(gfx::Canvas* canvas) override {
@@ -106,15 +110,18 @@
 
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override {
     return std::make_unique<views::CircleInkDropMask>(
-        size(), GetLocalBounds().CenterPoint(), kInkDropRadius);
+        size(), GetLocalBounds().CenterPoint(),
+        is_root_app_grid_page_switcher_ ? kInkDropRadiusForRootGrid
+                                        : kInkDropRadiusForFolderGrid);
   }
 
   std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override {
     gfx::Point center = GetLocalBounds().CenterPoint();
-    gfx::Rect bounds(center.x() - PageSwitcher::kMaxButtonRadius,
-                     center.y() - PageSwitcher::kMaxButtonRadius,
-                     2 * PageSwitcher::kMaxButtonRadius,
-                     2 * PageSwitcher::kMaxButtonRadius);
+    const int max_radius = is_root_app_grid_page_switcher_
+                               ? PageSwitcher::kMaxButtonRadiusForRootGrid
+                               : PageSwitcher::kMaxButtonRadiusForFolderGrid;
+    gfx::Rect bounds(center.x() - max_radius, center.y() - max_radius,
+                     2 * max_radius, 2 * max_radius);
     return std::make_unique<views::FloodFillInkDropRipple>(
         size(), GetLocalBounds().InsetsFrom(bounds),
         GetInkDropCenterBasedOnLastEvent(),
@@ -130,7 +137,8 @@
         std::make_unique<views::CircleLayerDelegate>(
             is_root_app_grid_page_switcher_ ? kDarkInkDropHighlightColor
                                             : kLightInkDropHighlightColor,
-            kInkDropRadius));
+            is_root_app_grid_page_switcher_ ? kInkDropRadiusForRootGrid
+                                            : kInkDropRadiusForFolderGrid));
   }
 
   void NotifyClick(const ui::Event& event) override {
@@ -228,14 +236,15 @@
 }
 
 gfx::Size PageSwitcher::CalculatePreferredSize() const {
+  const int max_radius = is_root_app_grid_page_switcher_
+                             ? PageSwitcher::kMaxButtonRadiusForRootGrid
+                             : PageSwitcher::kMaxButtonRadiusForFolderGrid;
   // Always return a size with correct width so that container resize is not
   // needed when more pages are added.
   if (is_root_app_grid_page_switcher_) {
-    return gfx::Size(kPreferredButtonStripWidth,
-                     buttons_->GetPreferredSize().height());
+    return gfx::Size(2 * max_radius, buttons_->GetPreferredSize().height());
   }
-  return gfx::Size(buttons_->GetPreferredSize().width(),
-                   kPreferredButtonStripWidth);
+  return gfx::Size(buttons_->GetPreferredSize().width(), 2 * max_radius);
 }
 
 void PageSwitcher::Layout() {
diff --git a/ash/app_list/views/page_switcher.h b/ash/app_list/views/page_switcher.h
index 1655d69..3e6ea90 100644
--- a/ash/app_list/views/page_switcher.h
+++ b/ash/app_list/views/page_switcher.h
@@ -19,8 +19,8 @@
                      public views::ButtonListener,
                      public ash::PaginationModelObserver {
  public:
-  static constexpr int kMaxButtonRadius = 16;
-  static constexpr int kPreferredButtonStripWidth = kMaxButtonRadius * 2;
+  static constexpr int kMaxButtonRadiusForRootGrid = 16;
+  static constexpr int kMaxButtonRadiusForFolderGrid = 10;
 
   PageSwitcher(ash::PaginationModel* model,
                bool is_root_app_grid_page_switcher,
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 635beb7..d324a87 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -442,6 +442,10 @@
                desc="The label used in the notification used to indicate that, the accessibility feature is being set as forced disabled.">
         deactivated
       </message>
+      <message name="IDS_ASH_ACCESSIBILITY_FEATURE_MANAGED"
+               desc="The label used to indicate that, the accessiblity feature is being managed by a policy.">
+        <ph name="FEATURE_NAME">$1<ex>Full-screen magnifier</ex></ph> This setting is enforced by your administrator.
+      </message>
 
       <message name="IDS_ASH_STATUS_TRAY_IME_SHORT" desc="The short label used for IME button in system tray bubble.">
         Keyboard
diff --git a/ash/home_screen/drag_window_from_shelf_controller.cc b/ash/home_screen/drag_window_from_shelf_controller.cc
index bbd89c8..081ff6b9 100644
--- a/ash/home_screen/drag_window_from_shelf_controller.cc
+++ b/ash/home_screen/drag_window_from_shelf_controller.cc
@@ -20,8 +20,11 @@
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/splitview/split_view_utils.h"
 #include "ash/wm/window_state.h"
+#include "ash/wm/window_transient_descendant_iterator.h"
 #include "base/numerics/ranges.h"
 #include "ui/base/hit_test.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/screen.h"
 #include "ui/wm/core/coordinate_conversion.h"
 
@@ -37,6 +40,88 @@
 constexpr base::TimeDelta kShowOverviewTimeWhenDragSuspend =
     base::TimeDelta::FromMilliseconds(40);
 
+// The time to do window transform to scale up to its original position or
+// scale down to homescreen animation.
+constexpr base::TimeDelta kWindowScaleUpOrDownTime =
+    base::TimeDelta::FromMilliseconds(350);
+
+// The delay to do window opacity fade out when scaling down the dragged window.
+constexpr base::TimeDelta kWindowFadeOutDelay =
+    base::TimeDelta::FromMilliseconds(100);
+
+// The window scale down factor if we head to home screen after drag ends.
+constexpr float kWindowScaleDownFactor = 0.001f;
+
+// The class the does the dragged window scale down animation to home screen
+// after drag ends. The window will be minimized after animation complete.
+class WindowTransformToHomeScreenAnimation
+    : public ui::ImplicitAnimationObserver,
+      public aura::WindowObserver {
+ public:
+  WindowTransformToHomeScreenAnimation(
+      aura::Window* window,
+      base::Optional<BackdropWindowMode> original_backdrop_mode)
+      : window_(window), original_backdrop_mode_(original_backdrop_mode) {
+    window_->AddObserver(this);
+
+    ui::ScopedLayerAnimationSettings settings(window_->layer()->GetAnimator());
+    settings.SetTransitionDuration(kWindowScaleUpOrDownTime);
+    settings.SetPreemptionStrategy(
+        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+    settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
+    settings.AddObserver(this);
+    window_->layer()->GetAnimator()->SchedulePauseForProperties(
+        kWindowFadeOutDelay, ui::LayerAnimationElement::OPACITY);
+    window_->layer()->SetTransform(GetWindowTransformToHomeScreen());
+    window_->layer()->SetOpacity(0.f);
+  }
+
+  ~WindowTransformToHomeScreenAnimation() override {
+    if (window_)
+      window_->RemoveObserver(this);
+  }
+
+  // ui::ImplicitAnimationObserver:
+  void OnImplicitAnimationsCompleted() override {
+    // Minimize the dragged window after transform animation is completed.
+    ScopedAnimationDisabler disable(window_);
+    window_->Hide();
+    WindowState::Get(window_)->Minimize();
+
+    // Reset its transform to identity transform and its original backdrop mode.
+    window_->layer()->SetTransform(gfx::Transform());
+    window_->layer()->SetOpacity(1.f);
+    if (original_backdrop_mode_.has_value())
+      window_->SetProperty(kBackdropWindowMode, *original_backdrop_mode_);
+
+    delete this;
+  }
+
+  // aura::WindowObserver:
+  void OnWindowDestroying(aura::Window* window) override {
+    window_ = nullptr;
+    delete this;
+  }
+
+ private:
+  // Returns the transform that should be applied to the dragged window if we
+  // should head to homescreen after dragging.
+  gfx::Transform GetWindowTransformToHomeScreen() {
+    const gfx::Rect work_area =
+        screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
+            window_);
+    gfx::Transform transform;
+    transform.Translate(work_area.width() / 2, work_area.height() / 2);
+    transform.Scale(kWindowScaleDownFactor, kWindowScaleDownFactor);
+    return transform;
+  }
+
+  aura::Window* window_;
+  base::Optional<BackdropWindowMode> original_backdrop_mode_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowTransformToHomeScreenAnimation);
+};
+
 }  // namespace
 
 DragWindowFromShelfController::DragWindowFromShelfController(
@@ -64,9 +149,9 @@
   UpdateDraggedWindow(location_in_screen);
 
   // Open overview if the window has been dragged far enough and the scroll
-  // delta has decreased to kShowOverviewThreshold or less.
+  // delta has decreased to kOpenOverviewThreshold or less.
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  if (std::abs(scroll_y) <= kShowOverviewThreshold &&
+  if (std::abs(scroll_y) <= kOpenOverviewThreshold &&
       !overview_controller->InOverviewSession()) {
     overview_controller->StartOverview(
         OverviewSession::EnterExitOverviewType::kImmediateEnter);
@@ -127,20 +212,20 @@
       overview_controller->EndOverview(
           OverviewSession::EnterExitOverviewType::kImmediateExit);
     }
-    // TODO(crbug.com/997885): Add animation.
-    WindowState::Get(window_)->Minimize();
+    ScaleDownWindowAfterDrag();
   } else if (ShouldRestoreToOriginalBounds(location_in_screen)) {
     // TODO(crbug.com/997885): Add animation.
     SetTransform(window_, gfx::Transform());
+    window_->SetProperty(kBackdropWindowMode, original_backdrop_mode_);
     if (!in_splitview && in_overview) {
       overview_controller->EndOverview(
           OverviewSession::EnterExitOverviewType::kImmediateExit);
     }
     ReshowHiddenWindowsOnDragEnd();
   } else if (!in_overview) {
-    // if overview is not active during the entire drag process, go to home
-    // screen.
-    WindowState::Get(window_)->Minimize();
+    // if overview is not active during the entire drag process, scale down the
+    // dragged window to go to home screen.
+    ScaleDownWindowAfterDrag();
   }
 
   OnDragEnded(location_in_screen,
@@ -157,6 +242,14 @@
   drag_started_ = false;
   // Reset the window's transform to identity transform.
   window_->SetTransform(gfx::Transform());
+  window_->SetProperty(kBackdropWindowMode, original_backdrop_mode_);
+
+  // End overview if it was opened during dragging.
+  OverviewController* overview_controller = Shell::Get()->overview_controller();
+  if (overview_controller->InOverviewSession()) {
+    overview_controller->EndOverview(
+        OverviewSession::EnterExitOverviewType::kImmediateExit);
+  }
   ReshowHiddenWindowsOnDragEnd();
 
   OnDragEnded(previous_location_in_screen_,
@@ -243,7 +336,6 @@
   }
 
   WindowState::Get(window_)->DeleteDragDetails();
-  window_->SetProperty(kBackdropWindowMode, original_backdrop_mode_);
   hidden_windows_.clear();
 }
 
@@ -406,4 +498,14 @@
       /*visible=*/true);
 }
 
+void DragWindowFromShelfController::ScaleDownWindowAfterDrag() {
+  // Do the scale-down transform for the entire transient tree.
+  for (auto* window : GetTransientTreeIterator(window_)) {
+    // self-destructed when window transform animation is done.
+    new WindowTransformToHomeScreenAnimation(
+        window, window == window_ ? base::make_optional(original_backdrop_mode_)
+                                  : base::nullopt);
+  }
+}
+
 }  // namespace ash
diff --git a/ash/home_screen/drag_window_from_shelf_controller.h b/ash/home_screen/drag_window_from_shelf_controller.h
index 70a245a..9e3a360 100644
--- a/ash/home_screen/drag_window_from_shelf_controller.h
+++ b/ash/home_screen/drag_window_from_shelf_controller.h
@@ -31,8 +31,12 @@
   // will snap back to its original position.
   static constexpr float kReturnToMaximizedThreshold = 116;
 
-  // The deceleration threshold to show overview during window dragging when
-  // dragging a window up from the shelf.
+  // The deceleration threshold to open overview behind the dragged window
+  // when swiping up from the shelf to drag the active window.
+  static constexpr float kOpenOverviewThreshold = 10.f;
+
+  // The deceleration threshold to show or hide overview during window dragging
+  // when dragging a window up from the shelf.
   static constexpr float kShowOverviewThreshold = 50.f;
 
   // The upward velocity threshold to take the user to the home launcher screen
@@ -101,6 +105,10 @@
   // up and split view indicators should be updated.
   void ShowOverviewDuringOrAfterDrag();
 
+  // Called when the dragged window should scale down and fade out to home
+  // screen after drag ends.
+  void ScaleDownWindowAfterDrag();
+
   aura::Window* const window_;
   gfx::Point initial_location_in_screen_;
   gfx::Point previous_location_in_screen_;
diff --git a/ash/home_screen/home_launcher_gesture_handler_unittest.cc b/ash/home_screen/home_launcher_gesture_handler_unittest.cc
index a0b2521..ffefd19 100644
--- a/ash/home_screen/home_launcher_gesture_handler_unittest.cc
+++ b/ash/home_screen/home_launcher_gesture_handler_unittest.cc
@@ -16,6 +16,8 @@
 #include "ash/wallpaper/wallpaper_widget_controller.h"
 #include "ash/wm/overview/overview_constants.h"
 #include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/overview/overview_grid.h"
+#include "ash/wm/overview/overview_item.h"
 #include "ash/wm/overview/overview_session.h"
 #include "ash/wm/overview/rounded_label_widget.h"
 #include "ash/wm/splitview/split_view_controller.h"
@@ -691,8 +693,8 @@
 
 // Test that in kDragWindowToHomeOrOverview mode, when swiping up from the
 // shelf, we only open overview when the y scroll delta (velocity) decrease to
-// kShowOverviewThreshold or less.
-TEST_F(HomeLauncherGestureHandlerTest, ShowOverviewWhenHold) {
+// kOpenOverviewThreshold or less.
+TEST_F(HomeLauncherGestureHandlerTest, OpenOverviewWhenHold) {
   UpdateDisplay("400x400");
   const gfx::Rect shelf_bounds =
       Shelf::ForWindow(Shell::GetPrimaryRootWindow())->GetIdealBounds();
@@ -702,12 +704,12 @@
                                     shelf_bounds.CenterPoint());
   GetGestureHandler()->OnScrollEvent(
       gfx::Point(200, 200), 0.f,
-      DragWindowFromShelfController::kShowOverviewThreshold + 1);
+      DragWindowFromShelfController::kOpenOverviewThreshold + 1);
   OverviewController* overview_controller = Shell::Get()->overview_controller();
   EXPECT_FALSE(overview_controller->InOverviewSession());
   GetGestureHandler()->OnScrollEvent(
       gfx::Point(200, 200), 0.f,
-      DragWindowFromShelfController::kShowOverviewThreshold);
+      DragWindowFromShelfController::kOpenOverviewThreshold);
   EXPECT_TRUE(overview_controller->InOverviewSession());
   GetGestureHandler()->OnReleaseEvent(gfx::Point(200, 200), base::nullopt);
 }
@@ -920,12 +922,31 @@
   GetGestureHandler()->OnPressEvent(Mode::kDragWindowToHomeOrOverview,
                                     shelf_bounds.CenterPoint());
   GetGestureHandler()->OnScrollEvent(gfx::Point(200, 200), 0.5f, 0.5f);
-  base::RunLoop().RunUntilIdle();
   OverviewController* overview_controller = Shell::Get()->overview_controller();
   EXPECT_TRUE(overview_controller->InOverviewSession());
+  // We test the visibility of overview by testing the drop target widget's
+  // visibility in the overview.
+  OverviewGrid* current_grid =
+      overview_controller->overview_session()->GetGridWithRootWindow(
+          window1->GetRootWindow());
+  OverviewItem* drop_target_item = current_grid->GetDropTarget();
+  EXPECT_TRUE(drop_target_item);
+  EXPECT_EQ(drop_target_item->GetWindow()->layer()->GetTargetOpacity(), 1.f);
 
-  GetGestureHandler()->OnReleaseEvent(shelf_bounds.CenterPoint(),
+  GetGestureHandler()->OnScrollEvent(
+      gfx::Point(200, 200), 0.5f,
+      DragWindowFromShelfController::kShowOverviewThreshold + 1);
+  // Test overview should be invisble.
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_EQ(drop_target_item->GetWindow()->layer()->GetTargetOpacity(), 0.f);
+
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(200, 200),
                                       /*velocity_y=*/base::nullopt);
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  // |window1| should have added to overview. Test its visibility.
+  EXPECT_TRUE(overview_controller->overview_session()->IsWindowInOverview(
+      window1.get()));
+  EXPECT_EQ(window1->layer()->GetTargetOpacity(), 1.f);
 }
 
 // Test that in kDragWindowToHomeOrOverview mode, we do not show drag-to-snap
@@ -955,6 +976,59 @@
                                       /*velocity_y=*/base::nullopt);
 }
 
+// Test there is no black backdrop behind the dragged window if we're doing the
+// scale down animation for the dragged window.
+TEST_F(HomeLauncherGestureHandlerTest, NoBackdropDuringWindowScaleDown) {
+  ui::ScopedAnimationDurationScaleMode test_duration_mode(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+
+  const gfx::Rect shelf_bounds =
+      Shelf::ForWindow(Shell::GetPrimaryRootWindow())->GetIdealBounds();
+  auto window = CreateWindowForTesting();
+  EXPECT_TRUE(window->layer()->GetTargetTransform().IsIdentity());
+  EXPECT_NE(window->GetProperty(kBackdropWindowMode),
+            BackdropWindowMode::kDisabled);
+
+  GetGestureHandler()->OnPressEvent(Mode::kDragWindowToHomeOrOverview,
+                                    shelf_bounds.CenterPoint());
+  GetGestureHandler()->OnScrollEvent(gfx::Point(0, 200), 0.f, 10.f);
+  GetGestureHandler()->OnReleaseEvent(
+      shelf_bounds.CenterPoint(),
+      base::make_optional(
+          -DragWindowFromShelfController::kVelocityToHomeScreenThreshold));
+  EXPECT_FALSE(window->layer()->GetTargetTransform().IsIdentity());
+  EXPECT_EQ(window->GetProperty(kBackdropWindowMode),
+            BackdropWindowMode::kDisabled);
+}
+
+// Test that if drag is cancelled, overview should be dismissed and other
+// hidden windows should restore to its previous visibility state.
+TEST_F(HomeLauncherGestureHandlerTest, CancelDragDismissOverview) {
+  const gfx::Rect shelf_bounds =
+      Shelf::ForWindow(Shell::GetPrimaryRootWindow())->GetIdealBounds();
+  auto window3 = CreateWindowForTesting();
+  auto window2 = CreateWindowForTesting();
+  auto window1 = CreateWindowForTesting();
+  EXPECT_TRUE(window1->IsVisible());
+  EXPECT_TRUE(window2->IsVisible());
+  EXPECT_TRUE(window3->IsVisible());
+
+  GetGestureHandler()->OnPressEvent(Mode::kDragWindowToHomeOrOverview,
+                                    shelf_bounds.CenterPoint());
+  GetGestureHandler()->OnScrollEvent(gfx::Point(200, 200), 0.5f, 0.5f);
+  OverviewController* overview_controller = Shell::Get()->overview_controller();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(window1->IsVisible());
+  EXPECT_FALSE(window2->IsVisible());
+  EXPECT_FALSE(window3->IsVisible());
+
+  GetGestureHandler()->Cancel();
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(window1->IsVisible());
+  EXPECT_TRUE(window2->IsVisible());
+  EXPECT_TRUE(window3->IsVisible());
+}
+
 class HomeLauncherModeGestureHandlerTest
     : public HomeLauncherGestureHandlerTest,
       public testing::WithParamInterface<Mode> {
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 54ae92d97..f6df89e9 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -80,14 +80,14 @@
 // should not affect visual appearance.
 constexpr int kNonEmptyHeightDp = 30;
 
-// Horizontal distance between the auth user and the media controls.
-constexpr int kDistanceBetweenAuthUserAndMediaControlsLandscapeDp = 100;
-constexpr int kDistanceBetweenAuthUserAndMediaControlsPortraitDp = 50;
-
 // Horizontal distance between two users in the low density layout.
 constexpr int kLowDensityDistanceBetweenUsersInLandscapeDp = 118;
 constexpr int kLowDensityDistanceBetweenUsersInPortraitDp = 32;
 
+constexpr int kMediaControlsSpacingThreshold = 1280;
+constexpr int kMediaControlsSmallSpaceFactor = 3;
+constexpr int kMediaControlsLargeSpaceFactor = 5;
+
 // Margin left of the auth user in the medium density layout.
 constexpr int kMediumDensityMarginLeftOfAuthUserLandscapeDp = 98;
 constexpr int kMediumDensityMarginLeftOfAuthUserPortraitDp = 0;
@@ -897,6 +897,11 @@
   disable_lock_screen_note_ = state->disable_auth;
   OnLockScreenNoteStateChanged(mojom::TrayActionState::kNotAvailable);
 
+  if (auth_disabled_data.disable_lock_screen_media) {
+    Shell::Get()->media_controller()->SuspendMediaSessions();
+    HideMediaControlsLayout();
+  }
+
   LoginBigUserView* big_user =
       TryToFindBigUser(user, true /*require_auth_active*/);
   if (big_user && big_user->auth_user()) {
@@ -1281,6 +1286,27 @@
                            std::min(available_width, desired_width));
 }
 
+void LockContentsView::SetMediaControlsSpacing(bool landscape) {
+  views::View* spacing_middle = media_controls_view_->GetMiddleSpacingView();
+
+  int total_width = GetPreferredSize().width();
+  int available_width =
+      total_width - (primary_big_view_->GetPreferredSize().width() +
+                     media_controls_view_->GetPreferredSize().width());
+  if (available_width <= 0) {
+    SetPreferredWidthForView(spacing_middle, 0);
+    return;
+  }
+
+  int desired_width;
+  if (!landscape || total_width <= kMediaControlsSpacingThreshold)
+    desired_width = available_width / kMediaControlsSmallSpaceFactor;
+  else
+    desired_width = available_width / kMediaControlsLargeSpaceFactor;
+
+  SetPreferredWidthForView(spacing_middle, desired_width);
+}
+
 bool LockContentsView::AreMediaControlsEnabled() const {
   return screen_type_ == LockScreen::ScreenType::kLock &&
          !expanded_view_->GetVisible() &&
@@ -1309,10 +1335,7 @@
 
   // Set |spacing_middle|.
   AddDisplayLayoutAction(base::BindRepeating(
-      &LockContentsView::SetLowDensitySpacing, base::Unretained(this),
-      media_controls_view_->GetMiddleSpacingView(), media_controls_view_.get(),
-      kDistanceBetweenAuthUserAndMediaControlsLandscapeDp,
-      kDistanceBetweenAuthUserAndMediaControlsPortraitDp));
+      &LockContentsView::SetMediaControlsSpacing, base::Unretained(this)));
 
   Layout();
 }
diff --git a/ash/login/ui/lock_contents_view.h b/ash/login/ui/lock_contents_view.h
index c64ebe3..be91f578 100644
--- a/ash/login/ui/lock_contents_view.h
+++ b/ash/login/ui/lock_contents_view.h
@@ -247,6 +247,9 @@
                             int portrait_dist,
                             bool landscape);
 
+  // Set |spacing_middle| for media controls.
+  void SetMediaControlsSpacing(bool landscape);
+
   // 1-2 users.
   void CreateLowDensityLayout(const std::vector<LoginUserInfo>& users);
   // 3-6 users.
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index 8654466..92ad2e74 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -100,6 +100,15 @@
   media_controls->MediaSessionInfoChanged(std::move(session_info));
 }
 
+// Returns sample AuthDisabledData to be used in tests, if the details are not
+// important.
+AuthDisabledData GetTestDisabledAuthData() {
+  return AuthDisabledData(ash::AuthDisabledReason::kTimeWindowLimit,
+                          base::Time::Now() + base::TimeDelta::FromHours(8),
+                          base::TimeDelta::FromHours(1),
+                          true /*disable_lock_screen_media*/);
+}
+
 }  // namespace
 
 using LockContentsViewUnitTest = LoginTestBase;
@@ -1844,11 +1853,8 @@
   EXPECT_FALSE(pin_view->GetVisible());
   EXPECT_FALSE(disabled_auth_message->GetVisible());
   // Setting auth disabled will hide the password field and show the message.
-  DataDispatcher()->DisableAuthForUser(
-      kFirstUserAccountId,
-      AuthDisabledData(ash::AuthDisabledReason::kTimeWindowLimit,
-                       base::Time::Now() + base::TimeDelta::FromHours(8),
-                       base::TimeDelta::FromHours(1)));
+  DataDispatcher()->DisableAuthForUser(kFirstUserAccountId,
+                                       GetTestDisabledAuthData());
   EXPECT_FALSE(password_view->GetVisible());
   EXPECT_FALSE(pin_view->GetVisible());
   EXPECT_TRUE(disabled_auth_message->GetVisible());
@@ -1859,11 +1865,8 @@
   EXPECT_FALSE(disabled_auth_message->GetVisible());
 
   // Set auth disabled again.
-  DataDispatcher()->DisableAuthForUser(
-      kFirstUserAccountId,
-      AuthDisabledData(ash::AuthDisabledReason::kTimeWindowLimit,
-                       base::Time::Now() + base::TimeDelta::FromHours(8),
-                       base::TimeDelta::FromHours(1)));
+  DataDispatcher()->DisableAuthForUser(kFirstUserAccountId,
+                                       GetTestDisabledAuthData());
   EXPECT_FALSE(password_view->GetVisible());
   EXPECT_FALSE(pin_view->GetVisible());
   EXPECT_TRUE(disabled_auth_message->GetVisible());
@@ -1898,22 +1901,16 @@
 
   EXPECT_TRUE(note_action_button->GetVisible());
   // Setting auth disabled hides the note action button.
-  DataDispatcher()->DisableAuthForUser(
-      kFirstUserAccountId,
-      AuthDisabledData(ash::AuthDisabledReason::kTimeWindowLimit,
-                       base::Time::Now() + base::TimeDelta::FromHours(8),
-                       base::TimeDelta::FromHours(1)));
+  DataDispatcher()->DisableAuthForUser(kFirstUserAccountId,
+                                       GetTestDisabledAuthData());
   EXPECT_FALSE(note_action_button->GetVisible());
   // Setting auth enabled shows the note action button.
   DataDispatcher()->EnableAuthForUser(kFirstUserAccountId);
   EXPECT_TRUE(note_action_button->GetVisible());
 
   // Set auth disabled again.
-  DataDispatcher()->DisableAuthForUser(
-      kFirstUserAccountId,
-      AuthDisabledData(ash::AuthDisabledReason::kTimeWindowLimit,
-                       base::Time::Now() + base::TimeDelta::FromHours(8),
-                       base::TimeDelta::FromHours(1)));
+  DataDispatcher()->DisableAuthForUser(kFirstUserAccountId,
+                                       GetTestDisabledAuthData());
   EXPECT_FALSE(note_action_button->GetVisible());
   // Set the lock screen note state to |kNotAvailable| while the note action
   // button is hidden.
@@ -1939,11 +1936,8 @@
   LoginUserView* user_view = auth_test_api.user_view();
 
   // The message is visible after disabling auth and it receives initial focus.
-  DataDispatcher()->DisableAuthForUser(
-      kFirstUserAccountId,
-      AuthDisabledData(ash::AuthDisabledReason::kTimeWindowLimit,
-                       base::Time::Now() + base::TimeDelta::FromHours(8),
-                       base::TimeDelta::FromHours(1)));
+  DataDispatcher()->DisableAuthForUser(kFirstUserAccountId,
+                                       GetTestDisabledAuthData());
   EXPECT_TRUE(disabled_auth_message->GetVisible());
   EXPECT_TRUE(HasFocusInAnyChildView(disabled_auth_message));
   // Tabbing from the message will move focus to the user view.
@@ -1963,6 +1957,66 @@
   EXPECT_TRUE(HasFocusInAnyChildView(status_area));
 }
 
+TEST_F(LockContentsViewUnitTest, DisableAuthAndMediaControls) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kLockScreenMediaControls);
+
+  auto* contents = new LockContentsView(
+      mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
+  SetUserCount(1);
+  std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
+
+  const AccountId& kFirstUserAccountId = users()[0].basic_user_info.account_id;
+  LockContentsView::TestApi lock_contents(contents);
+
+  // Simulate playing media session.
+  SimulateMediaSessionChanged(
+      lock_contents.media_controls_view(),
+      media_session::mojom::MediaPlaybackState::kPlaying);
+  EXPECT_TRUE(lock_contents.media_controls_view()->IsDrawn());
+
+  // Disable auth and media.
+  DataDispatcher()->DisableAuthForUser(
+      kFirstUserAccountId,
+      AuthDisabledData(ash::AuthDisabledReason::kTimeWindowLimit,
+                       base::Time::Now() + base::TimeDelta::FromHours(8),
+                       base::TimeDelta::FromHours(1),
+                       true /*disable_lock_screen_media*/));
+  EXPECT_FALSE(lock_contents.media_controls_view()->IsDrawn());
+}
+
+TEST_F(LockContentsViewUnitTest, DisableAuthAllowMediaControls) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kLockScreenMediaControls);
+
+  auto* contents = new LockContentsView(
+      mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
+  SetUserCount(1);
+  std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
+
+  const AccountId& kFirstUserAccountId = users()[0].basic_user_info.account_id;
+  LockContentsView::TestApi lock_contents(contents);
+
+  // Simulate playing media session.
+  SimulateMediaSessionChanged(
+      lock_contents.media_controls_view(),
+      media_session::mojom::MediaPlaybackState::kPlaying);
+  EXPECT_TRUE(lock_contents.media_controls_view()->IsDrawn());
+
+  // Disable auth, but allow media.
+  DataDispatcher()->DisableAuthForUser(
+      kFirstUserAccountId,
+      AuthDisabledData(ash::AuthDisabledReason::kTimeWindowLimit,
+                       base::Time::Now() + base::TimeDelta::FromHours(8),
+                       base::TimeDelta::FromHours(1),
+                       false /*disable_lock_screen_media*/));
+  EXPECT_TRUE(lock_contents.media_controls_view()->IsDrawn());
+}
+
 // Tests parent access dialog showing/hiding and focus behavior.
 TEST_F(LockContentsViewUnitTest, ParentAccessDialog) {
   auto* contents = new LockContentsView(
@@ -2020,11 +2074,7 @@
   SetWidget(CreateWidgetWithContent(contents));
 
   // Simulate initial state - user auth disabled and button shown.
-  DataDispatcher()->DisableAuthForUser(
-      child_id,
-      AuthDisabledData(ash::AuthDisabledReason::kTimeWindowLimit,
-                       base::Time::Now() + base::TimeDelta::FromHours(8),
-                       base::TimeDelta::FromHours(1)));
+  DataDispatcher()->DisableAuthForUser(child_id, GetTestDisabledAuthData());
   Shell::Get()->login_screen_controller()->ShowParentAccessButton(true);
   EXPECT_TRUE(ash::LoginScreenTestApi::IsParentAccessButtonShown());
 
diff --git a/ash/login/ui/lock_debug_view.cc b/ash/login/ui/lock_debug_view.cc
index 382ad699..793c6bc0 100644
--- a/ash/login/ui/lock_debug_view.cc
+++ b/ash/login/ui/lock_debug_view.cc
@@ -383,7 +383,8 @@
                            base::Time::Now() +
                                base::TimeDelta::FromHours(user_index) +
                                base::TimeDelta::FromHours(8),
-                           base::TimeDelta::FromMinutes(15)));
+                           base::TimeDelta::FromMinutes(15),
+                           true /*bool disable_lock_screen_media*/));
       UpdateAuthDisabledReason();
     }
   }
diff --git a/ash/media/media_notification_container_impl.cc b/ash/media/media_notification_container_impl.cc
index 6b98d13..8c63546 100644
--- a/ash/media/media_notification_container_impl.cc
+++ b/ash/media/media_notification_container_impl.cc
@@ -5,6 +5,7 @@
 #include "ash/media/media_notification_container_impl.h"
 
 #include "ui/message_center/message_center.h"
+#include "ui/message_center/public/cpp/message_center_constants.h"
 #include "ui/message_center/views/notification_control_buttons_view.h"
 #include "ui/native_theme/native_theme_dark_aura.h"
 #include "ui/views/background.h"
@@ -19,11 +20,12 @@
       control_buttons_view_(
           std::make_unique<message_center::NotificationControlButtonsView>(
               this)),
-      view_(this,
-            std::move(item),
-            control_buttons_view_.get(),
-            message_center::MessageCenter::Get()
-                ->GetSystemNotificationAppName()) {
+      view_(
+          this,
+          std::move(item),
+          control_buttons_view_.get(),
+          message_center::MessageCenter::Get()->GetSystemNotificationAppName(),
+          message_center::kNotificationWidth) {
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
   // Since we own these, we don't want Views to destroy them when their parent
diff --git a/ash/metrics/histogram_macros.cc b/ash/metrics/histogram_macros.cc
index 2d43e8f3..e820446 100644
--- a/ash/metrics/histogram_macros.cc
+++ b/ash/metrics/histogram_macros.cc
@@ -3,10 +3,9 @@
 // found in the LICENSE file.
 
 #include "ash/metrics/histogram_macros.h"
+
 #include "ash/shell.h"
-#include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
-#include "ui/aura/window.h"
 
 namespace ash {
 
@@ -16,13 +15,4 @@
   return tablet_mode_controller && tablet_mode_controller->InTabletMode();
 }
 
-bool IsInSplitView() {
-  const aura::Window::Windows root_windows = Shell::GetAllRootWindows();
-  return std::any_of(
-      root_windows.cbegin(), root_windows.cend(),
-      [](const aura::Window* root_window) {
-        return SplitViewController::Get(root_window)->InSplitViewMode();
-      });
-}
-
 }  // namespace ash
diff --git a/ash/metrics/histogram_macros.h b/ash/metrics/histogram_macros.h
index c83ab6e..6a41ba9 100644
--- a/ash/metrics/histogram_macros.h
+++ b/ash/metrics/histogram_macros.h
@@ -19,16 +19,17 @@
       UMA_HISTOGRAM_PERCENTAGE(name, __VA_ARGS__);    \
   } while (0)
 
-#define UMA_HISTOGRAM_PERCENTAGE_IN_SPLITVIEW(name, ...) \
-  do {                                                   \
-    if (ash::IsInSplitView())                            \
-      UMA_HISTOGRAM_PERCENTAGE(name, __VA_ARGS__);       \
+#define UMA_HISTOGRAM_PERCENTAGE_IN_SPLITVIEW(in_split_view, name, ...) \
+  do {                                                                  \
+    if (in_split_view)                                                  \
+      UMA_HISTOGRAM_PERCENTAGE(name, __VA_ARGS__);                      \
   } while (0)
 
-#define UMA_HISTOGRAM_PERCENTAGE_IN_TABLET_NON_SPLITVIEW(name, ...) \
-  do {                                                              \
-    if (ash::InTabletMode() && !ash::IsInSplitView())               \
-      UMA_HISTOGRAM_PERCENTAGE(name, __VA_ARGS__);                  \
+#define UMA_HISTOGRAM_PERCENTAGE_IN_TABLET_NON_SPLITVIEW(in_split_view, name, \
+                                                         ...)                 \
+  do {                                                                        \
+    if (ash::InTabletMode() && !in_split_view)                                \
+      UMA_HISTOGRAM_PERCENTAGE(name, __VA_ARGS__);                            \
   } while (0)
 
 #define UMA_HISTOGRAM_PERCENTAGE_IN_CLAMSHELL(name, ...) \
@@ -40,7 +41,6 @@
 namespace ash {
 
 ASH_EXPORT bool InTabletMode();
-ASH_EXPORT bool IsInSplitView();
 
 }  // namespace ash
 
diff --git a/ash/public/cpp/app_list/app_list_features.cc b/ash/public/cpp/app_list/app_list_features.cc
index 56fe098..e6e8f7d 100644
--- a/ash/public/cpp/app_list/app_list_features.cc
+++ b/ash/public/cpp/app_list/app_list_features.cc
@@ -50,6 +50,9 @@
 const base::Feature kEnableAggregatedMlSearchRanking{
     "EnableAggregatedMlSearchRanking", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kEnableCrOSActionRecorder{
+    "EnableCrOSActionRecorder", base::FEATURE_DISABLED_BY_DEFAULT};
+
 bool IsAnswerCardEnabled() {
   // Not using local static variable to allow tests to change this value.
   // Do not show answer card if the embedded Assistant UI is enabled.
@@ -126,6 +129,10 @@
   return base::FeatureList::IsEnabled(kEnableAggregatedMlSearchRanking);
 }
 
+bool IsCrOSActionRecorderEnabled() {
+  return base::FeatureList::IsEnabled(kEnableCrOSActionRecorder);
+}
+
 std::string AnswerServerUrl() {
   const std::string experiment_url =
       base::GetFieldTrialParamValueByFeature(kEnableAnswerCard, "ServerUrl");
@@ -147,6 +154,11 @@
   return std::string("MrfuAppLaunchPredictor");
 }
 
+int GetCrOSActionRecorderType() {
+  return base::GetFieldTrialParamByFeatureAsInt(kEnableCrOSActionRecorder,
+                                                "CrOSActionRecorderType", 0);
+}
+
 bool IsAppListLaunchRecordingEnabled() {
   return base::FeatureList::IsEnabled(kEnableAppListLaunchRecording);
 }
diff --git a/ash/public/cpp/app_list/app_list_features.h b/ash/public/cpp/app_list/app_list_features.h
index b5154752..6b32df4 100644
--- a/ash/public/cpp/app_list/app_list_features.h
+++ b/ash/public/cpp/app_list/app_list_features.h
@@ -81,6 +81,10 @@
 // non empty queries.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableAggregatedMlSearchRanking;
 
+// Enables CrOSActionRecorder which records some user actions.
+// Should be only enabled for users that joined the experiment.
+ASH_PUBLIC_EXPORT extern const base::Feature kEnableCrOSActionRecorder;
+
 bool ASH_PUBLIC_EXPORT IsAnswerCardEnabled();
 bool ASH_PUBLIC_EXPORT IsPlayStoreAppSearchEnabled();
 bool ASH_PUBLIC_EXPORT IsAppDataSearchEnabled();
@@ -100,10 +104,12 @@
 bool ASH_PUBLIC_EXPORT IsScalableAppListEnabled();
 bool ASH_PUBLIC_EXPORT IsFuzzyAppSearchEnabled();
 bool ASH_PUBLIC_EXPORT IsAggregatedMlSearchRankingEnabled();
+bool ASH_PUBLIC_EXPORT IsCrOSActionRecorderEnabled();
 
 std::string ASH_PUBLIC_EXPORT AnswerServerUrl();
 std::string ASH_PUBLIC_EXPORT AnswerServerQuerySuffix();
 std::string ASH_PUBLIC_EXPORT AppSearchResultRankerPredictorName();
+int ASH_PUBLIC_EXPORT GetCrOSActionRecorderType();
 
 }  // namespace app_list_features
 
diff --git a/ash/public/cpp/ash_pref_names.cc b/ash/public/cpp/ash_pref_names.cc
index a15a06e..6818f39 100644
--- a/ash/public/cpp/ash_pref_names.cc
+++ b/ash/public/cpp/ash_pref_names.cc
@@ -465,6 +465,7 @@
     "ash.launcher.assistant_privacy_info_dismissed";
 
 // A boolean pref that indicates whether lock screen media controls are enabled.
+// Controlled by user policy.
 const char kLockScreenMediaControlsEnabled[] =
     "ash.lock_screen_media_controls_enabled";
 
diff --git a/ash/public/cpp/login_types.cc b/ash/public/cpp/login_types.cc
index c8b158ba..0d32258 100644
--- a/ash/public/cpp/login_types.cc
+++ b/ash/public/cpp/login_types.cc
@@ -59,10 +59,12 @@
 AuthDisabledData::AuthDisabledData() = default;
 AuthDisabledData::AuthDisabledData(AuthDisabledReason reason,
                                    const base::Time& auth_reenabled_time,
-                                   const base::TimeDelta& device_used_time)
+                                   const base::TimeDelta& device_used_time,
+                                   bool disable_lock_screen_media)
     : reason(reason),
       auth_reenabled_time(auth_reenabled_time),
-      device_used_time(device_used_time) {}
+      device_used_time(device_used_time),
+      disable_lock_screen_media(disable_lock_screen_media) {}
 AuthDisabledData::AuthDisabledData(const AuthDisabledData& other) = default;
 AuthDisabledData::AuthDisabledData(AuthDisabledData&& other) = default;
 AuthDisabledData::~AuthDisabledData() = default;
diff --git a/ash/public/cpp/login_types.h b/ash/public/cpp/login_types.h
index 9dae63c..f85fbe0 100644
--- a/ash/public/cpp/login_types.h
+++ b/ash/public/cpp/login_types.h
@@ -273,7 +273,8 @@
   AuthDisabledData();
   AuthDisabledData(AuthDisabledReason reason,
                    const base::Time& auth_reenabled_time,
-                   const base::TimeDelta& device_used_time);
+                   const base::TimeDelta& device_used_time,
+                   bool disable_lock_screen_media);
   AuthDisabledData(const AuthDisabledData& other);
   AuthDisabledData(AuthDisabledData&& other);
   ~AuthDisabledData();
@@ -290,6 +291,10 @@
 
   // The amount of time that the user used this device.
   base::TimeDelta device_used_time;
+
+  // If true media will be suspended and media controls will be unavailable on
+  // lock screen.
+  bool disable_lock_screen_media = false;
 };
 
 // Possible reasons why the parent access code is required. This corresponds to
diff --git a/ash/public/cpp/shelf_config.h b/ash/public/cpp/shelf_config.h
index 17924c2..41d1f635 100644
--- a/ash/public/cpp/shelf_config.h
+++ b/ash/public/cpp/shelf_config.h
@@ -8,7 +8,6 @@
 #include "ash/ash_export.h"
 #include "ash/public/cpp/app_list/app_list_controller_observer.h"
 #include "ash/public/cpp/tablet_mode_observer.h"
-#include "ash/wm/overview/overview_observer.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
@@ -21,7 +20,6 @@
 // values could change at runtime.
 class ASH_EXPORT ShelfConfig : public TabletModeObserver,
                                public AppListControllerObserver,
-                               public OverviewObserver,
                                public display::DisplayObserver {
  public:
   class Observer : public base::CheckedObserver {
@@ -55,10 +53,6 @@
   // AppListControllerObserver:
   void OnAppListVisibilityWillChange(bool shown, int64_t display_id) override;
 
-  // OverviewObserver:
-  void OnOverviewModeStartingAnimationComplete(bool canceled) override;
-  void OnOverviewModeEndingAnimationComplete(bool canceled) override;
-
   // Size of the shelf when visible (height when the shelf is horizontal and
   // width when the shelf is vertical).
   int shelf_size() const;
diff --git a/ash/shelf/overflow_bubble_view.cc b/ash/shelf/overflow_bubble_view.cc
index 87c6f1a..6d74b18 100644
--- a/ash/shelf/overflow_bubble_view.cc
+++ b/ash/shelf/overflow_bubble_view.cc
@@ -236,7 +236,7 @@
   DCHECK(shelf_view_);
   DCHECK(GetShelf());
 
-  const int shelf_size = ShelfConfig::Get()->shelf_size() / 2;
+  const int shelf_size = ShelfConfig::Get()->shelf_size();
   set_border_radius(views::LayoutProvider::Get()->GetCornerRadiusMetric(
       views::EMPHASIS_MAXIMUM, {shelf_size, shelf_size}));
   SetArrow(views::BubbleBorder::NONE);
diff --git a/ash/shelf/scrollable_shelf_view.cc b/ash/shelf/scrollable_shelf_view.cc
index 60b7e5fd..fe087924 100644
--- a/ash/shelf/scrollable_shelf_view.cc
+++ b/ash/shelf/scrollable_shelf_view.cc
@@ -13,11 +13,13 @@
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/numerics/ranges.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/paint_recorder.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/vector2d_conversions.h"
 #include "ui/gfx/skia_paint_util.h"
+#include "ui/strings/grit/ui_strings.h"
 #include "ui/views/focus/focus_search.h"
 #include "ui/views/view_targeter_delegate.h"
 
@@ -763,6 +765,9 @@
   if (!view || !view->parent())
     return false;
 
+  if (view == left_arrow_ || view == right_arrow_)
+    return true;
+
   if (view->parent() != shelf_view_)
     return false;
 
@@ -799,6 +804,12 @@
   if (view->parent() == shelf_view_)
     return shelf_view_->GetTitleForView(view);
 
+  if (view == left_arrow_)
+    return l10n_util::GetStringUTF16(IDS_SHELF_PREVIOUS);
+
+  if (view == right_arrow_)
+    return l10n_util::GetStringUTF16(IDS_SHELF_NEXT);
+
   return base::string16();
 }
 
diff --git a/ash/shelf/scrollable_shelf_view_unittest.cc b/ash/shelf/scrollable_shelf_view_unittest.cc
index 2183301..0231be6 100644
--- a/ash/shelf/scrollable_shelf_view_unittest.cc
+++ b/ash/shelf/scrollable_shelf_view_unittest.cc
@@ -307,4 +307,37 @@
   CheckFirstAndLastTappableIconsBounds();
 }
 
+TEST_F(ScrollableShelfViewTest, ShowTooltipForArrowButtons) {
+  AddAppShortcutsUntilOverflow();
+  ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
+            scrollable_shelf_view_->layout_strategy_for_test());
+
+  // Check the initial state of |tooltip_manager|.
+  ShelfTooltipManager* tooltip_manager = test_api_->tooltip_manager();
+  EXPECT_FALSE(tooltip_manager->IsVisible());
+
+  // Verifies that tooltip should show for a visible shelf item.
+  views::View* right_arrow = scrollable_shelf_view_->right_arrow();
+  GetEventGenerator()->MoveMouseTo(
+      right_arrow->GetBoundsInScreen().CenterPoint());
+  tooltip_manager->ShowTooltip(right_arrow);
+  EXPECT_TRUE(tooltip_manager->IsVisible());
+
+  // Click right arrow button to scroll the shelf and show left arrow button.
+  GetEventGenerator()->ClickLeftButton();
+  ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
+            scrollable_shelf_view_->layout_strategy_for_test());
+
+  // Reset |tooltip_manager|.
+  GetEventGenerator()->MoveMouseTo(gfx::Point());
+  tooltip_manager->Close();
+  EXPECT_FALSE(tooltip_manager->IsVisible());
+
+  views::View* left_arrow = scrollable_shelf_view_->left_arrow();
+  GetEventGenerator()->MoveMouseTo(
+      left_arrow->GetBoundsInScreen().CenterPoint());
+  tooltip_manager->ShowTooltip(left_arrow);
+  EXPECT_TRUE(tooltip_manager->IsVisible());
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_config.cc b/ash/shelf/shelf_config.cc
index c096229..ad10689 100644
--- a/ash/shelf/shelf_config.cc
+++ b/ash/shelf/shelf_config.cc
@@ -10,7 +10,6 @@
 #include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
-#include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "ui/gfx/color_analysis.h"
@@ -84,7 +83,6 @@
   Shell* shell = Shell::Get();
   shell->tablet_mode_controller()->AddObserver(this);
   shell->app_list_controller()->AddObserver(this);
-  shell->overview_controller()->AddObserver(this);
   display::Screen::GetScreen()->AddObserver(this);
 }
 
@@ -94,7 +92,6 @@
 
   Shell* shell = Shell::Get();
   display::Screen::GetScreen()->RemoveObserver(this);
-  shell->overview_controller()->RemoveObserver(this);
   shell->app_list_controller()->RemoveObserver(this);
   shell->tablet_mode_controller()->RemoveObserver(this);
 }
@@ -122,14 +119,6 @@
   OnShelfConfigUpdated();
 }
 
-void ShelfConfig::OnOverviewModeStartingAnimationComplete(bool canceled) {
-  OnShelfConfigUpdated();
-}
-
-void ShelfConfig::OnOverviewModeEndingAnimationComplete(bool canceled) {
-  OnShelfConfigUpdated();
-}
-
 int ShelfConfig::shelf_size() const {
   // Before the hotseat redesign, the shelf always has the same size.
   if (!chromeos::switches::ShouldShowShelfHotseat())
@@ -204,13 +193,10 @@
 
 bool ShelfConfig::is_in_app() const {
   Shell* shell = Shell::Get();
-  const auto* overview = shell->overview_controller();
   const auto* session = shell->session_controller();
-  if (!overview || !session)
+  if (!session)
     return false;
-  return !overview->InOverviewSession() &&
-         !overview->IsCompletingShutdownAnimations() &&
-         session->GetSessionState() == session_manager::SessionState::ACTIVE &&
+  return session->GetSessionState() == session_manager::SessionState::ACTIVE &&
          !is_app_list_visible_;
 }
 
diff --git a/ash/shelf/shelf_config_unittest.cc b/ash/shelf/shelf_config_unittest.cc
index 8a7208d8..4b4717c 100644
--- a/ash/shelf/shelf_config_unittest.cc
+++ b/ash/shelf/shelf_config_unittest.cc
@@ -169,7 +169,7 @@
   // Now go into overview.
   OverviewController* overview_controller = Shell::Get()->overview_controller();
   overview_controller->StartOverview();
-  EXPECT_FALSE(ShelfConfig::Get()->is_in_app());
+  EXPECT_TRUE(ShelfConfig::Get()->is_in_app());
 
   // Back to the app.
   overview_controller->EndOverview();
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index d523c10..36c4b97 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -1966,7 +1966,8 @@
   // list.
   StartScroll(start);
   UpdateScroll(-AppListView::kDragSnapToPeekingThreshold);
-  EndScroll(true /* is_fling */, -(AppListView::kDragVelocityThreshold + 10));
+  EndScroll(true /* is_fling */,
+            -(AppListView::kDragVelocityFromShelfThreshold + 10));
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
   GetAppListTestHelper()->CheckState(ash::AppListViewState::kFullscreenAllApps);
@@ -1976,19 +1977,21 @@
   GetAppListTestHelper()->CheckVisibility(false);
   GetAppListTestHelper()->CheckState(ash::AppListViewState::kClosed);
 
-  // Fling down that exceeds the velocity threshold should close the app list.
+  // Fling down that exceeds the velocity threshold should go to peeking state.
   StartScroll(start);
   UpdateScroll(-AppListView::kDragSnapToPeekingThreshold);
-  EndScroll(true /* is_fling */, AppListView::kDragVelocityThreshold + 10);
+  EndScroll(true /* is_fling */,
+            AppListView::kDragVelocityFromShelfThreshold + 10);
   GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckVisibility(false);
-  GetAppListTestHelper()->CheckState(ash::AppListViewState::kClosed);
+  GetAppListTestHelper()->CheckVisibility(true);
+  GetAppListTestHelper()->CheckState(ash::AppListViewState::kPeeking);
 
   // Fling the app list not exceed the velocity threshold, the state depends on
   // the drag amount.
   StartScroll(start);
   UpdateScroll(-(AppListView::kDragSnapToPeekingThreshold - 10));
-  EndScroll(true /* is_fling */, -(AppListView::kDragVelocityThreshold - 10));
+  EndScroll(true /* is_fling */,
+            -(AppListView::kDragVelocityFromShelfThreshold - 10));
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
   GetAppListTestHelper()->CheckState(ash::AppListViewState::kPeeking);
diff --git a/ash/system/message_center/unified_message_list_view.cc b/ash/system/message_center/unified_message_list_view.cc
index 9d0d915..bd4ea9e 100644
--- a/ash/system/message_center/unified_message_list_view.cc
+++ b/ash/system/message_center/unified_message_list_view.cc
@@ -287,7 +287,11 @@
     int y_offset) const {
   std::vector<Notification*> notifications;
   for (views::View* view : children()) {
-    if (view->bounds().bottom() <= y_offset) {
+    int bottom_limit =
+        features::IsUnifiedMessageCenterRefactorEnabled()
+            ? view->bounds().y() + kNotificationIconStackThreshold
+            : view->bounds().bottom();
+    if (bottom_limit <= y_offset) {
       Notification* notification =
           MessageCenter::Get()->FindVisibleNotificationById(
               AsMVC(view)->GetNotificationId());
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index 8869863..efb7a9b 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -101,6 +101,7 @@
 constexpr int kMessageCenterCollapseThreshold = 175;
 constexpr int kStackedNotificationBarHeight = 32;
 constexpr int kStackedNotificationBarCollapsedHeight = 40;
+constexpr int kNotificationIconStackThreshold = 28;
 constexpr int kUnifiedSliderViewSpacing = 12;
 constexpr int kUnifiedMenuPadding = 8;
 constexpr int kUnifiedMessageCenterBubbleSpacing = 8;
diff --git a/ash/system/tray/tray_detailed_view.cc b/ash/system/tray/tray_detailed_view.cc
index 72923bc4..a590ca7 100644
--- a/ash/system/tray/tray_detailed_view.cc
+++ b/ash/system/tray/tray_detailed_view.cc
@@ -337,6 +337,10 @@
     bool checked,
     bool enterprise_managed) {
   HoverHighlightView* item = AddScrollListItem(icon, text);
+  if (enterprise_managed) {
+    item->SetAccessibleName(l10n_util::GetStringFUTF16(
+        IDS_ASH_ACCESSIBILITY_FEATURE_MANAGED, text));
+  }
   TrayPopupUtils::InitializeAsCheckableRow(item, checked, enterprise_managed);
   return item;
 }
diff --git a/ash/system/unified/unified_system_tray_bubble.cc b/ash/system/unified/unified_system_tray_bubble.cc
index 65d620e4..3f93b3c 100644
--- a/ash/system/unified/unified_system_tray_bubble.cc
+++ b/ash/system/unified/unified_system_tray_bubble.cc
@@ -316,9 +316,15 @@
 
   // Don't close the bubble if the message center is gaining or losing
   // activation.
-  if (features::IsUnifiedMessageCenterRefactorEnabled()) {
-    if (GetContainerForWindow(gained_active) ==
-        GetContainerForWindow(lost_active)) {
+  if (features::IsUnifiedMessageCenterRefactorEnabled() &&
+      tray_->IsMessageCenterBubbleShown()) {
+    views::Widget* message_center_widget =
+        tray_->message_center_bubble()->GetBubbleWidget();
+    if (message_center_widget ==
+            views::Widget::GetWidgetForNativeView(gained_active) ||
+        (lost_active &&
+         message_center_widget ==
+             views::Widget::GetWidgetForNativeView(lost_active))) {
       return;
     }
   }
diff --git a/ash/wm/overview/caption_container_view.cc b/ash/wm/overview/caption_container_view.cc
index 283865f..f402ff6 100644
--- a/ash/wm/overview/caption_container_view.cc
+++ b/ash/wm/overview/caption_container_view.cc
@@ -299,9 +299,10 @@
     preview_view_->SetBoundsRect(preview_bounds);
   }
 
-  // Position the header at the top.
+  // Position the header at the top. The close button should be right aligned so
+  // that the edge of its icon, not the button itself lines up with the margins.
   const gfx::Rect header_bounds(kOverviewMargin, kOverviewMargin,
-                                GetLocalBounds().width() - kOverviewMargin,
+                                GetLocalBounds().width() - kWindowMargin,
                                 kHeaderHeightDp);
   header_view_->SetBoundsRect(header_bounds);
 }
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index 0c6e6b1..085c0f0 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -119,8 +119,10 @@
 class OverviewFpsCounter : public FpsCounter {
  public:
   OverviewFpsCounter(ui::Compositor* compositor,
+                     bool in_split_view,
                      bool single_animation_in_clamshell)
       : FpsCounter(compositor),
+        in_split_view_(in_split_view),
         single_animation_in_clamshell_(single_animation_in_clamshell) {}
   ~OverviewFpsCounter() override {
     int smoothness = ComputeSmoothness();
@@ -130,11 +132,15 @@
       UMA_HISTOGRAM_PERCENTAGE_IN_CLAMSHELL(clamshell_single_name, smoothness);
     else
       UMA_HISTOGRAM_PERCENTAGE_IN_CLAMSHELL(clamshell_multi_name, smoothness);
-    UMA_HISTOGRAM_PERCENTAGE_IN_TABLET_NON_SPLITVIEW(tablet_name, smoothness);
-    UMA_HISTOGRAM_PERCENTAGE_IN_SPLITVIEW(splitview_name, smoothness);
+    UMA_HISTOGRAM_PERCENTAGE_IN_TABLET_NON_SPLITVIEW(in_split_view_,
+                                                     tablet_name, smoothness);
+    UMA_HISTOGRAM_PERCENTAGE_IN_SPLITVIEW(in_split_view_, splitview_name,
+                                          smoothness);
   }
 
  private:
+  bool in_split_view_;
+
   // True if only top window animates upon enter/exit overview in clamshell.
   bool single_animation_in_clamshell_;
 
@@ -155,8 +161,9 @@
 class ShutdownAnimationFpsCounterObserver : public OverviewObserver {
  public:
   ShutdownAnimationFpsCounterObserver(ui::Compositor* compositor,
+                                      bool in_split_view,
                                       bool single_animation)
-      : fps_counter_(compositor, single_animation) {
+      : fps_counter_(compositor, in_split_view, single_animation) {
     Shell::Get()->overview_controller()->AddObserver(this);
   }
   ~ShutdownAnimationFpsCounterObserver() override {
@@ -493,12 +500,14 @@
       (animate_count == 1 && !has_non_cover_animating) &&
       !Shell::Get()->tablet_mode_controller()->InTabletMode();
 
+  const bool in_split_view =
+      SplitViewController::Get(root_window_)->InSplitViewMode();
   // OverviewGrid in splitscreen does not include the window to be activated.
-  if (!window_list_.empty() ||
-      SplitViewController::Get(root_window_)->InSplitViewMode()) {
+  if (!window_list_.empty() || in_split_view) {
     // The following instance self-destructs when shutdown animation ends.
     new ShutdownAnimationFpsCounterObserver(
-        root_window_->layer()->GetCompositor(), single_animation_in_clamshell);
+        root_window_->layer()->GetCompositor(), in_split_view,
+        single_animation_in_clamshell);
   }
 
   while (!window_list_.empty())
@@ -598,6 +607,7 @@
         !Shell::Get()->tablet_mode_controller()->InTabletMode();
     fps_counter_ = std::make_unique<OverviewEnterFpsCounter>(
         window_list_[0]->GetWindow()->layer()->GetCompositor(),
+        SplitViewController::Get(root_window_)->InSplitViewMode(),
         single_animation_in_clamshell);
   }
 
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index d010d6c..906b280 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -408,9 +408,6 @@
   const bool is_first_update = target_bounds_.IsEmpty();
   target_bounds_ = target_bounds;
 
-  gfx::RectF inset_bounds(target_bounds);
-  inset_bounds.Inset(kWindowMargin, kWindowMargin);
-
   // If the window is minimized we can avoid applying transforms on the original
   // window.
   if (transform_window_.IsMinimized()) {
@@ -454,8 +451,8 @@
       }
     }
   } else {
-    // SetItemBounds is called before UpdateHeaderLayout so the header can
-    // properly use the updated windows bounds.
+    gfx::RectF inset_bounds(target_bounds);
+    inset_bounds.Inset(kWindowMargin, kWindowMargin);
     SetItemBounds(inset_bounds, new_animation_type, is_first_update);
     UpdateHeaderLayout(is_first_update ? OVERVIEW_ANIMATION_NONE
                                        : new_animation_type);
@@ -469,12 +466,10 @@
     UpdateRoundedCornersAndShadow();
 
   if (cannot_snap_widget_) {
-    inset_bounds.Inset(
-        gfx::Insets(static_cast<float>(kHeaderHeightDp), 0.f, 0.f, 0.f));
     SetWidgetBoundsAndMaybeAnimateTransform(
         cannot_snap_widget_.get(),
         cannot_snap_widget_->GetBoundsCenteredIn(
-            gfx::ToEnclosingRect(inset_bounds)),
+            gfx::ToEnclosingRect(GetWindowTargetBoundsWithInsets())),
         new_animation_type, nullptr);
   }
 
@@ -580,9 +575,8 @@
                               visible
                                   ? SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_IN
                                   : SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_OUT);
-  gfx::Rect bounds = gfx::ToEnclosingRect(target_bounds());
-  bounds.Inset(kWindowMargin, kWindowMargin);
-  bounds.Inset(gfx::Insets(kHeaderHeightDp, 0, 0, 0));
+  const gfx::Rect bounds =
+      gfx::ToEnclosingRect(GetWindowTargetBoundsWithInsets());
   cannot_snap_widget_->SetBoundsCenteredIn(bounds, /*animate=*/false);
 }
 
@@ -1086,6 +1080,13 @@
   return shadow_->content_bounds();
 }
 
+gfx::RectF OverviewItem::GetWindowTargetBoundsWithInsets() const {
+  gfx::RectF window_target_bounds = target_bounds_;
+  window_target_bounds.Inset(kWindowMargin, kWindowMargin);
+  window_target_bounds.Inset(0, kHeaderHeightDp, 0, 0);
+  return window_target_bounds;
+}
+
 void OverviewItem::OnWindowCloseAnimationCompleted() {
   transform_window_.Close();
 }
@@ -1155,6 +1156,15 @@
                                  bool is_first_update) {
   aura::Window* window = GetWindow();
   DCHECK(root_window_ == window->GetRootWindow());
+  // Do not set transform for drop target, set bounds instead.
+  if (overview_grid_->IsDropTargetWindow(window)) {
+    window->SetBoundsInScreen(
+        gfx::ToEnclosedRect(GetWindowTargetBoundsWithInsets()),
+        WindowState::Get(window)->GetDisplay());
+    window->SetTransform(gfx::Transform());
+    return;
+  }
+
   gfx::RectF screen_rect = gfx::RectF(GetTargetBoundsInScreen());
 
   // Avoid division by zero by ensuring screen bounds is not empty.
@@ -1166,13 +1176,6 @@
   gfx::RectF overview_item_bounds =
       transform_window_.ShrinkRectToFitPreservingAspectRatio(
           screen_rect, target_bounds, top_view_inset, kHeaderHeightDp);
-  // Do not set transform for drop target, set bounds instead.
-  if (overview_grid_->IsDropTargetWindow(window)) {
-    window->SetBoundsInScreen(gfx::ToEnclosedRect(overview_item_bounds),
-                              WindowState::Get(window)->GetDisplay());
-    window->SetTransform(gfx::Transform());
-    return;
-  }
 
   const gfx::Transform transform =
       gfx::TransformBetweenRects(screen_rect, overview_item_bounds);
@@ -1230,11 +1233,6 @@
 }
 
 void OverviewItem::UpdateHeaderLayout(OverviewAnimationType animation_type) {
-  gfx::RectF transformed_window_bounds =
-      transform_window_.overview_bounds().value_or(
-          transform_window_.GetTransformedBounds());
-  ::wm::TranslateRectFromScreen(root_window_, &transformed_window_bounds);
-
   aura::Window* widget_window = item_widget_->GetNativeWindow();
   ScopedOverviewAnimationSettings animation_settings(animation_type,
                                                      widget_window);
@@ -1247,16 +1245,15 @@
         std::move(enter_observer));
   }
 
-  // |widget_window| is sized to the same bounds as the original window plus
-  // some space for the header and a little padding.
-  gfx::Rect label_rect(0, -kHeaderHeightDp, transformed_window_bounds.width(),
-                       kHeaderHeightDp + transformed_window_bounds.height());
-  label_rect.Inset(-kOverviewMargin, -kOverviewMargin);
-  widget_window->SetBounds(label_rect);
+  gfx::RectF item_bounds = target_bounds_;
+  ::wm::TranslateRectFromScreen(root_window_, &item_bounds);
+  const gfx::Point origin = gfx::ToRoundedPoint(item_bounds.origin());
+  item_bounds.set_origin(gfx::PointF());
+  item_bounds.Inset(-kWindowMargin, -kWindowMargin);
+  widget_window->SetBounds(gfx::ToEnclosedRect(item_bounds));
 
   gfx::Transform label_transform;
-  label_transform.Translate(gfx::ToRoundedInt(transformed_window_bounds.x()),
-                            gfx::ToRoundedInt(transformed_window_bounds.y()));
+  label_transform.Translate(origin.x(), origin.y());
   widget_window->SetTransform(label_transform);
 }
 
diff --git a/ash/wm/overview/overview_item.h b/ash/wm/overview/overview_item.h
index 497c271..e8855c1e 100644
--- a/ash/wm/overview/overview_item.h
+++ b/ash/wm/overview/overview_item.h
@@ -277,6 +277,10 @@
   FRIEND_TEST_ALL_PREFIXES(SplitViewOverviewSessionTest,
                            OverviewUnsnappableIndicatorVisibility);
 
+  // Returns the target bounds of |window_|. Same as |target_bounds_|, with some
+  // insets.
+  gfx::RectF GetWindowTargetBoundsWithInsets() const;
+
   // Functions to be called back when their associated animations complete.
   void OnWindowCloseAnimationCompleted();
   void OnItemSpawnedAnimationCompleted();
diff --git a/ash/wm/pip/pip_window_resizer.cc b/ash/wm/pip/pip_window_resizer.cc
index 1c78f78d..0f39ec7 100644
--- a/ash/wm/pip/pip_window_resizer.cc
+++ b/ash/wm/pip/pip_window_resizer.cc
@@ -252,7 +252,8 @@
 }
 
 void PipWindowResizer::CompleteDrag() {
-  if (details().bounds_change & kBoundsChange_Resizes) {
+  const bool is_resize = details().bounds_change & kBoundsChange_Resizes;
+  if (is_resize) {
     CollectFreeResizeAreaMetric(kAshPipFreeResizeFinishAreaHistogramName,
                                 GetTarget());
   } else {
@@ -283,7 +284,7 @@
   } else {
     // Animate the PIP window to its resting position.
     gfx::Rect bounds;
-    if (fling_amount > kPipMovementFlingThresholdSquared) {
+    if (!is_resize && fling_amount > kPipMovementFlingThresholdSquared) {
       bounds = ComputeFlungPosition();
     } else {
       bounds = GetTarget()->GetBoundsInScreen();
diff --git a/ash/wm/pip/pip_window_resizer_unittest.cc b/ash/wm/pip/pip_window_resizer_unittest.cc
index 468a7d1..1bda7f6 100644
--- a/ash/wm/pip/pip_window_resizer_unittest.cc
+++ b/ash/wm/pip/pip_window_resizer_unittest.cc
@@ -255,6 +255,17 @@
   EXPECT_EQ(gfx::Rect(200, 200, 100, 110), test_state()->last_bounds());
 }
 
+TEST_P(PipWindowResizerTest, ResizingPipWindowDoesNotTriggerFling) {
+  PreparePipWindow(gfx::Rect(8, 8, 100, 100));
+  std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTBOTTOM));
+  ASSERT_TRUE(resizer.get());
+
+  Fling(std::move(resizer), 0.f, 4000.f);
+
+  // Ensure that the PIP window isn't flung to the bottom edge during resize.
+  EXPECT_EQ(gfx::Point(8, 8), test_state()->last_bounds().origin());
+}
+
 TEST_P(PipWindowResizerTest, PipWindowCanBeSwipeDismissed) {
   PreparePipWindow(gfx::Rect(8, 8, 100, 100));
   std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index 6f99d8b..6747e6c3 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -181,6 +181,7 @@
     /*observe_external_pointer_device_events=*/true,
     /*block_internal_input_device=*/true,
     /*always_show_overview_button=*/false,
+    /*force_physical_tablet_state=*/false,
 };
 
 // Defines the behavior that sticks to the current mode. Used to
@@ -191,6 +192,7 @@
     /*observe_external_pointer_device_events=*/false,
     /*block_internal_input_device=*/false,
     /*always_show_overview_button=*/true,
+    /*force_physical_tablet_state=*/false,
 };
 
 // Defines the behavior used for testing. It prevents the device from
@@ -201,6 +203,7 @@
     /*observe_external_pointer_device_events=*/true,
     /*block_internal_input_device=*/true,
     /*always_show_overview_button=*/false,
+    /*force_physical_tablet_state=*/true,
 };
 
 // Used for development purpose (currently debug shortcut shift-ctrl-alt). This
@@ -212,6 +215,7 @@
     /*observe_external_pointer_device_events=*/true,
     /*block_internal_input_device=*/false,
     /*always_show_overview_button=*/true,
+    /*force_physical_tablet_state=*/true,
 };
 
 }  // namespace
@@ -423,10 +427,7 @@
 void TabletModeController::SetEnabledForTest(bool enabled) {
   tablet_mode_behavior_ = enabled ? kOnForTest : kDefault;
 
-  SetTabletModeEnabledInternal(enabled);
-  // Notify observers to update the tray button.
-  for (auto& observer : tablet_mode_observers_)
-    observer.OnTabletModeEventsBlockingChanged();
+  SetIsInTabletPhysicalState(enabled);
 }
 
 void TabletModeController::OnShellInitialized() {
@@ -630,10 +631,7 @@
 void TabletModeController::SetEnabledForDev(bool enabled) {
   tablet_mode_behavior_ = enabled ? kOnForDev : kDefault;
 
-  SetTabletModeEnabledInternal(enabled);
-  // Notify observers to update the tray button.
-  for (auto& observer : tablet_mode_observers_)
-    observer.OnTabletModeEventsBlockingChanged();
+  SetIsInTabletPhysicalState(enabled);
 }
 
 bool TabletModeController::ShouldShowOverviewButton() const {
@@ -1020,6 +1018,9 @@
   if (!HasActiveInternalDisplay())
     return false;
 
+  if (tablet_mode_behavior_.force_physical_tablet_state)
+    return true;
+
   // For updated EC, the tablet mode switch activates at 200 degrees, and
   // deactivates at 160 degrees.
   // For old EC, the tablet mode switch activates at 300 degrees, so it's
@@ -1071,18 +1072,21 @@
 
   is_in_tablet_physical_state_ = new_state;
 
-  // Even if we do not change the UI mode, we should update the input devices
-  // blocker as tablet mode events may come in because of the lid angle/or
-  // folio keyboard state changes but UI mode might still stay the same.
-  UpdateInternalInputDevicesEventBlocker();
+  // InputDeviceBlocker must always be updated, but don't update it here if the
+  // UI state has changed because it's already done.
+  if (UpdateUiTabletState())
+    return;
 
-  UpdateUiTabletState();
+  UpdateInternalInputDevicesEventBlocker();
 }
 
-void TabletModeController::UpdateUiTabletState() {
+bool TabletModeController::UpdateUiTabletState() {
   const bool should_be_in_tablet_mode = ShouldUiBeInTabletMode();
-  if (should_be_in_tablet_mode != InTabletMode())
-    SetTabletModeEnabledInternal(should_be_in_tablet_mode);
+  if (should_be_in_tablet_mode == InTabletMode())
+    return false;
+
+  SetTabletModeEnabledInternal(should_be_in_tablet_mode);
+  return true;
 }
 
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.h b/ash/wm/tablet_mode/tablet_mode_controller.h
index 19c9952..2e1c0ef 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.h
+++ b/ash/wm/tablet_mode/tablet_mode_controller.h
@@ -181,6 +181,7 @@
     bool observe_external_pointer_device_events = true;
     bool block_internal_input_device = false;
     bool always_show_overview_button = false;
+    bool force_physical_tablet_state = false;
   };
 
  private:
@@ -296,8 +297,9 @@
   void SetIsInTabletPhysicalState(bool new_state);
 
   // Updates the UI by either entering or exiting UI tablet mode if necessary
-  // based on the current state.
-  void UpdateUiTabletState();
+  // based on the current state. Returns true if there's a change in the UI
+  // tablet mode state, false otherwise.
+  bool UpdateUiTabletState();
 
   // The tablet window manager (if enabled).
   std::unique_ptr<TabletModeWindowManager> tablet_mode_window_manager_;
diff --git a/ash/wm/window_preview_view.cc b/ash/wm/window_preview_view.cc
index 0dca0a3..bb1c9566 100644
--- a/ash/wm/window_preview_view.cc
+++ b/ash/wm/window_preview_view.cc
@@ -31,6 +31,8 @@
 }
 
 WindowPreviewView::~WindowPreviewView() {
+  for (auto* window : unparented_transient_children_)
+    window->RemoveObserver(this);
   for (auto entry : mirror_views_)
     entry.first->RemoveObserver(this);
   aura::client::GetTransientWindowClient()->RemoveObserver(this);
@@ -140,6 +142,14 @@
 }
 
 void WindowPreviewView::RemoveWindow(aura::Window* window) {
+  auto iter = unparented_transient_children_.find(window);
+  if (iter != unparented_transient_children_.end()) {
+    unparented_transient_children_.erase(iter);
+    window->RemoveObserver(this);
+    DCHECK(!mirror_views_.count(window));
+    return;
+  }
+
   auto it = mirror_views_.find(window);
   if (it == mirror_views_.end())
     return;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 03c367d..200b8dae 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1074,8 +1074,6 @@
     "win/i18n.h",
     "win/iat_patch_function.cc",
     "win/iat_patch_function.h",
-    "win/iunknown_impl.cc",
-    "win/iunknown_impl.h",
     "win/map.h",
     "win/message_window.cc",
     "win/message_window.h",
@@ -2831,7 +2829,6 @@
     "win/hstring_compare_unittest.cc",
     "win/hstring_reference_unittest.cc",
     "win/i18n_unittest.cc",
-    "win/iunknown_impl_unittest.cc",
     "win/map_unittest.cc",
     "win/message_window_unittest.cc",
     "win/object_watcher_unittest.cc",
diff --git a/base/android/java/src/org/chromium/base/BuildInfo.java b/base/android/java/src/org/chromium/base/BuildInfo.java
index ccee0f5b..149b7883 100644
--- a/base/android/java/src/org/chromium/base/BuildInfo.java
+++ b/base/android/java/src/org/chromium/base/BuildInfo.java
@@ -137,7 +137,7 @@
             try {
                 gmsPackageInfo = pm.getPackageInfo("com.google.android.gms", 0);
             } catch (NameNotFoundException e) {
-                Log.d(TAG, "GMS package is not found.", e);
+                Log.d(TAG, "GMS package is not found.");
             }
             gmsVersionCode = gmsPackageInfo != null
                     ? String.valueOf(packageVersionCode(gmsPackageInfo))
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index 4946e24..618cd806 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -403,9 +403,6 @@
 
             mLoaded = true;
         } catch (UnsatisfiedLinkError e) {
-            // Callers typically call System.exit() when catching this exception, make sure that it
-            // doesn't get lost.
-            Log.e(TAG, "Unable to load library.", e);
             throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e);
         }
     }
diff --git a/base/android/java/src/org/chromium/base/metrics/CachedMetrics.java b/base/android/java/src/org/chromium/base/metrics/CachedMetrics.java
index 0c0c12c..1ebf34c7 100644
--- a/base/android/java/src/org/chromium/base/metrics/CachedMetrics.java
+++ b/base/android/java/src/org/chromium/base/metrics/CachedMetrics.java
@@ -9,6 +9,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.annotation.concurrent.GuardedBy;
+
 /**
  * Utility classes for recording UMA metrics before the native library
  * may have been loaded.  Metrics are cached until the library is known
@@ -23,6 +25,7 @@
      * commit operation when the native library is loaded.
      */
     private abstract static class CachedMetric {
+        @GuardedBy("sMetrics")
         private static final List<CachedMetric> sMetrics = new ArrayList<CachedMetric>();
 
         protected final String mName;
@@ -41,6 +44,7 @@
          * Note: The synchronization is not done inside this function because subclasses
          * need to increment their held values under lock to ensure thread-safety.
          */
+        @GuardedBy("sMetrics")
         protected final void addToCache() {
             assert Thread.holdsLock(sMetrics);
 
@@ -53,6 +57,7 @@
          * Commits the metric. Expects the native library to be loaded.
          * Must be called while holding the synchronized(sMetrics) lock.
          */
+        @GuardedBy("sMetrics")
         protected abstract void commitAndClear();
     }
 
@@ -60,6 +65,7 @@
      * Caches an action that will be recorded after native side is loaded.
      */
     public static class ActionEvent extends CachedMetric {
+        @GuardedBy("CachedMetric.sMetrics")
         private int mCount;
 
         public ActionEvent(String actionName) {
@@ -82,6 +88,7 @@
         }
 
         @Override
+        @GuardedBy("CachedMetric.sMetrics")
         protected void commitAndClear() {
             while (mCount > 0) {
                 recordWithNative();
@@ -92,6 +99,7 @@
 
     /** Caches a set of integer histogram samples. */
     public static class SparseHistogramSample extends CachedMetric {
+        @GuardedBy("CachedMetric.sMetrics")
         private final List<Integer> mSamples = new ArrayList<Integer>();
 
         public SparseHistogramSample(String histogramName) {
@@ -114,6 +122,7 @@
         }
 
         @Override
+        @GuardedBy("CachedMetric.sMetrics")
         protected void commitAndClear() {
             for (Integer sample : mSamples) {
                 recordWithNative(sample);
@@ -148,6 +157,7 @@
         }
 
         @Override
+        @GuardedBy("CachedMetric.sMetrics")
         protected void commitAndClear() {
             for (Integer sample : mSamples) {
                 recordWithNative(sample);
@@ -158,6 +168,7 @@
 
     /** Caches a set of times histogram samples. */
     public static class TimesHistogramSample extends CachedMetric {
+        @GuardedBy("CachedMetric.sMetrics")
         private final List<Long> mSamples = new ArrayList<Long>();
 
         public TimesHistogramSample(String histogramName) {
@@ -180,6 +191,7 @@
         }
 
         @Override
+        @GuardedBy("CachedMetric.sMetrics")
         protected void commitAndClear() {
             for (Long sample : mSamples) {
                 recordWithNative(sample);
@@ -205,6 +217,7 @@
 
     /** Caches a set of boolean histogram samples. */
     public static class BooleanHistogramSample extends CachedMetric {
+        @GuardedBy("CachedMetric.sMetrics")
         private final List<Boolean> mSamples = new ArrayList<Boolean>();
 
         public BooleanHistogramSample(String histogramName) {
@@ -227,6 +240,7 @@
         }
 
         @Override
+        @GuardedBy("CachedMetric.sMetrics")
         protected void commitAndClear() {
             for (Boolean sample : mSamples) {
                 recordWithNative(sample);
@@ -240,6 +254,7 @@
      * Corresponds to UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro.
      */
     public static class CustomCountHistogramSample extends CachedMetric {
+        @GuardedBy("CachedMetric.sMetrics")
         private final List<Integer> mSamples = new ArrayList<Integer>();
         private final int mMin;
         private final int mMax;
@@ -268,6 +283,7 @@
         }
 
         @Override
+        @GuardedBy("CachedMetric.sMetrics")
         protected void commitAndClear() {
             for (Integer sample : mSamples) {
                 recordWithNative(sample);
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
index b38e8e0..696ac269 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
@@ -272,6 +272,7 @@
         mMainThread.start();
     }
 
+    @SuppressWarnings("checkstyle:SystemExitCheck") // Allowed due to http://crbug.com/928521#c16.
     public void onDestroy() {
         Log.i(TAG, "Destroying ChildProcessService pid=%d", Process.myPid());
         System.exit(0);
diff --git a/base/bind.h b/base/bind.h
index 1d4b3cc..7a400af 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -191,12 +191,14 @@
 struct InvokeFuncImpl;
 
 template <typename Invoker>
-struct InvokeFuncImpl<true, Invoker>
-    : std::integral_constant<decltype(&Invoker::RunOnce), &Invoker::RunOnce> {};
+struct InvokeFuncImpl<true, Invoker> {
+  static constexpr auto Value = &Invoker::RunOnce;
+};
 
 template <typename Invoker>
-struct InvokeFuncImpl<false, Invoker>
-    : std::integral_constant<decltype(&Invoker::Run), &Invoker::Run> {};
+struct InvokeFuncImpl<false, Invoker> {
+  static constexpr auto Value = &Invoker::Run;
+};
 
 template <template <typename> class CallbackT,
           typename Functor,
@@ -227,7 +229,7 @@
   // InvokeFuncStorage, so that we can ensure its type matches to
   // PolymorphicInvoke, to which CallbackType will cast back.
   using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
-  PolymorphicInvoke invoke_func = InvokeFuncImpl<kIsOnce, Invoker>::value;
+  PolymorphicInvoke invoke_func = InvokeFuncImpl<kIsOnce, Invoker>::Value;
 
   using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
   return CallbackType(BindState::Create(
diff --git a/base/process/launch_win.cc b/base/process/launch_win.cc
index 6bf9e18..59c4e8f 100644
--- a/base/process/launch_win.cc
+++ b/base/process/launch_win.cc
@@ -287,9 +287,9 @@
       << "job. https://crbug.com/820996";
   if (options.as_user) {
     flags |= CREATE_UNICODE_ENVIRONMENT;
-    void* enviroment_block = nullptr;
+    void* environment_block = nullptr;
 
-    if (!CreateEnvironmentBlock(&enviroment_block, options.as_user, FALSE)) {
+    if (!CreateEnvironmentBlock(&environment_block, options.as_user, FALSE)) {
       DPLOG(ERROR);
       return Process();
     }
@@ -300,9 +300,9 @@
 
     BOOL launched = CreateProcessAsUser(
         options.as_user, nullptr, as_writable_wcstr(writable_cmdline), nullptr,
-        nullptr, inherit_handles, flags, enviroment_block, current_directory,
+        nullptr, inherit_handles, flags, environment_block, current_directory,
         startup_info, &temp_process_info);
-    DestroyEnvironmentBlock(enviroment_block);
+    DestroyEnvironmentBlock(environment_block);
     if (!launched) {
       DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline)
                    << std::endl;
diff --git a/base/task/thread_pool/priority_queue.cc b/base/task/thread_pool/priority_queue.cc
index 28077b87..85eae1fe 100644
--- a/base/task/thread_pool/priority_queue.cc
+++ b/base/task/thread_pool/priority_queue.cc
@@ -139,20 +139,18 @@
 }
 
 RegisteredTaskSource PriorityQueue::RemoveTaskSource(
-    scoped_refptr<TaskSource> task_source) {
-  DCHECK(task_source);
-
+    const TaskSource& task_source) {
   if (IsEmpty())
     return nullptr;
 
-  const HeapHandle heap_handle = task_source->heap_handle();
+  const HeapHandle heap_handle = task_source.heap_handle();
   if (!heap_handle.IsValid())
     return nullptr;
 
   TaskSourceAndSortKey& task_source_and_sort_key =
       const_cast<PriorityQueue::TaskSourceAndSortKey&>(
           container_.at(heap_handle));
-  DCHECK_EQ(task_source_and_sort_key.task_source().get(), task_source);
+  DCHECK_EQ(task_source_and_sort_key.task_source().get(), &task_source);
   RegisteredTaskSource registered_task_source =
       task_source_and_sort_key.take_task_source();
 
diff --git a/base/task/thread_pool/priority_queue.h b/base/task/thread_pool/priority_queue.h
index fbe391e..d4d88842 100644
--- a/base/task/thread_pool/priority_queue.h
+++ b/base/task/thread_pool/priority_queue.h
@@ -49,7 +49,7 @@
   // RegisteredTaskSource which evaluates to true if successful, or false if
   // |task_source| is not currently in the PriorityQueue or the PriorityQueue is
   // empty.
-  RegisteredTaskSource RemoveTaskSource(scoped_refptr<TaskSource> task_source);
+  RegisteredTaskSource RemoveTaskSource(const TaskSource& task_source);
 
   // Updates the sort key of the TaskSource in |transaction| to
   // match its current traits. No-ops if the TaskSource is not in the
diff --git a/base/task/thread_pool/priority_queue_unittest.cc b/base/task/thread_pool/priority_queue_unittest.cc
index 82320b9..30ad206a 100644
--- a/base/task/thread_pool/priority_queue_unittest.cc
+++ b/base/task/thread_pool/priority_queue_unittest.cc
@@ -144,34 +144,34 @@
 
   // Remove |sequence_a| from the PriorityQueue. |sequence_b| is still the
   // sequence with the highest priority.
-  EXPECT_TRUE(pq.RemoveTaskSource(sequence_a).Unregister());
+  EXPECT_TRUE(pq.RemoveTaskSource(*sequence_a).Unregister());
   EXPECT_EQ(sort_key_b, pq.PeekSortKey());
   ExpectNumSequences(1U, 0U, 2U);
 
   // RemoveTaskSource() should return false if called on a sequence not in the
   // PriorityQueue.
-  EXPECT_FALSE(pq.RemoveTaskSource(sequence_a).Unregister());
+  EXPECT_FALSE(pq.RemoveTaskSource(*sequence_a).Unregister());
   ExpectNumSequences(1U, 0U, 2U);
 
   // Remove |sequence_b| from the PriorityQueue. |sequence_c| becomes the
   // sequence with the highest priority.
-  EXPECT_TRUE(pq.RemoveTaskSource(sequence_b).Unregister());
+  EXPECT_TRUE(pq.RemoveTaskSource(*sequence_b).Unregister());
   EXPECT_EQ(sort_key_c, pq.PeekSortKey());
   ExpectNumSequences(1U, 0U, 1U);
 
   // Remove |sequence_d| from the PriorityQueue. |sequence_c| is still the
   // sequence with the highest priority.
-  EXPECT_TRUE(pq.RemoveTaskSource(sequence_d).Unregister());
+  EXPECT_TRUE(pq.RemoveTaskSource(*sequence_d).Unregister());
   EXPECT_EQ(sort_key_c, pq.PeekSortKey());
   ExpectNumSequences(0U, 0U, 1U);
 
   // Remove |sequence_c| from the PriorityQueue, making it empty.
-  EXPECT_TRUE(pq.RemoveTaskSource(sequence_c).Unregister());
+  EXPECT_TRUE(pq.RemoveTaskSource(*sequence_c).Unregister());
   EXPECT_TRUE(pq.IsEmpty());
   ExpectNumSequences(0U, 0U, 0U);
 
   // Return false if RemoveTaskSource() is called on an empty PriorityQueue.
-  EXPECT_FALSE(pq.RemoveTaskSource(sequence_c).Unregister());
+  EXPECT_FALSE(pq.RemoveTaskSource(*sequence_c).Unregister());
   ExpectNumSequences(0U, 0U, 0U);
 }
 
diff --git a/base/task/thread_pool/test_utils.cc b/base/task/thread_pool/test_utils.cc
index c80de4e2..d623637 100644
--- a/base/task/thread_pool/test_utils.cc
+++ b/base/task/thread_pool/test_utils.cc
@@ -241,7 +241,7 @@
 
 void MockPooledTaskRunnerDelegate::RemoveJobTaskSource(
     scoped_refptr<JobTaskSource> task_source) {
-  thread_group_->RemoveTaskSource(std::move(task_source));
+  thread_group_->RemoveTaskSource(*task_source);
 }
 
 bool MockPooledTaskRunnerDelegate::IsRunningPoolWithTraits(
diff --git a/base/task/thread_pool/thread_group.cc b/base/task/thread_pool/thread_group.cc
index 7f6dcef..8de5b518 100644
--- a/base/task/thread_pool/thread_group.cc
+++ b/base/task/thread_pool/thread_group.cc
@@ -139,9 +139,9 @@
 }
 
 RegisteredTaskSource ThreadGroup::RemoveTaskSource(
-    scoped_refptr<TaskSource> task_source) {
+    const TaskSource& task_source) {
   CheckedAutoLock auto_lock(lock_);
-  return priority_queue_.RemoveTaskSource(std::move(task_source));
+  return priority_queue_.RemoveTaskSource(task_source);
 }
 
 void ThreadGroup::ReEnqueueTaskSourceLockRequired(
diff --git a/base/task/thread_pool/thread_group.h b/base/task/thread_pool/thread_group.h
index c0075541..4db7bd5f 100644
--- a/base/task/thread_pool/thread_group.h
+++ b/base/task/thread_pool/thread_group.h
@@ -65,7 +65,7 @@
   // RegisteredTaskSource that evaluats to true if successful, or false if
   // |task_source| is not currently in |priority_queue_|, such as when a worker
   // is running a task from it.
-  RegisteredTaskSource RemoveTaskSource(scoped_refptr<TaskSource> task_source);
+  RegisteredTaskSource RemoveTaskSource(const TaskSource& task_source);
 
   // Updates the position of the TaskSource in |transaction| in this
   // ThreadGroup's PriorityQueue based on the TaskSource's current traits.
diff --git a/base/task/thread_pool/thread_pool_impl.cc b/base/task/thread_pool/thread_pool_impl.cc
index f80e7d2..ff1bfa7 100644
--- a/base/task/thread_pool/thread_pool_impl.cc
+++ b/base/task/thread_pool/thread_pool_impl.cc
@@ -431,7 +431,7 @@
   auto transaction = task_source->BeginTransaction();
   ThreadGroup* const current_thread_group =
       GetThreadGroupForTraits(transaction.traits());
-  current_thread_group->RemoveTaskSource(std::move(task_source));
+  current_thread_group->RemoveTaskSource(*task_source);
 }
 
 bool ThreadPoolImpl::IsRunningPoolWithTraits(const TaskTraits& traits) const {
@@ -466,7 +466,7 @@
     // |task_source| is changing thread groups; remove it from its current
     // thread group and reenqueue it.
     auto registered_task_source =
-        current_thread_group->RemoveTaskSource(task_source);
+        current_thread_group->RemoveTaskSource(*task_source);
     if (registered_task_source) {
       DCHECK(task_source);
       new_thread_group->PushTaskSourceAndWakeUpWorkers(
diff --git a/base/test/ios/wait_util.mm b/base/test/ios/wait_util.mm
index c1538c5..6dd93f8 100644
--- a/base/test/ios/wait_util.mm
+++ b/base/test/ios/wait_util.mm
@@ -17,7 +17,7 @@
 namespace ios {
 
 const NSTimeInterval kSpinDelaySeconds = 0.01;
-const NSTimeInterval kWaitForJSCompletionTimeout = 4.0;
+const NSTimeInterval kWaitForJSCompletionTimeout = 6.0;
 const NSTimeInterval kWaitForUIElementTimeout = 4.0;
 const NSTimeInterval kWaitForDownloadTimeout = 10.0;
 const NSTimeInterval kWaitForPageLoadTimeout = 10.0;
diff --git a/base/test/launcher/test_launcher_unittest.cc b/base/test/launcher/test_launcher_unittest.cc
index 1feb57c..72b2916 100644
--- a/base/test/launcher/test_launcher_unittest.cc
+++ b/base/test/launcher/test_launcher_unittest.cc
@@ -231,7 +231,6 @@
                  {"firstTest", "secondTest", "DISABLED_firstTestDisabled"});
   SetUpExpectCalls();
   command_line->AppendSwitchASCII("gtest_filter", "Test*.first*");
-  using ::testing::_;
   std::vector<std::string> tests_names = {"Test.firstTest"};
   using ::testing::_;
   EXPECT_CALL(test_launcher, LaunchChildGTestProcess(
@@ -706,13 +705,13 @@
   // will need to change accordingly.
   std::string file_name = "../../base/test/launcher/test_launcher_unittest.cc";
   EXPECT_TRUE(test_launcher_utils::ValidateTestLocation(
-      val, "MockUnitTests.DISABLED_PassTest", file_name, 660));
+      val, "MockUnitTests.DISABLED_PassTest", file_name, 659));
   EXPECT_TRUE(test_launcher_utils::ValidateTestLocation(
-      val, "MockUnitTests.DISABLED_FailTest", file_name, 664));
+      val, "MockUnitTests.DISABLED_FailTest", file_name, 663));
   EXPECT_TRUE(test_launcher_utils::ValidateTestLocation(
-      val, "MockUnitTests.DISABLED_CrashTest", file_name, 668));
+      val, "MockUnitTests.DISABLED_CrashTest", file_name, 667));
   EXPECT_TRUE(test_launcher_utils::ValidateTestLocation(
-      val, "MockUnitTests.DISABLED_NoRunTest", file_name, 672));
+      val, "MockUnitTests.DISABLED_NoRunTest", file_name, 671));
 
   val = root->FindListKey("per_iteration_data");
   ASSERT_TRUE(val);
diff --git a/base/win/com_init_check_hook.h b/base/win/com_init_check_hook.h
index 304e62a..f143adee 100644
--- a/base/win/com_init_check_hook.h
+++ b/base/win/com_init_check_hook.h
@@ -21,9 +21,8 @@
 // binaries contain a convenient 2 byte hotpatch noop. This doesn't exist in
 // 64-bit binaries.
 
-#if DCHECK_IS_ON() && defined(ARCH_CPU_X86_FAMILY) &&             \
-    defined(ARCH_CPU_32_BITS) && !defined(GOOGLE_CHROME_BUILD) && \
-    !defined(OFFICIAL_BUILD) &&                                   \
+#if DCHECK_IS_ON() && defined(ARCH_CPU_X86_FAMILY) &&        \
+    defined(ARCH_CPU_32_BITS) && !defined(OFFICIAL_BUILD) && \
     !defined(COM_INIT_CHECK_HOOK_DISABLED)  // See crbug/737090 for details.
 #define COM_INIT_CHECK_HOOK_ENABLED
 #endif
diff --git a/base/win/enum_variant.cc b/base/win/enum_variant.cc
index 38861bfe7..9ae35e2f 100644
--- a/base/win/enum_variant.cc
+++ b/base/win/enum_variant.cc
@@ -4,6 +4,8 @@
 
 #include "base/win/enum_variant.h"
 
+#include <wrl/client.h>
+
 #include <algorithm>
 
 #include "base/logging.h"
@@ -26,27 +28,9 @@
   return items_[index].AsInput();
 }
 
-ULONG STDMETHODCALLTYPE EnumVariant::AddRef() {
-  return IUnknownImpl::AddRef();
-}
-
-ULONG STDMETHODCALLTYPE EnumVariant::Release() {
-  return IUnknownImpl::Release();
-}
-
-STDMETHODIMP EnumVariant::QueryInterface(REFIID riid, void** ppv) {
-  if (riid == IID_IEnumVARIANT) {
-    *ppv = static_cast<IEnumVARIANT*>(this);
-    AddRef();
-    return S_OK;
-  }
-
-  return IUnknownImpl::QueryInterface(riid, ppv);
-}
-
-STDMETHODIMP EnumVariant::Next(ULONG requested_count,
-                               VARIANT* out_elements,
-                               ULONG* out_elements_received) {
+HRESULT EnumVariant::Next(ULONG requested_count,
+                          VARIANT* out_elements,
+                          ULONG* out_elements_received) {
   if (!out_elements)
     return E_INVALIDARG;
 
@@ -65,7 +49,7 @@
   return (count == requested_count ? S_OK : S_FALSE);
 }
 
-STDMETHODIMP EnumVariant::Skip(ULONG skip_count) {
+HRESULT EnumVariant::Skip(ULONG skip_count) {
   ULONG count = skip_count;
   if (current_index_ + count > ULONG{items_.size()})
     count = ULONG{items_.size()} - current_index_;
@@ -74,24 +58,23 @@
   return (count == skip_count ? S_OK : S_FALSE);
 }
 
-STDMETHODIMP EnumVariant::Reset() {
+HRESULT EnumVariant::Reset() {
   current_index_ = 0;
   return S_OK;
 }
 
-STDMETHODIMP EnumVariant::Clone(IEnumVARIANT** out_cloned_object) {
+HRESULT EnumVariant::Clone(IEnumVARIANT** out_cloned_object) {
   if (!out_cloned_object)
     return E_INVALIDARG;
 
   size_t count = items_.size();
-  EnumVariant* other = new EnumVariant(ULONG{count});
+  Microsoft::WRL::ComPtr<EnumVariant> other =
+      Microsoft::WRL::Make<EnumVariant>(ULONG{count});
   for (size_t i = 0; i < count; ++i)
     other->items_[i] = static_cast<const VARIANT&>(items_[i]);
 
   other->Skip(current_index_);
-  other->AddRef();
-  *out_cloned_object = other;
-  return S_OK;
+  return other.CopyTo(IID_PPV_ARGS(out_cloned_object));
 }
 
 }  // namespace win
diff --git a/base/win/enum_variant.h b/base/win/enum_variant.h
index 47ffd07..1adf4aac 100644
--- a/base/win/enum_variant.h
+++ b/base/win/enum_variant.h
@@ -5,12 +5,11 @@
 #ifndef BASE_WIN_ENUM_VARIANT_H_
 #define BASE_WIN_ENUM_VARIANT_H_
 
-#include <unknwn.h>
+#include <wrl/implements.h>
 
 #include <memory>
 #include <vector>
 
-#include "base/win/iunknown_impl.h"
 #include "base/win/scoped_variant.h"
 
 namespace base {
@@ -18,29 +17,25 @@
 
 // A simple implementation of IEnumVARIANT.
 class BASE_EXPORT EnumVariant
-  : public IEnumVARIANT,
-    public IUnknownImpl {
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          IEnumVARIANT> {
  public:
   // The constructor allocates a vector of empty ScopedVariants of size |count|.
   // Use ItemAt to set the value of each item in the array.
   explicit EnumVariant(ULONG count);
 
+  // IEnumVARIANT:
+  IFACEMETHODIMP Next(ULONG requested_count,
+                      VARIANT* out_elements,
+                      ULONG* out_elements_received) override;
+  IFACEMETHODIMP Skip(ULONG skip_count) override;
+  IFACEMETHODIMP Reset() override;
+  IFACEMETHODIMP Clone(IEnumVARIANT** out_cloned_object) override;
+
   // Returns a mutable pointer to the item at position |index|.
   VARIANT* ItemAt(ULONG index);
 
-  // IUnknown.
-  ULONG STDMETHODCALLTYPE AddRef() override;
-  ULONG STDMETHODCALLTYPE Release() override;
-  STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
-
-  // IEnumVARIANT.
-  STDMETHODIMP Next(ULONG requested_count,
-                    VARIANT* out_elements,
-                    ULONG* out_elements_received) override;
-  STDMETHODIMP Skip(ULONG skip_count) override;
-  STDMETHODIMP Reset() override;
-  STDMETHODIMP Clone(IEnumVARIANT** out_cloned_object) override;
-
  private:
   ~EnumVariant() override;
 
diff --git a/base/win/enum_variant_unittest.cc b/base/win/enum_variant_unittest.cc
index 77b2fde..3165b5e 100644
--- a/base/win/enum_variant_unittest.cc
+++ b/base/win/enum_variant_unittest.cc
@@ -4,7 +4,11 @@
 
 #include "base/win/enum_variant.h"
 
+#include <wrl/client.h>
+#include <wrl/implements.h>
+
 #include "base/win/scoped_com_initializer.h"
+#include "base/win/scoped_variant.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -13,52 +17,34 @@
 TEST(EnumVariantTest, EmptyEnumVariant) {
   ScopedCOMInitializer com_initializer;
 
-  EnumVariant* ev = new EnumVariant(0);
-  ev->AddRef();
+  Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(0);
+  Microsoft::WRL::ComPtr<IEnumVARIANT> ienumvariant;
+  ASSERT_TRUE(SUCCEEDED(ev->QueryInterface(IID_PPV_ARGS(&ienumvariant))));
 
-  IUnknown* iunknown;
-  EXPECT_TRUE(SUCCEEDED(
-      ev->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(&iunknown))));
-  iunknown->Release();
-
-  IEnumVARIANT* ienumvariant;
-  EXPECT_TRUE(SUCCEEDED(
-      ev->QueryInterface(IID_IEnumVARIANT,
-                         reinterpret_cast<void**>(&ienumvariant))));
-  EXPECT_EQ(ev, ienumvariant);
-  ienumvariant->Release();
-
-  VARIANT out_element;
-  ::VariantInit(&out_element);
-  ULONG out_received = 0;
-  EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
-  EXPECT_EQ(0u, out_received);
-  ::VariantClear(&out_element);
+  {
+    base::win::ScopedVariant out_element;
+    ULONG out_received = 0;
+    EXPECT_EQ(S_FALSE, ev->Next(1, out_element.Receive(), &out_received));
+    EXPECT_EQ(0u, out_received);
+  }
 
   EXPECT_EQ(S_FALSE, ev->Skip(1));
 
   EXPECT_EQ(S_OK, ev->Reset());
 
-  IEnumVARIANT* ev2 = NULL;
+  Microsoft::WRL::ComPtr<IEnumVARIANT> ev2;
   EXPECT_EQ(S_OK, ev->Clone(&ev2));
 
-  EXPECT_NE(static_cast<IEnumVARIANT*>(NULL), ev2);
+  EXPECT_NE(nullptr, ev2);
   EXPECT_NE(ev, ev2);
   EXPECT_EQ(S_FALSE, ev2->Skip(1));
   EXPECT_EQ(S_OK, ev2->Reset());
-
-  ULONG ev2_finalrefcount = ev2->Release();
-  EXPECT_EQ(0u, ev2_finalrefcount);
-
-  ULONG ev_finalrefcount = ev->Release();
-  EXPECT_EQ(0u, ev_finalrefcount);
 }
 
 TEST(EnumVariantTest, SimpleEnumVariant) {
   ScopedCOMInitializer com_initializer;
 
-  EnumVariant* ev = new EnumVariant(3);
-  ev->AddRef();
+  Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(3);
   ev->ItemAt(0)->vt = VT_I4;
   ev->ItemAt(0)->lVal = 10;
   ev->ItemAt(1)->vt = VT_I4;
@@ -66,31 +52,36 @@
   ev->ItemAt(2)->vt = VT_I4;
   ev->ItemAt(2)->lVal = 30;
 
-  // Get elements one at a time.
-  VARIANT out_element;
-  ::VariantInit(&out_element);
-  ULONG out_received = 0;
-  EXPECT_EQ(S_OK, ev->Next(1, &out_element, &out_received));
-  EXPECT_EQ(1u, out_received);
-  EXPECT_EQ(VT_I4, out_element.vt);
-  EXPECT_EQ(10, out_element.lVal);
-  ::VariantClear(&out_element);
-  EXPECT_EQ(S_OK, ev->Skip(1));
-  EXPECT_EQ(S_OK, ev->Next(1, &out_element, &out_received));
-  EXPECT_EQ(1u, out_received);
-  EXPECT_EQ(VT_I4, out_element.vt);
-  EXPECT_EQ(30, out_element.lVal);
-  ::VariantClear(&out_element);
-  EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
-  ::VariantClear(&out_element);
+  // Get elements one at a time from index 0 and 2.
+  base::win::ScopedVariant out_element_0;
+  ULONG out_received_0 = 0;
+  EXPECT_EQ(S_OK, ev->Next(1, out_element_0.Receive(), &out_received_0));
+  EXPECT_EQ(1u, out_received_0);
+  EXPECT_EQ(VT_I4, out_element_0.ptr()->vt);
+  EXPECT_EQ(10, out_element_0.ptr()->lVal);
 
-  // Reset and get all elements at once.
+  EXPECT_EQ(S_OK, ev->Skip(1));
+
+  base::win::ScopedVariant out_element_2;
+  ULONG out_received_2 = 0;
+  EXPECT_EQ(S_OK, ev->Next(1, out_element_2.Receive(), &out_received_2));
+  EXPECT_EQ(1u, out_received_2);
+  EXPECT_EQ(VT_I4, out_element_2.ptr()->vt);
+  EXPECT_EQ(30, out_element_2.ptr()->lVal);
+
+  base::win::ScopedVariant placeholder_variant;
+  EXPECT_EQ(S_FALSE, ev->Next(1, placeholder_variant.Receive(), nullptr));
+
+  // Verify the reset works for the next step.
+  ASSERT_EQ(S_OK, ev->Reset());
+
+  // Get all elements at once.
   VARIANT out_elements[3];
+  ULONG out_received_multiple;
   for (int i = 0; i < 3; ++i)
     ::VariantInit(&out_elements[i]);
-  EXPECT_EQ(S_OK, ev->Reset());
-  EXPECT_EQ(S_OK, ev->Next(3, out_elements, &out_received));
-  EXPECT_EQ(3u, out_received);
+  EXPECT_EQ(S_OK, ev->Next(3, out_elements, &out_received_multiple));
+  EXPECT_EQ(3u, out_received_multiple);
   EXPECT_EQ(VT_I4, out_elements[0].vt);
   EXPECT_EQ(10, out_elements[0].lVal);
   EXPECT_EQ(VT_I4, out_elements[1].vt);
@@ -99,16 +90,31 @@
   EXPECT_EQ(30, out_elements[2].lVal);
   for (int i = 0; i < 3; ++i)
     ::VariantClear(&out_elements[i]);
-  EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
-  ::VariantClear(&out_element);
+
+  base::win::ScopedVariant placeholder_variant_multiple;
+  EXPECT_EQ(S_FALSE,
+            ev->Next(1, placeholder_variant_multiple.Receive(), nullptr));
+}
+
+TEST(EnumVariantTest, Clone) {
+  ScopedCOMInitializer com_initializer;
+
+  Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(3);
+  ev->ItemAt(0)->vt = VT_I4;
+  ev->ItemAt(0)->lVal = 10;
+  ev->ItemAt(1)->vt = VT_I4;
+  ev->ItemAt(1)->lVal = 20;
+  ev->ItemAt(2)->vt = VT_I4;
+  ev->ItemAt(2)->lVal = 30;
 
   // Clone it.
-  IEnumVARIANT* ev2 = NULL;
+  Microsoft::WRL::ComPtr<IEnumVARIANT> ev2;
   EXPECT_EQ(S_OK, ev->Clone(&ev2));
   EXPECT_TRUE(ev2 != NULL);
-  EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
-  ::VariantClear(&out_element);
-  EXPECT_EQ(S_OK, ev2->Reset());
+
+  VARIANT out_elements[3];
+  for (int i = 0; i < 3; ++i)
+    ::VariantInit(&out_elements[i]);
   EXPECT_EQ(S_OK, ev2->Next(3, out_elements, nullptr));
   EXPECT_EQ(VT_I4, out_elements[0].vt);
   EXPECT_EQ(10, out_elements[0].lVal);
@@ -118,14 +124,6 @@
   EXPECT_EQ(30, out_elements[2].lVal);
   for (int i = 0; i < 3; ++i)
     ::VariantClear(&out_elements[i]);
-  EXPECT_EQ(S_FALSE, ev2->Next(1, &out_element, nullptr));
-  ::VariantClear(&out_element);
-
-  ULONG ev2_finalrefcount = ev2->Release();
-  EXPECT_EQ(0u, ev2_finalrefcount);
-
-  ULONG ev_finalrefcount = ev->Release();
-  EXPECT_EQ(0u, ev_finalrefcount);
 }
 
 }  // namespace win
diff --git a/base/win/iunknown_impl.cc b/base/win/iunknown_impl.cc
deleted file mode 100644
index 2a88439..0000000
--- a/base/win/iunknown_impl.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/win/iunknown_impl.h"
-
-namespace base {
-namespace win {
-
-IUnknownImpl::IUnknownImpl()
-    : ref_count_(0) {
-}
-
-IUnknownImpl::~IUnknownImpl() {
-}
-
-ULONG STDMETHODCALLTYPE IUnknownImpl::AddRef() {
-  ref_count_.Increment();
-  return 1;
-}
-
-ULONG STDMETHODCALLTYPE IUnknownImpl::Release() {
-  if (!ref_count_.Decrement()) {
-    delete this;
-    return 0;
-  }
-  return 1;
-}
-
-STDMETHODIMP IUnknownImpl::QueryInterface(REFIID riid, void** ppv) {
-  if (riid == IID_IUnknown) {
-    *ppv = static_cast<IUnknown*>(this);
-    AddRef();
-    return S_OK;
-  }
-
-  *ppv = NULL;
-  return E_NOINTERFACE;
-}
-
-}  // namespace win
-}  // namespace base
diff --git a/base/win/iunknown_impl.h b/base/win/iunknown_impl.h
deleted file mode 100644
index b7de205..0000000
--- a/base/win/iunknown_impl.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_WIN_IUNKNOWN_IMPL_H_
-#define BASE_WIN_IUNKNOWN_IMPL_H_
-
-#include <unknwn.h>
-
-#include "base/atomic_ref_count.h"
-#include "base/base_export.h"
-#include "base/compiler_specific.h"
-
-namespace base {
-namespace win {
-
-// IUnknown implementation for other classes to derive from.
-class BASE_EXPORT IUnknownImpl : public IUnknown {
- public:
-  IUnknownImpl();
-
-  ULONG STDMETHODCALLTYPE AddRef() override;
-  ULONG STDMETHODCALLTYPE Release() override;
-
-  // Subclasses should extend this to return any interfaces they provide.
-  STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
-
- protected:
-  virtual ~IUnknownImpl();
-
- private:
-  AtomicRefCount ref_count_;
-};
-
-}  // namespace win
-}  // namespace base
-
-#endif  // BASE_WIN_IUNKNOWN_IMPL_H_
diff --git a/base/win/iunknown_impl_unittest.cc b/base/win/iunknown_impl_unittest.cc
deleted file mode 100644
index c6c35390..0000000
--- a/base/win/iunknown_impl_unittest.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/win/iunknown_impl.h"
-
-#include "base/win/scoped_com_initializer.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace win {
-
-class TestIUnknownImplSubclass : public IUnknownImpl {
- public:
-  TestIUnknownImplSubclass() {
-    ++instance_count;
-  }
-  ~TestIUnknownImplSubclass() override { --instance_count; }
-  static int instance_count;
-};
-
-// static
-int TestIUnknownImplSubclass::instance_count = 0;
-
-TEST(IUnknownImplTest, IUnknownImpl) {
-  ScopedCOMInitializer com_initializer;
-
-  EXPECT_EQ(0, TestIUnknownImplSubclass::instance_count);
-  IUnknown* u = new TestIUnknownImplSubclass();
-
-  EXPECT_EQ(1, TestIUnknownImplSubclass::instance_count);
-
-  EXPECT_EQ(1u, u->AddRef());
-  EXPECT_EQ(1u, u->AddRef());
-
-  IUnknown* other = NULL;
-  EXPECT_EQ(E_NOINTERFACE, u->QueryInterface(
-      IID_IDispatch, reinterpret_cast<void**>(&other)));
-  EXPECT_EQ(S_OK, u->QueryInterface(
-      IID_IUnknown, reinterpret_cast<void**>(&other)));
-  other->Release();
-
-  EXPECT_EQ(1u, u->Release());
-  EXPECT_EQ(0u, u->Release());
-  EXPECT_EQ(0, TestIUnknownImplSubclass::instance_count);
-}
-
-}  // namespace win
-}  // namespace base
diff --git a/base/win/pe_image_unittest.cc b/base/win/pe_image_unittest.cc
index caf241b8d..45279c3 100644
--- a/base/win/pe_image_unittest.cc
+++ b/base/win/pe_image_unittest.cc
@@ -182,29 +182,19 @@
   const char kTargetModuleStatic[] = "user32.dll";
   const char kTargetModuleDelay[] = "cfgmgr32.dll";
 
-#if defined(ARCH_CPU_ARM64)
-  const int kSections = 7;
-  const int kImportsDlls = 2;
+  const int kImportsDlls = 1;
   const int kDelayDlls = 1;
   const int kExports = 3;
   const int kImports = 2;
   const int kDelayImports = 1;
+#if defined(ARCH_CPU_ARM64)
+  const int kSections = 7;
   const int kRelocs = 740;
 #elif defined(ARCH_CPU_64_BITS)
   const int kSections = 6;
-  const int kImportsDlls = 1;
-  const int kDelayDlls = 1;
-  const int kExports = 3;
-  const int kImports = 2;
-  const int kDelayImports = 1;
   const int kRelocs = 976;
 #else
   const int kSections = 5;
-  const int kImportsDlls = 1;
-  const int kDelayDlls = 1;
-  const int kExports = 3;
-  const int kImports = 2;
-  const int kDelayImports = 1;
   const int kRelocs = 2114;
 #endif
 
diff --git a/build/android/gyp/bundletool.py b/build/android/gyp/bundletool.py
index 2201cc1..65be46e 100755
--- a/build/android/gyp/bundletool.py
+++ b/build/android/gyp/bundletool.py
@@ -13,6 +13,8 @@
 import subprocess
 import sys
 
+from util import build_utils
+
 # Assume this is stored under build/android/gyp/
 BUNDLETOOL_DIR = os.path.abspath(os.path.join(
     __file__, '..', '..', '..', '..', 'third_party', 'android_build_tools',
@@ -24,7 +26,7 @@
     BUNDLETOOL_DIR, 'bundletool-all-%s.jar' % BUNDLETOOL_VERSION)
 
 def RunBundleTool(args):
-  args = ['java', '-jar', BUNDLETOOL_JAR_PATH] + args
+  args = [build_utils.JAVA_PATH, '-jar', BUNDLETOOL_JAR_PATH] + args
   logging.debug(' '.join(args))
   subprocess.check_call(args)
 
diff --git a/build/android/gyp/create_app_bundle.py b/build/android/gyp/create_app_bundle.py
index eeb665bb..ae3f4662 100755
--- a/build/android/gyp/create_app_bundle.py
+++ b/build/android/gyp/create_app_bundle.py
@@ -411,12 +411,17 @@
     with open(tmp_bundle_config, 'w') as f:
       f.write(bundle_config)
 
-    cmd_args = ['java', '-jar', bundletool.BUNDLETOOL_JAR_PATH, 'build-bundle']
-    cmd_args += ['--modules=%s' % ','.join(module_zips)]
-    cmd_args += ['--output=%s' % tmp_unsigned_bundle]
-    cmd_args += ['--config=%s' % tmp_bundle_config]
+    cmd_args = [
+        build_utils.JAVA_PATH, '-jar', bundletool.BUNDLETOOL_JAR_PATH,
+        'build-bundle', '--modules=' + ','.join(module_zips),
+        '--output=' + tmp_unsigned_bundle, '--config=' + tmp_bundle_config
+    ]
 
-    build_utils.CheckOutput(cmd_args, print_stdout=True, print_stderr=True)
+    build_utils.CheckOutput(
+        cmd_args,
+        print_stdout=True,
+        print_stderr=True,
+        stderr_filter=build_utils.FilterReflectiveAccessJavaWarnings)
 
     if options.keystore_path:
       # NOTE: As stated by the public documentation, apksigner cannot be used
diff --git a/build/android/gyp/desugar.py b/build/android/gyp/desugar.py
index b9d04059..407b8f2c 100755
--- a/build/android/gyp/desugar.py
+++ b/build/android/gyp/desugar.py
@@ -31,7 +31,7 @@
   options.classpath = build_utils.ParseGnList(options.classpath)
 
   cmd = [
-      'java',
+      build_utils.JAVA_PATH,
       '-jar',
       options.desugar_jar,
       '--input',
@@ -46,7 +46,10 @@
     cmd += ['--bootclasspath_entry', path]
   for path in options.classpath:
     cmd += ['--classpath_entry', path]
-  build_utils.CheckOutput(cmd, print_stdout=False)
+  build_utils.CheckOutput(
+      cmd,
+      print_stdout=False,
+      stderr_filter=build_utils.FilterReflectiveAccessJavaWarnings)
 
   if options.depfile:
     build_utils.WriteDepfile(
diff --git a/build/android/gyp/dex.py b/build/android/gyp/dex.py
index 0994f48..043a08a 100755
--- a/build/android/gyp/dex.py
+++ b/build/android/gyp/dex.py
@@ -404,7 +404,10 @@
     final_dex_inputs = list(options.class_inputs)
   final_dex_inputs += options.dex_inputs
 
-  dex_cmd = ['java', '-jar', options.r8_jar_path, 'd8', '--no-desugaring']
+  dex_cmd = [
+      build_utils.JAVA_PATH, '-jar', options.r8_jar_path, 'd8',
+      '--no-desugaring'
+  ]
   if options.release:
     dex_cmd += ['--release']
   if options.min_api:
diff --git a/build/android/gyp/dexsplitter.py b/build/android/gyp/dexsplitter.py
index 926d2cd..7bbc066 100755
--- a/build/android/gyp/dexsplitter.py
+++ b/build/android/gyp/dexsplitter.py
@@ -49,7 +49,7 @@
 
 def _RunDexsplitter(options, output_dir):
   cmd = [
-      'java',
+      build_utils.JAVA_PATH,
       '-jar',
       options.r8_path,
       'dexsplitter',
diff --git a/build/android/gyp/jacoco_instr.py b/build/android/gyp/jacoco_instr.py
index 9c55fb3..9f4f55f 100755
--- a/build/android/gyp/jacoco_instr.py
+++ b/build/android/gyp/jacoco_instr.py
@@ -215,7 +215,9 @@
     source_files.extend(build_utils.ReadSourcesList(args.java_sources_file))
 
   with build_utils.TempDir() as temp_dir:
-    instrument_cmd = ['java', '-jar', args.jacococli_jar, 'instrument']
+    instrument_cmd = [
+        build_utils.JAVA_PATH, '-jar', args.jacococli_jar, 'instrument'
+    ]
 
     if not args.files_to_instrument:
       _InstrumentWholeJar(instrument_cmd, args.input_path, args.output_path,
diff --git a/build/android/gyp/main_dex_list.py b/build/android/gyp/main_dex_list.py
index 646250e..b75f8ee6 100755
--- a/build/android/gyp/main_dex_list.py
+++ b/build/android/gyp/main_dex_list.py
@@ -55,7 +55,7 @@
 def main():
   args = _ParseArgs()
   proguard_cmd = [
-      'java',
+      build_utils.JAVA_PATH,
       '-jar',
       args.r8_path,
       '--classfile',
@@ -105,7 +105,7 @@
       # Step 2: Expand inclusion list to all classes referenced by the .class
       # files of kept classes (non-recursive).
       main_dex_list_cmd = [
-          'java',
+          build_utils.JAVA_PATH,
           '-cp',
           args.dx_path,
           'com.android.multidex.MainDexListBuilder',
diff --git a/build/android/gyp/merge_manifest.py b/build/android/gyp/merge_manifest.py
index 5680ad9..61840bf6 100755
--- a/build/android/gyp/merge_manifest.py
+++ b/build/android/gyp/merge_manifest.py
@@ -92,7 +92,7 @@
 
   with build_utils.AtomicOutput(args.output) as output:
     cmd = [
-        'java',
+        build_utils.JAVA_PATH,
         '-cp',
         classpath,
         _MANIFEST_MERGER_MAIN_CLASS,
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index 091d7d7..fc93644 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -176,7 +176,7 @@
       os.mkdir(tmp_output)
 
     cmd = [
-        'java',
+        build_utils.JAVA_PATH,
         '-jar',
         options.r8_path,
         '--no-desugaring',
@@ -246,7 +246,7 @@
 
     if options.proguard_path.endswith('.jar'):
       cmd = [
-          'java', '-jar', options.proguard_path, '-include',
+          build_utils.JAVA_PATH, '-jar', options.proguard_path, '-include',
           combined_proguard_configs_path
       ]
     else:
diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py
index f27b884..ca7f0c1 100644
--- a/build/android/gyp/util/build_utils.py
+++ b/build/android/gyp/util/build_utils.py
@@ -33,6 +33,8 @@
 DIR_SOURCE_ROOT = os.environ.get('CHECKOUT_SOURCE_ROOT',
     os.path.abspath(os.path.join(os.path.dirname(__file__),
                                  os.pardir, os.pardir, os.pardir, os.pardir)))
+JAVA_PATH = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'jdk', 'current',
+                         'bin', 'java')
 
 try:
   string_types = basestring
@@ -206,6 +208,26 @@
       line for line in output.splitlines() if not re_filter.search(line))
 
 
+def FilterReflectiveAccessJavaWarnings(output):
+  """Filters out warnings about illegal reflective access operation.
+
+  These warnings were introduced in Java 9, and generally mean that dependencies
+  need to be updated.
+  """
+  #  WARNING: An illegal reflective access operation has occurred
+  #  WARNING: Illegal reflective access by ...
+  #  WARNING: Please consider reporting this to the maintainers of ...
+  #  WARNING: Use --illegal-access=warn to enable warnings of further ...
+  #  WARNING: All illegal access operations will be denied in a future release
+  return FilterLines(
+      output, r'WARNING: ('
+      'An illegal reflective|'
+      'Illegal reflective access|'
+      'Please consider reporting this to|'
+      'Use --illegal-access=warn|'
+      'All illegal access operations)')
+
+
 # This can be used in most cases like subprocess.check_output(). The output,
 # particularly when the command fails, better highlights the command's failure.
 # If the command fails, raises a build_utils.CalledProcessError.
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index c94ed05..c4d3afa 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -2102,10 +2102,6 @@
   #       Path for the generated map between original resource paths and
   #       shortend resource paths.
   #
-  #   srcjar_path: (optional)
-  #       Path to a generated .srcjar containing the generated R.java sources
-  #       for all dependent resource libraries.
-  #
   #   proguard_file: (optional)
   #       Path to proguard configuration file for this apk target.
   #
@@ -2119,10 +2115,12 @@
     if (defined(invoker.optimized_arsc_output)) {
       _optimized_arsc_output = invoker.optimized_arsc_output
     }
-    _srcjar_path = invoker.srcjar_path
+    _final_srcjar_path = "${target_gen_dir}/${target_name}.srcjar"
+    _intermediate_srcjar_path = _final_srcjar_path
     if (defined(invoker.post_process_script)) {
       _compile_resources_target_name = "${target_name}__intermediate"
-      _srcjar_path = "${_srcjar_path}.intermediate.srcjar"
+      _intermediate_srcjar_path =
+          "${target_gen_dir}/${target_name}.intermediate.srcjar"
       _intermediate_path =
           get_path_info(_arsc_output, "dir") + "/" +
           get_path_info(_arsc_output, "name") + ".intermediate.ap_"
@@ -2177,13 +2175,13 @@
 
       inputs += [ invoker.android_manifest ]
       outputs = [
-        _srcjar_path,
+        _intermediate_srcjar_path,
       ]
       args += [
         "--android-manifest",
         rebase_path(invoker.android_manifest, root_build_dir),
         "--srcjar-out",
-        rebase_path(_srcjar_path, root_build_dir),
+        rebase_path(_intermediate_srcjar_path, root_build_dir),
       ]
       if (defined(invoker.no_xml_namespaces) && invoker.no_xml_namespaces) {
         args += [ "--no-xml-namespaces" ]
@@ -2429,9 +2427,9 @@
           "--depfile",
           rebase_path(depfile, root_build_dir),
           "--srcjar-in",
-          rebase_path(_srcjar_path, root_build_dir),
+          rebase_path(_intermediate_srcjar_path, root_build_dir),
           "--srcjar-out",
-          rebase_path(invoker.srcjar_path, root_build_dir),
+          rebase_path(_final_srcjar_path, root_build_dir),
         ]
         if (defined(_optimized_arsc_output)) {
           _input_apk = _optimized_arsc_output
@@ -2456,14 +2454,14 @@
         }
         inputs = [
           _input_apk,
-          _srcjar_path,
+          _intermediate_srcjar_path,
         ]
         if (defined(invoker.post_process_script_inputs)) {
           inputs += invoker.post_process_script_inputs
         }
         outputs = [
           _output_apk,
-          invoker.srcjar_path,
+          _final_srcjar_path,
         ]
         public_deps = [
           ":${_compile_resources_target_name}",
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index aeff5719..4eb0322 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1404,7 +1404,6 @@
     compile_resources(_compile_resources_target) {
       deps = _deps + [ ":$_build_config_target_name" ]
       build_config = _build_config
-      srcjar_path = "${target_gen_dir}/${target_name}.srcjar"
       if (defined(_package_name)) {
         rename_manifest_package = _package_name
       }
@@ -2503,7 +2502,6 @@
       if (defined(invoker.post_process_package_resources_script)) {
         post_process_script = invoker.post_process_package_resources_script
       }
-      srcjar_path = "${target_gen_dir}/${target_name}.srcjar"
       r_text_out_path = _compile_resources_rtxt_out
       emit_ids_out_path = _compile_resources_emit_ids_out
       size_info_path = _res_size_info_path
diff --git a/build/config/chromecast_build.gni b/build/config/chromecast_build.gni
index 3702acc..b2640f5 100644
--- a/build/config/chromecast_build.gni
+++ b/build/config/chromecast_build.gni
@@ -41,9 +41,10 @@
 }
 
 declare_args() {
-  # True to enable the cast renderer.  It is enabled by default for non-android
-  # builds.
-  enable_cast_renderer = is_chromecast && !is_android
+  # True to enable the cast renderer.  It is enabled by default for linux and
+  # android audio only builds.
+  enable_cast_renderer =
+      is_chromecast && (is_linux || (is_cast_audio_only && is_android))
 }
 
 # Configures media options for cast.  See media/media_options.gni
@@ -51,22 +52,30 @@
 cast_mojo_media_host = "none"
 
 if (enable_cast_renderer) {
+  # In this path, mojo media services are hosted in two processes:
+  # 1. "renderer" and "cdm" run in browser process. This is hard coded in the
+  # code.
+  # 2. "video_decoder" runs in the process specified by "cast_mojo_media_host".
   cast_mojo_media_services = [
     "cdm",
     "renderer",
   ]
-  cast_mojo_media_host = "browser"
-} else if (is_android) {
+
+  if (!is_cast_audio_only) {
+    cast_mojo_media_services += [ "video_decoder" ]
+  }
+
+  cast_mojo_media_host = "gpu"
+} else if (is_android && !is_cast_audio_only) {
+  # On Android, all the enabled mojo media services run in the process specified
+  # by "cast_mojo_media_host".
   cast_mojo_media_services = [
     "cdm",
     "audio_decoder",
+    "video_decoder",
   ]
-  if (is_cast_audio_only) {
-    cast_mojo_media_host = "browser"
-  } else {
-    cast_mojo_media_services += [ "video_decoder" ]
-    cast_mojo_media_host = "gpu"
-  }
+
+  cast_mojo_media_host = "gpu"
 }
 
 # Assert that Chromecast is being built for a supported platform.
diff --git a/build/config/jumbo.gni b/build/config/jumbo.gni
index c93d5db..9c1685691 100644
--- a/build/config/jumbo.gni
+++ b/build/config/jumbo.gni
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/nacl/config.gni")  # To see if jumbo should be turned off
-import("//build/split_static_library.gni")  # When someone uses that target_type
 import("//build/toolchain/goma.gni")
 
 declare_args() {
@@ -49,9 +48,9 @@
 # Goma builds benefit from more parallelism
 jumbo_file_merge_goma = 8
 
-# Use one of the targets jumbo_source_set, jumbo_static_library,
-# jumbo_split_static_library or jumbo_component to generate a target
-# which merges sources if possible to compile much faster.
+# Use one of the targets jumbo_source_set, jumbo_static_library, or
+# jumbo_component to generate a target which merges sources if possible to
+# compile much faster.
 #
 # Special values.
 #
@@ -214,11 +213,6 @@
   }
 
   target_type = invoker.target_type
-  if (use_jumbo_build_for_target && target_type == "split_static_library") {
-    # Meaningless and also impossible if split_count > len(jumbo_files)
-    target_type = "static_library"
-    not_needed(invoker, [ "split_count" ])
-  }
 
   # Perform the actual operation, either on the original sources or
   # the sources post-jumbo merging.
@@ -281,22 +275,6 @@
 }
 
 # See documentation above by "internal_jumbo_target".
-template("jumbo_split_static_library") {
-  internal_jumbo_target(target_name) {
-    target_type = "split_static_library"
-    forward_variables_from(invoker, "*")
-  }
-}
-
-set_defaults("jumbo_split_static_library") {
-  # This sets the default list of configs when the
-  # jumbo_split_static_library target is defined. The
-  # default_compiler_configs comes from BUILDCONFIG.gn and is the list
-  # normally applied to static libraries and source sets.
-  configs = default_compiler_configs
-}
-
-# See documentation above by "internal_jumbo_target".
 template("jumbo_component") {
   internal_jumbo_target(target_name) {
     target_type = "component"
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 47f8142d..6ea1b65 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8899475862982290896
\ No newline at end of file
+8899502756487089920
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 7612fdc..f17fc2b9 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8899474127257994368
\ No newline at end of file
+8899505638123521760
\ No newline at end of file
diff --git a/build/toolchain/clang_code_coverage_wrapper.py b/build/toolchain/clang_code_coverage_wrapper.py
index 7d5daa4..36a5e9aa 100755
--- a/build/toolchain/clang_code_coverage_wrapper.py
+++ b/build/toolchain/clang_code_coverage_wrapper.py
@@ -58,21 +58,21 @@
     '-mllvm', '-limited-coverage-experimental=true'
 ]
 
+# Files that should not be built with coverage flags by default.
+_DEFAULT_COVERAGE_EXCLUSION_LIST = []
+
 # Map of exclusion lists indexed by target OS.
 # If no target OS is defined, or one is defined that doesn't have a specific
-# entry, use the 'default' exclusion_list. Anything added to 'default' will
-# apply to all platforms that don't have their own specific list.
+# entry, use _DEFAULT_COVERAGE_EXCLUSION_LIST.
 _COVERAGE_EXCLUSION_LIST_MAP = {
-    'default': [],
     'linux': [
         # These files caused a static initializer to be generated, which
         # shouldn't.
         # TODO(crbug.com/990948): Remove when the bug is fixed.
-        '../../chrome/browser/media/router/providers/cast/cast_internal_message_util.cc', #pylint: disable=line-too-long
+        '../../chrome/browser/media/router/providers/cast/cast_internal_message_util.cc',  #pylint: disable=line-too-long
         '../../chrome/common/media_router/providers/cast/cast_media_source.cc',
         '../../components/cast_channel/cast_channel_enum.cc',
-        '../../components/cast_channel/cast_message_util.cc'
-
+        '../../components/cast_channel/cast_message_util.cc',
     ],
     'chromeos': [
         # These files caused clang to crash while compiling them. They are
@@ -81,10 +81,12 @@
         '../../third_party/icu/source/common/uts46.cpp',
         '../../third_party/icu/source/common/ucnvmbcs.cpp',
         '../../base/android/android_image_reader_compat.cc',
-    ]
+    ],
+    'win': [],
 }
 
 
+
 def _remove_flags_from_command(command):
   # We need to remove the coverage flags for this file, but we only want to
   # remove them if we see the exact sequence defined in _COVERAGE_FLAGS.
@@ -128,25 +130,27 @@
   if not any('clang' in s for s in compile_command):
     return subprocess.call(compile_command)
 
+  target_os = parsed_args.target_os
+
   try:
     # The command is assumed to use Clang as the compiler, and the path to the
     # source file is behind the -c argument, and the path to the source path is
     # relative to the root build directory. For example:
     # clang++ -fvisibility=hidden -c ../../base/files/file_path.cc -o \
     #   obj/base/base/file_path.o
-    index_dash_c = compile_command.index('-c')
+    # On Windows, clang-cl.exe uses /c instead of -c.
+    source_flag = '/c' if target_os == 'win' else '-c'
+    source_flag_index = compile_command.index(source_flag)
   except ValueError:
-    print('-c argument is not found in the compile command.')
+    print('%s argument is not found in the compile command.' % source_flag)
     raise
 
-  if index_dash_c + 1 >= len(compile_command):
+  if source_flag_index + 1 >= len(compile_command):
     raise Exception('Source file to be compiled is missing from the command.')
 
-  compile_source_file = compile_command[index_dash_c + 1]
-  target_os = parsed_args.target_os
-  if target_os not in _COVERAGE_EXCLUSION_LIST_MAP:
-    target_os = 'default'
-  exclusion_list = _COVERAGE_EXCLUSION_LIST_MAP[target_os]
+  compile_source_file = compile_command[source_flag_index + 1]
+  exclusion_list = _COVERAGE_EXCLUSION_LIST_MAP.get(
+      target_os, _DEFAULT_COVERAGE_EXCLUSION_LIST)
 
   if compile_source_file in exclusion_list:
     _remove_flags_from_command(compile_command)
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn
index d6c9561..25e95f1 100644
--- a/build/toolchain/win/BUILD.gn
+++ b/build/toolchain/win/BUILD.gn
@@ -147,6 +147,38 @@
       sys_lib_flags = ""
     }
 
+    if (defined(toolchain_args.use_clang_coverage)) {
+      toolchain_use_clang_coverage = toolchain_args.use_clang_coverage
+    } else {
+      toolchain_use_clang_coverage = use_clang_coverage
+    }
+
+    if (toolchain_use_clang_coverage) {
+      assert(toolchain_args.is_clang,
+             "use_clang_coverage should only be used with Clang")
+      if (defined(toolchain_args.coverage_instrumentation_input_file)) {
+        toolchain_coverage_instrumentation_input_file =
+            toolchain_args.coverage_instrumentation_input_file
+      } else {
+        toolchain_coverage_instrumentation_input_file =
+            coverage_instrumentation_input_file
+      }
+
+      coverage_wrapper =
+          rebase_path("//build/toolchain/clang_code_coverage_wrapper.py",
+                      root_build_dir)
+      coverage_wrapper = coverage_wrapper + " --target-os=" + target_os
+      if (toolchain_coverage_instrumentation_input_file != "") {
+        coverage_wrapper =
+            coverage_wrapper + " --files-to-instrument=" +
+            rebase_path(toolchain_coverage_instrumentation_input_file,
+                        root_build_dir)
+      }
+      coverage_wrapper = "$python_path " + coverage_wrapper + " "
+    } else {
+      coverage_wrapper = ""
+    }
+
     tool("cc") {
       precompiled_header_type = "msvc"
       pdbname = "{{target_out_dir}}/{{label_name}}_c.pdb"
@@ -160,7 +192,7 @@
         "$object_subdir/{{source_name_part}}.obj",
       ]
 
-      command = "$env_wrapper$cl /nologo /showIncludes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
+      command = "$coverage_wrapper$env_wrapper$cl /nologo /showIncludes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
     }
 
     tool("cxx") {
@@ -176,7 +208,7 @@
         "$object_subdir/{{source_name_part}}.obj",
       ]
 
-      command = "$env_wrapper$cl /nologo /showIncludes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
+      command = "$coverage_wrapper$env_wrapper$cl /nologo /showIncludes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
     }
 
     tool("rc") {
@@ -369,6 +401,7 @@
           forward_variables_from(invoker.toolchain_args, "*")
         }
         is_clang = false
+        use_clang_coverage = false
         current_os = "win"
         current_cpu = toolchain_arch
       }
diff --git a/build_overrides/dawn.gni b/build_overrides/dawn.gni
index 873c59a..ba0c606 100644
--- a/build_overrides/dawn.gni
+++ b/build_overrides/dawn.gni
@@ -6,6 +6,9 @@
 dawn_glfw_dir = "//third_party/glfw/src"
 dawn_jinja2_dir = "//third_party/jinja2"
 dawn_googletest_dir = "//third_party/googletest/src"
+dawn_jsoncpp_dir = "//third_party/jsoncpp"
 dawn_shaderc_dir = "//third_party/shaderc/src"
 dawn_spirv_cross_dir = "//third_party/spirv-cross/spirv-cross"
 dawn_spirv_tools_dir = "//third_party/SPIRV-Tools/src"
+dawn_vulkan_validation_layers_dir =
+    "//third_party/angle/third_party/vulkan-validation-layers/src"
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index fe3b9bb..da40ffa 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -1,6 +1,7 @@
 # Copyright 2014 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+import("//build/config/sanitizers/sanitizers.gni")
 import("//gpu/vulkan/features.gni")
 
 import("//cc/cc.gni")
diff --git a/cc/animation/animation.cc b/cc/animation/animation.cc
index 2e4c5eb6..9d32d45 100644
--- a/cc/animation/animation.cc
+++ b/cc/animation/animation.cc
@@ -311,6 +311,13 @@
   return count;
 }
 
+bool Animation::AffectsCustomProperty() const {
+  for (const auto& keyframe_effect : keyframe_effects_)
+    if (keyframe_effect->AffectsCustomProperty())
+      return true;
+  return false;
+}
+
 void Animation::SetNeedsCommit() {
   DCHECK(animation_host_);
   animation_host_->SetNeedsCommit();
diff --git a/cc/animation/animation.h b/cc/animation/animation.h
index 8cc336c..06df055 100644
--- a/cc/animation/animation.h
+++ b/cc/animation/animation.h
@@ -95,8 +95,7 @@
 
   virtual void PushPropertiesTo(Animation* animation_impl);
 
-  virtual void UpdateState(bool start_ready_keyframe_models,
-                           AnimationEvents* events);
+  void UpdateState(bool start_ready_keyframe_models, AnimationEvents* events);
   virtual void Tick(base::TimeTicks monotonic_time);
 
   void AddToTicking();
@@ -108,6 +107,7 @@
   void NotifyKeyframeModelAborted(const AnimationEvent& event);
   void NotifyKeyframeModelTakeover(const AnimationEvent& event);
   size_t TickingKeyframeModelsCount() const;
+  bool AffectsCustomProperty() const;
 
   void SetNeedsPushProperties();
 
diff --git a/cc/animation/animation_delegate.h b/cc/animation/animation_delegate.h
index 0adbb11..013e3431 100644
--- a/cc/animation/animation_delegate.h
+++ b/cc/animation/animation_delegate.h
@@ -35,8 +35,6 @@
       int target_property,
       base::TimeTicks animation_start_time,
       std::unique_ptr<AnimationCurve> curve) = 0;
-  virtual void NotifyLocalTimeUpdated(
-      base::Optional<base::TimeDelta> local_time) = 0;
 
  protected:
   virtual ~AnimationDelegate() {}
diff --git a/cc/animation/animation_events.cc b/cc/animation/animation_events.cc
index 192800e..4d9387aa 100644
--- a/cc/animation/animation_events.cc
+++ b/cc/animation/animation_events.cc
@@ -13,25 +13,10 @@
                                base::TimeTicks monotonic_time)
     : type(type),
       element_id(element_id),
-      worklet_animation_id(),
       group_id(group_id),
       target_property(target_property),
       monotonic_time(monotonic_time),
-      is_impl_only(false),
-      opacity(0.f),
-      local_time() {}
-
-AnimationEvent::AnimationEvent(WorkletAnimationId worklet_animation_id,
-                               base::Optional<base::TimeDelta> local_time)
-    : type(TIME_UPDATED),
-      element_id(),
-      worklet_animation_id(worklet_animation_id),
-      group_id(),
-      target_property(),
-      monotonic_time(),
-      is_impl_only(false),
-      opacity(0.f),
-      local_time(local_time) {}
+      is_impl_only(false) {}
 
 AnimationEvent::AnimationEvent(const AnimationEvent& other) {
   type = other.type;
@@ -40,14 +25,9 @@
   target_property = other.target_property;
   monotonic_time = other.monotonic_time;
   is_impl_only = other.is_impl_only;
-  opacity = other.opacity;
-  transform = other.transform;
-  filters = other.filters;
   animation_start_time = other.animation_start_time;
   if (other.curve)
     curve = other.curve->Clone();
-  worklet_animation_id = other.worklet_animation_id;
-  local_time = other.local_time;
 }
 
 AnimationEvent& AnimationEvent::operator=(const AnimationEvent& other) {
@@ -57,14 +37,9 @@
   target_property = other.target_property;
   monotonic_time = other.monotonic_time;
   is_impl_only = other.is_impl_only;
-  opacity = other.opacity;
-  transform = other.transform;
-  filters = other.filters;
   animation_start_time = other.animation_start_time;
   if (other.curve)
     curve = other.curve->Clone();
-  worklet_animation_id = other.worklet_animation_id;
-  local_time = other.local_time;
   return *this;
 }
 
diff --git a/cc/animation/animation_events.h b/cc/animation/animation_events.h
index 8ac1ab71..cec7616 100644
--- a/cc/animation/animation_events.h
+++ b/cc/animation/animation_events.h
@@ -12,14 +12,12 @@
 #include "cc/animation/animation_export.h"
 #include "cc/animation/keyframe_model.h"
 #include "cc/paint/element_id.h"
-#include "cc/paint/filter_operations.h"
 #include "cc/trees/mutator_host.h"
-#include "ui/gfx/transform.h"
 
 namespace cc {
 
 struct CC_ANIMATION_EXPORT AnimationEvent {
-  enum Type { STARTED, FINISHED, ABORTED, TAKEOVER, TIME_UPDATED };
+  enum Type { STARTED, FINISHED, ABORTED, TAKEOVER };
 
   AnimationEvent(Type type,
                  ElementId element_id,
@@ -27,38 +25,21 @@
                  int target_property,
                  base::TimeTicks monotonic_time);
 
-  // Constructs AnimationEvent of TIME_UPDATED type.
-  AnimationEvent(WorkletAnimationId worklet_animation_id,
-                 base::Optional<base::TimeDelta> local_time);
-
   AnimationEvent(const AnimationEvent& other);
   AnimationEvent& operator=(const AnimationEvent& other);
 
   ~AnimationEvent();
 
   Type type;
-  // Either element_id or worklet_animation_id are set. worklet_animation_id is
-  // set for TIME_UPDATED event types, element_id is set for other events.
-  // TODO(http://crbug.com/1013727): Make all animation events use animation id
-  // to do targeting;
   ElementId element_id;
-  WorkletAnimationId worklet_animation_id;
   int group_id;
   int target_property;
   base::TimeTicks monotonic_time;
   bool is_impl_only;
-  // TODO(http://crbug.com/1013715): Remove unused members (opacity, transform,
-  // filters).
-  float opacity;
-  gfx::Transform transform;
-  FilterOperations filters;
 
   // For continuing a scroll offset animation on the main thread.
   base::TimeTicks animation_start_time;
   std::unique_ptr<AnimationCurve> curve;
-
-  // Set for TIME_UPDATED events.
-  base::Optional<base::TimeDelta> local_time;
 };
 
 class CC_ANIMATION_EXPORT AnimationEvents : public MutatorEvents {
diff --git a/cc/animation/animation_host.cc b/cc/animation/animation_host.cc
index 077d8b62..2e4dbb4 100644
--- a/cc/animation/animation_host.cc
+++ b/cc/animation/animation_host.cc
@@ -483,32 +483,6 @@
   return std::make_unique<AnimationEvents>();
 }
 
-void DispatchAnimationEventToElementAnimations(ElementAnimations* animations,
-                                               const AnimationEvent& event) {
-  switch (event.type) {
-    case AnimationEvent::STARTED:
-      animations->NotifyAnimationStarted(event);
-      break;
-
-    case AnimationEvent::FINISHED:
-      animations->NotifyAnimationFinished(event);
-      break;
-
-    case AnimationEvent::ABORTED:
-      animations->NotifyAnimationAborted(event);
-      break;
-
-    case AnimationEvent::TAKEOVER:
-      animations->NotifyAnimationTakeover(event);
-      break;
-
-    case AnimationEvent::TIME_UPDATED:
-      // Worklet animations are looked up in ticking animations.
-      NOTREACHED();
-      break;
-  }
-}
-
 void AnimationHost::SetAnimationEvents(
     std::unique_ptr<MutatorEvents> mutator_events) {
   auto events =
@@ -516,26 +490,31 @@
 
   for (size_t event_index = 0; event_index < events->events_.size();
        ++event_index) {
-    AnimationEvent& event = events->events_[event_index];
+    ElementId element_id = events->events_[event_index].element_id;
 
-    // TODO(http://crbug.com/1013727): Make all animation events use animation
-    // id to do targeting;
-    if (event.worklet_animation_id) {
-      DCHECK(event.type == AnimationEvent::TIME_UPDATED);
-      // Look up worklet animation in ticking animations.
-      WorkletAnimation* animation =
-          FindWorkletAnimation(event.worklet_animation_id);
-      if (animation)
-        animation->NotifyLocalTimeUpdated(event);
-    } else {
-      // Use the map of all ElementAnimations, not just ticking animations,
-      // since non-ticking animations may still receive events for impl-only
-      // animations.
-      const ElementToAnimationsMap& all_element_animations =
-          element_to_animations_map_;
-      auto iter = all_element_animations.find(event.element_id);
-      if (iter != all_element_animations.end())
-        DispatchAnimationEventToElementAnimations((*iter).second.get(), event);
+    // Use the map of all ElementAnimations, not just ticking animations, since
+    // non-ticking animations may still receive events for impl-only animations.
+    const ElementToAnimationsMap& all_element_animations =
+        element_to_animations_map_;
+    auto iter = all_element_animations.find(element_id);
+    if (iter != all_element_animations.end()) {
+      switch (events->events_[event_index].type) {
+        case AnimationEvent::STARTED:
+          (*iter).second->NotifyAnimationStarted(events->events_[event_index]);
+          break;
+
+        case AnimationEvent::FINISHED:
+          (*iter).second->NotifyAnimationFinished(events->events_[event_index]);
+          break;
+
+        case AnimationEvent::ABORTED:
+          (*iter).second->NotifyAnimationAborted(events->events_[event_index]);
+          break;
+
+        case AnimationEvent::TAKEOVER:
+          (*iter).second->NotifyAnimationTakeover(events->events_[event_index]);
+          break;
+      }
     }
   }
 }
@@ -751,20 +730,6 @@
   mutator_->SetClient(this);
 }
 
-WorkletAnimation* AnimationHost::FindWorkletAnimation(WorkletAnimationId id) {
-  // TODO(majidvp): Use a map to make lookup O(1)
-  auto animation = std::find_if(
-      ticking_animations_.begin(), ticking_animations_.end(), [id](auto& it) {
-        return it->IsWorkletAnimation() &&
-               ToWorkletAnimation(it.get())->worklet_animation_id() == id;
-      });
-
-  if (animation == ticking_animations_.end())
-    return nullptr;
-
-  return ToWorkletAnimation(animation->get());
-}
-
 void AnimationHost::SetMutationUpdate(
     std::unique_ptr<MutatorOutputState> output_state) {
   if (!output_state)
@@ -774,9 +739,17 @@
   for (auto& animation_state : output_state->animations) {
     WorkletAnimationId id = animation_state.worklet_animation_id;
 
-    WorkletAnimation* to_update = FindWorkletAnimation(id);
-    if (to_update)
-      to_update->SetOutputState(animation_state);
+    // TODO(majidvp): Use a map to make lookup O(1)
+    auto to_update = std::find_if(
+        ticking_animations_.begin(), ticking_animations_.end(), [id](auto& it) {
+          return it->IsWorkletAnimation() &&
+                 ToWorkletAnimation(it.get())->worklet_animation_id() == id;
+        });
+
+    if (to_update == ticking_animations_.end())
+      continue;
+
+    ToWorkletAnimation(to_update->get())->SetOutputState(animation_state);
   }
 }
 
@@ -817,6 +790,13 @@
   return main_thread_animations_count_;
 }
 
+bool AnimationHost::HasCustomPropertyAnimations() const {
+  for (const auto& it : ticking_animations_)
+    if (it->AffectsCustomProperty())
+      return true;
+  return false;
+}
+
 bool AnimationHost::CurrentFrameHadRAF() const {
   return current_frame_had_raf_;
 }
diff --git a/cc/animation/animation_host.h b/cc/animation/animation_host.h
index 1e0c185..fa0a1a5 100644
--- a/cc/animation/animation_host.h
+++ b/cc/animation/animation_host.h
@@ -32,7 +32,6 @@
 class KeyframeEffect;
 class ScrollOffsetAnimations;
 class ScrollOffsetAnimationsImpl;
-class WorkletAnimation;
 
 enum class ThreadInstance { MAIN, IMPL };
 
@@ -209,6 +208,7 @@
 
   size_t CompositedAnimationsCount() const override;
   size_t MainThreadAnimationsCount() const override;
+  bool HasCustomPropertyAnimations() const override;
   bool CurrentFrameHadRAF() const override;
   bool NextFrameHasPendingRAF() const override;
   void SetAnimationCounts(size_t total_animations_count,
@@ -235,10 +235,6 @@
       const ScrollTree& scroll_tree,
       bool is_active_tree);
 
-  // Returns a pointer to a worklet animation by worklet animation id or null
-  // if there is no match.
-  WorkletAnimation* FindWorkletAnimation(WorkletAnimationId id);
-
   ElementToAnimationsMap element_to_animations_map_;
   AnimationsList ticking_animations_;
 
diff --git a/cc/animation/animation_host_unittest.cc b/cc/animation/animation_host_unittest.cc
index 45fd8cc..206919e 100644
--- a/cc/animation/animation_host_unittest.cc
+++ b/cc/animation/animation_host_unittest.cc
@@ -177,7 +177,7 @@
 
   // Ticking host should cause layer tree mutator to update output state which
   // should take effect in the same animation frame.
-  TickAnimationsTransferEvents(base::TimeTicks(), 1u);
+  TickAnimationsTransferEvents(base::TimeTicks(), 0u);
 
   // Emulate behavior in PrepareToDraw. Animation worklet updates are best
   // effort, and the animation tick is deferred until draw to allow time for the
diff --git a/cc/animation/keyframe_effect.cc b/cc/animation/keyframe_effect.cc
index 7a24722..ecbd64a 100644
--- a/cc/animation/keyframe_effect.cc
+++ b/cc/animation/keyframe_effect.cc
@@ -468,6 +468,13 @@
   return ticking_keyframe_models_count;
 }
 
+bool KeyframeEffect::AffectsCustomProperty() const {
+  for (const auto& it : keyframe_models_)
+    if (it->target_property_id() == TargetProperty::CSS_CUSTOM_PROPERTY)
+      return true;
+  return false;
+}
+
 bool KeyframeEffect::HasNonDeletedKeyframeModel() const {
   for (const auto& keyframe_model : keyframe_models_) {
     if (keyframe_model->run_state() != KeyframeModel::WAITING_FOR_DELETION)
@@ -1094,9 +1101,8 @@
       animation_->NotifyKeyframeModelAborted(event);
       break;
     case AnimationEvent::TAKEOVER:
-    case AnimationEvent::TIME_UPDATED:
-      // We never expect to receive a TAKEOVER or TIME_UPDATED
-      // notifications on impl only animations.
+      // We never expect to receive a TAKEOVER notification on impl only
+      // animations.
       NOTREACHED();
       break;
   }
diff --git a/cc/animation/keyframe_effect.h b/cc/animation/keyframe_effect.h
index e4b0029..e3816763 100644
--- a/cc/animation/keyframe_effect.h
+++ b/cc/animation/keyframe_effect.h
@@ -119,6 +119,8 @@
   bool HasTickingKeyframeModel() const;
   size_t TickingKeyframeModelsCount() const;
 
+  bool AffectsCustomProperty() const;
+
   bool HasNonDeletedKeyframeModel() const;
 
   bool AnimationsPreserveAxisAlignment() const;
diff --git a/cc/animation/scroll_offset_animations_impl.h b/cc/animation/scroll_offset_animations_impl.h
index 3086c8e..4371deed 100644
--- a/cc/animation/scroll_offset_animations_impl.h
+++ b/cc/animation/scroll_offset_animations_impl.h
@@ -75,8 +75,6 @@
                                base::TimeTicks animation_start_time,
                                std::unique_ptr<AnimationCurve> curve) override {
   }
-  void NotifyLocalTimeUpdated(
-      base::Optional<base::TimeDelta> local_time) override {}
 
   bool IsAnimating() const;
 
diff --git a/cc/animation/worklet_animation.cc b/cc/animation/worklet_animation.cc
index 77de1e9..a6d109fa 100644
--- a/cc/animation/worklet_animation.cc
+++ b/cc/animation/worklet_animation.cc
@@ -5,7 +5,6 @@
 #include "cc/animation/worklet_animation.h"
 
 #include <utility>
-#include "cc/animation/animation_delegate.h"
 #include "cc/animation/animation_id_provider.h"
 #include "cc/animation/keyframe_effect.h"
 #include "cc/animation/scroll_timeline.h"
@@ -51,7 +50,6 @@
       options_(std::move(options)),
       effect_timings_(std::move(effect_timings)),
       local_time_(base::nullopt),
-      last_synced_local_time_(base::nullopt),
       start_time_(base::nullopt),
       last_current_time_(base::nullopt),
       has_pending_tree_lock_(false),
@@ -109,19 +107,6 @@
   keyframe_effect()->Tick(monotonic_time);
 }
 
-void WorkletAnimation::UpdateState(bool start_ready_animations,
-                                   AnimationEvents* events) {
-  Animation::UpdateState(start_ready_animations, events);
-  if (last_synced_local_time_ != local_time_) {
-    AnimationEvent event(worklet_animation_id_, local_time_);
-    // TODO(http://crbug.com/1013654): Instead of pushing multiple events per
-    // single main frame, push just the recent one to be handled by next main
-    // frame.
-    events->events_.push_back(event);
-    last_synced_local_time_ = local_time_;
-  }
-}
-
 void WorkletAnimation::UpdateInputState(MutatorInputState* input_state,
                                         base::TimeTicks monotonic_time,
                                         const ScrollTree& scroll_tree,
@@ -146,9 +131,6 @@
 
   DCHECK(is_timeline_active || state_ == State::REMOVED);
 
-  // TODO(https://crbug.com/1011138): Initialize current_time to null if the
-  // timeline is inactive. It might be inactive here when state is
-  // State::REMOVED.
   base::Optional<base::TimeDelta> current_time =
       CurrentTime(monotonic_time, scroll_tree, is_active_tree);
 
@@ -159,8 +141,6 @@
   if (!is_timeline_active)
     current_time = last_current_time_;
 
-  // TODO(https://crbug.com/1011138): Do not early exit if state is
-  // State::REMOVED. The animation must be removed in this case.
   if (!current_time)
     return;
   last_current_time_ = current_time;
@@ -201,11 +181,6 @@
   local_time_ = state.local_times[0];
 }
 
-void WorkletAnimation::NotifyLocalTimeUpdated(const AnimationEvent& event) {
-  if (animation_delegate_)
-    animation_delegate_->NotifyLocalTimeUpdated(event.local_time);
-}
-
 void WorkletAnimation::SetPlaybackRate(double playback_rate) {
   if (playback_rate == playback_rate_)
     return;
diff --git a/cc/animation/worklet_animation.h b/cc/animation/worklet_animation.h
index b653fbc..508c0be4 100644
--- a/cc/animation/worklet_animation.h
+++ b/cc/animation/worklet_animation.h
@@ -64,9 +64,6 @@
 
   void Tick(base::TimeTicks monotonic_time) override;
 
-  void UpdateState(bool start_ready_animations,
-                   AnimationEvents* events) override;
-
   void UpdateInputState(MutatorInputState* input_state,
                         base::TimeTicks monotonic_time,
                         const ScrollTree& scroll_tree,
@@ -92,7 +89,6 @@
   }
 
   void RemoveKeyframeModel(int keyframe_model_id) override;
-  void NotifyLocalTimeUpdated(const AnimationEvent& event);
 
   void ReleasePendingTreeLock() { has_pending_tree_lock_ = false; }
 
@@ -134,7 +130,7 @@
   }
 
   // Updates the playback rate of the Impl thread instance.
-  // Called by the UI thread WorkletAnimation instance during commit.
+  // Called by the UI thread WorletAnimation instance during commit.
   void SetPlaybackRate(double playback_rate);
 
   bool IsTimelineActive(const ScrollTree& scroll_tree,
@@ -167,11 +163,6 @@
   // The value comes from the user script that runs inside the animation worklet
   // global scope.
   base::Optional<base::TimeDelta> local_time_;
-  // Local time passed to the main thread worklet animation to update its
-  // keyframe effect. We only set the most recent local time, meaning that if
-  // there are multiple compositor frames without a single main frame only
-  // the local time associated with the latest frame is sent to the main thread.
-  base::Optional<base::TimeDelta> last_synced_local_time_;
 
   base::Optional<base::TimeTicks> start_time_;
 
diff --git a/cc/animation/worklet_animation_unittest.cc b/cc/animation/worklet_animation_unittest.cc
index 5671294d..4d0e61e 100644
--- a/cc/animation/worklet_animation_unittest.cc
+++ b/cc/animation/worklet_animation_unittest.cc
@@ -112,52 +112,6 @@
                                        expected_opacity);
 }
 
-// Test generation of animation events by worklet animations.
-TEST_F(WorkletAnimationTest, AnimationEventLocalTimeUpdate) {
-  AttachWorkletAnimation();
-
-  base::Optional<base::TimeDelta> local_time = base::TimeDelta::FromSecondsD(1);
-  MutatorOutputState::AnimationState state(worklet_animation_id_);
-  state.local_times.push_back(local_time);
-  worklet_animation_->SetOutputState(state);
-
-  std::unique_ptr<MutatorEvents> mutator_events = host_->CreateEvents();
-  auto* animation_events = static_cast<AnimationEvents*>(mutator_events.get());
-  worklet_animation_->UpdateState(true, animation_events);
-
-  // One event is generated as a result of update state.
-  EXPECT_EQ(1u, animation_events->events_.size());
-  AnimationEvent event = animation_events->events_[0];
-  EXPECT_EQ(AnimationEvent::TIME_UPDATED, event.type);
-  EXPECT_EQ(worklet_animation_id_, event.worklet_animation_id);
-  EXPECT_EQ(local_time, event.local_time);
-
-  // If the state is not updated no more events is generated.
-  mutator_events = host_->CreateEvents();
-  animation_events = static_cast<AnimationEvents*>(mutator_events.get());
-  worklet_animation_->UpdateState(true, animation_events);
-  EXPECT_EQ(0u, animation_events->events_.size());
-
-  // If local time is set to the same value no event is generated.
-  worklet_animation_->SetOutputState(state);
-  mutator_events = host_->CreateEvents();
-  animation_events = static_cast<AnimationEvents*>(mutator_events.get());
-  worklet_animation_->UpdateState(true, animation_events);
-  EXPECT_EQ(0u, animation_events->events_.size());
-
-  // If local time is set to null value, an animation event with null local
-  // time is generated.
-  state.local_times.clear();
-  local_time = base::nullopt;
-  state.local_times.push_back(local_time);
-  worklet_animation_->SetOutputState(state);
-  mutator_events = host_->CreateEvents();
-  animation_events = static_cast<AnimationEvents*>(mutator_events.get());
-  worklet_animation_->UpdateState(true, animation_events);
-  EXPECT_EQ(1u, animation_events->events_.size());
-  EXPECT_EQ(local_time, animation_events->events_[0].local_time);
-}
-
 TEST_F(WorkletAnimationTest, CurrentTimeCorrectlyUsesScrollTimeline) {
   auto scroll_timeline = std::make_unique<MockScrollTimeline>();
   EXPECT_CALL(*scroll_timeline, IsActive(_, _)).WillRepeatedly(Return(true));
diff --git a/cc/input/scrollbar_controller.cc b/cc/input/scrollbar_controller.cc
index 60b167e..2f4221e 100644
--- a/cc/input/scrollbar_controller.cc
+++ b/cc/input/scrollbar_controller.cc
@@ -402,15 +402,6 @@
   return scaled_scroller_to_scrollbar_ratio;
 }
 
-void ScrollbarController::DidUnregisterScrollbar(ElementId element_id) {
-  if (currently_captured_scrollbar_ &&
-      currently_captured_scrollbar_->scroll_element_id() == element_id)
-    currently_captured_scrollbar_ = nullptr;
-    drag_processed_for_current_frame_ = false;
-    drag_state_ = base::nullopt;
-    autoscroll_state_ = base::nullopt;
-}
-
 void ScrollbarController::RecomputeAutoscrollStateIfNeeded() {
   if (!autoscroll_state_.has_value())
     return;
diff --git a/cc/input/scrollbar_controller.h b/cc/input/scrollbar_controller.h
index 3c44689..e1b13dc2 100644
--- a/cc/input/scrollbar_controller.h
+++ b/cc/input/scrollbar_controller.h
@@ -38,7 +38,6 @@
   ScrollbarOrientation orientation() {
     return currently_captured_scrollbar_->orientation();
   }
-  void DidUnregisterScrollbar(ElementId element_id);
 
   void WillBeginImplFrame();
 
diff --git a/cc/metrics/compositor_timing_history.cc b/cc/metrics/compositor_timing_history.cc
index 9e7b2ac..6991812 100644
--- a/cc/metrics/compositor_timing_history.cc
+++ b/cc/metrics/compositor_timing_history.cc
@@ -48,13 +48,15 @@
   virtual void AddDrawDuration(base::TimeDelta duration) = 0;
   virtual void AddSubmitToAckLatency(base::TimeDelta duration) = 0;
 
-  // crbug.com/758439: the following 3 functions are used to report timing in
+  // crbug.com/758439: the following functions are used to report timing in
   // certain conditions targeting blink / compositor animations.
   // Only the renderer would get the meaningful data.
   virtual void AddDrawIntervalWithCompositedAnimations(
       base::TimeDelta duration) = 0;
   virtual void AddDrawIntervalWithMainThreadAnimations(
       base::TimeDelta duration) = 0;
+  virtual void AddDrawIntervalWithCustomPropertyAnimations(
+      base::TimeDelta duration) = 0;
 
   // Synchronization measurements
   virtual void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) = 0;
@@ -329,6 +331,13 @@
         "Scheduling.Renderer.DrawIntervalWithMainThreadAnimations", interval);
   }
 
+  void AddDrawIntervalWithCustomPropertyAnimations(
+      base::TimeDelta interval) override {
+    UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED(
+        "Scheduling.Renderer.DrawIntervalWithCustomPropertyAnimations",
+        interval);
+  }
+
   void AddBeginImplFrameLatency(base::TimeDelta delta) override {
     UMA_HISTOGRAM_CUSTOM_TIMES_DURATION(
         "Scheduling.Renderer.BeginImplFrameLatency", delta);
@@ -432,6 +441,9 @@
   void AddDrawIntervalWithMainThreadAnimations(
       base::TimeDelta interval) override {}
 
+  void AddDrawIntervalWithCustomPropertyAnimations(
+      base::TimeDelta interval) override {}
+
   void AddBeginImplFrameLatency(base::TimeDelta delta) override {
     UMA_HISTOGRAM_CUSTOM_TIMES_DURATION(
         "Scheduling.Browser.BeginImplFrameLatency", delta);
@@ -507,6 +519,8 @@
       base::TimeDelta inverval) override {}
   void AddDrawIntervalWithMainThreadAnimations(
       base::TimeDelta inverval) override {}
+  void AddDrawIntervalWithCustomPropertyAnimations(
+      base::TimeDelta inverval) override {}
   void AddBeginImplFrameLatency(base::TimeDelta delta) override {}
   void AddBeginMainFrameQueueDurationCriticalDuration(
       base::TimeDelta duration) override {}
@@ -964,7 +978,8 @@
                                       size_t composited_animations_count,
                                       size_t main_thread_animations_count,
                                       bool current_frame_had_raf,
-                                      bool next_frame_has_pending_raf) {
+                                      bool next_frame_has_pending_raf,
+                                      bool has_custom_property_animations) {
   DCHECK_NE(base::TimeTicks(), draw_start_time_);
   base::TimeTicks draw_end_time = Now();
   base::TimeDelta draw_duration = draw_end_time - draw_start_time_;
@@ -989,8 +1004,13 @@
     if (composited_animations_count > 0 &&
         previous_frame_had_composited_animations_)
       uma_reporter_->AddDrawIntervalWithCompositedAnimations(draw_interval);
+    if (has_custom_property_animations &&
+        previous_frame_had_custom_property_animations_)
+      uma_reporter_->AddDrawIntervalWithCustomPropertyAnimations(draw_interval);
   }
   previous_frame_had_composited_animations_ = composited_animations_count > 0;
+  previous_frame_had_custom_property_animations_ =
+      has_custom_property_animations;
   draw_end_time_prev_ = draw_end_time;
 
   if (used_new_active_tree) {
diff --git a/cc/metrics/compositor_timing_history.h b/cc/metrics/compositor_timing_history.h
index 3907a6b9..c39f69a 100644
--- a/cc/metrics/compositor_timing_history.h
+++ b/cc/metrics/compositor_timing_history.h
@@ -91,7 +91,8 @@
                size_t composited_animations_count,
                size_t main_thread_animations_count,
                bool current_frame_had_raf,
-               bool next_frame_has_pending_raf);
+               bool next_frame_has_pending_raf,
+               bool has_custom_property_animations);
   void DidSubmitCompositorFrame(uint32_t frame_token);
   void DidReceiveCompositorFrameAck();
   void DidPresentCompositorFrame(uint32_t frame_token,
@@ -176,6 +177,7 @@
   // Used only for reporting animation targeted UMA.
   bool previous_frame_had_composited_animations_ = false;
   bool previous_frame_had_main_thread_animations_ = false;
+  bool previous_frame_had_custom_property_animations_ = false;
   bool previous_frame_had_raf_ = false;
 
   TreePriority tree_priority_ = SAME_PRIORITY_FOR_BOTH_TREES;
diff --git a/cc/metrics/compositor_timing_history_unittest.cc b/cc/metrics/compositor_timing_history_unittest.cc
index c0fba798..81f72e6 100644
--- a/cc/metrics/compositor_timing_history_unittest.cc
+++ b/cc/metrics/compositor_timing_history_unittest.cc
@@ -53,6 +53,7 @@
 
   base::TimeTicks Now() { return now_; }
 
+  // TODO(xidachen): the composited_animations_count should just be 0.
   void DrawMainFrame(int advance_ms,
                      int composited_animations_count,
                      int main_thread_animations_count,
@@ -69,12 +70,13 @@
     AdvanceNowBy(base::TimeDelta::FromMicroseconds(advance_ms));
     timing_history_.DidDraw(true, Now(), composited_animations_count,
                             main_thread_animations_count, current_frame_had_raf,
-                            next_frame_has_pending_raf);
+                            next_frame_has_pending_raf, false);
   }
 
   void DrawImplFrame(int advance_ms,
                      int composited_animations_count,
-                     int main_thread_animations_count) {
+                     int main_thread_animations_count,
+                     bool has_custom_property_animation) {
     timing_history_.WillBeginMainFrame(true, Now());
     timing_history_.BeginMainFrameStarted(Now());
     timing_history_.BeginMainFrameAborted();
@@ -83,7 +85,8 @@
     timing_history_.WillDraw();
     AdvanceNowBy(base::TimeDelta::FromMicroseconds(advance_ms));
     timing_history_.DidDraw(false, Now(), composited_animations_count,
-                            main_thread_animations_count, false, false);
+                            main_thread_animations_count, false, false,
+                            has_custom_property_animation);
   }
 
  protected:
@@ -137,7 +140,7 @@
   AdvanceNowBy(one_second);
   timing_history_.WillDraw();
   AdvanceNowBy(draw_duration);
-  timing_history_.DidDraw(true, Now(), 0, 0, false, false);
+  timing_history_.DidDraw(true, Now(), 0, 0, false, false, false);
 
   EXPECT_EQ(begin_main_frame_queue_duration,
             timing_history_.BeginMainFrameQueueDurationCriticalEstimate());
@@ -188,7 +191,7 @@
   AdvanceNowBy(one_second);
   timing_history_.WillDraw();
   AdvanceNowBy(draw_duration);
-  timing_history_.DidDraw(false, Now(), 0, 0, false, false);
+  timing_history_.DidDraw(false, Now(), 0, 0, false, false, false);
 
   EXPECT_EQ(base::TimeDelta(),
             timing_history_.BeginMainFrameQueueDurationCriticalEstimate());
@@ -417,10 +420,10 @@
 TEST_F(CompositorTimingHistoryTest, AnimationsWithNewActiveTreeNotUsed) {
   base::HistogramTester histogram_tester;
 
-  DrawImplFrame(123, 1, 1);
+  DrawImplFrame(123, 1, 1, false);
   TestAnimationUMA(histogram_tester, 0, 0);
 
-  DrawImplFrame(456, 1, 0);
+  DrawImplFrame(456, 1, 0, false);
   TestAnimationUMA(histogram_tester, 1, 0);
   histogram_tester.ExpectBucketCount(
       "Scheduling.Renderer.DrawIntervalWithCompositedAnimations2", 456, 1);
@@ -430,7 +433,7 @@
 
   // This frame verifies that we record that there is a composited animation,
   // so in the next frame when there is a composited animation, we report it.
-  DrawImplFrame(234, 1, 1);
+  DrawImplFrame(234, 1, 1, false);
   TestAnimationUMA(histogram_tester, 1, 0);
 
   // Even though the previous frame had no main thread animation, we report it
@@ -445,11 +448,36 @@
   histogram_tester.ExpectBucketCount(
       "Scheduling.Renderer.DrawIntervalWithMainThreadAnimations2", 888, 1);
 
-  DrawImplFrame(123, 1, 0);
+  DrawImplFrame(123, 1, 0, false);
   TestAnimationUMA(histogram_tester, 3, 1);
   histogram_tester.ExpectBucketCount(
       "Scheduling.Renderer.DrawIntervalWithCompositedAnimations2", 123, 1);
 }
 
+TEST_F(CompositorTimingHistoryTest, CustomPropertyAnimations) {
+  base::HistogramTester histogram_tester;
+
+  DrawImplFrame(123, 1, 0, true);
+  TestAnimationUMA(histogram_tester, 0, 0);
+
+  DrawImplFrame(456, 1, 0, true);
+  TestAnimationUMA(histogram_tester, 1, 0);
+
+  histogram_tester.ExpectBucketCount(
+      "Scheduling.Renderer.DrawIntervalWithCompositedAnimations2", 456, 1);
+  histogram_tester.ExpectBucketCount(
+      "Scheduling.Renderer.DrawIntervalWithCustomPropertyAnimations2", 456, 1);
+
+  DrawImplFrame(1234, 1, 0, false);
+  DrawImplFrame(2345, 1, 0, true);
+  TestAnimationUMA(histogram_tester, 3, 0);
+  histogram_tester.ExpectBucketCount(
+      "Scheduling.Renderer.DrawIntervalWithCompositedAnimations2", 2345, 1);
+  // This impl frame does have custom property animation, but the previous impl
+  // frame doesn't, so we won't report it.
+  histogram_tester.ExpectBucketCount(
+      "Scheduling.Renderer.DrawIntervalWithCustomPropertyAnimations2", 2345, 0);
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 984fdc2..523b09c 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -731,8 +731,9 @@
       drawing_with_new_active_tree,
       begin_impl_frame_tracker_.DangerousMethodCurrentOrLast().frame_time,
       client_->CompositedAnimationsCount(),
-      client_->MainThreadAnimationsCount(),
-      client_->CurrentFrameHadRAF(), client_->NextFrameHasPendingRAF());
+      client_->MainThreadAnimationsCount(), client_->CurrentFrameHadRAF(),
+      client_->NextFrameHasPendingRAF(),
+      client_->HasCustomPropertyAnimations());
 }
 
 void Scheduler::DrawForced() {
@@ -749,8 +750,9 @@
       drawing_with_new_active_tree,
       begin_impl_frame_tracker_.DangerousMethodCurrentOrLast().frame_time,
       client_->CompositedAnimationsCount(),
-      client_->MainThreadAnimationsCount(),
-      client_->CurrentFrameHadRAF(), client_->NextFrameHasPendingRAF());
+      client_->MainThreadAnimationsCount(), client_->CurrentFrameHadRAF(),
+      client_->NextFrameHasPendingRAF(),
+      client_->HasCustomPropertyAnimations());
 }
 
 void Scheduler::SetDeferBeginMainFrame(bool defer_begin_main_frame) {
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index 6b2620f..23571c15 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -67,6 +67,7 @@
   // Functions used for reporting animation targeting UMA, crbug.com/758439.
   virtual size_t CompositedAnimationsCount() const = 0;
   virtual size_t MainThreadAnimationsCount() const = 0;
+  virtual bool HasCustomPropertyAnimations() const = 0;
   virtual bool CurrentFrameHadRAF() const = 0;
   virtual bool NextFrameHasPendingRAF() const = 0;
 
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 5f9a722..5437a54 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -259,6 +259,7 @@
 
   size_t CompositedAnimationsCount() const override { return 0; }
   size_t MainThreadAnimationsCount() const override { return 0; }
+  bool HasCustomPropertyAnimations() const override { return false; }
   bool CurrentFrameHadRAF() const override { return false; }
   bool NextFrameHasPendingRAF() const override { return false; }
 
diff --git a/cc/test/animation_timelines_test_common.cc b/cc/test/animation_timelines_test_common.cc
index 8ddf05b..74630d4 100644
--- a/cc/test/animation_timelines_test_common.cc
+++ b/cc/test/animation_timelines_test_common.cc
@@ -405,9 +405,6 @@
   takeover_ = true;
 }
 
-void TestAnimationDelegate::NotifyLocalTimeUpdated(
-    base::Optional<base::TimeDelta> local_time) {}
-
 AnimationTimelinesTest::AnimationTimelinesTest()
     : client_(ThreadInstance::MAIN),
       client_impl_(ThreadInstance::IMPL),
diff --git a/cc/test/animation_timelines_test_common.h b/cc/test/animation_timelines_test_common.h
index 34041fd..3cd113f4 100644
--- a/cc/test/animation_timelines_test_common.h
+++ b/cc/test/animation_timelines_test_common.h
@@ -242,8 +242,6 @@
                                int target_property,
                                base::TimeTicks animation_start_time,
                                std::unique_ptr<AnimationCurve> curve) override;
-  void NotifyLocalTimeUpdated(
-      base::Optional<base::TimeDelta> local_time) override;
 
   bool started() { return started_; }
 
diff --git a/cc/test/test_hooks.h b/cc/test/test_hooks.h
index 956049a..befc238c 100644
--- a/cc/test/test_hooks.h
+++ b/cc/test/test_hooks.h
@@ -131,8 +131,6 @@
                                base::TimeTicks animation_start_time,
                                std::unique_ptr<AnimationCurve> curve) override {
   }
-  void NotifyLocalTimeUpdated(
-      base::Optional<base::TimeDelta> local_time) override {}
 
   // OutputSurface indirections to the LayerTreeTest, that can be further
   // overridden.
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index f64a3dfe8..ad08ae8 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -713,14 +713,13 @@
   if (!property_trees->GetToTarget(node->transform_id, target_id, &to_target))
     return kEmptyRoundedCornerInfo;
 
-  DCHECK(to_target.Preserves2dAxisAlignment());
+  auto result =
+      std::make_pair(node->rounded_corner_bounds, node->is_fast_rounded_corner);
 
-  SkRRect result;
-  if (!SkRRect(node->rounded_corner_bounds)
-           .transform(to_target.matrix(), &result)) {
+  if (!to_target.TransformRRectF(&result.first))
     return kEmptyRoundedCornerInfo;
-  }
-  return std::make_pair(gfx::RRectF(result), node->is_fast_rounded_corner);
+
+  return result;
 }
 
 void UpdateRenderTarget(EffectTree* effect_tree) {
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 6e5ec70..6facf98 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -5317,10 +5317,6 @@
                                                        scrollbar_opacity);
 }
 
-void LayerTreeHostImpl::UnregisterScrollbarLayer(ElementId element_id) const {
-  scrollbar_controller_->DidUnregisterScrollbar(element_id);
-}
-
 void LayerTreeHostImpl::UnregisterScrollbarAnimationController(
     ElementId scroll_element_id) {
   scrollbar_animation_controllers_.erase(scroll_element_id);
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 60e28e2..4242cd1 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -373,8 +373,6 @@
     return need_update_gpu_rasterization_status_;
   }
 
-  void UnregisterScrollbarLayer(ElementId element_id) const;
-
   // MutatorHostClient implementation.
   bool IsElementInPropertyTrees(ElementId element_id,
                                 ElementListType list_type) const override;
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 1f92204e..27812c1 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1919,7 +1919,6 @@
     element_id_to_scrollbar_layer_ids_.erase(scroll_element_id);
     if (IsActiveTree()) {
       host_impl_->UnregisterScrollbarAnimationController(scroll_element_id);
-      host_impl_->UnregisterScrollbarLayer(scroll_element_id);
     }
   }
 }
diff --git a/cc/trees/layer_tree_mutator.cc b/cc/trees/layer_tree_mutator.cc
index ecd0d667..9e285596 100644
--- a/cc/trees/layer_tree_mutator.cc
+++ b/cc/trees/layer_tree_mutator.cc
@@ -36,6 +36,9 @@
                      }) &&
          std::all_of(
              removed_animations.cbegin(), removed_animations.cend(),
+             [worklet_id](auto& it) { return it.worklet_id == worklet_id; }) &&
+         std::all_of(
+             peeked_animations.cbegin(), peeked_animations.cend(),
              [worklet_id](auto& it) { return it.worklet_id == worklet_id; });
 }
 #endif
@@ -77,6 +80,12 @@
   worklet_input.removed_animations.push_back(worklet_animation_id);
 }
 
+void MutatorInputState::Peek(WorkletAnimationId worklet_animation_id) {
+  AnimationWorkletInput& worklet_input =
+      EnsureWorkletEntry(worklet_animation_id.worklet_id);
+  worklet_input.peeked_animations.push_back(worklet_animation_id);
+}
+
 std::unique_ptr<AnimationWorkletInput> MutatorInputState::TakeWorkletState(
     int worklet_id) {
   auto it = inputs_.find(worklet_id);
diff --git a/cc/trees/layer_tree_mutator.h b/cc/trees/layer_tree_mutator.h
index dc9ea0f1..ff1c325 100644
--- a/cc/trees/layer_tree_mutator.h
+++ b/cc/trees/layer_tree_mutator.h
@@ -51,18 +51,10 @@
   // animation_id is only guaranteed to be unique per animation worklet.
   int animation_id;
 
-  // Initialize with invalid id.
-  WorkletAnimationId() : worklet_id(0), animation_id(0) {}
-  WorkletAnimationId(int worklet_id, int animation_id)
-      : worklet_id(worklet_id), animation_id(animation_id) {}
-
   inline bool operator==(const WorkletAnimationId& rhs) const {
     return (this->worklet_id == rhs.worklet_id) &&
            (this->animation_id == rhs.animation_id);
   }
-  // Returns true if the WorkletAnimationId has been initialized with a valid
-  // id.
-  explicit operator bool() const { return !!worklet_id || !!animation_id; }
 };
 
 struct CC_EXPORT AnimationWorkletInput {
@@ -95,6 +87,7 @@
   std::vector<AddAndUpdateState> added_and_updated_animations;
   std::vector<UpdateState> updated_animations;
   std::vector<WorkletAnimationId> removed_animations;
+  std::vector<WorkletAnimationId> peeked_animations;
 
   AnimationWorkletInput();
   AnimationWorkletInput(const AnimationWorkletInput&) = delete;
@@ -120,6 +113,12 @@
   void Add(AnimationWorkletInput::AddAndUpdateState&& state);
   void Update(AnimationWorkletInput::UpdateState&& state);
   void Remove(WorkletAnimationId worklet_animation_id);
+  // |Update| asks for the animation to *animate* given a current time and
+  // return the output value while |Peek| only asks for the last output value
+  // (if one available) without requiring animate or providing a current time.
+  // In particular, composited animations are updated from compositor and peeked
+  // from main thread.
+  void Peek(WorkletAnimationId worklet_animation_id);
 
   // Returns input for animation worklet with the given |scope_id| and nullptr
   // if there is no input.
diff --git a/cc/trees/mutator_host.h b/cc/trees/mutator_host.h
index c52ce40..a77f841 100644
--- a/cc/trees/mutator_host.h
+++ b/cc/trees/mutator_host.h
@@ -158,6 +158,7 @@
 
   virtual size_t CompositedAnimationsCount() const = 0;
   virtual size_t MainThreadAnimationsCount() const = 0;
+  virtual bool HasCustomPropertyAnimations() const = 0;
   virtual bool CurrentFrameHadRAF() const = 0;
   virtual bool NextFrameHasPendingRAF() const = 0;
 };
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc
index 6a63c04f..a43c4b40 100644
--- a/cc/trees/proxy_impl.cc
+++ b/cc/trees/proxy_impl.cc
@@ -379,6 +379,10 @@
   return host_impl_->mutator_host()->MainThreadAnimationsCount();
 }
 
+bool ProxyImpl::HasCustomPropertyAnimations() const {
+  return host_impl_->mutator_host()->HasCustomPropertyAnimations();
+}
+
 bool ProxyImpl::CurrentFrameHadRAF() const {
   return host_impl_->mutator_host()->CurrentFrameHadRAF();
 }
diff --git a/cc/trees/proxy_impl.h b/cc/trees/proxy_impl.h
index 6320aab..b245b60 100644
--- a/cc/trees/proxy_impl.h
+++ b/cc/trees/proxy_impl.h
@@ -141,6 +141,7 @@
   void FrameIntervalUpdated(base::TimeDelta interval) override {}
   size_t CompositedAnimationsCount() const override;
   size_t MainThreadAnimationsCount() const override;
+  bool HasCustomPropertyAnimations() const override;
   bool CurrentFrameHadRAF() const override;
   bool NextFrameHasPendingRAF() const override;
 
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 13279a0..64cf02b 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -427,6 +427,10 @@
   return 0;
 }
 
+bool SingleThreadProxy::HasCustomPropertyAnimations() const {
+  return false;
+}
+
 bool SingleThreadProxy::CurrentFrameHadRAF() const {
   return false;
 }
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index 3d40bfb..3cde647 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -97,6 +97,7 @@
   void FrameIntervalUpdated(base::TimeDelta interval) override;
   size_t CompositedAnimationsCount() const override;
   size_t MainThreadAnimationsCount() const override;
+  bool HasCustomPropertyAnimations() const override;
   bool CurrentFrameHadRAF() const override;
   bool NextFrameHasPendingRAF() const override;
 
diff --git a/chrome/VERSION b/chrome/VERSION
index affdcbb..4e03f89 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=79
 MINOR=0
-BUILD=3944
+BUILD=3945
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 33770ddc..4bcbf9bd 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -670,6 +670,7 @@
     "//chrome/android/webapk/libs/client:client_java",
     "//chrome/android/webapk/libs/common:common_java",
     "//chrome/android/webapk/test:junit_test_support",
+    "//chrome/browser/image_fetcher:java",
     "//chrome/browser/ui/android/widget:ui_widget_junit_tests",
     "//chrome/test/android:chrome_java_test_support",
     "//components/background_task_scheduler:background_task_scheduler_java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index f6e1250..309d7af 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -965,6 +965,7 @@
   "java/src/org/chromium/chrome/browser/ntp/LogoDelegateImpl.java",
   "java/src/org/chromium/chrome/browser/ntp/LogoView.java",
   "java/src/org/chromium/chrome/browser/ntp/NativePageRootFrameLayout.java",
+  "java/src/org/chromium/chrome/browser/ntp/FakeboxDelegate.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPage.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPageScrollView.java",
@@ -1128,7 +1129,6 @@
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionProcessor.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/VoiceSuggestionProvider.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java",
-  "java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionView.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewBinder.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewProperties.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerText.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index c7a276d0..f0e32882 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -154,6 +154,7 @@
   "junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTrackerTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java",
+  "junit/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleHorizontalLayoutViewTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index a613b8fb..5b9dbb4 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -454,7 +454,6 @@
   "javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseActivityTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java",
   "javatests/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateTest.java",
-  "javatests/src/org/chromium/chrome/browser/tab/MockTab.java",
   "javatests/src/org/chromium/chrome/browser/tab/RepostFormWarningTest.java",
   "javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java",
   "javatests/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings.grd b/chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings.grd
index de79a9d..6c343a0 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings.grd
+++ b/chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings.grd
@@ -107,7 +107,7 @@
         Passwords available
       </message>
       <message name="IDS_AUTOFILL_KEYBOARD_ACCESSORY_MODERN_CONTENT_DESCRIPTION" desc="The text announced by the screen reader when the autofill suggestions and fallbacks are shown.">
-        Autofill suggestions available
+        Autofill suggestions available above the keyboard
       </message>
       <message name="IDS_KEYBOARD_ACCESSORY_SHEET_HIDE" desc="Description for the active icon button that closes an accessory sheet and brings back the keyboard.">
           Show keyboard
diff --git a/chrome/android/features/start_surface/internal/BUILD.gn b/chrome/android/features/start_surface/internal/BUILD.gn
index 603839c..e11e29e6 100644
--- a/chrome/android/features/start_surface/internal/BUILD.gn
+++ b/chrome/android/features/start_surface/internal/BUILD.gn
@@ -89,7 +89,6 @@
     "java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java",
     "java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java",
     "java/src/org/chromium/chrome/features/start_surface/StartSurfaceProperties.java",
-    "java/src/org/chromium/chrome/features/start_surface/StartSurfaceLocationBarDelegate.java",
     "java/src/org/chromium/chrome/features/start_surface/SecondaryTasksSurfaceViewBinder.java",
     "java/src/org/chromium/chrome/features/start_surface/TasksSurfaceViewBinder.java",
   ]
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java
index 1593afd..b7057874 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java
@@ -75,10 +75,6 @@
     @Nullable
     private TabSwitcher.OnTabSelectingListener mOnTabSelectingListener;
 
-    // Non-null in SurfaceMode.TASKS_ONLY, SurfaceMode.TWO_PANES and SurfaceMode.SINGLE_PANE modes.
-    @Nullable
-    private StartSurfaceLocationBarDelegate mStartSurfaceLocationBarDelegate;
-
     public StartSurfaceCoordinator(ChromeActivity activity) {
         mActivity = activity;
         mSurfaceMode = computeSurfaceMode();
@@ -100,7 +96,10 @@
                         : mExploreSurfaceCoordinator.getFeedSurfaceCreator(),
                 mSurfaceMode == SurfaceMode.SINGLE_PANE ? this::initializeSecondaryTasksSurface
                                                         : null,
-                mSurfaceMode, mStartSurfaceLocationBarDelegate,
+                mSurfaceMode,
+                mSurfaceMode != SurfaceMode.NO_START_SURFACE
+                        ? mActivity.getToolbarManager().getFakeboxDelegate()
+                        : null,
                 mActivity.getNightModeStateProvider());
     }
 
@@ -168,11 +167,8 @@
         mPropertyModel.set(TOP_BAR_HEIGHT,
                 mActivity.getResources().getDimensionPixelSize(R.dimen.toolbar_height_no_shadow));
 
-        // TODO(crbug.com/1000295): Formalize the way to get the location bar.
-        mStartSurfaceLocationBarDelegate = new StartSurfaceLocationBarDelegate(
-                mActivity.getToolbarManager().getToolbarLayoutForTesting().getLocationBar());
         mTasksSurface = TabManagementModuleProvider.getDelegate().createTasksSurface(mActivity,
-                mPropertyModel, mStartSurfaceLocationBarDelegate,
+                mPropertyModel, mActivity.getToolbarManager().getFakeboxDelegate(),
                 mSurfaceMode == SurfaceMode.SINGLE_PANE);
 
         // The tasks surface is added to the explore surface in the single pane mode below.
@@ -205,12 +201,12 @@
     private TabSwitcher.Controller initializeSecondaryTasksSurface() {
         assert mSurfaceMode == SurfaceMode.SINGLE_PANE;
         assert mSecondaryTasksSurface == null;
-        assert mStartSurfaceLocationBarDelegate != null;
 
         PropertyModel propertyModel = new PropertyModel(TasksSurfaceProperties.ALL_KEYS);
         mStartSurfaceMediator.setSecondaryTasksSurfacePropertyModel(propertyModel);
-        mSecondaryTasksSurface = TabManagementModuleProvider.getDelegate().createTasksSurface(
-                mActivity, propertyModel, mStartSurfaceLocationBarDelegate, false);
+        mSecondaryTasksSurface =
+                TabManagementModuleProvider.getDelegate().createTasksSurface(mActivity,
+                        propertyModel, mActivity.getToolbarManager().getFakeboxDelegate(), false);
         mSecondaryTasksSurfacePropertyModelChangeProcessor =
                 createTasksViewPropertyModelChangeProcessor(
                         mSecondaryTasksSurface.getContainerView(),
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
index b4f335f0..d3593df 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
@@ -34,6 +34,7 @@
 import org.chromium.chrome.browser.compositor.scene_layer.TabListSceneLayer;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher;
 import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.util.FeatureUtilities;
@@ -137,6 +138,12 @@
 
     // Layout implementation.
     @Override
+    public void setTabModelSelector(TabModelSelector modelSelector, TabContentManager manager) {
+        super.setTabModelSelector(modelSelector, manager);
+        mSceneLayer.setTabModelSelector(modelSelector);
+    }
+
+    @Override
     public LayoutTab getLayoutTab(int id) {
         return mDummyLayoutTab;
     }
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLocationBarDelegate.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLocationBarDelegate.java
deleted file mode 100644
index 96d7eaa..0000000
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLocationBarDelegate.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.features.start_surface;
-
-import androidx.annotation.Nullable;
-
-import org.chromium.chrome.browser.omnibox.LocationBar;
-import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
-import org.chromium.chrome.browser.tasks.TasksSurface;
-
-/** The delegate to interact with the {@link LocationBar}. */
-class StartSurfaceLocationBarDelegate
-        implements TasksSurface.FakeSearchBoxDelegate, StartSurfaceMediator.Delegate {
-    private final LocationBar mLocationBar;
-
-    StartSurfaceLocationBarDelegate(LocationBar locationBar) {
-        mLocationBar = locationBar;
-    }
-
-    // Implements TasksSurface.FakeSearchBoxDelegate.
-    @Override
-    public void requestUrlFocus(@Nullable String pastedText) {
-        mLocationBar.setUrlBarFocus(true);
-    }
-
-    // Implements StartSurfaceMediator.Delegate.
-    @Override
-    public void addUrlFocusChangeListener(UrlFocusChangeListener listener) {
-        mLocationBar.addUrlFocusChangeListener(listener);
-    }
-
-    @Override
-    public void removeUrlFocusChangeListener(UrlFocusChangeListener listener) {
-        mLocationBar.removeUrlFocusChangeListener(listener);
-    }
-}
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
index 3fb5be3..93a9f676 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.IS_FAKE_SEARCH_BOX_VISIBLE;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.IS_INCOGNITO;
+import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.IS_VOICE_RECOGNITION_BUTTON_VISIBLE;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.MORE_TABS_CLICK_LISTENER;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.MV_TILES_VISIBLE;
 import static org.chromium.chrome.features.start_surface.StartSurfaceProperties.BOTTOM_BAR_CLICKLISTENER;
@@ -25,9 +26,11 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ObserverList;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.feed.FeedSurfaceCoordinator;
 import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
+import org.chromium.chrome.browser.ntp.FakeboxDelegate;
 import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -52,21 +55,6 @@
         int SINGLE_PANE = 3;
     }
 
-    /** The delegate to interact with the location bar. */
-    interface Delegate {
-        /**
-         * Add the given url focus change listener.
-         * @param listener The given listener.
-         */
-        void addUrlFocusChangeListener(UrlFocusChangeListener listener);
-
-        /**
-         * Remove the given url focus change listener.
-         * @param listener The given listener.
-         */
-        void removeUrlFocusChangeListener(UrlFocusChangeListener listener);
-    }
-
     /** Interface to initialize a secondary tasks surface for more tabs. */
     interface SecondaryTasksSurfaceInitializer {
         /**
@@ -93,7 +81,7 @@
     private PropertyModel mSecondaryTasksSurfacePropertyModel;
     private boolean mIsIncognito;
     @Nullable
-    private Delegate mDelegate;
+    private FakeboxDelegate mFakeboxDelegate;
     private NightModeStateProvider mNightModeStateProvider;
     @Nullable
     UrlFocusChangeListener mUrlFocusChangeListener;
@@ -104,20 +92,20 @@
             @Nullable PropertyModel propertyModel,
             @Nullable ExploreSurfaceCoordinator.FeedSurfaceCreator feedSurfaceCreator,
             @Nullable SecondaryTasksSurfaceInitializer secondaryTasksSurfaceInitializer,
-            @SurfaceMode int surfaceMode, @Nullable Delegate delegate,
+            @SurfaceMode int surfaceMode, @Nullable FakeboxDelegate fakeboxDelegate,
             NightModeStateProvider nightModeStateProvider) {
         mController = controller;
         mPropertyModel = propertyModel;
         mFeedSurfaceCreator = feedSurfaceCreator;
         mSecondaryTasksSurfaceInitializer = secondaryTasksSurfaceInitializer;
         mSurfaceMode = surfaceMode;
-        mDelegate = delegate;
+        mFakeboxDelegate = fakeboxDelegate;
         mNightModeStateProvider = nightModeStateProvider;
 
         if (mPropertyModel != null) {
             assert mSurfaceMode == SurfaceMode.SINGLE_PANE || mSurfaceMode == SurfaceMode.TWO_PANES
                     || mSurfaceMode == SurfaceMode.TASKS_ONLY;
-            assert mDelegate != null;
+            assert mFakeboxDelegate != null;
 
             mIsIncognito = tabModelSelector.isIncognitoSelected();
             tabModelSelector.addObserver(new EmptyTabModelSelectorObserver() {
@@ -173,6 +161,11 @@
             }
             mPropertyModel.set(MV_TILES_VISIBLE, !mIsIncognito);
 
+            // Note that isVoiceSearchEnabled will return false in incognito mode.
+            mPropertyModel.set(IS_VOICE_RECOGNITION_BUTTON_VISIBLE,
+                    mFakeboxDelegate.getLocationBarVoiceRecognitionHandler()
+                            .isVoiceSearchEnabled());
+
             int toolbarHeight =
                     ContextUtils.getApplicationContext().getResources().getDimensionPixelSize(
                             R.dimen.toolbar_height_no_shadow);
@@ -202,8 +195,9 @@
         mSecondaryTasksSurfacePropertyModel.set(IS_INCOGNITO, mIsIncognito);
 
         // Secondary tasks surface is used for more Tabs or incognito mode single pane, where MV
-        // tiles should be invisible.
+        // tiles and voice recognition button should be invisible.
         mSecondaryTasksSurfacePropertyModel.set(MV_TILES_VISIBLE, false);
+        mSecondaryTasksSurfacePropertyModel.set(IS_VOICE_RECOGNITION_BUTTON_VISIBLE, false);
     }
 
     void setStateChangeObserver(StartSurface.StateObserver observer) {
@@ -267,7 +261,7 @@
             }
 
             mPropertyModel.set(IS_SHOWING_OVERVIEW, true);
-            mDelegate.addUrlFocusChangeListener(mUrlFocusChangeListener);
+            mFakeboxDelegate.addUrlFocusChangeListener(mUrlFocusChangeListener);
         }
 
         mController.showOverview(animate);
@@ -323,7 +317,7 @@
     public void startedHiding() {
         if (mPropertyModel != null) {
             mPropertyModel.set(IS_SHOWING_OVERVIEW, false);
-            mDelegate.removeUrlFocusChangeListener(mUrlFocusChangeListener);
+            mFakeboxDelegate.removeUrlFocusChangeListener(mUrlFocusChangeListener);
             destroyFeedSurfaceCoordinator();
         }
         for (StartSurface.OverviewModeObserver observer : mObservers) {
@@ -387,6 +381,16 @@
 
         mPropertyModel.set(MV_TILES_VISIBLE, !mIsIncognito);
 
+        // This is because LocationBarVoiceRecognitionHandler monitors incognito mode and returns
+        // false in incognito mode. However, when switching incognito mode, this class is notified
+        // earlier than the LocationBarVoiceRecognitionHandler, so isVoiceSearchEnabled returns
+        // incorrect state if check synchronously.
+        ThreadUtils.postOnUiThread(() -> {
+            mPropertyModel.set(IS_VOICE_RECOGNITION_BUTTON_VISIBLE,
+                    mFakeboxDelegate.getLocationBarVoiceRecognitionHandler()
+                            .isVoiceSearchEnabled());
+        });
+
         if (mSurfaceMode == SurfaceMode.SINGLE_PANE) {
             setExploreSurfaceVisibility(!mIsIncognito);
             setSecondaryTasksSurfaceVisibility(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurface.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurface.java
index 3117960..4bbf97b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurface.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurface.java
@@ -6,8 +6,6 @@
 
 import android.view.ViewGroup;
 
-import androidx.annotation.Nullable;
-
 import org.chromium.chrome.browser.compositor.layouts.Layout;
 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher;
 
@@ -17,15 +15,6 @@
  *  {@link TasksSurfaceCoordinator}.
  */
 public interface TasksSurface {
-    /** The delegate for the fake search box on the tasks surface. */
-    public interface FakeSearchBoxDelegate {
-        /**
-         * Interface to request focusing on the URL bar.
-         * @param pastedText The text to be pasted in the Omnibox or null.
-         * */
-        void requestUrlFocus(@Nullable String pastedText);
-    }
-
     /**
      * Set the listener to get the {@link Layout#onTabSelecting} event from the Grid Tab Switcher.
      * @param listener The {@link TabSwitcher.OnTabSelectingListener} to use.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java
index bbf7f8d8..18c851d5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java
@@ -9,6 +9,7 @@
 import android.widget.LinearLayout;
 
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ntp.FakeboxDelegate;
 import org.chromium.chrome.browser.tasks.tab_management.TabManagementModuleProvider;
 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher;
 import org.chromium.chrome.tab_ui.R;
@@ -27,7 +28,7 @@
     private final TasksSurfaceMediator mMediator;
 
     public TasksSurfaceCoordinator(ChromeActivity activity, PropertyModel propertyModel,
-            TasksSurface.FakeSearchBoxDelegate fakeSearchBoxDelegate, boolean isTabCarousel) {
+            FakeboxDelegate fakeboxDelegate, boolean isTabCarousel) {
         mView = (TasksView) LayoutInflater.from(activity).inflate(R.layout.tasks_view_layout, null);
         mPropertyModelChangeProcessor =
                 PropertyModelChangeProcessor.create(propertyModel, mView, TasksViewBinder::bind);
@@ -39,8 +40,8 @@
                     activity, mView.getTabSwitcherContainer());
         }
 
-        mMediator = new TasksSurfaceMediator(
-                activity, propertyModel, fakeSearchBoxDelegate, isTabCarousel);
+        mMediator =
+                new TasksSurfaceMediator(activity, propertyModel, fakeboxDelegate, isTabCarousel);
 
         LinearLayout mvTilesLayout = mView.findViewById(R.id.mv_tiles_layout);
         mMostVisitedList = new MostVisitedListCoordinator(activity, mvTilesLayout, propertyModel);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceMediator.java
index d52ebc6d4..70388895 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceMediator.java
@@ -5,14 +5,22 @@
 package org.chromium.chrome.browser.tasks;
 
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.FAKE_SEARCH_BOX_CLICK_LISTENER;
+import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.FAKE_SEARCH_BOX_TEXT_WATCHER;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.IS_FAKE_SEARCH_BOX_VISIBLE;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.IS_TAB_CAROUSEL;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.IS_VOICE_RECOGNITION_BUTTON_VISIBLE;
+import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.VOICE_SEARCH_BUTTON_CLICK_LISTENER;
 
 import android.content.Context;
 import android.support.annotation.Nullable;
+import android.text.Editable;
+import android.text.TextWatcher;
 import android.view.View;
 
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.ntp.FakeboxDelegate;
+import org.chromium.chrome.browser.omnibox.LocationBar;
+import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /**
@@ -20,20 +28,48 @@
  */
 class TasksSurfaceMediator {
     @Nullable
-    private final TasksSurface.FakeSearchBoxDelegate mFakeSearchBoxDelegate;
+    private final FakeboxDelegate mFakeboxDelegate;
     private final PropertyModel mModel;
 
-    TasksSurfaceMediator(Context context, PropertyModel model,
-            TasksSurface.FakeSearchBoxDelegate fakeSearchBoxDelegate, boolean isTabCarousel) {
-        mFakeSearchBoxDelegate = fakeSearchBoxDelegate;
-        assert mFakeSearchBoxDelegate != null;
+    TasksSurfaceMediator(Context context, PropertyModel model, FakeboxDelegate fakeboxDelegate,
+            boolean isTabCarousel) {
+        mFakeboxDelegate = fakeboxDelegate;
+        assert mFakeboxDelegate != null;
 
         mModel = model;
         mModel.set(IS_TAB_CAROUSEL, isTabCarousel);
         mModel.set(FAKE_SEARCH_BOX_CLICK_LISTENER, new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                mFakeSearchBoxDelegate.requestUrlFocus(null);
+                mFakeboxDelegate.setUrlBarFocus(
+                        true, null, LocationBar.OmniboxFocusReason.TASKS_SURFACE_FAKE_BOX_TAP);
+                RecordUserAction.record("TasksSurface.FakeBox.Tapped");
+            }
+        });
+        mModel.set(FAKE_SEARCH_BOX_TEXT_WATCHER, new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                if (s.length() == 0) return;
+                mFakeboxDelegate.setUrlBarFocus(true, s.toString(),
+                        LocationBar.OmniboxFocusReason.TASKS_SURFACE_FAKE_BOX_LONG_PRESS);
+                RecordUserAction.record("TasksSurface.FakeBox.LongPressed");
+
+                // This won't cause infinite loop since we checked s.length() == 0 above.
+                s.clear();
+            }
+        });
+        model.set(VOICE_SEARCH_BUTTON_CLICK_LISTENER, new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mFakeboxDelegate.getLocationBarVoiceRecognitionHandler().startVoiceRecognition(
+                        LocationBarVoiceRecognitionHandler.VoiceInteractionSource.TASKS_SURFACE);
+                RecordUserAction.record("TasksSurface.FakeBox.VoiceSearch");
             }
         });
 
@@ -42,7 +78,6 @@
         mModel.set(IS_VOICE_RECOGNITION_BUTTON_VISIBLE, false);
 
         // TODO(crbug.com/982018): Enable voice recognition button in the fake search box.
-        // TODO(crbug.com/982018): Enable long press and paste search in the fake search box.
         // TODO(crbug.com/982018): Change the fake search box in dark mode.
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceProperties.java
index 6cc4c9e..b931fdb 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceProperties.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.chrome.browser.tasks.MostVisitedListProperties.IS_VISIBLE;
 
+import android.text.TextWatcher;
 import android.view.View;
 
 import org.chromium.ui.modelutil.PropertyKey;
@@ -28,10 +29,17 @@
             .WritableObjectPropertyKey<View.OnClickListener> FAKE_SEARCH_BOX_CLICK_LISTENER =
             new PropertyModel.WritableObjectPropertyKey<View.OnClickListener>();
     public static final PropertyModel
+            .WritableObjectPropertyKey<TextWatcher> FAKE_SEARCH_BOX_TEXT_WATCHER =
+            new PropertyModel.WritableObjectPropertyKey<TextWatcher>();
+    public static final PropertyModel
             .WritableObjectPropertyKey<View.OnClickListener> MORE_TABS_CLICK_LISTENER =
             new PropertyModel.WritableObjectPropertyKey<View.OnClickListener>();
     public static final PropertyModel.WritableBooleanPropertyKey MV_TILES_VISIBLE = IS_VISIBLE;
+    public static final PropertyModel
+            .WritableObjectPropertyKey<View.OnClickListener> VOICE_SEARCH_BUTTON_CLICK_LISTENER =
+            new PropertyModel.WritableObjectPropertyKey<View.OnClickListener>();
     public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {IS_FAKE_SEARCH_BOX_VISIBLE,
             IS_INCOGNITO, IS_TAB_CAROUSEL, IS_VOICE_RECOGNITION_BUTTON_VISIBLE,
-            FAKE_SEARCH_BOX_CLICK_LISTENER, MORE_TABS_CLICK_LISTENER, MV_TILES_VISIBLE};
+            FAKE_SEARCH_BOX_CLICK_LISTENER, FAKE_SEARCH_BOX_TEXT_WATCHER, MORE_TABS_CLICK_LISTENER,
+            MV_TILES_VISIBLE, VOICE_SEARCH_BUTTON_CLICK_LISTENER};
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java
index 24a083d..f7a7817a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.support.v4.view.MarginLayoutParamsCompat;
+import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -76,6 +77,14 @@
     }
 
     /**
+     * Set the given watcher for the fake search box.
+     * @param textWatcher The given {@link TextWatcher}.
+     */
+    void setFakeSearchBoxTextWatcher(TextWatcher textWatcher) {
+        mSearchBoxText.addTextChangedListener(textWatcher);
+    }
+
+    /**
      * Set the visibility of the fake search box.
      * @param isVisible Whether it's visible.
      */
@@ -92,6 +101,14 @@
     }
 
     /**
+     * Set the voice recognition button click listener.
+     * @param listener The given listener.
+     */
+    void setVoiceRecognitionButtonClickListener(@Nullable View.OnClickListener listener) {
+        findViewById(R.id.voice_search_button).setOnClickListener(listener);
+    }
+
+    /**
      * Set the visibility of the Most Visited Tiles.
      */
     void setMostVisitedVisibility(int visibility) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java
index 8c2becd..8e8dc3a4 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java
@@ -5,12 +5,14 @@
 package org.chromium.chrome.browser.tasks;
 
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.FAKE_SEARCH_BOX_CLICK_LISTENER;
+import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.FAKE_SEARCH_BOX_TEXT_WATCHER;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.IS_FAKE_SEARCH_BOX_VISIBLE;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.IS_INCOGNITO;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.IS_TAB_CAROUSEL;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.IS_VOICE_RECOGNITION_BUTTON_VISIBLE;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.MORE_TABS_CLICK_LISTENER;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.MV_TILES_VISIBLE;
+import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.VOICE_SEARCH_BUTTON_CLICK_LISTENER;
 
 import android.view.View;
 
@@ -22,6 +24,8 @@
     public static void bind(PropertyModel model, TasksView view, PropertyKey propertyKey) {
         if (propertyKey == FAKE_SEARCH_BOX_CLICK_LISTENER) {
             view.setFakeSearchBoxClickListener(model.get(FAKE_SEARCH_BOX_CLICK_LISTENER));
+        } else if (propertyKey == FAKE_SEARCH_BOX_TEXT_WATCHER) {
+            view.setFakeSearchBoxTextWatcher(model.get(FAKE_SEARCH_BOX_TEXT_WATCHER));
         } else if (propertyKey == IS_FAKE_SEARCH_BOX_VISIBLE) {
             view.setFakeSearchBoxVisibility(model.get(IS_FAKE_SEARCH_BOX_VISIBLE));
         } else if (propertyKey == IS_INCOGNITO) {
@@ -35,6 +39,9 @@
             view.setMoreTabsOnClickListener(model.get(MORE_TABS_CLICK_LISTENER));
         } else if (propertyKey == MV_TILES_VISIBLE) {
             view.setMostVisitedVisibility(model.get(MV_TILES_VISIBLE) ? View.VISIBLE : View.GONE);
+        } else if (propertyKey == VOICE_SEARCH_BUTTON_CLICK_LISTENER) {
+            view.setVoiceRecognitionButtonClickListener(
+                    model.get(VOICE_SEARCH_BUTTON_CLICK_LISTENER));
         }
     }
 }
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
index 40ddf4e6..612388c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
@@ -31,6 +31,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ContextUtils;
@@ -47,7 +48,7 @@
  * Parent for TabGridDialog component.
  */
 public class TabGridDialogParent
-        implements TabSelectionEditorLayout.TabSelectionEditorLayoutPositionProvider {
+        implements TabSelectionEditorMediator.TabSelectionEditorPositionProvider {
     private static final int DIALOG_ANIMATION_DURATION = 300;
     private static final int DIALOG_ALPHA_ANIMATION_DURATION = 150;
     private static final int CARD_FADE_ANIMATION_DURATION = 50;
@@ -654,19 +655,17 @@
     }
 
     /**
-     * {@link TabSelectionEditorLayout.TabSelectionEditorLayoutPositionProvider} implementation.
+     * {@link TabSelectionEditorMediator.TabSelectionEditorPositionProvider} implementation.
      * Returns a {@link Rect} that indicates the current position of dialog.
      */
     @Override
+    @NonNull
     public Rect getSelectionEditorPositionRect() {
-        Rect positionRect = new Rect();
-        mDialogContainerView.getGlobalVisibleRect(positionRect);
+        // Get the status bar height as offset.
         Rect parentRect = new Rect();
         mParent.getGlobalVisibleRect(parentRect);
-
-        // Offset by the status bar height.
-        positionRect.offset(0, parentRect.top);
-        return positionRect;
+        return new Rect(mSideMargin, mTopMargin + parentRect.top, mCurrentScreenWidth - mSideMargin,
+                mCurrentScreenHeight - mTopMargin);
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
index 4cc1f3c..76d9f2a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
@@ -103,6 +103,9 @@
         ApiCompatibilityUtils.setImageTintList(mLeftButton, tint);
         ApiCompatibilityUtils.setImageTintList(mRightButton, tint);
         if (mTitleTextView != null) mTitleTextView.setTextColor(tint);
+        if (mMenuButton != null) {
+            ApiCompatibilityUtils.setImageTintList(mMenuButton, tint);
+        }
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java
index 42b68c6..e334998d5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java
@@ -12,6 +12,7 @@
 import org.chromium.chrome.browser.compositor.layouts.Layout;
 import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
+import org.chromium.chrome.browser.ntp.FakeboxDelegate;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tasks.TasksSurface;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
@@ -32,12 +33,12 @@
      * @param activity The {@link ChromeActivity} that creates this surface.
      * @param propertyModel The {@link PropertyModel} contains the {@link TasksSurfaceProperties} to
      *         communicate with this surface.
-     * @param fakeSearchBoxDelegate The delegate of the fake search box.
+     * @param fakeboxDelegate The delegate of the fake search box.
      * @param isTabCarousel Whether show the Tabs in carousel mode.
      * @return The {@TasksSurface}.
      */
     TasksSurface createTasksSurface(ChromeActivity activity, PropertyModel propertyModel,
-            TasksSurface.FakeSearchBoxDelegate fakeSearchBoxDelegate, boolean isTabCarousel);
+            FakeboxDelegate fakeboxDelegate, boolean isTabCarousel);
 
     /**
      * Create the {@link TabSwitcher} to display Tabs in grid.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
index d595035..d31945f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
@@ -17,6 +17,7 @@
 import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
+import org.chromium.chrome.browser.ntp.FakeboxDelegate;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tasks.TasksSurface;
 import org.chromium.chrome.browser.tasks.TasksSurfaceCoordinator;
@@ -35,9 +36,8 @@
 public class TabManagementDelegateImpl implements TabManagementDelegate {
     @Override
     public TasksSurface createTasksSurface(ChromeActivity activity, PropertyModel propertyModel,
-            TasksSurface.FakeSearchBoxDelegate fakeSearchBoxDelegate, boolean isTabCarousel) {
-        return new TasksSurfaceCoordinator(
-                activity, propertyModel, fakeSearchBoxDelegate, isTabCarousel);
+            FakeboxDelegate fakeboxDelegate, boolean isTabCarousel) {
+        return new TasksSurfaceCoordinator(activity, propertyModel, fakeboxDelegate, isTabCarousel);
     }
 
     @Override
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
index 2bd774a6..22ade65 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
@@ -74,7 +74,8 @@
 
     public TabSelectionEditorCoordinator(Context context, View parentView,
             TabModelSelector tabModelSelector, TabContentManager tabContentManager,
-            TabSelectionEditorLayout.TabSelectionEditorLayoutPositionProvider positionProvider) {
+            @Nullable TabSelectionEditorMediator
+                    .TabSelectionEditorPositionProvider positionProvider) {
         mContext = context;
         mParentView = parentView;
         mTabModelSelector = tabModelSelector;
@@ -87,15 +88,14 @@
                 .inflate(R.layout.tab_selection_editor_layout, null)
                 .findViewById(R.id.selectable_list);
         mTabSelectionEditorLayout.initialize(mParentView, mTabListCoordinator.getContainerView(),
-                mTabListCoordinator.getContainerView().getAdapter(), mSelectionDelegate,
-                positionProvider);
+                mTabListCoordinator.getContainerView().getAdapter(), mSelectionDelegate);
         mSelectionDelegate.setSelectionModeEnabledForZeroItems(true);
 
         mTabSelectionEditorLayoutChangeProcessor = PropertyModelChangeProcessor.create(
                 mModel, mTabSelectionEditorLayout, TabSelectionEditorLayoutBinder::bind);
 
-        mTabSelectionEditorMediator = new TabSelectionEditorMediator(
-                mContext, mTabModelSelector, this::resetWithListOfTabs, mModel, mSelectionDelegate);
+        mTabSelectionEditorMediator = new TabSelectionEditorMediator(mContext, mTabModelSelector,
+                this::resetWithListOfTabs, mModel, mSelectionDelegate, positionProvider);
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayout.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayout.java
index cef2310e..40a867d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayout.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayout.java
@@ -14,8 +14,10 @@
 import android.view.ViewTreeObserver;
 import android.widget.PopupWindow;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.widget.selection.SelectableListLayout;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.chrome.tab_ui.R;
@@ -27,22 +29,10 @@
     private final PopupWindow mWindow;
     private TabSelectionEditorToolbar mToolbar;
     private View mParentView;
-    private TabSelectionEditorLayoutPositionProvider mPositionProvider;
     private ViewTreeObserver.OnGlobalLayoutListener mParentLayoutListener;
+    private @Nullable Rect mPositionRect;
     private boolean mIsInitialized;
 
-    /**
-     * An interface to provide the {@link Rect} used to position the selection editor layout on
-     * screen.
-     */
-    public interface TabSelectionEditorLayoutPositionProvider {
-        /**
-         * This method fetches the {@link Rect} used to position the selection editor layout.
-         * @return The {@link Rect} indicates where to show the selection editor layout.
-         */
-        Rect getSelectionEditorPositionRect();
-    }
-
     // TODO(meiliang): inflates R.layout.tab_selection_editor_layout in
     // TabSelectionEditorCoordinator.
     public TabSelectionEditorLayout(Context context, AttributeSet attrs) {
@@ -60,28 +50,15 @@
      * @param adapter The adapter that provides views that represent items in the recycler view.
      * @param selectionDelegate The {@link SelectionDelegate} that will inform the toolbar of
      *                            selection changes.
-     * @param positionProvider The {@link TabSelectionEditorLayoutPositionProvider} that provides
-     *         the position rect to show the selection editor layout.
      */
     void initialize(View parentView, RecyclerView recyclerView, RecyclerView.Adapter adapter,
-            SelectionDelegate<Integer> selectionDelegate,
-            @Nullable TabSelectionEditorLayoutPositionProvider positionProvider) {
+            SelectionDelegate<Integer> selectionDelegate) {
         mIsInitialized = true;
         initializeRecyclerView(adapter, recyclerView);
         mToolbar =
                 (TabSelectionEditorToolbar) initializeToolbar(R.layout.tab_selection_editor_toolbar,
                         selectionDelegate, 0, 0, 0, null, false, true);
         mParentView = parentView;
-        mPositionProvider = positionProvider;
-        // Re-fetch the position rect and re-show the selection editor in case there is a global
-        // layout change on parent view, e.g. re-layout caused by orientation change.
-        mParentLayoutListener = () -> {
-            if (mWindow.isShowing() && mPositionProvider != null) {
-                hide();
-                show();
-            }
-        };
-        mParentView.getViewTreeObserver().addOnGlobalLayoutListener(mParentLayoutListener);
     }
 
     /**
@@ -89,14 +66,14 @@
      */
     public void show() {
         assert mIsInitialized;
-        if (mPositionProvider == null) {
+        if (mPositionRect == null) {
             mWindow.showAtLocation(mParentView, Gravity.CENTER, 0, 0);
             return;
         }
-        Rect rect = mPositionProvider.getSelectionEditorPositionRect();
-        mWindow.setWidth(rect.width());
-        mWindow.setHeight(rect.height());
-        mWindow.showAtLocation(mParentView, Gravity.NO_GRAVITY, rect.left, rect.top);
+        mWindow.setWidth(mPositionRect.width());
+        mWindow.setHeight(mPositionRect.height());
+        mWindow.showAtLocation(
+                mParentView, Gravity.NO_GRAVITY, mPositionRect.left, mPositionRect.top);
     }
 
     /**
@@ -115,6 +92,32 @@
     }
 
     /**
+     * Register a {@link ViewTreeObserver.OnGlobalLayoutListener} handling TabSelectionEditor
+     * related changes when parent view global layout changed.
+     */
+    public void registerGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
+        if (mParentView == null || listener == null) return;
+        if (mParentLayoutListener != null) {
+            mParentView.getViewTreeObserver().removeOnGlobalLayoutListener(mParentLayoutListener);
+        }
+        mParentLayoutListener = listener;
+        mParentView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
+    }
+
+    /**
+     * Update the {@link Rect} used to show the selection editor. If the editor is currently
+     * showing, update its positioning.
+     * @param rect  The {@link Rect} to update mPositionRect.
+     */
+    public void updateTabSelectionEditorPositionRect(@NonNull Rect rect) {
+        assert rect != null;
+        mPositionRect = rect;
+        if (mWindow.isShowing()) {
+            mWindow.update(rect.left, rect.top, rect.width(), rect.height());
+        }
+    }
+
+    /**
      * Destroy any members that needs clean up.
      */
     public void destroy() {
@@ -122,4 +125,9 @@
             mParentView.getViewTreeObserver().removeOnGlobalLayoutListener(mParentLayoutListener);
         }
     }
+
+    @VisibleForTesting
+    Rect getPositionRectForTesting() {
+        return mPositionRect;
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayoutBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayoutBinder.java
index a728eed..375b2e3 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayoutBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayoutBinder.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import android.graphics.Rect;
+
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -49,6 +51,17 @@
                 == propertyKey) {
             view.getToolbar().setActionButtonEnablingThreshold(model.get(
                     TabSelectionEditorProperties.TOOLBAR_ACTION_BUTTON_ENABLING_THRESHOLD));
+        } else if (TabSelectionEditorProperties.SELECTION_EDITOR_POSITION_RECT == propertyKey) {
+            Rect positionRect =
+                    model.get(TabSelectionEditorProperties.SELECTION_EDITOR_POSITION_RECT);
+            if (positionRect == null) {
+                return;
+            }
+            view.updateTabSelectionEditorPositionRect(positionRect);
+        } else if (TabSelectionEditorProperties.SELECTION_EDITOR_GLOBAL_LAYOUT_LISTENER
+                == propertyKey) {
+            view.registerGlobalLayoutListener(model.get(
+                    TabSelectionEditorProperties.SELECTION_EDITOR_GLOBAL_LAYOUT_LISTENER));
         }
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
index e468ca1..b2100be 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
@@ -6,10 +6,12 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.graphics.Rect;
 import android.support.annotation.ColorInt;
 import android.support.v7.content.res.AppCompatResources;
 import android.view.View;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import org.chromium.base.ApiCompatibilityUtils;
@@ -47,6 +49,19 @@
         void resetWithListOfTabs(@Nullable List<Tab> tabs);
     }
 
+    /**
+     * An interface to provide the {@link Rect} used to position the selection editor on screen.
+     */
+    public interface TabSelectionEditorPositionProvider {
+        /**
+         * This method fetches the {@link Rect} used to position the selection editor layout.
+         * @return The {@link Rect} indicates where to show the selection editor layout. This Rect
+         * should never be null.
+         */
+        @NonNull
+        Rect getSelectionEditorPositionRect();
+    }
+
     private final Context mContext;
     private final TabModelSelector mTabModelSelector;
     private final ResetHandler mResetHandler;
@@ -54,6 +69,7 @@
     private final SelectionDelegate<Integer> mSelectionDelegate;
     private final TabModelSelectorTabModelObserver mTabModelObserver;
     private final TabModelSelectorObserver mTabModelSelectorObserver;
+    private final TabSelectionEditorPositionProvider mPositionProvider;
     private TabSelectionEditorActionProvider mActionProvider;
 
     private final View.OnClickListener mNavigationClickListener = new View.OnClickListener() {
@@ -81,12 +97,15 @@
 
     TabSelectionEditorMediator(Context context, TabModelSelector tabModelSelector,
             ResetHandler resetHandler, PropertyModel model,
-            SelectionDelegate<Integer> selectionDelegate) {
+            SelectionDelegate<Integer> selectionDelegate,
+            @Nullable TabSelectionEditorMediator
+                    .TabSelectionEditorPositionProvider positionProvider) {
         mContext = context;
         mTabModelSelector = tabModelSelector;
         mResetHandler = resetHandler;
         mModel = model;
         mSelectionDelegate = selectionDelegate;
+        mPositionProvider = positionProvider;
 
         mModel.set(
                 TabSelectionEditorProperties.TOOLBAR_NAVIGATION_LISTENER, mNavigationClickListener);
@@ -144,6 +163,14 @@
         // Default action for action button is to group selected tabs.
         mActionProvider = new TabSelectionEditorActionProvider(mTabModelSelector, this,
                 TabSelectionEditorActionProvider.TabSelectionEditorAction.GROUP);
+
+        if (mPositionProvider != null) {
+            mModel.set(TabSelectionEditorProperties.SELECTION_EDITOR_GLOBAL_LAYOUT_LISTENER,
+                    ()
+                            -> mModel.set(
+                                    TabSelectionEditorProperties.SELECTION_EDITOR_POSITION_RECT,
+                                    mPositionProvider.getSelectionEditorPositionRect()));
+        }
     }
 
     private boolean isEditorVisible() {
@@ -157,6 +184,10 @@
     public void show(List<Tab> tabs) {
         mResetHandler.resetWithListOfTabs(tabs);
         mSelectionDelegate.setSelectionModeEnabledForZeroItems(true);
+        if (mPositionProvider != null) {
+            mModel.set(TabSelectionEditorProperties.SELECTION_EDITOR_POSITION_RECT,
+                    mPositionProvider.getSelectionEditorPositionRect());
+        }
         mModel.set(TabSelectionEditorProperties.IS_VISIBLE, true);
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorProperties.java
index 477da466..6f46377 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorProperties.java
@@ -5,7 +5,9 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import android.content.res.ColorStateList;
+import android.graphics.Rect;
 import android.view.View;
+import android.view.ViewTreeObserver;
 
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -45,8 +47,17 @@
     public static final PropertyModel.WritableIntPropertyKey TOOLBAR_TEXT_APPEARANCE =
             new PropertyModel.WritableIntPropertyKey();
 
+    public static final PropertyModel
+            .WritableObjectPropertyKey<Rect> SELECTION_EDITOR_POSITION_RECT =
+            new PropertyModel.WritableObjectPropertyKey<>();
+
+    public static final PropertyModel.WritableObjectPropertyKey<
+            ViewTreeObserver.OnGlobalLayoutListener> SELECTION_EDITOR_GLOBAL_LAYOUT_LISTENER =
+            new PropertyModel.WritableObjectPropertyKey<>();
+
     public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {IS_VISIBLE,
             TOOLBAR_ACTION_BUTTON_LISTENER, TOOLBAR_ACTION_BUTTON_TEXT,
             TOOLBAR_ACTION_BUTTON_ENABLING_THRESHOLD, TOOLBAR_NAVIGATION_LISTENER, PRIMARY_COLOR,
-            TOOLBAR_BACKGROUND_COLOR, TOOLBAR_GROUP_BUTTON_TINT, TOOLBAR_TEXT_APPEARANCE};
+            TOOLBAR_BACKGROUND_COLOR, TOOLBAR_GROUP_BUTTON_TINT, TOOLBAR_TEXT_APPEARANCE,
+            SELECTION_EDITOR_POSITION_RECT, SELECTION_EDITOR_GLOBAL_LAYOUT_LISTENER};
 }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayoutBinderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayoutBinderTest.java
index ea1bb65..e6f2ca11 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayoutBinderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayoutBinderTest.java
@@ -4,16 +4,26 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.graphics.Rect;
 import android.support.annotation.NonNull;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
 import android.support.v7.widget.RecyclerView;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -21,17 +31,11 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
 /**
  * Tests for {@link TabSelectionEditorLayoutBinder}.
  */
@@ -41,21 +45,20 @@
     private PropertyModel mModel = new PropertyModel(TabSelectionEditorProperties.ALL_KEYS);
     private PropertyModelChangeProcessor mMCP;
     private SelectionDelegate<Integer> mSelectionDelegate = new SelectionDelegate<>();
+    private ViewGroup mParentView;
 
     @Override
     public void setUpTest() throws Exception {
         super.setUpTest();
 
-        ViewGroup view = new LinearLayout(getActivity());
-        TabSelectionEditorLayout.TabSelectionEditorLayoutPositionProvider positionProvider =
-                () -> null;
+        mParentView = new LinearLayout(getActivity());
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            getActivity().setContentView(view);
+            getActivity().setContentView(mParentView);
             mEditorLayoutView =
                     (TabSelectionEditorLayout) getActivity().getLayoutInflater().inflate(
                             R.layout.tab_selection_editor_layout, null);
-            mEditorLayoutView.initialize(view, null, new RecyclerView.Adapter() {
+            mEditorLayoutView.initialize(mParentView, null, new RecyclerView.Adapter() {
                 @NonNull
                 @Override
                 public RecyclerView.ViewHolder onCreateViewHolder(
@@ -70,7 +73,7 @@
                 public int getItemCount() {
                     return 0;
                 }
-            }, mSelectionDelegate, positionProvider);
+            }, mSelectionDelegate);
         });
         mMCP = PropertyModelChangeProcessor.create(
                 mModel, mEditorLayoutView, TabSelectionEditorLayoutBinder::bind);
@@ -121,4 +124,30 @@
         mSelectionDelegate.setSelectedItems(selectedItem);
         assertFalse(button.isEnabled());
     }
-}
\ No newline at end of file
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testSetPositionRect() {
+        Assert.assertNull(mEditorLayoutView.getPositionRectForTesting());
+
+        Rect rect = new Rect();
+        mModel.set(TabSelectionEditorProperties.SELECTION_EDITOR_POSITION_RECT, rect);
+
+        assertEquals(rect, mModel.get(TabSelectionEditorProperties.SELECTION_EDITOR_POSITION_RECT));
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testRegisterGlobalLayoutListener() {
+        AtomicBoolean globalLayoutChanged = new AtomicBoolean();
+        globalLayoutChanged.set(false);
+
+        ViewTreeObserver.OnGlobalLayoutListener listener = () -> globalLayoutChanged.set(true);
+        mModel.set(TabSelectionEditorProperties.SELECTION_EDITOR_GLOBAL_LAYOUT_LISTENER, listener);
+        mParentView.getViewTreeObserver().dispatchOnGlobalLayout();
+
+        assertTrue(globalLayoutChanged.get());
+    }
+}
diff --git a/chrome/android/java/res/drawable-hdpi/safetytip_shield.png b/chrome/android/java/res/drawable-hdpi/safetytip_shield.png
index f869686..d94ea999 100644
--- a/chrome/android/java/res/drawable-hdpi/safetytip_shield.png
+++ b/chrome/android/java/res/drawable-hdpi/safetytip_shield.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/safetytip_shield.png b/chrome/android/java/res/drawable-mdpi/safetytip_shield.png
index 4a2b1f7..0cbba6c 100644
--- a/chrome/android/java/res/drawable-mdpi/safetytip_shield.png
+++ b/chrome/android/java/res/drawable-mdpi/safetytip_shield.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/safetytip_shield.png b/chrome/android/java/res/drawable-xhdpi/safetytip_shield.png
index bf1c828d..5df963e 100644
--- a/chrome/android/java/res/drawable-xhdpi/safetytip_shield.png
+++ b/chrome/android/java/res/drawable-xhdpi/safetytip_shield.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/safetytip_shield.png b/chrome/android/java/res/drawable-xxhdpi/safetytip_shield.png
index 5a2a54c..76cfbd6 100644
--- a/chrome/android/java/res/drawable-xxhdpi/safetytip_shield.png
+++ b/chrome/android/java/res/drawable-xxhdpi/safetytip_shield.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/safetytip_shield.png b/chrome/android/java/res/drawable-xxxhdpi/safetytip_shield.png
index 55c1343..c2cda480 100644
--- a/chrome/android/java/res/drawable-xxxhdpi/safetytip_shield.png
+++ b/chrome/android/java/res/drawable-xxxhdpi/safetytip_shield.png
Binary files differ
diff --git a/chrome/android/java/res/layout/ephemeral_tab_toolbar.xml b/chrome/android/java/res/layout/ephemeral_tab_toolbar.xml
index 7c5102d..9229373 100644
--- a/chrome/android/java/res/layout/ephemeral_tab_toolbar.xml
+++ b/chrome/android/java/res/layout/ephemeral_tab_toolbar.xml
@@ -33,8 +33,6 @@
                 android:layout_marginEnd="8dp"
                 android:layout_marginTop="12dp"
                 android:padding="12dp"
-                android:src="@drawable/ic_chrome"
-                app:tint="@color/default_icon_color_blue"
                 android:scaleType="fitCenter"
                 tools:ignore="ContentDescription" />
 
diff --git a/chrome/android/java/res/layout/omnibox_answer_suggestion.xml b/chrome/android/java/res/layout/omnibox_answer_suggestion.xml
index cbd6c39..2aee747 100644
--- a/chrome/android/java/res/layout/omnibox_answer_suggestion.xml
+++ b/chrome/android/java/res/layout/omnibox_answer_suggestion.xml
@@ -2,75 +2,32 @@
 <!-- Copyright 2019 The Chromium Authors. All rights reserved.
     Use of this source code is governed by a BSD-style license that can be
     found in the LICENSE file. -->
-<org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionView
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:clickable="false"
-    android:focusable="false"
+    android:id="@+id/omnibox_answer"
+    android:layout_width="match_parent"
     android:layout_height="@dimen/omnibox_suggestion_answer_height"
-    android:layout_width="match_parent">
+    android:layout_centerVertical="true"
+    android:orientation="vertical"
+    android:paddingVertical="10dp">
 
-    <view class="org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionView$FocusableView"
-        android:background="?attr/selectableItemBackground"
-        android:clickable="true"
-        android:focusable="true"
-        android:id="@+id/omnibox_answer"
-        android:layout_alignParentStart="true"
-        android:layout_centerVertical="true"
-        android:paddingVertical="10dp"
+    <TextView
+        android:ellipsize="end"
+        android:id="@+id/omnibox_answer_line_1"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_toStartOf="@+id/omnibox_answer_refine_icon"
-        android:layout_width="0dp">
+        android:maxLines="1"
+        android:singleLine="true"
+        android:textAlignment="viewStart" />
 
-        <ImageView
-            android:contentDescription="@null"
-            android:id="@+id/omnibox_answer_icon"
-            android:layout_centerVertical="true"
-            android:layout_height="36dp"
-            android:layout_marginEnd="@dimen/omnibox_suggestion_36dp_icon_margin_end"
-            android:layout_marginStart="@dimen/omnibox_suggestion_36dp_icon_margin_start"
-            android:layout_width="36dp"
-            android:scaleType="fitCenter" />
+    <TextView
+        android:ellipsize="end"
+        android:id="@+id/omnibox_answer_line_2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:maxLines="3"
+        android:singleLine="false"
+        android:textAlignment="viewStart" />
 
-        <TextView
-            android:ellipsize="end"
-            android:id="@+id/omnibox_answer_line_1"
-            android:layout_alignParentEnd="true"
-            android:layout_height="wrap_content"
-            android:layout_toEndOf="@id/omnibox_answer_icon"
-            android:layout_width="0dp"
-            android:maxLines="1"
-            android:singleLine="true"
-            android:textAlignment="viewStart" />
-
-        <TextView
-            android:ellipsize="end"
-            android:id="@+id/omnibox_answer_line_2"
-            android:layout_alignEnd="@id/omnibox_answer_line_1"
-            android:layout_alignStart="@id/omnibox_answer_line_1"
-            android:layout_below="@id/omnibox_answer_line_1"
-            android:layout_height="wrap_content"
-            android:layout_width="0dp"
-            android:maxLines="3"
-            android:singleLine="false"
-            android:textAlignment="viewStart" />
-
-    </view>
-
-    <ImageView
-        android:background="?attr/selectableItemBackground"
-        android:clickable="true"
-        android:contentDescription="@string/accessibility_omnibox_btn_refine"
-        android:focusable="true"
-        android:id="@id/omnibox_answer_refine_icon"
-        android:layout_alignBottom="@id/omnibox_answer"
-        android:layout_alignParentEnd="true"
-        android:layout_alignTop="@id/omnibox_answer"
-        android:layout_centerVertical="true"
-        android:layout_height="match_parent"
-        android:layout_marginEnd="@dimen/omnibox_suggestion_refine_view_modern_end_padding"
-        android:layout_width="@dimen/omnibox_suggestion_refine_width"
-        android:scaleType="center"
-        android:src="@drawable/btn_suggestion_refine" />
-
-</org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionView>
+</LinearLayout>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
index 6ed0901a..59d3633 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
@@ -19,7 +19,6 @@
 import org.chromium.base.CommandLineInitUtil;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.JNIUtils;
-import org.chromium.base.Log;
 import org.chromium.base.PathUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.annotations.MainDex;
@@ -54,7 +53,6 @@
  */
 public class ChromeApplication extends Application {
     private static final String COMMAND_LINE_FILE = "chrome-command-line";
-    private static final String TAG = "ChromiumApplication";
     // Public to allow use in ChromeBackupAgent
     public static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
 
@@ -170,8 +168,7 @@
         // out-of-date application. Kill old applications in this bad state. See
         // http://crbug.com/658130 for more context and http://b.android.com/56296 for the bug.
         if (ContextUtils.getApplicationAssets() == null) {
-            Log.e(TAG, "getResources() null, closing app.");
-            System.exit(0);
+            throw new RuntimeException("App out of date, getResources() null, closing app.");
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index c22630e..da03f96 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -106,6 +106,7 @@
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.omaha.OmahaBase;
+import org.chromium.chrome.browser.omnibox.LocationBar;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
@@ -1532,7 +1533,9 @@
                     assert false : "Unknown TabOpenType: " + tabOpenType;
                     break;
             }
-            getToolbarManager().setUrlBarFocusOnceNativeInitialized(focus);
+            getToolbarManager().setUrlBarFocusOnceNativeInitialized(focus,
+                    focus ? LocationBar.OmniboxFocusReason.LAUNCH_NEW_INCOGNITO_TAB
+                          : LocationBar.OmniboxFocusReason.UNFOCUS);
         }
 
         @Override
@@ -1828,7 +1831,8 @@
             boolean isUrlBarVisible = !mOverviewModeController.overviewVisible()
                     && (!isTablet() || getCurrentTabModel().getCount() != 0);
             if (isUrlBarVisible) {
-                getToolbarManager().setUrlBarFocus(true);
+                getToolbarManager().setUrlBarFocus(
+                        true, LocationBar.OmniboxFocusReason.MENU_OR_KEYBOARD_ACTION);
             }
         } else if (id == R.id.downloads_menu_id) {
             DownloadUtils.showDownloadManager(this, currentTab, DownloadOpenSource.MENU);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index 88296db..c5893e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -395,6 +395,7 @@
      * Handles launching a {@link ChromeTabbedActivity}.
      */
     @SuppressLint("InlinedApi")
+    @SuppressWarnings("checkstyle:SystemExitCheck") // Allowed due to https://crbug.com/847921#c17.
     private @Action int dispatchToTabbedActivity() {
         if (mIsVrIntent) {
             for (Activity activity : ApplicationStatus.getRunningActivities()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/SingleTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/SingleTabActivity.java
index 6f3b264..21f57c3b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/SingleTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/SingleTabActivity.java
@@ -91,14 +91,19 @@
             tab = TabBuilder.createFromFrozenState()
                           .setId(tabId)
                           .setWindow(getWindowAndroid())
+                          .setDelegateFactory(createTabDelegateFactory())
+                          .setTabState(tabState)
+                          .setUnfreeze(unfreeze)
                           .build();
         } else {
             tab = new TabBuilder()
                           .setWindow(getWindowAndroid())
                           .setLaunchType(TabLaunchType.FROM_CHROME_UI)
+                          .setDelegateFactory(createTabDelegateFactory())
+                          .setTabState(tabState)
+                          .setUnfreeze(unfreeze)
                           .build();
         }
-        tab.initialize(null, createTabDelegateFactory(), false, tabState, unfreeze);
         return tab;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
index 3b362c4d..9714ef70 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
@@ -9,6 +9,7 @@
 import android.os.Handler;
 import android.support.annotation.DrawableRes;
 import android.text.TextUtils;
+import android.view.View;
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.R;
@@ -20,6 +21,7 @@
 import org.chromium.chrome.browser.favicon.FaviconUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.ssl.SecurityStateModel;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
 import org.chromium.chrome.browser.ui.widget.RoundedIconGenerator;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
@@ -36,7 +38,7 @@
  * Central class for ephemeral tab, responsible for spinning off other classes necessary to display
  * the preview tab UI.
  */
-public class EphemeralTabCoordinator {
+public class EphemeralTabCoordinator implements View.OnLayoutChangeListener {
     /** The delay (four video frames) after which the hide progress will be hidden. */
     private static final long HIDE_PROGRESS_BAR_DELAY_MS = (1000 / 60) * 4;
 
@@ -78,10 +80,7 @@
     public void requestOpenSheet(String url, String title, boolean isIncognito) {
         mUrl = url;
         mIsIncognito = isIncognito;
-        if (mSheetContent == null) {
-            mSheetContent = new EphemeralTabSheetContent(
-                    mActivity, () -> openInNewTab(), () -> onToolbarClick());
-        }
+        if (mSheetContent == null) mSheetContent = createSheetContent();
 
         getContent().loadUrl(url, true);
         getContent().updateBrowserControlsState(true);
@@ -119,6 +118,8 @@
             mWebContentsObserver.destroy();
             mWebContentsObserver = null;
         }
+
+        mActivity.getWindow().getDecorView().removeOnLayoutChangeListener(this);
     }
 
     private void openInNewTab() {
@@ -146,6 +147,28 @@
         mSheetContent.startFaviconAnimation(drawable);
     }
 
+    private EphemeralTabSheetContent createSheetContent() {
+        mSheetContent = new EphemeralTabSheetContent(
+                mActivity, () -> openInNewTab(), () -> onToolbarClick(), getMaxSheetHeight());
+
+        mActivity.getWindow().getDecorView().addOnLayoutChangeListener(this);
+        return mSheetContent;
+    }
+
+    @Override
+    public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft,
+            int oldTop, int oldRight, int oldBottom) {
+        if (mSheetContent == null) return;
+        if ((oldBottom - oldTop) == (bottom - top)) return;
+        mSheetContent.updateContentHeight(getMaxSheetHeight());
+    }
+
+    private int getMaxSheetHeight() {
+        Tab tab = mActivity.getActivityTabProvider().get();
+        if (tab == null) return 0;
+        return (int) (tab.getHeight() * 0.9f);
+    }
+
     private WebContentsObserver createWebContentsObserver() {
         return new WebContentsObserver(mPanelContent.getWebContents()) {
             @Override
@@ -196,6 +219,7 @@
 
         @Override
         public void onSSLStateUpdated() {
+            if (mSheetContent == null) return;
             int securityLevel = SecurityStateModel.getSecurityLevelForWebContents(
                     mPanelContent.getWebContents());
             mSheetContent.setSecurityIcon(getSecurityIconResource(securityLevel));
@@ -258,9 +282,15 @@
          */
         public void loadFavicon(final String url, Callback<Drawable> callback) {
             FaviconHelper.FaviconImageCallback imageCallback = (bitmap, iconUrl) -> {
-                Drawable drawable =
-                        FaviconUtils.getIconDrawableWithFilter(bitmap, url, mIconGenerator,
-                                mDefaultFaviconHelper, mContext.getResources(), mFaviconSize);
+                Drawable drawable;
+                if (bitmap != null) {
+                    drawable = FaviconUtils.createRoundedBitmapDrawable(
+                            mContext.getResources(), bitmap);
+                } else {
+                    drawable = mDefaultFaviconHelper.getDefaultFaviconDrawable(
+                            mContext.getResources(), iconUrl, true);
+                }
+
                 callback.onResult(drawable);
             };
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java
index 7db25b1..6bbfc81 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java
@@ -39,6 +39,7 @@
     private final Context mContext;
     private final Runnable mOpenNewTabCallback;
     private final Runnable mToolbarClickCallback;
+    private final int mToolbarHeightPx;
 
     private ViewGroup mToolbarView;
     private ViewGroup mSheetContentView;
@@ -55,14 +56,17 @@
      * @param context An Android context.
      * @param openNewTabCallback Callback invoked to open a new tab.
      * @param toolbarClickCallback Callback invoked when user clicks on the toolbar.
+     * @param maxSheetHeight The height of the sheet in full height position.
      */
-    public EphemeralTabSheetContent(
-            Context context, Runnable openNewTabCallback, Runnable toolbarClickCallback) {
+    public EphemeralTabSheetContent(Context context, Runnable openNewTabCallback,
+            Runnable toolbarClickCallback, int maxSheetHeight) {
         mContext = context;
         mOpenNewTabCallback = openNewTabCallback;
         mToolbarClickCallback = toolbarClickCallback;
+        mToolbarHeightPx =
+                mContext.getResources().getDimensionPixelSize(R.dimen.preview_tab_toolbar_height);
 
-        createThinWebView();
+        createThinWebView(maxSheetHeight);
         createToolbarView();
     }
 
@@ -84,15 +88,15 @@
      * Create a ThinWebView, add it to the view hierarchy, which represents the contents of the
      * bottom sheet.
      */
-    private void createThinWebView() {
+    private void createThinWebView(int maxSheetHeight) {
         mThinWebView = ThinWebViewFactory.create(mContext, new ActivityWindowAndroid(mContext));
 
         mSheetContentView = new FrameLayout(mContext);
+        mThinWebView.getView().setLayoutParams(new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, maxSheetHeight - mToolbarHeightPx));
         mSheetContentView.addView(mThinWebView.getView());
 
-        int topPadding =
-                mContext.getResources().getDimensionPixelSize(R.dimen.preview_tab_toolbar_height);
-        mSheetContentView.setPadding(0, topPadding, 0, 0);
+        mSheetContentView.setPadding(0, mToolbarHeightPx, 0, 0);
     }
 
     private void createToolbarView() {
@@ -112,9 +116,25 @@
         mCurrentFavicon = mFaviconView.getDrawable();
     }
 
+    /**
+     * Resizes the thin webview as per the given sheet height. This should never be more than the
+     * tab height for it to function correctly.
+     * @param maxContentHeight The height of the bottom sheet in the maximized state.
+     */
+    void updateContentHeight(int maxContentHeight) {
+        if (maxContentHeight == 0) return;
+        ViewGroup.LayoutParams layoutParams = mThinWebView.getView().getLayoutParams();
+        layoutParams.height = maxContentHeight - mToolbarHeightPx;
+        mSheetContentView.requestLayout();
+    }
+
     /** Method to be called to start the favicon anmiation. */
     public void startFaviconAnimation(Drawable favicon) {
-        assert favicon != null;
+        if (favicon == null) {
+            mCurrentFavicon = null;
+            mFaviconView.setImageDrawable(null);
+            return;
+        }
 
         // TODO(shaktisahu): Find out if there is a better way for this animation.
         Drawable presentedDrawable = favicon;
@@ -211,11 +231,6 @@
     }
 
     @Override
-    public float getCustomFullRatio() {
-        return 0.9f;
-    }
-
-    @Override
     public int getSheetContentDescriptionStringId() {
         return R.string.ephemeral_tab_sheet_description;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
index 62fe973..de4b2c6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.util.ColorUtils;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.ui.resources.ResourceManager;
 
 /**
@@ -145,7 +146,8 @@
     protected int getTabListBackgroundColor(Context context) {
         int colorId = R.color.modern_primary_color;
 
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID)) {
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID)
+                || FeatureUtilities.isGridTabSwitcherEnabled()) {
             if (mTabModelSelector != null && mTabModelSelector.isIncognitoSelected()) {
                 colorId = R.color.incognito_modern_primary_color;
             } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
index 6b025cf..55d0b08 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
@@ -117,8 +117,9 @@
         Tab tab = new TabBuilder()
                           .setWindow(new WindowAndroid(context))
                           .setLaunchType(TabLaunchType.FROM_SPECULATIVE_BACKGROUND_CREATION)
+                          .setDelegateFactory(CustomTabDelegateFactory.createDummy())
+                          .setInitiallyHidden(true)
                           .build();
-        tab.initialize(null, CustomTabDelegateFactory.createDummy(), true, null, false);
 
         // Resize the webContent to avoid expensive post load resize when attaching the tab.
         Rect bounds = ExternalPrerenderHandler.estimateContentSize(context, false);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index cf245a5..c0cabc13 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -355,7 +355,7 @@
 
     private Tab createTab() {
         WebContents webContents = takeWebContents();
-        Tab tab = mTabFactory.createTab();
+        Tab tab = mTabFactory.createTab(webContents, mCustomTabDelegateFactory.get());
         int launchSource = mIntent.getIntExtra(
                 CustomTabIntentDataProvider.EXTRA_BROWSER_LAUNCH_SOURCE, LaunchSourceType.OTHER);
         if (launchSource == LaunchSourceType.WEBAPK) {
@@ -366,9 +366,6 @@
                     mConnection.getClientPackageNameForSession(mSession));
         }
 
-        tab.initialize(webContents, mCustomTabDelegateFactory.get(), false /*initiallyHidden*/,
-                null, false /*unfreeze*/);
-
         if (mIntentDataProvider.shouldEnableEmbeddedMediaExperience()) {
             if (tab.getWebContents() != null) tab.getWebContents().notifyRendererPreferenceUpdate();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
index 5686aba3..4e7fc1bc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
@@ -24,6 +24,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
 import org.chromium.chrome.browser.util.IntentUtils;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.ActivityWindowAndroid;
 
 import javax.inject.Inject;
@@ -93,7 +94,7 @@
     }
 
     /** Creates a new tab for a Custom Tab activity */
-    public Tab createTab() {
+    public Tab createTab(WebContents webContents, TabDelegateFactory delegateFactory) {
         Intent intent = mIntentDataProvider.getIntent();
         int assignedTabId =
                 IntentUtils.safeGetIntExtra(intent, IntentHandler.EXTRA_TAB_ID, Tab.INVALID_TAB_ID);
@@ -106,6 +107,8 @@
                 .setIncognito(mIntentDataProvider.isIncognito())
                 .setWindow(mActivityWindowAndroid.get())
                 .setLaunchType(TabLaunchType.FROM_EXTERNAL_APP)
+                .setWebContents(webContents)
+                .setDelegateFactory(delegateFactory)
                 .build();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
index 51d0a14..dda8d369 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.download.home.list.holder;
 
-import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -193,25 +192,33 @@
 
         @Override
         public void maybeResizeImage(Drawable drawable) {
-            if (!(drawable instanceof BitmapDrawable)) return;
+            Matrix matrix = null;
 
-            Matrix matrix = upscaleBitmapIfNecessary(((BitmapDrawable) drawable).getBitmap());
+            if (drawable instanceof BitmapDrawable) {
+                matrix = upscaleBitmapIfNecessary((BitmapDrawable) drawable);
+            }
+
             mImageView.setImageMatrix(matrix);
             mImageView.setScaleType(
                     matrix == null ? ImageView.ScaleType.CENTER_CROP : ImageView.ScaleType.MATRIX);
         }
 
-        private Matrix upscaleBitmapIfNecessary(Bitmap bitmap) {
-            if (bitmap == null) return null;
+        private Matrix upscaleBitmapIfNecessary(BitmapDrawable drawable) {
+            if (drawable == null) return null;
 
-            float scale = computeScaleFactor(bitmap);
+            int width = drawable.getBitmap().getWidth();
+            int height = drawable.getBitmap().getHeight();
+
+            float scale = computeScaleFactor(width, height);
             if (scale <= 1.f) return null;
 
             // Compute the required matrix to scale and center the bitmap.
+            float dx = (mImageView.getWidth() - width * scale) / 2.f;
+            float dy = (mImageView.getHeight() - height * scale) / 2.f;
+
             Matrix matrix = new Matrix();
-            matrix.setScale(scale, scale);
-            matrix.postTranslate((mImageView.getWidth() - scale * bitmap.getWidth()) * 0.5f,
-                    (mImageView.getHeight() - scale * bitmap.getHeight()) * 0.5f);
+            matrix.postScale(scale, scale);
+            matrix.postTranslate(dx, dy);
             return matrix;
         }
 
@@ -220,9 +227,9 @@
          * dimensions. The scaled bitmap will be centered inside the view. No scaling if the
          * dimensions are comparable.
          */
-        private float computeScaleFactor(Bitmap bitmap) {
-            float widthRatio = (float) mImageView.getWidth() / bitmap.getWidth();
-            float heightRatio = (float) mImageView.getHeight() / bitmap.getHeight();
+        private float computeScaleFactor(int width, int height) {
+            float widthRatio = (float) mImageView.getWidth() / width;
+            float heightRatio = (float) mImageView.getHeight() / height;
 
             UmaUtils.recordImageViewRequiredStretch(widthRatio, heightRatio, mFilter);
             if (Math.max(widthRatio, heightRatio) < IMAGE_VIEW_MAX_SCALE_FACTOR) return 1.f;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheet.java
index ae0fa48..097de22 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheet.java
@@ -34,12 +34,6 @@
          * Navigates to the page associated with the given index.
          */
         void navigateToIndex(int index);
-
-        /**
-         * Sets a runnable to execute when the associated Tab is closed.
-         * @param runnable Runnable to execute.
-         */
-        void setTabCloseRunnable(Runnable runnable);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
index 989047e..139278ab0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
@@ -82,8 +82,6 @@
     private final int mContentPadding;
     private final View mParentView;
 
-    private final Runnable mCloseRunnable = () -> close(true);
-
     private static class NavigationItemViewBinder {
         public static void bind(PropertyModel model, View view, PropertyKey propertyKey) {
             if (ItemProperties.ICON == propertyKey) {
@@ -167,7 +165,6 @@
 
     private void expandSheet() {
         mBottomSheetController.get().expandSheet();
-        mDelegate.setTabCloseRunnable(mCloseRunnable);
         GestureNavMetrics.recordHistogram("GestureNavigation.Sheet.Viewed", mForward);
     }
 
@@ -228,7 +225,6 @@
     private void close(boolean animate) {
         if (!isHidden()) mBottomSheetController.get().hideContent(this, animate);
         mBottomSheetController.get().getBottomSheet().removeObserver(mSheetObserver);
-        mDelegate.setTabCloseRunnable(null);
         mMediator.clear();
     }
 
@@ -324,11 +320,6 @@
     }
 
     @Override
-    public boolean hasCustomLifecycle() {
-        return true;
-    }
-
-    @Override
     public boolean hideOnScroll() {
         return false;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/TabbedSheetDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/TabbedSheetDelegate.java
index 619990c..af22c1a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/TabbedSheetDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/TabbedSheetDelegate.java
@@ -6,7 +6,6 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.history.HistoryManagerUtils;
-import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.util.UrlConstants;
 import org.chromium.content_public.browser.NavigationEntry;
@@ -23,25 +22,6 @@
     private final Tab mTab;
     private final String mFullHistoryMenu;
 
-    // TabObserver that monitors tab closing event to close the navigation sheet
-    // together if open. For now this is necessary when closing all incognito tabs
-    // through Android notification.
-    private static class CloseTabObserver extends EmptyTabObserver {
-        private Runnable mCloseRunnable;
-
-        public void setCloseRunnable(Runnable runnable) {
-            mCloseRunnable = runnable;
-        }
-
-        @Override
-        public void onDestroyed(Tab tab) {
-            mCloseRunnable.run();
-            tab.removeObserver(this);
-        }
-    }
-
-    private final CloseTabObserver mCloseTabObserver = new CloseTabObserver();
-
     public TabbedSheetDelegate(Tab tab) {
         mTab = tab;
         mFullHistoryMenu = tab.getActivity().getResources().getString(R.string.show_full_history);
@@ -65,14 +45,4 @@
             mTab.getWebContents().getNavigationController().goToNavigationIndex(index);
         }
     }
-
-    @Override
-    public void setTabCloseRunnable(Runnable runnable) {
-        if (runnable != null) {
-            mCloseTabObserver.setCloseRunnable(runnable);
-            mTab.addObserver(mCloseTabObserver);
-        } else {
-            mTab.removeObserver(mCloseTabObserver);
-        }
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java
index b265fb3..3c83bc11 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java
@@ -55,7 +55,6 @@
     @Override
     public void createContent(InfoBarLayout layout) {
         super.createContent(layout);
-        layout.setIsUsingBigIcon();
         layout.getMessageLayout().addDescription(mDescription);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java b/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
index f9f4bf7..0225f0c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
@@ -176,21 +176,18 @@
             mLoadUrlParams.setReferrer(new Referrer(referrer, ReferrerPolicy.DEFAULT));
         }
 
-        // Create a detached tab and navigate it.
+        // Create a detached tab, but don't add it to the tab model yet. We'll do that
+        // later if the loadUrlParams etc... match.
         mTab = TabBuilder.createLiveTab(false)
                        .setIncognito(false)
                        .setLaunchType(TabLaunchType.FROM_EXTERNAL_APP)
                        .setWindow(mWindowAndroid)
+                       .setWebContents(webContents)
+                       .setDelegateFactory(chromeTabCreator.createDefaultTabDelegateFactory())
                        .build();
 
         mObserver = new StartupTabObserver();
         mTab.addObserver(mObserver);
-
-        // Create and load the tab, but don't add it to the tab model yet. We'll do that
-        // later if the loadUrlParams etc... match.
-        mTab.initialize(webContents, chromeTabCreator.createDefaultTabDelegateFactory(), false,
-                /* tabState */ null,
-                /* unfreeze */ false);
         mTab.loadUrl(mLoadUrlParams);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java
index 046cf88..92c0be3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java
@@ -22,6 +22,7 @@
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
+import org.chromium.chrome.browser.omnibox.LocationBar;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabAttributeKeys;
 import org.chromium.chrome.browser.tab.TabAttributes;
@@ -326,7 +327,8 @@
             // Force toolbar to show and disable overflow menu.
             onTabModalDialogStateChanged(true);
 
-            mChromeActivity.getToolbarManager().setUrlBarFocus(false);
+            mChromeActivity.getToolbarManager().setUrlBarFocus(
+                    false, LocationBar.OmniboxFocusReason.UNFOCUS);
 
             menuButton.setEnabled(false);
         } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/FakeboxDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/FakeboxDelegate.java
new file mode 100644
index 0000000..2be47eed
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/FakeboxDelegate.java
@@ -0,0 +1,56 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ntp;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.chrome.browser.native_page.NativePage;
+import org.chromium.chrome.browser.omnibox.LocationBar.OmniboxFocusReason;
+import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
+import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
+
+/**
+ * Handles user interaction with the fakebox (the URL bar in the NTP and tasks surface).
+ */
+public interface FakeboxDelegate {
+    /**
+     * Signal a {@link UrlBar} focus change request.
+     * @param shouldBeFocused Whether the focus should be requested or cleared. True requests
+     *         focus
+     *        and False clears focus.
+     * @param pastedText The given pasted text when focus, which could be null.
+     * @param reason The given reason.
+     */
+    void setUrlBarFocus(
+            boolean shouldBeFocused, @Nullable String pastedText, @OmniboxFocusReason int reason);
+
+    /**
+     * @return Whether the URL bar is currently focused.
+     */
+    boolean isUrlBarFocused();
+
+    /**
+     * @return whether the provided native page is the one currently displayed to the user.
+     */
+    boolean isCurrentPage(NativePage nativePage);
+
+    /**
+     * Get the {@link LocationBarVoiceRecognitionHandler}.
+     * @return the {@link LocationBarVoiceRecognitionHandler}
+     */
+    LocationBarVoiceRecognitionHandler getLocationBarVoiceRecognitionHandler();
+
+    /**
+     * Adds a URL focus change listener that will be notified when the URL gains or loses focus.
+     * @param listener The listener to be registered.
+     */
+    default void addUrlFocusChangeListener(UrlFocusChangeListener listener) {}
+
+    /**
+     * Removes a URL focus change listener that was previously added.
+     * @param listener The listener to be removed.
+     */
+    default void removeUrlFocusChangeListener(UrlFocusChangeListener listener) {}
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 5216811..32518cd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -41,6 +41,7 @@
 import org.chromium.chrome.browser.ntp.cards.ItemViewType;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter;
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
+import org.chromium.chrome.browser.omnibox.LocationBar.OmniboxFocusReason;
 import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
@@ -150,30 +151,6 @@
     }
 
     /**
-     * Handles user interaction with the fakebox (the URL bar in the NTP).
-     */
-    public interface FakeboxDelegate {
-        /**
-         * @return Whether the URL bar is currently focused.
-         */
-        boolean isUrlBarFocused();
-
-        /**
-         * Focuses the URL bar when the user taps the fakebox, types in the fakebox, or pastes text
-         * into the fakebox.
-         *
-         * @param pastedText The text that was pasted or typed into the fakebox, or null if the user
-         *                   just tapped the fakebox.
-         */
-        void requestUrlFocusFromFakebox(String pastedText);
-
-        /**
-         * @return whether the provided native page is the one currently displayed to the user.
-         */
-        boolean isCurrentPage(NativePage nativePage);
-    }
-
-    /**
      * @param url The URL to check whether it is for the NTP.
      * @return Whether the passed in URL is used to render the NTP.
      */
@@ -230,7 +207,9 @@
                 mVoiceRecognitionHandler.startVoiceRecognition(
                         LocationBarVoiceRecognitionHandler.VoiceInteractionSource.NTP);
             } else if (mFakeboxDelegate != null) {
-                mFakeboxDelegate.requestUrlFocusFromFakebox(pastedText);
+                mFakeboxDelegate.setUrlBarFocus(true, pastedText,
+                        pastedText == null ? OmniboxFocusReason.FAKE_BOX_TAP
+                                           : OmniboxFocusReason.FAKE_BOX_LONG_PRESS);
             }
         }
 
@@ -591,14 +570,8 @@
             mNewTabPageLayout.setUrlFocusChangeAnimationPercent(
                     fakeboxDelegate.isUrlBarFocused() ? 1f : 0f);
         }
-    }
 
-    /**
-     * Sets the {@link LocationBarVoiceRecognitionHandler} this page interacts with.
-     */
-    public void setVoiceRecognitionHandler(
-            LocationBarVoiceRecognitionHandler voiceRecognitionHandler) {
-        mVoiceRecognitionHandler = voiceRecognitionHandler;
+        mVoiceRecognitionHandler = mFakeboxDelegate.getLocationBarVoiceRecognitionHandler();
         if (mVoiceRecognitionHandler != null) {
             mNewTabPageLayout.updateVoiceSearchButtonVisibility();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index d8ea29eb..82fbf8b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -22,7 +22,6 @@
 import org.chromium.chrome.browser.gesturenav.HistoryNavigationDelegateFactory;
 import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
-import org.chromium.chrome.browser.ntp.NewTabPage.FakeboxDelegate;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
index ce25254..42267963 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
@@ -7,7 +7,7 @@
 import android.content.Context;
 import android.graphics.Region;
 
-import org.chromium.chrome.browser.ntp.NewTabPage.FakeboxDelegate;
+import org.chromium.chrome.browser.ntp.FakeboxDelegate;
 import org.chromium.chrome.browser.ntp.NewTabPageLayout;
 import org.chromium.chrome.browser.ntp.SnapScrollHelper;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
index 0bda0f2b..723fdf2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
@@ -9,8 +9,11 @@
 import android.view.ViewGroup;
 import android.view.Window;
 
+import androidx.annotation.IntDef;
+
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.WindowDelegate;
+import org.chromium.chrome.browser.ntp.FakeboxDelegate;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarDelegate;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -20,10 +23,41 @@
 import org.chromium.chrome.browser.toolbar.top.ToolbarActionModeCallback;
 import org.chromium.ui.base.WindowAndroid;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Container that holds the {@link UrlBar} and SSL state related with the current {@link Tab}.
  */
-public interface LocationBar extends UrlBarDelegate {
+public interface LocationBar extends UrlBarDelegate, FakeboxDelegate {
+    /** A means of tracking which mechanism is being used to focus the omnibox. */
+    @IntDef({OmniboxFocusReason.OMNIBOX_TAP, OmniboxFocusReason.OMNIBOX_LONG_PRESS,
+            OmniboxFocusReason.FAKE_BOX_TAP, OmniboxFocusReason.FAKE_BOX_LONG_PRESS,
+            OmniboxFocusReason.ACCELERATOR_TAP, OmniboxFocusReason.TAB_SWITCHER_OMNIBOX_TAP,
+            OmniboxFocusReason.TASKS_SURFACE_FAKE_BOX_TAP,
+            OmniboxFocusReason.TASKS_SURFACE_FAKE_BOX_LONG_PRESS,
+            OmniboxFocusReason.DEFAULT_WITH_HARDWARE_KEYBOARD, OmniboxFocusReason.SEARCH_QUERY,
+            OmniboxFocusReason.LAUNCH_NEW_INCOGNITO_TAB, OmniboxFocusReason.MENU_OR_KEYBOARD_ACTION,
+            OmniboxFocusReason.UNFOCUS})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface OmniboxFocusReason {
+        int OMNIBOX_TAP = 0;
+        int OMNIBOX_LONG_PRESS = 1;
+        int FAKE_BOX_TAP = 2;
+        int FAKE_BOX_LONG_PRESS = 3;
+        int ACCELERATOR_TAP = 4;
+        // TAB_SWITCHER_OMNIBOX_TAP has not been used anymore, keep it for record for now.
+        int TAB_SWITCHER_OMNIBOX_TAP = 5;
+        int TASKS_SURFACE_FAKE_BOX_TAP = 6;
+        int TASKS_SURFACE_FAKE_BOX_LONG_PRESS = 7;
+        int DEFAULT_WITH_HARDWARE_KEYBOARD = 8;
+        int SEARCH_QUERY = 9;
+        int LAUNCH_NEW_INCOGNITO_TAB = 10;
+        int MENU_OR_KEYBOARD_ACTION = 11;
+        int UNFOCUS = 12;
+        int NUM_ENTRIES = 13;
+    }
+
     /**
      * Cleanup resources when this goes out of scope.
      */
@@ -107,25 +141,6 @@
             ActivityTabProvider provider);
 
     /**
-     * Adds a URL focus change listener that will be notified when the URL gains or loses focus.
-     * @param listener The listener to be registered.
-     */
-    default void addUrlFocusChangeListener(UrlFocusChangeListener listener) {}
-
-    /**
-     * Removes a URL focus change listener that was previously added.
-     * @param listener The listener to be removed.
-     */
-    default void removeUrlFocusChangeListener(UrlFocusChangeListener listener) {}
-
-    /**
-     * Signal a {@link UrlBar} focus change request.
-     * @param shouldBeFocused Whether the focus should be requested or cleared. True requests focus
-     *        and False clears focus.
-     */
-    void setUrlBarFocus(boolean shouldBeFocused);
-
-    /**
      * Triggers the cursor to be visible in the UrlBar without triggering any of the focus animation
      * logic.
      * <p>
@@ -134,11 +149,6 @@
     void showUrlBarCursorWithoutFocusAnimations();
 
     /**
-     * @return Whether the UrlBar currently has focus.
-     */
-    boolean isUrlBarFocused();
-
-    /**
      * Selects all of the editable text in the UrlBar.
      */
     void selectAll();
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 0e2456c..7ac6b5f1 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
@@ -30,6 +30,7 @@
 import org.chromium.base.CommandLine;
 import org.chromium.base.ObserverList;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.metrics.CachedMetrics.EnumeratedHistogramSample;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ActivityTabProvider;
@@ -38,9 +39,10 @@
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.native_page.NativePage;
 import org.chromium.chrome.browser.native_page.NativePageFactory;
+import org.chromium.chrome.browser.ntp.FakeboxDelegate;
 import org.chromium.chrome.browser.ntp.NewTabPage;
-import org.chromium.chrome.browser.ntp.NewTabPage.FakeboxDelegate;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
+import org.chromium.chrome.browser.omnibox.LocationBar.OmniboxFocusReason;
 import org.chromium.chrome.browser.omnibox.UrlBar.ScrollType;
 import org.chromium.chrome.browser.omnibox.UrlBarCoordinator.SelectionState;
 import org.chromium.chrome.browser.omnibox.geo.GeolocationHeader;
@@ -56,7 +58,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
-import org.chromium.chrome.browser.toolbar.ToolbarManager;
 import org.chromium.chrome.browser.toolbar.top.ToolbarActionModeCallback;
 import org.chromium.chrome.browser.ui.widget.CompositeTouchDelegate;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
@@ -77,6 +78,10 @@
 public class LocationBarLayout extends FrameLayout
         implements OnClickListener, LocationBar, AutocompleteDelegate, FakeboxDelegate,
                    LocationBarVoiceRecognitionHandler.Delegate {
+    private static final EnumeratedHistogramSample ENUMERATED_FOCUS_REASON =
+            new EnumeratedHistogramSample(
+                    "Android.OmniboxFocusReason", OmniboxFocusReason.NUM_ENTRIES);
+
     protected ImageButton mDeleteButton;
     protected ImageButton mMicButton;
     protected UrlBar mUrlBar;
@@ -259,7 +264,7 @@
                 && newConfig.keyboard != Configuration.KEYBOARD_QWERTY) {
             // If we lose the hardware keyboard and the focus animations were not run, then the
             // user has not typed any text, so we will just clear the focus instead.
-            setUrlBarFocus(false);
+            setUrlBarFocus(false, null, LocationBar.OmniboxFocusReason.UNFOCUS);
         }
     }
 
@@ -366,23 +371,8 @@
     }
 
     @Override
-    public void setUrlBarFocus(boolean shouldBeFocused) {
-        if (shouldBeFocused) {
-            mUrlBar.requestFocus();
-        } else {
-            hideKeyboard();
-            mUrlBar.clearFocus();
-        }
-    }
-
-    @Override
-    public boolean isUrlBarFocused() {
-        return mUrlHasFocus;
-    }
-
-    @Override
     public void clearOmniboxFocus() {
-        setUrlBarFocus(false);
+        setUrlBarFocus(false, null, LocationBar.OmniboxFocusReason.UNFOCUS);
     }
 
     @Override
@@ -534,43 +524,19 @@
     }
 
     @Override
-    public void requestUrlFocusFromFakebox(String pastedText) {
-        mUrlFocusedFromFakebox = true;
-        if (mUrlHasFocus && mUrlFocusedWithoutAnimations) {
-            handleUrlFocusAnimation(mUrlHasFocus);
-        } else {
-            setUrlBarFocus(true);
-        }
-
-        if (pastedText != null) {
-            ToolbarManager.recordOmniboxFocusReason(
-                    ToolbarManager.OmniboxFocusReason.FAKE_BOX_LONG_PRESS);
-            // This must be happen after requestUrlFocus(), which changes the selection.
-            mUrlCoordinator.setUrlBarData(UrlBarData.forNonUrlText(pastedText),
-                    UrlBar.ScrollType.NO_SCROLL, UrlBarCoordinator.SelectionState.SELECT_END);
-            forceOnTextChanged();
-        } else {
-            ToolbarManager.recordOmniboxFocusReason(ToolbarManager.OmniboxFocusReason.FAKE_BOX_TAP);
-        }
-    }
-
-    @Override
     public boolean didFocusUrlFromFakebox() {
         return mUrlFocusedFromFakebox;
     }
 
     @Override
-    public boolean isCurrentPage(NativePage nativePage) {
-        assert nativePage != null;
-        return nativePage == mToolbarDataProvider.getNewTabPageForCurrentTab();
-    }
-
-    @Override
     public void showUrlBarCursorWithoutFocusAnimations() {
         if (mUrlHasFocus || mUrlFocusedFromFakebox) return;
 
         mUrlFocusedWithoutAnimations = true;
-        setUrlBarFocus(true);
+
+        // This interface should only be called to devices with a hardware keyboard attached as
+        // described in the LocationBar.
+        setUrlBarFocus(true, null, LocationBar.OmniboxFocusReason.DEFAULT_WITH_HARDWARE_KEYBOARD);
     }
 
     /**
@@ -588,16 +554,6 @@
     }
 
     @Override
-    public void addUrlFocusChangeListener(UrlFocusChangeListener listener) {
-        mUrlFocusChangeListeners.addObserver(listener);
-    }
-
-    @Override
-    public void removeUrlFocusChangeListener(UrlFocusChangeListener listener) {
-        mUrlFocusChangeListeners.removeObserver(listener);
-    }
-
-    @Override
     public final ToolbarDataProvider getToolbarDataProvider() {
         return mToolbarDataProvider;
     }
@@ -799,7 +755,7 @@
 
         setUrlBarText(UrlBarData.forNonUrlText(query), UrlBar.ScrollType.NO_SCROLL,
                 SelectionState.SELECT_ALL);
-        setUrlBarFocus(true);
+        setUrlBarFocus(true, null, LocationBar.OmniboxFocusReason.SEARCH_QUERY);
         mAutocompleteCoordinator.startAutocompleteForQuery(query);
         post(new Runnable() {
             @Override
@@ -826,7 +782,7 @@
 
     @Override
     public void backKeyPressed() {
-        setUrlBarFocus(false);
+        setUrlBarFocus(false, null, LocationBar.OmniboxFocusReason.UNFOCUS);
         // Revert the URL to match the current page.
         setUrlToPageUrl();
         focusCurrentTab();
@@ -845,6 +801,12 @@
         return mToolbarDataProvider.getDisplaySearchTerms() != null;
     }
 
+    @Override
+    public void gestureDetected(boolean isLongPress) {
+        recordOmniboxFocusReason(isLongPress ? LocationBar.OmniboxFocusReason.OMNIBOX_LONG_PRESS
+                                             : LocationBar.OmniboxFocusReason.OMNIBOX_TAP);
+    }
+
     /**
      * @return Returns the original url of the page.
      */
@@ -870,7 +832,7 @@
                 // If we did not run the focus animations, then the user has not typed any text.
                 // So, clear the focus and accept whatever URL the page is currently attempting to
                 // display. If the NTP is showing, the current page's URL should not be displayed.
-                setUrlBarFocus(false);
+                setUrlBarFocus(false, null, LocationBar.OmniboxFocusReason.UNFOCUS);
             } else {
                 return;
             }
@@ -1016,6 +978,64 @@
     }
 
     @Override
+    public void setUrlBarFocus(
+            boolean shouldBeFocused, @Nullable String pastedText, @OmniboxFocusReason int reason) {
+        if (shouldBeFocused) {
+            if (!mUrlHasFocus) recordOmniboxFocusReason(reason);
+
+            if (reason == LocationBar.OmniboxFocusReason.FAKE_BOX_TAP
+                    || reason == LocationBar.OmniboxFocusReason.FAKE_BOX_LONG_PRESS
+                    || reason == LocationBar.OmniboxFocusReason.TASKS_SURFACE_FAKE_BOX_LONG_PRESS
+                    || reason == LocationBar.OmniboxFocusReason.TASKS_SURFACE_FAKE_BOX_TAP) {
+                mUrlFocusedFromFakebox = true;
+            }
+
+            if (mUrlHasFocus && mUrlFocusedWithoutAnimations) {
+                handleUrlFocusAnimation(mUrlHasFocus);
+            } else {
+                mUrlBar.requestFocus();
+            }
+        } else {
+            assert pastedText == null;
+            hideKeyboard();
+            mUrlBar.clearFocus();
+        }
+
+        if (pastedText != null) {
+            // This must be happen after requestUrlFocus(), which changes the selection.
+            mUrlCoordinator.setUrlBarData(UrlBarData.forNonUrlText(pastedText),
+                    UrlBar.ScrollType.NO_SCROLL, UrlBarCoordinator.SelectionState.SELECT_END);
+            forceOnTextChanged();
+        }
+    }
+
+    @Override
+    public boolean isUrlBarFocused() {
+        return mUrlHasFocus;
+    }
+
+    @Override
+    public boolean isCurrentPage(NativePage nativePage) {
+        assert nativePage != null;
+        return nativePage == mToolbarDataProvider.getNewTabPageForCurrentTab();
+    }
+
+    @Override
+    public LocationBarVoiceRecognitionHandler getLocationBarVoiceRecognitionHandler() {
+        return mVoiceRecognitionHandler;
+    }
+
+    @Override
+    public void addUrlFocusChangeListener(UrlFocusChangeListener listener) {
+        mUrlFocusChangeListeners.addObserver(listener);
+    }
+
+    @Override
+    public void removeUrlFocusChangeListener(UrlFocusChangeListener listener) {
+        mUrlFocusChangeListeners.removeObserver(listener);
+    }
+
+    @Override
     protected void onWindowVisibilityChanged(int visibility) {
         super.onWindowVisibilityChanged(visibility);
         if (visibility == View.VISIBLE) updateMicButtonState();
@@ -1085,7 +1105,6 @@
     @Override
     public void onTabLoadingNTP(NewTabPage ntp) {
         ntp.setFakeboxDelegate(this);
-        ntp.setVoiceRecognitionHandler(mVoiceRecognitionHandler);
     }
 
     @Override
@@ -1119,4 +1138,8 @@
         String textWithAutocomplete = mUrlCoordinator.getTextWithAutocomplete();
         mAutocompleteCoordinator.onTextChanged(textWithoutAutocomplete, textWithAutocomplete);
     }
+
+    private void recordOmniboxFocusReason(@OmniboxFocusReason int reason) {
+        ENUMERATED_FOCUS_REASON.record(reason);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
index 6f64e69..b96bb5e1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
@@ -208,6 +208,7 @@
         }
 
         updateButtonVisibility();
+        mStatusViewCoordinator.setUrlFocusChangePercent(percent);
     }
 
     @Override
@@ -279,7 +280,6 @@
     protected void updateButtonVisibility() {
         super.updateButtonVisibility();
         updateMicButtonVisibility(mUrlFocusChangePercent);
-        updateStatusButtonVisibility(mUrlFocusChangePercent);
     }
 
     @Override
@@ -294,24 +294,6 @@
     }
 
     /**
-     * Updates the display of the status button.
-     *
-     * @param urlFocusChangePercent The completion percentage of the URL focus change animation.
-     */
-    private void updateStatusButtonVisibility(float urlFocusChangePercent) {
-        if (mIconView == null
-                || !SearchEngineLogoUtils.shouldShowSearchEngineLogo(
-                        getToolbarDataProvider().isIncognito())) {
-            return;
-        }
-
-        if (mToolbarDataProvider.getNewTabPageForCurrentTab() != null
-                && mToolbarDataProvider.getNewTabPageForCurrentTab().isLocationBarShownInNTP()) {
-            mIconView.setAlpha(urlFocusChangePercent);
-        }
-    }
-
-    /**
      * @param softInputMode The software input resize mode.
      * @param delay Delay the change in input mode.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandler.java
index 6bfd210ca9..e585e55 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandler.java
@@ -72,12 +72,13 @@
     // VoiceInteractionEventSource defined in tools/metrics/histograms/enums.xml.
     // Do not reorder or remove items, only add new items before HISTOGRAM_BOUNDARY.
     @IntDef({VoiceInteractionSource.OMNIBOX, VoiceInteractionSource.NTP,
-            VoiceInteractionSource.SEARCH_WIDGET})
+            VoiceInteractionSource.SEARCH_WIDGET, VoiceInteractionSource.TASKS_SURFACE})
     public @interface VoiceInteractionSource {
         int OMNIBOX = 0;
         int NTP = 1;
         int SEARCH_WIDGET = 2;
-        int HISTOGRAM_BOUNDARY = 3;
+        int TASKS_SURFACE = 3;
+        int HISTOGRAM_BOUNDARY = 4;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index af4f428..4d2bda3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -42,7 +42,6 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.CachedMetrics;
 import org.chromium.chrome.browser.WindowDelegate;
-import org.chromium.chrome.browser.toolbar.ToolbarManager;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 
 import java.lang.annotation.Retention;
@@ -210,6 +209,12 @@
          *         whatever's in the URL bar verbatim.
          */
         boolean shouldCutCopyVerbatim();
+
+        /**
+         * Called to notify that a tap or long press gesture has been detected.
+         * @param isLongPress Whether or not is a long press gesture.
+         */
+        void gestureDetected(boolean isLongPress);
     }
 
     /** Provides updates about the URL text changes. */
@@ -272,16 +277,14 @@
                 new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
                     @Override
                     public void onLongPress(MotionEvent e) {
-                        ToolbarManager.recordOmniboxFocusReason(
-                                ToolbarManager.OmniboxFocusReason.OMNIBOX_LONG_PRESS);
+                        mUrlBarDelegate.gestureDetected(true);
                         performLongClick();
                     }
 
                     @Override
                     public boolean onSingleTapUp(MotionEvent e) {
                         requestFocus();
-                        ToolbarManager.recordOmniboxFocusReason(
-                                ToolbarManager.OmniboxFocusReason.OMNIBOX_TAP);
+                        mUrlBarDelegate.gestureDetected(false);
                         return true;
                     }
                 }, ThreadUtils.getUiThreadHandler());
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 c3aa3bd..a48305ef 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
@@ -92,6 +92,7 @@
     private UrlBarEditingTextStateProvider mUrlBarEditingTextStateProvider;
     private String mUrlBarTextWithAutocomplete = "";
     private boolean mUrlBarTextIsValidUrl;
+    private float mUrlFocusPercent;
 
     StatusMediator(PropertyModel model, Resources resources,
             UrlBarEditingTextStateProvider urlBarEditingTextStateProvider) {
@@ -232,6 +233,26 @@
     }
 
     /**
+     * Set the url focus change percent.
+     * @param percent The current focus percent.
+     */
+    void setUrlFocusChangePercent(float percent) {
+        mUrlFocusPercent = percent;
+        if (!SearchEngineLogoUtils.shouldShowSearchEngineLogo(
+                    mToolbarCommonPropertiesModel.isIncognito())) {
+            return;
+        }
+
+        if (mToolbarCommonPropertiesModel.getNewTabPageForCurrentTab() != null
+                && mToolbarCommonPropertiesModel.getNewTabPageForCurrentTab()
+                           .isLocationBarShownInNTP()) {
+            mModel.set(StatusProperties.STATUS_ALPHA, percent);
+        }
+
+        updateLocationBarIcon();
+    }
+
+    /**
      * Reports whether the first omnibox suggestion is a search query.
      */
     void setFirstSuggestionIsSearchType(boolean firstSuggestionIsSearchQuery) {
@@ -405,7 +426,8 @@
     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 && mShowStatusIconWhenUrlFocused;
+        boolean showFocused =
+                (mUrlHasFocus || mUrlFocusPercent > 0) && 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
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
index 5932ba2..1f622d21 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
@@ -9,6 +9,7 @@
 
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
@@ -29,6 +30,9 @@
     static final WritableObjectPropertyKey<Bitmap> STATUS_ICON =
             new WritableObjectPropertyKey<>(true);
 
+    /** Specifies the icon alpha. */
+    static final WritableFloatPropertyKey STATUS_ALPHA = new WritableFloatPropertyKey();
+
     /** Specifies accessibility string presented to user upon long click on security icon. */
     public static final WritableIntPropertyKey STATUS_ICON_ACCESSIBILITY_TOAST_RES =
             new WritableIntPropertyKey();
@@ -63,7 +67,7 @@
 
     public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {ANIMATIONS_ENABLED,
             STATUS_ICON_ACCESSIBILITY_TOAST_RES, STATUS_ICON_RES, STATUS_ICON_TINT_RES, STATUS_ICON,
-            STATUS_ICON_DESCRIPTION_RES, SEPARATOR_COLOR_RES, STATUS_CLICK_LISTENER,
+            STATUS_ALPHA, STATUS_ICON_DESCRIPTION_RES, SEPARATOR_COLOR_RES, STATUS_CLICK_LISTENER,
             VERBOSE_STATUS_TEXT_COLOR_RES, VERBOSE_STATUS_TEXT_STRING_RES,
             VERBOSE_STATUS_TEXT_VISIBLE, VERBOSE_STATUS_TEXT_WIDTH, INCOGNITO_BADGE_VISIBLE};
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
index 9aa103d..abdcbc33 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
@@ -317,6 +317,13 @@
         animateStatusIcon();
     }
 
+    /** Specify the status icon alpha. */
+    void setStatusIconAlpha(float alpha) {
+        if (mIconView == null) return;
+        mIconView.setAlpha(alpha);
+        mIconView.setVisibility(alpha > 0f ? VISIBLE : GONE);
+    }
+
     /**
      * Specify accessibility string presented to user upon long click.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
index 31549fc3..110a5283 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
@@ -22,6 +22,8 @@
             view.setStatusIcon(model.get(StatusProperties.STATUS_ICON_RES));
         } else if (StatusProperties.STATUS_ICON.equals(propertyKey)) {
             view.setStatusIcon(model.get(StatusProperties.STATUS_ICON));
+        } else if (StatusProperties.STATUS_ALPHA.equals(propertyKey)) {
+            view.setStatusIconAlpha(model.get(StatusProperties.STATUS_ALPHA));
         } else if (StatusProperties.STATUS_ICON_ACCESSIBILITY_TOAST_RES.equals(propertyKey)) {
             view.setStatusIconAccessibilityToast(
                     model.get(StatusProperties.STATUS_ICON_ACCESSIBILITY_TOAST_RES));
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 75545f9..1f7de9c0 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
@@ -95,6 +95,14 @@
     }
 
     /**
+     * Set the url focus change percent.
+     * @param percent The current focus percent.
+     */
+    public void setUrlFocusChangePercent(float percent) {
+        mMediator.setUrlFocusChangePercent(percent);
+    }
+
+    /**
      * @param useDarkColors Whether dark colors should be for the status icon and text.
      */
     public void setUseDarkColors(boolean useDarkColors) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java
index cdab2c3..e32b714 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java
@@ -24,8 +24,8 @@
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController.OnSuggestionsReceivedListener;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionListViewBinder.SuggestionListViewHolder;
-import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionView;
 import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionViewBinder;
+import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionView;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionView;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewViewBinder;
 import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionProcessor;
@@ -129,9 +129,9 @@
 
                 adapter.registerType(
                         OmniboxSuggestionUiType.ANSWER_SUGGESTION,
-                        () -> (AnswerSuggestionView) LayoutInflater.from(mListView.getContext())
-                                .inflate(R.layout.omnibox_answer_suggestion, null),
-                        AnswerSuggestionViewBinder::bind);
+                        () -> new BaseSuggestionView(mListView.getContext(),
+                                                     R.layout.omnibox_answer_suggestion),
+                        new AnswerSuggestionViewBinder());
 
                 adapter.registerType(
                         OmniboxSuggestionUiType.ENTITY_SUGGESTION,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
index 6399aae..bfe5add 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
@@ -6,9 +6,11 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.support.annotation.DrawableRes;
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.GlobalDiscardableReferencePool;
 import org.chromium.chrome.browser.image_fetcher.ImageFetcher;
 import org.chromium.chrome.browser.image_fetcher.ImageFetcherConfig;
@@ -17,10 +19,9 @@
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
-import org.chromium.chrome.browser.omnibox.suggestions.SuggestionProcessor;
-import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionViewProperties.AnswerIcon;
+import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProcessor;
+import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
 import org.chromium.chrome.browser.util.ConversionUtils;
 import org.chromium.components.omnibox.AnswerType;
 import org.chromium.components.omnibox.SuggestionAnswer;
@@ -32,14 +33,13 @@
 import java.util.Map;
 
 /** A class that handles model and view creation for the most commonly used omnibox suggestion. */
-public class AnswerSuggestionProcessor implements SuggestionProcessor {
+public class AnswerSuggestionProcessor extends BaseSuggestionViewProcessor {
     private static final int MAX_CACHE_SIZE = 500 * ConversionUtils.BYTES_PER_KILOBYTE;
-
     private final Map<String, List<PropertyModel>> mPendingAnswerRequestUrls;
     private final Context mContext;
     private final SuggestionHost mSuggestionHost;
-    private ImageFetcher mImageFetcher;
     private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
+    private ImageFetcher mImageFetcher;
 
     /**
      * @param context An Android context.
@@ -47,6 +47,7 @@
      */
     public AnswerSuggestionProcessor(Context context, SuggestionHost suggestionHost,
             UrlBarEditingTextStateProvider editingTextProvider) {
+        super(context, suggestionHost);
         mContext = context;
         mSuggestionHost = suggestionHost;
         mPendingAnswerRequestUrls = new HashMap<>();
@@ -82,11 +83,8 @@
 
     @Override
     public void populateModel(OmniboxSuggestion suggestion, PropertyModel model, int position) {
-        SuggestionViewDelegate delegate =
-                mSuggestionHost.createSuggestionViewDelegate(suggestion, position);
-
-        setStateForNewSuggestion(model, suggestion, delegate);
-        maybeFetchAnswerIcon(suggestion, model);
+        super.populateModel(suggestion, model, position);
+        setStateForSuggestion(model, suggestion);
     }
 
     @Override
@@ -114,7 +112,15 @@
         // https://cs.chromium.org/Omnibox.SuggestionUsed.AnswerInSuggest
     }
 
-    private void maybeFetchAnswerIcon(OmniboxSuggestion suggestion, PropertyModel model) {
+    /**
+     * Specify ImageFetcher instance to be used for testing purposes.
+     * TODO(ender): Create fetcher instance in AutocompleteMediator and pass it to the constructor.
+     */
+    void setImageFetcherForTesting(ImageFetcher fetcher) {
+        mImageFetcher = fetcher;
+    }
+
+    private void maybeFetchAnswerIcon(PropertyModel model, OmniboxSuggestion suggestion) {
         ThreadUtils.assertOnUiThread();
 
         // Attempting to fetch answer data before we have a profile to request it for.
@@ -145,13 +151,19 @@
         mImageFetcher.fetchImage(
                 url, ImageFetcher.ANSWER_SUGGESTIONS_UMA_CLIENT_NAME, (Bitmap bitmap) -> {
                     ThreadUtils.assertOnUiThread();
-
+                    // Remove models for the URL ahead of all the checks to ensure we
+                    // do not keep them around waiting in case image fetch failed.
                     List<PropertyModel> currentModels = mPendingAnswerRequestUrls.remove(url);
+                    if (currentModels == null || bitmap == null) return;
+
                     boolean didUpdate = false;
                     for (int i = 0; i < currentModels.size(); i++) {
                         PropertyModel currentModel = currentModels.get(i);
                         if (!mSuggestionHost.isActiveModel(currentModel)) continue;
-                        model.set(AnswerSuggestionViewProperties.ANSWER_IMAGE, bitmap);
+                        setSuggestionDrawableState(currentModel,
+                                SuggestionDrawableState.Builder.forBitmap(bitmap)
+                                        .setLarge(true)
+                                        .build());
                         didUpdate = true;
                     }
                     if (didUpdate) mSuggestionHost.notifyPropertyModelsChanged();
@@ -161,17 +173,11 @@
     /**
      * Sets both lines of the Omnibox suggestion based on an Answers in Suggest result.
      */
-    private void setStateForNewSuggestion(
-            PropertyModel model, OmniboxSuggestion suggestion, SuggestionViewDelegate delegate) {
+    private void setStateForSuggestion(PropertyModel model, OmniboxSuggestion suggestion) {
         SuggestionAnswer answer = suggestion.getAnswer();
         AnswerText[] details = AnswerTextNewLayout.from(
                 mContext, suggestion, mUrlBarEditingTextProvider.getTextWithoutAutocomplete());
 
-        model.set(AnswerSuggestionViewProperties.DELEGATE, delegate);
-
-        model.set(AnswerSuggestionViewProperties.TEXT_LINE_1_SIZE, details[0].mHeightSp);
-        model.set(AnswerSuggestionViewProperties.TEXT_LINE_2_SIZE, details[1].mHeightSp);
-
         model.set(AnswerSuggestionViewProperties.TEXT_LINE_1_TEXT, details[0].mText);
         model.set(AnswerSuggestionViewProperties.TEXT_LINE_2_TEXT, details[1].mText);
 
@@ -183,43 +189,49 @@
         model.set(AnswerSuggestionViewProperties.TEXT_LINE_1_MAX_LINES, details[0].mMaxLines);
         model.set(AnswerSuggestionViewProperties.TEXT_LINE_2_MAX_LINES, details[1].mMaxLines);
 
-        @AnswerIcon
-        int icon = AnswerIcon.UNDEFINED;
+        setSuggestionDrawableState(model,
+                SuggestionDrawableState.Builder
+                        .forDrawableRes(mContext, getSuggestionIcon(suggestion))
+                        .setLarge(true)
+                        .build());
 
+        maybeFetchAnswerIcon(model, suggestion);
+    }
+
+    /**
+     * Get default suggestion icon for supplied suggestion.
+     */
+    @DrawableRes
+    int getSuggestionIcon(OmniboxSuggestion suggestion) {
+        SuggestionAnswer answer = suggestion.getAnswer();
         if (answer != null) {
             switch (answer.getType()) {
                 case AnswerType.DICTIONARY:
-                    icon = AnswerIcon.DICTIONARY;
-                    break;
+                    return R.drawable.ic_book_round;
                 case AnswerType.FINANCE:
-                    icon = AnswerIcon.FINANCE;
-                    break;
+                    return R.drawable.ic_swap_vert_round;
                 case AnswerType.KNOWLEDGE_GRAPH:
-                    icon = AnswerIcon.KNOWLEDGE;
-                    break;
+                    return R.drawable.ic_google_round;
                 case AnswerType.SUNRISE:
-                    icon = AnswerIcon.SUNRISE;
-                    break;
+                    return R.drawable.ic_wb_sunny_round;
                 case AnswerType.TRANSLATION:
-                    icon = AnswerIcon.TRANSLATION;
-                    break;
+                    return R.drawable.logo_translate_round;
                 case AnswerType.WEATHER:
-                    icon = AnswerIcon.WEATHER;
-                    break;
+                    return R.drawable.logo_partly_cloudy;
                 case AnswerType.WHEN_IS:
-                    icon = AnswerIcon.EVENT;
-                    break;
+                    return R.drawable.ic_event_round;
                 case AnswerType.CURRENCY:
-                    icon = AnswerIcon.CURRENCY;
-                    break;
+                    return R.drawable.ic_loop_round;
                 case AnswerType.SPORTS:
-                    icon = AnswerIcon.SPORTS;
+                    return R.drawable.ic_google_round;
+                default:
+                    assert false : "Unsupported answer type";
+                    break;
             }
         } else {
             assert suggestion.getType() == OmniboxSuggestionType.CALCULATOR;
-            icon = AnswerIcon.CALCULATOR;
+            return R.drawable.ic_equals_sign_round;
         }
-
-        model.set(AnswerSuggestionViewProperties.ANSWER_ICON_TYPE, icon);
+        return 0;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionView.java
deleted file mode 100644
index 6848375..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionView.java
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.omnibox.suggestions.answer;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.text.Spannable;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import androidx.annotation.DrawableRes;
-
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
-import org.chromium.chrome.browser.util.ColorUtils;
-
-/**
- * Container view for omnibox answer suggestions.
- */
-public class AnswerSuggestionView extends RelativeLayout {
-    private SuggestionViewDelegate mSuggestionDelegate;
-    private View mAnswerView;
-    private TextView mTextView1;
-    private TextView mTextView2;
-    private ImageView mAnswerIconView;
-    private ImageView mRefineView;
-
-    /**
-     * Container view for omnibox suggestions allowing soft focus from keyboard.
-     */
-    public static class FocusableView extends RelativeLayout {
-        /** Creates new instance of FocusableView. */
-        public FocusableView(Context context, AttributeSet attributes) {
-            super(context, attributes);
-        }
-
-        @Override
-        public boolean isFocused() {
-            return super.isFocused() || (isSelected() && !isInTouchMode());
-        }
-    }
-
-    /** Creates new instance of AnswerSuggestionView. */
-    public AnswerSuggestionView(Context context, AttributeSet attributes) {
-        super(context, attributes);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mTextView1 = findViewById(R.id.omnibox_answer_line_1);
-        mTextView2 = findViewById(R.id.omnibox_answer_line_2);
-        mAnswerIconView = findViewById(R.id.omnibox_answer_icon);
-        mAnswerView = findViewById(R.id.omnibox_answer);
-        mRefineView = findViewById(R.id.omnibox_answer_refine_icon);
-    }
-
-    @Override
-    public void setSelected(boolean selected) {
-        super.setSelected(selected);
-        mAnswerView.setSelected(selected);
-        if (selected && !isInTouchMode()) {
-            postDelegateAction(() -> mSuggestionDelegate.onSetUrlToSuggestion());
-        }
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        // Whenever the suggestion dropdown is touched, we dispatch onGestureDown which is
-        // used to let autocomplete controller know that it should stop updating suggestions.
-        switch (ev.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN:
-                mSuggestionDelegate.onGestureDown();
-                break;
-            case MotionEvent.ACTION_UP:
-                mSuggestionDelegate.onGestureUp(ev.getEventTime());
-                break;
-        }
-        return super.dispatchTouchEvent(ev);
-    }
-
-    /** Specify delegate receiving click/refine events. */
-    void setDelegate(SuggestionViewDelegate delegate) {
-        mSuggestionDelegate = delegate;
-        mAnswerView.setOnClickListener(
-                (View v) -> postDelegateAction(() -> mSuggestionDelegate.onSelection()));
-        mAnswerView.setOnLongClickListener((View v) -> {
-            postDelegateAction(() -> mSuggestionDelegate.onLongPress());
-            return true;
-        });
-        mRefineView.setOnClickListener(
-                (View v) -> postDelegateAction(() -> mSuggestionDelegate.onRefineSuggestion()));
-    }
-
-    /**
-     * Toggles theme.
-     * @param useDarkColors specifies whether UI should use dark theme.
-     */
-    void setUseDarkColors(boolean useDarkColors) {
-        Drawable drawable = mRefineView.getDrawable();
-        DrawableCompat.setTint(
-                drawable, ColorUtils.getIconTint(getContext(), !useDarkColors).getDefaultColor());
-    }
-
-    /**
-     * Specifies text accessibility description of the first text line.
-     * @param text Text to be announced.
-     */
-    void setLine1AccessibilityDescription(String text) {
-        mTextView1.setContentDescription(text);
-    }
-
-    /**
-     * Specifies text accessibility description of the second text line.
-     * @param text Text to be announced.
-     */
-    void setLine2AccessibilityDescription(String text) {
-        mTextView2.setContentDescription(text);
-    }
-
-    /**
-     * Specifies text content of the first text line.
-     * @param text Text to be displayed.
-     */
-    void setLine1TextContent(Spannable text) {
-        mTextView1.setText(text);
-    }
-
-    /**
-     * Specifies text content of the second text line.
-     * @param text Text to be displayed.
-     */
-    void setLine2TextContent(Spannable text) {
-        mTextView2.setText(text);
-    }
-
-    /**
-     * Specifies image bitmap to be displayed as an answer icon.
-     * @param bitmap Decoded image to be displayed as an answer icon.
-     */
-    void setIconBitmap(Bitmap bitmap) {
-        BitmapDrawable drawable = new BitmapDrawable(bitmap);
-        mAnswerIconView.setImageDrawable(drawable);
-    }
-
-    /**
-     * Specifies fallback image to be presented if no image is available.
-     * @param res Drawable resource ID to be used in place of answer image.
-     */
-    void setFallbackIconRes(@DrawableRes int res) {
-        mAnswerIconView.setImageResource(res);
-    }
-
-    /**
-     * Post delegate action to main thread. Invoked only if delegate is specified.
-     * @param action Delegate action to invoke on the UI thread.
-     */
-    private void postDelegateAction(Runnable action) {
-        if (mSuggestionDelegate == null) return;
-        if (!post(action)) action.run();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewBinder.java
index 9e11de43..dded89b8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewBinder.java
@@ -4,81 +4,43 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions.answer;
 
-import android.support.v4.view.ViewCompat;
+import android.widget.TextView;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
-import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionViewProperties.AnswerIcon;
+import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionView;
+import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewBinder;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** A mechanism binding AnswerSuggestion properties to its view. */
-public class AnswerSuggestionViewBinder {
+public class AnswerSuggestionViewBinder extends BaseSuggestionViewBinder {
     /** @see PropertyModelChangeProcessor.ViewBinder#bind(Object, Object, Object) */
-    public static void bind(
-            PropertyModel model, AnswerSuggestionView view, PropertyKey propertyKey) {
-        if (AnswerSuggestionViewProperties.DELEGATE.equals(propertyKey)) {
-            view.setDelegate(model.get(AnswerSuggestionViewProperties.DELEGATE));
-        } else if (SuggestionCommonProperties.USE_DARK_COLORS.equals(propertyKey)) {
-            view.setUseDarkColors(model.get(SuggestionCommonProperties.USE_DARK_COLORS));
-        } else if (AnswerSuggestionViewProperties.ANSWER_IMAGE.equals(propertyKey)) {
-            view.setIconBitmap(model.get(AnswerSuggestionViewProperties.ANSWER_IMAGE));
-        } else if (AnswerSuggestionViewProperties.ANSWER_ICON_TYPE.equals(propertyKey)) {
-            int type = model.get(AnswerSuggestionViewProperties.ANSWER_ICON_TYPE);
-            if (type == AnswerIcon.UNDEFINED) return;
-            view.setFallbackIconRes(getAnswerIcon(type));
-        } else if (AnswerSuggestionViewProperties.TEXT_LINE_1_TEXT.equals(propertyKey)) {
-            view.setLine1TextContent(model.get(AnswerSuggestionViewProperties.TEXT_LINE_1_TEXT));
-        } else if (AnswerSuggestionViewProperties.TEXT_LINE_2_TEXT.equals(propertyKey)) {
-            view.setLine2TextContent(model.get(AnswerSuggestionViewProperties.TEXT_LINE_2_TEXT));
-        } else if (AnswerSuggestionViewProperties.TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION.equals(
-                           propertyKey)) {
-            view.setLine1AccessibilityDescription(model.get(
-                    AnswerSuggestionViewProperties.TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION));
-        } else if (AnswerSuggestionViewProperties.TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION.equals(
-                           propertyKey)) {
-            view.setLine2AccessibilityDescription(model.get(
-                    AnswerSuggestionViewProperties.TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION));
-        } else if (SuggestionCommonProperties.LAYOUT_DIRECTION.equals(propertyKey)) {
-            ViewCompat.setLayoutDirection(
-                    view, model.get(SuggestionCommonProperties.LAYOUT_DIRECTION));
-        }
-    }
+    @Override
+    public void bind(PropertyModel model, BaseSuggestionView view, PropertyKey propertyKey) {
+        super.bind(model, view, propertyKey);
 
-    /**
-     * Convert AnswerIcon type to drawable resource type representing answer icon.
-     *
-     * Answers are not shown when user is in incognito mode, so we can rely on
-     * configuration UI to define colors for light and dark mode that will be used
-     * by the icons below.
-     *
-     * @param type AnswerIcon type to get drawable for.
-     */
-    private static final int getAnswerIcon(@AnswerIcon int type) {
-        switch (type) {
-            case AnswerIcon.CALCULATOR:
-                return R.drawable.ic_equals_sign_round;
-            case AnswerIcon.DICTIONARY:
-                return R.drawable.ic_book_round;
-            case AnswerIcon.FINANCE:
-                return R.drawable.ic_swap_vert_round;
-            case AnswerIcon.KNOWLEDGE:
-                return R.drawable.ic_google_round;
-            case AnswerIcon.SUNRISE:
-                return R.drawable.ic_wb_sunny_round;
-            case AnswerIcon.TRANSLATION:
-                return R.drawable.logo_translate_round;
-            case AnswerIcon.WEATHER:
-                return R.drawable.logo_partly_cloudy;
-            case AnswerIcon.EVENT:
-                return R.drawable.ic_event_round;
-            case AnswerIcon.CURRENCY:
-                return R.drawable.ic_loop_round;
-            case AnswerIcon.SPORTS:
-                return R.drawable.ic_google_round;
-            default:
-                assert false : "Invalid answer type: " + type;
-                return 0;
+        if (AnswerSuggestionViewProperties.TEXT_LINE_1_TEXT == propertyKey) {
+            TextView tv = view.findContentView(R.id.omnibox_answer_line_1);
+            tv.setText(model.get(AnswerSuggestionViewProperties.TEXT_LINE_1_TEXT));
+        } else if (AnswerSuggestionViewProperties.TEXT_LINE_2_TEXT == propertyKey) {
+            TextView tv = view.findContentView(R.id.omnibox_answer_line_2);
+            tv.setText(model.get(AnswerSuggestionViewProperties.TEXT_LINE_2_TEXT));
+        } else if (AnswerSuggestionViewProperties.TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION
+                == propertyKey) {
+            TextView tv = view.findContentView(R.id.omnibox_answer_line_1);
+            tv.setContentDescription(model.get(
+                    AnswerSuggestionViewProperties.TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION));
+        } else if (AnswerSuggestionViewProperties.TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION
+                == propertyKey) {
+            TextView tv = view.findContentView(R.id.omnibox_answer_line_2);
+            tv.setContentDescription(model.get(
+                    AnswerSuggestionViewProperties.TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION));
+        } else if (AnswerSuggestionViewProperties.TEXT_LINE_1_MAX_LINES == propertyKey) {
+            TextView tv = view.findContentView(R.id.omnibox_answer_line_1);
+            tv.setMaxLines(model.get(AnswerSuggestionViewProperties.TEXT_LINE_1_MAX_LINES));
+        } else if (AnswerSuggestionViewProperties.TEXT_LINE_2_MAX_LINES == propertyKey) {
+            TextView tv = view.findContentView(R.id.omnibox_answer_line_2);
+            tv.setMaxLines(model.get(AnswerSuggestionViewProperties.TEXT_LINE_2_MAX_LINES));
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewProperties.java
index 71f61925..a8dcaec4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewProperties.java
@@ -4,54 +4,18 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions.answer;
 
-import android.graphics.Bitmap;
 import android.text.Spannable;
 
-import androidx.annotation.IntDef;
-
-import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
+import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * The properties associated with rendering the answer suggestion view.
  */
 class AnswerSuggestionViewProperties {
-    @IntDef({AnswerIcon.UNDEFINED, AnswerIcon.CALCULATOR, AnswerIcon.DICTIONARY, AnswerIcon.FINANCE,
-            AnswerIcon.KNOWLEDGE, AnswerIcon.SUNRISE, AnswerIcon.TRANSLATION, AnswerIcon.WEATHER,
-            AnswerIcon.EVENT, AnswerIcon.CURRENCY, AnswerIcon.SPORTS})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AnswerIcon {
-        int UNDEFINED = 0;
-        int CALCULATOR = 1;
-        int DICTIONARY = 2;
-        int FINANCE = 3;
-        int KNOWLEDGE = 4;
-        int SUNRISE = 5;
-        int TRANSLATION = 6;
-        int WEATHER = 7;
-        int EVENT = 8;
-        int CURRENCY = 9;
-        int SPORTS = 10;
-    }
-
-    /** The delegate to handle actions on the suggestion view. */
-    public static final WritableObjectPropertyKey<SuggestionViewDelegate> DELEGATE =
-            new WritableObjectPropertyKey<>();
-    /** The answer image to be shown. */
-    public static final WritableObjectPropertyKey<Bitmap> ANSWER_IMAGE =
-            new WritableObjectPropertyKey<>();
-    /** The suggestion icon type shown. */
-    public static final WritableIntPropertyKey ANSWER_ICON_TYPE = new WritableIntPropertyKey();
-
-    /** The sizing information for the first line of text. */
-    public static final WritableIntPropertyKey TEXT_LINE_1_SIZE = new WritableIntPropertyKey();
     /** The maximum number of lines to be shown for the first line of text. */
     public static final WritableIntPropertyKey TEXT_LINE_1_MAX_LINES = new WritableIntPropertyKey();
     /** The actual text content for the first line of text. */
@@ -61,8 +25,6 @@
     public static final WritableObjectPropertyKey<String> TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION =
             new WritableObjectPropertyKey<>();
 
-    /** The sizing information for the second line of text. */
-    public static final WritableIntPropertyKey TEXT_LINE_2_SIZE = new WritableIntPropertyKey();
     /** The maximum number of lines to be shown for the second line of text. */
     public static final WritableIntPropertyKey TEXT_LINE_2_MAX_LINES = new WritableIntPropertyKey();
     /** The actual text content for the second line of text. */
@@ -72,11 +34,10 @@
     public static final WritableObjectPropertyKey<String> TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION =
             new WritableObjectPropertyKey<>();
 
-    public static final PropertyKey[] ALL_UNIQUE_KEYS = new PropertyKey[] {DELEGATE, ANSWER_IMAGE,
-            ANSWER_ICON_TYPE, TEXT_LINE_1_SIZE, TEXT_LINE_1_MAX_LINES, TEXT_LINE_1_TEXT,
-            TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION, TEXT_LINE_2_SIZE, TEXT_LINE_2_MAX_LINES,
-            TEXT_LINE_2_TEXT, TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION};
+    public static final PropertyKey[] ALL_UNIQUE_KEYS = new PropertyKey[] {TEXT_LINE_1_TEXT,
+            TEXT_LINE_1_MAX_LINES, TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION, TEXT_LINE_2_TEXT,
+            TEXT_LINE_2_MAX_LINES, TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION};
 
     public static final PropertyKey[] ALL_KEYS =
-            PropertyModel.concatKeys(ALL_UNIQUE_KEYS, SuggestionCommonProperties.ALL_KEYS);
+            PropertyModel.concatKeys(ALL_UNIQUE_KEYS, BaseSuggestionViewProperties.ALL_KEYS);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
index 92125e7..f19c651a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.omnibox.suggestions.base;
 
 import android.content.Context;
+import android.support.annotation.IdRes;
 import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -145,4 +146,17 @@
     ImageView getActionImageView() {
         return mActionView;
     }
+
+    /**
+     * Find content view by view id.
+     *
+     * Scoped {@link #findViewById(int)} search for the view specified in
+     * {@link #setContentView(View)}.
+     *
+     * @param id View ID of the sought view.
+     * @return View with the specified ID or null, if view could not be found.
+     */
+    public <T extends View> T findContentView(@IdRes int id) {
+        return mContentView.findContentView(id);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
index 8ff9f3b..fd4e857e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
@@ -52,18 +52,18 @@
         final RoundedCornerImageView view = baseView.getSuggestionImageView();
         final SuggestionDrawableState sds = model.get(BaseSuggestionViewProperties.ICON);
         final Resources res = view.getContext().getResources();
-        final int paddingStart = res.getDimensionPixelSize(sds.isLarge()
+        final int paddingStart = res.getDimensionPixelSize(sds.isLarge
                         ? R.dimen.omnibox_suggestion_36dp_icon_margin_start
                         : R.dimen.omnibox_suggestion_24dp_icon_margin_start);
 
-        final int paddingEnd = res.getDimensionPixelSize(sds.isLarge()
+        final int paddingEnd = res.getDimensionPixelSize(sds.isLarge
                         ? R.dimen.omnibox_suggestion_36dp_icon_margin_end
                         : R.dimen.omnibox_suggestion_24dp_icon_margin_end);
 
         // TODO(ender): move logic applying corner rounding to updateIcon when action images use
         // RoundedCornerImageView too.
         RoundedCornerImageView rciv = (RoundedCornerImageView) view;
-        int radius = sds.isRounded()
+        int radius = sds.useRoundedCorners
                 ? res.getDimensionPixelSize(R.dimen.default_rounded_corner_radius)
                 : 0;
         rciv.setRoundedCorners(radius, radius, radius, radius);
@@ -91,8 +91,8 @@
             return;
         }
 
-        view.setImageDrawable(sds.getDrawable());
-        if (sds.isTintable()) {
+        view.setImageDrawable(sds.drawable);
+        if (sds.allowTint) {
             ApiCompatibilityUtils.setImageTintList(
                     view, ColorUtils.getIconTint(view.getContext(), !useDarkColors));
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java
index cac3bed..e0f5ed1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions.base;
 
+import android.content.Context;
+
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
@@ -14,12 +17,14 @@
  * A class that handles base properties and model for most suggestions.
  */
 public abstract class BaseSuggestionViewProcessor implements SuggestionProcessor {
+    private final Context mContext;
     private final SuggestionHost mSuggestionHost;
 
     /**
      * @param host A handle to the object using the suggestions.
      */
-    public BaseSuggestionViewProcessor(SuggestionHost host) {
+    public BaseSuggestionViewProcessor(Context context, SuggestionHost host) {
+        mContext = context;
         mSuggestionHost = host;
     }
 
@@ -55,7 +60,16 @@
                 mSuggestionHost.createSuggestionViewDelegate(suggestion, position);
 
         model.set(BaseSuggestionViewProperties.SUGGESTION_DELEGATE, delegate);
-        model.set(BaseSuggestionViewProperties.ICON, null);
-        model.set(BaseSuggestionViewProperties.ACTION_ICON, null);
+
+        if (canRefine(suggestion)) {
+            setActionDrawableState(model,
+                    SuggestionDrawableState.Builder
+                            .forDrawableRes(mContext, R.drawable.btn_suggestion_refine)
+                            .setLarge(true)
+                            .setAllowTint(true)
+                            .build());
+        } else {
+            setActionDrawableState(model, null);
+        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java
index 49248e7..40ea71f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.support.annotation.DrawableRes;
+import android.support.annotation.IdRes;
 import android.view.View;
 import android.widget.ImageView;
 
@@ -59,4 +60,11 @@
     public boolean isFocused() {
         return super.isFocused() || (isSelected() && !isInTouchMode());
     }
+
+    <ViewType extends View> ViewType findContentView(@IdRes int id) {
+        if (mContentView == null) {
+            return null;
+        }
+        return mContentView.findViewById(id);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SuggestionDrawableState.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SuggestionDrawableState.java
index 20e7c2c..1b448ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SuggestionDrawableState.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SuggestionDrawableState.java
@@ -7,106 +7,132 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.PaintDrawable;
 import android.support.annotation.ColorInt;
+import android.support.annotation.ColorRes;
 import android.support.annotation.DrawableRes;
 import android.support.v4.util.ObjectsCompat;
 import android.support.v7.content.res.AppCompatResources;
 
-import org.chromium.chrome.R;
-
 /** Represents graphical decoration for the suggestion components. */
 public class SuggestionDrawableState {
-    private Drawable mDrawable;
-    private boolean mAllowTint;
-    private boolean mUseRoundedCorners;
-    private boolean mIsLarge;
-
-    /**
-     * Create new SuggestionDrawableState representing a supplied Drawable object.
-     *
-     * @param cxt Current context.
-     * @param drawable Drawable object to use.
-     * @param isLarge Whether drawable object should be large (or small).
-     * @param allowTint Whether supplied drawable can be tinted.
-     * @param rounded Whether supplied drawable should use rounded corners.
-     */
-    public SuggestionDrawableState(
-            Context ctx, Drawable drawable, boolean isLarge, boolean allowTint, boolean rounded) {
-        mDrawable = drawable;
-        mIsLarge = isLarge;
-        mAllowTint = allowTint;
-        mUseRoundedCorners = rounded;
-    }
-
-    /**
-     * Create new SuggestionDrawableState representing a supplied Bitmap.
-     * Bitmap drawables are not tintable.
-     *
-     * @param cxt Current context.
-     * @param bitmap Bitmap to use.
-     * @param isLarge Whether drawable object should be large (or small).
-     * @param rounded Whether supplied drawable should use rounded corners.
-     */
-    public SuggestionDrawableState(Context ctx, Bitmap bitmap, boolean isLarge, boolean rounded) {
-        this(ctx, new BitmapDrawable(bitmap), isLarge, false, rounded);
-    }
-
-    /**
-     * Create new SuggestionDrawableState representing a supplied Color.
-     * This instance is not tintable.
-     * This method does not utilize ColorDrawables directly, because these are freely resize-able,
-     * making it impossible to restrict its aspect ratio to a rectangle.
-     *
-     * @param cxt Current context.
-     * @param color Color to show.
-     * @param isLarge Whether drawable object should be large (or small).
-     * @param rounded Whether supplied drawable should use rounded corners.
-     */
-    public SuggestionDrawableState(
-            Context ctx, @ColorInt int color, boolean isLarge, boolean rounded) {
-        this(ctx, new PaintDrawable(color), isLarge, false, rounded);
-        final int edgeSize = ctx.getResources().getDimensionPixelSize(isLarge
-                        ? R.dimen.omnibox_suggestion_36dp_icon_size
-                        : R.dimen.omnibox_suggestion_24dp_icon_size);
-        final PaintDrawable drawable = (PaintDrawable) mDrawable;
-        drawable.setIntrinsicWidth(edgeSize);
-        drawable.setIntrinsicHeight(edgeSize);
-    }
-
-    /**
-     * Create new SuggestionDrawableState representing a supplied drawable resource.
-     *
-     * @param cxt Current context.
-     * @param res Drawable resource to use.
-     * @param isLarge Whether drawable object should be large (or small).
-     * @param allowTint Whether supplied drawable can be tinted.
-     * @param rounded Whether supplied drawable should use rounded corners.
-     */
-    public SuggestionDrawableState(Context ctx, @DrawableRes int res, boolean isLarge,
-            boolean allowTint, boolean rounded) {
-        this(ctx, AppCompatResources.getDrawable(ctx, res), isLarge, allowTint, rounded);
-    }
-
-    /** Get Drawable associated with this instance. */
-    Drawable getDrawable() {
-        return mDrawable;
-    }
-
-    /** Whether drawable can be tinted. */
-    boolean isTintable() {
-        return mAllowTint;
-    }
-
-    /** Whether drawable should be drawn as large. */
-    boolean isLarge() {
-        return mIsLarge;
-    }
-
+    /** Embedded drawable object. */
+    public final Drawable drawable;
+    /** Whether supplied drawable can be tinted */
+    public final boolean allowTint;
     /** Whether drawable should be rounded. */
-    boolean isRounded() {
-        return mUseRoundedCorners;
+    public final boolean useRoundedCorners;
+    /** Whether drawable should be displayed as large. */
+    public final boolean isLarge;
+
+    public static final class Builder {
+        private Drawable mDrawable;
+        private boolean mAllowTint;
+        private boolean mUseRoundedCorners;
+        private boolean mIsLarge;
+
+        /**
+         * Create new Builder object.
+         *
+         * @param cxt Current context.
+         */
+        private Builder(Drawable drawable) {
+            assert drawable != null : "SuggestionDrawableState needs a Drawable object";
+            mDrawable = drawable;
+        }
+
+        /**
+         * Associate Bitmap with built SuggestionDrawableState object.
+         *
+         * @param bitmap Bitmap to use.
+         */
+        public static Builder forBitmap(Bitmap bitmap) {
+            return new Builder(new BitmapDrawable(bitmap));
+        }
+
+        /**
+         * Associate Color with built SuggestionDrawableState object.
+         *
+         * @param color Color to use.
+         */
+        public static Builder forColor(@ColorInt int color) {
+            return new Builder(new ColorDrawable(color));
+        }
+
+        /**
+         * Associate Color with built SuggestionDrawableState object.
+         *
+         * @param ctx Current context.
+         * @param colorRes Color resource to use.
+         */
+        public static Builder forColorRes(Context ctx, @ColorRes int colorRes) {
+            return new Builder(new ColorDrawable(ctx.getResources().getColor(colorRes)));
+        }
+
+        /**
+         * Associate Drawable with built SuggestionDrawableState object.
+         *
+         * @param ctx Current context.
+         * @param res Drawable resource to use.
+         */
+        public static Builder forDrawableRes(Context ctx, @DrawableRes int res) {
+            return new Builder(AppCompatResources.getDrawable(ctx, res));
+        }
+
+        /**
+         * Create new SuggestionDrawableState representing a supplied Drawable object.
+         *
+         * @param drawable Drawable object to use.
+         */
+        public static Builder forDrawable(Drawable d) {
+            return new Builder(d);
+        }
+
+        /**
+         * Specify whether built object should be rounded.
+         *
+         * @param useRoundedCorners true, if image should be rounded.
+         */
+        public Builder setUseRoundedCorners(boolean useRoundedCorners) {
+            mUseRoundedCorners = useRoundedCorners;
+            return this;
+        }
+
+        /**
+         * Specify whether build object should receive tint.
+         *
+         * @param allowTint true, if built drawable state should be tinted to reflect theme.
+         */
+        public Builder setAllowTint(boolean allowTint) {
+            mAllowTint = allowTint;
+            return this;
+        }
+
+        /**
+         * Specify whether build object should be presented as small (24dp) or large (36dp).
+         *
+         * @param isLarge true, if drawable should be shown large (36dp), otherwise 24dp.
+         */
+        public Builder setLarge(boolean isLarge) {
+            mIsLarge = isLarge;
+            return this;
+        }
+
+        /**
+         * Build SuggestionDrawableState object.
+         */
+        public SuggestionDrawableState build() {
+            return new SuggestionDrawableState(mDrawable, mUseRoundedCorners, mIsLarge, mAllowTint);
+        }
+    };
+
+    private SuggestionDrawableState(
+            Drawable drawable, boolean useRoundedCorners, boolean isLarge, boolean allowTint) {
+        this.drawable = drawable;
+        this.useRoundedCorners = useRoundedCorners;
+        this.isLarge = isLarge;
+        this.allowTint = allowTint;
     }
 
     @Override
@@ -115,8 +141,7 @@
         if (!(object instanceof SuggestionDrawableState)) return false;
         SuggestionDrawableState other = (SuggestionDrawableState) object;
 
-        return mIsLarge == other.mIsLarge && mUseRoundedCorners == other.mUseRoundedCorners
-                && mAllowTint == other.mAllowTint
-                && ObjectsCompat.equals(mDrawable, other.mDrawable);
+        return isLarge == other.isLarge && useRoundedCorners == other.useRoundedCorners
+                && allowTint == other.allowTint && ObjectsCompat.equals(drawable, other.drawable);
     }
 };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index eff2782d..86f4808 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -172,10 +172,6 @@
     public void finishNativeInitialization() {
         super.finishNativeInitialization();
 
-        mTab = new TabBuilder()
-                       .setWindow(getWindowAndroid())
-                       .setLaunchType(TabLaunchType.FROM_EXTERNAL_APP)
-                       .build();
         TabDelegateFactory factory = new TabDelegateFactory() {
             @Override
             public TabWebContentsDelegateAndroid createWebContentsDelegate(Tab tab) {
@@ -218,8 +214,12 @@
                 return null;
             }
         };
-        mTab.initialize(
-                WebContentsFactory.createWebContents(false, false), factory, false, null, false);
+        mTab = new TabBuilder()
+                       .setWindow(getWindowAndroid())
+                       .setLaunchType(TabLaunchType.FROM_EXTERNAL_APP)
+                       .setWebContents(WebContentsFactory.createWebContents(false, false))
+                       .setDelegateFactory(factory)
+                       .build();
         mTab.loadUrl(new LoadUrlParams(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL));
 
         mSearchBoxDataProvider.onNativeLibraryReady(mTab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/InvalidationGcmUpstreamSender.java b/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/InvalidationGcmUpstreamSender.java
index a0a2a010..e839664f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/InvalidationGcmUpstreamSender.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/InvalidationGcmUpstreamSender.java
@@ -22,7 +22,7 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.init.ProcessInitializationHandler;
-import org.chromium.chrome.browser.signin.IdentityServicesProvider;
+import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.ChromeSigninController;
 import org.chromium.components.signin.OAuth2TokenService;
 import org.chromium.components.sync.SyncConstants;
@@ -68,7 +68,9 @@
         }
 
         // Attempt to retrieve a token for the user.
-        IdentityServicesProvider.getOAuth2TokenService().getAccessToken(account,
+        // crbug.com/1014098: Do not use IdentityServicesProvider because the profile may not be
+        // initialized yet.
+        OAuth2TokenService.getAccessTokenWithFacade(AccountManagerFacade.get(), account,
                 SyncConstants.CHROME_SYNC_OAUTH2_SCOPE,
                 new OAuth2TokenService.GetAccessTokenCallback() {
                     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 122a7b9..8a13abc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -276,18 +276,13 @@
      *
      * Package-private. Use {@link TabBuilder} to create an instance.
      *
-     * @param id            The id this tab should be identified with.
-     * @param parent        The tab that caused this tab to be opened.
-     * @param incognito     Whether or not this tab is incognito.
-     * @param window        An instance of a {@link WindowAndroid}.
-     * @param launchType    Type indicating how this tab was launched.
-     * @param creationState State in which the tab is created.
-     * @param loadUrlParams Parameters used for a lazily loaded Tab.
+     * @param id The id this tab should be identified with.
+     * @param parent The tab that caused this tab to be opened.
+     * @param incognito Whether or not this tab is incognito.
+     * @param launchType Type indicating how this tab was launched.
      */
     @SuppressLint("HandlerLeak")
-    Tab(int id, Tab parent, boolean incognito, WindowAndroid window,
-            @Nullable @TabLaunchType Integer launchType,
-            @Nullable @TabCreationState Integer creationState, LoadUrlParams loadUrlParams) {
+    Tab(int id, Tab parent, boolean incognito, @Nullable @TabLaunchType Integer launchType) {
         mId = TabIdManager.getInstance().generateValidId(id);
         mIncognito = incognito;
         if (parent == null) {
@@ -308,13 +303,7 @@
                 ContextUtils.getApplicationContext(), ChromeActivity.getThemeId(),
                 false /*nightMode*/);
 
-        mWindowAndroid = window;
         mLaunchType = launchType;
-        mLaunchTypeAtCreation = launchType;
-        mPendingLoadParams = loadUrlParams;
-        if (loadUrlParams != null) mUrl = loadUrlParams.getUrl();
-
-        TabHelpers.initTabHelpers(this, parent, creationState);
 
         mAttachStateChangeListener = new OnAttachStateChangeListener() {
             @Override
@@ -847,21 +836,30 @@
     /**
      * Initializes {@link Tab} with {@code webContents}.  If {@code webContents} is {@code null} a
      * new {@link WebContents} will be created for this {@link Tab}.
-     * @param webContents       A {@link WebContents} object or {@code null} if one should be
-     *                          created.
-     * @param delegateFactory   The {@link TabDelegateFactory} to be used for delegate creation.
-     * @param initiallyHidden   Only used if {@code webContents} is {@code null}.  Determines
-     *                          whether or not the newly created {@link WebContents} will be hidden
-     *                          or not.
-     * @param tabState          State containing information about this Tab, if it was persisted.
-     * @param unfreeze          Whether there should be an attempt to restore state at the end of
-     *                          the initialization.
+     * @param parent The tab that caused this tab to be opened.
+     * @param creationState State in which the tab is created.
+     * @param loadUrlParams Parameters used for a lazily loaded Tab.
+     * @param webContents A {@link WebContents} object or {@code null} if one should be created.
+     * @param delegateFactory The {@link TabDelegateFactory} to be used for delegate creation.
+     * @param initiallyHidden Only used if {@code webContents} is {@code null}.  Determines
+     *        whether or not the newly created {@link WebContents} will be hidden or not.
+     * @param tabState State containing information about this Tab, if it was persisted.
+     * @param unfreeze Whether there should be an attempt to restore state at the end of
+     *        the initialization.
      */
-    public void initialize(WebContents webContents, @Nullable TabDelegateFactory delegateFactory,
-            boolean initiallyHidden, TabState tabState, boolean unfreeze) {
+    void initialize(Tab parent, @Nullable @TabCreationState Integer creationState,
+            LoadUrlParams loadUrlParams, WebContents webContents,
+            @Nullable TabDelegateFactory delegateFactory, boolean initiallyHidden,
+            TabState tabState, boolean unfreeze) {
         try {
             TraceEvent.begin("Tab.initialize");
 
+            mLaunchTypeAtCreation = mLaunchType;
+            mPendingLoadParams = loadUrlParams;
+            if (loadUrlParams != null) mUrl = loadUrlParams.getUrl();
+
+            TabHelpers.initTabHelpers(this, parent, creationState);
+
             if (tabState != null) restoreFieldsFromState(tabState);
 
             initializeNative();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBuilder.java
index ba1785c..8caa84c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBuilder.java
@@ -7,6 +7,7 @@
 import org.chromium.chrome.browser.tab.TabUma.TabCreationState;
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
 import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
 
 /**
@@ -23,6 +24,12 @@
     private boolean mFromFrozenState;
     private LoadUrlParams mLoadUrlParams;
 
+    private WebContents mWebContents;
+    private TabDelegateFactory mDelegateFactory;
+    private boolean mInitiallyHidden;
+    private TabState mTabState;
+    private boolean mUnfreeze;
+
     /**
      * Sets the id with which the Tab to create should be identified.
      * @param id The id of the Tab.
@@ -73,6 +80,59 @@
         return this;
     }
 
+    /**
+     * Sets a {@link WebContents} object to be used on the Tab. If not set, a new one
+     * will be created.
+     * @param webContents {@link WebContents} object.
+     * @return {@link TabBuilder} creating the Tab.
+     */
+    public TabBuilder setWebContents(WebContents webContents) {
+        mWebContents = webContents;
+        return this;
+    }
+
+    /**
+     * Sets a {@link TabDelegateFactory} object.
+     * @param delegateFactory The factory delegated to create various Tab-related objects.
+     * @return {@link TabBuilder} creating the Tab.
+     */
+    public TabBuilder setDelegateFactory(TabDelegateFactory delegateFactory) {
+        mDelegateFactory = delegateFactory;
+        return this;
+    }
+
+    /**
+     * Sets a flag indicating whether the Tab should start as hidden. Only used if
+     * {@code webContents} is {@code null}.
+     * @param initiallyHidden {@code true} if the newly created {@link WebContents} will be hidden.
+     * @return {@link TabBuilder} creating the Tab.
+     */
+    public TabBuilder setInitiallyHidden(boolean initiallyHidden) {
+        mInitiallyHidden = initiallyHidden;
+        return this;
+    }
+
+    /**
+     * Sets a {@link TabState} object containing information about this Tab, if it was persisted.
+     * @param tabState State object.
+     * @return {@link TabBuilder} creating the Tab.
+     */
+    public TabBuilder setTabState(TabState tabState) {
+        mTabState = tabState;
+        return this;
+    }
+
+    /**
+     * Sets a flag indicating if there should be an attempt to restore state at the end of
+     *        the initialization.
+     * @param unfreeze {@code true} if WebContents needs restoring from its saved state.
+     * @return {@link TabBuilder} creating the Tab.
+     */
+    public TabBuilder setUnfreeze(boolean unfreeze) {
+        mUnfreeze = unfreeze;
+        return this;
+    }
+
     public Tab build() {
         // Pre-condition check
         if (mCreationType != null) {
@@ -86,8 +146,14 @@
             if (mFromFrozenState) assert mLaunchType == TabLaunchType.FROM_RESTORE;
         }
 
-        return new Tab(
-                mId, mParent, mIncognito, mWindow, mLaunchType, mCreationType, mLoadUrlParams);
+        Tab tab = new Tab(mId, mParent, mIncognito, mLaunchType);
+        tab.updateWindowAndroid(mWindow);
+
+        // Initializes Tab. Its user data objects are also initialized through the event
+        // |onInitialized| of TabObserver they register.
+        tab.initialize(mParent, mCreationType, mLoadUrlParams, mWebContents, mDelegateFactory,
+                mInitiallyHidden, mTabState, mUnfreeze);
+        return tab;
     }
 
     private TabBuilder setCreationType(@TabCreationState int type) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
index 7b835f7d..e0ac4a6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
@@ -142,8 +142,10 @@
                               .setIncognito(mIncognito)
                               .setWindow(mNativeWindow)
                               .setLaunchType(type)
+                              .setWebContents(webContents)
+                              .setDelegateFactory(delegateFactory)
+                              .setInitiallyHidden(!openInForeground)
                               .build();
-                tab.initialize(webContents, delegateFactory, !openInForeground, null, false);
                 TabParentIntent.from(tab).set(parentIntent);
                 webContents.resumeLoadingCreatedWebContents();
             } else if (!openInForeground && SysUtils.isLowEndDevice()) {
@@ -155,8 +157,9 @@
                               .setIncognito(mIncognito)
                               .setWindow(mNativeWindow)
                               .setLaunchType(type)
+                              .setDelegateFactory(delegateFactory)
+                              .setInitiallyHidden(!openInForeground)
                               .build();
-                tab.initialize(null, delegateFactory, !openInForeground, null, false);
             } else {
                 tab = (mStartupTabPreloader != null)
                         ? mStartupTabPreloader.takeTabIfMatchingOrDestroy(loadUrlParams, type)
@@ -169,8 +172,9 @@
                                   .setIncognito(mIncognito)
                                   .setWindow(mNativeWindow)
                                   .setLaunchType(type)
+                                  .setDelegateFactory(delegateFactory)
+                                  .setInitiallyHidden(!openInForeground)
                                   .build();
-                    tab.initialize(null, delegateFactory, !openInForeground, null, false);
                     tab.loadUrl(loadUrlParams);
                     TraceEvent.end("ChromeTabCreator.loadUrl");
                 }
@@ -209,8 +213,10 @@
                           .setIncognito(mIncognito)
                           .setWindow(mNativeWindow)
                           .setLaunchType(type)
+                          .setWebContents(webContents)
+                          .setDelegateFactory(delegateFactory)
+                          .setInitiallyHidden(!openInForeground)
                           .build();
-        tab.initialize(webContents, delegateFactory, !openInForeground, null, false);
         mTabModel.addTab(tab, position, type);
         return true;
     }
@@ -308,15 +314,17 @@
     public Tab createFrozenTab(TabState state, int id, int index) {
         TabModelSelector selector = mActivity.getTabModelSelector();
         Tab parent = selector != null ? selector.getTabById(state.parentId) : null;
+        boolean selectTab = mOrderController.willOpenInForeground(
+                TabLaunchType.FROM_RESTORE, state.isIncognito());
         Tab tab = TabBuilder.createFromFrozenState()
                           .setId(id)
                           .setParent(parent)
                           .setIncognito(state.isIncognito())
                           .setWindow(mNativeWindow)
+                          .setDelegateFactory(createDefaultTabDelegateFactory())
+                          .setInitiallyHidden(!selectTab)
+                          .setTabState(state)
                           .build();
-        boolean selectTab = mOrderController.willOpenInForeground(TabLaunchType.FROM_RESTORE,
-                state.isIncognito());
-        tab.initialize(null, createDefaultTabDelegateFactory(), !selectTab, state, false);
         assert state.isIncognito() == mIncognito;
         mTabModel.addTab(tab, index, TabLaunchType.FROM_RESTORE);
         return tab;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index dcb70eba..3f30b1d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -19,7 +19,6 @@
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 
-import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 
@@ -27,7 +26,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.CachedMetrics.ActionEvent;
-import org.chromium.base.metrics.CachedMetrics.EnumeratedHistogramSample;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
@@ -62,6 +60,7 @@
 import org.chromium.chrome.browser.native_page.NativePage;
 import org.chromium.chrome.browser.native_page.NativePageFactory;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
+import org.chromium.chrome.browser.ntp.FakeboxDelegate;
 import org.chromium.chrome.browser.ntp.IncognitoNewTabPage;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
@@ -125,8 +124,6 @@
 import org.chromium.ui.widget.Toast;
 import org.chromium.ui.widget.ViewRectProvider;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
@@ -147,23 +144,6 @@
         void updateReloadButtonState(boolean isLoading);
     }
 
-    /** A means of tracking which mechanism is being used to focus the omnibox. */
-    @IntDef({OmniboxFocusReason.OMNIBOX_TAP, OmniboxFocusReason.OMNIBOX_LONG_PRESS,
-            OmniboxFocusReason.FAKE_BOX_TAP, OmniboxFocusReason.FAKE_BOX_LONG_PRESS,
-            OmniboxFocusReason.ACCELERATOR_TAP, OmniboxFocusReason.TAB_SWITCHER_OMNIBOX_TAP})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface OmniboxFocusReason {
-        int OMNIBOX_TAP = 0;
-        int OMNIBOX_LONG_PRESS = 1;
-        int FAKE_BOX_TAP = 2;
-        int FAKE_BOX_LONG_PRESS = 3;
-        int ACCELERATOR_TAP = 4;
-        int TAB_SWITCHER_OMNIBOX_TAP = 5;
-        int NUM_ENTRIES = 6;
-    }
-    private static final EnumeratedHistogramSample ENUMERATED_FOCUS_REASON =
-            new EnumeratedHistogramSample(
-                    "Android.OmniboxFocusReason", OmniboxFocusReason.NUM_ENTRIES);
     private static final ActionEvent ACCELERATOR_BUTTON_TAP_ACTION =
             new ActionEvent("MobileToolbarOmniboxAcceleratorTap");
 
@@ -746,7 +726,7 @@
 
     @Override
     public void onScrimClick() {
-        setUrlBarFocus(false);
+        setUrlBarFocus(false, LocationBar.OmniboxFocusReason.UNFOCUS);
     }
 
     /**
@@ -757,17 +737,6 @@
     }
 
     /**
-     * @param reason A {@link OmniboxFocusReason} that the omnibox was focused.
-     */
-    public static void recordOmniboxFocusReason(@OmniboxFocusReason int reason) {
-        if (OmniboxFocusReason.OMNIBOX_TAP == reason
-                && ReturnToChromeExperimentsUtil.isInOverviewWithOmnibox()) {
-            reason = OmniboxFocusReason.TAB_SWITCHER_OMNIBOX_TAP;
-        }
-        ENUMERATED_FOCUS_REASON.record(reason);
-    }
-
-    /**
      * Enable the bottom toolbar.
      */
     public void enableBottomToolbar() {
@@ -779,9 +748,8 @@
 
         final OnClickListener searchAcceleratorListener = v -> {
             recordBottomToolbarUseForIPH();
-            recordOmniboxFocusReason(OmniboxFocusReason.ACCELERATOR_TAP);
             ACCELERATOR_BUTTON_TAP_ACTION.record();
-            setUrlBarFocus(true);
+            setUrlBarFocus(true, LocationBar.OmniboxFocusReason.ACCELERATOR_TAP);
         };
 
         final OnClickListener shareButtonListener = v -> {
@@ -1374,7 +1342,7 @@
                 if (isVisible) {
                     // Defocus here to avoid handling focus in multiple places, e.g., when the
                     // forward button is pressed. (see crbug.com/414219)
-                    setUrlBarFocus(false);
+                    setUrlBarFocus(false, LocationBar.OmniboxFocusReason.UNFOCUS);
 
                     if (!mActivity.isInOverviewMode() && isShowingAppMenuUpdateBadge()) {
                         // The app menu badge should be removed the first time the menu is opened.
@@ -1675,11 +1643,12 @@
      * If you request focus and the UrlBar was already focused, this will select all of the text.
      *
      * @param focused Whether URL bar should be focused.
+     * @param reason The given reason.
      */
-    public void setUrlBarFocus(boolean focused) {
+    public void setUrlBarFocus(boolean focused, @LocationBar.OmniboxFocusReason int reason) {
         if (!isInitialized()) return;
         boolean wasFocused = mLocationBar.isUrlBarFocused();
-        mLocationBar.setUrlBarFocus(focused);
+        mLocationBar.setUrlBarFocus(focused, null, reason);
         if (wasFocused && focused) {
             mLocationBar.selectAll();
         }
@@ -1689,9 +1658,10 @@
      * See {@link #setUrlBarFocus}, but if native is not loaded it will queue the request instead
      * of dropping it.
      */
-    public void setUrlBarFocusOnceNativeInitialized(boolean focused) {
+    public void setUrlBarFocusOnceNativeInitialized(
+            boolean focused, @LocationBar.OmniboxFocusReason int reason) {
         if (isInitialized()) {
-            setUrlBarFocus(focused);
+            setUrlBarFocus(focused, reason);
             return;
         }
 
@@ -1700,7 +1670,7 @@
             // initialized. This is important for the Launch to Incognito Tab flow (see
             // IncognitoTabLauncher.
             mOnInitializedRunnable = () -> {
-                setUrlBarFocus(focused);
+                setUrlBarFocus(focused, reason);
             };
         } else {
             mOnInitializedRunnable = null;
@@ -1859,7 +1829,7 @@
 
             // Ensure the URL bar loses focus if the tab it was interacting with is changed from
             // underneath it.
-            setUrlBarFocus(false);
+            setUrlBarFocus(false, LocationBar.OmniboxFocusReason.UNFOCUS);
 
             // Place the cursor in the Omnibox if applicable.  We always clear the focus above to
             // ensure the shield placed over the content is dismissed when switching tabs.  But if
@@ -1965,6 +1935,14 @@
         mToolbar.setProgressBarAnchorView(anchor);
     }
 
+    /**
+     * @return The {@link FakeboxDelegate}.
+     */
+    public FakeboxDelegate getFakeboxDelegate() {
+        // TODO(crbug.com/1000295): Split fakebox component out of ntp package.
+        return mLocationBar;
+    }
+
     private boolean shouldShowCursorInLocationBar() {
         Tab tab = mLocationBarModel.getTab();
         if (tab == null) return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
index eabdc4c..134d172f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
@@ -46,9 +46,11 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.WindowDelegate;
+import org.chromium.chrome.browser.native_page.NativePage;
 import org.chromium.chrome.browser.native_page.NativePageFactory;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.omnibox.LocationBar;
+import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
 import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.browser.omnibox.UrlBarCoordinator;
 import org.chromium.chrome.browser.omnibox.UrlBarCoordinator.SelectionState;
@@ -609,6 +611,9 @@
         }
 
         @Override
+        public void gestureDetected(boolean isLongPress) {}
+
+        @Override
         public void setShowTitle(boolean showTitle) {
             if (showTitle) {
                 mState = STATE_DOMAIN_AND_TITLE;
@@ -803,17 +808,9 @@
         }
 
         @Override
-        public void setUrlBarFocus(boolean shouldBeFocused) {}
-
-        @Override
         public void showUrlBarCursorWithoutFocusAnimations() {}
 
         @Override
-        public boolean isUrlBarFocused() {
-            return false;
-        }
-
-        @Override
         public void selectAll() {}
 
         @Override
@@ -842,5 +839,25 @@
         @Override
         public void updateSearchEngineStatusIcon(boolean shouldShowSearchEngineLogo,
                 boolean isSearchEngineGoogle, String searchEngineUrl) {}
+
+        // Implements FakeBoxDelegate.
+        @Override
+        public boolean isUrlBarFocused() {
+            return false;
+        }
+
+        @Override
+        public void setUrlBarFocus(boolean shouldBeFocused, @Nullable String pastedText,
+                @LocationBar.OmniboxFocusReason int reason) {}
+
+        @Override
+        public boolean isCurrentPage(NativePage nativePage) {
+            return false;
+        }
+
+        @Override
+        public LocationBarVoiceRecognitionHandler getLocationBarVoiceRecognitionHandler() {
+            return null;
+        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
index e1641c60..8bc47f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
@@ -797,7 +797,9 @@
      * @return Whether or not the current Tab did go back.
      */
     boolean back() {
-        if (getLocationBar() != null) getLocationBar().setUrlBarFocus(false);
+        if (getLocationBar() != null) {
+            getLocationBar().setUrlBarFocus(false, null, LocationBar.OmniboxFocusReason.UNFOCUS);
+        }
         return mToolbarTabController != null && mToolbarTabController.back() != null;
     }
 
@@ -806,7 +808,9 @@
      * @return Whether or not the current Tab did go forward.
      */
     boolean forward() {
-        if (getLocationBar() != null) getLocationBar().setUrlBarFocus(false);
+        if (getLocationBar() != null) {
+            getLocationBar().setUrlBarFocus(false, null, LocationBar.OmniboxFocusReason.UNFOCUS);
+        }
         return mToolbarTabController != null ? mToolbarTabController.forward() : false;
     }
 
@@ -817,7 +821,9 @@
      * <p>The buttons of the toolbar will be updated as a result of making this call.
      */
     void stopOrReloadCurrentTab() {
-        if (getLocationBar() != null) getLocationBar().setUrlBarFocus(false);
+        if (getLocationBar() != null) {
+            getLocationBar().setUrlBarFocus(false, null, LocationBar.OmniboxFocusReason.UNFOCUS);
+        }
         if (mToolbarTabController != null) mToolbarTabController.stopOrReloadCurrentTab();
     }
 
@@ -825,7 +831,9 @@
      * Opens hompage in the current tab.
      */
     void openHomepage() {
-        if (getLocationBar() != null) getLocationBar().setUrlBarFocus(false);
+        if (getLocationBar() != null) {
+            getLocationBar().setUrlBarFocus(false, null, LocationBar.OmniboxFocusReason.UNFOCUS);
+        }
         if (mToolbarTabController != null) mToolbarTabController.openHomepage();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 9e07763..0a3c7282 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -2199,10 +2199,6 @@
 
                 if (getToolbarDataProvider().shouldShowLocationBarInOverviewMode()) {
                     mLocationBar.updateStatusIcon();
-
-                    if (getToolbarDataProvider().isInOverviewAndShowingOmnibox()) {
-                        mUrlBar.setText("");
-                    }
                 }
             }
         });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index 475ba159..17e5596 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -688,7 +688,7 @@
 
     @Override
     public int getBaseStatusBarColor() {
-        return isStatusBarDefaultThemeColor() ? Color.BLACK : mBrandColor;
+        return isStatusBarDefaultThemeColor() ? Color.WHITE : mBrandColor;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
index 6b77aeaca..c90b807 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
@@ -103,10 +103,16 @@
         int DISABLED = -2;
     }
 
-    /** The different reasons that the sheet's state can change. */
+    /**
+     * The different reasons that the sheet's state can change.
+     *
+     * Needs to stay in sync with BottomSheet.StateChangeReason in enums.xml. These values are
+     * persisted to logs. Entries should not be renumbered and numeric values should never be
+     * reused.
+     */
     @IntDef({StateChangeReason.NONE, StateChangeReason.SWIPE, StateChangeReason.BACK_PRESS,
             StateChangeReason.TAP_SCRIM, StateChangeReason.NAVIGATION,
-            StateChangeReason.COMPOSITED_UI, StateChangeReason.VR})
+            StateChangeReason.COMPOSITED_UI, StateChangeReason.VR, StateChangeReason.MAX_VALUE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface StateChangeReason {
         int NONE = 0;
@@ -116,6 +122,7 @@
         int NAVIGATION = 4;
         int COMPOSITED_UI = 5;
         int VR = 6;
+        int MAX_VALUE = VR;
     }
 
     /** The different priorities that the sheet's content can have. */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
index b54a4eb..1fac7734 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
@@ -34,8 +34,8 @@
 import org.chromium.chrome.browser.compositor.layouts.phone.stack.Stack;
 import org.chromium.chrome.browser.compositor.layouts.phone.stack.StackTab;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabBuilder;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
@@ -557,6 +557,6 @@
 
     @Override
     public Tab createTab(int id, boolean incognito) {
-        return new TabBuilder().setId(id).setIncognito(incognito).build();
+        return MockTab.createAndInitialize(id, incognito);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationSheetTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationSheetTest.java
index b337d62..a60a6e1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationSheetTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationSheetTest.java
@@ -107,9 +107,6 @@
         public void navigateToIndex(int index) {
             mNavigationController.goToNavigationIndex(index);
         }
-
-        @Override
-        public void setTabCloseRunnable(Runnable runnable) {}
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
index 8b8f064..cd9c137a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
@@ -20,6 +20,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
@@ -250,4 +251,48 @@
         onView(withId(R.id.location_bar_status))
                 .check((view, e) -> Assert.assertEquals(iconView.getVisibility(), GONE));
     }
+
+    @Test
+    @SmallTest
+    public void testSetUrlBarFocus() {
+        final LocationBarLayout locationBar = getLocationBar();
+
+        Assert.assertEquals(
+                0, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            locationBar.setUrlBarFocus(
+                    true, SEARCH_TERMS_URL, LocationBar.OmniboxFocusReason.FAKE_BOX_LONG_PRESS);
+        });
+        Assert.assertTrue(locationBar.isUrlBarFocused());
+        Assert.assertTrue(locationBar.didFocusUrlFromFakebox());
+        Assert.assertEquals(SEARCH_TERMS_URL, getUrlText(getUrlBar()));
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            locationBar.setUrlBarFocus(
+                    true, SEARCH_TERMS, LocationBar.OmniboxFocusReason.SEARCH_QUERY);
+        });
+        Assert.assertTrue(locationBar.isUrlBarFocused());
+        Assert.assertTrue(locationBar.didFocusUrlFromFakebox());
+        Assert.assertEquals(SEARCH_TERMS, getUrlText(getUrlBar()));
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            locationBar.setUrlBarFocus(false, null, LocationBar.OmniboxFocusReason.UNFOCUS);
+        });
+        Assert.assertFalse(locationBar.isUrlBarFocused());
+        Assert.assertFalse(locationBar.didFocusUrlFromFakebox());
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            locationBar.setUrlBarFocus(true, null, LocationBar.OmniboxFocusReason.OMNIBOX_TAP);
+        });
+        Assert.assertTrue(locationBar.isUrlBarFocused());
+        Assert.assertFalse(locationBar.didFocusUrlFromFakebox());
+        Assert.assertEquals(
+                2, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java
index 72cdc61..54033ef 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java
@@ -34,8 +34,8 @@
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionListEmbedder;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabBuilder;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -434,7 +434,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mWindowAndroid = new TestWindowAndroid(mActivityTestRule.getActivity());
             mWindowAndroid.setAndroidPermissionDelegate(mPermissionDelegate);
-            mTab = new TabBuilder().setId(0).setWindow(mWindowAndroid).build();
+            mTab = new MockTab(0, false);
         });
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/ProfileDataCacheRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/ProfileDataCacheRenderTest.java
index 3f5af50..2d3867e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/ProfileDataCacheRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/ProfileDataCacheRenderTest.java
@@ -24,8 +24,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.params.ParameterAnnotations.ClassParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterSet;
+import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
 import org.chromium.chrome.test.util.RenderTestRule;
 import org.chromium.components.signin.ProfileDataSource;
@@ -34,16 +38,26 @@
 import org.chromium.ui.widget.ChromeImageView;
 
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * Tests for ProfileDataCache image scaling. Leverages RenderTest instead of reimplementing
  * bitmap comparison to simplify access to the compared images on buildbots (via result_details).
  */
-@RunWith(ChromeJUnit4ClassRunner.class)
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 public class ProfileDataCacheRenderTest extends DummyUiActivityTestCase {
-    // Anything different than logo_avatar_anonymous size should work.
-    // TODO(https://crbug.com/967770): Make parameterized and test scaling to different sizes.
-    private static final @Px int IMAGE_SIZE = 64;
+    @ClassParameter
+    private static final List<ParameterSet> sClassParams =
+            Arrays.asList(new ParameterSet().value(64).name("ImageSize64"),
+                    new ParameterSet().value(128).name("ImageSize128"));
+
+    private final @Px int mImageSize;
+
+    public ProfileDataCacheRenderTest(int imageSize) {
+        mImageSize = imageSize;
+    }
 
     @Rule
     public RenderTestRule mRenderTestRule =
@@ -66,7 +80,7 @@
 
             mProfileDataSource = new FakeProfileDataSource();
             mProfileDataCache =
-                    new ProfileDataCache(getActivity(), IMAGE_SIZE, null, mProfileDataSource);
+                    new ProfileDataCache(getActivity(), mImageSize, null, mProfileDataSource);
             // ProfileDataCache only populates the cache when an observer is added.
             mProfileDataCache.addObserver(accountId -> {});
         });
@@ -76,17 +90,10 @@
     @MediumTest
     @Feature("RenderTest")
     public void testPlaceholderIsScaled() throws IOException {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            String accountName = "no.data.for.this.account@example.com";
-            DisplayableProfileData displayableProfileData =
-                    mProfileDataCache.getProfileDataOrDefault(accountName);
-            Drawable placeholderImage = displayableProfileData.getImage();
-            assertEquals(IMAGE_SIZE, placeholderImage.getIntrinsicHeight());
-            assertEquals(IMAGE_SIZE, placeholderImage.getIntrinsicWidth());
-            mImageView.setImageDrawable(placeholderImage);
-        });
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { checkImageIsScaled("no.data.for.this.account@example.com"); });
 
-        mRenderTestRule.render(mImageView, "profile_data_cache_placeholder");
+        mRenderTestRule.render(mImageView, "profile_data_cache_placeholder" + mImageSize);
     }
 
     @Test
@@ -98,16 +105,19 @@
             ProfileDataSource.ProfileData profileData = new ProfileDataSource.ProfileData(
                     accountName, createAvatar(), "Full Name", "Given Name");
             mProfileDataSource.setProfileData(accountName, profileData);
-
-            DisplayableProfileData displayableProfileData =
-                    mProfileDataCache.getProfileDataOrDefault(accountName);
-            Drawable placeholderImage = displayableProfileData.getImage();
-            assertEquals(IMAGE_SIZE, placeholderImage.getIntrinsicHeight());
-            assertEquals(IMAGE_SIZE, placeholderImage.getIntrinsicWidth());
-            mImageView.setImageDrawable(placeholderImage);
+            checkImageIsScaled(accountName);
         });
 
-        mRenderTestRule.render(mImageView, "profile_data_cache_avatar");
+        mRenderTestRule.render(mImageView, "profile_data_cache_avatar" + mImageSize);
+    }
+
+    private void checkImageIsScaled(String accountName) {
+        DisplayableProfileData displayableProfileData =
+                mProfileDataCache.getProfileDataOrDefault(accountName);
+        Drawable placeholderImage = displayableProfileData.getImage();
+        assertEquals(mImageSize, placeholderImage.getIntrinsicHeight());
+        assertEquals(mImageSize, placeholderImage.getIntrinsicWidth());
+        mImageView.setImageDrawable(placeholderImage);
     }
 
     /**
@@ -116,7 +126,7 @@
      */
     private Bitmap createAvatar() {
         final int avatarSize = 100;
-        assertNotEquals("Should be different to test scaling", IMAGE_SIZE, avatarSize);
+        assertNotEquals("Should be different to test scaling", mImageSize, avatarSize);
 
         Bitmap result = Bitmap.createBitmap(avatarSize, avatarSize, Bitmap.Config.ARGB_8888);
         Canvas canvas = new Canvas(result);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java
deleted file mode 100644
index 62666bd1..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.tab;
-
-/**
- * Tab used for various testing purposes.
- */
-public class MockTab extends Tab {
-    /**
-     * Constructor for id and incognito atrribute. Tests often need to initialize
-     * these two fields only.
-     */
-    public MockTab(int id, boolean incognito) {
-        super(id, null, incognito, null, null, null, null);
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java
index d67fa5d7..8823e45 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java
@@ -61,6 +61,10 @@
         mTestServer.stopAndDestroyServer();
     }
 
+    private TabDelegateFactoryImpl createTabDelegateFactory() {
+        return new TabDelegateFactoryImpl(mActivityTestRule.getActivity());
+    }
+
     /**
      * Verify that Tab.StatusWhenSwitchedBackToForeground is correctly recording lazy loads.
      */
@@ -74,9 +78,9 @@
                 Tab bgTab = TabBuilder.createForLazyLoad(new LoadUrlParams(mTestUrl))
                                     .setWindow(mActivityTestRule.getActivity().getWindowAndroid())
                                     .setLaunchType(TabLaunchType.FROM_LONGPRESS_BACKGROUND)
+                                    .setDelegateFactory(createTabDelegateFactory())
+                                    .setInitiallyHidden(true)
                                     .build();
-                bgTab.initialize(null, new TabDelegateFactoryImpl(mActivityTestRule.getActivity()),
-                        true, null, false);
                 return bgTab;
             }
         });
@@ -120,9 +124,9 @@
                 Tab bgTab = TabBuilder.createLiveTab(true)
                                     .setWindow(mActivityTestRule.getActivity().getWindowAndroid())
                                     .setLaunchType(TabLaunchType.FROM_LONGPRESS_BACKGROUND)
+                                    .setDelegateFactory(createTabDelegateFactory())
+                                    .setInitiallyHidden(true)
                                     .build();
-                bgTab.initialize(null, new TabDelegateFactoryImpl(mActivityTestRule.getActivity()),
-                        true, null, false);
                 bgTab.loadUrl(new LoadUrlParams(mTestUrl));
                 bgTab.show(TabSelectionType.FROM_USER);
                 return bgTab;
@@ -139,9 +143,9 @@
                 Tab bgTab = TabBuilder.createLiveTab(true)
                                     .setWindow(mActivityTestRule.getActivity().getWindowAndroid())
                                     .setLaunchType(TabLaunchType.FROM_LONGPRESS_BACKGROUND)
+                                    .setDelegateFactory(createTabDelegateFactory())
+                                    .setInitiallyHidden(true)
                                     .build();
-                bgTab.initialize(null, new TabDelegateFactoryImpl(mActivityTestRule.getActivity()),
-                        true, null, false);
                 bgTab.loadUrl(new LoadUrlParams(mTestUrl));
                 // Simulate the renderer being killed by the OS.
                 ChromeTabUtils.simulateRendererKilledForTesting(bgTab, false);
@@ -160,9 +164,9 @@
                 Tab bgTab = TabBuilder.createForLazyLoad(new LoadUrlParams(mTestUrl))
                                     .setWindow(mActivityTestRule.getActivity().getWindowAndroid())
                                     .setLaunchType(TabLaunchType.FROM_LONGPRESS_BACKGROUND)
+                                    .setDelegateFactory(createTabDelegateFactory())
+                                    .setInitiallyHidden(true)
                                     .build();
-                bgTab.initialize(null, new TabDelegateFactoryImpl(mActivityTestRule.getActivity()),
-                        true, null, false);
                 bgTab.show(TabSelectionType.FROM_USER);
                 return bgTab;
             }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabObserverTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabObserverTest.java
index 68e633c..15e3578 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabObserverTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabObserverTest.java
@@ -16,8 +16,8 @@
 import org.chromium.base.ObserverList.RewindableIterator;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabBuilder;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tab.TabTestUtils;
 import org.chromium.content_public.browser.LoadUrlParams;
@@ -135,9 +135,7 @@
     }
 
     private Tab createTestTab(boolean incognito) {
-        return ThreadUtils.runOnUiThreadBlockingNoException(
-                new TabBuilder().setIncognito(incognito).setWindow(
-                        mTestRule.getWindowAndroid())::build);
+        return ThreadUtils.runOnUiThreadBlockingNoException(() -> new MockTab(0, incognito));
     }
 
     private static void addTab(TabModel tabModel, Tab tab) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
index bc880fa..d5addd3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
@@ -32,8 +32,8 @@
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelper;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.snackbar.undo.UndoBarController;
+import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabBuilder;
 import org.chromium.chrome.browser.tab.TabState;
 import org.chromium.chrome.browser.tab.TabTestUtils;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager.TabCreator;
@@ -101,10 +101,7 @@
 
         @Override
         public Tab createNewTab(LoadUrlParams loadUrlParams, @TabLaunchType int type, Tab parent) {
-            Tab tab = TabBuilder.createForLazyLoad(loadUrlParams)
-                              .setIncognito(mIsIncognito)
-                              .setLaunchType(TabLaunchType.FROM_LINK)
-                              .build();
+            Tab tab = new MockTab(0, mIsIncognito, TabLaunchType.FROM_LINK);
             mSelector.getModel(mIsIncognito).addTab(tab, TabModel.INVALID_TAB_INDEX, type);
             storeTabInfo(null, tab.getId());
             return tab;
@@ -112,10 +109,7 @@
 
         @Override
         public Tab createFrozenTab(TabState state, int id, int index) {
-            Tab tab = TabBuilder.createFromFrozenState()
-                              .setId(id)
-                              .setIncognito(state.isIncognito())
-                              .build();
+            Tab tab = new MockTab(id, state.isIncognito(), TabLaunchType.FROM_RESTORE);
             TabTestUtils.restoreFieldsFromState(tab, state);
             mSelector.getModel(mIsIncognito).addTab(tab, index, TabLaunchType.FROM_RESTORE);
             storeTabInfo(state, id);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java
index d29eb16..d3b359d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java
@@ -20,8 +20,8 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
+import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabBuilder;
 import org.chromium.chrome.browser.tabmodel.TabWindowManager.TabModelSelectorFactory;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModelSelector;
@@ -295,7 +295,7 @@
         AsyncTabParamsManager.getAsyncTabParams().clear();
         final int asyncTabId = 123;
         final TabReparentingParams dummyParams =
-                new TabReparentingParams(new TabBuilder().setId(0).build(), null, null);
+                new TabReparentingParams(new MockTab(0, false), null, null);
         Assert.assertFalse(manager.tabExistsInAnySelector(asyncTabId));
         AsyncTabParamsManager.add(asyncTabId, dummyParams);
         try {
@@ -330,7 +330,7 @@
         AsyncTabParamsManager.getAsyncTabParams().clear();
         final int asyncTabId = 123;
         final TabReparentingParams dummyParams =
-                new TabReparentingParams(new TabBuilder().setId(0).build(), null, null);
+                new TabReparentingParams(new MockTab(0, false), null, null);
         Assert.assertNull(manager.getTabById(asyncTabId));
         AsyncTabParamsManager.add(asyncTabId, dummyParams);
         try {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenTest.java
index 16fc9bef..d931786 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenTest.java
@@ -86,7 +86,7 @@
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
 
         Assert.assertEquals(
-                Color.BLACK, mActivityTestRule.getActivity().getWindow().getStatusBarColor());
+                Color.WHITE, mActivityTestRule.getActivity().getWindow().getStatusBarColor());
     }
 
     @Test
diff --git a/chrome/android/junit/DEPS b/chrome/android/junit/DEPS
index 22f9b33..1519dfea 100644
--- a/chrome/android/junit/DEPS
+++ b/chrome/android/junit/DEPS
@@ -1,11 +1,13 @@
 include_rules = [
   "!clank/java/src/org/chromium/chrome/browser/AppHooksImpl.java",
   "+chrome/lib/lifecycle/public",
+  "+chrome/browser/image_fetcher",
   "+chrome/browser/ui/android/widget",
   "+components/autofill/android/java/src/org/chromium/components/autofill",
   "+components/background_task_scheduler/android",
   "+components/bookmarks/common/android",
   "+components/offline_items_collection/core/android/java/src",
+  "+components/omnibox/browser/android/java/src/org/chromium/components/omnibox",
   "+components/payments/content/android/java/src/org/chromium/components/payments",
   "+components/search_engines/android/java/src/org/chromium/components/search_engines",
   "+components/sync/android/java/src/org/chromium/components/sync",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
index 6d985033f..3bdd7fea 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
@@ -112,7 +112,7 @@
         when(intentDataProvider.getIntent()).thenReturn(intent);
         when(intentDataProvider.getSession()).thenReturn(session);
         when(intentDataProvider.getUrlToLoad()).thenReturn(INITIAL_URL);
-        when(tabFactory.createTab()).thenReturn(tabFromFactory);
+        when(tabFactory.createTab(webContentsCaptor.capture(), any())).thenReturn(tabFromFactory);
         when(tabFactory.getTabModelSelector()).thenReturn(tabModelSelector);
         when(tabModelSelector.getModel(anyBoolean())).thenReturn(tabModel);
         when(connection.getSpeculatedUrl(any())).thenReturn(SPECULATED_URL);
@@ -124,9 +124,6 @@
 
         doNothing().when(activityTabProvider).addObserverAndTrigger(
                 activityTabObserverCaptor.capture());
-        doNothing()
-                .when(tabFromFactory)
-                .initialize(webContentsCaptor.capture(), any(), anyBoolean(), any(), anyBoolean());
     }
 
     @Override
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerTest.java
index 9eb48f4..d25d8e8c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerTest.java
@@ -81,7 +81,7 @@
         Tab savedTab = env.prepareTab();
         env.saveTab(savedTab);
         env.reachNativeInit(mTabController);
-        verify(env.tabFactory, never()).createTab();
+        verify(env.tabFactory, never()).createTab(any(), any());
     }
 
     @Test
@@ -98,7 +98,7 @@
 
         clearInvocations(env.tabFactory);
         mTabController.onFinishNativeInitialization();
-        verify(env.tabFactory, never()).createTab();
+        verify(env.tabFactory, never()).createTab(any(), any());
     }
 
     @Test
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 988c67ea..821135a 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
@@ -78,6 +78,19 @@
 
     @Test
     @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO)
+    public void searchEngineLogo_showGoogleLogo_whenScrolled() {
+        setupSearchEngineLogoForTesting(true, false, false, false);
+
+        mMediator.setUrlHasFocus(false);
+        mMediator.setShowIconsWhenUrlFocused(true);
+        mMediator.setUrlFocusChangePercent(1f);
+        mMediator.updateSearchEngineStatusIcon(true, true, TEST_SEARCH_URL);
+        Assert.assertEquals(
+                R.drawable.ic_logo_googleg_24dp, mModel.get(StatusProperties.STATUS_ICON_RES));
+    }
+
+    @Test
+    @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO)
     public void searchEngineLogo_showGoogleLogo_searchLoupeEverywhere() {
         setupSearchEngineLogoForTesting(true, true, true, false);
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
new file mode 100644
index 0000000..24e065a
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
@@ -0,0 +1,505 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omnibox.suggestions.answer;
+
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.text.Spannable;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.Callback;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.image_fetcher.ImageFetcher;
+import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
+import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
+import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
+import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
+import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
+import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.omnibox.AnswerTextStyle;
+import org.chromium.components.omnibox.AnswerTextType;
+import org.chromium.components.omnibox.AnswerType;
+import org.chromium.components.omnibox.SuggestionAnswer;
+import org.chromium.components.omnibox.SuggestionAnswer.ImageLine;
+import org.chromium.components.omnibox.SuggestionAnswer.TextField;
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+
+import java.util.Arrays;
+
+/**
+ * Tests for {@link AnswerSuggestionProcessor}.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class AnswerSuggestionProcessorUnitTest {
+    private static final @AnswerType int ANSWER_TYPES[] = {AnswerType.DICTIONARY,
+            AnswerType.FINANCE, AnswerType.KNOWLEDGE_GRAPH, AnswerType.SPORTS, AnswerType.SUNRISE,
+            AnswerType.TRANSLATION, AnswerType.WEATHER, AnswerType.WHEN_IS, AnswerType.CURRENCY};
+
+    @Mock
+    SuggestionHost mSuggestionHost;
+
+    @Mock
+    UrlBarEditingTextStateProvider mUrlStateProvider;
+
+    @Mock
+    ImageFetcher mImageFetcher;
+
+    @Mock
+    Bitmap mFakeBitmap;
+
+    @Mock
+    Profile mProfile;
+
+    private Activity mActivity;
+    private AnswerSuggestionProcessor mProcessor;
+
+    /**
+     * Base Suggestion class that can be used for testing.
+     * Holds all mechanisms that are required to processSuggestion and validate suggestions.
+     */
+    class SuggestionTestHelper {
+        // Stores created OmniboxSuggestion
+        protected final OmniboxSuggestion mSuggestion;
+        // Stores PropertyModel for the suggestion.
+        protected final PropertyModel mModel;
+        // Stores Answer object associated with OmniboxSuggestion (if any).
+        private final SuggestionAnswer mAnswer;
+        // Current user input, used by calculation suggestion.
+        private final String mUserQuery;
+
+        private boolean mIsActive;
+
+        private SuggestionTestHelper(OmniboxSuggestion suggestion, SuggestionAnswer answer,
+                PropertyModel model, String userQuery) {
+            mSuggestion = suggestion;
+            mAnswer = answer;
+            mModel = model;
+            mIsActive = true;
+            mUserQuery = userQuery;
+        }
+
+        /** Specify whether suggestion should be reported by SuggestionHost as currently shown. */
+        void setActive(boolean isActive) {
+            mIsActive = isActive;
+        }
+
+        /** Check the content of first suggestion line. */
+        private void verifyLine(String expectedTitle, int expectedMaxLineCount,
+                String expectedDescription, WritableObjectPropertyKey<Spannable> titleKey,
+                WritableIntPropertyKey maxLineCountKey,
+                WritableObjectPropertyKey<String> descriptionKey) {
+            final Spannable actualTitleSpan = mModel.get(titleKey);
+            final String actualTitle = actualTitleSpan == null ? null : actualTitleSpan.toString();
+            final String actualDescription = mModel.get(descriptionKey);
+
+            Assert.assertNotNull(actualTitle);
+            Assert.assertEquals(expectedTitle, actualTitle);
+
+            Assert.assertEquals(expectedDescription, actualDescription);
+            Assert.assertEquals(expectedMaxLineCount, mModel.get(maxLineCountKey));
+        }
+
+        void verifyLine1(
+                String expectedTitle, int expectedMaxLineCount, String expectedDescription) {
+            verifyLine(expectedTitle, expectedMaxLineCount, expectedDescription,
+                    AnswerSuggestionViewProperties.TEXT_LINE_1_TEXT,
+                    AnswerSuggestionViewProperties.TEXT_LINE_1_MAX_LINES,
+                    AnswerSuggestionViewProperties.TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION);
+        }
+
+        /** Check the content of first suggestion line. */
+        void verifyLine2(
+                String expectedTitle, int expectedMaxLineCount, String expectedDescription) {
+            verifyLine(expectedTitle, expectedMaxLineCount, expectedDescription,
+                    AnswerSuggestionViewProperties.TEXT_LINE_2_TEXT,
+                    AnswerSuggestionViewProperties.TEXT_LINE_2_MAX_LINES,
+                    AnswerSuggestionViewProperties.TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION);
+        }
+
+        /** Get Drawable associated with the suggestion. */
+        Drawable getIcon() {
+            final SuggestionDrawableState state = mModel.get(BaseSuggestionViewProperties.ICON);
+            return state == null ? null : state.drawable;
+        }
+    };
+
+    /** Create Calculation Suggestion. */
+    SuggestionTestHelper createCalculationSuggestion(String displayText, String userQuery) {
+        OmniboxSuggestion suggestion = new OmniboxSuggestion(OmniboxSuggestionType.CALCULATOR,
+                /* isSearchType */ true, /* relevance */ 0, /* transition */ 0, displayText,
+                /* displayTextClassifications */ null, /* description */ null,
+                /* descriptionClassifications */ null,
+                /* suggestionAnswer */ null, /* fillIntoEdit */ "", /* url */ "",
+                /* imageUrl */ "", /* imageDominantColor */ "",
+                /* isStarred */ false, /* isDeletable */ false);
+        PropertyModel model = mProcessor.createModelForSuggestion(suggestion);
+        return new SuggestionTestHelper(suggestion, null, model, userQuery);
+    }
+
+    /** Create Answer Suggestion. */
+    SuggestionTestHelper createAnswerSuggestion(@AnswerType int type, String line1Text,
+            int line1Size, String line2Text, int line2Size, String url) {
+        SuggestionAnswer answer =
+                new SuggestionAnswer(type, createAnswerImageLine(line1Text, line1Size, null),
+                        createAnswerImageLine(line2Text, line2Size, url));
+        OmniboxSuggestion suggestion = new OmniboxSuggestion(
+                /* nativeType */ 0,
+                /* isSearchType */ true, /* relevance */ 0, /* transition */ 0,
+                /* displayText */ null, /* displayTextClassifications */ null,
+                /* description */ null, /* descriptionClassifications */ null, answer,
+                /* fillIntoEdit */ "", /* url */ "", /* imageUrl */ "",
+                /* imageDominantColor */ "",
+                /* isStarred */ false, /* isDeletable */ false);
+        PropertyModel model = mProcessor.createModelForSuggestion(suggestion);
+        return new SuggestionTestHelper(suggestion, answer, model, null);
+    }
+
+    /** Create a (possibly) multi-line ImageLine object with default formatting. */
+    private ImageLine createAnswerImageLine(String text, int lines, String url) {
+        return new ImageLine(Arrays.asList(new TextField(AnswerTextType.SUGGESTION, text,
+                                     AnswerTextStyle.NORMAL, lines)),
+                /* additionalText */ null, /* statusText */ null, url);
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mActivity = Robolectric.buildActivity(Activity.class).setup().get();
+        when(mSuggestionHost.getCurrentProfile()).thenReturn(mProfile);
+
+        mProcessor = new AnswerSuggestionProcessor(mActivity, mSuggestionHost, mUrlStateProvider);
+        mProcessor.setImageFetcherForTesting(mImageFetcher);
+    }
+
+    /** Populate model for associated suggestion. */
+    void processSuggestion(SuggestionTestHelper helper) {
+        // Note: Calculation needs access to raw, unmodified content of the Omnibox to present
+        // the formula the user typed in.
+        when(mUrlStateProvider.getTextWithoutAutocomplete()).thenReturn(helper.mUserQuery);
+        when(mSuggestionHost.isActiveModel(helper.mModel)).thenReturn(helper.mIsActive);
+
+        mProcessor.populateModel(helper.mSuggestion, helper.mModel, 0);
+
+        when(mUrlStateProvider.getTextWithoutAutocomplete()).thenReturn(null);
+    }
+
+    @Test
+    public void regularAnswer_order() {
+        final SuggestionTestHelper suggHelper =
+                createAnswerSuggestion(AnswerType.KNOWLEDGE_GRAPH, "Query", 1, "Answer", 1, null);
+        processSuggestion(suggHelper);
+
+        // Note: Most answers are shown in Answer -> Question order, but announced in
+        // Question -> Answer order.
+        suggHelper.verifyLine1("Answer", 1, "Query");
+        suggHelper.verifyLine2("Query", 1, "Answer");
+    }
+
+    @Test
+    public void dictionaryAnswer_order() {
+        final SuggestionTestHelper suggHelper =
+                createAnswerSuggestion(AnswerType.DICTIONARY, "Query", 1, "Answer", 1, null);
+        processSuggestion(suggHelper);
+
+        // Note: Dictionary answers are shown in Question -> Answer order.
+        suggHelper.verifyLine1("Query", 1, "Query");
+        suggHelper.verifyLine2("Answer", 1, "Answer");
+    }
+
+    @Test
+    public void calculationAnswer_order() {
+        final SuggestionTestHelper suggHelper = createCalculationSuggestion("12345", "123 + 45");
+        processSuggestion(suggHelper);
+
+        suggHelper.verifyLine1("123 + 45", 1, null);
+        suggHelper.verifyLine2("12345", 1, null);
+    }
+
+    @Test
+    public void regularAnswer_shortMultiline() {
+        final SuggestionTestHelper suggHelper =
+                createAnswerSuggestion(AnswerType.KNOWLEDGE_GRAPH, "", 1, "", 3, null);
+        processSuggestion(suggHelper);
+
+        suggHelper.verifyLine1("", 3, "");
+        suggHelper.verifyLine2("", 1, "");
+    }
+
+    @Test
+    public void dictionaryAnswer_shortMultiline() {
+        final SuggestionTestHelper suggHelper =
+                createAnswerSuggestion(AnswerType.DICTIONARY, "", 1, "", 3, null);
+        processSuggestion(suggHelper);
+
+        suggHelper.verifyLine1("", 1, "");
+        suggHelper.verifyLine2("", 3, "");
+    }
+
+    // Check that multiline answers that span across more than 3 lines - are reduced to 3 lines.
+    // Check that multiline titles are truncated to a single line.
+    @Test
+    public void regularAnswer_truncatedMultiline() {
+        final SuggestionTestHelper suggHelper =
+                createAnswerSuggestion(AnswerType.KNOWLEDGE_GRAPH, "", 3, "", 10, null);
+        processSuggestion(suggHelper);
+
+        suggHelper.verifyLine1("", 3, "");
+        suggHelper.verifyLine2("", 1, "");
+    }
+
+    @Test
+    public void dictionaryAnswer_truncatedMultiline() {
+        final SuggestionTestHelper suggHelper =
+                createAnswerSuggestion(AnswerType.DICTIONARY, "", 3, "", 10, null);
+        processSuggestion(suggHelper);
+
+        suggHelper.verifyLine1("", 1, "");
+        suggHelper.verifyLine2("", 3, "");
+    }
+
+    // Image fetching and icon association tests.
+    @Test
+    public void answerImage_fallbackIcons() {
+        for (@AnswerType int type : ANSWER_TYPES) {
+            SuggestionTestHelper suggHelper = createAnswerSuggestion(type, "", 1, "", 1, null);
+            processSuggestion(suggHelper);
+            // Note: model is re-created on every iteration.
+            Assert.assertNotNull("No icon associated with type: " + type, suggHelper.getIcon());
+        }
+    }
+
+    @Test
+    public void answerImage_iconAssociation() {
+        SuggestionTestHelper suggHelper =
+                createAnswerSuggestion(AnswerType.DICTIONARY, "", 1, "", 1, null);
+        Assert.assertEquals(
+                R.drawable.ic_book_round, mProcessor.getSuggestionIcon(suggHelper.mSuggestion));
+
+        suggHelper = createAnswerSuggestion(AnswerType.FINANCE, "", 1, "", 1, null);
+        Assert.assertEquals(R.drawable.ic_swap_vert_round,
+                mProcessor.getSuggestionIcon(suggHelper.mSuggestion));
+
+        suggHelper = createAnswerSuggestion(AnswerType.KNOWLEDGE_GRAPH, "", 1, "", 1, null);
+        Assert.assertEquals(
+                R.drawable.ic_google_round, mProcessor.getSuggestionIcon(suggHelper.mSuggestion));
+
+        suggHelper = createAnswerSuggestion(AnswerType.SPORTS, "", 1, "", 1, null);
+        Assert.assertEquals(
+                R.drawable.ic_google_round, mProcessor.getSuggestionIcon(suggHelper.mSuggestion));
+
+        suggHelper = createAnswerSuggestion(AnswerType.SUNRISE, "", 1, "", 1, null);
+        Assert.assertEquals(
+                R.drawable.ic_wb_sunny_round, mProcessor.getSuggestionIcon(suggHelper.mSuggestion));
+
+        suggHelper = createAnswerSuggestion(AnswerType.TRANSLATION, "", 1, "", 1, null);
+        Assert.assertEquals(R.drawable.logo_translate_round,
+                mProcessor.getSuggestionIcon(suggHelper.mSuggestion));
+
+        suggHelper = createAnswerSuggestion(AnswerType.WEATHER, "", 1, "", 1, null);
+        Assert.assertEquals(R.drawable.logo_partly_cloudy,
+                mProcessor.getSuggestionIcon(suggHelper.mSuggestion));
+
+        suggHelper = createAnswerSuggestion(AnswerType.WHEN_IS, "", 1, "", 1, null);
+        Assert.assertEquals(
+                R.drawable.ic_event_round, mProcessor.getSuggestionIcon(suggHelper.mSuggestion));
+
+        suggHelper = createAnswerSuggestion(AnswerType.CURRENCY, "", 1, "", 1, null);
+        Assert.assertEquals(
+                R.drawable.ic_loop_round, mProcessor.getSuggestionIcon(suggHelper.mSuggestion));
+
+        suggHelper = createCalculationSuggestion("", "");
+        Assert.assertEquals(R.drawable.ic_equals_sign_round,
+                mProcessor.getSuggestionIcon(suggHelper.mSuggestion));
+    }
+
+    @Test
+    public void answerImage_repeatedUrlsAreFetchedOnlyOnce() {
+        final String url1 = "http://site1.com";
+        final String url2 = "http://site2.com";
+        final SuggestionTestHelper sugg1 =
+                createAnswerSuggestion(AnswerType.WEATHER, "", 1, "", 1, url1);
+        final SuggestionTestHelper sugg2 =
+                createAnswerSuggestion(AnswerType.DICTIONARY, "", 1, "", 1, url1);
+        final SuggestionTestHelper sugg3 =
+                createAnswerSuggestion(AnswerType.SPORTS, "", 1, "", 1, url2);
+        final SuggestionTestHelper sugg4 =
+                createAnswerSuggestion(AnswerType.CURRENCY, "", 1, "", 1, url2);
+
+        processSuggestion(sugg1);
+        processSuggestion(sugg2);
+        processSuggestion(sugg3);
+        processSuggestion(sugg4);
+
+        verify(mImageFetcher, times(1)).fetchImage(eq(url1), anyString(), any());
+        verify(mImageFetcher, times(1)).fetchImage(eq(url2), anyString(), any());
+        verify(mImageFetcher, times(2)).fetchImage(anyString(), anyString(), any());
+    }
+
+    @Test
+    public void answerImage_bitmapReplacesIconForAllSuggestionsWithSameUrl() {
+        final String url = "http://site.com";
+        final ArgumentCaptor<Callback<Bitmap>> callback = ArgumentCaptor.forClass(Callback.class);
+        final SuggestionTestHelper sugg1 =
+                createAnswerSuggestion(AnswerType.WEATHER, "", 1, "", 1, url);
+        final SuggestionTestHelper sugg2 =
+                createAnswerSuggestion(AnswerType.DICTIONARY, "", 1, "", 1, url);
+        final SuggestionTestHelper sugg3 =
+                createAnswerSuggestion(AnswerType.SPORTS, "", 1, "", 1, url);
+
+        processSuggestion(sugg1);
+        processSuggestion(sugg2);
+        processSuggestion(sugg3);
+
+        verify(mImageFetcher).fetchImage(eq(url), anyString(), callback.capture());
+
+        final Drawable icon1 = sugg1.getIcon();
+        final Drawable icon2 = sugg2.getIcon();
+        final Drawable icon3 = sugg3.getIcon();
+        callback.getValue().onResult(mFakeBitmap);
+        final Drawable newIcon1 = sugg1.getIcon();
+        final Drawable newIcon2 = sugg2.getIcon();
+        final Drawable newIcon3 = sugg3.getIcon();
+
+        Assert.assertNotEquals(icon1, newIcon1);
+        Assert.assertNotEquals(icon2, newIcon2);
+        Assert.assertNotEquals(icon3, newIcon3);
+
+        Assert.assertThat(newIcon1, instanceOf(BitmapDrawable.class));
+        Assert.assertThat(newIcon2, instanceOf(BitmapDrawable.class));
+        Assert.assertThat(newIcon3, instanceOf(BitmapDrawable.class));
+
+        Assert.assertEquals(mFakeBitmap, ((BitmapDrawable) newIcon1).getBitmap());
+        Assert.assertEquals(mFakeBitmap, ((BitmapDrawable) newIcon2).getBitmap());
+        Assert.assertEquals(mFakeBitmap, ((BitmapDrawable) newIcon3).getBitmap());
+    }
+
+    @Test
+    public void answerImage_failedBitmapFetchDoesNotClearIcons() {
+        final String url = "http://site.com";
+        final ArgumentCaptor<Callback<Bitmap>> callback = ArgumentCaptor.forClass(Callback.class);
+        final SuggestionTestHelper suggHelper =
+                createAnswerSuggestion(AnswerType.WEATHER, "", 1, "", 1, url);
+
+        processSuggestion(suggHelper);
+
+        verify(mImageFetcher).fetchImage(eq(url), anyString(), callback.capture());
+
+        final Drawable icon = suggHelper.getIcon();
+        callback.getValue().onResult(null);
+        final Drawable newIcon = suggHelper.getIcon();
+
+        Assert.assertEquals(icon, newIcon);
+    }
+
+    @Test
+    public void answerImage_noImageFetchWhenProfileIsUnavailable() {
+        final String url = "http://site.com";
+        when(mSuggestionHost.getCurrentProfile()).thenReturn(null);
+        final SuggestionTestHelper suggHelper =
+                createAnswerSuggestion(AnswerType.WEATHER, "", 1, "", 1, url);
+        processSuggestion(suggHelper);
+
+        verify(mImageFetcher, times(0)).fetchImage(anyString(), anyString(), any());
+    }
+
+    @Test
+    public void answerImage_oldModelsAreNotUpdated() {
+        final ArgumentCaptor<Callback<Bitmap>> callback1 = ArgumentCaptor.forClass(Callback.class);
+        final ArgumentCaptor<Callback<Bitmap>> callback2 = ArgumentCaptor.forClass(Callback.class);
+
+        final String url1 = "http://site1.com";
+        final String url2 = "http://site2.com";
+
+        final SuggestionTestHelper sugg1 =
+                createAnswerSuggestion(AnswerType.WEATHER, "", 1, "", 1, url1);
+        final SuggestionTestHelper sugg2 =
+                createAnswerSuggestion(AnswerType.DICTIONARY, "", 1, "", 1, url1);
+        final SuggestionTestHelper sugg3 =
+                createAnswerSuggestion(AnswerType.SPORTS, "", 1, "", 1, url2);
+        final SuggestionTestHelper sugg4 =
+                createAnswerSuggestion(AnswerType.CURRENCY, "", 1, "", 1, url2);
+
+        sugg2.setActive(false);
+        sugg4.setActive(false);
+
+        processSuggestion(sugg1);
+        processSuggestion(sugg2);
+        processSuggestion(sugg3);
+        processSuggestion(sugg4);
+
+        verify(mImageFetcher, times(1)).fetchImage(eq(url1), anyString(), callback1.capture());
+        verify(mImageFetcher, times(1)).fetchImage(eq(url2), anyString(), callback2.capture());
+
+        final Drawable icon1 = sugg1.getIcon();
+        final Drawable icon2 = sugg2.getIcon();
+        final Drawable icon3 = sugg3.getIcon();
+        final Drawable icon4 = sugg4.getIcon();
+        callback1.getValue().onResult(mFakeBitmap);
+        callback2.getValue().onResult(mFakeBitmap);
+        final Drawable newIcon1 = sugg1.getIcon();
+        final Drawable newIcon2 = sugg2.getIcon();
+        final Drawable newIcon3 = sugg3.getIcon();
+        final Drawable newIcon4 = sugg4.getIcon();
+
+        Assert.assertNotEquals(icon1, newIcon1);
+        Assert.assertEquals(icon2, newIcon2);
+        Assert.assertNotEquals(icon3, newIcon3);
+        Assert.assertEquals(icon4, newIcon4);
+    }
+
+    @Test
+    public void answerImage_associatedModelsAreErasedFromPendingListAfterImageFetch() {
+        ArgumentCaptor<Callback<Bitmap>> callback = ArgumentCaptor.forClass(Callback.class);
+        final String url = "http://site1.com";
+
+        final SuggestionTestHelper sugg1 =
+                createAnswerSuggestion(AnswerType.WEATHER, "", 1, "", 1, url);
+        final SuggestionTestHelper sugg2 =
+                createAnswerSuggestion(AnswerType.DICTIONARY, "", 1, "", 1, url);
+
+        processSuggestion(sugg1);
+        processSuggestion(sugg2);
+
+        verify(mImageFetcher, times(1)).fetchImage(eq(url), anyString(), callback.capture());
+
+        final Drawable icon1 = sugg1.getIcon();
+        final Drawable icon2 = sugg2.getIcon();
+
+        // Invoke callback twice. If models were not erased, these should be updated.
+        callback.getValue().onResult(null);
+        callback.getValue().onResult(mFakeBitmap);
+
+        final Drawable newIcon1 = sugg1.getIcon();
+        final Drawable newIcon2 = sugg2.getIcon();
+
+        // Observe no change, despite updated image.
+        Assert.assertEquals(icon1, newIcon1);
+        Assert.assertEquals(icon2, newIcon2);
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
index 9f970b1..f052515 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
@@ -73,7 +73,7 @@
         features.put("ShoppingAssist", true);
         ChromeFeatureList.setTestFeatures(features);
 
-        mTab = new Tab(TAB1_ID, null, false, mWindowAndroid, null, null, mLoadUrlParams);
+        mTab = new Tab(TAB1_ID, null, false, null);
         mTab.addObserver(mObserver);
     }
 
diff --git a/chrome/android/modules/dev_ui/provider/dev_ui_module_provider.cc b/chrome/android/modules/dev_ui/provider/dev_ui_module_provider.cc
index 1879bbf1..4e251373 100644
--- a/chrome/android/modules/dev_ui/provider/dev_ui_module_provider.cc
+++ b/chrome/android/modules/dev_ui/provider/dev_ui_module_provider.cc
@@ -12,16 +12,20 @@
 
 namespace dev_ui {
 
-// static
-DevUiModuleProvider* DevUiModuleProvider::test_instance_ = nullptr;
+namespace {
+
+// Global DevUiModuleProvider instance for testing.
+DevUiModuleProvider* g_test_instance = nullptr;
+
+}  // namespace
 
 // Destructor is public to enable management by std::unique_ptr<>.
 DevUiModuleProvider::~DevUiModuleProvider() = default;
 
 // static
 DevUiModuleProvider* DevUiModuleProvider::GetInstance() {
-  if (test_instance_)
-    return test_instance_;
+  if (g_test_instance)
+    return g_test_instance;
 
   static DevUiModuleProvider instance;
   return &instance;
@@ -29,7 +33,7 @@
 
 // static
 void DevUiModuleProvider::SetTestInstance(DevUiModuleProvider* test_instance) {
-  test_instance_ = test_instance;
+  g_test_instance = test_instance;
 }
 
 bool DevUiModuleProvider::ModuleInstalled() {
diff --git a/chrome/android/modules/dev_ui/provider/dev_ui_module_provider.h b/chrome/android/modules/dev_ui/provider/dev_ui_module_provider.h
index c8eb037..0f925d9 100644
--- a/chrome/android/modules/dev_ui/provider/dev_ui_module_provider.h
+++ b/chrome/android/modules/dev_ui/provider/dev_ui_module_provider.h
@@ -14,20 +14,20 @@
   // Returns the singleton, which can be overridden using SetTestInstance().
   static DevUiModuleProvider* GetInstance();
 
-  // Overrides the singleton with caller-owned |test_instance|. Caller tests
+  // Overrides the singleton with caller-owned |test_instance|. Callers in tests
   // are responsible for resetting this to null on cleanup.
   static void SetTestInstance(DevUiModuleProvider* test_instance);
 
-  // Returns true if the DevUI module is installed.
+  // Returns true if the DevUI module is installed. Virtual to enable testing.
   virtual bool ModuleInstalled();
 
   // Asynchronously requests to install the DevUI module. |on_complete| is
   // called after the module install is completed, and takes a bool to indicate
-  // whether module install is successful.
+  // whether module install is successful. Virtual to enable testing.
   virtual void InstallModule(base::OnceCallback<void(bool)> on_complete);
 
   // Assuming that the DevUI module is installed, loads DevUI resources if not
-  // already loaded.
+  // already loaded. Virtual to enable testing.
   virtual void LoadModule();
 
  protected:
@@ -35,9 +35,6 @@
   virtual ~DevUiModuleProvider();
   DevUiModuleProvider(const DevUiModuleProvider&) = delete;
   DevUiModuleProvider& operator=(const DevUiModuleProvider&) = delete;
-
- private:
-  static DevUiModuleProvider* test_instance_;
 };
 
 }  // namespace dev_ui
diff --git a/chrome/android/webapk/shell_apk/AndroidManifest.xml b/chrome/android/webapk/shell_apk/AndroidManifest.xml
index ccfd7ad..621525d 100644
--- a/chrome/android/webapk/shell_apk/AndroidManifest.xml
+++ b/chrome/android/webapk/shell_apk/AndroidManifest.xml
@@ -42,6 +42,11 @@
                 <category android:name="android.intent.category.BROWSABLE"></category>
                 <data android:scheme="{{{scope_url_scheme}}}" android:host="{{{scope_url_host}}}" {{{scope_url_path_type}}}="{{{scope_url_path}}}"></data>
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.nfc.action.NDEF_DISCOVERED"></action>
+                <category android:name="android.intent.category.DEFAULT"></category>
+                <data android:scheme="{{{scope_url_scheme}}}" android:host="{{{scope_url_host}}}" {{{scope_url_path_type}}}="{{{scope_url_path}}}"></data>
+            </intent-filter>
             {{/intent_filters}}
             {{{raw_intent_filters}}}
          </activity>
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index ad30203..181af5ce 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 109
+current_shell_apk_version = 110
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
index 35e79f0..bce6ea0 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
@@ -29,7 +29,7 @@
     private static final int MINIMUM_REQUIRED_INTENT_HELPER_VERSION = 2;
 
     // Lowest version of Chromium which supports ShellAPK showing the splash screen.
-    public static final int MINIMUM_REQUIRED_CHROMIUM_VERSION_NEW_SPLASH = 77;
+    public static final int MINIMUM_REQUIRED_CHROMIUM_VERSION_NEW_SPLASH = 78;
 
     private static final String VERSION_NAME_DEVELOPER_BUILD = "Developer Build";
 
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java
index 4f2244c..bcc1996 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java
@@ -44,9 +44,6 @@
 public class WebApkUtils {
     private static final String TAG = "cr_WebApkUtils";
 
-    /** Percentage to darken a color by when setting the status bar color. */
-    private static final float DARKEN_COLOR_FRACTION = 0.6f;
-
     private static final float CONTRAST_LIGHT_ITEM_THRESHOLD = 3f;
 
     /** Returns whether the application is installed and enabled. */
@@ -205,28 +202,6 @@
     }
 
     /**
-     * Darkens the given color to use on the status bar.
-     * @param color Color which should be darkened.
-     * @return Color that should be used for Android status bar.
-     */
-    public static int getDarkenedColorForStatusBar(int color) {
-        return getDarkenedColor(color, DARKEN_COLOR_FRACTION);
-    }
-
-    /**
-     * Darken a color to a fraction of its current brightness.
-     * @param color The input color.
-     * @param darkenFraction The fraction of the current brightness the color should be.
-     * @return The new darkened color.
-     */
-    public static int getDarkenedColor(int color, float darkenFraction) {
-        float[] hsv = new float[3];
-        Color.colorToHSV(color, hsv);
-        hsv[2] *= darkenFraction;
-        return Color.HSVToColor(hsv);
-    }
-
-    /**
      * Check whether lighter or darker foreground elements (i.e. text, drawables etc.)
      * should be used depending on the given background color.
      * @param backgroundColor The background color value which is being queried.
@@ -258,6 +233,25 @@
     }
 
     /**
+     * Sets the status bar icons to dark or light. Note that this is only valid for
+     * Android M+.
+     *
+     * @param rootView The root view used to request updates to the system UI theming.
+     * @param useDarkIcons Whether the status bar icons should be dark.
+     */
+    public static void setStatusBarIconColor(View rootView, boolean useDarkIcons) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
+
+        int systemUiVisibility = rootView.getSystemUiVisibility();
+        if (useDarkIcons) {
+            systemUiVisibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        } else {
+            systemUiVisibility &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        }
+        rootView.setSystemUiVisibility(systemUiVisibility);
+    }
+
+    /**
      * @see android.view.Window#setStatusBarColor(int color).
      */
     public static void setStatusBarColor(Window window, int statusBarColor) {
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/SplashActivity.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/SplashActivity.java
index 701e4f1..72c447f 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/SplashActivity.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/SplashActivity.java
@@ -150,10 +150,7 @@
 
     private void showSplashScreen() {
         Bundle metadata = WebApkUtils.readMetaData(this);
-        int themeColor = (int) WebApkMetaDataUtils.getLongFromMetaData(
-                metadata, WebApkMetaDataKeys.THEME_COLOR, Color.BLACK);
-        WebApkUtils.setStatusBarColor(
-                getWindow(), WebApkUtils.getDarkenedColorForStatusBar(themeColor));
+        updateStatusBar(metadata);
 
         int orientation = WebApkUtils.computeScreenLockOrientationFromMetaData(this, metadata);
         if (orientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
@@ -175,6 +172,19 @@
         setContentView(mSplashView);
     }
 
+    /**
+     * Sets the the color of the status bar and status bar icons.
+     */
+    private void updateStatusBar(Bundle metadata) {
+        int statusBarColor = (int) WebApkMetaDataUtils.getLongFromMetaData(
+                metadata, WebApkMetaDataKeys.THEME_COLOR, Color.WHITE);
+        WebApkUtils.setStatusBarColor(getWindow(), statusBarColor);
+        boolean needsDarkStatusBarIcons =
+                !WebApkUtils.shouldUseLightForegroundOnBackground(statusBarColor);
+        WebApkUtils.setStatusBarIconColor(
+                getWindow().getDecorView().getRootView(), needsDarkStatusBarIcons);
+    }
+
     /** Called once the host browser has been selected. */
     private void onHostBrowserSelected(HostBrowserLauncherParams params) {
         if (params == null) {
diff --git a/chrome/app/app_management_strings.grdp b/chrome/app/app_management_strings.grdp
index d8e5b684..23d9c53 100644
--- a/chrome/app/app_management_strings.grdp
+++ b/chrome/app/app_management_strings.grdp
@@ -1,84 +1,36 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <grit-part>
-  <message name="IDS_APP_MANAGEMENT_ANDROID_SETTINGS" desc="Label for the open Android settings button on the ARC app permissions page.">
-    Open Android settings
-  </message>
-  <message name="IDS_APP_MANAGEMENT_APP_LIST_PREVIEW" desc="A label that summarizes a list of apps by naming up to 3 apps, finishing with 'X more apps' for the rest.">
-    {COUNT, plural,
-     =0 {None}
-     =1 {<ph name="APP_1">$1<ex>Gmail</ex></ph>}
-     =2 {<ph name="APP_1">$1<ex>Gmail</ex></ph> and <ph name="APP_2">$2<ex>Youtube</ex></ph>}
-     =3 {<ph name="APP_1">$1<ex>Gmail</ex></ph>, <ph name="APP_2">$2<ex>Youtube</ex></ph>, and <ph name="APP_3">$3<ex>Calendar</ex></ph>}
-     =4 {<ph name="APP_1">$1<ex>Gmail</ex></ph>, <ph name="APP_2">$2<ex>Youtube</ex></ph>, <ph name="APP_3">$3<ex>Calendar</ex></ph>, and 1 other app}
-     other {<ph name="APP_1">$1<ex>Gmail</ex></ph>, <ph name="APP_2">$2<ex>Youtube</ex></ph>, <ph name="APP_3">$3<ex>Calendar</ex></ph>, and <ph name="EXTRA_APPS">$4<ex>4</ex></ph> other apps}}
-  </message>
-  <message name="IDS_APP_MANAGEMENT_APP_LIST_TITLE" desc="Title of app list card on main app management page.">
-    Manage your apps
-  </message>
-  <message name="IDS_APP_MANAGEMENT_BACK" desc="Label for button for going back to the previous page.">
-    Back
-  </message>
   <message name="IDS_APP_MANAGEMENT_CAMERA" desc="Label for the camera permission toggle.">
     Camera
   </message>
-  <message name="IDS_APP_MANAGEMENT_EXTENSIONS_SETTINGS" desc="Label for the open Extensions settings button on the Chrome app permissions page.">
-    Open Extensions settings
-  </message>
-  <message name="IDS_APP_MANAGEMENT_LESS_APPS" desc="Label for showing less apps.">
-    Show less
-  </message>
   <message name="IDS_APP_MANAGEMENT_LOCATION" desc="Label for the location permission toggle.">
     Location
   </message>
   <message name="IDS_APP_MANAGEMENT_MICROPHONE" desc="Label for the microphone permission toggle.">
     Microphone
   </message>
-  <message name="IDS_APP_MANAGEMENT_MORE_APPS" desc="Label for more apps button at bottom of app list card.">
-    Show <ph name="NUMBER_OF_MORE_APPS">$1<ex>4</ex></ph> more apps
-  </message>
   <message name="IDS_APP_MANAGEMENT_NO_APPS_FOUND" desc="Text shown when a search of apps yeilds no results.">
     No apps found
   </message>
   <message name="IDS_APP_MANAGEMENT_NOTIFICATIONS" desc="Label for notifications section in the app settings page.">
     Notifications
   </message>
-  <message name="IDS_APP_MANAGEMENT_NOTIFICATIONS_SUBLABEL" desc="Sublabel for the Notifications item in the main App Management view">
-    <ph name="NUMBER_OF_MORE_APPS">$1<ex>4</ex></ph> apps
-  </message>
   <message name="IDS_APP_MANAGEMENT_PERMISSIONS" desc="Label for permissions section in the app settings page.">
     Permissions
   </message>
   <message name="IDS_APP_MANAGEMENT_MORE_SETTINGS" desc="Label for a link to more settings and permissions for an app.">
     More settings and permissions
   </message>
-  <message name="IDS_APP_MANAGEMENT_THIS_APP_CAN" desc="Label for permissions that a Chrome app can use.">
-    This app can:
-  </message>
   <message name="IDS_APP_MANAGEMENT_PIN_TO_SHELF" desc="Label for the pin to shelf button in the app settings page.">
     Pin to shelf
   </message>
   <message name="IDS_APP_MANAGEMENT_SEARCH_PROMPT" desc="Prompt in search bar of main app management page.">
     Search apps
   </message>
-  <message name="IDS_APP_MANAGEMENT_SITE_SETTING" desc="Label for the open site settings for PWA app settings page.">
-    Open site settings
-  </message>
-  <message name="IDS_APP_MANAGEMENT_TITLE" desc="Title of the app management page.">
-    Apps
-  </message>
-  <message name="IDS_APP_MANAGEMENT_SIZE" desc="Label for the size of an app in the app settings page.">
-    Size: <ph name="APP_SIZE">$1<ex>8.0MB</ex></ph>
-  </message>
   <message name="IDS_APP_MANAGEMENT_UNINSTALL_APP" desc="Label for the uninstall button in the app settings page.">
     Uninstall
   </message>
-  <message name="IDS_APP_MANAGEMENT_VERSION" desc="Label for version of an app in the app settings page.">
-    Version: <ph name="VERSION">$1<ex>4.0</ex></ph>
-  </message>
-  <message name="IDS_APP_MANAGEMENT_PIN_ENFORCED_BY_POLICY" desc="Short text for the pin/unpin context menu to tell user the setting is enforced by policy of an app.">
-    Pinned by administrator
-  </message>
   <message name="IDS_APP_MANAGEMENT_CONTACTS" desc="Label for the Android contacts (address book) permission toggle.">
     Contacts
   </message>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 116b5fcd..fbbadfa 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5351,29 +5351,15 @@
       <message name="IDS_TOOLTIP_LOCATION_ICON" desc="The tooltip for location icon in the omnibox">
         View site information
       </message>
-      <if expr="not use_titlecase">
-        <message name="IDS_TOOLTIP_NEW_TAB" desc="The tooltip for the new tab button">
-          New tab
-        </message>
-      </if>
-      <if expr="use_titlecase">
-        <message name="IDS_TOOLTIP_NEW_TAB" desc="The tooltip for the new tab button">
-          New Tab
-        </message>
-       </if>
+      <message name="IDS_TOOLTIP_NEW_TAB" desc="The tooltip for the new tab button">
+        New tab
+      </message>
       <message name="IDS_TOOLTIP_MIC_SEARCH" desc="The tooltip for search-by-voice button">
         Search by voice
       </message>
-      <if expr="not use_titlecase">
-        <message name="IDS_TOOLTIP_SAVE_CREDIT_CARD" desc="The tooltip for the icon that shows the save credit card bubble">
-          Save credit card
-        </message>
-      </if>
-      <if expr="use_titlecase">
-        <message name="IDS_TOOLTIP_SAVE_CREDIT_CARD" desc="In Title Case: The tooltip for the icon that shows the save card bubble">
-          Save Card
-        </message>
-       </if>
+      <message name="IDS_TOOLTIP_SAVE_CREDIT_CARD" desc="The tooltip for the icon that shows the save credit card bubble">
+        Save credit card
+      </message>
       <message name="IDS_TOOLTIP_MIGRATE_LOCAL_CARD" desc="Tooltip for the credit card icon that appears in the address bar. Icon is shown when a user has a credit card stored locally in Chrome but not in Google Pay.">
         Save payment method
       </message>
diff --git a/chrome/app/global_media_controls_strings.grdp b/chrome/app/global_media_controls_strings.grdp
index 1cda455..c80ba504 100644
--- a/chrome/app/global_media_controls_strings.grdp
+++ b/chrome/app/global_media_controls_strings.grdp
@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- Global Media Controls-specific strings (included from generated_resources.grd). -->
 <grit-part>
+  <message name="IDS_GLOBAL_MEDIA_CONTROLS_BACK_TO_TAB" desc="A11y text for the Global Media Controls container describing how clicking it takes you back to the tab.">
+   Back to tab
+  </message>
   <message name="IDS_GLOBAL_MEDIA_CONTROLS_ICON_TOOLTIP_TEXT" desc="Tooltip for the Global Media Controls icon, which appears in the toolbar. The tooltip appears on mouseover of the icon.">
    Control your music, videos, and more
   </message>
diff --git a/chrome/app/global_media_controls_strings_grdp/IDS_GLOBAL_MEDIA_CONTROLS_BACK_TO_TAB.png.sha1 b/chrome/app/global_media_controls_strings_grdp/IDS_GLOBAL_MEDIA_CONTROLS_BACK_TO_TAB.png.sha1
new file mode 100644
index 0000000..2854a1a5
--- /dev/null
+++ b/chrome/app/global_media_controls_strings_grdp/IDS_GLOBAL_MEDIA_CONTROLS_BACK_TO_TAB.png.sha1
@@ -0,0 +1 @@
+c19525fb78d22d1e8351eac5871b1ff939f32c36
\ No newline at end of file
diff --git a/chrome/app/printing_strings.grdp b/chrome/app/printing_strings.grdp
index 498470ec..baef905d 100644
--- a/chrome/app/printing_strings.grdp
+++ b/chrome/app/printing_strings.grdp
@@ -148,10 +148,10 @@
       Headers and footers
     </message>
     <message name="IDS_PRINT_PREVIEW_OPTION_FIT_TO_PAGE" desc="Dropdown option shown in print preview page to auto fit the PDF page to the paper's printable area.">
-      Fit to page
+      Fit to printable area
     </message>
     <message name="IDS_PRINT_PREVIEW_OPTION_FIT_TO_PAPER" desc="Dropdown option shown in print preview page to auto fit the PDF page to the paper size.">
-      Fit to paper size
+      Fit to paper
     </message>
     <message name="IDS_PRINT_PREVIEW_OPTION_BACKGROUND_COLORS_AND_IMAGES" desc="Checkbox label shown in print preview page to print webpage backgrounds.">
       Background graphics
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 3f9d5c2..b3267b56 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -3674,7 +3674,7 @@
     Block third-party cookies
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIE_SUBLABEL" desc="A sub-label below the Block 3rd-party cookie checkbox.">
-    Prevent third-party websites from saving and reading cookie data
+    When turned on, sites can't use cookies that track you across the web. Some websites might break.
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_ADOBE_FLASH_SETTINGS" desc="The text for the link that points the user to the Adobe Flash Player Storage settings on the web">
     Adobe Flash Player Storage settings
diff --git a/chrome/app/theme/default_100_percent/common/safety_tip_lookalike_illustration.png b/chrome/app/theme/default_100_percent/common/safety_tip_lookalike_illustration.png
new file mode 100644
index 0000000..4615ebc
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/common/safety_tip_lookalike_illustration.png
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/safety_tip_suspicious_illustration.png b/chrome/app/theme/default_100_percent/common/safety_tip_suspicious_illustration.png
new file mode 100644
index 0000000..90b7085
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/common/safety_tip_suspicious_illustration.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/safety_tip_lookalike_illustration.png b/chrome/app/theme/default_200_percent/common/safety_tip_lookalike_illustration.png
new file mode 100644
index 0000000..0fb9eeb
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/common/safety_tip_lookalike_illustration.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/safety_tip_suspicious_illustration.png b/chrome/app/theme/default_200_percent/common/safety_tip_suspicious_illustration.png
new file mode 100644
index 0000000..ebd41fa
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/common/safety_tip_suspicious_illustration.png
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 70716c2..590ed41 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -251,6 +251,10 @@
         <structure type="chrome_scaled_image" name="IDR_RESET_WARNING" file="cros/reset_warning.png" />
       </if>
       <structure type="chrome_scaled_image" name="IDR_RESTORE_BUTTON_MASK" file="common/restore_button_mask.png" />
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_SAFETY_TIP_LOOKALIKE_ILLUSTRATION" file="common/safety_tip_lookalike_illustration.png" />
+        <structure type="chrome_scaled_image" name="IDR_SAFETY_TIP_SUSPICIOUS_ILLUSTRATION" file="common/safety_tip_suspicious_illustration.png" />
+      </if>
       <structure type="chrome_scaled_image" name="IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP" file="screen_capture_notification_grip.png" />
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_SECONDARY_USER_SETTINGS" file="cros/secondary_user_settings.png" />
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index 3a6f479..4f92529 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -105,6 +105,7 @@
     "sync.icon",
     "sync_circle.icon",
     "sync_error_circle.icon",
+    "sync_paused.icon",
     "sync_paused_circle.icon",
     "sync_problem.icon",
     "sync_switch_account.icon",
diff --git a/chrome/app/vector_icons/sync_paused.icon b/chrome/app/vector_icons/sync_paused.icon
new file mode 100644
index 0000000..0ac73156
--- /dev/null
+++ b/chrome/app/vector_icons/sync_paused.icon
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 6.67f, 4.23f,
+LINE_TO, 6.67f, 2.84f,
+CUBIC_TO, 6.13f, 2.98f, 5.63f, 3.2f, 5.18f, 3.48f,
+LINE_TO, 6.15f, 4.45f,
+CUBIC_TO, 6.32f, 4.37f, 6.49f, 4.29f, 6.67f, 4.23f,
+CLOSE,
+MOVE_TO, 1.91f, 3.61f,
+LINE_TO, 3.48f, 5.18f,
+CUBIC_TO, 2.97f, 5.99f, 2.67f, 6.96f, 2.67f, 8,
+CUBIC_TO, 2.67f, 9.47f, 3.27f, 10.8f, 4.24f, 11.76f,
+LINE_TO, 2.67f, 13.33f,
+LINE_TO, 6.67f, 13.33f,
+LINE_TO, 6.67f, 9.33f,
+LINE_TO, 5.17f, 10.83f,
+CUBIC_TO, 4.45f, 10.1f, 4, 9.11f, 4, 8,
+CUBIC_TO, 4, 7.33f, 4.17f, 6.71f, 4.45f, 6.15f,
+LINE_TO, 9.84f, 11.54f,
+CUBIC_TO, 9.67f, 11.63f, 9.51f, 11.71f, 9.33f, 11.77f,
+LINE_TO, 9.33f, 13.16f,
+CUBIC_TO, 9.86f, 13.02f, 10.36f, 12.8f, 10.81f, 12.52f,
+LINE_TO, 12.39f, 14.09f,
+LINE_TO, 13.23f, 13.25f,
+LINE_TO, 2.76f, 2.76f,
+LINE_TO, 1.91f, 3.61f,
+CLOSE,
+MOVE_TO, 13.33f, 2.67f,
+LINE_TO, 9.33f, 2.67f,
+LINE_TO, 9.33f, 6.67f,
+LINE_TO, 10.83f, 5.17f,
+CUBIC_TO, 11.55f, 5.9f, 12, 6.89f, 12, 8,
+CUBIC_TO, 12, 8.67f, 11.83f, 9.29f, 11.55f, 9.85f,
+LINE_TO, 12.52f, 10.82f,
+CUBIC_TO, 13.03f, 10.01f, 13.33f, 9.04f, 13.33f, 8,
+CUBIC_TO, 13.33f, 6.53f, 12.73f, 5.2f, 11.76f, 4.24f,
+LINE_TO, 13.33f, 2.67f,
+CLOSE
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b889871..5f4cb5b1 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -9,7 +9,6 @@
 import("//build/config/jumbo.gni")
 import("//build/config/linux/gtk/gtk.gni")
 import("//build/config/ui.gni")
-import("//build/split_static_library.gni")
 import("//chrome/common/features.gni")
 import("//components/feature_engagement/features.gni")
 import("//components/feed/features.gni")
@@ -73,17 +72,7 @@
 
 # Use a static library here because many test binaries depend on this but don't
 # require many files from it. This makes linking more efficient.
-jumbo_split_static_library("browser") {
-  # Split into multiple static libraries on Windows builds. We have hit size
-  # limits on Windows official builds and on goma builds when symbol_level = 2
-  # is selected. Always splitting on Windows builds is simpler than trying to
-  # perfectly calculate the scenarios where it is required.
-  if (is_win) {
-    split_count = 5
-  } else {
-    split_count = 1
-  }
-
+jumbo_static_library("browser") {
   sources = [
     "about_flags.cc",
     "about_flags.h",
@@ -991,10 +980,6 @@
     "page_load_metrics/observers/ad_metrics/frame_data.h",
     "page_load_metrics/observers/amp_page_load_metrics_observer.cc",
     "page_load_metrics/observers/amp_page_load_metrics_observer.h",
-    "page_load_metrics/observers/click_input_tracker.cc",
-    "page_load_metrics/observers/click_input_tracker.h",
-    "page_load_metrics/observers/core_page_load_metrics_observer.cc",
-    "page_load_metrics/observers/core_page_load_metrics_observer.h",
     "page_load_metrics/observers/data_reduction_proxy_metrics_observer.cc",
     "page_load_metrics/observers/data_reduction_proxy_metrics_observer.h",
     "page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc",
@@ -1017,8 +1002,6 @@
     "page_load_metrics/observers/https_engagement_metrics/https_engagement_service.h",
     "page_load_metrics/observers/https_engagement_metrics/https_engagement_service_factory.cc",
     "page_load_metrics/observers/https_engagement_metrics/https_engagement_service_factory.h",
-    "page_load_metrics/observers/largest_contentful_paint_handler.cc",
-    "page_load_metrics/observers/largest_contentful_paint_handler.h",
     "page_load_metrics/observers/live_tab_count_page_load_metrics_observer.cc",
     "page_load_metrics/observers/live_tab_count_page_load_metrics_observer.h",
     "page_load_metrics/observers/loading_predictor_page_load_metrics_observer.cc",
@@ -1087,6 +1070,8 @@
     "performance_manager/chrome_browser_main_extra_parts_performance_manager.h",
     "performance_manager/chrome_content_browser_client_performance_manager_part.cc",
     "performance_manager/chrome_content_browser_client_performance_manager_part.h",
+    "performance_manager/decorators/frame_priority_decorator.cc",
+    "performance_manager/decorators/frame_priority_decorator.h",
     "performance_manager/decorators/frozen_frame_aggregator.cc",
     "performance_manager/decorators/frozen_frame_aggregator.h",
     "performance_manager/decorators/page_aggregator.cc",
@@ -2911,13 +2896,12 @@
     }
 
     if (dfmify_dev_ui) {
+      # TODO(huangs): Extracting this to a separate target.
       sources += [
-        "android/dev_ui/dev_ui_url_handler.cc",
-        "android/dev_ui/dev_ui_url_handler.h",
-        "ui/webui/android/dev_ui_loader/dev_ui_loader_message_handler.cc",
-        "ui/webui/android/dev_ui_loader/dev_ui_loader_message_handler.h",
-        "ui/webui/android/dev_ui_loader/dev_ui_loader_ui.cc",
-        "ui/webui/android/dev_ui_loader/dev_ui_loader_ui.h",
+        "dev_ui/android/dev_ui_loader_error_page.cc",
+        "dev_ui/android/dev_ui_loader_error_page.h",
+        "dev_ui/android/dev_ui_loader_throttle.cc",
+        "dev_ui/android/dev_ui_loader_throttle.h",
       ]
       deps += [ "//chrome/android/modules/dev_ui/provider:native" ]
     }
@@ -2978,8 +2962,6 @@
       "chrome_browser_field_trials_desktop.h",
       "chrome_process_singleton.cc",
       "chrome_process_singleton.h",
-      "component_updater/intervention_policy_database_component_installer.cc",
-      "component_updater/intervention_policy_database_component_installer.h",
       "component_updater/tls_deprecation_config_component_installer.cc",
       "component_updater/tls_deprecation_config_component_installer.h",
       "custom_handlers/register_protocol_handler_permission_request.cc",
@@ -3263,8 +3245,6 @@
       "resource_coordinator/decision_details.h",
       "resource_coordinator/discard_metrics_lifecycle_unit_observer.cc",
       "resource_coordinator/discard_metrics_lifecycle_unit_observer.h",
-      "resource_coordinator/intervention_policy_database.cc",
-      "resource_coordinator/intervention_policy_database.h",
       "resource_coordinator/leveldb_site_characteristics_database.cc",
       "resource_coordinator/leveldb_site_characteristics_database.h",
       "resource_coordinator/lifecycle_unit.cc",
@@ -3544,7 +3524,6 @@
       "//chrome/app/vector_icons",
       "//chrome/browser/policy:path_parser",
       "//chrome/browser/profile_resetter:profile_reset_report_proto",
-      "//chrome/browser/resource_coordinator:intervention_policy_database_proto",
       "//chrome/browser/resource_coordinator:tab_metrics_event_proto",
       "//chrome/browser/resource_coordinator/tab_ranker",
       "//chrome/browser/resources:component_extension_resources",
@@ -4050,6 +4029,8 @@
       "notifications/screen_lock_notification_blocker.cc",
       "notifications/screen_lock_notification_blocker.h",
       "platform_util.cc",
+      "policy/chrome_browser_cloud_management_register_watcher.cc",
+      "policy/chrome_browser_cloud_management_register_watcher.h",
       "policy/cloud/chrome_browser_cloud_management_helper.cc",
       "policy/cloud/chrome_browser_cloud_management_helper.h",
       "policy/cloud/user_policy_signin_service.cc",
@@ -4057,8 +4038,6 @@
       "policy/cloud/user_policy_signin_service_internal.h",
       "policy/machine_level_user_cloud_policy_controller.cc",
       "policy/machine_level_user_cloud_policy_controller.h",
-      "policy/machine_level_user_cloud_policy_register_watcher.cc",
-      "policy/machine_level_user_cloud_policy_register_watcher.h",
       "profiles/avatar_menu.cc",
       "profiles/avatar_menu.h",
       "profiles/avatar_menu_actions_desktop.cc",
@@ -4695,6 +4674,8 @@
       "offline_pages/offliner_helper.h",
       "offline_pages/offliner_user_data.cc",
       "offline_pages/offliner_user_data.h",
+      "offline_pages/prefetch/gcm_token.cc",
+      "offline_pages/prefetch/gcm_token.h",
       "offline_pages/prefetch/offline_metrics_collector_impl.cc",
       "offline_pages/prefetch/offline_metrics_collector_impl.h",
       "offline_pages/prefetch/offline_prefetch_download_client.cc",
@@ -4702,8 +4683,6 @@
       "offline_pages/prefetch/prefetch_background_task_handler_impl.cc",
       "offline_pages/prefetch/prefetch_background_task_handler_impl.h",
       "offline_pages/prefetch/prefetch_background_task_scheduler.h",
-      "offline_pages/prefetch/prefetch_instance_id_proxy.cc",
-      "offline_pages/prefetch/prefetch_instance_id_proxy.h",
       "offline_pages/prefetch/prefetch_service_factory.cc",
       "offline_pages/prefetch/prefetch_service_factory.h",
       "offline_pages/prefetch/prefetched_pages_notifier.cc",
@@ -5353,6 +5332,7 @@
   if (is_chromeos) {
     deps += [
       "//chrome/browser/resources/chromeos/crostini_installer:polymer3_elements",
+      "//chrome/browser/resources/chromeos/set_time_dialog:polymer3_elements",
       "//chrome/browser/ui/webui/chromeos/add_supervision:mojo_bindings_js",
       "//chrome/browser/ui/webui/chromeos/crostini_installer:mojo_bindings_js",
       "//chrome/browser/ui/webui/chromeos/machine_learning:mojo_bindings_js",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 04a70d7..c1ac012 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -416,11 +416,9 @@
   "+third_party/blink/public/platform/web_mouse_event.h",
   "+third_party/blink/public/platform/web_mouse_wheel_event.h",
   "+third_party/blink/public/platform/web_touch_event.h",
-  "+third_party/blink/public/platform/web_sudden_termination_disabler_type.h",
   "+third_party/blink/public/public_buildflags.h",
   "+third_party/blink/public/web/web_context_menu_data.h",
   "+third_party/blink/public/web/web_media_player_action.h",
-  "+third_party/blink/public/web/web_triggering_event_info.h",
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 860c4cb..b16423b 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -422,6 +422,7 @@
 
 #endif  // OS_ANDROID
 
+#if !defined(OS_CHROMEOS)
 const FeatureEntry::FeatureParam kForceDark_SimpleHsl[] = {
     {"inversion_method", "hsl_based"},
     {"image_behavior", "none"},
@@ -473,6 +474,7 @@
     {"with selective inversion of everything",
      kForceDark_SelectiveGeneralInversion,
      base::size(kForceDark_SelectiveGeneralInversion), nullptr}};
+#endif  // !OS_CHROMEOS
 
 #if defined(OS_ANDROID)
 const FeatureEntry::FeatureParam kCloseTabSuggestionsStale_4Hours[] = {
@@ -1354,6 +1356,18 @@
      kBackForwardCache_ServiceWorkerSupport, 1, nullptr},
 };
 
+#if defined(OS_CHROMEOS)
+const FeatureEntry::FeatureParam kCrOSActionRecorderLogWithHash[] = {
+    {"CrOSActionRecorderType", "1"}};
+const FeatureEntry::FeatureParam kCrOSActionRecorderLogWithoutHash[] = {
+    {"CrOSActionRecorderType", "2"}};
+const FeatureEntry::FeatureVariation kCrOSActionRecorderVariations[] = {
+    {"Log with Hash", kCrOSActionRecorderLogWithHash,
+     base::size(kCrOSActionRecorderLogWithHash), nullptr},
+    {"Log without Hash", kCrOSActionRecorderLogWithoutHash,
+     base::size(kCrOSActionRecorderLogWithoutHash), nullptr}};
+#endif  // defined(OS_CHROMEOS)
+
 // RECORDING USER METRICS FOR FLAGS:
 // -----------------------------------------------------------------------------
 // The first line of the entry is the internal name.
@@ -2433,11 +2447,16 @@
      flag_descriptions::kPasswordEditingAndroidDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(password_manager::features::kPasswordEditingAndroid)},
 #endif  // OS_ANDROID
+#if !defined(OS_CHROMEOS)
+    // TODO(https://crbug.com/1011696): Investigate crash reports and re-enable
+    // for ChromeOS.
     {"enable-force-dark", flag_descriptions::kForceWebContentsDarkModeName,
-     flag_descriptions::kForceWebContentsDarkModeDescription, kOsAll,
+     flag_descriptions::kForceWebContentsDarkModeDescription,
+     kOsWin | kOsLinux | kOsMac | kOsAndroid,
      FEATURE_WITH_PARAMS_VALUE_TYPE(blink::features::kForceWebContentsDarkMode,
                                     kForceDarkVariations,
                                     "ForceDarkVariations")},
+#endif  // !OS_CHROMEOS
 #if defined(OS_ANDROID)
 #if BUILDFLAG(ENABLE_ANDROID_NIGHT_MODE)
     {"enable-android-night-mode", flag_descriptions::kAndroidNightModeName,
@@ -3497,12 +3516,6 @@
 #endif
 
 #if !defined(OS_ANDROID)
-    {"session-restore-prioritizes-background-use-cases",
-     flag_descriptions::kSessionRestorePrioritizesBackgroundUseCasesName,
-     flag_descriptions::kSessionRestorePrioritizesBackgroundUseCasesDescription,
-     kOsDesktop,
-     FEATURE_VALUE_TYPE(
-         features::kSessionRestorePrioritizesBackgroundUseCases)},
     {"proactive-tab-freeze", flag_descriptions::kTabFreezeName,
      flag_descriptions::kTabFreezeDescription, kOsDesktop,
      FEATURE_WITH_PARAMS_VALUE_TYPE(
@@ -3575,6 +3588,11 @@
      FEATURE_VALUE_TYPE(app_list_features::kEnableAppReinstallZeroState)},
 #endif  // OS_CHROMEOS
 
+    {"enable-sync-device-info-in-transport-mode",
+     flag_descriptions::kSyncDeviceInfoInTransportModeName,
+     flag_descriptions::kSyncDeviceInfoInTransportModeDescription, kOsAll,
+     FEATURE_VALUE_TYPE(switches::kSyncDeviceInfoInTransportMode)},
+
     {"enable-sync-uss-bookmarks",
      flag_descriptions::kEnableSyncUSSBookmarksName,
      flag_descriptions::kEnableSyncUSSBookmarksDescription, kOsAll,
@@ -4468,9 +4486,9 @@
      FEATURE_VALUE_TYPE(chrome::android::kTabSwitcherLongpressMenu)},
 #endif  // defined(OS_ANDROID)
 
-    {"bundled-exchanges", flag_descriptions::kBundledHTTPExchangesName,
-     flag_descriptions::kBundledHTTPExchangesDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kBundledHTTPExchanges)},
+    {"web-bundles", flag_descriptions::kWebBundlesName,
+     flag_descriptions::kWebBundlesDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kWebBundles)},
 
 #if defined(OS_ANDROID)
     {"darken-websites-checkbox-in-themes-setting",
@@ -4643,6 +4661,16 @@
      flag_descriptions::kEnableDeJellyDescription, kOsAll,
      SINGLE_VALUE_TYPE(switches::kEnableDeJelly)},
 
+#if defined(OS_CHROMEOS)
+    {"enable-cros-action-recorder",
+     flag_descriptions::kEnableCrOSActionRecorderName,
+     flag_descriptions::kEnableCrOSActionRecorderDescription, kOsCrOS,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(
+         app_list_features::kEnableCrOSActionRecorder,
+         kCrOSActionRecorderVariations,
+         "CrOSActionRecorderTypeVariations")},
+#endif  // defined(OS_CHROMEOS)
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/dev_ui/dev_ui_url_handler.h b/chrome/browser/android/dev_ui/dev_ui_url_handler.h
deleted file mode 100644
index 6554bbe..0000000
--- a/chrome/browser/android/dev_ui/dev_ui_url_handler.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ANDROID_DEV_UI_DEV_UI_URL_HANDLER_H_
-#define CHROME_BROWSER_ANDROID_DEV_UI_DEV_UI_URL_HANDLER_H_
-
-#include "build/build_config.h"
-#include "chrome/android/features/dev_ui/buildflags.h"
-
-#if !defined(OS_ANDROID) || !BUILDFLAG(DFMIFY_DEV_UI)
-#error Unsupported platform.
-#endif
-
-class GURL;
-
-namespace content {
-class BrowserContext;
-}
-
-namespace chrome {
-namespace android {
-
-// Handles chrome:// hosts for pages in the Developer UI Dynamic Feature Module
-// (DevUI DFM). If not installed or not loaded, then replaces |url| with the
-// chrome:// URL to the DevUI loader to install and load the DevUI DFM.
-bool HandleDfmifiedDevUiPageURL(GURL* url,
-                                content::BrowserContext* /* browser_context */);
-
-}  // namespace android
-}  // namespace chrome
-
-#endif  // CHROME_BROWSER_ANDROID_DEV_UI_DEV_UI_URL_HANDLER_H_
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc b/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc
index dea4eb3..6afebfc 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc
@@ -18,6 +18,7 @@
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "device/vr/test/fake_vr_device.h"
 #include "device/vr/test/fake_vr_service_client.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -136,14 +137,15 @@
 
     frame_provider.Bind(std::move(session_->data_provider));
     frame_provider->GetEnvironmentIntegrationProvider(
-        mojo::MakeRequest(&environment_provider));
+        environment_provider.BindNewEndpointAndPassReceiver());
     std::move(quit_closure).Run();
   }
 
   StubMailboxToSurfaceBridge* bridge;
   StubArCoreSessionUtils* session_utils;
   mojo::Remote<mojom::XRFrameDataProvider> frame_provider;
-  mojom::XREnvironmentIntegrationProviderAssociatedPtr environment_provider;
+  mojo::AssociatedRemote<mojom::XREnvironmentIntegrationProvider>
+      environment_provider;
   std::unique_ptr<base::RunLoop> run_loop;
   base::OnceClosure quit_closure;
 
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
index 1768b2f..91cba32 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
@@ -100,8 +100,7 @@
 ArCoreGl::ArCoreGl(std::unique_ptr<ArImageTransport> ar_image_transport)
     : gl_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       ar_image_transport_(std::move(ar_image_transport)),
-      webxr_(std::make_unique<vr::WebXrPresentationState>()),
-      environment_binding_(this) {
+      webxr_(std::make_unique<vr::WebXrPresentationState>()) {
   DVLOG(1) << __func__;
 }
 
@@ -618,15 +617,16 @@
 }
 
 void ArCoreGl::GetEnvironmentIntegrationProvider(
-    device::mojom::XREnvironmentIntegrationProviderAssociatedRequest
-        environment_request) {
+    mojo::PendingAssociatedReceiver<
+        device::mojom::XREnvironmentIntegrationProvider> environment_provider) {
   DVLOG(3) << __func__;
 
   DCHECK(IsOnGlThread());
   DCHECK(is_initialized_);
 
-  environment_binding_.Bind(std::move(environment_request));
-  environment_binding_.set_connection_error_handler(base::BindOnce(
+  environment_receiver_.reset();
+  environment_receiver_.Bind(std::move(environment_provider));
+  environment_receiver_.set_disconnect_handler(base::BindOnce(
       &ArCoreGl::OnBindingDisconnect, weak_ptr_factory_.GetWeakPtr()));
 }
 
@@ -919,7 +919,7 @@
 void ArCoreGl::CloseBindingsIfOpen() {
   DVLOG(3) << __func__;
 
-  environment_binding_.Close();
+  environment_receiver_.reset();
   frame_data_receiver_.reset();
   session_controller_receiver_.reset();
   presentation_receiver_.reset();
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.h b/chrome/browser/android/vr/arcore_device/arcore_gl.h
index 78ffff9..a6893bd 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.h
@@ -18,7 +18,8 @@
 #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "device/vr/util/fps_meter.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -87,7 +88,7 @@
                     GetFrameDataCallback callback) override;
 
   void GetEnvironmentIntegrationProvider(
-      mojom::XREnvironmentIntegrationProviderAssociatedRequest
+      mojo::PendingAssociatedReceiver<mojom::XREnvironmentIntegrationProvider>
           environment_provider) override;
   void SetInputSourceButtonListener(
       mojo::PendingAssociatedRemote<device::mojom::XRInputSourceButtonListener>)
@@ -226,8 +227,8 @@
 
   mojo::Receiver<mojom::XRFrameDataProvider> frame_data_receiver_{this};
   mojo::Receiver<mojom::XRSessionController> session_controller_receiver_{this};
-  mojo::AssociatedBinding<mojom::XREnvironmentIntegrationProvider>
-      environment_binding_;
+  mojo::AssociatedReceiver<mojom::XREnvironmentIntegrationProvider>
+      environment_receiver_{this};
 
   void OnBindingDisconnect();
   void CloseBindingsIfOpen();
diff --git a/chrome/browser/android/vr/gvr_scheduler_delegate.cc b/chrome/browser/android/vr/gvr_scheduler_delegate.cc
index 9a71f50..851fdd5 100644
--- a/chrome/browser/android/vr/gvr_scheduler_delegate.cc
+++ b/chrome/browser/android/vr/gvr_scheduler_delegate.cc
@@ -1317,8 +1317,8 @@
 }
 
 void GvrSchedulerDelegate::GetEnvironmentIntegrationProvider(
-    device::mojom::XREnvironmentIntegrationProviderAssociatedRequest
-        environment_provider) {
+    mojo::PendingAssociatedReceiver<
+        device::mojom::XREnvironmentIntegrationProvider> environment_provider) {
   // Environment integration is not supported. This call should not
   // be made on this device.
   mojo::ReportBadMessage("Environment integration is not supported.");
diff --git a/chrome/browser/android/vr/gvr_scheduler_delegate.h b/chrome/browser/android/vr/gvr_scheduler_delegate.h
index 443d0d7..6bd55be 100644
--- a/chrome/browser/android/vr/gvr_scheduler_delegate.h
+++ b/chrome/browser/android/vr/gvr_scheduler_delegate.h
@@ -20,6 +20,7 @@
 #include "chrome/browser/vr/base_scheduler_delegate.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "device/vr/util/sliding_average.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -140,8 +141,9 @@
                     device::mojom::XRFrameDataProvider::GetFrameDataCallback
                         callback) override;
   void GetEnvironmentIntegrationProvider(
-      device::mojom::XREnvironmentIntegrationProviderAssociatedRequest
-          environment_provider) override;
+      mojo::PendingAssociatedReceiver<
+          device::mojom::XREnvironmentIntegrationProvider> environment_provider)
+      override;
   void SetInputSourceButtonListener(
       mojo::PendingAssociatedRemote<device::mojom::XRInputSourceButtonListener>)
       override;
diff --git a/chrome/browser/apps/app_service/crostini_apps.cc b/chrome/browser/apps/app_service/crostini_apps.cc
index 0e2635e..ba9b9b0 100644
--- a/chrome/browser/apps/app_service/crostini_apps.cc
+++ b/chrome/browser/apps/app_service/crostini_apps.cc
@@ -8,6 +8,7 @@
 
 #include "chrome/browser/apps/app_service/dip_px_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_features.h"
+#include "chrome/browser/chromeos/crostini/crostini_package_service.h"
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
@@ -159,7 +160,8 @@
 void CrostiniApps::Uninstall(const std::string& app_id,
                              bool clear_site_data,
                              bool report_abuse) {
-  NOTIMPLEMENTED();
+  crostini::CrostiniPackageService::GetForProfile(profile_)
+      ->QueueUninstallApplication(app_id);
 }
 
 void CrostiniApps::OpenNativeSettings(const std::string& app_id) {
diff --git a/chrome/browser/apps/app_service/uninstall_dialog.cc b/chrome/browser/apps/app_service/uninstall_dialog.cc
index fbe0bbe..5d235208 100644
--- a/chrome/browser/apps/app_service/uninstall_dialog.cc
+++ b/chrome/browser/apps/app_service/uninstall_dialog.cc
@@ -7,22 +7,50 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/services/app_service/public/cpp/icon_loader.h"
 
+namespace {
+
+constexpr int32_t kUninstallIconSize = 32;
+constexpr int32_t kArcUninstallIconSize = 48;
+
+}  // namespace
+
 namespace apps {
 
 UninstallDialog::UninstallDialog(Profile* profile,
                                  apps::mojom::AppType app_type,
                                  const std::string& app_id,
+                                 const std::string& app_name,
                                  apps::mojom::IconKeyPtr icon_key,
                                  apps::IconLoader* icon_loader,
                                  UninstallCallback uninstall_callback)
     : profile_(profile),
       app_type_(app_type),
       app_id_(app_id),
+      app_name_(app_name),
       uninstall_callback_(std::move(uninstall_callback)) {
+  int32_t size_hint_in_dip;
+  switch (app_type) {
+    case apps::mojom::AppType::kCrostini:
+      // Crostini uninstall dialog doesn't show the icon.
+      UiBase::Create(profile_, app_type_, app_id_, app_name, gfx::ImageSkia(),
+                     this);
+      return;
+    case apps::mojom::AppType::kArc:
+      // Currently ARC apps only support 48*48 native icon.
+      size_hint_in_dip = kArcUninstallIconSize;
+      break;
+    case apps::mojom::AppType::kExtension:
+    case apps::mojom::AppType::kWeb:
+      size_hint_in_dip = kUninstallIconSize;
+      break;
+    default:
+      NOTREACHED();
+      return;
+  }
   constexpr bool kAllowPlaceholderIcon = false;
   icon_loader->LoadIconFromIconKey(
       app_type, app_id, std::move(icon_key),
-      apps::mojom::IconCompression::kUncompressed, kSizeHintInDip,
+      apps::mojom::IconCompression::kUncompressed, size_hint_in_dip,
       kAllowPlaceholderIcon,
       base::BindOnce(&UninstallDialog::OnLoadIcon,
                      weak_ptr_factory_.GetWeakPtr()));
@@ -43,7 +71,8 @@
     return;
   }
 
-  UiBase::Create(profile_, app_type_, app_id_, icon_value->uncompressed, this);
+  UiBase::Create(profile_, app_type_, app_id_, app_name_,
+                 icon_value->uncompressed, this);
 }
 
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/uninstall_dialog.h b/chrome/browser/apps/app_service/uninstall_dialog.h
index 74f98fdbd..8c1bae8 100644
--- a/chrome/browser/apps/app_service/uninstall_dialog.h
+++ b/chrome/browser/apps/app_service/uninstall_dialog.h
@@ -55,6 +55,7 @@
     static void Create(Profile* profile,
                        apps::mojom::AppType app_type,
                        const std::string& app_id,
+                       const std::string& app_name,
                        gfx::ImageSkia image,
                        UninstallDialog* uninstall_dialog);
 
@@ -79,11 +80,10 @@
                               bool report_rebuse,
                               UninstallDialog* uninstall_dialog)>;
 
-  static constexpr int32_t kSizeHintInDip = 32;
-
   UninstallDialog(Profile* profile,
                   apps::mojom::AppType app_type,
                   const std::string& app_id,
+                  const std::string& app_name,
                   apps::mojom::IconKeyPtr icon_key,
                   IconLoader* icon_loader,
                   UninstallCallback uninstall_callback);
@@ -100,6 +100,7 @@
   Profile* profile_;
   apps::mojom::AppType app_type_;
   const std::string app_id_;
+  const std::string app_name_;
   UninstallCallback uninstall_callback_;
 
   base::WeakPtrFactory<UninstallDialog> weak_ptr_factory_{this};
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 43961e3..0c1c6ca 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -171,6 +171,7 @@
       <include name="IDR_TRANSLATE_INTERNALS_JS" file="../../components/translate/translate_internals/translate_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" />
       <include name="IDR_FEEDBACK_MANIFEST" file="resources\feedback\manifest.json" type="BINDATA" />
       <if expr="is_android">
+        <include name="IDR_DEV_UI_LOADER_ERROR_HTML" file="resources/dev_ui/dev_ui_loader_error.html" compress="gzip" type="BINDATA" />
         <include name="IDR_EXPLORE_SITES_INTERNALS_HTML" file="resources\explore_sites_internals\explore_sites_internals.html" allowexternalscript="true" compress="gzip" type="BINDATA" />
         <include name="IDR_EXPLORE_SITES_INTERNALS_CSS" file="resources\explore_sites_internals\explore_sites_internals.css" compress="gzip" type="BINDATA" />
         <include name="IDR_EXPLORE_SITES_INTERNALS_JS" file="resources\explore_sites_internals\explore_sites_internals.js" compress="gzip" type="BINDATA" />
@@ -187,7 +188,6 @@
         <include name="IDR_SNIPPETS_INTERNALS_CSS" file="resources\snippets_internals\snippets_internals.css" compress="gzip" type="BINDATA" />
         <include name="IDR_SNIPPETS_INTERNALS_JS" file="resources\snippets_internals\snippets_internals.js" compress="gzip" type="BINDATA" />
         <include name="IDR_SNIPPETS_INTERNALS_MOJOM_LITE_JS" file="${root_gen_dir}\chrome\browser\ui\webui\snippets_internals\snippets_internals.mojom-lite.js" use_base_dir="false" type="BINDATA" compress="gzip" />
-        <part file="resources/dev_ui_loader/dev_ui_loader.grdp" />
       </if>
 
       <if expr="enable_supervised_users">
@@ -514,9 +514,8 @@
         <include name="IDR_DEVICE_EMULATOR_SHARED_STYLES_HTML" file="resources\chromeos\emulator\shared_styles.html" type="BINDATA" />
       </if>
       <if expr="chromeos">
-        <include name="IDR_SET_TIME_DIALOG_HTML" file="resources\chromeos\set_time_dialog\set_time_dialog.html" type="BINDATA" compress="gzip" />
-        <include name="IDR_SET_TIME_DIALOG_JS" file="resources\chromeos\set_time_dialog\set_time_dialog.js" type="BINDATA" compress="gzip" />
-        <include name="IDR_SET_TIME_BROWSER_PROXY_HTML" file="resources\chromeos\set_time_dialog\set_time_browser_proxy.html" type="BINDATA" compress="gzip" />
+        <include name="IDR_SET_TIME_HTML" file="resources\chromeos\set_time_dialog\set_time.html" type="BINDATA" compress="gzip" />
+        <include name="IDR_SET_TIME_DIALOG_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\set_time_dialog\set_time_dialog.js" use_base_dir="false" type="BINDATA" compress="gzip" />
         <include name="IDR_SET_TIME_BROWSER_PROXY_JS" file="resources\chromeos\set_time_dialog\set_time_browser_proxy.js" type="BINDATA" compress="gzip" />
       </if>
       <if expr="chromeos">
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index c61e364..33d7fec3 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -328,7 +328,6 @@
 #endif
 
 #if !defined(OS_ANDROID)
-#include "chrome/browser/component_updater/intervention_policy_database_component_installer.h"
 #include "chrome/browser/component_updater/tls_deprecation_config_component_installer.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
 #endif
@@ -535,8 +534,6 @@
 #endif  // defined(OS_WIN)
 
 #if !defined(OS_ANDROID)
-  RegisterInterventionPolicyDatabaseComponent(
-      cus, g_browser_process->GetTabManager()->intervention_policy_database());
   RegisterTLSDeprecationConfigComponent(cus, path);
 #endif
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index d32f48c..8171dbb 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -415,7 +415,7 @@
 #include "ui/base/resource/resource_bundle_android.h"
 #include "ui/base/ui_base_paths.h"
 #if BUILDFLAG(DFMIFY_DEV_UI)
-#include "chrome/browser/android/dev_ui/dev_ui_url_handler.h"
+#include "chrome/browser/dev_ui/android/dev_ui_loader_throttle.h"
 #endif  // BUILDFLAG(DFMIFY_DEV_UI)
 #elif defined(OS_POSIX)
 #include "chrome/browser/chrome_browser_main_posix.h"
@@ -1120,7 +1120,7 @@
   registry->RegisterIntegerPref(prefs::kDiskCacheSize, 0);
   registry->RegisterStringPref(prefs::kIsolateOrigins, std::string());
   registry->RegisterBooleanPref(prefs::kSitePerProcess, false);
-  registry->RegisterBooleanPref(prefs::kTabLifecyclesEnabled, true);
+  registry->RegisterBooleanPref(prefs::kTabFreezingEnabled, true);
   registry->RegisterBooleanPref(prefs::kWebDriverOverridesIncompatiblePolicies,
                                 false);
 }
@@ -3349,12 +3349,6 @@
   // Handler to rewrite chrome://newtab on Android.
   handler->AddHandlerPair(&chrome::android::HandleAndroidNativePageURL,
                           BrowserURLHandler::null_handler());
-#if BUILDFLAG(DFMIFY_DEV_UI)
-  // Handler to rewrite chrome:// URLs in the DevUI DFM, if not installed.
-  handler->AddHandlerPair(&chrome::android::HandleDfmifiedDevUiPageURL,
-                          BrowserURLHandler::null_handler());
-#endif  // BUILDFLAG(DFMIFY_DEV_UI)
-
 #else   // defined(OS_ANDROID)
   // Handler to rewrite chrome://newtab for InstantExtended.
   handler->AddHandlerPair(&search::HandleNewTabURLRewrite,
@@ -3832,6 +3826,14 @@
             handle, navigation_interception::SynchronyMode::kAsync));
   }
   throttles.push_back(InterceptOMADownloadNavigationThrottle::Create(handle));
+
+#if BUILDFLAG(DFMIFY_DEV_UI)
+  // If the DevUI DFM is already installed, then this is a no-op, except for the
+  // side effect of ensuring that the DevUI DFM is loaded.
+  MaybeAddThrottle(&throttles,
+                   dev_ui::DevUiLoaderThrottle::MaybeCreateThrottleFor(handle));
+#endif  // BUILDFLAG(DFMIFY_DEV_UI)
+
 #elif BUILDFLAG(ENABLE_EXTENSIONS)
   if (handle->IsInMainFrame()) {
     // Redirect some navigations to apps that have registered matching URL
@@ -5405,3 +5407,14 @@
 #endif
   return false;
 }
+
+bool ChromeContentBrowserClient::ArePersistentMediaDeviceIDsAllowed(
+    content::BrowserContext* browser_context,
+    const GURL& url,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& top_frame_origin) {
+  // Persistent MediaDevice IDs are allowed if cookies are allowed.
+  return CookieSettingsFactory::GetForProfile(
+             Profile::FromBrowserContext(browser_context))
+      ->IsCookieAccessAllowed(url, site_for_cookies, top_frame_origin);
+}
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index aa67c8e..b80edc76 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -618,6 +618,12 @@
 
   bool ShouldLoadExtraIcuDataFile() override;
 
+  bool ArePersistentMediaDeviceIDsAllowed(
+      content::BrowserContext* browser_context,
+      const GURL& scope,
+      const GURL& site_for_cookies,
+      const base::Optional<url::Origin>& top_frame_origin) override;
+
   content::PreviewsState DetermineAllowedPreviewsWithoutHoldback(
       content::PreviewsState initial_state,
       content::NavigationHandle* navigation_handle,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 3086f63..3bba2e6 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2847,6 +2847,7 @@
     "smb_client/discovery/network_scanner_unittest.cc",
     "smb_client/smb_errors_unittest.cc",
     "smb_client/smb_file_system_id_test.cc",
+    "smb_client/smb_file_system_unittest.cc",
     "smb_client/smb_service_helper_unittest.cc",
     "smb_client/smb_service_unittest.cc",
     "smb_client/smb_share_finder_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
index 5de0b21..1bc2c41 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
@@ -299,13 +299,6 @@
 
   GetProperty(AXStringProperty::PANE_TITLE, &pane_title);
 
-  // If it exists, set tooltip value as description on node.
-  std::string tooltip;
-  if (GetProperty(AXStringProperty::TOOLTIP, &tooltip)) {
-    out_data->AddStringAttribute(ax::mojom::StringAttribute::kDescription,
-                                 tooltip);
-  }
-
   if (!text.empty() || !content_description.empty() || !label.empty() ||
       !pane_title.empty()) {
     // Append non empty properties to name attribute.
@@ -361,6 +354,11 @@
                                  place_holder);
   }
 
+  // If it exists, set tooltip value as on node.
+  std::string tooltip;
+  if (GetProperty(AXStringProperty::TOOLTIP, &tooltip))
+    out_data->AddStringAttribute(ax::mojom::StringAttribute::kTooltip, tooltip);
+
   // Int properties.
   int traversal_before = -1, traversal_after = -1;
   if (GetProperty(AXIntProperty::TRAVERSAL_BEFORE, &traversal_before)) {
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
index 6c6bb0e..bda31ec 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
@@ -526,6 +526,13 @@
       data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
   ASSERT_EQ("root label text", name);
 
+  // Set tooltip on child2.
+  SetProperty(child2, AXStringProperty::TOOLTIP, "tooltip text");
+  CallSerializeNode(child2, &data);
+  ASSERT_TRUE(
+      data->GetStringAttribute(ax::mojom::StringAttribute::kTooltip, &name));
+  ASSERT_EQ("tooltip text", name);
+
   // Clearing both clickable and name from root, the name should not be
   // populated.
   root->boolean_properties->clear();
diff --git a/chrome/browser/chromeos/attestation/platform_verification_dialog.cc b/chrome/browser/chromeos/attestation/platform_verification_dialog.cc
index c9ee348..ff0d50e 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_dialog.cc
+++ b/chrome/browser/chromeos/attestation/platform_verification_dialog.cc
@@ -80,6 +80,10 @@
       domain_(domain),
       callback_(std::move(callback)),
       learn_more_button_(nullptr) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK, l10n_util::GetStringUTF16(IDS_PERMISSION_ALLOW));
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_CANCEL, l10n_util::GetStringUTF16(IDS_PERMISSION_DENY));
   SetLayoutManager(std::make_unique<views::FillLayout>());
   SetBorder(views::CreateEmptyBorder(
       views::LayoutProvider::Get()->GetDialogInsetsForContentType(
@@ -125,19 +129,6 @@
   return true;
 }
 
-base::string16 PlatformVerificationDialog::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  switch (button) {
-    case ui::DIALOG_BUTTON_OK:
-      return l10n_util::GetStringUTF16(IDS_PERMISSION_ALLOW);
-    case ui::DIALOG_BUTTON_CANCEL:
-      return l10n_util::GetStringUTF16(IDS_PERMISSION_DENY);
-    default:
-      NOTREACHED();
-  }
-  return base::string16();
-}
-
 ui::ModalType PlatformVerificationDialog::GetModalType() const {
   return ui::MODAL_TYPE_CHILD;
 }
diff --git a/chrome/browser/chromeos/attestation/platform_verification_dialog.h b/chrome/browser/chromeos/attestation/platform_verification_dialog.h
index d1d00c7e..e24562b 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_dialog.h
+++ b/chrome/browser/chromeos/attestation/platform_verification_dialog.h
@@ -55,7 +55,6 @@
   bool Cancel() override;
   bool Accept() override;
   bool Close() override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
 
   // views::WidgetDelegate:
   ui::ModalType GetModalType() const override;
diff --git a/chrome/browser/chromeos/attestation/tpm_challenge_key.cc b/chrome/browser/chromeos/attestation/tpm_challenge_key.cc
index c2c8725..36fd92bb 100644
--- a/chrome/browser/chromeos/attestation/tpm_challenge_key.cc
+++ b/chrome/browser/chromeos/attestation/tpm_challenge_key.cc
@@ -227,7 +227,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   PrefService* prefs = profile_->GetPrefs();
-  if (prefs && prefs->IsManagedPreference(prefs::kAttestationEnabled)) {
+  // TODO(crbug.com/1000589): Check it's mandatory after fixing corp policy.
+  if (prefs) {
     return prefs->GetBoolean(prefs::kAttestationEnabled);
   }
   return false;
diff --git a/chrome/browser/chromeos/child_accounts/screen_time_controller.cc b/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
index a29540a32..b8fcbe4 100644
--- a/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
+++ b/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/media_client_impl.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
@@ -81,8 +80,7 @@
                        ->CreateDeepCopy()),
       time_limit_notifier_(context) {
   session_manager::SessionManager::Get()->AddObserver(this);
-  if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier))
-    UsageTimeStateNotifier::GetInstance()->AddObserver(this);
+  UsageTimeStateNotifier::GetInstance()->AddObserver(this);
 
   system::TimezoneSettings::GetInstance()->AddObserver(this);
   chromeos::SystemClockClient::Get()->AddObserver(this);
@@ -101,8 +99,7 @@
     parent_access::ParentAccessService::Get().RemoveObserver(this);
 
   session_manager::SessionManager::Get()->RemoveObserver(this);
-  if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier))
-    UsageTimeStateNotifier::GetInstance()->RemoveObserver(this);
+  UsageTimeStateNotifier::GetInstance()->RemoveObserver(this);
 
   system::TimezoneSettings::GetInstance()->RemoveObserver(this);
   SystemClockClient::Get()->RemoveObserver(this);
@@ -191,8 +188,7 @@
           notification_type.value(), remaining_time);
     }
 
-    if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier))
-      ScheduleUsageTimeLimitWarning(state);
+    ScheduleUsageTimeLimitWarning(state);
   }
 
   // Trigger policy update notifications.
@@ -283,16 +279,14 @@
   ScreenLocker::default_screen_locker()->DisableAuthForUser(
       account_id,
       ash::AuthDisabledData(ConvertLockReason(active_policy), next_unlock_time,
-                            GetScreenTimeDuration()));
+                            GetScreenTimeDuration(),
+                            true /*disable_lock_screen_media*/));
 
   // Add parent access code button.
   // TODO(agawronska): Once feature flag is removed, showing shelf button could
   // be moved to ash.
   if (base::FeatureList::IsEnabled(features::kParentAccessCode))
     ash::LoginScreen::Get()->ShowParentAccessButton(true);
-
-  // Prevent media from continuing to play after device is locked.
-  MediaClientImpl::Get()->SuspendMediaSessions();
 }
 
 void ScreenTimeController::OnScreenLockByPolicyEnd() {
@@ -507,25 +501,11 @@
 void ScreenTimeController::OnSessionStateChanged() {
   session_manager::SessionState session_state =
       session_manager::SessionManager::Get()->session_state();
-  if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier)) {
-    base::Optional<usage_time_limit::State> last_state = GetLastStateFromPref();
-    if (session_state == session_manager::SessionState::LOCKED && last_state &&
-        last_state->is_locked) {
-      OnScreenLockByPolicy(last_state->active_policy,
-                           last_state->next_unlock_time);
-    }
-    return;
-  }
-
-  if (session_state == session_manager::SessionState::LOCKED) {
-    base::Optional<usage_time_limit::State> last_state = GetLastStateFromPref();
-    if (last_state && last_state->is_locked) {
-      OnScreenLockByPolicy(last_state->active_policy,
-                           last_state->next_unlock_time);
-    }
-    ResetInSessionTimers();
-  } else if (session_state == session_manager::SessionState::ACTIVE) {
-    CheckTimeLimit("OnSessionStateChanged");
+  base::Optional<usage_time_limit::State> last_state = GetLastStateFromPref();
+  if (session_state == session_manager::SessionState::LOCKED && last_state &&
+      last_state->is_locked) {
+    OnScreenLockByPolicy(last_state->active_policy,
+                         last_state->next_unlock_time);
   }
 }
 
diff --git a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
index 5e73bcd..02e4fa2 100644
--- a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
+++ b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
@@ -7,7 +7,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -23,7 +22,6 @@
 #include "chrome/browser/chromeos/policy/user_policy_test_helper.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/policy_constants.h"
@@ -60,10 +58,7 @@
 
 namespace utils = time_limit_test_utils;
 
-// Allows testing ScreenTimeController with UsageTimeStateNotifier enabled
-// (instantiated with |true|) or disabled (instantiated with |false|).
-class ScreenTimeControllerTest : public policy::LoginPolicyTestBase,
-                                 public testing::WithParamInterface<bool> {
+class ScreenTimeControllerTest : public policy::LoginPolicyTestBase {
  public:
   ScreenTimeControllerTest() = default;
 
@@ -71,14 +66,6 @@
 
   // policy::LoginPolicyTestBase:
   void SetUp() override {
-    is_feature_enabled_ = GetParam();
-    base::test::ScopedFeatureList feature_list;
-    if (is_feature_enabled_) {
-      feature_list.InitAndEnableFeature(features::kUsageTimeStateNotifier);
-    } else {
-      feature_list.InitAndDisableFeature(features::kUsageTimeStateNotifier);
-    }
-
     // Recognize example.com (used by LoginPolicyTestBase) as non-enterprise
     // account.
     policy::BrowserPolicyConnector::SetNonEnterpriseDomainForTesting(
@@ -146,14 +133,13 @@
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
 
   Profile* child_profile_ = nullptr;
-  bool is_feature_enabled_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ScreenTimeControllerTest);
 };
 
 // Tests a simple lock override.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, LockOverride) {
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, LockOverride) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -182,7 +168,7 @@
 }
 
 // Tests an unlock override on a bedtime.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, UnlockBedtime) {
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, UnlockBedtime) {
   LogInChildAndSetupClockWithTime("5 Jan 2018 22:00:00 BRT");
   ScreenLockerTester().Lock();
 
@@ -234,7 +220,7 @@
 }
 
 // Tests an override with duration on a bedtime before it's locked.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, OverrideBedtimeWithDuration) {
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, OverrideBedtimeWithDuration) {
   LogInChildAndSetupClockWithTime("5 Jan 2018 20:45:00 PST");
   ScreenLockerTester().Lock();
 
@@ -303,7 +289,7 @@
 }
 
 // Tests an override with duration on a daily limit before it's locked.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest,
                        OverrideDailyLimitWithDuration) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 BRT");
   ScreenLockerTester().Lock();
@@ -368,7 +354,7 @@
 }
 
 // Tests an unlock override with duration on a bedtime.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, UnlockBedtimeWithDuration) {
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, UnlockBedtimeWithDuration) {
   LogInChildAndSetupClockWithTime("5 Jan 2018 22:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -434,7 +420,7 @@
 }
 
 // Tests an unlock override with duration on a daily limit.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, UnlockDailyLimitWithDuration) {
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, UnlockDailyLimitWithDuration) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
   ScreenLockerTester().Lock();
 
@@ -498,7 +484,7 @@
 }
 
 // Tests the default time window limit.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DefaultBedtime) {
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, DefaultBedtime) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -561,7 +547,7 @@
 }
 
 // Tests the default time window limit.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DefaultDailyLimit) {
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, DefaultDailyLimit) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -627,7 +613,7 @@
 }
 
 // Tests that the bedtime locks an active session when it is reached.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, ActiveSessionBedtime) {
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, ActiveSessionBedtime) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
 
   system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
@@ -665,7 +651,7 @@
 }
 
 // Tests that the daily limit locks the device when it is reached.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, ActiveSessionDailyLimit) {
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, ActiveSessionDailyLimit) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
 
   system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
@@ -701,7 +687,7 @@
 }
 
 // Tests bedtime during timezone changes.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, BedtimeOnTimezoneChange) {
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, BedtimeOnTimezoneChange) {
   LogInChildAndSetupClockWithTime("3 Jan 2018 10:00:00 GMT-0600");
   ScreenLockerTester().Lock();
 
@@ -752,7 +738,7 @@
 }
 
 // Tests bedtime during timezone changes that make the clock go back in time.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest,
                        BedtimeOnEastToWestTimezoneChanges) {
   LogInChildAndSetupClockWithTime("3 Jan 2018 8:00:00 GMT+1300");
   ScreenLockerTester().Lock();
@@ -797,9 +783,7 @@
 }
 
 // Tests if call the observers for usage time limit warning.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, CallObservers) {
-  if (!is_feature_enabled_)
-    return;
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, CallObservers) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
 
   system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
@@ -879,6 +863,4 @@
       ->RemoveObserver(&observer);
 }
 
-INSTANTIATE_TEST_SUITE_P(, ScreenTimeControllerTest, testing::Bool());
-
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index d291d55..70c78bcd 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -1307,6 +1307,82 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(key)));
 }
 
+namespace {
+vm_tools::cicerone::UpgradeContainerRequest::Version ConvertVersion(
+    ContainerVersion from) {
+  switch (from) {
+    case ContainerVersion::STRETCH:
+      return vm_tools::cicerone::UpgradeContainerRequest::DEBIAN_STRETCH;
+    case ContainerVersion::BUSTER:
+      return vm_tools::cicerone::UpgradeContainerRequest::DEBIAN_BUSTER;
+    case ContainerVersion::UNKNOWN:
+    default:
+      return vm_tools::cicerone::UpgradeContainerRequest::UNKNOWN;
+  }
+}
+
+}  // namespace
+
+void CrostiniManager::UpgradeContainer(const ContainerId& key,
+                                       ContainerVersion source_version,
+                                       ContainerVersion target_version,
+                                       CrostiniResultCallback callback) {
+  const auto& vm_name = key.vm_name;
+  const auto& container_name = key.container_name;
+  if (vm_name.empty()) {
+    LOG(ERROR) << "vm_name is required";
+    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    return;
+  }
+  if (container_name.empty()) {
+    LOG(ERROR) << "container_name is required";
+    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    return;
+  }
+  if (!GetCiceroneClient()->IsUpgradeContainerProgressSignalConnected()) {
+    // Technically we could still start the upgrade, but we wouldn't be able to
+    // detect when the upgrade completes, successfully or otherwise.
+    LOG(ERROR)
+        << "Attempted to upgrade container when progress signal not connected.";
+    std::move(callback).Run(CrostiniResult::UPGRADE_CONTAINER_FAILED);
+    return;
+  }
+  vm_tools::cicerone::UpgradeContainerRequest request;
+  request.set_owner_id(owner_id_);
+  request.set_vm_name(vm_name);
+  request.set_container_name(container_name);
+  request.set_source_version(ConvertVersion(source_version));
+  request.set_target_version(ConvertVersion(target_version));
+  GetCiceroneClient()->UpgradeContainer(
+      std::move(request),
+      base::BindOnce(&CrostiniManager::OnUpgradeContainer,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void CrostiniManager::CancelUpgradeContainer(const ContainerId& key,
+                                             CrostiniResultCallback callback) {
+  const auto& vm_name = key.vm_name;
+  const auto& container_name = key.container_name;
+  if (vm_name.empty()) {
+    LOG(ERROR) << "vm_name is required";
+    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    return;
+  }
+  if (container_name.empty()) {
+    LOG(ERROR) << "container_name is required";
+    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    return;
+  }
+  vm_tools::cicerone::CancelUpgradeContainerRequest request;
+  request.set_owner_id(owner_id_);
+  request.set_vm_name(vm_name);
+  request.set_container_name(container_name);
+  GetCiceroneClient()->CancelUpgradeContainer(
+      std::move(request),
+      base::BindOnce(&CrostiniManager::OnCancelUpgradeContainer,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
 void CrostiniManager::LaunchContainerApplication(
     std::string vm_name,
     std::string container_name,
@@ -1750,6 +1826,16 @@
   import_container_progress_observers_.RemoveObserver(observer);
 }
 
+void CrostiniManager::AddUpgradeContainerProgressObserver(
+    UpgradeContainerProgressObserver* observer) {
+  upgrade_container_progress_observers_.AddObserver(observer);
+}
+
+void CrostiniManager::RemoveUpgradeContainerProgressObserver(
+    UpgradeContainerProgressObserver* observer) {
+  upgrade_container_progress_observers_.RemoveObserver(observer);
+}
+
 void CrostiniManager::AddVmShutdownObserver(VmShutdownObserver* observer) {
   vm_shutdown_observers_.AddObserver(observer);
 }
@@ -2146,6 +2232,41 @@
       ->OnApplyAnsiblePlaybookProgress(signal.status());
 }
 
+void CrostiniManager::OnUpgradeContainerProgress(
+    const vm_tools::cicerone::UpgradeContainerProgressSignal& signal) {
+  if (signal.owner_id() != owner_id_)
+    return;
+
+  UpgradeContainerProgressStatus status;
+  switch (signal.status()) {
+    case vm_tools::cicerone::UpgradeContainerProgressSignal::SUCCEEDED:
+      status = UpgradeContainerProgressStatus::SUCCEEDED;
+      break;
+    case vm_tools::cicerone::UpgradeContainerProgressSignal::UNKNOWN:
+    case vm_tools::cicerone::UpgradeContainerProgressSignal::FAILED:
+      status = UpgradeContainerProgressStatus::FAILED;
+      LOG(ERROR) << "Upgrade failed: " << signal.failure_reason();
+      break;
+    case vm_tools::cicerone::UpgradeContainerProgressSignal::IN_PROGRESS:
+      status = UpgradeContainerProgressStatus::UPGRADING;
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  std::vector<std::string> progress_messages;
+  progress_messages.reserve(signal.progress_messages().size());
+  for (const auto& msg : signal.progress_messages()) {
+    progress_messages.push_back(msg);
+  }
+
+  ContainerId container_id(signal.vm_name(), signal.container_name());
+  for (auto& observer : upgrade_container_progress_observers_) {
+    observer.OnUpgradeContainerProgress(container_id, status,
+                                        progress_messages);
+  }
+}
+
 void CrostiniManager::OnUninstallPackageOwningFile(
     CrostiniResultCallback callback,
     base::Optional<vm_tools::cicerone::UninstallPackageOwningFileResponse>
@@ -2809,6 +2930,66 @@
   }
 }
 
+void CrostiniManager::OnUpgradeContainer(
+    CrostiniResultCallback callback,
+    base::Optional<vm_tools::cicerone::UpgradeContainerResponse> response) {
+  if (!response) {
+    LOG(ERROR) << "Failed to start upgrading container. Empty response";
+    std::move(callback).Run(CrostiniResult::UPGRADE_CONTAINER_FAILED);
+    return;
+  }
+  CrostiniResult result = CrostiniResult::SUCCESS;
+  switch (response->status()) {
+    case vm_tools::cicerone::UpgradeContainerResponse::STARTED:
+      break;
+    case vm_tools::cicerone::UpgradeContainerResponse::ALREADY_RUNNING:
+      result = CrostiniResult::UPGRADE_CONTAINER_ALREADY_RUNNING;
+      LOG(ERROR) << "Upgrade already running. Nothing to do.";
+      break;
+    case vm_tools::cicerone::UpgradeContainerResponse::ALREADY_UPGRADED:
+      LOG(ERROR) << "Container already upgraded. Nothing to do.";
+      result = CrostiniResult::UPGRADE_CONTAINER_ALREADY_UPGRADED;
+      break;
+    case vm_tools::cicerone::UpgradeContainerResponse::NOT_SUPPORTED:
+      result = CrostiniResult::UPGRADE_CONTAINER_NOT_SUPPORTED;
+      break;
+    case vm_tools::cicerone::UpgradeContainerResponse::UNKNOWN:
+    case vm_tools::cicerone::UpgradeContainerResponse::FAILED:
+    default:
+      LOG(ERROR) << "Upgrade container failed. Failure reason "
+                 << response->failure_reason();
+      result = CrostiniResult::UPGRADE_CONTAINER_FAILED;
+      break;
+  }
+  std::move(callback).Run(result);
+}
+
+void CrostiniManager::OnCancelUpgradeContainer(
+    CrostiniResultCallback callback,
+    base::Optional<vm_tools::cicerone::CancelUpgradeContainerResponse>
+        response) {
+  if (!response) {
+    LOG(ERROR) << "Failed to cancel upgrading container. Empty response";
+    std::move(callback).Run(CrostiniResult::CANCEL_UPGRADE_CONTAINER_FAILED);
+    return;
+  }
+  CrostiniResult result = CrostiniResult::SUCCESS;
+  switch (response->status()) {
+    case vm_tools::cicerone::CancelUpgradeContainerResponse::CANCELLED:
+    case vm_tools::cicerone::CancelUpgradeContainerResponse::NOT_RUNNING:
+      break;
+
+    case vm_tools::cicerone::CancelUpgradeContainerResponse::UNKNOWN:
+    case vm_tools::cicerone::CancelUpgradeContainerResponse::FAILED:
+    default:
+      LOG(ERROR) << "Cancel upgrade container failed. Failure reason "
+                 << response->failure_reason();
+      result = CrostiniResult::CANCEL_UPGRADE_CONTAINER_FAILED;
+      break;
+  }
+  std::move(callback).Run(result);
+}
+
 void CrostiniManager::OnPendingAppListUpdates(
     const vm_tools::cicerone::PendingAppListUpdatesSignal& signal) {
   ContainerId container_id(signal.vm_name(), signal.container_name());
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.h b/chrome/browser/chromeos/crostini/crostini_manager.h
index 9ec8b5119..d59be539 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.h
+++ b/chrome/browser/chromeos/crostini/crostini_manager.h
@@ -90,6 +90,14 @@
       uint64_t minimum_required_space) = 0;
 };
 
+class UpgradeContainerProgressObserver {
+ public:
+  virtual void OnUpgradeContainerProgress(
+      const ContainerId& container_id,
+      UpgradeContainerProgressStatus status,
+      const std::vector<std::string>& messages) = 0;
+};
+
 class InstallerViewStatusObserver : public base::CheckedObserver {
  public:
   // Called when the CrostiniInstallerView is opened or closed.
@@ -275,6 +283,19 @@
   // CiceroneClient::CancelImportLxdContainer.
   void CancelImportLxdContainer(ContainerId key);
 
+  // Checks the arguments for upgrading an existing container via
+  // CiceroneClient::UpgradeContainer. An UpgradeProgressObserver should be used
+  // to monitor further results.
+  void UpgradeContainer(const ContainerId& key,
+                        ContainerVersion source_version,
+                        ContainerVersion target_version,
+                        CrostiniResultCallback callback);
+
+  // Checks the arguments for canceling the upgrade of an existing container via
+  // CiceroneClient::CancelUpgradeContainer.
+  void CancelUpgradeContainer(const ContainerId& key,
+                              CrostiniResultCallback callback);
+
   // Asynchronously launches an app as specified by its desktop file id.
   void LaunchContainerApplication(std::string vm_name,
                                   std::string container_name,
@@ -420,6 +441,12 @@
   void RemoveImportContainerProgressObserver(
       ImportContainerProgressObserver* observer);
 
+  // Add/remove observers for container upgrade
+  void AddUpgradeContainerProgressObserver(
+      UpgradeContainerProgressObserver* observer);
+  void RemoveUpgradeContainerProgressObserver(
+      UpgradeContainerProgressObserver* observer);
+
   // Add/remove vm shutdown observers.
   void AddVmShutdownObserver(VmShutdownObserver* observer);
   void RemoveVmShutdownObserver(VmShutdownObserver* observer);
@@ -464,6 +491,9 @@
   void OnApplyAnsiblePlaybookProgress(
       const vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal& signal)
       override;
+  void OnUpgradeContainerProgress(
+      const vm_tools::cicerone::UpgradeContainerProgressSignal& signal)
+      override;
 
   // chromeos::PowerManagerClient::Observer overrides:
   void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
@@ -641,6 +671,17 @@
       base::Optional<vm_tools::cicerone::CancelImportLxdContainerResponse>
           response);
 
+  // Callback for CiceroneClient::UpgradeContainer.
+  void OnUpgradeContainer(
+      CrostiniResultCallback callback,
+      base::Optional<vm_tools::cicerone::UpgradeContainerResponse> response);
+
+  // Callback for CiceroneClient::CancelUpgradeContainer.
+  void OnCancelUpgradeContainer(
+      CrostiniResultCallback callback,
+      base::Optional<vm_tools::cicerone::CancelUpgradeContainerResponse>
+          response);
+
   // Callback for CrostiniManager::LaunchContainerApplication.
   void OnLaunchContainerApplication(
       BoolCallback callback,
@@ -771,6 +812,9 @@
   base::ObserverList<ImportContainerProgressObserver>::Unchecked
       import_container_progress_observers_;
 
+  base::ObserverList<UpgradeContainerProgressObserver>::Unchecked
+      upgrade_container_progress_observers_;
+
   base::ObserverList<VmShutdownObserver> vm_shutdown_observers_;
 
   // Only one restarter flow is actually running for a given container, other
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
index 4287954e..a00e9fe 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
@@ -1587,4 +1587,92 @@
   run_loop()->Run();
 }
 
+class CrostiniManagerUpgradeContainerTest
+    : public CrostiniManagerTest,
+      public UpgradeContainerProgressObserver {
+ public:
+  void SetUp() override {
+    CrostiniManagerTest::SetUp();
+    progress_signal_.set_owner_id(CryptohomeIdForProfile(profile()));
+    progress_signal_.set_vm_name(kVmName);
+    progress_signal_.set_container_name(kContainerName);
+    progress_run_loop_ = std::make_unique<base::RunLoop>();
+    crostini_manager()->AddUpgradeContainerProgressObserver(this);
+  }
+
+  void TearDown() override {
+    crostini_manager()->RemoveUpgradeContainerProgressObserver(this);
+    progress_run_loop_.reset();
+    CrostiniManagerTest::TearDown();
+  }
+
+  void RunUntilUpgradeDone(UpgradeContainerProgressStatus final_status) {
+    final_status_ = final_status;
+    progress_run_loop_->Run();
+  }
+
+  void SendProgressSignal() {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &chromeos::FakeCiceroneClient::NotifyUpgradeContainerProgress,
+            base::Unretained(fake_cicerone_client_), progress_signal_));
+  }
+
+ protected:
+  // UpgradeContainerProgressObserver
+  void OnUpgradeContainerProgress(
+      const ContainerId& container_id,
+      UpgradeContainerProgressStatus status,
+      const std::vector<std::string>& messages) override {
+    if (status == final_status_) {
+      progress_run_loop_->Quit();
+    }
+  }
+
+  ContainerId container_id_ = ContainerId(kVmName, kContainerName);
+
+  UpgradeContainerProgressStatus final_status_ =
+      UpgradeContainerProgressStatus::FAILED;
+
+  vm_tools::cicerone::UpgradeContainerProgressSignal progress_signal_;
+  // must be created on UI thread
+  std::unique_ptr<base::RunLoop> progress_run_loop_;
+};
+
+TEST_F(CrostiniManagerUpgradeContainerTest, UpgradeContainerSuccess) {
+  crostini_manager()->UpgradeContainer(
+      container_id_, ContainerVersion::STRETCH, ContainerVersion::BUSTER,
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
+                     CrostiniResult::SUCCESS));
+
+  run_loop()->Run();
+
+  progress_signal_.set_status(
+      vm_tools::cicerone::UpgradeContainerProgressSignal::SUCCEEDED);
+
+  SendProgressSignal();
+  RunUntilUpgradeDone(UpgradeContainerProgressStatus::SUCCEEDED);
+}
+
+TEST_F(CrostiniManagerUpgradeContainerTest, CancelUpgradeContainerSuccess) {
+  crostini_manager()->UpgradeContainer(
+      container_id_, ContainerVersion::STRETCH, ContainerVersion::BUSTER,
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
+                     CrostiniResult::SUCCESS));
+
+  progress_signal_.set_status(
+      vm_tools::cicerone::UpgradeContainerProgressSignal::IN_PROGRESS);
+
+  SendProgressSignal();
+  run_loop()->Run();
+
+  base::RunLoop run_loop2;
+  crostini_manager()->CancelUpgradeContainer(
+      container_id_,
+      base::BindOnce(&ExpectCrostiniResult, run_loop2.QuitClosure(),
+                     CrostiniResult::SUCCESS));
+  run_loop2.Run();
+}
+
 }  // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_simple_types.h b/chrome/browser/chromeos/crostini/crostini_simple_types.h
index 9f38e22..946d9ca9 100644
--- a/chrome/browser/chromeos/crostini/crostini_simple_types.h
+++ b/chrome/browser/chromeos/crostini/crostini_simple_types.h
@@ -63,7 +63,13 @@
   CONTAINER_EXPORT_IMPORT_CANCELLED = 37,
   RESTART_ABORTED = 38,
   RESTART_FAILED_VM_STOPPED = 39,
-  kMaxValue = RESTART_FAILED_VM_STOPPED,
+  UPGRADE_CONTAINER_STARTED = 40,
+  UPGRADE_CONTAINER_ALREADY_RUNNING = 41,
+  UPGRADE_CONTAINER_NOT_SUPPORTED = 42,
+  UPGRADE_CONTAINER_ALREADY_UPGRADED = 43,
+  UPGRADE_CONTAINER_FAILED = 44,
+  CANCEL_UPGRADE_CONTAINER_FAILED = 45,
+  kMaxValue = CANCEL_UPGRADE_CONTAINER_FAILED,
 };
 
 enum class InstallLinuxPackageProgressStatus {
@@ -101,6 +107,18 @@
   FAILURE_SPACE,
 };
 
+enum class UpgradeContainerProgressStatus {
+  SUCCEEDED,
+  FAILED,
+  UPGRADING,
+};
+
+enum class ContainerVersion {
+  UNKNOWN,
+  STRETCH,
+  BUSTER,
+};
+
 struct VmInfo {
   VmState state;
   vm_tools::concierge::VmInfo info;
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 07bc0b07..ed37caa 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -345,7 +345,7 @@
   switch (event_type) {
     case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTNORMAL:
       return ash::WindowStateType::kNormal;
-    case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTMAXMIZE:
+    case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTMAXIMIZE:
       return ash::WindowStateType::kMaximized;
     case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTMINIMIZE:
       return ash::WindowStateType::kMinimized;
@@ -365,7 +365,7 @@
   switch (event_type) {
     case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTNORMAL:
       return ash::WMEventType::WM_EVENT_NORMAL;
-    case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTMAXMIZE:
+    case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTMAXIMIZE:
       return ash::WMEventType::WM_EVENT_MAXIMIZE;
     case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTMINIMIZE:
       return ash::WMEventType::WM_EVENT_MINIMIZE;
diff --git a/chrome/browser/chromeos/external_protocol_dialog.cc b/chrome/browser/chromeos/external_protocol_dialog.cc
index 4314461..1e2857375 100644
--- a/chrome/browser/chromeos/external_protocol_dialog.cc
+++ b/chrome/browser/chromeos/external_protocol_dialog.cc
@@ -67,11 +67,6 @@
   return ui::DIALOG_BUTTON_OK;
 }
 
-base::string16 ExternalProtocolDialog::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_OK_BUTTON_TEXT);
-}
-
 base::string16 ExternalProtocolDialog::GetWindowTitle() const {
   // If click to call feature is available, we display a message to the user on
   // how to use the feature.
@@ -110,6 +105,10 @@
                                                const GURL& url)
     : creation_time_(base::TimeTicks::Now()),
       scheme_(url.scheme()) {
+  views::DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_OK_BUTTON_TEXT));
+
   views::MessageBoxView::InitParams params((base::string16()));
   params.message_width = kMessageWidth;
   message_box_view_ = new views::MessageBoxView(params);
diff --git a/chrome/browser/chromeos/external_protocol_dialog.h b/chrome/browser/chromeos/external_protocol_dialog.h
index 32ab3f7d..3eb7fdb0 100644
--- a/chrome/browser/chromeos/external_protocol_dialog.h
+++ b/chrome/browser/chromeos/external_protocol_dialog.h
@@ -34,7 +34,6 @@
 
   // views::DialogDelegate Methods:
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   base::string16 GetWindowTitle() const override;
   void DeleteDelegate() override;
   views::View* GetContentsView() override;
diff --git a/chrome/browser/chromeos/login/help_app_launcher.cc b/chrome/browser/chromeos/login/help_app_launcher.cc
index e8dd6863..5e8d4d5 100644
--- a/chrome/browser/chromeos/login/help_app_launcher.cc
+++ b/chrome/browser/chromeos/login/help_app_launcher.cc
@@ -81,10 +81,6 @@
   LoginWebDialog* dialog = new LoginWebDialog(
       profile, NULL, parent_window_,
       l10n_util::GetStringUTF16(IDS_LOGIN_OOBE_HELP_DIALOG_TITLE), topic_url);
-  dialog->SetDialogSize(l10n_util::GetLocalizedContentsWidthInPixels(
-                            IDS_HELP_APP_DIALOG_WIDTH_PIXELS),
-                        l10n_util::GetLocalizedContentsWidthInPixels(
-                            IDS_HELP_APP_DIALOG_HEIGHT_PIXELS));
   dialog->Show();
   // The dialog object will be deleted on dialog close.
 }
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc b/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
index 4a6946ff..7ed62f8 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
@@ -223,7 +223,8 @@
       user_manager::StubAccountId(),
       ash::AuthDisabledData(ash::AuthDisabledReason::kTimeWindowLimit,
                             base::Time::Now() + base::TimeDelta::FromHours(1),
-                            base::TimeDelta::FromHours(1)));
+                            base::TimeDelta::FromHours(1),
+                            true /*disable_lock_screen_media*/));
 
   // Try to authenticate with password.
   tester.UnlockWithPassword(user_manager::StubAccountId(), kPassword);
@@ -261,7 +262,8 @@
       user_manager::StubAccountId(),
       ash::AuthDisabledData(ash::AuthDisabledReason::kTimeUsageLimit,
                             base::Time::Now() + base::TimeDelta::FromHours(1),
-                            base::TimeDelta::FromHours(3)));
+                            base::TimeDelta::FromHours(3),
+                            true /*disable_lock_screen_media*/));
 
   // Try to authenticate with fingerprint.
   AuthenticateWithFingerprint();
diff --git a/chrome/browser/chromeos/login/ui/login_web_dialog.cc b/chrome/browser/chromeos/login/ui/login_web_dialog.cc
index 86bdbd0..7edd9d2 100644
--- a/chrome/browser/chromeos/login/ui/login_web_dialog.cc
+++ b/chrome/browser/chromeos/login/ui/login_web_dialog.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
+#include "ui/aura/window.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/views/widget/widget.h"
@@ -24,13 +25,9 @@
 
 namespace {
 
-// Default width/height ratio of screen size.
-const double kDefaultWidthRatio = 0.6;
-const double kDefaultHeightRatio = 0.6;
-
-// Default width/height ratio of minimal dialog size.
-const double kMinimumWidthRatio = 0.25;
-const double kMinimumHeightRatio = 0.25;
+constexpr gfx::Insets kMinMargins{64, 64};
+constexpr gfx::Size kMinSize{128, 128};
+constexpr gfx::Size kMaxSize{512, 512};
 
 base::LazyInstance<base::circular_deque<WebContents*>>::DestructorAtExit
     g_web_contents_stack = LAZY_INSTANCE_INITIALIZER;
@@ -58,9 +55,6 @@
       delegate_(delegate),
       title_(title),
       url_(url) {
-  gfx::Rect screen_bounds(CalculateScreenBounds(gfx::Size()));
-  width_ = static_cast<int>(kDefaultWidthRatio * screen_bounds.width());
-  height_ = static_cast<int>(kDefaultHeightRatio * screen_bounds.height());
 }
 
 LoginWebDialog::~LoginWebDialog() {}
@@ -70,13 +64,6 @@
       chrome::ShowWebDialog(parent_window_, browser_context_, this);
 }
 
-void LoginWebDialog::SetDialogSize(int width, int height) {
-  DCHECK_GE(width, 0);
-  DCHECK_GE(height, 0);
-  width_ = width;
-  height_ = height;
-}
-
 void LoginWebDialog::SetDialogTitle(const base::string16& title) {
   title_ = title;
 }
@@ -100,13 +87,15 @@
     std::vector<WebUIMessageHandler*>* handlers) const {}
 
 void LoginWebDialog::GetDialogSize(gfx::Size* size) const {
-  size->SetSize(width_, height_);
+  gfx::Rect bounds = parent_window_->bounds();
+  bounds.Inset(kMinMargins);
+  *size = bounds.size();
+  size->SetToMin(kMaxSize);
+  size->SetToMax(kMinSize);
 }
 
 void LoginWebDialog::GetMinimumDialogSize(gfx::Size* size) const {
-  gfx::Rect screen_bounds(CalculateScreenBounds(gfx::Size()));
-  size->SetSize(kMinimumWidthRatio * screen_bounds.width(),
-                kMinimumHeightRatio * screen_bounds.height());
+  *size = kMinSize;
 }
 
 std::string LoginWebDialog::GetDialogArgs() const {
diff --git a/chrome/browser/chromeos/login/ui/login_web_dialog.h b/chrome/browser/chromeos/login/ui/login_web_dialog.h
index 245cb63..10bfcb7 100644
--- a/chrome/browser/chromeos/login/ui/login_web_dialog.h
+++ b/chrome/browser/chromeos/login/ui/login_web_dialog.h
@@ -45,9 +45,6 @@
 
   void Show();
 
-  // Overrides default width/height for dialog.
-  void SetDialogSize(int width, int height);
-
   // Overrides dialog title.
   void SetDialogTitle(const base::string16& title);
 
@@ -95,10 +92,6 @@
   base::string16 title_;
   const GURL url_;
 
-  // Dialog display size.
-  int width_;
-  int height_;
-
   DISALLOW_COPY_AND_ASSIGN(LoginWebDialog);
 };
 
diff --git a/chrome/browser/chromeos/login/ui/login_web_dialog_browsertest.cc b/chrome/browser/chromeos/login/ui/login_web_dialog_browsertest.cc
index 5f5ed30..2e1f650 100644
--- a/chrome/browser/chromeos/login/ui/login_web_dialog_browsertest.cc
+++ b/chrome/browser/chromeos/login/ui/login_web_dialog_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
@@ -19,7 +20,8 @@
 // Tests that LoginWebDialog is not minimizable.
 IN_PROC_BROWSER_TEST_F(LoginWebDialogTest, CannotMinimize) {
   LoginWebDialog* dialog = new LoginWebDialog(
-      browser()->profile(), nullptr, nullptr, base::string16(), GURL());
+      browser()->profile(), nullptr, browser()->window()->GetNativeWindow(),
+      base::string16(), GURL());
   dialog->Show();
   aura::Window* window = dialog->get_dialog_window_for_test();
   ASSERT_TRUE(window);
@@ -30,7 +32,8 @@
 // Tests that LoginWebDialog can be closed by 'Shift + BrowserBack' accelerator.
 IN_PROC_BROWSER_TEST_F(LoginWebDialogTest, CloseDialogByAccelerator) {
   LoginWebDialog* dialog = new LoginWebDialog(
-      browser()->profile(), nullptr, nullptr, base::string16(), GURL());
+      browser()->profile(), nullptr, browser()->window()->GetNativeWindow(),
+      base::string16(), GURL());
   dialog->Show();
   gfx::NativeWindow window = dialog->get_dialog_window_for_test();
   ASSERT_TRUE(window);
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index c9ef91f..6dab8ec 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -936,10 +936,14 @@
           policy::EnrollmentConfig::MODE_RECOVERY ||
       prescribed_enrollment_config_.mode ==
           policy::EnrollmentConfig::MODE_ENROLLED_ROLLBACK) {
+    LOG(WARNING) << "Restart Chrome to pick up the policy changes";
     chrome::AttemptRestart();
     return;
   }
 
+  // We need a log to understand when the device finished enrollment.
+  VLOG(1) << "Enrollment done";
+
   if (KioskAppManager::Get()->IsAutoLaunchEnabled())
     AutoLaunchKioskApp();
   else
diff --git a/chrome/browser/chromeos/policy/status_collector/child_status_collector.cc b/chrome/browser/chromeos/policy/status_collector/child_status_collector.cc
index 9ef0467..0f9b2f37 100644
--- a/chrome/browser/chromeos/policy/status_collector/child_status_collector.cc
+++ b/chrome/browser/chromeos/policy/status_collector/child_status_collector.cc
@@ -38,8 +38,6 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/power_manager/idle.pb.h"
 #include "chromeos/dbus/util/version_loader.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/settings/cros_settings_names.h"
@@ -53,8 +51,6 @@
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/session_manager/core/session_manager.h"
-#include "components/session_manager/session_manager_types.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_type.h"
@@ -136,10 +132,7 @@
     chromeos::system::StatisticsProvider* provider,
     const AndroidStatusFetcher& android_status_fetcher,
     TimeDelta activity_day_start)
-    : StatusCollector(provider,
-                      chromeos::CrosSettings::Get(),
-                      chromeos::PowerManagerClient::Get(),
-                      session_manager::SessionManager::Get()),
+    : StatusCollector(provider, chromeos::CrosSettings::Get()),
       pref_service_(pref_service),
       android_status_fetcher_(android_status_fetcher) {
   // protected fields of `StatusCollector`.
@@ -167,12 +160,7 @@
       chromeos::kReportDeviceBootMode, callback);
 
   // Watch for changes on the device state to calculate the child's active time.
-  if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier)) {
-    chromeos::UsageTimeStateNotifier::GetInstance()->AddObserver(this);
-  } else {
-    power_manager_->AddObserver(this);
-    session_manager_->AddObserver(this);
-  }
+  chromeos::UsageTimeStateNotifier::GetInstance()->AddObserver(this);
 
   // Fetch the current values of the policies.
   UpdateReportingSettings();
@@ -193,12 +181,7 @@
 }
 
 ChildStatusCollector::~ChildStatusCollector() {
-  if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier)) {
-    chromeos::UsageTimeStateNotifier::GetInstance()->RemoveObserver(this);
-  } else {
-    power_manager_->RemoveObserver(this);
-    session_manager_->RemoveObserver(this);
-  }
+  chromeos::UsageTimeStateNotifier::GetInstance()->RemoveObserver(this);
 }
 
 TimeDelta ChildStatusCollector::GetActiveChildScreenTime() {
@@ -231,52 +214,13 @@
                              &report_boot_mode_);
 }
 
-void ChildStatusCollector::OnSessionStateChanged() {
-  UpdateChildUsageTime();
-  last_state_active_ =
-      session_manager::SessionManager::Get()->session_state() ==
-      session_manager::SessionState::ACTIVE;
-}
-
 void ChildStatusCollector::OnUsageTimeStateChange(
     chromeos::UsageTimeStateNotifier::UsageTimeState state) {
-  DCHECK(base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier));
-
   UpdateChildUsageTime();
   last_state_active_ =
       state == chromeos::UsageTimeStateNotifier::UsageTimeState::ACTIVE;
 }
 
-void ChildStatusCollector::ScreenIdleStateChanged(
-    const power_manager::ScreenIdleState& state) {
-  DCHECK(!base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier));
-
-  UpdateChildUsageTime();
-  // It is active if screen is on and if the session is also active.
-  last_state_active_ =
-      !state.off() && session_manager_->session_state() ==
-                          session_manager::SessionState::ACTIVE;
-}
-
-void ChildStatusCollector::SuspendImminent(
-    power_manager::SuspendImminent::Reason reason) {
-  DCHECK(!base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier));
-
-  UpdateChildUsageTime();
-  // Device is going to be suspended, so it won't be active.
-  last_state_active_ = false;
-}
-
-void ChildStatusCollector::SuspendDone(const base::TimeDelta& sleep_duration) {
-  DCHECK(!base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier));
-
-  UpdateChildUsageTime();
-  // Device is returning from suspension, so it is considered active if the
-  // session is also active.
-  last_state_active_ = session_manager_->session_state() ==
-                       session_manager::SessionState::ACTIVE;
-}
-
 void ChildStatusCollector::UpdateChildUsageTime() {
   if (!report_activity_times_) {
     return;
diff --git a/chrome/browser/chromeos/policy/status_collector/child_status_collector.h b/chrome/browser/chromeos/policy/status_collector/child_status_collector.h
index 20d3d04..b28d4ebd 100644
--- a/chrome/browser/chromeos/policy/status_collector/child_status_collector.h
+++ b/chrome/browser/chromeos/policy/status_collector/child_status_collector.h
@@ -21,10 +21,7 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/chromeos/child_accounts/usage_time_state_notifier.h"
 #include "chrome/browser/chromeos/policy/status_collector/status_collector.h"
-#include "chromeos/dbus/power/power_manager_client.h"
 #include "components/policy/proto/device_management_backend.pb.h"
-#include "components/session_manager/core/session_manager_observer.h"
-#include "ui/base/idle/idle.h"
 
 namespace chromeos {
 namespace system {
@@ -36,10 +33,6 @@
 class User;
 }
 
-namespace session_manager {
-class SessionManager;
-}
-
 class PrefService;
 
 namespace policy {
@@ -52,9 +45,7 @@
 // itself (e.g. OS version). Doesn't include anything related to other users on
 // the device.
 class ChildStatusCollector : public StatusCollector,
-                             public session_manager::SessionManagerObserver,
-                             public chromeos::UsageTimeStateNotifier::Observer,
-                             public chromeos::PowerManagerClient::Observer {
+                             public chromeos::UsageTimeStateNotifier::Observer {
  public:
   // Passed into asynchronous mojo interface for communicating with Android.
   using AndroidStatusReceiver =
@@ -89,6 +80,8 @@
   bool ShouldReportHardwareStatus() const override;
 
   // How often, in seconds, to poll to see if the user is idle.
+  // Note: This in only used in tests and not referenced in .cc. It should
+  // probably be moved.
   static const unsigned int kIdlePollIntervalSeconds = 30;
 
   // Returns the amount of time the child has used so far today. If there is no
@@ -96,23 +89,10 @@
   base::TimeDelta GetActiveChildScreenTime();
 
  protected:
-  // session_manager::SessionManagerObserver:
-  void OnSessionStateChanged() override;
-
   // chromeos::UsageTimeStateNotifier::Observer:
   void OnUsageTimeStateChange(
       chromeos::UsageTimeStateNotifier::UsageTimeState state) override;
 
-  // power_manager::PowerManagerClient::Observer:
-  void ScreenIdleStateChanged(
-      const power_manager::ScreenIdleState& state) override;
-
-  // power_manager::PowerManagerClient::Observer:
-  void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
-
-  // power_manager::PowerManagerClient::Observer:
-  void SuspendDone(const base::TimeDelta& sleep_duration) override;
-
   // Updates the child's active time.
   void UpdateChildUsageTime();
 
@@ -158,9 +138,6 @@
   // Mainly used to store activity periods for reporting. Not owned.
   PrefService* const pref_service_;
 
-  // The last time an idle state check was performed.
-  base::Time last_idle_check_;
-
   // The last time an active state check was performed.
   base::Time last_active_check_;
 
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
index e29bd27d..602ebef 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
@@ -84,7 +84,6 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "components/session_manager/session_manager_types.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_type.h"
@@ -819,10 +818,7 @@
     const EMMCLifetimeFetcher& emmc_lifetime_fetcher,
     const StatefulPartitionInfoFetcher& stateful_partition_info_fetcher,
     const CrosHealthdDataFetcher& cros_healthd_data_fetcher)
-    : StatusCollector(provider,
-                      chromeos::CrosSettings::Get(),
-                      chromeos::PowerManagerClient::Get(),
-                      session_manager::SessionManager::Get()),
+    : StatusCollector(provider, chromeos::CrosSettings::Get()),
       pref_service_(pref_service),
       volume_info_fetcher_(volume_info_fetcher),
       cpu_statistics_fetcher_(cpu_statistics_fetcher),
@@ -832,6 +828,7 @@
       emmc_lifetime_fetcher_(emmc_lifetime_fetcher),
       stateful_partition_info_fetcher_(stateful_partition_info_fetcher),
       cros_healthd_data_fetcher_(cros_healthd_data_fetcher),
+      power_manager_(chromeos::PowerManagerClient::Get()),
       runtime_probe_(
           chromeos::DBusThreadManager::Get()->GetRuntimeProbeClient()) {
   // protected fields of `StatusCollector`.
@@ -913,13 +910,9 @@
   board_status_subscription_ = cros_settings_->AddSettingsObserver(
       chromeos::kReportDeviceBoardStatus, callback);
 
-  // Watch for changes on the device state to calculate the child's active time.
   power_manager_->AddObserver(this);
-  if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier)) {
-    chromeos::UsageTimeStateNotifier::GetInstance()->AddObserver(this);
-  } else {
-    session_manager_->AddObserver(this);
-  }
+  // Watch for changes on the device state to calculate the child's active time.
+  chromeos::UsageTimeStateNotifier::GetInstance()->AddObserver(this);
 
   // Fetch the current values of the policies.
   UpdateReportingSettings();
@@ -956,11 +949,7 @@
 
 DeviceStatusCollector::~DeviceStatusCollector() {
   power_manager_->RemoveObserver(this);
-  if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier)) {
-    chromeos::UsageTimeStateNotifier::GetInstance()->RemoveObserver(this);
-  } else {
-    session_manager_->RemoveObserver(this);
-  }
+  chromeos::UsageTimeStateNotifier::GetInstance()->RemoveObserver(this);
 }
 
 // static
@@ -1092,13 +1081,6 @@
   last_idle_check_ = now;
 }
 
-void DeviceStatusCollector::OnSessionStateChanged() {
-  UpdateChildUsageTime();
-  last_state_active_ =
-      session_manager::SessionManager::Get()->session_state() ==
-      session_manager::SessionState::ACTIVE;
-}
-
 void DeviceStatusCollector::OnUsageTimeStateChange(
     chromeos::UsageTimeStateNotifier::UsageTimeState state) {
   UpdateChildUsageTime();
@@ -1106,45 +1088,6 @@
       state == chromeos::UsageTimeStateNotifier::UsageTimeState::ACTIVE;
 }
 
-void DeviceStatusCollector::ScreenIdleStateChanged(
-    const power_manager::ScreenIdleState& state) {
-  // This logic are going to be done by OnUsageTimeStateChange method if
-  // UsageTimeStateNotifier feature is enabled.
-  if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier))
-    return;
-
-  UpdateChildUsageTime();
-  // It is active if screen is on and if the session is also active.
-  last_state_active_ =
-      !state.off() && session_manager_->session_state() ==
-                          session_manager::SessionState::ACTIVE;
-}
-
-void DeviceStatusCollector::SuspendImminent(
-    power_manager::SuspendImminent::Reason reason) {
-  // This logic are going to be done by OnUsageTimeStateChange method if
-  // UsageTimeStateNotifier feature is enabled.
-  if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier))
-    return;
-
-  UpdateChildUsageTime();
-  // Device is going to be suspeded, so it won't be active.
-  last_state_active_ = false;
-}
-
-void DeviceStatusCollector::SuspendDone(const base::TimeDelta& sleep_duration) {
-  // This logic are going to be done by OnUsageTimeStateChange method if
-  // UsageTimeStateNotifier feature is enabled.
-  if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier))
-    return;
-
-  UpdateChildUsageTime();
-  // Device is returning from suspension, so it is considered active if the
-  // session is also active.
-  last_state_active_ = session_manager_->session_state() ==
-                       session_manager::SessionState::ACTIVE;
-}
-
 void DeviceStatusCollector::PowerChanged(
     const power_manager::PowerSupplyProperties& prop) {
   if (!power_status_callback_.is_null())
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector.h b/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
index 2454e117..c00f2d53 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
@@ -33,8 +33,6 @@
 #include "chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_member.h"
-#include "components/session_manager/core/session_manager.h"
-#include "components/session_manager/core/session_manager_observer.h"
 #include "ui/base/idle/idle.h"
 
 namespace chromeos {
@@ -115,7 +113,6 @@
 
 // Collects and summarizes the status of an enterprise-managed ChromeOS device.
 class DeviceStatusCollector : public StatusCollector,
-                              public session_manager::SessionManagerObserver,
                               public chromeos::UsageTimeStateNotifier::Observer,
                               public chromeos::PowerManagerClient::Observer {
  public:
@@ -229,24 +226,11 @@
   // next device status update.
   void SampleResourceUsage();
 
-  // session_manager::SessionManagerObserver:
-  void OnSessionStateChanged() override;
-
   // chromeos::UsageTimeStateNotifier::Observer:
   void OnUsageTimeStateChange(
       chromeos::UsageTimeStateNotifier::UsageTimeState state) override;
 
   // power_manager::PowerManagerClient::Observer:
-  void ScreenIdleStateChanged(
-      const power_manager::ScreenIdleState& state) override;
-
-  // power_manager::PowerManagerClient::Observer:
-  void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
-
-  // power_manager::PowerManagerClient::Observer:
-  void SuspendDone(const base::TimeDelta& sleep_duration) override;
-
-  // power_manager::PowerManagerClient::Observer:
   void PowerChanged(const power_manager::PowerSupplyProperties& prop) override;
 
   // Updates the child's active time.
@@ -429,6 +413,9 @@
 
   PowerStatusCallback power_status_callback_;
 
+  // Power manager client. Used to listen to power changed events.
+  chromeos::PowerManagerClient* const power_manager_;
+
   // Runtime probe client. Used to fetch hardware data.
   chromeos::RuntimeProbeClient* const runtime_probe_;
 
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
index e0b2220..7848efa 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
@@ -78,6 +78,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_type.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/chromeos/policy/status_collector/status_collector.cc b/chrome/browser/chromeos/policy/status_collector/status_collector.cc
index 4cae5d5..ab8a5e2 100644
--- a/chrome/browser/chromeos/policy/status_collector/status_collector.cc
+++ b/chrome/browser/chromeos/policy/status_collector/status_collector.cc
@@ -98,15 +98,9 @@
   return base::nullopt;
 }
 
-StatusCollector::StatusCollector(
-    chromeos::system::StatisticsProvider* provider,
-    chromeos::CrosSettings* cros_settings,
-    chromeos::PowerManagerClient* power_manager,
-    session_manager::SessionManager* session_manager)
-    : statistics_provider_(provider),
-      cros_settings_(cros_settings),
-      power_manager_(power_manager),
-      session_manager_(session_manager) {}
+StatusCollector::StatusCollector(chromeos::system::StatisticsProvider* provider,
+                                 chromeos::CrosSettings* cros_settings)
+    : statistics_provider_(provider), cros_settings_(cros_settings) {}
 
 StatusCollector::~StatusCollector() = default;
 
diff --git a/chrome/browser/chromeos/policy/status_collector/status_collector.h b/chrome/browser/chromeos/policy/status_collector/status_collector.h
index 7ec1a05..5f62618 100644
--- a/chrome/browser/chromeos/policy/status_collector/status_collector.h
+++ b/chrome/browser/chromeos/policy/status_collector/status_collector.h
@@ -12,10 +12,7 @@
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chromeos/dbus/power/power_manager_client.h"
 #include "components/policy/proto/device_management_backend.pb.h"
-#include "components/session_manager/core/session_manager.h"
-#include "components/session_manager/core/session_manager_observer.h"
 
 class PrefRegistrySimple;
 class Profile;
@@ -75,9 +72,7 @@
       chromeos::system::StatisticsProvider* statistics_provider);
 
   StatusCollector(chromeos::system::StatisticsProvider* provider,
-                  chromeos::CrosSettings* cros_settings,
-                  chromeos::PowerManagerClient* power_manager,
-                  session_manager::SessionManager* session_manager);
+                  chromeos::CrosSettings* cros_settings);
   virtual ~StatusCollector();
 
   // Gathers status information and calls the passed response callback.
@@ -124,12 +119,6 @@
 
   chromeos::CrosSettings* const cros_settings_;
 
-  // Power manager client. Used to listen to suspend and idle events.
-  chromeos::PowerManagerClient* const power_manager_;
-
-  // Session manager. Used to listen to session state changes.
-  session_manager::SessionManager* const session_manager_;
-
   // Cached values of the reporting settings.
   bool report_version_info_ = false;
   bool report_activity_times_ = false;
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager.cc b/chrome/browser/chromeos/printing/cups_printers_manager.cc
index 844397d..d5b321b 100644
--- a/chrome/browser/chromeos/printing/cups_printers_manager.cc
+++ b/chrome/browser/chromeos/printing/cups_printers_manager.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/chromeos/printing/automatic_usb_printer_configurer.h"
 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
 #include "chrome/browser/chromeos/printing/ppd_resolution_tracker.h"
+#include "chrome/browser/chromeos/printing/print_servers_provider.h"
 #include "chrome/browser/chromeos/printing/printer_configurer.h"
 #include "chrome/browser/chromeos/printing/printer_event_tracker.h"
 #include "chrome/browser/chromeos/printing/printer_event_tracker_factory.h"
@@ -595,6 +596,7 @@
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterBooleanPref(prefs::kPrintingSendUsernameAndFilenameEnabled,
                                 false);
+  PrintServersProvider::RegisterProfilePrefs(registry);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/print_server.cc b/chrome/browser/chromeos/printing/print_server.cc
index c5bab92..e95498c 100644
--- a/chrome/browser/chromeos/printing/print_server.cc
+++ b/chrome/browser/chromeos/printing/print_server.cc
@@ -8,7 +8,9 @@
 
 namespace chromeos {
 
-PrintServer::PrintServer(const GURL& url, const std::string& name)
-    : url_(url), name_(name) {}
+PrintServer::PrintServer(const std::string& id,
+                         const GURL& url,
+                         const std::string& name)
+    : id_(id), url_(url), name_(name) {}
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/print_server.h b/chrome/browser/chromeos/printing/print_server.h
index f2301bb6..7e77f79 100644
--- a/chrome/browser/chromeos/printing/print_server.h
+++ b/chrome/browser/chromeos/printing/print_server.h
@@ -14,7 +14,10 @@
 // Simple class representing Print Server.
 class PrintServer {
  public:
-  PrintServer(const GURL& url, const std::string& name);
+  PrintServer(const std::string& id, const GURL& url, const std::string& name);
+
+  // Returns server's id.
+  const std::string& GetId() const { return id_; }
 
   // Returns server's URL, used for communication over IPP protocol.
   const GURL& GetUrl() const { return url_; }
@@ -24,10 +27,11 @@
 
   // Comparison operator.
   bool operator==(const PrintServer& obj) const {
-    return url_ == obj.url_ && name_ == obj.name_;
+    return url_ == obj.url_ && id_ == obj.id_ && name_ == obj.name_;
   }
 
  private:
+  std::string id_;
   GURL url_;
   std::string name_;
 };
diff --git a/chrome/browser/chromeos/printing/print_servers_provider.cc b/chrome/browser/chromeos/printing/print_servers_provider.cc
index 165eaab..43e94fb 100644
--- a/chrome/browser/chromeos/printing/print_servers_provider.cc
+++ b/chrome/browser/chromeos/printing/print_servers_provider.cc
@@ -18,6 +18,11 @@
 #include "base/threading/scoped_blocking_call.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/printing/print_server.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
 
@@ -66,13 +71,7 @@
   }
 
   base::Value::ListStorage& json_list = json_blob.GetList();
-  if (json_list.size() > kMaxRecords) {
-    LOG(WARNING) << "Too many records in print servers policy: "
-                 << json_list.size() << ". Only the first " << kMaxRecords
-                 << " records will be read.";
-    json_list.resize(kMaxRecords);
-  }
-
+  std::set<std::string> print_server_ids;
   std::set<GURL> print_server_urls;
   task_data.servers.reserve(json_list.size());
   for (const base::Value& val : json_list) {
@@ -81,11 +80,12 @@
                    << "Not a dictionary.";
       continue;
     }
+    const std::string* id = val.FindStringKey("id");
     const std::string* url = val.FindStringKey("url");
     const std::string* name = val.FindStringKey("display_name");
-    if (url == nullptr || name == nullptr) {
+    if (id == nullptr || url == nullptr || name == nullptr) {
       LOG(WARNING) << "Entry in print servers policy skipped. The following "
-                   << "fields are required: url, display_name.";
+                   << "fields are required: id, url, display_name.";
       continue;
     }
     GURL gurl(*url);
@@ -128,16 +128,18 @@
       replacement.SetPortStr("631");
       gurl = gurl.ReplaceComponents(replacement);
     }
-    // Checks if a set of URLs contains this URL. If not, the URL is added to
-    // the set. Otherwise, a warning is emitted and the record is skipped.
-    if (!print_server_urls.insert(gurl).second) {
-      // The set already contained this URL. It means that a duplicate record
-      // was found.
+    // Checks if server's ID and URL is not already used. If yes, a warning is
+    // emitted and the record is skipped.
+    if (print_server_ids.count(*id) || print_server_urls.count(gurl)) {
       LOG(WARNING) << "Entry in print servers policy skipped. There is "
-                   << "already a record with the same URL: " << gurl.spec();
+                   << "already a record with the same ID (" << *id << ") or "
+                   << "the same URL (" << gurl.spec() << ")";
       continue;
     }
-    task_data.servers.emplace_back(gurl, *name);
+    // Update the set of IDs and the set of URLs and add a new print server.
+    print_server_ids.insert(*id);
+    print_server_urls.insert(gurl);
+    task_data.servers.emplace_back(*id, gurl, *name);
   }
 
   return task_data;
@@ -156,10 +158,26 @@
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   }
 
+  void SetProfile(Profile* profile) override {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    if (profile_ != nullptr) {
+      // Some unit tests may create more than one profile with the same user.
+      return;
+    }
+    profile_ = profile;
+    pref_change_registrar_.Init(profile->GetPrefs());
+    // Bind UpdateWhitelist() method and call it once.
+    pref_change_registrar_.Add(
+        prefs::kExternalPrintServersWhitelist,
+        base::BindRepeating(&PrintServersProviderImpl::UpdateWhitelist,
+                            base::Unretained(this)));
+    UpdateWhitelist();
+  }
+
   void AddObserver(PrintServersProvider::Observer* observer) override {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     observers_.AddObserver(observer);
-    observer->OnServersChanged(IsCompleted(), servers_);
+    observer->OnServersChanged(IsCompleted(), result_servers_);
   }
 
   void RemoveObserver(PrintServersProvider::Observer* observer) override {
@@ -169,29 +187,30 @@
 
   void ClearData() override {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    const bool was_complete = IsCompleted();
-    const bool was_empty = servers_.empty();
+    const bool previously_completed = IsCompleted();
+    const bool previously_empty = result_servers_.empty();
     last_processed_task_ = ++last_received_task_;
     servers_.clear();
-    if (!(was_complete && was_empty)) {
+    result_servers_.clear();
+    if (!(previously_completed && previously_empty)) {
       // Notify observers.
       for (auto& observer : observers_)
-        observer.OnServersChanged(true, servers_);
+        observer.OnServersChanged(true, result_servers_);
     }
   }
 
   void SetData(std::unique_ptr<std::string> data) override {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    const bool was_complete = IsCompleted();
+    const bool previously_completed = IsCompleted();
     base::PostTaskAndReplyWithResult(
         task_runner_.get(), FROM_HERE,
         base::BindOnce(&ParseData, ++last_received_task_, std::move(data)),
         base::BindOnce(&PrintServersProviderImpl::OnComputationComplete,
                        weak_ptr_factory_.GetWeakPtr()));
-    if (was_complete) {
+    if (previously_completed) {
       // Notify observers.
       for (auto& observer : observers_)
-        observer.OnServersChanged(false, servers_);
+        observer.OnServersChanged(false, result_servers_);
     }
   }
 
@@ -205,6 +224,68 @@
     // The case when there is at least one unfinished task.
     if (last_processed_task_ != last_received_task_)
       return false;
+    // The case when a profile is not set.
+    if (profile_ == nullptr)
+      return false;
+    return true;
+  }
+
+  // Called when a new whitelist is available.
+  void UpdateWhitelist() {
+    whitelist_.clear();
+    whitelist_is_set_ = false;
+    // Fetch and parse the whitelist.
+    const PrefService::Preference* pref = profile_->GetPrefs()->FindPreference(
+        prefs::kExternalPrintServersWhitelist);
+    if (pref != nullptr && !pref->IsDefaultValue()) {
+      const base::ListValue* list =
+          profile_->GetPrefs()->GetList(prefs::kExternalPrintServersWhitelist);
+      if (list != nullptr) {
+        whitelist_is_set_ = true;
+        for (const base::Value& value : *list) {
+          if (value.is_string()) {
+            whitelist_.insert(value.GetString());
+          }
+        }
+      }
+    }
+    // Calculate resultant list and notify observers in case of changes.
+    const bool has_changes = CalculateResultantList();
+    if (has_changes) {
+      const bool is_completed = IsCompleted();
+      for (auto& observer : observers_)
+        observer.OnServersChanged(is_completed, result_servers_);
+    }
+  }
+
+  // Recalculate the value of |result_servers_| field. Returns true if the new
+  // list is different than the previous one.
+  bool CalculateResultantList() {
+    std::vector<PrintServer> new_servers;
+    if (profile_ == nullptr) {
+      // |result_servers_| remains empty when profile is not set.
+      return false;
+    }
+    if (!whitelist_is_set_) {
+      new_servers = servers_;
+    } else {
+      for (auto& print_server : servers_) {
+        if (whitelist_.count(print_server.GetId())) {
+          if (new_servers.size() == kMaxRecords) {
+            LOG(WARNING) << "The list of resultant print servers read from "
+                         << "policies is too long. Only the first "
+                         << kMaxRecords << " print servers will be taken into "
+                         << "account";
+            break;
+          }
+          new_servers.push_back(print_server);
+        }
+      }
+    }
+    if (new_servers == result_servers_) {
+      return false;
+    }
+    result_servers_ = std::move(new_servers);
     return true;
   }
 
@@ -217,15 +298,19 @@
       return;
     }
     last_processed_task_ = task_data.task_id;
+    // IsCompleted() was false before (this task was pending).
     const bool is_complete = IsCompleted();
     if (!is_complete && servers_ == task_data.servers) {
       // No changes in the object's state.
       return;
     }
     servers_ = std::move(task_data.servers);
-    // Notifies observers about changes.
-    for (auto& observer : observers_)
-      observer.OnServersChanged(is_complete, servers_);
+    const bool has_changes = CalculateResultantList();
+    // Notify observers if something changed.
+    if (is_complete || has_changes) {
+      for (auto& observer : observers_)
+        observer.OnServersChanged(is_complete, result_servers_);
+    }
   }
 
   // The sequence used for parsing JSON and computing the list of servers.
@@ -235,9 +320,16 @@
   int last_received_task_ = 0;
   // Id of the last completed task.
   int last_processed_task_ = 0;
-  // The current list of servers.
+  // The current input list of servers.
   std::vector<PrintServer> servers_;
+  // The current whitelist.
+  bool whitelist_is_set_ = false;
+  std::set<std::string> whitelist_;
+  // The current resultant list of servers.
+  std::vector<PrintServer> result_servers_;
 
+  Profile* profile_ = nullptr;
+  PrefChangeRegistrar pref_change_registrar_;
   base::ObserverList<PrintServersProvider::Observer>::Unchecked observers_;
   base::WeakPtrFactory<PrintServersProviderImpl> weak_ptr_factory_{this};
 
@@ -247,6 +339,12 @@
 }  // namespace
 
 // static
+void PrintServersProvider::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  registry->RegisterListPref(prefs::kExternalPrintServersWhitelist);
+}
+
+// static
 std::unique_ptr<PrintServersProvider> PrintServersProvider::Create() {
   return std::make_unique<PrintServersProviderImpl>();
 }
diff --git a/chrome/browser/chromeos/printing/print_servers_provider.h b/chrome/browser/chromeos/printing/print_servers_provider.h
index 7b3f0eda..4ccb6bb 100644
--- a/chrome/browser/chromeos/printing/print_servers_provider.h
+++ b/chrome/browser/chromeos/printing/print_servers_provider.h
@@ -12,6 +12,12 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/printing/print_server.h"
 
+class Profile;
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
 namespace chromeos {
 
 // This class observes values of policies related to external print servers
@@ -35,9 +41,14 @@
                                   const std::vector<PrintServer>& servers) = 0;
   };
 
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
   static std::unique_ptr<PrintServersProvider> Create();
   virtual ~PrintServersProvider() = default;
 
+  // This method set profile to fetch non-external policies. It is needed to
+  // calculate resultant list of servers.
+  virtual void SetProfile(Profile* profile) = 0;
+
   // This method also calls directly OnServersChanged(...) from |observer|.
   virtual void AddObserver(Observer* observer) = 0;
   virtual void RemoveObserver(Observer* observer) = 0;
diff --git a/chrome/browser/chromeos/printing/print_servers_provider_unittest.cc b/chrome/browser/chromeos/printing/print_servers_provider_unittest.cc
index bef107d..5d4c8a88 100644
--- a/chrome/browser/chromeos/printing/print_servers_provider_unittest.cc
+++ b/chrome/browser/chromeos/printing/print_servers_provider_unittest.cc
@@ -8,6 +8,9 @@
 #include <vector>
 
 #include "chrome/browser/chromeos/printing/print_server.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -19,12 +22,15 @@
 constexpr char kPrintServersPolicyJson1[] = R"json(
 [
   {
+    "id": "id1",
     "display_name": "MyPrintServer",
     "url": "ipp://192.168.1.5"
   }, {
+    "id": "id2",
     "display_name": "Server API",
     "url":"ipps://print-server.intra.example.com:444/ipp/cl2k4"
   }, {
+    "id": "id3",
     "display_name": "YaLP",
     "url": "http://192.168.1.8/bleble/print"
   }
@@ -32,15 +38,24 @@
 
 // Corresponding vector with PrintServers.
 const std::vector<PrintServer> kPrintServersPolicyData1 = {
-    {GURL("http://192.168.1.5:631"), "MyPrintServer"},
-    {GURL("https://print-server.intra.example.com:444/ipp/cl2k4"),
+    {"id1", GURL("http://192.168.1.5:631"), "MyPrintServer"},
+    {"id2", GURL("https://print-server.intra.example.com:444/ipp/cl2k4"),
      "Server API"},
-    {GURL("http://192.168.1.8/bleble/print"), "YaLP"}};
+    {"id3", GURL("http://192.168.1.8/bleble/print"), "YaLP"}};
+
+// An example whitelist.
+const std::vector<std::string> kPrintServersPolicyWhitelist1 = {"id3", "idX",
+                                                                "id1"};
+
+// Corresponding vector filtered with the whitelist defined above.
+const std::vector<PrintServer> kPrintServersPolicyData1Whitelist1 = {
+    kPrintServersPolicyData1[0], kPrintServersPolicyData1[2]};
 
 // A different configuration file with print servers.
 constexpr char kPrintServersPolicyJson2[] = R"json(
 [
   {
+    "id": "id1",
     "display_name": "CUPS",
     "url": "ipp://192.168.1.15"
   }
@@ -48,29 +63,42 @@
 
 // Corresponding vector with PrintServers.
 const std::vector<PrintServer> kPrintServersPolicyData2 = {
-    {GURL("http://192.168.1.15:631"), "CUPS"}};
+    {"id1", GURL("http://192.168.1.15:631"), "CUPS"}};
 
 // Another configuration file with print servers, this time with invalid URLs.
 constexpr char kPrintServersPolicyJson3[] = R"json(
 [
   {
+    "id": "1",
     "display_name": "server_1",
     "url": "ipp://aaa.bbb.ccc:666/xx"
   }, {
+    "id": "2",
     "display_name": "server_2",
     "url":"ipps:/print.server.intra.example.com:443z/ipp"
   }, {
+    "id": "3",
     "display_name": "server_3",
     "url": "file://192.168.1.8/bleble/print"
   }, {
+    "id": "4",
     "display_name": "server_4",
     "url": "http://aaa.bbb.ccc:666/xx"
   }, {
+    "id": "5",
     "display_name": "server_5",
     "url": "\n \t ipps://aaa.bbb.ccc:666/yy"
   }, {
+    "id": "6",
     "display_name": "server_6",
     "url":"ipps:/print.server^.intra.example.com/ipp"
+  }, {
+    "id": "3",
+    "display_name": "server_7",
+    "url": "file://194.169.2.18/bleble2/print"
+  }, {
+    "display_name": "server_8",
+    "url": "file://195.161.3.28/bleble/print"
   }
 ])json";
 
@@ -82,9 +110,11 @@
 // server_4 - duplicate of server_1
 // server_5 - leading whitespaces, but OK
 // server_6 - invalid URL - forbidden character
+// server_7 - duplicate id
+// server_8 - missing id
 const std::vector<PrintServer> kPrintServersPolicyData3 = {
-    {GURL("http://aaa.bbb.ccc:666/xx"), "server_1"},
-    {GURL("https://aaa.bbb.ccc:666/yy"), "server_5"}};
+    {"1", GURL("http://aaa.bbb.ccc:666/xx"), "server_1"},
+    {"5", GURL("https://aaa.bbb.ccc:666/yy"), "server_5"}};
 
 // Observer that stores all its calls.
 class TestObserver : public PrintServersProvider::Observer {
@@ -118,8 +148,11 @@
       : external_servers_(PrintServersProvider::Create()) {}
 
  protected:
-  // everything must be called on Chrome_UIThread
+  // Everything must be called on Chrome_UIThread.
   content::BrowserTaskEnvironment task_environment_;
+  // User profile.
+  TestingProfile profile_;
+  // Tested object.
   std::unique_ptr<PrintServersProvider> external_servers_;
 };
 
@@ -140,6 +173,7 @@
 // After initialization "complete" flags = false.
 TEST_F(PrintServersProviderTest, InitialConditions) {
   TestObserver obs;
+  external_servers_->SetProfile(&profile_);
   external_servers_->AddObserver(&obs);
   ASSERT_EQ(obs.GetCalls().size(), 1u);
   EXPECT_EQ(obs.GetCalls().back().complete, false);
@@ -151,6 +185,7 @@
 // ClearData() sets empty list and "complete" flag = true.
 TEST_F(PrintServersProviderTest, ClearData2) {
   TestObserver obs;
+  external_servers_->SetProfile(&profile_);
   external_servers_->AddObserver(&obs);
   external_servers_->ClearData();
   ASSERT_EQ(obs.GetCalls().size(), 2u);
@@ -168,6 +203,7 @@
 TEST_F(PrintServersProviderTest, SetData) {
   auto blob1 = std::make_unique<std::string>(kPrintServersPolicyJson1);
   TestObserver obs;
+  external_servers_->SetProfile(&profile_);
   external_servers_->AddObserver(&obs);
   external_servers_->SetData(std::move(blob1));
   // single call from AddObserver, since SetData(...) is not processed yet
@@ -186,6 +222,7 @@
   auto blob1 = std::make_unique<std::string>(kPrintServersPolicyJson1);
   auto blob2 = std::make_unique<std::string>(kPrintServersPolicyJson2);
   TestObserver obs;
+  external_servers_->SetProfile(&profile_);
   external_servers_->AddObserver(&obs);
   external_servers_->SetData(std::move(blob1));
   // single call from AddObserver, since SetData(...) is not processed yet
@@ -207,6 +244,7 @@
 TEST_F(PrintServersProviderTest, SetDataClearData) {
   auto blob1 = std::make_unique<std::string>(kPrintServersPolicyJson1);
   TestObserver obs;
+  external_servers_->SetProfile(&profile_);
   external_servers_->AddObserver(&obs);
   external_servers_->SetData(std::move(blob1));
   // single call from AddObserver, since SetData(...) is not processed yet
@@ -227,6 +265,7 @@
 TEST_F(PrintServersProviderTest, ClearDataSetData) {
   auto blob1 = std::make_unique<std::string>(kPrintServersPolicyJson1);
   TestObserver obs;
+  external_servers_->SetProfile(&profile_);
   external_servers_->ClearData();
   external_servers_->AddObserver(&obs);
   // single call from AddObserver, but with effects of ClearData()
@@ -252,6 +291,7 @@
 TEST_F(PrintServersProviderTest, InvalidURLs) {
   auto blob3 = std::make_unique<std::string>(kPrintServersPolicyJson3);
   TestObserver obs;
+  external_servers_->SetProfile(&profile_);
   external_servers_->AddObserver(&obs);
   external_servers_->SetData(std::move(blob3));
   task_environment_.RunUntilIdle();
@@ -261,5 +301,37 @@
   external_servers_->RemoveObserver(&obs);
 }
 
+// Verify that whitelist works as expected.
+TEST_F(PrintServersProviderTest, Whitelist) {
+  // The sequence from SetData test.
+  auto blob1 = std::make_unique<std::string>(kPrintServersPolicyJson1);
+  TestObserver obs;
+  external_servers_->SetProfile(&profile_);
+  external_servers_->AddObserver(&obs);
+  external_servers_->SetData(std::move(blob1));
+  // Apply an empty whitelist on the top.
+  auto* prefs = profile_.GetTestingPrefService();
+  auto value = std::make_unique<base::ListValue>();
+  prefs->SetManagedPref(prefs::kExternalPrintServersWhitelist,
+                        std::move(value));
+  // Check the resultant list - is is supposed to be empty.
+  task_environment_.RunUntilIdle();
+  ASSERT_FALSE(obs.GetCalls().empty());
+  EXPECT_TRUE(obs.GetCalls().back().complete);
+  EXPECT_TRUE(obs.GetCalls().back().servers.empty());
+  // Apply whitelist1.
+  value = std::make_unique<base::ListValue>();
+  for (const std::string& id : kPrintServersPolicyWhitelist1)
+    value->Append(base::Value(id));
+  prefs->SetManagedPref(prefs::kExternalPrintServersWhitelist,
+                        std::move(value));
+  // Check the resultant list.
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(obs.GetCalls().back().complete);
+  EXPECT_EQ(obs.GetCalls().back().servers, kPrintServersPolicyData1Whitelist1);
+  // The end.
+  external_servers_->RemoveObserver(&obs);
+}
+
 }  // namespace
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/server_printers_provider.cc b/chrome/browser/chromeos/printing/server_printers_provider.cc
index c8f51f5..3e5bc9d 100644
--- a/chrome/browser/chromeos/printing/server_printers_provider.cc
+++ b/chrome/browser/chromeos/printing/server_printers_provider.cc
@@ -41,6 +41,7 @@
       : servers_provider_(
             PrintServersProviderFactory::Get()->GetForProfile(profile)) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    servers_provider_->SetProfile(profile);
     servers_provider_->AddObserver(this);
   }
 
diff --git a/chrome/browser/chromeos/smb_client/smb_file_system.cc b/chrome/browser/chromeos/smb_client/smb_file_system.cc
index 3c01855a..f1b20ba 100644
--- a/chrome/browser/chromeos/smb_client/smb_file_system.cc
+++ b/chrome/browser/chromeos/smb_client/smb_file_system.cc
@@ -370,12 +370,21 @@
     bool recursive,
     storage::AsyncFileUtil::StatusCallback callback) {
   OperationId operation_id = task_queue_.GetNextOperationId();
+  SmbTask task;
 
-  auto reply = base::BindOnce(&SmbFileSystem::HandleGetDeleteListCallback,
-                              AsWeakPtr(), std::move(callback), operation_id);
-  SmbTask task = base::BindOnce(&SmbProviderClient::GetDeleteList,
-                                GetWeakSmbProviderClient(), GetMountId(),
-                                entry_path, std::move(reply));
+  if (recursive) {
+    auto reply = base::BindOnce(&SmbFileSystem::HandleGetDeleteListCallback,
+                                AsWeakPtr(), std::move(callback), operation_id);
+    task = base::BindOnce(&SmbProviderClient::GetDeleteList,
+                          GetWeakSmbProviderClient(), GetMountId(), entry_path,
+                          std::move(reply));
+  } else {
+    auto reply = base::BindOnce(&SmbFileSystem::HandleStatusCallback,
+                                AsWeakPtr(), std::move(callback));
+    task = base::BindOnce(&SmbProviderClient::DeleteEntry,
+                          GetWeakSmbProviderClient(), GetMountId(), entry_path,
+                          false /* recursive */, std::move(reply));
+  }
 
   EnqueueTask(std::move(task), operation_id);
   return CreateAbortCallback(operation_id);
diff --git a/chrome/browser/chromeos/smb_client/smb_file_system_unittest.cc b/chrome/browser/chromeos/smb_client/smb_file_system_unittest.cc
new file mode 100644
index 0000000..f3c9a82
--- /dev/null
+++ b/chrome/browser/chromeos/smb_client/smb_file_system_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/smb_client/smb_file_system.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_smb_provider_client.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::WithArg;
+
+namespace chromeos {
+namespace smb_client {
+namespace {
+
+const ProviderId kProviderId = ProviderId::CreateFromNativeId("smb");
+constexpr char kSharePath[] = "\\\\server\\foobar";
+constexpr int32_t kMountId = 4;
+constexpr char kDirectoryPath[] = "foo/bar";
+
+class MockSmbProviderClient : public chromeos::FakeSmbProviderClient {
+ public:
+  MockSmbProviderClient()
+      : FakeSmbProviderClient(true /* should_run_synchronously */) {}
+
+  MOCK_METHOD(void,
+              DeleteEntry,
+              (int32_t, const base::FilePath&, bool, StatusCallback),
+              (override));
+};
+
+class SmbFileSystemTest : public testing::Test {
+ protected:
+  SmbFileSystemTest()
+      : task_environment_(content::BrowserTaskEnvironment::REAL_IO_THREAD) {}
+
+  void SetUp() override {
+    ProvidedFileSystemInfo file_system_info(
+        kProviderId, {}, base::FilePath(kSharePath), false /* configurable */,
+        false /* watchable */, extensions::SOURCE_NETWORK,
+        chromeos::file_system_provider::IconSet());
+    file_system_ = std::make_unique<SmbFileSystem>(
+        file_system_info,
+        base::BindRepeating(
+            [](const ProvidedFileSystemInfo&) { return kMountId; }),
+        SmbFileSystem::UnmountCallback(),
+        SmbFileSystem::RequestCredentialsCallback(),
+        SmbFileSystem::RequestUpdatedSharePathCallback());
+
+    std::unique_ptr<MockSmbProviderClient> mock_client =
+        std::make_unique<MockSmbProviderClient>();
+    mock_client_ = mock_client.get();
+    // The mock needs to be marked as leaked because ownership is passed to
+    // DBusThreadManager.
+    testing::Mock::AllowLeak(mock_client.get());
+    chromeos::DBusThreadManager::GetSetterForTesting()->SetSmbProviderClient(
+        std::move(mock_client));
+  }
+
+  void TearDown() override {
+    // Because the mock is potentially leaked, expectations needs to be manually
+    // verified.
+    EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_client_));
+  }
+
+  content::BrowserTaskEnvironment task_environment_;
+  MockSmbProviderClient* mock_client_;  // Owned by DBusThreadManager.
+  std::unique_ptr<SmbFileSystem> file_system_;
+};
+
+TEST_F(SmbFileSystemTest, DeleteEntry_NonRecursive) {
+  base::FilePath dir(kDirectoryPath);
+
+  base::RunLoop run_loop;
+  EXPECT_CALL(*mock_client_, DeleteEntry(kMountId, dir, false, _))
+      .WillOnce(WithArg<3>(Invoke([](SmbProviderClient::StatusCallback cb) {
+        std::move(cb).Run(smbprovider::ErrorType::ERROR_OK);
+      })));
+  file_system_->DeleteEntry(
+      dir, false /* recursive */,
+      base::BindLambdaForTesting([&run_loop](base::File::Error error) {
+        EXPECT_EQ(error, base::File::FILE_OK);
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+}
+
+TEST_F(SmbFileSystemTest, DeleteEntry_NonRecursiveFailed) {
+  base::FilePath dir(kDirectoryPath);
+
+  base::RunLoop run_loop;
+  EXPECT_CALL(*mock_client_, DeleteEntry(kMountId, dir, false, _))
+      .WillOnce(WithArg<3>(Invoke([](SmbProviderClient::StatusCallback cb) {
+        std::move(cb).Run(smbprovider::ErrorType::ERROR_NOT_EMPTY);
+      })));
+  file_system_->DeleteEntry(
+      dir, false /* recursive */,
+      base::BindLambdaForTesting([&run_loop](base::File::Error error) {
+        EXPECT_EQ(error, base::File::FILE_ERROR_NOT_EMPTY);
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+}
+
+}  // namespace
+}  // namespace smb_client
+}  // namespace chromeos
diff --git a/chrome/browser/component_updater/intervention_policy_database_component_installer.cc b/chrome/browser/component_updater/intervention_policy_database_component_installer.cc
deleted file mode 100644
index 1da07b90..0000000
--- a/chrome/browser/component_updater/intervention_policy_database_component_installer.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/component_updater/intervention_policy_database_component_installer.h"
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/stl_util.h"
-#include "chrome/browser/resource_coordinator/intervention_policy_database.h"
-#include "components/component_updater/component_updater_paths.h"
-#include "components/component_updater/component_updater_service.h"
-#include "components/crx_file/id_util.h"
-
-using component_updater::ComponentUpdateService;
-
-namespace {
-
-// The SHA256 of the SubjectPublicKeyInfo used to sign the component.
-// The component id is: copjbmjbojbakpaedmpkhmiplmmehfck
-const uint8_t kInterventionPolicyDatabasePublicKeySHA256[32] = {
-    0x2e, 0xf9, 0x1c, 0x91, 0xe9, 0x10, 0xaf, 0x04, 0x3c, 0xfa, 0x7c,
-    0x8f, 0xbc, 0xc4, 0x75, 0x2a, 0x48, 0x9a, 0x64, 0x74, 0xc6, 0xda,
-    0xb7, 0xb9, 0xdf, 0x5f, 0x51, 0x3e, 0x50, 0x39, 0x04, 0xab};
-
-// The name of the component, used in the chrome://components page.
-const char kInterventionPolicyDatabaseComponentName[] =
-    "Intervention Policy Database";
-
-// The name of the database file inside of an installation of this component.
-const base::FilePath::CharType kInterventionPolicyDatabaseBinaryPbFileName[] =
-    FILE_PATH_LITERAL("intervention_policy_database.pb");
-
-}  // namespace
-
-namespace component_updater {
-
-InterventionPolicyDatabaseComponentInstallerPolicy::
-    InterventionPolicyDatabaseComponentInstallerPolicy(
-        resource_coordinator::InterventionPolicyDatabase* database)
-    : database_(database) {
-  DCHECK(database_);
-}
-
-bool InterventionPolicyDatabaseComponentInstallerPolicy::
-    SupportsGroupPolicyEnabledComponentUpdates() const {
-  return false;
-}
-
-bool InterventionPolicyDatabaseComponentInstallerPolicy::
-    RequiresNetworkEncryption() const {
-  // Public data is delivered via this component, no need for encryption.
-  return false;
-}
-
-update_client::CrxInstaller::Result
-InterventionPolicyDatabaseComponentInstallerPolicy::OnCustomInstall(
-    const base::DictionaryValue& manifest,
-    const base::FilePath& install_dir) {
-  return update_client::CrxInstaller::Result(0);
-}
-
-void InterventionPolicyDatabaseComponentInstallerPolicy::OnCustomUninstall() {}
-
-// Called during startup and installation before ComponentReady().
-bool InterventionPolicyDatabaseComponentInstallerPolicy::VerifyInstallation(
-    const base::DictionaryValue& manifest,
-    const base::FilePath& install_dir) const {
-  return base::PathExists(
-      install_dir.Append(kInterventionPolicyDatabaseBinaryPbFileName));
-}
-
-// NOTE: This is always called on the main UI thread. It is called once every
-// startup to notify of an already installed component, and may be called
-// repeatedly after that every time a new component is ready.
-void InterventionPolicyDatabaseComponentInstallerPolicy::ComponentReady(
-    const base::Version& version,
-    const base::FilePath& install_dir,
-    std::unique_ptr<base::DictionaryValue> manifest) {
-  DCHECK(database_);
-  database_->InitializeDatabaseWithProtoFile(
-      install_dir.Append(kInterventionPolicyDatabaseBinaryPbFileName), version,
-      std::move(manifest));
-}
-
-base::FilePath
-InterventionPolicyDatabaseComponentInstallerPolicy::GetRelativeInstallDir()
-    const {
-  return base::FilePath(FILE_PATH_LITERAL("InterventionPolicyDatabase"));
-}
-
-void InterventionPolicyDatabaseComponentInstallerPolicy::GetHash(
-    std::vector<uint8_t>* hash) const {
-  hash->assign(kInterventionPolicyDatabasePublicKeySHA256,
-               kInterventionPolicyDatabasePublicKeySHA256 +
-                   base::size(kInterventionPolicyDatabasePublicKeySHA256));
-}
-
-std::string InterventionPolicyDatabaseComponentInstallerPolicy::GetName()
-    const {
-  return kInterventionPolicyDatabaseComponentName;
-}
-
-update_client::InstallerAttributes
-InterventionPolicyDatabaseComponentInstallerPolicy::GetInstallerAttributes()
-    const {
-  return update_client::InstallerAttributes();
-}
-
-std::vector<std::string>
-InterventionPolicyDatabaseComponentInstallerPolicy::GetMimeTypes() const {
-  return std::vector<std::string>();
-}
-
-void RegisterInterventionPolicyDatabaseComponent(
-    ComponentUpdateService* cus,
-    resource_coordinator::InterventionPolicyDatabase* database) {
-  std::unique_ptr<ComponentInstallerPolicy> policy(
-      new InterventionPolicyDatabaseComponentInstallerPolicy(database));
-  auto installer = base::MakeRefCounted<ComponentInstaller>(std::move(policy));
-  installer->Register(cus, base::OnceClosure());
-}
-
-}  // namespace component_updater
diff --git a/chrome/browser/component_updater/intervention_policy_database_component_installer.h b/chrome/browser/component_updater/intervention_policy_database_component_installer.h
deleted file mode 100644
index 1abe36d..0000000
--- a/chrome/browser/component_updater/intervention_policy_database_component_installer.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_COMPONENT_UPDATER_INTERVENTION_POLICY_DATABASE_COMPONENT_INSTALLER_H_
-#define CHROME_BROWSER_COMPONENT_UPDATER_INTERVENTION_POLICY_DATABASE_COMPONENT_INSTALLER_H_
-
-#include "components/component_updater/component_installer.h"
-
-namespace resource_coordinator {
-class InterventionPolicyDatabase;
-}
-
-namespace component_updater {
-
-class ComponentUpdateService;
-
-// Component for receiving the intervention policy database. The database
-// consists in a proto, defined in
-// chrome/browser/resource_coordinator/intervention_policy_database.proto.
-class InterventionPolicyDatabaseComponentInstallerPolicy
-    : public ComponentInstallerPolicy {
- public:
-  InterventionPolicyDatabaseComponentInstallerPolicy(
-      resource_coordinator::InterventionPolicyDatabase* database);
-  ~InterventionPolicyDatabaseComponentInstallerPolicy() override = default;
-
- private:
-  // ComponentInstallerPolicy:
-  bool SupportsGroupPolicyEnabledComponentUpdates() const override;
-  bool RequiresNetworkEncryption() const override;
-  update_client::CrxInstaller::Result OnCustomInstall(
-      const base::DictionaryValue& manifest,
-      const base::FilePath& install_dir) override;
-  void OnCustomUninstall() override;
-  bool VerifyInstallation(const base::DictionaryValue& manifest,
-                          const base::FilePath& install_dir) const override;
-  void ComponentReady(const base::Version& version,
-                      const base::FilePath& install_dir,
-                      std::unique_ptr<base::DictionaryValue> manifest) override;
-  base::FilePath GetRelativeInstallDir() const override;
-  void GetHash(std::vector<uint8_t>* hash) const override;
-  std::string GetName() const override;
-  update_client::InstallerAttributes GetInstallerAttributes() const override;
-  std::vector<std::string> GetMimeTypes() const override;
-
-  resource_coordinator::InterventionPolicyDatabase* database_;
-
-  DISALLOW_COPY_AND_ASSIGN(InterventionPolicyDatabaseComponentInstallerPolicy);
-};
-
-// Call once to make the component update service aware of the Intervention
-// Policy Database component.
-void RegisterInterventionPolicyDatabaseComponent(
-    ComponentUpdateService* cus,
-    resource_coordinator::InterventionPolicyDatabase* database);
-
-}  // namespace component_updater
-
-#endif  // CHROME_BROWSER_COMPONENT_UPDATER_INTERVENTION_POLICY_DATABASE_COMPONENT_INSTALLER_H_
diff --git a/chrome/browser/dev_ui/OWNERS b/chrome/browser/dev_ui/OWNERS
new file mode 100644
index 0000000..1553f0ec
--- /dev/null
+++ b/chrome/browser/dev_ui/OWNERS
@@ -0,0 +1,2 @@
+huangs@chromium.org
+tiborg@chromium.org
diff --git a/chrome/browser/android/dev_ui/DEPS b/chrome/browser/dev_ui/android/DEPS
similarity index 100%
rename from chrome/browser/android/dev_ui/DEPS
rename to chrome/browser/dev_ui/android/DEPS
diff --git a/chrome/browser/dev_ui/android/dev_ui_loader_error_page.cc b/chrome/browser/dev_ui/android/dev_ui_loader_error_page.cc
new file mode 100644
index 0000000..cd1cc3d
--- /dev/null
+++ b/chrome/browser/dev_ui/android/dev_ui_loader_error_page.cc
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/dev_ui/android/dev_ui_loader_error_page.h"
+
+#include "chrome/grit/browser_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/template_expressions.h"
+#include "ui/strings/grit/ui_strings.h"
+
+namespace dev_ui {
+
+std::string BuildErrorPageHtml() {
+  ui::TemplateReplacements replacements;
+  replacements["h1"] =
+      l10n_util::GetStringUTF8(IDS_DEV_UI_LOADER_ERROR_HEADING);
+  replacements["p"] =
+      l10n_util::GetStringUTF8(IDS_ERRORPAGES_SUGGESTION_LIST_HEADER);
+  replacements["li-1"] =
+      l10n_util::GetStringUTF8(IDS_DEV_UI_LOADER_ERROR_SUGGEST_RELOAD);
+  replacements["li-2"] =
+      l10n_util::GetStringUTF8(IDS_DEV_UI_LOADER_ERROR_SUGGEST_CHECK_INTERNET);
+
+  std::string source =
+      ui::ResourceBundle::GetSharedInstance().DecompressDataResource(
+          IDR_DEV_UI_LOADER_ERROR_HTML);
+  return ui::ReplaceTemplateExpressions(source, replacements);
+}
+
+}  // namespace dev_ui
diff --git a/chrome/browser/dev_ui/android/dev_ui_loader_error_page.h b/chrome/browser/dev_ui/android/dev_ui_loader_error_page.h
new file mode 100644
index 0000000..695cddd
--- /dev/null
+++ b/chrome/browser/dev_ui/android/dev_ui_loader_error_page.h
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_DEV_UI_ANDROID_DEV_UI_LOADER_ERROR_PAGE_H_
+#define CHROME_BROWSER_DEV_UI_ANDROID_DEV_UI_LOADER_ERROR_PAGE_H_
+
+#include <string>
+
+namespace dev_ui {
+
+std::string BuildErrorPageHtml();
+
+}  // namespace dev_ui
+
+#endif  // CHROME_BROWSER_DEV_UI_ANDROID_DEV_UI_LOADER_ERROR_PAGE_H_
diff --git a/chrome/browser/android/dev_ui/dev_ui_url_handler.cc b/chrome/browser/dev_ui/android/dev_ui_loader_throttle.cc
similarity index 64%
rename from chrome/browser/android/dev_ui/dev_ui_url_handler.cc
rename to chrome/browser/dev_ui/android/dev_ui_loader_throttle.cc
index a90b2906..022b78c 100644
--- a/chrome/browser/android/dev_ui/dev_ui_url_handler.cc
+++ b/chrome/browser/dev_ui/android/dev_ui_loader_throttle.cc
@@ -2,15 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/android/dev_ui/dev_ui_url_handler.h"
+#include "chrome/browser/dev_ui/android/dev_ui_loader_throttle.h"
 
 #include <string>
+#include <utility>
 
+#include "base/logging.h"
 #include "chrome/android/modules/dev_ui/provider/dev_ui_module_provider.h"
+#include "chrome/browser/dev_ui/android/dev_ui_loader_error_page.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/safe_browsing/web_ui/constants.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
 #include "content/public/common/url_constants.h"
-#include "net/base/url_util.h"
+#include "net/base/net_errors.h"
 #include "url/gurl.h"
 
 namespace {
@@ -71,30 +76,65 @@
 
 }  // namespace
 
-namespace chrome {
-namespace android {
+namespace dev_ui {
 
-bool HandleDfmifiedDevUiPageURL(
-    GURL* url,
-    content::BrowserContext* /* browser_context */) {
-  if (!url->SchemeIs(content::kChromeUIScheme) ||
-      !IsWebUiHostInDevUiDfm(url->host())) {
-    return false;
-  }
+// static
+bool DevUiLoaderThrottle::ShouldInstallDevUiDfm(const GURL& url) {
+  return url.SchemeIs(content::kChromeUIScheme) &&
+         IsWebUiHostInDevUiDfm(url.host());
+}
+
+// static
+std::unique_ptr<content::NavigationThrottle>
+DevUiLoaderThrottle::MaybeCreateThrottleFor(content::NavigationHandle* handle) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(handle);
+  if (!handle->IsInMainFrame())
+    return nullptr;
+
+  if (!ShouldInstallDevUiDfm(handle->GetURL()))
+    return nullptr;
 
   // If module is already installed, ensure that it is loaded.
   if (dev_ui::DevUiModuleProvider::GetInstance()->ModuleInstalled()) {
     // Synchronously load module (if not already loaded).
     dev_ui::DevUiModuleProvider::GetInstance()->LoadModule();
-    return false;
+    return nullptr;
   }
-  // Create URL to the DevUI loader with "?url=<escaped original URL>" so that
-  // after install, the loader can redirect to the original URL.
-  *url = net::AppendQueryParameter(
-      GURL(std::string(kChromeUIDevUiLoaderURL) + "dev_ui_loader.html"), "url",
-      url->spec());
-  return true;
+
+  return std::make_unique<DevUiLoaderThrottle>(handle);
 }
 
-}  // namespace android
-}  // namespace chrome
+DevUiLoaderThrottle::DevUiLoaderThrottle(
+    content::NavigationHandle* navigation_handle)
+    : content::NavigationThrottle(navigation_handle) {}
+
+DevUiLoaderThrottle::~DevUiLoaderThrottle() = default;
+
+const char* DevUiLoaderThrottle::GetNameForLogging() {
+  return "DevUiLoaderThrottle";
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+DevUiLoaderThrottle::WillStartRequest() {
+  if (!dev_ui::DevUiModuleProvider::GetInstance()->ModuleInstalled()) {
+    // Can handle multiple install requests.
+    dev_ui::DevUiModuleProvider::GetInstance()->InstallModule(
+        base::BindOnce(&DevUiLoaderThrottle::OnDevUiDfmInstallWithStatus,
+                       weak_ptr_factory_.GetWeakPtr()));
+    return DEFER;
+  }
+  return PROCEED;
+}
+
+void DevUiLoaderThrottle::OnDevUiDfmInstallWithStatus(bool success) {
+  if (success) {
+    dev_ui::DevUiModuleProvider::GetInstance()->LoadModule();
+    Resume();
+  } else {
+    std::string html = BuildErrorPageHtml();
+    CancelDeferredNavigation({BLOCK_REQUEST, net::ERR_CONNECTION_FAILED, html});
+  }
+}
+
+}  // namespace dev_ui
diff --git a/chrome/browser/dev_ui/android/dev_ui_loader_throttle.h b/chrome/browser/dev_ui/android/dev_ui_loader_throttle.h
new file mode 100644
index 0000000..24c2407
--- /dev/null
+++ b/chrome/browser/dev_ui/android/dev_ui_loader_throttle.h
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_DEV_UI_ANDROID_DEV_UI_LOADER_THROTTLE_H_
+#define CHROME_BROWSER_DEV_UI_ANDROID_DEV_UI_LOADER_THROTTLE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/navigation_throttle.h"
+
+namespace content {
+class NavigationHandle;
+}  // namespace content
+
+class GURL;
+
+namespace dev_ui {
+
+// For DevUI page navigations, if the DevUI DFM is not installed then delay
+// navigation and perform installation. On success, resumes navigation. On
+// failure, displays error (retries install on refresh).
+class DevUiLoaderThrottle : public content::NavigationThrottle {
+ public:
+  // Determines whether visiting |url| should trigger DevUI DFM install.
+  static bool ShouldInstallDevUiDfm(const GURL& url);
+
+  // Creates a throttle if the DevUI DFM needs to be installed. If the DevUI DFM
+  // will be used, is installed, but is not loaded, then resource load takes
+  // place as a side effect.
+  static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
+      content::NavigationHandle* handle);
+
+  explicit DevUiLoaderThrottle(content::NavigationHandle* navigation_handle);
+  ~DevUiLoaderThrottle() override;
+  DevUiLoaderThrottle(const DevUiLoaderThrottle&) = delete;
+  const DevUiLoaderThrottle& operator=(const DevUiLoaderThrottle&) = delete;
+
+  // content::NavigationThrottle:
+  const char* GetNameForLogging() override;
+  ThrottleCheckResult WillStartRequest() override;
+
+ private:
+  // Callback for dev_ui::DevUiModuleProvider::InstallModule().
+  void OnDevUiDfmInstallWithStatus(bool success);
+
+  // Factory for creating references in callbacks.
+  base::WeakPtrFactory<DevUiLoaderThrottle> weak_ptr_factory_{this};
+};
+
+}  // namespace dev_ui
+
+#endif  // CHROME_BROWSER_DEV_UI_ANDROID_DEV_UI_LOADER_THROTTLE_H_
diff --git a/chrome/browser/dev_ui/android/dev_ui_loader_throttle_unittest.cc b/chrome/browser/dev_ui/android/dev_ui_loader_throttle_unittest.cc
new file mode 100644
index 0000000..c4c76ee
--- /dev/null
+++ b/chrome/browser/dev_ui/android/dev_ui_loader_throttle_unittest.cc
@@ -0,0 +1,308 @@
+// Copyright 2019 The Chromium 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/dev_ui/android/dev_ui_loader_throttle.h"
+
+#include <deque>
+#include <memory>
+#include <utility>
+
+#include "chrome/android/modules/dev_ui/provider/dev_ui_module_provider.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/test/mock_navigation_handle.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace dev_ui {
+
+namespace {
+
+const char* const kNonDevUiUrls[] = {
+    "https://example.com",
+    "https://example.com/path?query#frag",
+    "chrome://credits",
+    "chrome://credits/path?query#frag",
+};
+
+const char* const kDevUiUrls[] = {
+    "chrome://bluetooth-internals",
+    "chrome://bluetooth-internals/path?query#frag",
+};
+
+/******** MockDevUiModuleProvider ********/
+
+// Mock DevUiModuleProvider that overrides the main module install / load
+// functions, and provides SimulateAsyncInstall().
+class MockDevUiModuleProvider : public DevUiModuleProvider {
+ public:
+  MockDevUiModuleProvider() = default;
+  ~MockDevUiModuleProvider() override = default;
+
+  // DevUiModuleProvider:
+  bool ModuleInstalled() override { return is_installed_; }
+
+  // DevUiModuleProvider:
+  void InstallModule(base::OnceCallback<void(bool)> on_complete) override {
+    on_complete_queue_.emplace_back(std::move(on_complete));
+  }
+
+  // DevUiModuleProvider:
+  void LoadModule() override { is_loaded_ = true; }
+
+  void Reset() {
+    is_installed_ = false;
+    is_loaded_ = false;
+    on_complete_queue_.clear();
+  }
+
+  // Simulate DevUI DFN install, and dispatches all on-complete callbacks.
+  void SimulateAsyncInstall(bool install_succeeds) {
+    is_installed_ = is_installed_ || install_succeeds;
+    while (!on_complete_queue_.empty()) {
+      std::move(on_complete_queue_.front()).Run(install_succeeds);
+      on_complete_queue_.pop_front();
+    }
+  }
+
+  // Direct access of fake installation states.
+  void SetIsInstalled(bool is_installed) { is_installed_ = is_installed; }
+  bool GetIsInstalled() const { return is_installed_; }
+  void SetIsLoaded(bool is_loaded) { is_loaded_ = is_loaded; }
+  bool GetIsLoaded() const { return is_loaded_; }
+
+ private:
+  bool is_installed_ = false;
+  bool is_loaded_ = false;
+  std::deque<base::OnceCallback<void(bool)>> on_complete_queue_;
+};
+
+/******** TestDevUiLoaderThrottle ********/
+
+// DevUiLoaderThrottle that overrides and captures Resume() and
+// CancelDeferredNavigation(). This is needed because:
+// * In actual usage, both functions can lead to throttle deletion, which is
+//   problematic since the tests own the throttle instance.
+// * We want to record results for verification.
+// Note that this is instantiated directly, instead of using
+// DevUiLoaderThrottle::MaybeCreateThrottleFor().
+class TestDevUiLoaderThrottle : public DevUiLoaderThrottle {
+ public:
+  explicit TestDevUiLoaderThrottle(content::NavigationHandle* navigation_handle)
+      : DevUiLoaderThrottle(navigation_handle), cancel_result(LAST) {}
+  ~TestDevUiLoaderThrottle() override = default;
+  TestDevUiLoaderThrottle(const TestDevUiLoaderThrottle&) = delete;
+  const TestDevUiLoaderThrottle& operator=(const TestDevUiLoaderThrottle&) =
+      delete;
+
+  // content::NavigationThrottle:
+  void Resume() override { called_resume = true; }
+
+  // content::NavigationThrottle:
+  void CancelDeferredNavigation(
+      content::NavigationThrottle::ThrottleCheckResult result) override {
+    called_cancel = true;
+    cancel_result = std::move(result);
+  }
+
+  bool called_resume = false;
+  bool called_cancel = false;
+  content::NavigationThrottle::ThrottleCheckResult cancel_result;
+};
+
+/******** DevUiLoaderThrottleTest ********/
+
+class DevUiLoaderThrottleTest : public ChromeRenderViewHostTestHarness {
+ public:
+  DevUiLoaderThrottleTest() = default;
+  ~DevUiLoaderThrottleTest() override = default;
+  DevUiLoaderThrottleTest(const DevUiLoaderThrottleTest&) = delete;
+  const DevUiLoaderThrottleTest& operator=(const DevUiLoaderThrottleTest&) =
+      delete;
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+    DevUiModuleProvider::SetTestInstance(&mock_provider_);
+  }
+
+  void TearDown() override {
+    mock_provider_.Reset();
+    DevUiModuleProvider::SetTestInstance(nullptr);
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
+ protected:
+  MockDevUiModuleProvider mock_provider_;
+};
+
+}  // namespace
+
+TEST_F(DevUiLoaderThrottleTest, ShouldInstallDevUiDfm) {
+  auto ShouldInstallDevUiDfm = DevUiLoaderThrottle::ShouldInstallDevUiDfm;
+  for (const char* url_string : kNonDevUiUrls)
+    EXPECT_FALSE(ShouldInstallDevUiDfm(GURL(url_string)));
+  for (const char* url_string : kDevUiUrls)
+    EXPECT_TRUE(ShouldInstallDevUiDfm(GURL(url_string)));
+}
+
+TEST_F(DevUiLoaderThrottleTest, MaybeCreateThrottleFor) {
+  bool is_installed = false;
+  auto creates_throttle = [&](const std::string& url_string) -> bool {
+    mock_provider_.Reset();
+    mock_provider_.SetIsInstalled(is_installed);
+    content::MockNavigationHandle handle(GURL(url_string), main_rfh());
+    std::unique_ptr<content::NavigationThrottle> throttle =
+        DevUiLoaderThrottle::MaybeCreateThrottleFor(&handle);
+    return throttle != nullptr;
+  };
+
+  // Case 1: DevUI DFM is not installed.
+  is_installed = false;
+  // Case 1a: Non DevUI URLs: Throttle not created.
+  for (const char* url_string : kNonDevUiUrls) {
+    EXPECT_FALSE(creates_throttle(url_string));
+    EXPECT_FALSE(mock_provider_.GetIsLoaded());
+  }
+  // Case 1b: DevUI URLs: Throttle created, but no load takes place.
+  for (const char* url_string : kDevUiUrls) {
+    EXPECT_TRUE(creates_throttle(url_string));
+    EXPECT_FALSE(mock_provider_.GetIsLoaded());
+  }
+
+  // Case 2: DevUI DFM is installed.
+  is_installed = true;
+  // Case 2a: Non DevUI URLs: Throttle not created.
+  for (const char* url_string : kNonDevUiUrls) {
+    EXPECT_FALSE(creates_throttle(url_string));
+    EXPECT_FALSE(mock_provider_.GetIsLoaded());
+  }
+  // Case 2b: DevUI URLs: Throttle not created, but load takes place.
+  for (const char* url_string : kDevUiUrls) {
+    EXPECT_FALSE(creates_throttle(url_string));
+    EXPECT_TRUE(mock_provider_.GetIsLoaded());
+  }
+}
+
+TEST_F(DevUiLoaderThrottleTest, InstallSuccess) {
+  for (const char* url_string : kDevUiUrls) {
+    mock_provider_.Reset();
+    content::MockNavigationHandle handle(GURL(url_string), main_rfh());
+    auto throttle = std::make_unique<TestDevUiLoaderThrottle>(&handle);
+    EXPECT_FALSE(throttle->called_resume);
+    EXPECT_FALSE(throttle->called_cancel);
+    EXPECT_FALSE(mock_provider_.GetIsInstalled());
+    EXPECT_FALSE(mock_provider_.GetIsLoaded());
+
+    // Simulate request start.
+    EXPECT_EQ(content::NavigationThrottle::DEFER, throttle->WillStartRequest());
+    EXPECT_FALSE(throttle->called_resume);
+    EXPECT_FALSE(throttle->called_cancel);
+    EXPECT_FALSE(mock_provider_.GetIsInstalled());
+    EXPECT_FALSE(mock_provider_.GetIsLoaded());
+
+    // Simulate actual install (success)
+    mock_provider_.SimulateAsyncInstall(true);  // !
+    EXPECT_TRUE(throttle->called_resume);
+    EXPECT_FALSE(throttle->called_cancel);
+    EXPECT_TRUE(mock_provider_.GetIsInstalled());
+    EXPECT_TRUE(mock_provider_.GetIsLoaded());
+  }
+}
+
+TEST_F(DevUiLoaderThrottleTest, InstallQueued) {
+  mock_provider_.Reset();
+  // Page 1 request.
+  content::MockNavigationHandle handle1(GURL(kDevUiUrls[0]), main_rfh());
+  auto throttle1 = std::make_unique<TestDevUiLoaderThrottle>(&handle1);
+  // Page 2 request.
+  content::MockNavigationHandle handle2(GURL(kDevUiUrls[1]), main_rfh());
+  auto throttle2 = std::make_unique<TestDevUiLoaderThrottle>(&handle2);
+  EXPECT_FALSE(mock_provider_.GetIsInstalled());
+  EXPECT_FALSE(mock_provider_.GetIsLoaded());
+
+  // Simulate request start of page 1. This triggers install.
+  EXPECT_EQ(content::NavigationThrottle::DEFER, throttle1->WillStartRequest());
+
+  // Simulate request start of page 2. This does not trigger install. However,
+  // the on-complete callback is queued, and will be called.
+  EXPECT_EQ(content::NavigationThrottle::DEFER, throttle2->WillStartRequest());
+
+  // Simulate actual install.
+  mock_provider_.SimulateAsyncInstall(true);  // !
+  EXPECT_TRUE(throttle1->called_resume);
+  EXPECT_FALSE(throttle1->called_cancel);
+  EXPECT_TRUE(throttle2->called_resume);
+  EXPECT_FALSE(throttle2->called_cancel);
+  EXPECT_TRUE(mock_provider_.GetIsInstalled());
+  EXPECT_TRUE(mock_provider_.GetIsLoaded());
+}
+
+TEST_F(DevUiLoaderThrottleTest, InstallRedundant) {
+  mock_provider_.Reset();
+  // Page 1 request.
+  content::MockNavigationHandle handle1(GURL(kDevUiUrls[0]), main_rfh());
+  auto throttle1 = std::make_unique<TestDevUiLoaderThrottle>(&handle1);
+  // Page 2 request.
+  content::MockNavigationHandle handle2(GURL(kDevUiUrls[1]), main_rfh());
+  auto throttle2 = std::make_unique<TestDevUiLoaderThrottle>(&handle2);
+  EXPECT_FALSE(mock_provider_.GetIsInstalled());
+  EXPECT_FALSE(mock_provider_.GetIsLoaded());
+
+  // Simulate request start of page 1.
+  EXPECT_EQ(content::NavigationThrottle::DEFER, throttle1->WillStartRequest());
+
+  // Simulate actual install.
+  mock_provider_.SimulateAsyncInstall(true);  // !
+  EXPECT_TRUE(throttle1->called_resume);
+  EXPECT_FALSE(throttle1->called_cancel);
+  EXPECT_TRUE(mock_provider_.GetIsInstalled());
+  EXPECT_TRUE(mock_provider_.GetIsLoaded());
+
+  // Simulate request start of page 2. The request was made before install
+  // completes, but request start occurs after install completes. In this case,
+  // simply proceed.
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            throttle2->WillStartRequest());
+  EXPECT_FALSE(throttle2->called_resume);
+  EXPECT_FALSE(throttle2->called_cancel);
+  EXPECT_TRUE(mock_provider_.GetIsInstalled());
+  EXPECT_TRUE(mock_provider_.GetIsLoaded());
+}
+
+TEST_F(DevUiLoaderThrottleTest, InstallFailure) {
+  for (const char* url_string : kDevUiUrls) {
+    mock_provider_.Reset();
+    content::MockNavigationHandle handle(GURL(url_string), main_rfh());
+    auto throttle = std::make_unique<TestDevUiLoaderThrottle>(&handle);
+    EXPECT_FALSE(throttle->called_resume);
+    EXPECT_FALSE(throttle->called_cancel);
+    EXPECT_FALSE(mock_provider_.GetIsInstalled());
+    EXPECT_FALSE(mock_provider_.GetIsLoaded());
+
+    // Simulate request start.
+    EXPECT_EQ(content::NavigationThrottle::DEFER, throttle->WillStartRequest());
+    EXPECT_FALSE(throttle->called_resume);
+    EXPECT_FALSE(throttle->called_cancel);
+    EXPECT_FALSE(mock_provider_.GetIsInstalled());
+    EXPECT_FALSE(mock_provider_.GetIsLoaded());
+
+    // Simulate actual install (failure)
+    mock_provider_.SimulateAsyncInstall(false);  // !
+    EXPECT_FALSE(throttle->called_resume);
+    EXPECT_TRUE(throttle->called_cancel);
+    EXPECT_FALSE(mock_provider_.GetIsInstalled());
+    EXPECT_FALSE(mock_provider_.GetIsLoaded());
+    EXPECT_EQ(net::ERR_CONNECTION_FAILED,
+              throttle->cancel_result.net_error_code());
+    EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST,
+              throttle->cancel_result.action());
+    EXPECT_TRUE(throttle->cancel_result.error_page_content().has_value());
+    std::string page_content =
+        throttle->cancel_result.error_page_content().value();
+    EXPECT_NE(std::string::npos, page_content.find("<!DOCTYPE html"));
+  }
+}
+
+}  // namespace dev_ui
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 6bcb9dd..2866ef5 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -831,7 +831,7 @@
     main_web_contents_->SetInitialFocus();
 
     PrefsTabHelper::CreateForWebContents(main_web_contents_);
-    main_web_contents_->GetRenderViewHost()->SyncRendererPrefs();
+    main_web_contents_->SyncRendererPrefs();
 
     DoAction(action);
     return;
@@ -1555,7 +1555,7 @@
   browser_->tab_strip_model()->AddWebContents(
       std::move(owned_main_web_contents_), -1,
       ui::PAGE_TRANSITION_AUTO_TOPLEVEL, TabStripModel::ADD_ACTIVE);
-  main_web_contents_->GetRenderViewHost()->SyncRendererPrefs();
+  main_web_contents_->SyncRendererPrefs();
 }
 
 BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
diff --git a/chrome/browser/download/download_status_updater_linux.cc b/chrome/browser/download/download_status_updater_linux.cc
index 6d79178..6b64d395 100644
--- a/chrome/browser/download/download_status_updater_linux.cc
+++ b/chrome/browser/download/download_status_updater_linux.cc
@@ -10,6 +10,8 @@
 #include <string>
 
 #include "base/environment.h"
+#include "base/memory/protected_memory.h"
+#include "base/memory/protected_memory_cfi.h"
 #include "base/nix/xdg_util.h"
 #include "chrome/common/channel_info.h"
 #include "ui/base/glib/glib_integers.h"
@@ -45,12 +47,24 @@
 UnityLauncherEntry* chrome_entry = nullptr;
 
 // Retrieved functions from libunity.
-unity_inspector_get_unity_running_func get_unity_running = nullptr;
-unity_launcher_entry_set_count_func entry_set_count = nullptr;
-unity_launcher_entry_set_count_visible_func entry_set_count_visible = nullptr;
-unity_launcher_entry_set_progress_func entry_set_progress = nullptr;
-unity_launcher_entry_set_progress_visible_func entry_set_progress_visible =
-    nullptr;
+PROTECTED_MEMORY_SECTION
+base::ProtectedMemory<unity_inspector_get_default_func> inspector_get_default;
+PROTECTED_MEMORY_SECTION
+base::ProtectedMemory<unity_inspector_get_unity_running_func> get_unity_running;
+PROTECTED_MEMORY_SECTION
+base::ProtectedMemory<unity_launcher_entry_get_for_desktop_id_func>
+    entry_get_for_desktop_id;
+PROTECTED_MEMORY_SECTION
+base::ProtectedMemory<unity_launcher_entry_set_count_func> entry_set_count;
+PROTECTED_MEMORY_SECTION
+base::ProtectedMemory<unity_launcher_entry_set_count_visible_func>
+    entry_set_count_visible;
+PROTECTED_MEMORY_SECTION
+base::ProtectedMemory<unity_launcher_entry_set_progress_func>
+    entry_set_progress;
+PROTECTED_MEMORY_SECTION
+base::ProtectedMemory<unity_launcher_entry_set_progress_visible_func>
+    entry_set_progress_visible;
 
 void EnsureLibUnityLoaded() {
   using base::nix::GetDesktopEnvironment;
@@ -77,57 +91,75 @@
   if (!unity_lib)
     return;
 
-  unity_inspector_get_default_func inspector_get_default =
-      reinterpret_cast<unity_inspector_get_default_func>(
-          dlsym(unity_lib, "unity_inspector_get_default"));
-  if (inspector_get_default) {
-    inspector = inspector_get_default();
+  static base::ProtectedMemory<unity_inspector_get_default_func>::Initializer
+      inspector_get_default_init(
+          &inspector_get_default,
+          reinterpret_cast<unity_inspector_get_default_func>(
+              dlsym(unity_lib, "unity_inspector_get_default")));
+  if (*inspector_get_default) {
+    inspector = UnsanitizedCfiCall(inspector_get_default)();
 
-    get_unity_running =
-        reinterpret_cast<unity_inspector_get_unity_running_func>(
-            dlsym(unity_lib, "unity_inspector_get_unity_running"));
+    static base::ProtectedMemory<unity_inspector_get_unity_running_func>::
+        Initializer get_unity_running_init(
+            &get_unity_running,
+            reinterpret_cast<unity_inspector_get_unity_running_func>(
+                dlsym(unity_lib, "unity_inspector_get_unity_running")));
   }
 
-  unity_launcher_entry_get_for_desktop_id_func entry_get_for_desktop_id =
-      reinterpret_cast<unity_launcher_entry_get_for_desktop_id_func>(
-          dlsym(unity_lib, "unity_launcher_entry_get_for_desktop_id"));
-  if (entry_get_for_desktop_id) {
+  static base::ProtectedMemory<unity_launcher_entry_get_for_desktop_id_func>::
+      Initializer entry_get_for_desktop_id_init(
+          &entry_get_for_desktop_id,
+          reinterpret_cast<unity_launcher_entry_get_for_desktop_id_func>(
+              dlsym(unity_lib, "unity_launcher_entry_get_for_desktop_id")));
+  if (*entry_get_for_desktop_id) {
     std::string desktop_id = chrome::GetDesktopName(env.get());
-    chrome_entry = entry_get_for_desktop_id(desktop_id.c_str());
+    chrome_entry =
+        UnsanitizedCfiCall(entry_get_for_desktop_id)(desktop_id.c_str());
 
-    entry_set_count = reinterpret_cast<unity_launcher_entry_set_count_func>(
-        dlsym(unity_lib, "unity_launcher_entry_set_count"));
+    static base::ProtectedMemory<unity_launcher_entry_set_count_func>::
+        Initializer entry_set_count_init(
+            &entry_set_count,
+            reinterpret_cast<unity_launcher_entry_set_count_func>(
+                dlsym(unity_lib, "unity_launcher_entry_set_count")));
 
-    entry_set_count_visible =
-        reinterpret_cast<unity_launcher_entry_set_count_visible_func>(
-            dlsym(unity_lib, "unity_launcher_entry_set_count_visible"));
+    static base::ProtectedMemory<unity_launcher_entry_set_count_visible_func>::
+        Initializer entry_set_count_visible_init(
+            &entry_set_count_visible,
+            reinterpret_cast<unity_launcher_entry_set_count_visible_func>(
+                dlsym(unity_lib, "unity_launcher_entry_set_count_visible")));
 
-    entry_set_progress =
-        reinterpret_cast<unity_launcher_entry_set_progress_func>(
-            dlsym(unity_lib, "unity_launcher_entry_set_progress"));
+    static base::ProtectedMemory<unity_launcher_entry_set_progress_func>::
+        Initializer entry_set_progress_init(
+            &entry_set_progress,
+            reinterpret_cast<unity_launcher_entry_set_progress_func>(
+                dlsym(unity_lib, "unity_launcher_entry_set_progress")));
 
-    entry_set_progress_visible =
-        reinterpret_cast<unity_launcher_entry_set_progress_visible_func>(
-            dlsym(unity_lib, "unity_launcher_entry_set_progress_visible"));
+    static base::ProtectedMemory<
+        unity_launcher_entry_set_progress_visible_func>::Initializer
+        entry_set_progress_visible_init(
+            &entry_set_progress_visible,
+            reinterpret_cast<unity_launcher_entry_set_progress_visible_func>(
+                dlsym(unity_lib, "unity_launcher_entry_set_progress_visible")));
   }
 }
 
 bool IsRunning() {
-  return inspector && get_unity_running && get_unity_running(inspector);
+  return inspector && *get_unity_running &&
+         UnsanitizedCfiCall(get_unity_running)(inspector);
 }
 
 void SetDownloadCount(int count) {
-  if (chrome_entry && entry_set_count && entry_set_count_visible) {
-    entry_set_count(chrome_entry, count);
-    entry_set_count_visible(chrome_entry, count != 0);
+  if (chrome_entry && *entry_set_count && *entry_set_count_visible) {
+    UnsanitizedCfiCall(entry_set_count)(chrome_entry, count);
+    UnsanitizedCfiCall(entry_set_count_visible)(chrome_entry, count != 0);
   }
 }
 
 void SetProgressFraction(float percentage) {
-  if (chrome_entry && entry_set_progress && entry_set_progress_visible) {
-    entry_set_progress(chrome_entry, percentage);
-    entry_set_progress_visible(chrome_entry,
-                               percentage > 0.0 && percentage < 1.0);
+  if (chrome_entry && *entry_set_progress && *entry_set_progress_visible) {
+    UnsanitizedCfiCall(entry_set_progress)(chrome_entry, percentage);
+    UnsanitizedCfiCall(entry_set_progress_visible)(
+        chrome_entry, percentage > 0.0 && percentage < 1.0);
   }
 }
 
diff --git a/chrome/browser/extensions/api/preference/preference_api.cc b/chrome/browser/extensions/api/preference/preference_api.cc
index d8d8836..f961cdd 100644
--- a/chrome/browser/extensions/api/preference/preference_api.cc
+++ b/chrome/browser/extensions/api/preference/preference_api.cc
@@ -17,14 +17,12 @@
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/api/content_settings/content_settings_service.h"
 #include "chrome/browser/extensions/api/preference/preference_api_constants.h"
 #include "chrome/browser/extensions/api/preference/preference_helpers.h"
 #include "chrome/browser/extensions/api/proxy/proxy_api.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/net/prediction_options.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "components/autofill/core/common/autofill_prefs.h"
 #include "components/content_settings/core/common/pref_names.h"
@@ -35,9 +33,6 @@
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/spellcheck/browser/pref_names.h"
 #include "components/translate/core/browser/translate_pref_names.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_source.h"
 #include "extensions/browser/extension_pref_value_map.h"
 #include "extensions/browser/extension_pref_value_map_factory.h"
 #include "extensions/browser/extension_prefs.h"
@@ -364,14 +359,15 @@
                    base::Bind(&PreferenceEventRouter::OnPrefChanged,
                               base::Unretained(this), registrar_.prefs()));
   }
-  notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
-                              content::NotificationService::AllSources());
-  notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
-                              content::NotificationService::AllSources());
-  OnIncognitoProfileCreated(profile->GetOffTheRecordPrefs());
+  DCHECK(!profile_->IsOffTheRecord());
+  observed_profiles_.Add(profile_);
+  if (profile->HasOffTheRecordProfile())
+    OnOffTheRecordProfileCreated(profile->GetOffTheRecordProfile());
+  else
+    ObserveOffTheRecordPrefs(profile->GetReadOnlyOffTheRecordPrefs());
 }
 
-PreferenceEventRouter::~PreferenceEventRouter() { }
+PreferenceEventRouter::~PreferenceEventRouter() = default;
 
 void PreferenceEventRouter::OnPrefChanged(PrefService* pref_service,
                                           const std::string& browser_pref) {
@@ -423,33 +419,22 @@
       browser_pref);
 }
 
-void PreferenceEventRouter::Observe(
-    int type,
-    const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-  switch (type) {
-    case chrome::NOTIFICATION_PROFILE_CREATED: {
-      Profile* profile = content::Source<Profile>(source).ptr();
-      if (profile != profile_ && profile->GetOriginalProfile() == profile_) {
-        OnIncognitoProfileCreated(profile->GetPrefs());
-      }
-      break;
-    }
-    case chrome::NOTIFICATION_PROFILE_DESTROYED: {
-      Profile* profile = content::Source<Profile>(source).ptr();
-      if (profile != profile_ && profile->GetOriginalProfile() == profile_) {
-        // The real PrefService is about to be destroyed so we must make sure we
-        // get the "dummy" one.
-        OnIncognitoProfileCreated(profile_->GetReadOnlyOffTheRecordPrefs());
-      }
-      break;
-    }
-    default:
-      NOTREACHED();
+void PreferenceEventRouter::OnOffTheRecordProfileCreated(
+    Profile* off_the_record) {
+  observed_profiles_.Add(off_the_record);
+  ObserveOffTheRecordPrefs(off_the_record->GetPrefs());
+}
+
+void PreferenceEventRouter::OnProfileWillBeDestroyed(Profile* profile) {
+  observed_profiles_.Remove(profile);
+  if (profile->IsOffTheRecord()) {
+    // The real PrefService is about to be destroyed so we must make sure we
+    // get the "dummy" one.
+    ObserveOffTheRecordPrefs(profile_->GetReadOnlyOffTheRecordPrefs());
   }
 }
 
-void PreferenceEventRouter::OnIncognitoProfileCreated(PrefService* prefs) {
+void PreferenceEventRouter::ObserveOffTheRecordPrefs(PrefService* prefs) {
   incognito_registrar_ = std::make_unique<PrefChangeRegistrar>();
   incognito_registrar_->Init(prefs);
   for (const auto& pref : kPrefMapping) {
diff --git a/chrome/browser/extensions/api/preference/preference_api.h b/chrome/browser/extensions/api/preference/preference_api.h
index 086434a..fbb6320 100644
--- a/chrome/browser/extensions/api/preference/preference_api.h
+++ b/chrome/browser/extensions/api/preference/preference_api.h
@@ -10,11 +10,12 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/scoped_observer.h"
 #include "chrome/browser/extensions/api/content_settings/content_settings_store.h"
 #include "chrome/browser/extensions/chrome_extension_function.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_observer.h"
 #include "components/prefs/pref_change_registrar.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_prefs_scope.h"
@@ -29,7 +30,7 @@
 namespace extensions {
 class ExtensionPrefs;
 
-class PreferenceEventRouter : public content::NotificationObserver {
+class PreferenceEventRouter : public ProfileObserver {
  public:
   explicit PreferenceEventRouter(Profile* profile);
   ~PreferenceEventRouter() override;
@@ -38,20 +39,20 @@
   void OnPrefChanged(PrefService* pref_service,
                      const std::string& pref_key);
 
-  // content::NotificationObserver:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
+  // ProfileObserver:
+  void OnOffTheRecordProfileCreated(Profile* off_the_record) override;
+  void OnProfileWillBeDestroyed(Profile* profile) override;
 
-  void OnIncognitoProfileCreated(PrefService* prefs);
+  void ObserveOffTheRecordPrefs(PrefService* prefs);
 
-  content::NotificationRegistrar notification_registrar_;
   PrefChangeRegistrar registrar_;
   std::unique_ptr<PrefChangeRegistrar> incognito_registrar_;
 
   // Weak, owns us (transitively via ExtensionService).
   Profile* profile_;
 
+  ScopedObserver<Profile, ProfileObserver> observed_profiles_{this};
+
   DISALLOW_COPY_AND_ASSIGN(PreferenceEventRouter);
 };
 
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 5e316e3..068ab0658 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -599,8 +599,9 @@
       << message_;
 }
 
-// Flaky on ChromeOS: https://crbug.com/1003661
-#if defined(OS_CHROMEOS)
+// Flaky on ChromeOS: https://crbug.com/1003661 and Linux:
+// https://crbug.com/1864717
+#if defined(OS_CHROMEOS) || defined(OS_LINUX)
 #define MAYBE_WebRequestExtraHeaders DISABLED_WebRequestExtraHeaders
 #else
 #define MAYBE_WebRequestExtraHeaders WebRequestExtraHeaders
diff --git a/chrome/browser/extensions/content_verifier_browsertest.cc b/chrome/browser/extensions/content_verifier_browsertest.cc
index 928c009..2a86557 100644
--- a/chrome/browser/extensions/content_verifier_browsertest.cc
+++ b/chrome/browser/extensions/content_verifier_browsertest.cc
@@ -432,6 +432,26 @@
   EXPECT_FALSE(reasons);
 }
 
+// Tests that navigating to an extension resource with '.' at end does not
+// disable the extension.
+//
+// Regression test for https://crbug.com/696208.
+IN_PROC_BROWSER_TEST_F(ContentVerifierTest,
+                       RemainsEnabledOnNavigateToPathEndingWithDot) {
+  const Extension* extension = InstallExtensionFromWebstore(
+      test_data_dir_.AppendASCII("content_verifier/dot_slash_paths.crx"), 1);
+  ASSERT_TRUE(extension);
+  const ExtensionId kExtensionId = extension->id();
+
+  GURL page_url = extension->GetResourceURL("page.html.");
+  ui_test_utils::NavigateToURLWithDispositionBlockUntilNavigationsComplete(
+      browser(), page_url, 1, WindowOpenDisposition::CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
+  int reasons = prefs->GetDisableReasons(kExtensionId);
+  EXPECT_EQ(disable_reason::DISABLE_NONE, reasons);
+}
+
 class ContentVerifierPolicyTest : public ContentVerifierTest {
  public:
   // We need to do this work here because the force-install policy values are
diff --git a/chrome/browser/extensions/extension_sync_service.cc b/chrome/browser/extensions/extension_sync_service.cc
index 10911598..3d772cf 100644
--- a/chrome/browser/extensions/extension_sync_service.cc
+++ b/chrome/browser/extensions/extension_sync_service.cc
@@ -547,7 +547,7 @@
   auto* provider = web_app::WebAppProviderBase::GetProviderBase(profile_);
   DCHECK(provider);
 
-  provider->install_manager().InstallOrUpdateWebAppFromSync(
+  provider->install_manager().InstallWebAppFromSync(
       extension_sync_data.id(), std::move(web_app_info), base::DoNothing());
 }
 
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index 253a1785f..27a1398 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -44,6 +44,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/external_install_info.h"
 #include "extensions/browser/external_provider_interface.h"
+#include "extensions/browser/pref_names.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -711,7 +712,6 @@
 #else
   check_admin_permissions_on_mac = ExternalPrefLoader::NONE;
 #endif
-
 #if !defined(OS_WIN)
   int bundled_extension_creation_flags = Extension::NO_FLAGS;
 #endif
@@ -760,45 +760,49 @@
     chromeos::DemoSession::Get()->SetExtensionsExternalLoader(loader);
     provider_list->push_back(std::move(demo_apps_provider));
   }
-#elif defined(OS_LINUX)
-  provider_list->push_back(std::make_unique<ExternalProviderImpl>(
-      service,
-      base::MakeRefCounted<ExternalPrefLoader>(
-          chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS,
-          ExternalPrefLoader::USE_USER_TYPE_PROFILE_FILTER, profile),
-      profile, Manifest::EXTERNAL_PREF, Manifest::EXTERNAL_PREF_DOWNLOAD,
-      bundled_extension_creation_flags));
 #endif
-
-  if (!profile->IsLegacySupervised()) {
-#if defined(OS_WIN)
-    auto registry_provider = std::make_unique<ExternalProviderImpl>(
-        service, new ExternalRegistryLoader, profile,
-        Manifest::EXTERNAL_REGISTRY, Manifest::EXTERNAL_PREF_DOWNLOAD,
-        Extension::NO_FLAGS);
-    registry_provider->set_allow_updates(true);
-    provider_list->push_back(std::move(registry_provider));
-#else
+  if (!profile->GetPrefs()->GetBoolean(pref_names::kBlockExternalExtensions)) {
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
     provider_list->push_back(std::make_unique<ExternalProviderImpl>(
         service,
         base::MakeRefCounted<ExternalPrefLoader>(
-            chrome::DIR_EXTERNAL_EXTENSIONS, check_admin_permissions_on_mac,
-            nullptr),
+            chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS,
+            ExternalPrefLoader::USE_USER_TYPE_PROFILE_FILTER, profile),
         profile, Manifest::EXTERNAL_PREF, Manifest::EXTERNAL_PREF_DOWNLOAD,
         bundled_extension_creation_flags));
+#endif
+    if (!profile->IsLegacySupervised()) {
+#if defined(OS_WIN)
+      auto registry_provider = std::make_unique<ExternalProviderImpl>(
+          service, new ExternalRegistryLoader, profile,
+          Manifest::EXTERNAL_REGISTRY, Manifest::EXTERNAL_PREF_DOWNLOAD,
+          Extension::NO_FLAGS);
+      registry_provider->set_allow_updates(true);
+      provider_list->push_back(std::move(registry_provider));
+#else
+      provider_list->push_back(std::make_unique<ExternalProviderImpl>(
+          service,
+          base::MakeRefCounted<ExternalPrefLoader>(
+              chrome::DIR_EXTERNAL_EXTENSIONS, check_admin_permissions_on_mac,
+              nullptr),
+          profile, Manifest::EXTERNAL_PREF, Manifest::EXTERNAL_PREF_DOWNLOAD,
+          bundled_extension_creation_flags));
 
-    // Define a per-user source of external extensions.
+      // Define a per-user source of external extensions.
 #if defined(OS_MACOSX) || (defined(OS_LINUX) && BUILDFLAG(CHROMIUM_BRANDING))
-    provider_list->push_back(std::make_unique<ExternalProviderImpl>(
-        service,
-        base::MakeRefCounted<ExternalPrefLoader>(
-            chrome::DIR_USER_EXTERNAL_EXTENSIONS, ExternalPrefLoader::NONE,
-            nullptr),
-        profile, Manifest::EXTERNAL_PREF, Manifest::EXTERNAL_PREF_DOWNLOAD,
-        Extension::NO_FLAGS));
+      provider_list->push_back(std::make_unique<ExternalProviderImpl>(
+          service,
+          base::MakeRefCounted<ExternalPrefLoader>(
+              chrome::DIR_USER_EXTERNAL_EXTENSIONS, ExternalPrefLoader::NONE,
+              nullptr),
+          profile, Manifest::EXTERNAL_PREF, Manifest::EXTERNAL_PREF_DOWNLOAD,
+          Extension::NO_FLAGS));
 #endif
 #endif
+    }
+  }
 
+  if (!profile->IsLegacySupervised()) {
 #if !defined(OS_CHROMEOS)
     // The default apps are installed as INTERNAL but use the external
     // extension installer codeflow.
diff --git a/chrome/browser/extensions/external_provider_impl_unittest.cc b/chrome/browser/extensions/external_provider_impl_unittest.cc
index 99085dc..bf00954aa 100644
--- a/chrome/browser/extensions/external_provider_impl_unittest.cc
+++ b/chrome/browser/extensions/external_provider_impl_unittest.cc
@@ -12,7 +12,10 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/string16.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_path_override.h"
 #include "build/branding_buildflags.h"
@@ -31,6 +34,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/browser/pref_names.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
@@ -51,12 +55,17 @@
 const char kManifestPath[] = "/update_manifest";
 const char kAppPath[] = "/app.crx";
 
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+const char kExternalAppId[] = "kekdneafjmhmndejhmbcadfiiofngffo";
+#endif
+
 class ExternalProviderImplTest : public ExtensionServiceTestBase {
  public:
   ExternalProviderImplTest() {}
   ~ExternalProviderImplTest() override {}
 
-  void InitServiceWithExternalProviders() {
+  void InitServiceWithExternalProviders(
+      const base::Optional<bool> block_external = base::nullopt) {
 #if defined(OS_CHROMEOS)
     user_manager::ScopedUserManager scoped_user_manager(
         std::make_unique<chromeos::FakeChromeUserManager>());
@@ -71,6 +80,9 @@
     // would cause the external updates to never finish install.
     profile_->GetPrefs()->SetString(prefs::kDefaultApps, "");
 
+    if (block_external.has_value())
+      SetExternalExtensionsBlockedByPolicy(block_external.value());
+
     ProviderCollection providers;
     extensions::ExternalProviderImpl::CreateExternalProviders(
         service_, profile_.get(), &providers);
@@ -79,6 +91,16 @@
       service_->AddProviderForTesting(std::move(provider));
   }
 
+  void OverrideExternalExtensionsPath() {
+    external_externsions_overrides_.reset(new base::ScopedPathOverride(
+        chrome::DIR_EXTERNAL_EXTENSIONS, data_dir().AppendASCII("external")));
+  }
+
+  void SetExternalExtensionsBlockedByPolicy(const bool block_external) {
+    profile_->GetPrefs()->SetBoolean(pref_names::kBlockExternalExtensions,
+                                     block_external);
+  }
+
   void InitializeExtensionServiceWithUpdaterAndPrefs() {
     ExtensionServiceInitParams params = CreateDefaultInitParams();
     params.autoupdate_enabled = true;
@@ -142,6 +164,7 @@
     return nullptr;
   }
 
+  std::unique_ptr<base::ScopedPathOverride> external_externsions_overrides_;
   std::unique_ptr<net::test_server::EmbeddedTestServer> test_server_;
   std::unique_ptr<ExtensionCacheFake> test_extension_cache_;
 
@@ -160,19 +183,43 @@
 TEST_F(ExternalProviderImplTest, InAppPayments) {
   InitServiceWithExternalProviders();
 
-  scoped_refptr<content::MessageLoopRunner> runner =
-      new content::MessageLoopRunner;
+  base::RunLoop run_loop;
   service_->set_external_updates_finished_callback_for_test(
-      runner->QuitClosure());
-
+      run_loop.QuitClosure());
   service_->CheckForExternalUpdates();
-  runner->Run();
+  run_loop.Run();
 
   EXPECT_TRUE(registry()->GetInstalledExtension(
       extension_misc::kInAppPaymentsSupportAppId));
   EXPECT_TRUE(service_->IsExtensionEnabled(
       extension_misc::kInAppPaymentsSupportAppId));
 }
+
+TEST_F(ExternalProviderImplTest, BlockedExternalUserProviders) {
+  OverrideExternalExtensionsPath();
+  InitServiceWithExternalProviders(true);
+
+  base::RunLoop run_loop;
+  service_->set_external_updates_finished_callback_for_test(
+      run_loop.QuitClosure());
+  service_->CheckForExternalUpdates();
+  run_loop.Run();
+
+  EXPECT_FALSE(registry()->GetInstalledExtension(kExternalAppId));
+}
+
+TEST_F(ExternalProviderImplTest, NotBlockedExternalUserProviders) {
+  OverrideExternalExtensionsPath();
+  InitServiceWithExternalProviders(false);
+
+  base::RunLoop run_loop;
+  service_->set_external_updates_finished_callback_for_test(
+      run_loop.QuitClosure());
+  service_->CheckForExternalUpdates();
+  run_loop.Run();
+
+  EXPECT_TRUE(registry()->GetInstalledExtension(kExternalAppId));
+}
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 
 }  // namespace extensions
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 709e2d1..2321237 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -356,11 +356,6 @@
     "expiry_milestone": 80
   },
   {
-    "name": "bundled-exchanges",
-    "owners": [ "toyoshim", "ksakamoto", "horo", "kinuko" ],
-    "expiry_milestone": 83
-  },
-  {
     "name": "BundledConnectionHelp",
     "owners": [ "carlosil" ],
     "expiry_milestone": 76
@@ -1124,6 +1119,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "enable-cros-action-recorder",
+    "owners": [ "charleszhao", "tby" ],
+    "expiry_milestone": 84
+  },
+  {
     "name": "enable-cros-ime-input-logic-fst",
     "owners": [ "essential-inputs-team@google.com" ],
     "expiry_milestone": 77
@@ -1765,6 +1765,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "enable-sync-device-info-in-transport-mode",
+    "owners": ["//components/sync/OWNERS", "//chrome/browser/sharing/OWNERS"],
+    "expiry_milestone": 81
+  },
+  {
     "name": "enable-sync-uss-bookmarks",
     "owners": [ "mamir", "//components/sync/OWNERS" ],
     "expiry_milestone": 80
@@ -1925,7 +1930,7 @@
   },
   {
     "name": "enable-webassembly-baseline",
-    "owners": [ "clemensh", "wasm-team@google.com" ],
+    "owners": [ "clemensb", "wasm-team@google.com" ],
     "expiry_milestone": 83
   },
   {
@@ -1935,7 +1940,7 @@
   },
   {
     "name": "enable-webassembly-code-gc",
-    "owners": [ "clemensh", "wasm-team@google.com" ],
+    "owners": [ "clemensb", "wasm-team@google.com" ],
     "expiry_milestone": 83
   },
   {
@@ -2382,7 +2387,7 @@
   {
     "name": "manual-password-generation-android",
     "owners": [ "ioanap" ],
-    "expiry_milestone": 78
+    "expiry_milestone": 80
   },
   {
     "name": "media-router-cast-allow-all-ips",
@@ -2939,8 +2944,8 @@
   },
   {
     "name": "reduced-referrer-granularity",
-    "owners": [ "jochen", "mkwst" ],
-    "expiry_milestone": 78
+    "owners": [ "davidvc", "mkwst", "jochen" ],
+    "expiry_milestone": 81
   },
   {
     "name": "release-notes",
@@ -2993,11 +2998,6 @@
     "expiry_milestone": 79
   },
   {
-    "name": "session-restore-prioritizes-background-use-cases",
-    "owners": [ "chrisha" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "set-market-url-for-testing",
     "owners": [ "//chrome/android/java/src/org/chromium/chrome/browser/omaha/OWNERS" ],
      // This is required by test teams to verify functionality on devices which
@@ -3435,6 +3435,11 @@
     "expiry_milestone": -1
   },
   {
+    "name": "web-bundles",
+    "owners": [ "toyoshim", "ksakamoto", "horo", "kinuko" ],
+    "expiry_milestone": 83
+  },
+  {
     "name": "web-contents-occlusion",
     "owners": [ "davidbienvenu" ],
     "expiry_milestone": 81
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 13c8d41..504b6813 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -294,10 +294,6 @@
     "an interstitial after clicking the 'Learn More' link on a previous "
     "interstitial.";
 
-const char kBundledHTTPExchangesName[] = "Bundled HTTP Exchanges";
-const char kBundledHTTPExchangesDescription[] =
-    "Enables experimental supports for Bundled HTTP Exchanges core navigation.";
-
 const char kBypassAppBannerEngagementChecksName[] =
     "Bypass user engagement checks";
 const char kBypassAppBannerEngagementChecksDescription[] =
@@ -1835,13 +1831,6 @@
     "Enables browser process logic related to service workers to run on the UI "
     "thread rather than the IO thread.";
 
-const char kSessionRestorePrioritizesBackgroundUseCasesName[] =
-    "Session restore prioritizes background use cases.";
-const char kSessionRestorePrioritizesBackgroundUseCasesDescription[] =
-    "When enabled session restore logic will prioritize sites that make use of "
-    "background communication mechanisms (favicon and tab title switches, "
-    "notifications, etc) over sites that do not.";
-
 const char kSettingsWindowName[] = "Show settings in a window";
 const char kSettingsWindowDescription[] =
     "Settings will be shown in a dedicated window instead of as a browser tab.";
@@ -1994,6 +1983,12 @@
     "Match Autofill suggestions based on substrings (token prefixes) rather "
     "than just prefixes.";
 
+const char kSyncDeviceInfoInTransportModeName[] =
+    "Enable syncing DeviceInfo in transport-only sync mode.";
+const char kSyncDeviceInfoInTransportModeDescription[] =
+    "When enabled, allows syncing DeviceInfo datatype for users who are "
+    "signed-in but not necessary sync-ing.";
+
 const char kSyncSandboxName[] = "Use Chrome Sync sandbox";
 const char kSyncSandboxDescription[] =
     "Connects to the testing server for Chrome Sync.";
@@ -2212,6 +2207,11 @@
 const char kWalletServiceUseSandboxDescription[] =
     "For developers: use the sandbox service for Google Payments API calls.";
 
+const char kWebBundlesName[] = "Web Bundles";
+const char kWebBundlesDescription[] =
+    "Enables experimental supports for Web Bundles (Bundled HTTP Exchanges) "
+    "navigation.";
+
 const char kWebglDraftExtensionsName[] = "WebGL Draft Extensions";
 const char kWebglDraftExtensionsDescription[] =
     "Enabling this option allows web applications to access the WebGL "
@@ -3381,6 +3381,12 @@
 const char kEnableBackgroundBlurDescription[] =
     "Enables background blur for the Launcher, Shelf, Unified System Tray etc.";
 
+const char kEnableCrOSActionRecorderName[] = "Enable CrOS action recorder";
+const char kEnableCrOSActionRecorderDescription[] =
+    "When enabled, each app launching, file opening, setting change, and url "
+    "visiting will be logged locally into an encrypted file. Should not be "
+    "enabled. Be aware that hash option only provides a thin layer of privacy.";
+
 const char kEnableDiscoverAppName[] = "Enable Discover App";
 const char kEnableDiscoverAppDescription[] =
     "Enable Discover App icon in launcher.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index fb879734..4ea35e35 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -184,9 +184,6 @@
 extern const char kBundledConnectionHelpName[];
 extern const char kBundledConnectionHelpDescription[];
 
-extern const char kBundledHTTPExchangesName[];
-extern const char kBundledHTTPExchangesDescription[];
-
 extern const char kBypassAppBannerEngagementChecksName[];
 extern const char kBypassAppBannerEngagementChecksDescription[];
 
@@ -1082,9 +1079,6 @@
 extern const char kServiceWorkerOnUIName[];
 extern const char kServiceWorkerOnUIDescription[];
 
-extern const char kSessionRestorePrioritizesBackgroundUseCasesName[];
-extern const char kSessionRestorePrioritizesBackgroundUseCasesDescription[];
-
 extern const char kSettingsWindowName[];
 extern const char kSettingsWindowDescription[];
 
@@ -1177,6 +1171,9 @@
 extern const char kSuggestionsWithSubStringMatchName[];
 extern const char kSuggestionsWithSubStringMatchDescription[];
 
+extern const char kSyncDeviceInfoInTransportModeName[];
+extern const char kSyncDeviceInfoInTransportModeDescription[];
+
 extern const char kSyncSandboxName[];
 extern const char kSyncSandboxDescription[];
 
@@ -1294,6 +1291,9 @@
 extern const char kWalletServiceUseSandboxName[];
 extern const char kWalletServiceUseSandboxDescription[];
 
+extern const char kWebBundlesName[];
+extern const char kWebBundlesDescription[];
+
 extern const char kWebglDraftExtensionsName[];
 extern const char kWebglDraftExtensionsDescription[];
 
@@ -2008,6 +2008,9 @@
 extern const char kEnableBackgroundBlurName[];
 extern const char kEnableBackgroundBlurDescription[];
 
+extern const char kEnableCrOSActionRecorderName[];
+extern const char kEnableCrOSActionRecorderDescription[];
+
 extern const char kEnableDiscoverAppName[];
 extern const char kEnableDiscoverAppDescription[];
 
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest_perf.cc b/chrome/browser/media/webrtc/webrtc_browsertest_perf.cc
index 4d9140f..9a61637 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest_perf.cc
+++ b/chrome/browser/media/webrtc/webrtc_browsertest_perf.cc
@@ -9,7 +9,106 @@
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "testing/perf/perf_test.h"
+#include "testing/perf/perf_result_reporter.h"
+
+namespace {
+
+constexpr char kMetricPrefixAudioReceive[] = "WebRtcAudioReceive.";
+constexpr char kMetricPrefixAudioSend[] = "WebRtcAudioSend.";
+constexpr char kMetricPrefixVideoSend[] = "WebRtcVideoSend.";
+constexpr char kMetricPrefixVideoRecieve[] = "WebRtcVideoReceive.";
+constexpr char kMetricPrefixBwe[] = "WebRtcBwe.";
+constexpr char kMetricPacketsLostFrames[] = "packets_lost";
+constexpr char kMetricGoogJitterRecvMs[] = "goog_jitter_recv";
+constexpr char kMetricGoogExpandRatePercent[] = "goog_expand_rate";
+constexpr char kMetricGoogSpeechExpandRatePercent[] = "goog_speech_expand_rate";
+constexpr char kMetricGoogSecondaryDecodeRatePercent[] =
+    "goog_secondary_decode_rate";
+constexpr char kMetricGoogRttMs[] = "goog_rtt";
+constexpr char kMetricPacketsPerSecondPackets[] = "packets_per_second";
+constexpr char kMetricGoogFpsSentFps[] = "goog_frame_rate_sent";
+constexpr char kMetricGoogFpsInputFps[] = "goog_frame_rate_input";
+constexpr char kMetricGoogFirsReceivedUnitless[] = "goog_firs_recv";
+constexpr char kMetricGoogNacksReceivedUnitless[] = "goog_nacks_recv";
+constexpr char kMetricGoogFrameWidthCount[] = "goog_frame_width";
+constexpr char kMetricGoogFrameHeightCount[] = "goog_frame_height";
+constexpr char kMetricGoogAvgEncodeMs[] = "goog_avg_encode";
+constexpr char kMetricGoogEncodeCpuUsagePercent[] = "goog_encode_cpu_usage";
+constexpr char kMetricGoogFpsRecvFps[] = "goog_frame_rate_recv";
+constexpr char kMetricGoogFpsOutputFps[] = "goog_frame_rate_output";
+constexpr char kMetricGoogActualDelayMs[] = "goog_actual_delay";
+constexpr char kMetricGoogTargetDelayMs[] = "goog_target_delay";
+constexpr char kMetricGoogDecodeTimeMs[] = "goog_decode_time";
+constexpr char kMetricGoogMaxDecodeTimeMs[] = "goog_max_decode_time";
+constexpr char kMetricGoogJitterBufferMs[] = "goog_jitter_buffer";
+constexpr char kMetricGoogRenderDelayMs[] = "goog_render_delay";
+constexpr char kMetricAvailableSendBandwidthBitsPerS[] = "available_send_bw";
+constexpr char kMetricAvailableRecvBandwidthBitsPerS[] = "available_recv_bw";
+constexpr char kMetricTargetEncodeBitrateBitsPerS[] = "target_encode_bitrate";
+constexpr char kMetricActualEncodeBitrateBitsPerS[] = "actual_encode_bitrate";
+constexpr char kMetricTransmitBitrateBitsPerS[] = "transmit_bitrate";
+
+perf_test::PerfResultReporter SetUpAudioReceiveReporter(
+    const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixAudioReceive, story);
+  reporter.RegisterFyiMetric(kMetricPacketsLostFrames, "frames");
+  reporter.RegisterFyiMetric(kMetricGoogJitterRecvMs, "ms");
+  reporter.RegisterFyiMetric(kMetricGoogExpandRatePercent, "%");
+  reporter.RegisterFyiMetric(kMetricGoogSpeechExpandRatePercent, "%");
+  reporter.RegisterFyiMetric(kMetricGoogSecondaryDecodeRatePercent, "%");
+  return reporter;
+}
+
+perf_test::PerfResultReporter SetUpAudioSendReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixAudioSend, story);
+  reporter.RegisterFyiMetric(kMetricGoogJitterRecvMs, "ms");
+  reporter.RegisterFyiMetric(kMetricGoogRttMs, "ms");
+  reporter.RegisterFyiMetric(kMetricPacketsPerSecondPackets, "packets");
+  return reporter;
+}
+
+perf_test::PerfResultReporter SetUpVideoSendReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixVideoSend, story);
+  reporter.RegisterFyiMetric(kMetricGoogFpsSentFps, "fps");
+  reporter.RegisterFyiMetric(kMetricGoogFpsInputFps, "fps");
+  reporter.RegisterFyiMetric(kMetricGoogFirsReceivedUnitless, "unitless");
+  reporter.RegisterFyiMetric(kMetricGoogNacksReceivedUnitless, "unitless");
+  reporter.RegisterFyiMetric(kMetricGoogFrameWidthCount, "count");
+  reporter.RegisterFyiMetric(kMetricGoogFrameHeightCount, "count");
+  reporter.RegisterFyiMetric(kMetricGoogAvgEncodeMs, "ms");
+  reporter.RegisterFyiMetric(kMetricGoogRttMs, "ms");
+  reporter.RegisterFyiMetric(kMetricGoogEncodeCpuUsagePercent, "%");
+  return reporter;
+}
+
+perf_test::PerfResultReporter SetUpVideoReceiveReporter(
+    const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixVideoRecieve, story);
+  reporter.RegisterFyiMetric(kMetricGoogFpsRecvFps, "fps");
+  reporter.RegisterFyiMetric(kMetricGoogFpsOutputFps, "fps");
+  reporter.RegisterFyiMetric(kMetricPacketsLostFrames, "frames");
+  reporter.RegisterFyiMetric(kMetricGoogFrameWidthCount, "count");
+  reporter.RegisterFyiMetric(kMetricGoogFrameHeightCount, "count");
+  reporter.RegisterFyiMetric(kMetricGoogActualDelayMs, "ms");
+  reporter.RegisterFyiMetric(kMetricGoogTargetDelayMs, "ms");
+  reporter.RegisterFyiMetric(kMetricGoogDecodeTimeMs, "ms");
+  reporter.RegisterFyiMetric(kMetricGoogMaxDecodeTimeMs, "ms");
+  reporter.RegisterFyiMetric(kMetricGoogJitterBufferMs, "ms");
+  reporter.RegisterFyiMetric(kMetricGoogRenderDelayMs, "ms");
+  return reporter;
+}
+
+perf_test::PerfResultReporter SetUpBweReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixBwe, story);
+  reporter.RegisterFyiMetric(kMetricAvailableSendBandwidthBitsPerS, "bits/s");
+  reporter.RegisterFyiMetric(kMetricAvailableRecvBandwidthBitsPerS, "bits/s");
+  reporter.RegisterFyiMetric(kMetricTargetEncodeBitrateBitsPerS, "bits/s");
+  reporter.RegisterFyiMetric(kMetricActualEncodeBitrateBitsPerS, "bits/s");
+  reporter.RegisterFyiMetric(kMetricTransmitBitrateBitsPerS, "bits/s");
+  return reporter;
+}
+
+}  // namespace
 
 static std::string Statistic(const std::string& statistic,
                              const std::string& bucket) {
@@ -23,59 +122,53 @@
 }
 
 static void MaybePrintResultsForAudioReceive(
-    const std::string& ssrc, const base::DictionaryValue& pc_dict,
-    const std::string& modifier) {
+    const std::string& ssrc,
+    const base::DictionaryValue& pc_dict,
+    const std::string& story) {
   std::string value;
   if (!pc_dict.GetString(Statistic("audioOutputLevel", ssrc), &value)) {
     // Not an audio receive stream.
     return;
   }
 
+  auto reporter = SetUpAudioReceiveReporter(story);
   EXPECT_TRUE(pc_dict.GetString(Statistic("packetsLost", ssrc), &value));
-  perf_test::PrintResult(
-      "audio_misc", modifier, "packets_lost", value, "frames", false);
+  reporter.AddResult(kMetricPacketsLostFrames, value);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googJitterReceived", ssrc), &value));
-  perf_test::PrintResult(
-      "audio_rx", modifier, "goog_jitter_recv", value, "ms", false);
+  reporter.AddResult(kMetricGoogJitterRecvMs, value);
 
   EXPECT_TRUE(pc_dict.GetString(Statistic("googExpandRate", ssrc), &value));
-  perf_test::PrintResult(
-      "audio_rates", modifier, "goog_expand_rate", value, "%", false);
+  reporter.AddResult(kMetricGoogExpandRatePercent, value);
   EXPECT_TRUE(
       pc_dict.GetString(Statistic("googSpeechExpandRate", ssrc), &value));
-  perf_test::PrintResult(
-      "audio_rates", modifier, "goog_speech_expand_rate", value, "%", false);
+  reporter.AddResult(kMetricGoogSpeechExpandRatePercent, value);
   EXPECT_TRUE(
       pc_dict.GetString(Statistic("googSecondaryDecodedRate", ssrc), &value));
-  perf_test::PrintResult(
-      "audio_rates", modifier, "goog_secondary_decoded_rate", value, "%",
-      false);
+  reporter.AddResult(kMetricGoogSecondaryDecodeRatePercent, value);
 }
 
-static void MaybePrintResultsForAudioSend(
-    const std::string& ssrc, const base::DictionaryValue& pc_dict,
-    const std::string& modifier) {
+static void MaybePrintResultsForAudioSend(const std::string& ssrc,
+                                          const base::DictionaryValue& pc_dict,
+                                          const std::string& story) {
   std::string value;
   if (!pc_dict.GetString(Statistic("audioInputLevel", ssrc), &value)) {
     // Not an audio send stream.
     return;
   }
 
+  auto reporter = SetUpAudioSendReporter(story);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googJitterReceived", ssrc), &value));
-  perf_test::PrintResult(
-      "audio_tx", modifier, "goog_jitter_recv", value, "ms", false);
+  reporter.AddResult(kMetricGoogJitterRecvMs, value);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googRtt", ssrc), &value));
-  perf_test::PrintResult(
-      "audio_tx", modifier, "goog_rtt", value, "ms", false);
+  reporter.AddResult(kMetricGoogRttMs, value);
   EXPECT_TRUE(
       pc_dict.GetString(Statistic("packetsSentPerSecond", ssrc), &value));
-  perf_test::PrintResult("audio_tx", modifier, "packets_sent_per_second", value,
-                         "packets", false);
+  reporter.AddResult(kMetricPacketsPerSecondPackets, value);
 }
 
-static void MaybePrintResultsForVideoSend(
-    const std::string& ssrc, const base::DictionaryValue& pc_dict,
-    const std::string& modifier) {
+static void MaybePrintResultsForVideoSend(const std::string& ssrc,
+                                          const base::DictionaryValue& pc_dict,
+                                          const std::string& story) {
   std::string value;
   if (!pc_dict.GetString(Statistic("googFrameRateSent", ssrc), &value)) {
     // Not a video send stream.
@@ -85,86 +178,70 @@
   // Graph these by unit: the dashboard expects all stats in one graph to have
   // the same unit (e.g. ms, fps, etc). Most graphs, like video_fps, will also
   // be populated by the counterparts on the video receiving side.
-  perf_test::PrintResult(
-      "video_fps", modifier, "goog_frame_rate_sent", value, "fps", false);
+  auto reporter = SetUpVideoSendReporter(story);
+  reporter.AddResult(kMetricGoogFpsSentFps, value);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googFrameRateInput", ssrc), &value));
-  perf_test::PrintResult(
-      "video_fps", modifier, "goog_frame_rate_input", value, "fps", false);
+  reporter.AddResult(kMetricGoogFpsInputFps, value);
 
   EXPECT_TRUE(pc_dict.GetString(Statistic("googFirsReceived", ssrc), &value));
-  perf_test::PrintResult(
-      "video_misc", modifier, "goog_firs_recv", value, "", false);
+  reporter.AddResult(kMetricGoogFirsReceivedUnitless, value);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googNacksReceived", ssrc), &value));
-  perf_test::PrintResult(
-      "video_misc", modifier, "goog_nacks_recv", value, "", false);
+  reporter.AddResult(kMetricGoogNacksReceivedUnitless, value);
 
   EXPECT_TRUE(pc_dict.GetString(Statistic("googFrameWidthSent", ssrc), &value));
-  perf_test::PrintResult("video_resolution", modifier, "goog_frame_width_sent",
-                         value, "pixels", false);
+  reporter.AddResult(kMetricGoogFrameWidthCount, value);
   EXPECT_TRUE(
       pc_dict.GetString(Statistic("googFrameHeightSent", ssrc), &value));
-  perf_test::PrintResult("video_resolution", modifier, "goog_frame_height_sent",
-                         value, "pixels", false);
+  reporter.AddResult(kMetricGoogFrameHeightCount, value);
 
   EXPECT_TRUE(pc_dict.GetString(Statistic("googAvgEncodeMs", ssrc), &value));
-  perf_test::PrintResult(
-      "video_tx", modifier, "goog_avg_encode_ms", value, "ms", false);
+  reporter.AddResult(kMetricGoogAvgEncodeMs, value);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googRtt", ssrc), &value));
-  perf_test::PrintResult("video_tx", modifier, "goog_rtt", value, "ms", false);
+  reporter.AddResult(kMetricGoogRttMs, value);
 
   EXPECT_TRUE(pc_dict.GetString(
       Statistic("googEncodeUsagePercent", ssrc), &value));
-  perf_test::PrintResult("video_cpu_usage", modifier,
-                         "goog_encode_usage_percent", value, "%", false);
+  reporter.AddResult(kMetricGoogEncodeCpuUsagePercent, value);
 }
 
 static void MaybePrintResultsForVideoReceive(
-    const std::string& ssrc, const base::DictionaryValue& pc_dict,
-    const std::string& modifier) {
+    const std::string& ssrc,
+    const base::DictionaryValue& pc_dict,
+    const std::string& story) {
   std::string value;
   if (!pc_dict.GetString(Statistic("googFrameRateReceived", ssrc), &value)) {
     // Not a video receive stream.
     return;
   }
 
-  perf_test::PrintResult(
-      "video_fps", modifier, "goog_frame_rate_recv", value, "fps", false);
+  auto reporter = SetUpVideoReceiveReporter(story);
+  reporter.AddResult(kMetricGoogFpsRecvFps, value);
   EXPECT_TRUE(
       pc_dict.GetString(Statistic("googFrameRateOutput", ssrc), &value));
-  perf_test::PrintResult(
-      "video_fps", modifier, "goog_frame_rate_output", value, "fps", false);
+  reporter.AddResult(kMetricGoogFpsOutputFps, value);
 
   EXPECT_TRUE(pc_dict.GetString(Statistic("packetsLost", ssrc), &value));
-  perf_test::PrintResult("video_misc", modifier, "packets_lost", value,
-                         "frames", false);
+  reporter.AddResult(kMetricPacketsLostFrames, value);
 
   EXPECT_TRUE(
       pc_dict.GetString(Statistic("googFrameWidthReceived", ssrc), &value));
-  perf_test::PrintResult("video_resolution", modifier, "goog_frame_width_recv",
-                         value, "pixels", false);
+  reporter.AddResult(kMetricGoogFrameWidthCount, value);
   EXPECT_TRUE(
       pc_dict.GetString(Statistic("googFrameHeightReceived", ssrc), &value));
-  perf_test::PrintResult("video_resolution", modifier, "goog_frame_height_recv",
-                         value, "pixels", false);
+  reporter.AddResult(kMetricGoogFrameHeightCount, value);
 
   EXPECT_TRUE(pc_dict.GetString(Statistic("googCurrentDelayMs", ssrc), &value));
-  perf_test::PrintResult(
-      "video_rx", modifier, "goog_current_delay_ms", value, "ms", false);
+  reporter.AddResult(kMetricGoogActualDelayMs, value);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googTargetDelayMs", ssrc), &value));
-  perf_test::PrintResult(
-      "video_rx", modifier, "goog_target_delay_ms", value, "ms", false);
+  reporter.AddResult(kMetricGoogTargetDelayMs, value);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googDecodeMs", ssrc), &value));
-  perf_test::PrintResult("video_rx", modifier, "goog_decode_ms", value, "ms",
-                         false);
+  reporter.AddResult(kMetricGoogDecodeTimeMs, value);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googMaxDecodeMs", ssrc), &value));
-  perf_test::PrintResult(
-      "video_rx", modifier, "goog_max_decode_ms", value, "ms", false);
+  reporter.AddResult(kMetricGoogMaxDecodeTimeMs, value);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googJitterBufferMs", ssrc), &value));
-  perf_test::PrintResult(
-      "video_rx", modifier, "goog_jitter_buffer_ms", value, "ms", false);
+  reporter.AddResult(kMetricGoogJitterBufferMs, value);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googRenderDelayMs", ssrc), &value));
-  perf_test::PrintResult(
-      "video_rx", modifier, "goog_render_delay_ms", value, "ms", false);
+  reporter.AddResult(kMetricGoogRenderDelayMs, value);
 }
 
 static std::string ExtractSsrcIdentifier(const std::string& key) {
@@ -195,30 +272,26 @@
 void PrintBweForVideoMetrics(const base::DictionaryValue& pc_dict,
                              const std::string& modifier,
                              const std::string& video_codec) {
-  std::string video_modifier =
-      video_codec.empty() ? modifier : modifier + "_" + video_codec;
+  std::string story = video_codec.empty() ? "baseline_story" : video_codec;
+  story += modifier;
   const std::string kBweStatsKey = "bweforvideo";
   std::string value;
+  auto reporter = SetUpBweReporter(story);
   ASSERT_TRUE(pc_dict.GetString(
       Statistic("googAvailableSendBandwidth", kBweStatsKey), &value));
-  perf_test::PrintResult("bwe_stats", video_modifier, "available_send_bw",
-                         value, "bit/s", false);
+  reporter.AddResult(kMetricAvailableSendBandwidthBitsPerS, value);
   ASSERT_TRUE(pc_dict.GetString(
       Statistic("googAvailableReceiveBandwidth", kBweStatsKey), &value));
-  perf_test::PrintResult("bwe_stats", video_modifier, "available_recv_bw",
-                         value, "bit/s", false);
+  reporter.AddResult(kMetricAvailableRecvBandwidthBitsPerS, value);
   ASSERT_TRUE(pc_dict.GetString(
       Statistic("googTargetEncBitrate", kBweStatsKey), &value));
-  perf_test::PrintResult("bwe_stats", video_modifier, "target_enc_bitrate",
-                         value, "bit/s", false);
+  reporter.AddResult(kMetricTargetEncodeBitrateBitsPerS, value);
   ASSERT_TRUE(pc_dict.GetString(
       Statistic("googActualEncBitrate", kBweStatsKey), &value));
-  perf_test::PrintResult("bwe_stats", video_modifier, "actual_enc_bitrate",
-                         value, "bit/s", false);
+  reporter.AddResult(kMetricActualEncodeBitrateBitsPerS, value);
   ASSERT_TRUE(pc_dict.GetString(
       Statistic("googTransmitBitrate", kBweStatsKey), &value));
-  perf_test::PrintResult("bwe_stats", video_modifier, "transmit_bitrate", value,
-                         "bit/s", false);
+  reporter.AddResult(kMetricTransmitBitrateBitsPerS, value);
 }
 
 void PrintMetricsForAllStreams(const base::DictionaryValue& pc_dict,
@@ -231,8 +304,8 @@
 void PrintMetricsForSendStreams(const base::DictionaryValue& pc_dict,
                                 const std::string& modifier,
                                 const std::string& video_codec) {
-  std::string video_modifier =
-      video_codec.empty() ? modifier : modifier + "_" + video_codec;
+  std::string story = video_codec.empty() ? "baseline_story" : video_codec;
+  story += modifier;
   const base::DictionaryValue* stats_dict;
   ASSERT_TRUE(pc_dict.GetDictionary("stats", &stats_dict));
   std::set<std::string> ssrc_identifiers = FindAllSsrcIdentifiers(*stats_dict);
@@ -240,16 +313,16 @@
   auto ssrc_iterator = ssrc_identifiers.begin();
   for (; ssrc_iterator != ssrc_identifiers.end(); ++ssrc_iterator) {
     const std::string& ssrc = *ssrc_iterator;
-    MaybePrintResultsForAudioSend(ssrc, pc_dict, modifier);
-    MaybePrintResultsForVideoSend(ssrc, pc_dict, video_modifier);
+    MaybePrintResultsForAudioSend(ssrc, pc_dict, story);
+    MaybePrintResultsForVideoSend(ssrc, pc_dict, story);
   }
 }
 
 void PrintMetricsForRecvStreams(const base::DictionaryValue& pc_dict,
                                 const std::string& modifier,
                                 const std::string& video_codec) {
-  std::string video_modifier =
-      video_codec.empty() ? modifier : modifier + "_" + video_codec;
+  std::string story = video_codec.empty() ? "baseline_story" : video_codec;
+  story += modifier;
   const base::DictionaryValue* stats_dict;
   ASSERT_TRUE(pc_dict.GetDictionary("stats", &stats_dict));
   std::set<std::string> ssrc_identifiers = FindAllSsrcIdentifiers(*stats_dict);
@@ -257,8 +330,8 @@
   auto ssrc_iterator = ssrc_identifiers.begin();
   for (; ssrc_iterator != ssrc_identifiers.end(); ++ssrc_iterator) {
     const std::string& ssrc = *ssrc_iterator;
-    MaybePrintResultsForAudioReceive(ssrc, pc_dict, modifier);
-    MaybePrintResultsForVideoReceive(ssrc, pc_dict, video_modifier);
+    MaybePrintResultsForAudioReceive(ssrc, pc_dict, story);
+    MaybePrintResultsForVideoReceive(ssrc, pc_dict, story);
   }
 }
 
diff --git a/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc b/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc
index 8eb632c10..b5fea966 100644
--- a/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc
@@ -7,17 +7,23 @@
 #include "base/strings/string_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
+#include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/media/webrtc/webrtc_browsertest_base.h"
 #include "chrome/browser/media/webrtc/webrtc_browsertest_common.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browsing_data_remover.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/browsing_data_remover_test_util.h"
 #include "media/audio/audio_device_description.h"
 #include "media/audio/audio_manager.h"
 #include "media/base/media_switches.h"
@@ -134,6 +140,30 @@
     EXPECT_TRUE(found_video_input);
   }
 
+  static void CheckEnumerationsAreDifferent(
+      const std::vector<MediaDeviceInfo>& devices,
+      const std::vector<MediaDeviceInfo>& devices2) {
+    for (auto& device : devices) {
+      auto it = std::find_if(devices2.begin(), devices2.end(),
+                             [&device](const MediaDeviceInfo& device_info) {
+                               return device.device_id == device_info.device_id;
+                             });
+      if (device.device_id == media::AudioDeviceDescription::kDefaultDeviceId ||
+          device.device_id ==
+              media::AudioDeviceDescription::kCommunicationsDeviceId) {
+        EXPECT_NE(it, devices2.end());
+      } else {
+        EXPECT_EQ(it, devices2.end());
+      }
+
+      it = std::find_if(devices2.begin(), devices2.end(),
+                        [&device](const MediaDeviceInfo& device_info) {
+                          return device.group_id == device_info.group_id;
+                        });
+      EXPECT_EQ(it, devices2.end());
+    }
+  }
+
   bool has_audio_output_devices_initialized_;
   bool has_audio_output_devices_;
 
@@ -178,7 +208,37 @@
 }
 
 IN_PROC_BROWSER_TEST_P(WebRtcGetMediaDevicesBrowserTest,
-                       DeviceIdEqualsGroupIdDiffersAcrossTabs) {
+                       DeviceIdSameGroupIdDiffersAfterReload) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
+  ui_test_utils::NavigateToURL(browser(), url);
+  content::WebContents* tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  std::vector<MediaDeviceInfo> devices;
+  EnumerateDevices(tab, &devices);
+
+  ui_test_utils::NavigateToURL(browser(), url);
+  std::vector<MediaDeviceInfo> devices2;
+  EnumerateDevices(tab, &devices2);
+
+  EXPECT_EQ(devices.size(), devices2.size());
+  for (auto& device : devices) {
+    auto it = std::find_if(devices2.begin(), devices2.end(),
+                           [&device](const MediaDeviceInfo& device_info) {
+                             return device.device_id == device_info.device_id;
+                           });
+    EXPECT_NE(it, devices2.end());
+
+    it = std::find_if(devices2.begin(), devices2.end(),
+                      [&device](const MediaDeviceInfo& device_info) {
+                        return device.group_id == device_info.group_id;
+                      });
+    EXPECT_EQ(it, devices2.end());
+  }
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetMediaDevicesBrowserTest,
+                       DeviceIdSameGroupIdDiffersAcrossTabs) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
   ui_test_utils::NavigateToURL(browser(), url);
@@ -211,6 +271,78 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_P(WebRtcGetMediaDevicesBrowserTest,
+                       DeviceIdDiffersAfterClearingCookies) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
+  ui_test_utils::NavigateToURL(browser(), url);
+  content::WebContents* tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  std::vector<MediaDeviceInfo> devices;
+  EnumerateDevices(tab, &devices);
+
+  auto* remover =
+      content::BrowserContext::GetBrowsingDataRemover(browser()->profile());
+  content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
+  remover->RemoveAndReply(
+      base::Time(), base::Time::Max(),
+      content::BrowsingDataRemover::DATA_TYPE_COOKIES,
+      content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
+      &completion_observer);
+  completion_observer.BlockUntilCompletion();
+
+  std::vector<MediaDeviceInfo> devices2;
+  EnumerateDevices(tab, &devices2);
+
+  EXPECT_EQ(devices.size(), devices2.size());
+  CheckEnumerationsAreDifferent(devices, devices2);
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetMediaDevicesBrowserTest,
+                       DeviceIdDiffersAcrossTabsWithCookiesDisabled) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
+  ui_test_utils::NavigateToURL(browser(), url);
+  CookieSettingsFactory::GetForProfile(browser()->profile())
+      ->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
+  content::WebContents* tab1 =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  std::vector<MediaDeviceInfo> devices;
+  EnumerateDevices(tab1, &devices);
+
+  chrome::AddTabAt(browser(), GURL(), -1, true);
+  ui_test_utils::NavigateToURL(browser(), url);
+  content::WebContents* tab2 =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  std::vector<MediaDeviceInfo> devices2;
+  EnumerateDevices(tab2, &devices2);
+
+  EXPECT_NE(tab1, tab2);
+  EXPECT_EQ(devices.size(), devices2.size());
+  CheckEnumerationsAreDifferent(devices, devices2);
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetMediaDevicesBrowserTest,
+                       DeviceIdDiffersSameTabAfterReloadWithCookiesDisabled) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
+  ui_test_utils::NavigateToURL(browser(), url);
+  CookieSettingsFactory::GetForProfile(browser()->profile())
+      ->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
+  content::WebContents* tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  std::vector<MediaDeviceInfo> devices;
+  EnumerateDevices(tab, &devices);
+
+  ui_test_utils::NavigateToURL(browser(), url);
+  tab = browser()->tab_strip_model()->GetActiveWebContents();
+  std::vector<MediaDeviceInfo> devices2;
+  EnumerateDevices(tab, &devices2);
+
+  EXPECT_EQ(devices.size(), devices2.size());
+  CheckEnumerationsAreDifferent(devices, devices2);
+}
+
 // We run these tests with the audio service both in and out of the the browser
 // process to have waterfall coverage while the feature rolls out. It should be
 // removed after launch.
diff --git a/chrome/browser/media/webrtc/webrtc_stats_perf_browsertest.cc b/chrome/browser/media/webrtc/webrtc_stats_perf_browsertest.cc
index 133b9fa..049fc2ce 100644
--- a/chrome/browser/media/webrtc/webrtc_stats_perf_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_stats_perf_browsertest.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/media/webrtc/webrtc_browsertest_common.h"
 #include "content/public/common/content_switches.h"
 #include "media/base/media_switches.h"
-#include "testing/perf/perf_test.h"
+#include "testing/perf/perf_result_reporter.h"
 #include "third_party/blink/public/common/features.h"
 
 namespace content {
@@ -26,6 +26,35 @@
 const char kInboundRtp[] = "inbound-rtp";
 const char kOutboundRtp[] = "outbound-rtp";
 
+constexpr int kBitsPerByte = 8;
+
+constexpr char kMetricPrefixAudioStats[] = "WebRtcAudioStats.";
+constexpr char kMetricPrefixVideoStats[] = "WebRtcVideoStats.";
+constexpr char kMetricPrefixGetStats[] = "WebRtcGetStats.";
+constexpr char kMetricSendRateBitsPerS[] = "send_rate";
+constexpr char kMetricReceiveRateBitsPerS[] = "receive_rate";
+constexpr char kMetricInvocationTimeMs[] = "invocation_time";
+
+perf_test::PerfResultReporter SetUpAudioReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixAudioStats, story);
+  reporter.RegisterFyiMetric(kMetricSendRateBitsPerS, "bits/s");
+  reporter.RegisterFyiMetric(kMetricReceiveRateBitsPerS, "bits/s");
+  return reporter;
+}
+
+perf_test::PerfResultReporter SetUpVideoReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixVideoStats, story);
+  reporter.RegisterFyiMetric(kMetricSendRateBitsPerS, "bits/s");
+  reporter.RegisterFyiMetric(kMetricReceiveRateBitsPerS, "bits/s");
+  return reporter;
+}
+
+perf_test::PerfResultReporter SetUpGetStatsReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixGetStats, story);
+  reporter.RegisterFyiMetric(kMetricInvocationTimeMs, "ms");
+  return reporter;
+}
+
 enum class GetStatsVariation {
   PROMISE_BASED,
   CALLBACK_BASED
@@ -200,13 +229,11 @@
           (audio_bytes_received_after - audio_bytes_received_before) /
           measure_duration_seconds;
 
-      std::string audio_codec_modifier = "_" + audio_codec;
-      perf_test::PrintResult(
-          "audio", audio_codec_modifier, "send_rate", audio_send_rate,
-          "bytes/second", false);
-      perf_test::PrintResult(
-          "audio", audio_codec_modifier, "receive_rate", audio_receive_rate,
-          "bytes/second", false);
+      auto reporter = SetUpAudioReporter(audio_codec);
+      reporter.AddResult(kMetricSendRateBitsPerS,
+                         audio_send_rate * kBitsPerByte);
+      reporter.AddResult(kMetricReceiveRateBitsPerS,
+                         audio_receive_rate * kBitsPerByte);
     }
     if (video_codec != kUseDefaultVideoCodec) {
       double video_bytes_sent_after = GetVideoBytesSent(report.get());
@@ -219,15 +246,14 @@
           (video_bytes_received_after - video_bytes_received_before) /
           measure_duration_seconds;
 
-      std::string video_codec_modifier =
-          "_" + (video_codec_print_modifier.empty()
-                     ? video_codec
-                     : video_codec_print_modifier);
-      perf_test::PrintResult("video", video_codec_modifier, "send_rate",
-                             video_send_rate, "bytes/second", false);
-      perf_test::PrintResult(
-          "video", video_codec_modifier, "receive_rate", video_receive_rate,
-          "bytes/second", false);
+      std::string story =
+          (video_codec_print_modifier.empty() ? video_codec
+                                              : video_codec_print_modifier);
+      auto reporter = SetUpVideoReporter(story);
+      reporter.AddResult(kMetricSendRateBitsPerS,
+                         video_send_rate * kBitsPerByte);
+      reporter.AddResult(kMetricReceiveRateBitsPerS,
+                         video_receive_rate * kBitsPerByte);
     }
 
     EndCall();
@@ -252,14 +278,9 @@
              MeasureGetStatsCallbackPerformance(right_tab_)) / 2.0;
         break;
     }
-    perf_test::PrintResult(
-        "getStats",
-        (variation == GetStatsVariation::PROMISE_BASED) ?
-            "_promise" : "_callback",
-        "invocation_time",
-        invocation_time,
-        "milliseconds",
-        false);
+    auto reporter = SetUpGetStatsReporter(
+        variation == GetStatsVariation::PROMISE_BASED ? "promise" : "callback");
+    reporter.AddResult(kMetricInvocationTimeMs, invocation_time);
 
     EndCall();
   }
diff --git a/chrome/browser/media/webrtc/webrtc_video_display_perf_browsertest.cc b/chrome/browser/media/webrtc/webrtc_video_display_perf_browsertest.cc
index 59e65740..1b90baa 100644
--- a/chrome/browser/media/webrtc/webrtc_video_display_perf_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_video_display_perf_browsertest.cc
@@ -21,7 +21,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "media/base/media_switches.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "testing/perf/perf_test.h"
+#include "testing/perf/perf_result_reporter.h"
 #include "third_party/blink/public/common/features.h"
 #include "ui/gl/gl_switches.h"
 
@@ -49,10 +49,42 @@
 static const char kVideoFrameSubmitterEventName[] = "VideoFrameSubmitter";
 
 static const char kEventMatchKey[] = "Timestamp";
-static const char kTestResultString[] = "TestVideoDisplayPerf";
 static const char kMainWebrtcTestHtmlPage[] =
     "/webrtc/webrtc_video_display_perf_test.html";
 
+constexpr char kMetricPrefixVideoDisplayPerf[] = "WebRtcVideoDisplayPerf.";
+constexpr char kMetricSkippedFramesPercent[] = "skipped_frames";
+constexpr char kMetricPassingToRenderAlgoLatencyUs[] =
+    "passing_to_render_algorithm_latency";
+constexpr char kMetricRenderAlgoLatencyUs[] = "render_algorithm_latency";
+constexpr char kMetricCompositorPickingFrameLatencyUs[] =
+    "compositor_picking_frame_latency";
+constexpr char kMetricCompositorResourcePreparationLatencyUs[] =
+    "compositor_resource_preparation_latency";
+constexpr char kMetricVsyncLatencyUs[] = "vsync_latency";
+constexpr char kMetricTotalControlledLatencyUs[] = "total_controlled_latency";
+constexpr char kMetricTotalLatencyUs[] = "total_latency";
+constexpr char kMetricPostDecodeToRasterLatencyUs[] =
+    "post_decode_to_raster_latency";
+constexpr char kMetricWebRtcDecodeLatencyUs[] = "webrtc_decode_latency";
+
+perf_test::PerfResultReporter SetUpReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixVideoDisplayPerf, story);
+  reporter.RegisterImportantMetric(kMetricSkippedFramesPercent, "percent");
+  reporter.RegisterImportantMetric(kMetricPassingToRenderAlgoLatencyUs, "us");
+  reporter.RegisterImportantMetric(kMetricRenderAlgoLatencyUs, "us");
+  reporter.RegisterImportantMetric(kMetricCompositorPickingFrameLatencyUs,
+                                   "us");
+  reporter.RegisterImportantMetric(
+      kMetricCompositorResourcePreparationLatencyUs, "us");
+  reporter.RegisterImportantMetric(kMetricVsyncLatencyUs, "us");
+  reporter.RegisterImportantMetric(kMetricTotalControlledLatencyUs, "us");
+  reporter.RegisterImportantMetric(kMetricTotalLatencyUs, "us");
+  reporter.RegisterImportantMetric(kMetricPostDecodeToRasterLatencyUs, "us");
+  reporter.RegisterImportantMetric(kMetricWebRtcDecodeLatencyUs, "us");
+  return reporter;
+}
+
 struct VideoDisplayPerfTestConfig {
   int width;
   int height;
@@ -60,36 +92,13 @@
   bool disable_render_smoothness_algorithm;
 };
 
-void CalculateMeanAndMax(const std::vector<double>& inputs,
-                         double* mean,
-                         double* std_dev,
-                         double* max) {
-  double sum = 0.0;
-  double sqr_sum = 0.0;
-  double max_so_far = 0.0;
-  size_t count = inputs.size();
-  for (const auto& input : inputs) {
-    sum += input;
-    sqr_sum += input * input;
-    max_so_far = std::max(input, max_so_far);
+std::string VectorToString(const std::vector<double>& values) {
+  std::string ret = "";
+  for (double val : values) {
+    ret += base::StringPrintf("%.0lf,", val);
   }
-  *max = max_so_far;
-  *mean = sum / count;
-  *std_dev = sqrt(std::max(0.0, count * sqr_sum - sum * sum)) / count;
-}
-
-void PrintMeanAndMax(const std::string& var_name,
-                     const std::string& name_modifier,
-                     const std::vector<double>& vars) {
-  double mean = 0.0;
-  double std_dev = 0.0;
-  double max = 0.0;
-  CalculateMeanAndMax(vars, &mean, &std_dev, &max);
-  perf_test::PrintResultMeanAndError(
-      kTestResultString, name_modifier, var_name + " Mean",
-      base::StringPrintf("%.0lf,%.0lf", mean, std_dev), "μs", true);
-  perf_test::PrintResult(kTestResultString, name_modifier, var_name + " Max",
-                         base::StringPrintf("%.0lf", max), "μs", true);
+  // Strip of trailing comma.
+  return ret.substr(0, ret.length() - 1);
 }
 
 void FindEvents(trace_analyzer::TraceAnalyzer* analyzer,
@@ -402,35 +411,36 @@
     std::string smoothness_indicator =
         test_config_.disable_render_smoothness_algorithm ? "_DisableSmoothness"
                                                          : "";
-    std::string name_modifier = base::StringPrintf(
+    std::string story = base::StringPrintf(
         "%s_%dp%df%s", video_codec.c_str(), test_config_.height,
         test_config_.fps, smoothness_indicator.c_str());
-    perf_test::PrintResult(
-        kTestResultString, name_modifier, "Skipped frames",
-        base::StringPrintf("%.2lf", skipped_frame_percentage_), "percent",
-        true);
+    auto reporter = SetUpReporter(story);
+    reporter.AddResult(kMetricSkippedFramesPercent,
+                       base::StringPrintf("%.2lf", skipped_frame_percentage_));
     // We identify intervals in a way that can help us easily bisect the source
     // of added latency in case of a regression. From these intervals, "Render
     // Algorithm" can take random amount of times based on the vsync cycle it is
     // closest to. Therefore, "Total Controlled Latency" refers to the total
     // times without that section for semi-consistent results.
-    PrintMeanAndMax("Passing to Render Algorithm Latency", name_modifier,
-                    enqueue_frame_durations_);
-    PrintMeanAndMax("Render Algorithm Latency", name_modifier,
-                    set_frame_durations_);
-    PrintMeanAndMax("Compositor Picking Frame Latency", name_modifier,
-                    get_frame_durations_);
-    PrintMeanAndMax("Compositor Resource Preparation Latency", name_modifier,
-                    resource_ready_durations_);
-    PrintMeanAndMax("Vsync Latency", name_modifier, vsync_durations_);
-    PrintMeanAndMax("Total Controlled Latency", name_modifier,
-                    total_controlled_durations_);
-    PrintMeanAndMax("Total Latency", name_modifier, total_durations_);
+    reporter.AddResultList(kMetricPassingToRenderAlgoLatencyUs,
+                           VectorToString(enqueue_frame_durations_));
+    reporter.AddResultList(kMetricRenderAlgoLatencyUs,
+                           VectorToString(set_frame_durations_));
+    reporter.AddResultList(kMetricCompositorPickingFrameLatencyUs,
+                           VectorToString(get_frame_durations_));
+    reporter.AddResultList(kMetricCompositorResourcePreparationLatencyUs,
+                           VectorToString(resource_ready_durations_));
+    reporter.AddResultList(kMetricVsyncLatencyUs,
+                           VectorToString(vsync_durations_));
+    reporter.AddResultList(kMetricTotalControlledLatencyUs,
+                           VectorToString(total_controlled_durations_));
+    reporter.AddResultList(kMetricTotalLatencyUs,
+                           VectorToString(total_durations_));
 
-    PrintMeanAndMax("Post-decode-to-raster latency", name_modifier,
-                    video_frame_submmitter_latencies_);
-    PrintMeanAndMax("WebRTC decode latency", name_modifier,
-                    webrtc_decode_latencies_);
+    reporter.AddResultList(kMetricPostDecodeToRasterLatencyUs,
+                           VectorToString(video_frame_submmitter_latencies_));
+    reporter.AddResultList(kMetricWebRtcDecodeLatencyUs,
+                           VectorToString(webrtc_decode_latencies_));
   }
 
   VideoDisplayPerfTestConfig test_config_;
diff --git a/chrome/browser/media/webrtc/webrtc_video_high_bitrate_browsertest.cc b/chrome/browser/media/webrtc/webrtc_video_high_bitrate_browsertest.cc
index 535da41..a8eebc6 100644
--- a/chrome/browser/media/webrtc/webrtc_video_high_bitrate_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_video_high_bitrate_browsertest.cc
@@ -12,7 +12,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
 #include "media/base/media_switches.h"
-#include "testing/perf/perf_test.h"
+#include "testing/perf/perf_result_reporter.h"
 #include "ui/gl/gl_switches.h"
 
 namespace {
@@ -22,6 +22,19 @@
 static const char kInboundRtp[] = "inbound-rtp";
 static const char kOutboundRtp[] = "outbound-rtp";
 
+constexpr int kBitsPerByte = 8;
+
+constexpr char kMetricPrefixHighBitrate[] = "WebRtcHighBitrateVideo.";
+constexpr char kMetricSendRateBitsPerS[] = "send_rate";
+constexpr char kMetricReceiveRateBitsPerS[] = "receive_rate";
+
+perf_test::PerfResultReporter SetUpReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixHighBitrate, story);
+  reporter.RegisterFyiMetric(kMetricSendRateBitsPerS, "bits/s");
+  reporter.RegisterFyiMetric(kMetricReceiveRateBitsPerS, "bits/s");
+  return reporter;
+}
+
 // Sums up "RTC[In/Out]boundRTPStreamStats.bytes_[received/sent]" values.
 double GetTotalRTPStreamBytes(content::TestStatsReportDictionary* report,
                               const char* type,
@@ -138,10 +151,10 @@
       (video_bytes_received_after - video_bytes_received_before) /
       duration_in_seconds;
 
-  perf_test::PrintResult("video", "", "send_rate", video_send_rate,
-                         "bytes/second", false);
-  perf_test::PrintResult("video", "", "receive_rate", video_receive_rate,
-                         "bytes/second", false);
+  auto reporter = SetUpReporter("baseline_story");
+  reporter.AddResult(kMetricSendRateBitsPerS, video_send_rate * kBitsPerByte);
+  reporter.AddResult(kMetricReceiveRateBitsPerS,
+                     video_receive_rate * kBitsPerByte);
 
   HangUp(left_tab);
   HangUp(right_tab);
diff --git a/chrome/browser/net/dns_probe_test_util.cc b/chrome/browser/net/dns_probe_test_util.cc
index 103a8eaa..88984b3 100644
--- a/chrome/browser/net/dns_probe_test_util.cc
+++ b/chrome/browser/net/dns_probe_test_util.cc
@@ -65,7 +65,7 @@
 void FakeHostResolver::MdnsListen(
     const net::HostPortPair& host,
     net::DnsQueryType query_type,
-    network::mojom::MdnsListenClientPtr response_client,
+    mojo::PendingRemote<network::mojom::MdnsListenClient> response_client,
     MdnsListenCallback callback) {
   NOTREACHED();
 }
@@ -89,7 +89,7 @@
 void HangingHostResolver::MdnsListen(
     const net::HostPortPair& host,
     net::DnsQueryType query_type,
-    network::mojom::MdnsListenClientPtr response_client,
+    mojo::PendingRemote<network::mojom::MdnsListenClient> response_client,
     MdnsListenCallback callback) {
   NOTREACHED();
 }
diff --git a/chrome/browser/net/dns_probe_test_util.h b/chrome/browser/net/dns_probe_test_util.h
index b76859ba..e9b1dbb 100644
--- a/chrome/browser/net/dns_probe_test_util.h
+++ b/chrome/browser/net/dns_probe_test_util.h
@@ -50,10 +50,11 @@
                    mojo::PendingRemote<network::mojom::ResolveHostClient>
                        pending_response_client) override;
 
-  void MdnsListen(const net::HostPortPair& host,
-                  net::DnsQueryType query_type,
-                  network::mojom::MdnsListenClientPtr response_client,
-                  MdnsListenCallback callback) override;
+  void MdnsListen(
+      const net::HostPortPair& host,
+      net::DnsQueryType query_type,
+      mojo::PendingRemote<network::mojom::MdnsListenClient> response_client,
+      MdnsListenCallback callback) override;
 
  private:
   mojo::Receiver<network::mojom::HostResolver> receiver_;
@@ -72,10 +73,11 @@
                    mojo::PendingRemote<network::mojom::ResolveHostClient>
                        response_client) override;
 
-  void MdnsListen(const net::HostPortPair& host,
-                  net::DnsQueryType query_type,
-                  network::mojom::MdnsListenClientPtr response_client,
-                  MdnsListenCallback callback) override;
+  void MdnsListen(
+      const net::HostPortPair& host,
+      net::DnsQueryType query_type,
+      mojo::PendingRemote<network::mojom::MdnsListenClient> response_client,
+      MdnsListenCallback callback) override;
 
  private:
   mojo::Receiver<network::mojom::HostResolver> receiver_;
diff --git a/chrome/browser/offline_pages/prefetch/gcm_token.cc b/chrome/browser/offline_pages/prefetch/gcm_token.cc
new file mode 100644
index 0000000..e7ccd74
--- /dev/null
+++ b/chrome/browser/offline_pages/prefetch/gcm_token.cc
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/offline_pages/prefetch/gcm_token.h"
+
+#include <map>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/gcm/instance_id/instance_id_profile_service_factory.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/gcm_driver/instance_id/instance_id_driver.h"
+#include "components/gcm_driver/instance_id/instance_id_profile_service.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+
+using instance_id::InstanceID;
+using instance_id::InstanceIDProfileService;
+using instance_id::InstanceIDProfileServiceFactory;
+
+namespace {
+
+const char kScopeGCM[] = "GCM";
+const char kProdSenderId[] = "864229763856";
+}  // namespace
+
+namespace offline_pages {
+
+void GetGCMToken(content::BrowserContext* context,
+                 const std::string& app_id,
+                 instance_id::InstanceID::GetTokenCallback callback) {
+  DCHECK(IsPrefetchingOfflinePagesEnabled());
+  // If the callback is canceled, |context| may not be alive anymore.
+  if (!callback.MaybeValid())
+    return;
+
+  DCHECK(context);
+  InstanceIDProfileService* service =
+      InstanceIDProfileServiceFactory::GetForProfile(context);
+  DCHECK(service);
+
+  InstanceID* instance_id = service->driver()->GetInstanceID(app_id);
+  if (!instance_id) {
+    DLOG(ERROR) << "GetInstanceID() returned null";
+    return;
+  }
+
+  instance_id->GetToken(kProdSenderId, kScopeGCM, /*options=*/{},
+                        /*flags=*/{}, std::move(callback));
+}
+
+}  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/prefetch/gcm_token.h b/chrome/browser/offline_pages/prefetch/gcm_token.h
new file mode 100644
index 0000000..8dc4a912
--- /dev/null
+++ b/chrome/browser/offline_pages/prefetch/gcm_token.h
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_GCM_TOKEN_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_GCM_TOKEN_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace offline_pages {
+
+// Returns a GCM token to be used for prefetching.
+void GetGCMToken(content::BrowserContext* context,
+                 const std::string& app_id,
+                 instance_id::InstanceID::GetTokenCallback callback);
+
+}  // namespace offline_pages
+
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_GCM_TOKEN_H_
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy_unittest.cc b/chrome/browser/offline_pages/prefetch/gcm_token_unittest.cc
similarity index 89%
rename from chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy_unittest.cc
rename to chrome/browser/offline_pages/prefetch/gcm_token_unittest.cc
index 06d8c1c..71e8d76c 100644
--- a/chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy_unittest.cc
+++ b/chrome/browser/offline_pages/prefetch/gcm_token_unittest.cc
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.h"
+#include "chrome/browser/offline_pages/prefetch/gcm_token.h"
 
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "build/build_config.h"
 #include "chrome/browser/gcm/gcm_profile_service_factory.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/gcm_driver/fake_gcm_profile_service.h"
@@ -42,14 +43,11 @@
   // Sync wrapper for async version.
   std::string GetToken();
 
-  PrefetchInstanceIDProxy* proxy() { return proxy_.get(); }
-
- private:
+ protected:
   void GetTokenCompleted(const std::string& token, InstanceID::Result result);
 
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile_;
-  std::unique_ptr<PrefetchInstanceIDProxy> proxy_;
 
   gcm::FakeGCMProfileService* gcm_profile_service_;
 
@@ -71,7 +69,6 @@
 
 void PrefetchInstanceIDProxyTest::SetUp() {
   scoped_feature_list_.InitAndEnableFeature(kPrefetchingOfflinePagesFeature);
-  proxy_ = std::make_unique<PrefetchInstanceIDProxy>(kAppIdForTest, &profile_);
   gcm_profile_service_ = static_cast<gcm::FakeGCMProfileService*>(
       gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
           &profile_, base::BindRepeating(&gcm::FakeGCMProfileService::Build)));
@@ -95,8 +92,9 @@
   token_.clear();
   result_ = InstanceID::UNKNOWN_ERROR;
 
-  proxy()->GetGCMToken(base::Bind(
-      &PrefetchInstanceIDProxyTest::GetTokenCompleted, base::Unretained(this)));
+  GetGCMToken(&profile_, kAppIdForTest,
+              base::Bind(&PrefetchInstanceIDProxyTest::GetTokenCompleted,
+                         base::Unretained(this)));
   WaitForAsyncOperation();
   return token_;
 }
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.cc b/chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.cc
deleted file mode 100644
index fd01d45..0000000
--- a/chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.h"
-
-#include <map>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/gcm/instance_id/instance_id_profile_service_factory.h"
-#include "components/gcm_driver/instance_id/instance_id.h"
-#include "components/gcm_driver/instance_id/instance_id_driver.h"
-#include "components/gcm_driver/instance_id/instance_id_profile_service.h"
-#include "components/offline_pages/core/offline_page_feature.h"
-
-using instance_id::InstanceID;
-using instance_id::InstanceIDProfileService;
-using instance_id::InstanceIDProfileServiceFactory;
-
-namespace {
-
-const char kScopeGCM[] = "GCM";
-const char kProdSenderId[] = "864229763856";
-
-}  // namespace
-
-namespace offline_pages {
-
-PrefetchInstanceIDProxy::PrefetchInstanceIDProxy(
-    const std::string& app_id,
-    content::BrowserContext* context)
-    : app_id_(app_id), context_(context) {}
-
-PrefetchInstanceIDProxy::~PrefetchInstanceIDProxy() = default;
-
-void PrefetchInstanceIDProxy::GetGCMToken(
-    InstanceID::GetTokenCallback callback) {
-  DCHECK(IsPrefetchingOfflinePagesEnabled());
-  if (!token_.empty()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&PrefetchInstanceIDProxy::GotGCMToken,
-                       weak_factory_.GetWeakPtr(), std::move(callback), token_,
-                       InstanceID::SUCCESS));
-    return;
-  }
-
-  InstanceIDProfileService* service =
-      InstanceIDProfileServiceFactory::GetForProfile(context_);
-  DCHECK(service);
-
-  InstanceID* instance_id = service->driver()->GetInstanceID(app_id_);
-  DCHECK(instance_id);
-
-  instance_id->GetToken(
-      kProdSenderId, kScopeGCM, std::map<std::string, std::string>(),
-      /*flags=*/{},
-      base::BindOnce(&PrefetchInstanceIDProxy::GotGCMToken,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void PrefetchInstanceIDProxy::GotGCMToken(InstanceID::GetTokenCallback callback,
-                                          const std::string& token,
-                                          InstanceID::Result result) {
-  DVLOG(1) << "Got an Instance ID token for GCM: " << token
-           << " with result: " << result;
-  std::move(callback).Run(token, result);
-}
-
-}  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.h b/chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.h
deleted file mode 100644
index daa680b8..0000000
--- a/chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.h
+++ /dev/null
@@ -1,50 +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 CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_PREFETCH_INSTANCE_ID_PROXY_H_
-#define CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_PREFETCH_INSTANCE_ID_PROXY_H_
-
-#include <string>
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "components/gcm_driver/instance_id/instance_id.h"
-#include "components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h"
-
-namespace content {
-class BrowserContext;
-}
-
-namespace offline_pages {
-
-// A factory that can create prefetching InstanceID tokens from a
-// BrowserContext, requesting the InstanceIDProfileService on demand.
-class PrefetchInstanceIDProxy : public PrefetchGCMAppHandler::TokenFactory {
- public:
-  PrefetchInstanceIDProxy(const std::string& app_id,
-                          content::BrowserContext* context);
-  ~PrefetchInstanceIDProxy() override;
-
-  // PrefetchGCMAppHandler::TokenFactory implementation.
-  void GetGCMToken(instance_id::InstanceID::GetTokenCallback callback) override;
-
- private:
-  void GotGCMToken(instance_id::InstanceID::GetTokenCallback callback,
-                   const std::string& token,
-                   instance_id::InstanceID::Result result);
-  std::string app_id_;
-  std::string token_;
-
-  // Unowned, the owner should make sure that this class does not outlive the
-  // browser context.
-  content::BrowserContext* context_;
-
-  base::WeakPtrFactory<PrefetchInstanceIDProxy> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(PrefetchInstanceIDProxy);
-};
-
-}  // namespace offline_pages
-
-#endif  // CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_PREFETCH_INSTANCE_ID_PROXY_H_
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
index de3fc62e..bab8e2a 100644
--- a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
+++ b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
@@ -20,9 +20,9 @@
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/ntp_snippets/content_suggestions_service_factory.h"
 #include "chrome/browser/offline_pages/offline_page_model_factory.h"
+#include "chrome/browser/offline_pages/prefetch/gcm_token.h"
 #include "chrome/browser/offline_pages/prefetch/offline_metrics_collector_impl.h"
 #include "chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.h"
-#include "chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.h"
 #include "chrome/browser/offline_pages/prefetch/thumbnail_fetcher_impl.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_key.h"
@@ -72,10 +72,6 @@
 }
 
 void OnProfileCreated(PrefetchServiceImpl* prefetch_service, Profile* profile) {
-  auto gcm_app_handler = std::make_unique<PrefetchGCMAppHandler>(
-      std::make_unique<PrefetchInstanceIDProxy>(kPrefetchingOfflinePagesAppId,
-                                                profile));
-  prefetch_service->SetPrefetchGCMHandler(std::move(gcm_app_handler));
   if (IsPrefetchingOfflinePagesEnabled()) {
     // Trigger an update of the cached GCM token. This needs to be post tasked
     // because otherwise leads to circular dependency between
@@ -86,8 +82,9 @@
     base::PostTask(
         FROM_HERE,
         {content::BrowserThread::UI, base::TaskPriority::BEST_EFFORT},
-        base::BindOnce(&PrefetchServiceImpl::RefreshGCMToken,
-                       prefetch_service->GetWeakPtr()));
+        base::BindOnce(&GetGCMToken, profile, kPrefetchingOfflinePagesAppId,
+                       base::BindOnce(&PrefetchServiceImpl::GCMTokenReceived,
+                                      prefetch_service->GetWeakPtr())));
   }
 
   SwitchToFullBrowserImageFetcher(prefetch_service, profile->GetProfileKey());
@@ -184,6 +181,7 @@
       std::move(prefetch_network_request_factory), offline_page_model,
       std::move(prefetch_store), std::move(suggested_articles_observer),
       std::move(prefetch_downloader), std::move(prefetch_importer),
+      std::make_unique<PrefetchGCMAppHandler>(),
       std::move(prefetch_background_task_handler), std::move(thumbnail_fetcher),
       image_fetcher, profile_key->GetPrefs());
 
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
index 4a8b8f1b..769c0b4 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
@@ -23,6 +23,7 @@
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
 #include "components/page_load_metrics/browser/page_load_metrics_util.h"
 #include "components/page_load_metrics/browser/resource_tracker.h"
+#include "components/page_load_metrics/common/page_end_reason.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
 #include "components/subresource_filter/core/common/common_features.h"
 #include "components/ukm/content/source_url_recorder.h"
@@ -443,8 +444,7 @@
   // remove it from storage. All child frames should be deleted by this point.
   if (ancestor_data && ancestor_data->root_frame_tree_node_id() ==
                            render_frame_host->GetFrameTreeNodeId()) {
-    RecordPerFrameHistogramsForAdTagging(*ancestor_data);
-    RecordPerFrameHistogramsForCpuUsage(*ancestor_data);
+    RecordPerFrameHistograms(*ancestor_data);
     ancestor_data->RecordAdFrameLoadUkmEvent(GetDelegate().GetSourceId());
     DCHECK(id_and_data->second != ad_frames_data_storage_.end());
     ad_frames_data_storage_.erase(id_and_data->second);
@@ -623,8 +623,7 @@
 void AdsPageLoadMetricsObserver::RecordHistograms(ukm::SourceId source_id) {
   // Record per-frame metrics for any existing frames.
   for (const auto& frame_data : ad_frames_data_storage_) {
-    RecordPerFrameHistogramsForAdTagging(frame_data);
-    RecordPerFrameHistogramsForCpuUsage(frame_data);
+    RecordPerFrameHistograms(frame_data);
     frame_data.RecordAdFrameLoadUkmEvent(source_id);
   }
 
@@ -638,9 +637,152 @@
   RecordAggregateHistogramsForAdTagging(
       FrameData::FrameVisibility::kAnyVisibility);
   RecordAggregateHistogramsForCpuUsage();
+  RecordAggregateHistogramsForHeavyAds();
   RecordPageResourceTotalHistograms(source_id);
 }
 
+void AdsPageLoadMetricsObserver::RecordAggregateHistogramsForCpuUsage() {
+  // If the page has an ad with the relevant visibility and non-zero bytes.
+  if (aggregate_ad_info_by_visibility_
+          [static_cast<int>(FrameData::FrameVisibility::kAnyVisibility)]
+              .num_frames == 0)
+    return;
+
+  // Get the relevant durations, set pre-interactive if the page never hit it.
+  base::TimeDelta total_duration =
+      GetDelegate().GetVisibilityTracker().GetForegroundDuration();
+  if (time_interactive_.is_null())
+    pre_interactive_duration_ = total_duration;
+
+  base::TimeDelta post_interactive_duration =
+      total_duration - pre_interactive_duration_;
+  DCHECK(total_duration >= base::TimeDelta());
+  DCHECK(pre_interactive_duration_ >= base::TimeDelta());
+  DCHECK(post_interactive_duration >= base::TimeDelta());
+
+  // Only record cpu usage aggregate data for the AnyVisibility suffix as these
+  // numbers do not change for different visibility types.
+  FrameData::FrameVisibility visibility =
+      FrameData::FrameVisibility::kAnyVisibility;
+
+  // Record the aggregate data, which is never considered activated.
+  base::TimeDelta task_duration_pre =
+      aggregate_frame_data_->GetInteractiveCpuUsage(
+          FrameData::InteractiveStatus::kPreInteractive);
+  base::TimeDelta task_duration_post =
+      aggregate_frame_data_->GetInteractiveCpuUsage(
+          FrameData::InteractiveStatus::kPostInteractive);
+  base::TimeDelta task_duration_total = task_duration_pre + task_duration_post;
+  if (total_duration.InMilliseconds() > 0) {
+    ADS_HISTOGRAM("Cpu.FullPage.TotalUsage", PAGE_LOAD_HISTOGRAM, visibility,
+                  task_duration_total);
+    ADS_HISTOGRAM("Cpu.FullPage.PeakWindowedPercent", UMA_HISTOGRAM_PERCENTAGE,
+                  visibility,
+                  aggregate_frame_data_->peak_windowed_cpu_percent());
+    if (aggregate_frame_data_->peak_window_start_time()) {
+      ADS_HISTOGRAM("Cpu.FullPage.PeakWindowStartTime", PAGE_LOAD_HISTOGRAM,
+                    visibility,
+                    aggregate_frame_data_->peak_window_start_time().value() -
+                        GetDelegate().GetNavigationStart());
+    }
+  }
+  if (pre_interactive_duration_.InMilliseconds() > 0) {
+    ADS_HISTOGRAM("Cpu.FullPage.TotalUsage.PreInteractive", PAGE_LOAD_HISTOGRAM,
+                  visibility, task_duration_pre);
+  }
+  if (post_interactive_duration.InMilliseconds() > 0) {
+    ADS_HISTOGRAM("Cpu.FullPage.TotalUsage.PostInteractive",
+                  PAGE_LOAD_HISTOGRAM, visibility, task_duration_post);
+  }
+}
+
+void AdsPageLoadMetricsObserver::RecordAggregateHistogramsForAdTagging(
+    FrameData::FrameVisibility visibility) {
+  if (aggregate_frame_data_->bytes() == 0)
+    return;
+
+  const auto& aggregate_ad_info =
+      aggregate_ad_info_by_visibility_[static_cast<int>(visibility)];
+
+  ADS_HISTOGRAM("FrameCounts.AdFrames.Total", UMA_HISTOGRAM_COUNTS_1000,
+                visibility, aggregate_ad_info.num_frames);
+
+  // Don't post UMA for pages that don't have ads.
+  if (aggregate_ad_info.num_frames == 0)
+    return;
+
+  ADS_HISTOGRAM("Bytes.NonAdFrames.Aggregate.Total2", PAGE_BYTES_HISTOGRAM,
+                visibility,
+                aggregate_frame_data_->bytes() - aggregate_ad_info.bytes);
+
+  ADS_HISTOGRAM("Bytes.FullPage.Total2", PAGE_BYTES_HISTOGRAM, visibility,
+                aggregate_frame_data_->bytes());
+  ADS_HISTOGRAM("Bytes.FullPage.Network", PAGE_BYTES_HISTOGRAM, visibility,
+                aggregate_frame_data_->network_bytes());
+
+  if (aggregate_frame_data_->bytes()) {
+    ADS_HISTOGRAM(
+        "Bytes.FullPage.Total.PercentAds2", UMA_HISTOGRAM_PERCENTAGE,
+        visibility,
+        aggregate_ad_info.bytes * 100 / aggregate_frame_data_->bytes());
+  }
+  if (aggregate_frame_data_->network_bytes()) {
+    ADS_HISTOGRAM("Bytes.FullPage.Network.PercentAds", UMA_HISTOGRAM_PERCENTAGE,
+                  visibility,
+                  aggregate_ad_info.network_bytes * 100 /
+                      aggregate_frame_data_->network_bytes());
+  }
+
+  ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Total2", PAGE_BYTES_HISTOGRAM,
+                visibility, aggregate_ad_info.bytes);
+  ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Network", PAGE_BYTES_HISTOGRAM,
+                visibility, aggregate_ad_info.network_bytes);
+
+  if (aggregate_ad_info.bytes) {
+    ADS_HISTOGRAM(
+        "Bytes.AdFrames.Aggregate.PercentNetwork2", UMA_HISTOGRAM_PERCENTAGE,
+        visibility,
+        aggregate_ad_info.network_bytes * 100 / aggregate_ad_info.bytes);
+  }
+
+  // Only record same origin and main frame totals for the AnyVisibility suffix
+  // as these numbers do not change for different visibility types.
+  if (visibility != FrameData::FrameVisibility::kAnyVisibility)
+    return;
+  ADS_HISTOGRAM("Bytes.FullPage.SameOrigin2", PAGE_BYTES_HISTOGRAM, visibility,
+                aggregate_frame_data_->same_origin_bytes());
+  if (aggregate_frame_data_->bytes()) {
+    ADS_HISTOGRAM("Bytes.FullPage.PercentSameOrigin2", UMA_HISTOGRAM_PERCENTAGE,
+                  visibility,
+                  aggregate_frame_data_->same_origin_bytes() * 100 /
+                      aggregate_frame_data_->bytes());
+  }
+  ADS_HISTOGRAM("Bytes.MainFrame.Network", PAGE_BYTES_HISTOGRAM, visibility,
+                main_frame_data_->network_bytes());
+  ADS_HISTOGRAM("Bytes.MainFrame.Total2", PAGE_BYTES_HISTOGRAM, visibility,
+                main_frame_data_->bytes());
+  ADS_HISTOGRAM("Bytes.MainFrame.Ads.Network", PAGE_BYTES_HISTOGRAM, visibility,
+                main_frame_data_->ad_network_bytes());
+  ADS_HISTOGRAM("Bytes.MainFrame.Ads.Total2", PAGE_BYTES_HISTOGRAM, visibility,
+                main_frame_data_->ad_bytes());
+}
+
+void AdsPageLoadMetricsObserver::RecordAggregateHistogramsForHeavyAds() {
+  if (!heavy_ad_on_page_)
+    return;
+
+  UMA_HISTOGRAM_BOOLEAN(
+      "PageLoad.Clients.Ads.HeavyAds.UserDidReload",
+      GetDelegate().GetPageEndReason() == page_load_metrics::END_RELOAD);
+}
+
+void AdsPageLoadMetricsObserver::RecordPerFrameHistograms(
+    const FrameData& ad_frame_data) {
+  RecordPerFrameHistogramsForCpuUsage(ad_frame_data);
+  RecordPerFrameHistogramsForAdTagging(ad_frame_data);
+  RecordPerFrameHistogramsForHeavyAds(ad_frame_data);
+}
+
 void AdsPageLoadMetricsObserver::RecordPerFrameHistogramsForCpuUsage(
     const FrameData& ad_frame_data) {
   // Get the relevant durations, set pre-interactive if the page never hit it.
@@ -737,61 +879,6 @@
   }
 }
 
-void AdsPageLoadMetricsObserver::RecordAggregateHistogramsForCpuUsage() {
-  // If the page has an ad with the relevant visibility and non-zero bytes.
-  if (aggregate_ad_info_by_visibility_
-          [static_cast<int>(FrameData::FrameVisibility::kAnyVisibility)]
-              .num_frames == 0)
-    return;
-
-  // Get the relevant durations, set pre-interactive if the page never hit it.
-  base::TimeDelta total_duration =
-      GetDelegate().GetVisibilityTracker().GetForegroundDuration();
-  if (time_interactive_.is_null())
-    pre_interactive_duration_ = total_duration;
-
-  base::TimeDelta post_interactive_duration =
-      total_duration - pre_interactive_duration_;
-  DCHECK(total_duration >= base::TimeDelta());
-  DCHECK(pre_interactive_duration_ >= base::TimeDelta());
-  DCHECK(post_interactive_duration >= base::TimeDelta());
-
-  // Only record cpu usage aggregate data for the AnyVisibility suffix as these
-  // numbers do not change for different visibility types.
-  FrameData::FrameVisibility visibility =
-      FrameData::FrameVisibility::kAnyVisibility;
-
-  // Record the aggregate data, which is never considered activated.
-  base::TimeDelta task_duration_pre =
-      aggregate_frame_data_->GetInteractiveCpuUsage(
-          FrameData::InteractiveStatus::kPreInteractive);
-  base::TimeDelta task_duration_post =
-      aggregate_frame_data_->GetInteractiveCpuUsage(
-          FrameData::InteractiveStatus::kPostInteractive);
-  base::TimeDelta task_duration_total = task_duration_pre + task_duration_post;
-  if (total_duration.InMilliseconds() > 0) {
-    ADS_HISTOGRAM("Cpu.FullPage.TotalUsage", PAGE_LOAD_HISTOGRAM, visibility,
-                  task_duration_total);
-    ADS_HISTOGRAM("Cpu.FullPage.PeakWindowedPercent", UMA_HISTOGRAM_PERCENTAGE,
-                  visibility,
-                  aggregate_frame_data_->peak_windowed_cpu_percent());
-    if (aggregate_frame_data_->peak_window_start_time()) {
-      ADS_HISTOGRAM("Cpu.FullPage.PeakWindowStartTime", PAGE_LOAD_HISTOGRAM,
-                    visibility,
-                    aggregate_frame_data_->peak_window_start_time().value() -
-                        GetDelegate().GetNavigationStart());
-    }
-  }
-  if (pre_interactive_duration_.InMilliseconds() > 0) {
-    ADS_HISTOGRAM("Cpu.FullPage.TotalUsage.PreInteractive", PAGE_LOAD_HISTOGRAM,
-                  visibility, task_duration_pre);
-  }
-  if (post_interactive_duration.InMilliseconds() > 0) {
-    ADS_HISTOGRAM("Cpu.FullPage.TotalUsage.PostInteractive",
-                  PAGE_LOAD_HISTOGRAM, visibility, task_duration_post);
-  }
-}
-
 void AdsPageLoadMetricsObserver::RecordPerFrameHistogramsForAdTagging(
     const FrameData& ad_frame_data) {
   if (!ad_frame_data.ShouldRecordFrameForMetrics())
@@ -840,83 +927,35 @@
     ADS_HISTOGRAM("FrameCounts.AdFrames.PerFrame.UserActivation",
                   UMA_HISTOGRAM_ENUMERATION, visibility,
                   ad_frame_data.user_activation_status());
+  }
+}
+
+void AdsPageLoadMetricsObserver::RecordPerFrameHistogramsForHeavyAds(
+    const FrameData& ad_frame_data) {
+  if (!ad_frame_data.ShouldRecordFrameForMetrics())
+    return;
+
+  // Record per frame histograms to the appropriate visibility prefixes.
+  for (const auto visibility : {FrameData::FrameVisibility::kAnyVisibility,
+                                ad_frame_data.visibility()}) {
     ADS_HISTOGRAM("HeavyAds.ComputedType2", UMA_HISTOGRAM_ENUMERATION,
                   visibility, ad_frame_data.heavy_ad_status());
     ADS_HISTOGRAM("HeavyAds.ComputedTypeWithThresholdNoise",
                   UMA_HISTOGRAM_ENUMERATION, visibility,
                   ad_frame_data.heavy_ad_status_with_noise());
   }
-}
 
-void AdsPageLoadMetricsObserver::RecordAggregateHistogramsForAdTagging(
-    FrameData::FrameVisibility visibility) {
-  if (aggregate_frame_data_->bytes() == 0)
+  // Only record the following histograms if the frame was a heavy ad.
+  if (ad_frame_data.heavy_ad_status_with_noise() ==
+      FrameData::HeavyAdStatus::kNone)
     return;
 
-  const auto& aggregate_ad_info =
-      aggregate_ad_info_by_visibility_[static_cast<int>(visibility)];
+  heavy_ad_on_page_ = true;
 
-  ADS_HISTOGRAM("FrameCounts.AdFrames.Total", UMA_HISTOGRAM_COUNTS_1000,
-                visibility, aggregate_ad_info.num_frames);
-
-  // Don't post UMA for pages that don't have ads.
-  if (aggregate_ad_info.num_frames == 0)
-    return;
-
-  ADS_HISTOGRAM("Bytes.NonAdFrames.Aggregate.Total2", PAGE_BYTES_HISTOGRAM,
-                visibility,
-                aggregate_frame_data_->bytes() - aggregate_ad_info.bytes);
-
-  ADS_HISTOGRAM("Bytes.FullPage.Total2", PAGE_BYTES_HISTOGRAM, visibility,
-                aggregate_frame_data_->bytes());
-  ADS_HISTOGRAM("Bytes.FullPage.Network", PAGE_BYTES_HISTOGRAM, visibility,
-                aggregate_frame_data_->network_bytes());
-
-  if (aggregate_frame_data_->bytes()) {
-    ADS_HISTOGRAM(
-        "Bytes.FullPage.Total.PercentAds2", UMA_HISTOGRAM_PERCENTAGE,
-        visibility,
-        aggregate_ad_info.bytes * 100 / aggregate_frame_data_->bytes());
-  }
-  if (aggregate_frame_data_->network_bytes()) {
-    ADS_HISTOGRAM("Bytes.FullPage.Network.PercentAds", UMA_HISTOGRAM_PERCENTAGE,
-                  visibility,
-                  aggregate_ad_info.network_bytes * 100 /
-                      aggregate_frame_data_->network_bytes());
-  }
-
-  ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Total2", PAGE_BYTES_HISTOGRAM,
-                visibility, aggregate_ad_info.bytes);
-  ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Network", PAGE_BYTES_HISTOGRAM,
-                visibility, aggregate_ad_info.network_bytes);
-
-  if (aggregate_ad_info.bytes) {
-    ADS_HISTOGRAM(
-        "Bytes.AdFrames.Aggregate.PercentNetwork2", UMA_HISTOGRAM_PERCENTAGE,
-        visibility,
-        aggregate_ad_info.network_bytes * 100 / aggregate_ad_info.bytes);
-  }
-
-  // Only record same origin and main frame totals for the AnyVisibility suffix
-  // as these numbers do not change for different visibility types.
-  if (visibility != FrameData::FrameVisibility::kAnyVisibility)
-    return;
-  ADS_HISTOGRAM("Bytes.FullPage.SameOrigin2", PAGE_BYTES_HISTOGRAM, visibility,
-                aggregate_frame_data_->same_origin_bytes());
-  if (aggregate_frame_data_->bytes()) {
-    ADS_HISTOGRAM("Bytes.FullPage.PercentSameOrigin2", UMA_HISTOGRAM_PERCENTAGE,
-                  visibility,
-                  aggregate_frame_data_->same_origin_bytes() * 100 /
-                      aggregate_frame_data_->bytes());
-  }
-  ADS_HISTOGRAM("Bytes.MainFrame.Network", PAGE_BYTES_HISTOGRAM, visibility,
-                main_frame_data_->network_bytes());
-  ADS_HISTOGRAM("Bytes.MainFrame.Total2", PAGE_BYTES_HISTOGRAM, visibility,
-                main_frame_data_->bytes());
-  ADS_HISTOGRAM("Bytes.MainFrame.Ads.Network", PAGE_BYTES_HISTOGRAM, visibility,
-                main_frame_data_->ad_network_bytes());
-  ADS_HISTOGRAM("Bytes.MainFrame.Ads.Total2", PAGE_BYTES_HISTOGRAM, visibility,
-                main_frame_data_->ad_bytes());
+  // Record whether the frame was removed prior to the page being unloaded.
+  UMA_HISTOGRAM_BOOLEAN(
+      "PageLoad.Clients.Ads.HeavyAds.FrameRemovedPriorToPageEnd",
+      GetDelegate().GetPageEndReason() == page_load_metrics::END_NONE);
 }
 
 void AdsPageLoadMetricsObserver::ProcessOngoingNavigationResource(
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
index 63a4ff33..3d62ef0 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
@@ -148,11 +148,16 @@
 
   void RecordPageResourceTotalHistograms(ukm::SourceId source_id);
   void RecordHistograms(ukm::SourceId source_id);
+  void RecordAggregateHistogramsForCpuUsage();
   void RecordAggregateHistogramsForAdTagging(
       FrameData::FrameVisibility visibility);
-  void RecordAggregateHistogramsForCpuUsage();
+  void RecordAggregateHistogramsForHeavyAds();
+
+  // Should be called on all frames prior to recording any aggregate histograms.
+  void RecordPerFrameHistograms(const FrameData& ad_frame_data);
   void RecordPerFrameHistogramsForAdTagging(const FrameData& ad_frame_data);
   void RecordPerFrameHistogramsForCpuUsage(const FrameData& ad_frame_data);
+  void RecordPerFrameHistogramsForHeavyAds(const FrameData& ad_frame_data);
 
   // Checks to see if a resource is waiting for a navigation in the given
   // RenderFrameHost to commit before it can be processed. If so, call
@@ -248,6 +253,9 @@
   // Whether the heavy ad blocklist feature is enabled.
   const bool heavy_ad_blocklist_enabled_;
 
+  // Whether there was a heavy ad on the page at some point.
+  bool heavy_ad_on_page_ = false;
+
   std::unique_ptr<HeavyAdThresholdNoiseProvider>
       heavy_ad_threshold_noise_provider_;
 
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
index ac0ac8cf..1707d598 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
@@ -31,8 +31,6 @@
 #include "components/subresource_filter/core/common/test_ruleset_utils.h"
 #include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
 #include "components/ukm/test_ukm_recorder.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/common/content_features.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -100,7 +98,9 @@
     : public subresource_filter::SubresourceFilterBrowserTest {
  public:
   AdsPageLoadMetricsObserverBrowserTest()
-      : subresource_filter::SubresourceFilterBrowserTest() {}
+      : subresource_filter::SubresourceFilterBrowserTest() {
+    scoped_feature_list_.InitAndEnableFeature(subresource_filter::kAdTagging);
+  }
   ~AdsPageLoadMetricsObserverBrowserTest() override {}
 
   std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter>
@@ -111,20 +111,6 @@
         web_contents);
   }
 
-  void SetUp() override {
-    std::vector<base::Feature> enabled = {subresource_filter::kAdTagging,
-                                          features::kSitePerProcess};
-    std::vector<base::Feature> disabled = {};
-
-    if (use_process_priority_) {
-      enabled.push_back(features::kUseFramePriorityInRenderProcessHost);
-    } else {
-      disabled.push_back(features::kUseFramePriorityInRenderProcessHost);
-    }
-    scoped_feature_list_.InitWithFeatures(enabled, disabled);
-    subresource_filter::SubresourceFilterBrowserTest::SetUp();
-  }
-
   void SetUpOnMainThread() override {
     SubresourceFilterBrowserTest::SetUpOnMainThread();
     SetRulesetWithRules(
@@ -134,9 +120,6 @@
              "expensive_animation_frame.html*")});
   }
 
- protected:
-  bool use_process_priority_ = false;
-
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 
@@ -1447,156 +1430,3 @@
   histogram_tester.ExpectTotalCount(
       "PageLoad.Clients.Ads.FrameCounts.AdFrames.Total", 0);
 }
-
-IN_PROC_BROWSER_TEST_F(
-    AdsPageLoadMetricsObserverBrowserTest,
-    RenderProcessHostNotBackgroundedWhenFramePriorityDisabled) {
-  // Used for assignment during testing.
-  auto frame1_pred = base::BindRepeating(&content::FrameMatchesName, "iframe1");
-  auto frame2_pred = base::BindRepeating(&content::FrameMatchesName, "iframe2");
-
-  // Navigate to a page with an iframe.  Make sure the two frames share a
-  // process and that process is not low priority.
-  ui_test_utils::NavigateToURL(
-      browser(), embedded_test_server()->GetURL("/two_iframes_blank.html"));
-  content::RenderFrameHost* main_frame = web_contents()->GetMainFrame();
-  content::RenderFrameHost* frame1 =
-      content::FrameMatchingPredicate(web_contents(), frame1_pred);
-  content::RenderFrameHost* frame2 =
-      content::FrameMatchingPredicate(web_contents(), frame2_pred);
-  EXPECT_EQ(main_frame->GetProcess(), frame1->GetProcess());
-  EXPECT_EQ(main_frame->GetProcess(), frame2->GetProcess());
-  EXPECT_FALSE(main_frame->GetProcess()->IsProcessBackgrounded());
-
-  // Navigate iframe1 to a cross-origin non-ad frame.  It should be on a
-  // different process, but still not be low priority.
-  NavigateIframeToURL(
-      web_contents(), "iframe1",
-      embedded_test_server()->GetURL("a.com", "/iframe_blank.html"));
-  frame1 = content::FrameMatchingPredicate(web_contents(), frame1_pred);
-  EXPECT_NE(main_frame->GetProcess(), frame1->GetProcess());
-  EXPECT_FALSE(main_frame->GetProcess()->IsProcessBackgrounded());
-  EXPECT_FALSE(frame1->GetProcess()->IsProcessBackgrounded());
-
-  // Navigate iframe1 to an ad on its current domain.  It should have the same
-  // process host but because the feature is turned off, not be low priority.
-  content::DOMMessageQueue message_queue1(web_contents());
-  NavigateIframeToURL(
-      web_contents(), "iframe1",
-      embedded_test_server()->GetURL(
-          "a.com", "/ads_observer/expensive_animation_frame.html?delay=0"));
-  WaitForRAF(&message_queue1);
-  EXPECT_EQ(frame1->GetProcess(),
-            content::FrameMatchingPredicate(web_contents(), frame1_pred)
-                ->GetProcess());
-  EXPECT_FALSE(main_frame->GetProcess()->IsProcessBackgrounded());
-  EXPECT_FALSE(frame1->GetProcess()->IsProcessBackgrounded());
-}
-
-class AdsPageLoadMetricsObserverWithBackgroundingBrowserTest
-    : public AdsPageLoadMetricsObserverBrowserTest {
- public:
-  AdsPageLoadMetricsObserverWithBackgroundingBrowserTest()
-      : AdsPageLoadMetricsObserverBrowserTest() {
-    use_process_priority_ = true;
-  }
-  ~AdsPageLoadMetricsObserverWithBackgroundingBrowserTest() override {}
-};
-
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverWithBackgroundingBrowserTest,
-                       RenderProcessHostBackgroundedForAd) {
-  // Used for assignment during testing.
-  auto frame1_pred = base::BindRepeating(&content::FrameMatchesName, "iframe1");
-  auto frame2_pred = base::BindRepeating(&content::FrameMatchesName, "iframe2");
-
-  // Navigate to a page with an iframe.  Make sure the two frames share a
-  // process and that process is not low priority.
-  ui_test_utils::NavigateToURL(
-      browser(), embedded_test_server()->GetURL("/two_iframes_blank.html"));
-  content::RenderFrameHost* main_frame = web_contents()->GetMainFrame();
-  content::RenderFrameHost* frame1 =
-      content::FrameMatchingPredicate(web_contents(), frame1_pred);
-  content::RenderFrameHost* frame2 =
-      content::FrameMatchingPredicate(web_contents(), frame2_pred);
-  EXPECT_EQ(main_frame->GetProcess(), frame1->GetProcess());
-  EXPECT_EQ(main_frame->GetProcess(), frame2->GetProcess());
-  EXPECT_FALSE(main_frame->GetProcess()->IsProcessBackgrounded());
-
-  // Navigate iframe1 to a cross-origin non-ad frame.  It should be on a
-  // different process, but still not be low priority.
-  NavigateIframeToURL(
-      web_contents(), "iframe1",
-      embedded_test_server()->GetURL("a.com", "/iframe_blank.html"));
-  frame1 = content::FrameMatchingPredicate(web_contents(), frame1_pred);
-  EXPECT_NE(main_frame->GetProcess(), frame1->GetProcess());
-  EXPECT_FALSE(main_frame->GetProcess()->IsProcessBackgrounded());
-  EXPECT_FALSE(frame1->GetProcess()->IsProcessBackgrounded());
-
-  // Navigate iframe1 to an ad on its current domain.  It should have the
-  // same process host but now be low priority.
-  content::DOMMessageQueue message_queue1(web_contents());
-  NavigateIframeToURL(
-      web_contents(), "iframe1",
-      embedded_test_server()->GetURL(
-          "a.com", "/ads_observer/expensive_animation_frame.html?delay=0"));
-  WaitForRAF(&message_queue1);
-  EXPECT_EQ(frame1->GetProcess(),
-            content::FrameMatchingPredicate(web_contents(), frame1_pred)
-                ->GetProcess());
-  EXPECT_FALSE(main_frame->GetProcess()->IsProcessBackgrounded());
-  EXPECT_TRUE(frame1->GetProcess()->IsProcessBackgrounded());
-
-  // Navigate the iframe2 to a non-ad on the same domain as iframe1.  Make sure
-  // that they get assigned the same process and that it's not low priority.
-  NavigateIframeToURL(
-      web_contents(), "iframe2",
-      embedded_test_server()->GetURL("a.com", "/iframe_blank.html"));
-  frame2 = content::FrameMatchingPredicate(web_contents(), frame2_pred);
-  EXPECT_EQ(frame1->GetProcess(), frame2->GetProcess());
-  EXPECT_FALSE(main_frame->GetProcess()->IsProcessBackgrounded());
-  EXPECT_FALSE(frame1->GetProcess()->IsProcessBackgrounded());
-
-  // Delete iframe2, make sure that iframe1 is now low priority, as it now only
-  // has ads assigned to it.
-  EXPECT_TRUE(content::ExecuteScriptWithoutUserGesture(
-      web_contents(),
-      "var frame = document.getElementById('iframe2'); "
-      "frame.parentNode.removeChild(frame);"));
-  EXPECT_TRUE(frame1->GetProcess()->IsProcessBackgrounded());
-
-  // Navigate the subframe to a non-ad on its current domain.  Even though this
-  // is a non-ad, the frame is still identified as an ad because it was
-  // previously identified as an ad by SubresourceFilterThrottle.
-  NavigateIframeToURL(
-      web_contents(), "iframe1",
-      embedded_test_server()->GetURL("a.com", "/iframe_blank.html"));
-  EXPECT_EQ(frame1->GetProcess(),
-            content::FrameMatchingPredicate(web_contents(), frame1_pred)
-                ->GetProcess());
-  EXPECT_FALSE(main_frame->GetProcess()->IsProcessBackgrounded());
-  EXPECT_TRUE(frame1->GetProcess()->IsProcessBackgrounded());
-
-  // Navigate the subframe to an ad on the original domain.  It should now have
-  // the same process as the main frame, but not be low priority because it
-  // shares a process with a non-ad frame (the main frame).
-  content::DOMMessageQueue message_queue2(web_contents());
-  NavigateIframeToURL(
-      web_contents(), "iframe1",
-      embedded_test_server()->GetURL(
-          "/ads_observer/expensive_animation_frame.html?delay=0"));
-  WaitForRAF(&message_queue2);
-  frame1 = content::FrameMatchingPredicate(web_contents(), frame1_pred);
-  EXPECT_EQ(main_frame->GetProcess(), frame1->GetProcess());
-  EXPECT_FALSE(frame1->GetProcess()->IsProcessBackgrounded());
-
-  // Navigate the subframe to a non-ad on a different domain.  Even though this
-  // is a non-ad, the frame is still identified as an ad because it was
-  // previously identified as an ad by SubresourceFilterThrottle.
-  NavigateIframeToURL(
-      web_contents(), "iframe1",
-      embedded_test_server()->GetURL("b.com", "/iframe_blank.html"));
-  frame1 = content::FrameMatchingPredicate(web_contents(), frame1_pred);
-  EXPECT_NE(main_frame->GetProcess(), frame1->GetProcess());
-  EXPECT_FALSE(main_frame->GetProcess()->IsProcessBackgrounded());
-  EXPECT_TRUE(frame1->GetProcess()->IsProcessBackgrounded());
-}
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index aa11190..30464023 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -579,7 +579,13 @@
     auto observer = std::make_unique<AdsPageLoadMetricsObserver>(
         clock_.get(), test_blocklist_.get());
     ads_observer_ = observer.get();
+
+    // Mock the noise provider to make tests deterministic. Tests can override
+    // this again to test non-zero noise.
+    ads_observer_->SetHeavyAdThresholdNoiseProviderForTesting(
+        std::make_unique<MockNoiseProvider>(0 /* noise */));
     tracker->AddObserver(std::move(observer));
+
     // Swap out the ui::ScopedVisibilityTracker to use the test clock.
     if (clock_) {
       ui::ScopedVisibilityTracker visibility_tracker(clock_.get(), true);
@@ -1671,10 +1677,6 @@
   OverrideVisibilityTrackerWithMockClock();
 
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-
-  OverrideHeavyAdNoiseProvider(
-      std::make_unique<MockNoiseProvider>(0 /* network noise */));
-
   RenderFrameHost* ad_frame_none =
       CreateAndNavigateSubFrame(kAdUrl, main_frame);
   RenderFrameHost* ad_frame_net = CreateAndNavigateSubFrame(kAdUrl, main_frame);
@@ -1734,6 +1736,10 @@
 
   histogram_tester().ExpectTotalCount(
       SuffixedHistogram("HeavyAds.InterventionType2"), 0);
+
+  // There were heavy ads on the page and the page was navigated not reloaded.
+  histogram_tester().ExpectUniqueSample(
+      SuffixedHistogram("HeavyAds.UserDidReload"), false, 1);
 }
 
 TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdNetworkUsage_InterventionFired) {
@@ -1741,9 +1747,6 @@
   feature_list.InitAndEnableFeature(features::kHeavyAdIntervention);
 
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-
-  OverrideHeavyAdNoiseProvider(
-      std::make_unique<MockNoiseProvider>(0 /* network noise */));
   RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
 
   // Load just under the threshold amount of bytes.
@@ -1971,6 +1974,100 @@
       FrameData::HeavyAdStatus::kNone, 1);
 }
 
+TEST_F(AdsPageLoadMetricsObserverTest,
+       HeavyAdPageNavigated_FrameMarkedAsNotRemoved) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kHeavyAdIntervention);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  OverrideHeavyAdNoiseProvider(
+      std::make_unique<MockNoiseProvider>(0 /* network noise */));
+  RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
+
+  // Add enough data to trigger the intervention.
+  ResourceDataUpdate(ad_frame, ResourceCached::kNotCached,
+                     (heavy_ad_thresholds::kMaxNetworkBytes / 1024) + 1);
+
+  NavigateMainFrame(kNonAdUrl);
+
+  histogram_tester().ExpectUniqueSample(
+      SuffixedHistogram("HeavyAds.FrameRemovedPriorToPageEnd"), false, 1);
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest,
+       HeavyAdFrameRemoved_FrameMarkedAsRemoved) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(features::kHeavyAdIntervention);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  OverrideHeavyAdNoiseProvider(
+      std::make_unique<MockNoiseProvider>(0 /* network noise */));
+  RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
+
+  // Add enough data to trigger the intervention.
+  ResourceDataUpdate(ad_frame, ResourceCached::kNotCached,
+                     (heavy_ad_thresholds::kMaxNetworkBytes / 1024) + 1);
+
+  // Delete the root ad frame.
+  content::RenderFrameHostTester::For(ad_frame)->Detach();
+
+  NavigateMainFrame(kNonAdUrl);
+
+  histogram_tester().ExpectUniqueSample(
+      SuffixedHistogram("HeavyAds.FrameRemovedPriorToPageEnd"), true, 1);
+}
+
+// Verifies when a user reloads a page with a heavy ad we log it to metrics.
+TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdPageReload_MetricsRecorded) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kHeavyAdIntervention);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  OverrideHeavyAdNoiseProvider(
+      std::make_unique<MockNoiseProvider>(0 /* network noise */));
+  RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
+
+  // Add enough data to trigger the intervention.
+  ResourceDataUpdate(ad_frame, ResourceCached::kNotCached,
+                     (heavy_ad_thresholds::kMaxNetworkBytes / 1024) + 1);
+
+  // Reload the page.
+  NavigationSimulator::Reload(web_contents());
+
+  histogram_tester().ExpectUniqueSample(
+      SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"),
+      FrameData::HeavyAdStatus::kNetwork, 1);
+  histogram_tester().ExpectUniqueSample(
+      SuffixedHistogram("HeavyAds.UserDidReload"), true, 1);
+}
+
+// Verifies when there is no heavy ad on the page, we do not record aggregate
+// heavy ad metrics.
+TEST_F(AdsPageLoadMetricsObserverTest,
+       HeavyAdsNoHeavyAdFrame_AggregateHistogramsNotRecorded) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kHeavyAdIntervention);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  OverrideHeavyAdNoiseProvider(
+      std::make_unique<MockNoiseProvider>(0 /* network noise */));
+  RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
+
+  // Don't load enough to reach the heavy ad threshold.
+  ResourceDataUpdate(ad_frame, ResourceCached::kNotCached,
+                     (heavy_ad_thresholds::kMaxNetworkBytes / 1024) - 1);
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  histogram_tester().ExpectTotalCount(
+      SuffixedHistogram("HeavyAds.UserDidReload"), 0);
+}
+
 TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdBlocklistFull_NotFired) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(features::kHeavyAdIntervention);
@@ -2003,9 +2100,6 @@
     blocklist()->AddEntry(GURL(kNonAdUrl).host(), true, 0);
 
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-
-  OverrideHeavyAdNoiseProvider(
-      std::make_unique<MockNoiseProvider>(0 /* network noise */));
   RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
 
   // Add enough data to trigger the intervention.
@@ -2034,9 +2128,6 @@
     blocklist()->AddEntry(GURL(kNonAdUrl).host(), true, 0);
 
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-
-  OverrideHeavyAdNoiseProvider(
-      std::make_unique<MockNoiseProvider>(0 /* network noise */));
   RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
 
   // Add enough data to trigger the intervention.
diff --git a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h
index e0750b3..59e8bccd 100644
--- a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h
@@ -8,7 +8,7 @@
 #include <jni.h>
 
 #include "base/macros.h"
-#include "chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h"
+#include "components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h"
 #include "components/page_load_metrics/browser/page_load_metrics_observer.h"
 
 namespace network {
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
index 0ad9720..8170d66 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
-#include "chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h"
 #include "chrome/browser/prerender/prerender_final_status.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h
index 327a41b..0a32f133 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h
@@ -9,7 +9,7 @@
 #include "base/metrics/ukm_source_id.h"
 #include "base/optional.h"
 #include "base/time/time.h"
-#include "chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h"
+#include "components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h"
 #include "components/page_load_metrics/browser/page_load_metrics_observer.h"
 #include "net/http/http_response_info.h"
 #include "services/metrics/public/cpp/ukm_source.h"
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
index 1efe4f7..4fb9319 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -24,7 +24,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h"
-#include "chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h"
@@ -56,6 +55,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
+#include "components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h"
 #include "components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h"
 #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index 8cef33a..9bb1490 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h"
-#include "chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/data_use_metrics_observer.h"
@@ -99,7 +98,6 @@
   if (!IsPrerendering()) {
     tracker->AddObserver(std::make_unique<AbortsPageLoadMetricsObserver>());
     tracker->AddObserver(std::make_unique<AMPPageLoadMetricsObserver>());
-    tracker->AddObserver(std::make_unique<CorePageLoadMetricsObserver>());
     tracker->AddObserver(
         std::make_unique<
             data_reduction_proxy::DataReductionProxyMetricsObserver>());
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 047b2168..460e921 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -68,6 +68,7 @@
 #include "components/translate/core/browser/translate_manager.h"
 #include "components/ukm/content/source_url_recorder.h"
 #include "components/version_info/version_info.h"
+#include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/navigation_entry.h"
@@ -1101,6 +1102,18 @@
   if (!instance)
     return;
 
+  // Disable BackForwardCache for this page.
+  // This is necessary because ContentCredentialManager::DisconnectBinding()
+  // will be called when the page is navigated away from, leaving it
+  // in an unusable state if the page is restored from the BackForwardCache.
+  //
+  // It looks like in order to remove this workaround, we probably just need to
+  // make the CredentialManager mojo API rebind on the renderer side when the
+  // next call is made, if it has become disconnected.
+  // TODO(https://crbug.com/1015358): Remove this workaround.
+  content::BackForwardCache::DisableForRenderFrameHost(
+      render_frame_host, "ChromePasswordManagerClient::BindCredentialManager");
+
   instance->content_credential_manager_.BindRequest(std::move(receiver));
 }
 
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 32800e40..48ff19e 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -4004,20 +4004,37 @@
   CheckThatCredentialsStored("temp", "random");
 }
 
+// Test that if the credentials API is used, it makes the page ineligible for
+// caching in the BackForwardCache.
+//
+// See where content::BackForwardCache::DisableForRenderFrameHost is called
+// in chrome_password_manager_client.cc for explanation.
 IN_PROC_BROWSER_TEST_F(PasswordManagerBackForwardCacheBrowserTest,
-                       CallAPIOnRestoredPage) {
+                       NotCachedIfCredentialsAPIUsed) {
   // Navigate to a page with a password form.
   NavigateToFile("/password/password_form.html");
   content::RenderFrameHost* rfh = WebContents()->GetMainFrame();
   content::RenderFrameDeletedObserver rfh_deleted_observer(rfh);
 
-  // Make sure the password manager API works.
+  // Use the password manager API, this should make the page uncacheable.
   EXPECT_TRUE(IsGetCredentialsSuccessful());
 
-  // Navigate away so that the password form page is stored in the cache.
+  // Navigate away.
   EXPECT_TRUE(NavigateToURL(
       WebContents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
-  EXPECT_FALSE(rfh_deleted_observer.deleted());
+  // The page should not have been cached.
+  rfh_deleted_observer.WaitUntilDeleted();
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordManagerBackForwardCacheBrowserTest,
+                       CredentialsAPIOnlyCalledOnRestoredPage) {
+  // Navigate to a page with a password form.
+  NavigateToFile("/password/password_form.html");
+  content::RenderFrameHost* rfh = WebContents()->GetMainFrame();
+
+  // Navigate away.
+  EXPECT_TRUE(NavigateToURL(
+      WebContents(), embedded_test_server()->GetURL("b.com", "/title1.html")));
   EXPECT_TRUE(content::IsInBackForwardCache(rfh));
 
   // Restore the cached page.
@@ -4025,10 +4042,12 @@
   EXPECT_TRUE(WaitForLoadStop(WebContents()));
   EXPECT_EQ(rfh, WebContents()->GetMainFrame());
 
-  // Make sure the password manager API still works now that the page has been
-  // stored/restored.
-  // TODO(https://crbug.com/1012707): This is currently broken.
-  EXPECT_FALSE(IsGetCredentialsSuccessful());
+  // Make sure the password manager API works. Since it was never connected, it
+  // shouldn't have been affected by the
+  // ContentCredentialManager::DisconnectBinding call in
+  // ChromePasswordManagerClient::DidFinishNavigation, (this GetCredentials call
+  // will establish the mojo connection for the first time).
+  EXPECT_TRUE(IsGetCredentialsSuccessful());
 }
 
 }  // namespace
diff --git a/chrome/browser/performance_manager/decorators/frame_priority_decorator.cc b/chrome/browser/performance_manager/decorators/frame_priority_decorator.cc
new file mode 100644
index 0000000..2ad64764
--- /dev/null
+++ b/chrome/browser/performance_manager/decorators/frame_priority_decorator.cc
@@ -0,0 +1,69 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/performance_manager/decorators/frame_priority_decorator.h"
+
+#include "components/performance_manager/graph/frame_node_impl.h"
+#include "components/performance_manager/graph/node_attached_data_impl.h"
+
+namespace performance_manager {
+
+// Helper class providing access to FrameNodeImpl::accepted_vote_.
+class FramePriorityAccess {
+ public:
+  static frame_priority::AcceptedVote* GetAcceptedVote(
+      FrameNodeImpl* frame_node) {
+    return &frame_node->accepted_vote_;
+  }
+};
+
+namespace frame_priority {
+
+FramePriorityDecorator::FramePriorityDecorator() : factory_(this) {}
+FramePriorityDecorator::~FramePriorityDecorator() = default;
+
+VotingChannel FramePriorityDecorator::GetVotingChannel() {
+  DCHECK_EQ(0u, factory_.voting_channels_issued());
+  auto channel = factory_.BuildVotingChannel();
+  voter_id_ = channel.voter_id();
+  return channel;
+}
+
+VoteReceipt FramePriorityDecorator::SubmitVote(VoterId voter_id,
+                                               const Vote& vote) {
+  DCHECK_EQ(voter_id_, voter_id);
+  auto* frame_node = FrameNodeImpl::FromNode(vote.frame_node());
+  auto* accepted_vote = FramePriorityAccess::GetAcceptedVote(frame_node);
+  DCHECK(!accepted_vote->IsValid());
+  *accepted_vote = AcceptedVote(this, voter_id, vote);
+  frame_node->SetPriorityAndReason(
+      PriorityAndReason(vote.priority(), vote.reason()));
+  return accepted_vote->IssueReceipt();
+}
+
+VoteReceipt FramePriorityDecorator::ChangeVote(VoteReceipt receipt,
+                                               AcceptedVote* old_vote,
+                                               const Vote& new_vote) {
+  auto* frame_node = FrameNodeImpl::FromNode(new_vote.frame_node());
+  auto* accepted_vote = FramePriorityAccess::GetAcceptedVote(frame_node);
+  DCHECK_EQ(accepted_vote, old_vote);
+  DCHECK(accepted_vote->IsValid());
+  accepted_vote->UpdateVote(new_vote);
+  frame_node->SetPriorityAndReason(
+      PriorityAndReason(new_vote.priority(), new_vote.reason()));
+  return receipt;
+}
+
+void FramePriorityDecorator::VoteInvalidated(AcceptedVote* vote) {
+  auto* frame_node = FrameNodeImpl::FromNode(vote->vote().frame_node());
+  auto* accepted_vote = FramePriorityAccess::GetAcceptedVote(frame_node);
+  DCHECK_EQ(accepted_vote, vote);
+  DCHECK(!accepted_vote->IsValid());
+  // Update the priority by falling back to the default priority.
+  frame_node->SetPriorityAndReason(PriorityAndReason(
+      base::TaskPriority::LOWEST, FrameNodeImpl::kDefaultPriorityReason));
+}
+
+}  // namespace frame_priority
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/decorators/frame_priority_decorator.h b/chrome/browser/performance_manager/decorators/frame_priority_decorator.h
new file mode 100644
index 0000000..ca4cb13e2
--- /dev/null
+++ b/chrome/browser/performance_manager/decorators/frame_priority_decorator.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_DECORATORS_FRAME_PRIORITY_DECORATOR_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_DECORATORS_FRAME_PRIORITY_DECORATOR_H_
+
+#include "components/performance_manager/public/graph/frame_node.h"
+#include "components/performance_manager/public/graph/graph.h"
+
+namespace performance_manager {
+namespace frame_priority {
+
+// The FramePriorityDecorator acts as the root node of a hierarchy of frame
+// priority voters. It is responsible for taking aggregated votes and applying
+// them to the actual frame nodes in a graph.
+class FramePriorityDecorator : public GraphOwnedDefaultImpl,
+                               public VoteConsumer {
+ public:
+  FramePriorityDecorator();
+  ~FramePriorityDecorator() override;
+
+  // Issues a voting channel (registers the sole incoming voter).
+  VotingChannel GetVotingChannel();
+
+ protected:
+  // VoteConsumer implementation:
+  VoteReceipt SubmitVote(VoterId voter_id, const Vote& vote) override;
+  VoteReceipt ChangeVote(VoteReceipt receipt,
+                         AcceptedVote* old_vote,
+                         const Vote& new_vote) override;
+  void VoteInvalidated(AcceptedVote* vote) override;
+
+  // Our VotingChannelFactory for providing VotingChannels to our input voters.
+  VotingChannelFactory factory_;
+
+  // The ID of the only voting channel we've vended.
+  VoterId voter_id_ = kInvalidVoterId;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FramePriorityDecorator);
+};
+
+}  // namespace frame_priority
+}  // namespace performance_manager
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_DECORATORS_FRAME_PRIORITY_DECORATOR_H_
diff --git a/chrome/browser/performance_manager/decorators/frame_priority_decorator_unittest.cc b/chrome/browser/performance_manager/decorators/frame_priority_decorator_unittest.cc
new file mode 100644
index 0000000..00cb3dfc
--- /dev/null
+++ b/chrome/browser/performance_manager/decorators/frame_priority_decorator_unittest.cc
@@ -0,0 +1,89 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/performance_manager/decorators/frame_priority_decorator.h"
+
+#include "components/performance_manager/graph/frame_node_impl.h"
+#include "components/performance_manager/graph/page_node_impl.h"
+#include "components/performance_manager/graph/process_node_impl.h"
+#include "components/performance_manager/test_support/frame_priority.h"
+#include "components/performance_manager/test_support/graph_test_harness.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace performance_manager {
+namespace frame_priority {
+
+namespace {
+
+class LenientMockFrameNodeObserver : public FrameNode::ObserverDefaultImpl {
+ public:
+  LenientMockFrameNodeObserver() = default;
+  ~LenientMockFrameNodeObserver() override = default;
+
+  MOCK_METHOD1(OnPriorityAndReasonChanged, void(const FrameNode*));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LenientMockFrameNodeObserver);
+};
+
+using MockFrameNodeObserver =
+    ::testing::StrictMock<LenientMockFrameNodeObserver>;
+
+class FramePriorityDecoratorTest : public GraphTestHarness {
+ public:
+  FramePriorityDecoratorTest() = default;
+  ~FramePriorityDecoratorTest() override = default;
+};
+
+}  // namespace
+
+TEST_F(FramePriorityDecoratorTest, VotesForwardedToGraph) {
+  FramePriorityDecorator* fpd = new FramePriorityDecorator();
+  graph()->PassToGraph(base::WrapUnique(fpd));
+
+  TestNodeWrapper<ProcessNodeImpl> process = CreateNode<ProcessNodeImpl>();
+  TestNodeWrapper<PageNodeImpl> page = CreateNode<PageNodeImpl>();
+  TestNodeWrapper<FrameNodeImpl> frame =
+      graph()->CreateFrameNodeAutoId(process.get(), page.get());
+
+  test::DummyVoter voter;
+  voter.SetVotingChannel(fpd->GetVotingChannel());
+
+  MockFrameNodeObserver obs;
+  graph()->AddFrameNodeObserver(&obs);
+
+  EXPECT_EQ(base::TaskPriority::LOWEST,
+            frame->priority_and_reason().priority());
+  EXPECT_EQ(FrameNodeImpl::kDefaultPriorityReason,
+            frame->priority_and_reason().reason());
+
+  // Do not expect a notification when an identical vote is submitted.
+  voter.EmitVote(frame.get(), frame->priority_and_reason().priority(),
+                 frame->priority_and_reason().reason());
+  auto& receipt = voter.receipts_[0];
+  testing::Mock::VerifyAndClear(&obs);
+
+  // Update the vote with a new priority and expect that to propagate.
+  EXPECT_CALL(obs, OnPriorityAndReasonChanged(frame.get()));
+  receipt.ChangeVote(base::TaskPriority::HIGHEST, test::DummyVoter::kReason);
+  testing::Mock::VerifyAndClear(&obs);
+  EXPECT_EQ(base::TaskPriority::HIGHEST,
+            frame->priority_and_reason().priority());
+  EXPECT_EQ(test::DummyVoter::kReason, frame->priority_and_reason().reason());
+
+  // Cancel the existing vote and expect it to go back to the default.
+  EXPECT_CALL(obs, OnPriorityAndReasonChanged(frame.get()));
+  receipt.Reset();
+  testing::Mock::VerifyAndClear(&obs);
+  EXPECT_EQ(base::TaskPriority::LOWEST,
+            frame->priority_and_reason().priority());
+  EXPECT_EQ(FrameNodeImpl::kDefaultPriorityReason,
+            frame->priority_and_reason().reason());
+
+  graph()->RemoveFrameNodeObserver(&obs);
+}
+
+}  // namespace frame_priority
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc
index c01ed420b..a2afb3e 100644
--- a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc
+++ b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc
@@ -24,8 +24,6 @@
   LenientMockProcessNodeObserver() = default;
   ~LenientMockProcessNodeObserver() override = default;
 
-  virtual bool ShouldObserve(const NodeBase* node) { return false; }
-
   MOCK_METHOD1(OnAllFramesInProcessFrozen, void(const ProcessNode*));
 
  private:
diff --git a/chrome/browser/permissions/permission_prompt_android.cc b/chrome/browser/permissions/permission_prompt_android.cc
index 6ae03b6..7240f11 100644
--- a/chrome/browser/permissions/permission_prompt_android.cc
+++ b/chrome/browser/permissions/permission_prompt_android.cc
@@ -23,6 +23,7 @@
     : web_contents_(web_contents),
       delegate_(delegate),
       permission_request_notification_(nullptr),
+      permission_infobar_(nullptr),
       weak_factory_(this) {
   DCHECK(web_contents);
 
@@ -31,8 +32,8 @@
   if (infobar_service &&
       GroupedPermissionInfoBarDelegate::ShouldShowMiniInfobar(
           GetContentSettingType(0u /* position */))) {
-    GroupedPermissionInfoBarDelegate::Create(weak_factory_.GetWeakPtr(),
-                                             infobar_service);
+    permission_infobar_ = GroupedPermissionInfoBarDelegate::Create(
+        weak_factory_.GetWeakPtr(), infobar_service);
     return;
   }
 
@@ -46,7 +47,14 @@
   PermissionDialogDelegate::Create(web_contents_, this);
 }
 
-PermissionPromptAndroid::~PermissionPromptAndroid() {}
+PermissionPromptAndroid::~PermissionPromptAndroid() {
+  if (permission_infobar_) {
+    InfoBarService* infobar_service =
+        InfoBarService::FromWebContents(web_contents_);
+
+    infobar_service->RemoveInfoBar(permission_infobar_);
+  }
+}
 
 void PermissionPromptAndroid::UpdateAnchorPosition() {
   NOTREACHED() << "UpdateAnchorPosition is not implemented";
diff --git a/chrome/browser/permissions/permission_prompt_android.h b/chrome/browser/permissions/permission_prompt_android.h
index be458112..d52d1b09 100644
--- a/chrome/browser/permissions/permission_prompt_android.h
+++ b/chrome/browser/permissions/permission_prompt_android.h
@@ -17,6 +17,9 @@
 namespace content {
 class WebContents;
 }
+namespace infobars {
+class InfoBar;
+}
 class PermissionRequestNotificationAndroid;
 
 class PermissionPromptAndroid : public PermissionPrompt {
@@ -55,6 +58,10 @@
   std::unique_ptr<PermissionRequestNotificationAndroid>
       permission_request_notification_;
 
+  // The infobar used to display the permission request, if displayed in that
+  // format. Never assume that this pointer is currently alive.
+  infobars::InfoBar* permission_infobar_;
+
   base::WeakPtrFactory<PermissionPromptAndroid> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(PermissionPromptAndroid);
diff --git a/chrome/browser/permissions/permission_request_manager.cc b/chrome/browser/permissions/permission_request_manager.cc
index 9c2c275..c12cf7e1 100644
--- a/chrome/browser/permissions/permission_request_manager.cc
+++ b/chrome/browser/permissions/permission_request_manager.cc
@@ -80,7 +80,6 @@
 }
 
 void PermissionRequestManager::AddRequest(PermissionRequest* request) {
-
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDenyPermissionPrompts)) {
     request->PermissionDenied();
@@ -140,6 +139,12 @@
   }
   queued_requests_.push_back(request);
 
+  // If we're displaying a quiet permission request, kill it in favor of this
+  // permission request.
+  if (ShouldShowQuietPermissionPrompt()) {
+    FinalizeBubble(PermissionAction::IGNORED);
+  }
+
   if (!IsBubbleVisible())
     ScheduleShowBubble();
 }
@@ -553,17 +558,21 @@
 }
 
 bool PermissionRequestManager::ShouldShowQuietPermissionPrompt() {
-  if (!requests_.size())
+  if (!requests_.size() ||
+      requests_.front()->GetPermissionRequestType() !=
+          PermissionRequestType::PERMISSION_NOTIFICATIONS) {
     return false;
+  }
 
-#if !defined(OS_ANDROID)
   const auto ui_flavor = QuietNotificationsPromptConfig::UIFlavorToUse();
-  return (requests_.front()->GetPermissionRequestType() ==
-              PermissionRequestType::PERMISSION_NOTIFICATIONS &&
-          (ui_flavor == QuietNotificationsPromptConfig::STATIC_ICON ||
-           ui_flavor == QuietNotificationsPromptConfig::ANIMATED_ICON));
+#if !defined(OS_ANDROID)
+  return ui_flavor == QuietNotificationsPromptConfig::STATIC_ICON ||
+         ui_flavor == QuietNotificationsPromptConfig::ANIMATED_ICON;
 #else   // OS_ANDROID
-  return false;
+  return ui_flavor == QuietNotificationsPromptConfig::QUIET_NOTIFICATION ||
+         ui_flavor == QuietNotificationsPromptConfig::HEADS_UP_NOTIFICATION ||
+         ui_flavor == QuietNotificationsPromptConfig::MINI_INFOBAR;
+
 #endif  // OS_ANDROID
 }
 
diff --git a/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc b/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc
index 2c66cd4..8d7b527 100644
--- a/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc
+++ b/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc
@@ -159,12 +159,10 @@
   // Prepare the params to navigate to the placeholder.
   std::string html = GetPDFPlaceholderHTML(navigation_handle()->GetURL());
   GURL data_url("data:text/html," + net::EscapePath(html));
-  content::OpenURLParams params(
-      data_url, content::Referrer(navigation_handle()->GetReferrer()),
-      navigation_handle()->GetFrameTreeNodeId(),
-      WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_AUTO_SUBFRAME,
-      navigation_handle()->IsRendererInitiated());
-  params.initiator_origin = navigation_handle()->GetInitiatorOrigin();
+  content::OpenURLParams params =
+      content::OpenURLParams::FromNavigationHandle(navigation_handle());
+  params.url = data_url;
+  params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
 
   // Post a task to navigate to the placeholder HTML. We don't navigate
   // synchronously here, as starting a navigation within a navigation is
@@ -177,5 +175,5 @@
   base::PostTask(
       FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(&PdfWebContentsLifetimeHelper::NavigateIFrameToPlaceholder,
-                     helper->GetWeakPtr(), params));
+                     helper->GetWeakPtr(), std::move(params)));
 }
diff --git a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.cc b/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.cc
similarity index 83%
rename from chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.cc
rename to chrome/browser/policy/chrome_browser_cloud_management_register_watcher.cc
index 08e1e84..b5fba59 100644
--- a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.cc
+++ b/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.h"
+#include "chrome/browser/policy/chrome_browser_cloud_management_register_watcher.h"
 
 #include <utility>
 
@@ -20,21 +20,21 @@
 using RegisterResult = MachineLevelUserCloudPolicyController::RegisterResult;
 
 const char
-    MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName[] =
+    ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName[] =
         "Enterprise.MachineLevelUserCloudPolicyEnrollment.StartupDialog";
 
-MachineLevelUserCloudPolicyRegisterWatcher::
-    MachineLevelUserCloudPolicyRegisterWatcher(
+ChromeBrowserCloudManagementRegisterWatcher::
+    ChromeBrowserCloudManagementRegisterWatcher(
         MachineLevelUserCloudPolicyController* controller)
     : controller_(controller) {
   controller_->AddObserver(this);
 }
-MachineLevelUserCloudPolicyRegisterWatcher::
-    ~MachineLevelUserCloudPolicyRegisterWatcher() {
+ChromeBrowserCloudManagementRegisterWatcher::
+    ~ChromeBrowserCloudManagementRegisterWatcher() {
   controller_->RemoveObserver(this);
 }
 
-RegisterResult MachineLevelUserCloudPolicyRegisterWatcher::
+RegisterResult ChromeBrowserCloudManagementRegisterWatcher::
     WaitUntilCloudPolicyEnrollmentFinished() {
   BrowserDMTokenStorage* token_storage = BrowserDMTokenStorage::Get();
 
@@ -46,7 +46,7 @@
     return RegisterResult::kEnrollmentSuccessBeforeDialogDisplayed;
 
   EnterpriseStartupDialog::DialogResultCallback callback = base::BindOnce(
-      &MachineLevelUserCloudPolicyRegisterWatcher::OnDialogClosed,
+      &ChromeBrowserCloudManagementRegisterWatcher::OnDialogClosed,
       base::Unretained(this));
   if (dialog_creation_callback_)
     dialog_ = std::move(dialog_creation_callback_).Run(std::move(callback));
@@ -77,11 +77,11 @@
 
   if (!token_storage->ShouldDisplayErrorMessageOnFailure() &&
       register_result_) {
-    SYSLOG(ERROR) << "Machine level user cloud policy enrollment has failed.";
+    SYSLOG(ERROR) << "Chrome browser cloud management enrollment has failed.";
     return RegisterResult::kEnrollmentFailedSilently;
   }
 
-  SYSLOG(ERROR) << "Can not start Chrome as machine level user cloud policy "
+  SYSLOG(ERROR) << "Can not start Chrome as chrome browser cloud management "
                    "enrollment has failed. Please double check network "
                    "connection and the status of enrollment token then open "
                    "Chrome again.";
@@ -91,22 +91,22 @@
   return RegisterResult::kQuitDueToFailure;
 }
 
-bool MachineLevelUserCloudPolicyRegisterWatcher::IsDialogShowing() {
+bool ChromeBrowserCloudManagementRegisterWatcher::IsDialogShowing() {
   return (dialog_ && dialog_->IsShowing()) || run_loop_.running();
 }
 
-void MachineLevelUserCloudPolicyRegisterWatcher::
+void ChromeBrowserCloudManagementRegisterWatcher::
     SetDialogCreationCallbackForTesting(DialogCreationCallback callback) {
   dialog_creation_callback_ = std::move(callback);
 }
 
 // static
-void MachineLevelUserCloudPolicyRegisterWatcher::RecordEnrollmentStartDialog(
+void ChromeBrowserCloudManagementRegisterWatcher::RecordEnrollmentStartDialog(
     EnrollmentStartupDialog dialog_startup) {
   UMA_HISTOGRAM_ENUMERATION(kStartupDialogHistogramName, dialog_startup);
 }
 
-void MachineLevelUserCloudPolicyRegisterWatcher::OnPolicyRegisterFinished(
+void ChromeBrowserCloudManagementRegisterWatcher::OnPolicyRegisterFinished(
     bool succeeded) {
   register_result_ = succeeded;
 
@@ -123,7 +123,7 @@
   }
 }
 
-void MachineLevelUserCloudPolicyRegisterWatcher::OnDialogClosed(
+void ChromeBrowserCloudManagementRegisterWatcher::OnDialogClosed(
     bool is_accepted,
     bool can_show_browser_window) {
   if (can_show_browser_window) {
@@ -157,7 +157,7 @@
   run_loop_.Quit();
 }
 
-void MachineLevelUserCloudPolicyRegisterWatcher::DisplayErrorMessage() {
+void ChromeBrowserCloudManagementRegisterWatcher::DisplayErrorMessage() {
   dialog_->DisplayErrorMessage(
       l10n_util::GetStringUTF16(
           IDS_ENTERPRISE_STARTUP_CLOUD_POLICY_ENROLLMENT_ERROR),
diff --git a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.h b/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.h
similarity index 73%
rename from chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.h
rename to chrome/browser/policy/chrome_browser_cloud_management_register_watcher.h
index fa31df6c..af8ed9a 100644
--- a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.h
+++ b/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_POLICY_MACHINE_LEVEL_USER_CLOUD_POLICY_REGISTER_WATCHER_H_
-#define CHROME_BROWSER_POLICY_MACHINE_LEVEL_USER_CLOUD_POLICY_REGISTER_WATCHER_H_
+#ifndef CHROME_BROWSER_POLICY_CHROME_BROWSER_CLOUD_MANAGEMENT_REGISTER_WATCHER_H_
+#define CHROME_BROWSER_POLICY_CHROME_BROWSER_CLOUD_MANAGEMENT_REGISTER_WATCHER_H_
 
 #include <memory>
 #include <string>
@@ -17,24 +17,24 @@
 #include "chrome/browser/policy/machine_level_user_cloud_policy_controller.h"
 #include "chrome/browser/ui/enterprise_startup_dialog.h"
 
-class MachineLevelUserCloudPolicyRegisterWatcherTest;
+class ChromeBrowserCloudManagementRegisterWatcherTest;
 
 namespace policy {
 
-// Watches the status of machine level user cloud policy enrollment.
+// Watches the status of chrome browser cloud management enrollment.
 // Shows the blocking dialog for ongoing enrollment and failed enrollment.
-class MachineLevelUserCloudPolicyRegisterWatcher
+class ChromeBrowserCloudManagementRegisterWatcher
     : public MachineLevelUserCloudPolicyController::Observer {
  public:
   using DialogCreationCallback =
       base::OnceCallback<std::unique_ptr<EnterpriseStartupDialog>(
           EnterpriseStartupDialog::DialogResultCallback)>;
 
-  explicit MachineLevelUserCloudPolicyRegisterWatcher(
+  explicit ChromeBrowserCloudManagementRegisterWatcher(
       MachineLevelUserCloudPolicyController* controller);
-  ~MachineLevelUserCloudPolicyRegisterWatcher() override;
+  ~ChromeBrowserCloudManagementRegisterWatcher() override;
 
-  // Blocks until the machine level user cloud policy enrollment process
+  // Blocks until the  chrome browser cloud management enrollment process
   // finishes. Returns the result of enrollment.
   MachineLevelUserCloudPolicyController::RegisterResult
   WaitUntilCloudPolicyEnrollmentFinished();
@@ -45,25 +45,25 @@
   void SetDialogCreationCallbackForTesting(DialogCreationCallback callback);
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
+  FRIEND_TEST_ALL_PREFIXES(ChromeBrowserCloudManagementRegisterWatcherTest,
                            EnrollmentSucceed);
-  FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
+  FRIEND_TEST_ALL_PREFIXES(ChromeBrowserCloudManagementRegisterWatcherTest,
                            EnrollmentSucceedWithNoErrorMessageSetup);
-  FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
+  FRIEND_TEST_ALL_PREFIXES(ChromeBrowserCloudManagementRegisterWatcherTest,
                            EnrollmentFailedAndQuit);
-  FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
+  FRIEND_TEST_ALL_PREFIXES(ChromeBrowserCloudManagementRegisterWatcherTest,
                            EnrollmentFailedAndRestart);
-  FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
+  FRIEND_TEST_ALL_PREFIXES(ChromeBrowserCloudManagementRegisterWatcherTest,
                            EnrollmentCanceledBeforeFinish);
   FRIEND_TEST_ALL_PREFIXES(
-      MachineLevelUserCloudPolicyRegisterWatcherTest,
+      ChromeBrowserCloudManagementRegisterWatcherTest,
       EnrollmentCanceledBeforeFinishWithNoErrorMessageSetup);
-  FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
+  FRIEND_TEST_ALL_PREFIXES(ChromeBrowserCloudManagementRegisterWatcherTest,
                            EnrollmentFailedBeforeDialogDisplay);
-  FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
+  FRIEND_TEST_ALL_PREFIXES(ChromeBrowserCloudManagementRegisterWatcherTest,
                            EnrollmentFailedWithoutErrorMessage);
   FRIEND_TEST_ALL_PREFIXES(
-      MachineLevelUserCloudPolicyRegisterWatcherTest,
+      ChromeBrowserCloudManagementRegisterWatcherTest,
       EnrollmentFailedBeforeDialogDisplayWithoutErrorMessage);
 
   // Enum used with kStartupDialogHistogramName.
@@ -119,9 +119,9 @@
 
   base::Time visible_start_time_;
 
-  DISALLOW_COPY_AND_ASSIGN(MachineLevelUserCloudPolicyRegisterWatcher);
+  DISALLOW_COPY_AND_ASSIGN(ChromeBrowserCloudManagementRegisterWatcher);
 };
 
 }  // namespace policy
 
-#endif  // CHROME_BROWSER_POLICY_MACHINE_LEVEL_USER_CLOUD_POLICY_REGISTER_WATCHER_H_
+#endif  // CHROME_BROWSER_POLICY_CHROME_BROWSER_CLOUD_MANAGEMENT_REGISTER_WATCHER_H_
diff --git a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher_unittest.cc b/chrome/browser/policy/chrome_browser_cloud_management_register_watcher_unittest.cc
similarity index 73%
rename from chrome/browser/policy/machine_level_user_cloud_policy_register_watcher_unittest.cc
rename to chrome/browser/policy/chrome_browser_cloud_management_register_watcher_unittest.cc
index be8bbcc..e869223e 100644
--- a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher_unittest.cc
+++ b/chrome/browser/policy/chrome_browser_cloud_management_register_watcher_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.h"
+#include "chrome/browser/policy/chrome_browser_cloud_management_register_watcher.h"
 
 #include <utility>
 
@@ -17,9 +17,9 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::testing::_;
 using ::testing::InvokeWithoutArgs;
 using ::testing::Return;
-using ::testing::_;
 using RegisterResult =
     policy::MachineLevelUserCloudPolicyController::RegisterResult;
 
@@ -83,9 +83,9 @@
 
 }  // namespace
 
-class MachineLevelUserCloudPolicyRegisterWatcherTest : public ::testing::Test {
+class ChromeBrowserCloudManagementRegisterWatcherTest : public ::testing::Test {
  public:
-  MachineLevelUserCloudPolicyRegisterWatcherTest()
+  ChromeBrowserCloudManagementRegisterWatcherTest()
       : watcher_(&controller_),
         dialog_(std::make_unique<MockEnterpriseStartupDialog>()),
         dialog_ptr_(dialog_.get()) {
@@ -93,7 +93,7 @@
     storage_.SetDMToken(std::string());
     storage_.SetClientId(kClientId);
     watcher_.SetDialogCreationCallbackForTesting(
-        base::BindOnce(&MachineLevelUserCloudPolicyRegisterWatcherTest::
+        base::BindOnce(&ChromeBrowserCloudManagementRegisterWatcherTest::
                            CreateEnterpriseStartupDialog,
                        base::Unretained(this)));
   }
@@ -103,7 +103,7 @@
   FakeMachineLevelUserCloudPolicyController* controller() {
     return &controller_;
   }
-  MachineLevelUserCloudPolicyRegisterWatcher* watcher() { return &watcher_; }
+  ChromeBrowserCloudManagementRegisterWatcher* watcher() { return &watcher_; }
   MockEnterpriseStartupDialog* dialog() { return dialog_ptr_; }
 
   std::unique_ptr<EnterpriseStartupDialog> CreateEnterpriseStartupDialog(
@@ -116,22 +116,22 @@
   content::BrowserTaskEnvironment task_environment_;
 
   FakeMachineLevelUserCloudPolicyController controller_;
-  MachineLevelUserCloudPolicyRegisterWatcher watcher_;
+  ChromeBrowserCloudManagementRegisterWatcher watcher_;
   FakeBrowserDMTokenStorage storage_;
   std::unique_ptr<MockEnterpriseStartupDialog> dialog_;
   MockEnterpriseStartupDialog* dialog_ptr_;
 
-  DISALLOW_COPY_AND_ASSIGN(MachineLevelUserCloudPolicyRegisterWatcherTest);
+  DISALLOW_COPY_AND_ASSIGN(ChromeBrowserCloudManagementRegisterWatcherTest);
 };
 
-TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+TEST_F(ChromeBrowserCloudManagementRegisterWatcherTest,
        NoEnrollmentNeededWithDMToken) {
   storage()->SetDMToken(kDMToken);
   EXPECT_EQ(RegisterResult::kEnrollmentSuccessBeforeDialogDisplayed,
             watcher()->WaitUntilCloudPolicyEnrollmentFinished());
 }
 
-TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+TEST_F(ChromeBrowserCloudManagementRegisterWatcherTest,
        NoEnrollmentNeededWithoutEnrollmentToken) {
   storage()->SetEnrollmentToken(std::string());
   storage()->SetDMToken(std::string());
@@ -139,7 +139,7 @@
             watcher()->WaitUntilCloudPolicyEnrollmentFinished());
 }
 
-TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest, EnrollmentSucceed) {
+TEST_F(ChromeBrowserCloudManagementRegisterWatcherTest, EnrollmentSucceed) {
   base::HistogramTester histogram_tester;
 
   EXPECT_CALL(*dialog(), DisplayLaunchingInformationWithThrobber(_));
@@ -152,18 +152,18 @@
   EXPECT_EQ(RegisterResult::kEnrollmentSuccess,
             watcher()->WaitUntilCloudPolicyEnrollmentFinished());
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kShown,
       1);
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kClosedSuccess,
       1);
 }
 
-TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+TEST_F(ChromeBrowserCloudManagementRegisterWatcherTest,
        EnrollmentSucceedWithNoErrorMessageSetup) {
   base::HistogramTester histogram_tester;
 
@@ -178,18 +178,18 @@
   EXPECT_EQ(RegisterResult::kEnrollmentSuccess,
             watcher()->WaitUntilCloudPolicyEnrollmentFinished());
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kShown,
       1);
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kClosedSuccess,
       1);
 }
 
-TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+TEST_F(ChromeBrowserCloudManagementRegisterWatcherTest,
        EnrollmentFailedAndQuit) {
   base::HistogramTester histogram_tester;
 
@@ -206,18 +206,18 @@
   EXPECT_EQ(RegisterResult::kQuitDueToFailure,
             watcher()->WaitUntilCloudPolicyEnrollmentFinished());
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kShown,
       1);
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kClosedFail,
       1);
 }
 
-TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+TEST_F(ChromeBrowserCloudManagementRegisterWatcherTest,
        EnrollmentFailedAndRestart) {
   base::HistogramTester histogram_tester;
 
@@ -234,18 +234,18 @@
   EXPECT_EQ(RegisterResult::kRestartDueToFailure,
             watcher()->WaitUntilCloudPolicyEnrollmentFinished());
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kShown,
       1);
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kClosedRelaunch,
       1);
 }
 
-TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+TEST_F(ChromeBrowserCloudManagementRegisterWatcherTest,
        EnrollmentCanceledBeforeFinish) {
   base::HistogramTester histogram_tester;
 
@@ -257,18 +257,18 @@
   EXPECT_EQ(RegisterResult::kQuitDueToFailure,
             watcher()->WaitUntilCloudPolicyEnrollmentFinished());
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kShown,
       1);
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kClosedAbort,
       1);
 }
 
-TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+TEST_F(ChromeBrowserCloudManagementRegisterWatcherTest,
        EnrollmentCanceledBeforeFinishWithNoErrorMessageSetup) {
   base::HistogramTester histogram_tester;
 
@@ -282,18 +282,18 @@
   EXPECT_EQ(RegisterResult::kQuitDueToFailure,
             watcher()->WaitUntilCloudPolicyEnrollmentFinished());
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kShown,
       1);
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kClosedAbort,
       1);
 }
 
-TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+TEST_F(ChromeBrowserCloudManagementRegisterWatcherTest,
        EnrollmentFailedBeforeDialogDisplay) {
   base::HistogramTester histogram_tester;
 
@@ -304,18 +304,18 @@
   EXPECT_EQ(RegisterResult::kQuitDueToFailure,
             watcher()->WaitUntilCloudPolicyEnrollmentFinished());
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kShown,
       1);
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kClosedFail,
       1);
 }
 
-TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+TEST_F(ChromeBrowserCloudManagementRegisterWatcherTest,
        EnrollmentFailedWithoutErrorMessage) {
   base::HistogramTester histogram_tester;
 
@@ -330,18 +330,18 @@
   EXPECT_EQ(RegisterResult::kEnrollmentFailedSilently,
             watcher()->WaitUntilCloudPolicyEnrollmentFinished());
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kShown,
       1);
   histogram_tester.ExpectBucketCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
-      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::EnrollmentStartupDialog::
           kClosedFailAndIgnore,
       1);
 }
 
-TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+TEST_F(ChromeBrowserCloudManagementRegisterWatcherTest,
        EnrollmentFailedBeforeDialogDisplayWithoutErrorMessage) {
   base::HistogramTester histogram_tester;
 
@@ -350,7 +350,7 @@
   EXPECT_EQ(RegisterResult::kEnrollmentFailedSilentlyBeforeDialogDisplayed,
             watcher()->WaitUntilCloudPolicyEnrollmentFinished());
   histogram_tester.ExpectTotalCount(
-      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
+      ChromeBrowserCloudManagementRegisterWatcher::kStartupDialogHistogramName,
       0);
 }
 
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 5be2190..51bff24 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -752,6 +752,9 @@
   { key::kUserNativePrintersAllowed,
     prefs::kUserNativePrintersAllowed,
     base::Value::Type::BOOLEAN },
+  { key::kExternalPrintServersWhitelist,
+    prefs::kExternalPrintServersWhitelist,
+    base::Value::Type::LIST },
   { key::kAllowedLanguages,
     prefs::kAllowedLanguages,
     base::Value::Type::LIST },
@@ -1096,8 +1099,8 @@
     prefs::kManagedWebUsbBlockedForUrls,
     base::Value::Type::LIST },
 
-  { key::kTabLifecyclesEnabled,
-    prefs::kTabLifecyclesEnabled,
+  { key::kTabFreezingEnabled,
+    prefs::kTabFreezingEnabled,
     base::Value::Type::BOOLEAN },
   { key::kCoalesceH2ConnectionsWithClientCertificatesForHosts,
     prefs::kH2ClientCertCoalescingHosts,
diff --git a/chrome/browser/policy/machine_level_user_cloud_policy_controller.cc b/chrome/browser/policy/machine_level_user_cloud_policy_controller.cc
index b9efc7ab..2f219b7 100644
--- a/chrome/browser/policy/machine_level_user_cloud_policy_controller.cc
+++ b/chrome/browser/policy/machine_level_user_cloud_policy_controller.cc
@@ -23,9 +23,9 @@
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/policy/browser_dm_token_storage.h"
+#include "chrome/browser/policy/chrome_browser_cloud_management_register_watcher.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/cloud/chrome_browser_cloud_management_helper.h"
-#include "chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "components/policy/core/common/cloud/cloud_external_data_manager.h"
@@ -204,8 +204,8 @@
       url_loader_factory);
 
   if (dm_token.empty()) {
-    policy_register_watcher_ =
-        std::make_unique<MachineLevelUserCloudPolicyRegisterWatcher>(this);
+    cloud_management_register_watcher_ =
+        std::make_unique<ChromeBrowserCloudManagementRegisterWatcher>(this);
 
     enrollment_start_time_ = base::Time::Now();
 
@@ -230,9 +230,9 @@
 
 bool MachineLevelUserCloudPolicyController::
     WaitUntilPolicyEnrollmentFinished() {
-  if (policy_register_watcher_) {
-    switch (
-        policy_register_watcher_->WaitUntilCloudPolicyEnrollmentFinished()) {
+  if (cloud_management_register_watcher_) {
+    switch (cloud_management_register_watcher_
+                ->WaitUntilCloudPolicyEnrollmentFinished()) {
       case RegisterResult::kNoEnrollmentNeeded:
       case RegisterResult::kEnrollmentSuccessBeforeDialogDisplayed:
       case RegisterResult::kEnrollmentFailedSilentlyBeforeDialogDisplayed:
@@ -263,8 +263,8 @@
 }
 
 bool MachineLevelUserCloudPolicyController::IsEnterpriseStartupDialogShowing() {
-  return policy_register_watcher_ &&
-         policy_register_watcher_->IsDialogShowing();
+  return cloud_management_register_watcher_ &&
+         cloud_management_register_watcher_->IsDialogShowing();
 }
 
 void MachineLevelUserCloudPolicyController::NotifyPolicyRegisterFinished(
diff --git a/chrome/browser/policy/machine_level_user_cloud_policy_controller.h b/chrome/browser/policy/machine_level_user_cloud_policy_controller.h
index 4e0e4aa..d59814f 100644
--- a/chrome/browser/policy/machine_level_user_cloud_policy_controller.h
+++ b/chrome/browser/policy/machine_level_user_cloud_policy_controller.h
@@ -29,7 +29,7 @@
 class ConfigurationPolicyProvider;
 class MachineLevelUserCloudPolicyManager;
 class MachineLevelUserCloudPolicyFetcher;
-class MachineLevelUserCloudPolicyRegisterWatcher;
+class ChromeBrowserCloudManagementRegisterWatcher;
 
 // A class that setups and manages MachineLevelUserCloudPolicy.
 class MachineLevelUserCloudPolicyController {
@@ -109,8 +109,8 @@
   std::unique_ptr<MachineLevelUserCloudPolicyFetcher> policy_fetcher_;
     // This is an observer of the controller and needs to be declared after the
     // |observers_|.
-  std::unique_ptr<MachineLevelUserCloudPolicyRegisterWatcher>
-      policy_register_watcher_;
+  std::unique_ptr<ChromeBrowserCloudManagementRegisterWatcher>
+      cloud_management_register_watcher_;
 
   // Time at which the enrollment process was started.  Used to log UMA metric.
   base::Time enrollment_start_time_;
diff --git a/chrome/browser/profiles/profile_info_cache.cc b/chrome/browser/profiles/profile_info_cache.cc
index 1f4a466e..3945acf3 100644
--- a/chrome/browser/profiles/profile_info_cache.cc
+++ b/chrome/browser/profiles/profile_info_cache.cc
@@ -106,6 +106,8 @@
   // profile names.
   if (!disable_avatar_download_for_testing_)
     MigrateLegacyProfileNamesAndDownloadAvatars();
+
+  RecomputeProfileNamesIfNeeded();
 }
 
 ProfileInfoCache::~ProfileInfoCache() {
@@ -789,6 +791,28 @@
                                    image_path);
 }
 
+void ProfileInfoCache::RecomputeProfileNamesIfNeeded() {
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+  std::vector<ProfileAttributesEntry*> entries = GetAllProfilesAttributes();
+  if (entries.size() < 2)
+    return;
+
+  for (size_t i = 0; i < entries.size() - 1; i++) {
+    base::string16 name = entries[i]->GetLocalProfileName();
+    if (!IsDefaultProfileName(name))
+      continue;
+
+    for (size_t j = i + 1; j < entries.size(); j++) {
+      if (name == entries[j]->GetLocalProfileName()) {
+        entries[j]->SetLocalProfileName(
+            ChooseNameForNewProfile(entries[j]->GetAvatarIconIndex()));
+        UpdateSortForProfileIndex(entries[j]->profile_index());
+      }
+    }
+  }
+#endif
+}
+
 void ProfileInfoCache::MigrateLegacyProfileNamesAndDownloadAvatars() {
   // Only do this on desktop platforms.
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
diff --git a/chrome/browser/profiles/profile_info_cache.h b/chrome/browser/profiles/profile_info_cache.h
index 4cf7e58..c7b2632 100644
--- a/chrome/browser/profiles/profile_info_cache.h
+++ b/chrome/browser/profiles/profile_info_cache.h
@@ -192,6 +192,11 @@
   // used by the profiles.
   void MigrateLegacyProfileNamesAndDownloadAvatars();
 
+  // Recompute profile names to guarantee there are no duplicates of "Person n"
+  // exist, i.e. Two or more profiles with the profile name "Person 1" would be
+  // recomputed to "Person 1" and "Person 2".
+  void RecomputeProfileNamesIfNeeded();
+
   std::vector<std::string> sorted_keys_;
   const base::FilePath user_data_dir_;
 
diff --git a/chrome/browser/profiles/profile_info_cache_unittest.cc b/chrome/browser/profiles/profile_info_cache_unittest.cc
index d99453d..6d91c2d 100644
--- a/chrome/browser/profiles/profile_info_cache_unittest.cc
+++ b/chrome/browser/profiles/profile_info_cache_unittest.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <algorithm>
 #include <vector>
 
 #include "base/bind.h"
@@ -110,6 +111,11 @@
   testing_profile_manager_.profile_info_cache()->AddObserver(&name_observer_);
 }
 
+void ProfileInfoCacheTest::RemoveObserver() {
+  testing_profile_manager_.profile_info_cache()->RemoveObserver(
+      &name_observer_);
+}
+
 void ProfileInfoCacheTest::TearDown() {
   // Drain remaining tasks to make sure all tasks are completed. This prevents
   // memory leaks.
@@ -798,6 +804,58 @@
 }
 
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+TEST_F(ProfileInfoCacheTest, RecomputeProfileNamesIfNeeded) {
+  // Duplicate profile names causes the observer to crash.
+  RemoveObserver();
+  EXPECT_EQ(0U, GetCache()->GetNumberOfProfiles());
+
+  base::FilePath path_1 = GetProfilePath("path_1");
+  base::string16 name_1 = ASCIIToUTF16("Person 3");
+  GetCache()->AddProfileToCache(path_1, name_1, std::string(), base::string16(),
+                                false, 0, std::string(), EmptyAccountId());
+  base::FilePath path_2 = GetProfilePath("path_2");
+  GetCache()->AddProfileToCache(path_2, ASCIIToUTF16("Person 1"), std::string(),
+                                base::string16(), false, 1, std::string(),
+                                EmptyAccountId());
+  base::FilePath path_3 = GetProfilePath("path_3");
+  GetCache()->AddProfileToCache(path_3, ASCIIToUTF16("Person 2"), std::string(),
+                                base::string16(), false, 2, std::string(),
+                                EmptyAccountId());
+  base::FilePath path_4 = GetProfilePath("path_4");
+  GetCache()->AddProfileToCache(path_4, ASCIIToUTF16("Person 1"), std::string(),
+                                base::string16(), false, 3, std::string(),
+                                EmptyAccountId());
+  base::string16 name_5 = ASCIIToUTF16("Smith");
+  base::FilePath path_5 = GetProfilePath("path_5");
+  GetCache()->AddProfileToCache(path_5, name_5, std::string(), base::string16(),
+                                false, 2, std::string(), EmptyAccountId());
+  base::FilePath path_6 = GetProfilePath("path_6");
+  GetCache()->AddProfileToCache(path_6, ASCIIToUTF16("Person 2"), std::string(),
+                                base::string16(), false, 2, std::string(),
+                                EmptyAccountId());
+
+  EXPECT_EQ(6U, GetCache()->GetNumberOfProfiles());
+
+  ResetCache();
+  // Check non-duplicate profile name is not changed.
+  EXPECT_EQ(name_1, GetCache()->GetNameToDisplayOfProfileAtIndex(
+                        GetCache()->GetIndexOfProfileWithPath(path_1)));
+  // Check nondefault profile name is not changed.
+  EXPECT_EQ(name_5, GetCache()->GetNameToDisplayOfProfileAtIndex(
+                        GetCache()->GetIndexOfProfileWithPath(path_5)));
+
+  // Check there is no duplicate profile name.
+  std::vector<ProfileAttributesEntry*> entries =
+      GetCache()->GetAllProfilesAttributes();
+  for (size_t index = 1; index < entries.size(); index++) {
+    base::string16 name = entries[index - 1]->GetLocalProfileName();
+    EXPECT_TRUE(std::none_of(entries.begin() + index, entries.end(),
+                             [name](ProfileAttributesEntry* entry) {
+                               return entry->GetLocalProfileName() == name;
+                             }));
+  }
+}
+
 TEST_F(ProfileInfoCacheTest, MigrateLegacyProfileNamesWithNewAvatarMenu) {
   EXPECT_EQ(0U, GetCache()->GetNumberOfProfiles());
 
diff --git a/chrome/browser/profiles/profile_info_cache_unittest.h b/chrome/browser/profiles/profile_info_cache_unittest.h
index ea13f916..c2da1a6e 100644
--- a/chrome/browser/profiles/profile_info_cache_unittest.h
+++ b/chrome/browser/profiles/profile_info_cache_unittest.h
@@ -54,6 +54,7 @@
   ProfileInfoCache* GetCache();
   base::FilePath GetProfilePath(const std::string& base_name);
   void ResetCache();
+  void RemoveObserver();
 
  private:
   // BrowserTaskEnvironment needs to be up through the destruction of the
diff --git a/chrome/browser/profiles/profile_manager.h b/chrome/browser/profiles/profile_manager.h
index b60df76d..7d02af7 100644
--- a/chrome/browser/profiles/profile_manager.h
+++ b/chrome/browser/profiles/profile_manager.h
@@ -410,6 +410,10 @@
       const base::FilePath& profile_dir);
 #endif  // !defined(OS_ANDROID)
 
+  // Destroy after |profile_info_cache_| since Profile destruction may trigger
+  // some observers to unregister themselves.
+  base::ObserverList<ProfileManagerObserver> observers_;
+
   // Object to cache various information about profiles. Contains information
   // about every profile which has been created for this instance of Chrome,
   // if it has not been explicitly deleted. It must be destroyed after
@@ -451,8 +455,6 @@
   // Controls whether to initialize some services. Only disabled for testing.
   bool do_final_services_init_ = true;
 
-  base::ObserverList<ProfileManagerObserver> observers_;
-
   // TODO(chrome/browser/profiles/OWNERS): Usage of this in profile_manager.cc
   // should likely be turned into DCHECK_CURRENTLY_ON(BrowserThread::UI) for
   // consistency with surrounding code in the same file but that wasn't trivial
diff --git a/chrome/browser/resource_coordinator/BUILD.gn b/chrome/browser/resource_coordinator/BUILD.gn
index bf5e45c..c308b79 100644
--- a/chrome/browser/resource_coordinator/BUILD.gn
+++ b/chrome/browser/resource_coordinator/BUILD.gn
@@ -18,12 +18,6 @@
   ]
 }
 
-proto_library("intervention_policy_database_proto") {
-  sources = [
-    "intervention_policy_database.proto",
-  ]
-}
-
 source_set("tab_manager_features") {
   public = [
     "tab_manager_features.h",
diff --git a/chrome/browser/resource_coordinator/DEPS b/chrome/browser/resource_coordinator/DEPS
index b214e26..1602685 100644
--- a/chrome/browser/resource_coordinator/DEPS
+++ b/chrome/browser/resource_coordinator/DEPS
@@ -3,9 +3,9 @@
   "+chrome/browser/performance_manager",
 
   "+services/resource_coordinator/public",
-  # No inclusion of WebKit from the browser, other than strictly enum/POD,
+  # No inclusion of blink from the browser, other than strictly enum/POD,
   # header-only types, and some selected common code.
-  "+third_party/blink/public/platform/web_sudden_termination_disabler_type.h",
+  "+third_party/blink/public/common/sudden_termination_disabler_type.h",
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/resource_coordinator/intervention_policy_database.cc b/chrome/browser/resource_coordinator/intervention_policy_database.cc
deleted file mode 100644
index e15b9dacb..0000000
--- a/chrome/browser/resource_coordinator/intervention_policy_database.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/resource_coordinator/intervention_policy_database.h"
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/task/post_task.h"
-#include "base/task_runner_util.h"
-#include "base/values.h"
-#include "chrome/browser/resource_coordinator/utils.h"
-
-namespace resource_coordinator {
-
-InterventionPolicyDatabase::OriginInterventionPolicies::
-    OriginInterventionPolicies(InterventionPolicy discarding_policy,
-                               InterventionPolicy freezing_policy)
-    : discarding_policy(discarding_policy), freezing_policy(freezing_policy) {}
-
-InterventionPolicyDatabase::InterventionPolicyDatabase() {}
-InterventionPolicyDatabase::~InterventionPolicyDatabase() = default;
-
-InterventionPolicyDatabase::InterventionPolicy
-InterventionPolicyDatabase::GetDiscardingPolicy(
-    const url::Origin& origin) const {
-  const auto iter = database_.find(SerializeOriginIntoDatabaseKey(origin));
-  if (iter == database_.end())
-    return OriginInterventions::DEFAULT;
-  return iter->second.discarding_policy;
-}
-
-InterventionPolicyDatabase::InterventionPolicy
-InterventionPolicyDatabase::GetFreezingPolicy(const url::Origin& origin) const {
-  const auto iter = database_.find(SerializeOriginIntoDatabaseKey(origin));
-  if (iter == database_.end())
-    return OriginInterventions::DEFAULT;
-  return iter->second.freezing_policy;
-}
-
-void InterventionPolicyDatabase::InitializeDatabaseWithProtoFile(
-    const base::FilePath& proto_location,
-    const base::Version& version,
-    std::unique_ptr<base::DictionaryValue> manifest) {
-  // TODO(sebmarchand): Validate the version and the manifest?
-  base::PostTaskAndReplyWithResult(
-      FROM_HERE,
-      {base::ThreadPool(), base::TaskPriority::BEST_EFFORT,
-       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, base::MayBlock()},
-      base::BindOnce(
-          &InterventionPolicyDatabase::ReadDatabaseFromProtoFileOnSequence,
-          proto_location),
-      base::BindOnce(&InterventionPolicyDatabase::OnReadDatabaseProtoFromFile,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void InterventionPolicyDatabase::AddOriginPoliciesForTesting(
-    const url::Origin& origin,
-    OriginInterventionPolicies policies) {
-  database_.emplace(SerializeOriginIntoDatabaseKey(origin),
-                    std::move(policies));
-}
-
-// static
-InterventionPolicyDatabase::InterventionsMap
-InterventionPolicyDatabase::ReadDatabaseFromProtoFileOnSequence(
-    const base::FilePath& proto_location) {
-  DCHECK(base::PathExists(proto_location));
-
-  InterventionsMap database;
-
-  std::string proto_str;
-  if (!base::ReadFileToString(proto_location, &proto_str)) {
-    DLOG(ERROR) << "Failed to read the interventon policy database.";
-    return database;
-  }
-
-  OriginInterventionsDatabase proto;
-  if (!proto.ParseFromString(proto_str)) {
-    DLOG(ERROR) << "Unable to parse the intervention policy database proto.";
-    return database;
-  }
-
-  database.reserve(proto.origin_interventions_size());
-  for (int i = 0; i < proto.origin_interventions_size(); ++i) {
-    const OriginInterventions& origin_interventions_proto =
-        proto.origin_interventions(i);
-    OriginInterventionPolicies origin_intervention_policies(
-        origin_interventions_proto.discarding_policy(),
-        origin_interventions_proto.freezing_policy());
-    database.emplace(origin_interventions_proto.host_hash(),
-                     std::move(origin_intervention_policies));
-  }
-  return database;
-}
-
-void InterventionPolicyDatabase::OnReadDatabaseProtoFromFile(
-    InterventionsMap database) {
-  database_ = std::move(database);
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/intervention_policy_database.h b/chrome/browser/resource_coordinator/intervention_policy_database.h
deleted file mode 100644
index 42b9df1..0000000
--- a/chrome/browser/resource_coordinator/intervention_policy_database.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_RESOURCE_COORDINATOR_INTERVENTION_POLICY_DATABASE_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_INTERVENTION_POLICY_DATABASE_H_
-
-#include "base/containers/flat_map.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/version.h"
-#include "chrome/browser/resource_coordinator/intervention_policy_database.pb.h"
-#include "url/origin.h"
-
-namespace base {
-class DictionaryValue;
-}
-
-namespace resource_coordinator {
-
-// Intervention policy database, this should receive data from the
-// InterventionPolicyDatabaseComponentInstallerPolicy component once it's ready.
-//
-// It is meant to be used to assist intervention decisions made for the
-// LifecycleUnits.
-class InterventionPolicyDatabase {
- public:
-  using InterventionPolicy = OriginInterventions::InterventionPolicy;
-
-  // The intervention policies to use for a given origin.
-  struct OriginInterventionPolicies {
-    OriginInterventionPolicies(InterventionPolicy discarding_policy,
-                               InterventionPolicy freezing_policy);
-
-    InterventionPolicy discarding_policy;
-    InterventionPolicy freezing_policy;
-  };
-
-  InterventionPolicyDatabase();
-  ~InterventionPolicyDatabase();
-
-  InterventionPolicy GetDiscardingPolicy(const url::Origin& origin) const;
-  InterventionPolicy GetFreezingPolicy(const url::Origin& origin) const;
-
-  // Initialize the database with the OriginInterventionsDatabase protobuf
-  // stored in |proto_location|.
-  void InitializeDatabaseWithProtoFile(
-      const base::FilePath& proto_location,
-      const base::Version& version,
-      std::unique_ptr<base::DictionaryValue> manifest);
-
-  void AddOriginPoliciesForTesting(const url::Origin& origin,
-                                   OriginInterventionPolicies policies);
-
- protected:
-  // Map that associates the MD5 hash of an origin to its polices.
-  using InterventionsMap =
-      base::flat_map<std::string, OriginInterventionPolicies>;
-
-  friend class InterventionPolicyDatabaseTest;
-
-  const InterventionsMap& database_for_testing() { return database_; }
-
- private:
-  // Asynchronously initialize an |InterventionsMap| object with the content of
-  // the OriginInterventionsDatabase proto stored at |proto_location|.
-  static InterventionsMap ReadDatabaseFromProtoFileOnSequence(
-      const base::FilePath& proto_location);
-
-  // Needs to be called to initialize |database_| with the data read in
-  // InitializeDatabaseWithProtoAsync.
-  void OnReadDatabaseProtoFromFile(InterventionsMap database);
-
-  // The map that stores all the per-origin intervention policies.
-  InterventionsMap database_;
-
-  base::WeakPtrFactory<InterventionPolicyDatabase> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(InterventionPolicyDatabase);
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_INTERVENTION_POLICY_DATABASE_H_
diff --git a/chrome/browser/resource_coordinator/intervention_policy_database.proto b/chrome/browser/resource_coordinator/intervention_policy_database.proto
deleted file mode 100644
index 876952e..0000000
--- a/chrome/browser/resource_coordinator/intervention_policy_database.proto
+++ /dev/null
@@ -1,31 +0,0 @@
-syntax = "proto2";
-
-option optimize_for = LITE_RUNTIME;
-
-// Information about an origin and the intervention it supports.
-// Next Id: 4
-message OriginInterventions {
-  // The MD5 hash of the origin, the value used to generate this hash
-  // should be the ‘host’ part of the origin, e.g. in
-  // “http://example.com/foo” the host is “example.com”.
-  required string host_hash = 1;
-
-  // Policy associated with an intervention. A DEFAULT value indicates
-  // that the existing heuristics should be applied.
-  enum InterventionPolicy {
-    OPT_IN = 0;
-    OPT_OUT = 1;
-    DEFAULT = 2;
-  }
-  // The discarding policy.
-  required InterventionPolicy discarding_policy = 2;
-  // The freezing policy.
-  required InterventionPolicy freezing_policy = 3;
-}
-
-// The database that contains all the origins and the interventions they
-// support.
-// Next Id: 2
-message OriginInterventionsDatabase {
-  repeated OriginInterventions origin_interventions = 1;
-}
diff --git a/chrome/browser/resource_coordinator/intervention_policy_database_unittest.cc b/chrome/browser/resource_coordinator/intervention_policy_database_unittest.cc
deleted file mode 100644
index cb7c389d..0000000
--- a/chrome/browser/resource_coordinator/intervention_policy_database_unittest.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/resource_coordinator/intervention_policy_database.h"
-
-#include <map>
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/run_loop.h"
-#include "base/test/task_environment.h"
-#include "base/values.h"
-#include "chrome/browser/resource_coordinator/intervention_policy_database.pb.h"
-#include "chrome/browser/resource_coordinator/utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace resource_coordinator {
-
-namespace {
-
-using InterventionPolicy = InterventionPolicyDatabase::InterventionPolicy;
-using OriginInterventionPolicies =
-    InterventionPolicyDatabase::OriginInterventionPolicies;
-
-// Initialize a protobuf in |path| with the content of |data_map|.
-void WriteProtoToFile(
-    const base::FilePath& path,
-    std::map<url::Origin, OriginInterventionPolicies> data_map) {
-  OriginInterventionsDatabase proto;
-
-  for (const auto& iter : data_map) {
-    OriginInterventions* origin_interventions =
-        proto.add_origin_interventions();
-    EXPECT_TRUE(origin_interventions);
-    origin_interventions->set_host_hash(
-        SerializeOriginIntoDatabaseKey(iter.first));
-    origin_interventions->set_discarding_policy(iter.second.discarding_policy);
-    origin_interventions->set_freezing_policy(iter.second.freezing_policy);
-  }
-  std::string serialized_proto;
-  EXPECT_TRUE(proto.SerializeToString(&serialized_proto));
-  EXPECT_EQ(static_cast<int>(serialized_proto.length()),
-            base::WriteFile(path, serialized_proto.c_str(),
-                            serialized_proto.length()));
-}
-
-}  // namespace
-
-class InterventionPolicyDatabaseTest : public ::testing::Test {
- protected:
-  InterventionPolicyDatabaseTest() = default;
-
-  void WaitForDatabaseToBeInitialized() {
-    while (intervention_policy_database_.database_for_testing().empty())
-      test_env_.RunUntilIdle();
-  }
-
-  InterventionPolicyDatabase* GetDatabase() {
-    return &intervention_policy_database_;
-  }
-
- private:
-  base::test::TaskEnvironment test_env_;
-  InterventionPolicyDatabase intervention_policy_database_;
-};
-
-TEST_F(InterventionPolicyDatabaseTest, EndToEnd) {
-  base::ScopedTempDir temp_dir;
-  EXPECT_TRUE(temp_dir.CreateUniqueTempDir());
-  base::FilePath proto_path;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir.GetPath(), &proto_path));
-
-  std::map<url::Origin, OriginInterventionPolicies> policy_map;
-  policy_map.emplace(url::Origin::Create(GURL("https://a.com")),
-                     OriginInterventionPolicies(OriginInterventions::OPT_IN,
-                                                OriginInterventions::OPT_IN));
-  policy_map.emplace(url::Origin::Create(GURL("https://b.com")),
-                     OriginInterventionPolicies(OriginInterventions::OPT_IN,
-                                                OriginInterventions::OPT_OUT));
-  policy_map.emplace(url::Origin::Create(GURL("https://c.com")),
-                     OriginInterventionPolicies(OriginInterventions::OPT_OUT,
-                                                OriginInterventions::OPT_OUT));
-  policy_map.emplace(url::Origin::Create(GURL("https://d.com")),
-                     OriginInterventionPolicies(OriginInterventions::OPT_IN,
-                                                OriginInterventions::DEFAULT));
-  WriteProtoToFile(proto_path, policy_map);
-
-  GetDatabase()->InitializeDatabaseWithProtoFile(proto_path, base::Version(),
-                                                 nullptr);
-
-  WaitForDatabaseToBeInitialized();
-
-  for (const auto& iter : policy_map) {
-    EXPECT_EQ(iter.second.discarding_policy,
-              GetDatabase()->GetDiscardingPolicy(iter.first));
-    EXPECT_EQ(iter.second.freezing_policy,
-              GetDatabase()->GetFreezingPolicy(iter.first));
-  }
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/resource_coordinator_parts.cc b/chrome/browser/resource_coordinator/resource_coordinator_parts.cc
index 7273896..577b26d 100644
--- a/chrome/browser/resource_coordinator/resource_coordinator_parts.cc
+++ b/chrome/browser/resource_coordinator/resource_coordinator_parts.cc
@@ -11,8 +11,7 @@
 ResourceCoordinatorParts::ResourceCoordinatorParts()
 #if !defined(OS_ANDROID)
     : tab_manager_(&tab_load_tracker_),
-      tab_lifecycle_unit_source_(tab_manager_.intervention_policy_database(),
-                                 tab_manager_.usage_clock())
+      tab_lifecycle_unit_source_(tab_manager_.usage_clock())
 #endif
 {
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/resource_coordinator/session_restore_policy.cc b/chrome/browser/resource_coordinator/session_restore_policy.cc
index 27490926..47213e36 100644
--- a/chrome/browser/resource_coordinator/session_restore_policy.cc
+++ b/chrome/browser/resource_coordinator/session_restore_policy.cc
@@ -242,11 +242,8 @@
   // because they are only used very sporadically, but it is important that they
   // are loaded because if not loaded the user can miss important messages.
   bool enforce_site_engagement_score = true;
-  if (base::FeatureList::IsEnabled(
-          features::kSessionRestorePrioritizesBackgroundUseCases) &&
-      tab_data.used_in_bg) {
+  if (tab_data.used_in_bg)
     enforce_site_engagement_score = false;
-  }
 
   // Enforce a minimum site engagement score if applicable.
   if (enforce_site_engagement_score &&
@@ -390,30 +387,13 @@
 bool SessionRestorePolicy::ScoreTab(TabData* tab_data) {
   float score = 0.0f;
 
-  if (base::FeatureList::IsEnabled(
-          features::kSessionRestorePrioritizesBackgroundUseCases)) {
-    // Give higher priorities to tabs used in the background, and lowest
-    // priority to internal tabs. Apps and pinned tabs are simply treated as
-    // normal tabs.
-    if (tab_data->used_in_bg) {
-      score = 2;
-    } else if (!tab_data->is_internal) {
-      score = 1;
-    }
-  } else {
-    // Replicate the logic of the existing ordering mechanism:
-    // - apps
-    // - pinned tabs
-    // - normal tabs
-    // - internal tabs
-    // Within each category, restore newest tab first.
-    if (tab_data->is_app) {
-      score = 3;
-    } else if (tab_data->is_pinned) {
-      score = 2;
-    } else if (!tab_data->is_internal) {
-      score = 1;
-    }
+  // Give higher priorities to tabs used in the background, and lowest
+  // priority to internal tabs. Apps and pinned tabs are simply treated as
+  // normal tabs.
+  if (tab_data->used_in_bg) {
+    score = 2;
+  } else if (!tab_data->is_internal) {
+    score = 1;
   }
 
   // Refine the score using the age of the tab. More recently used tabs have
diff --git a/chrome/browser/resource_coordinator/session_restore_policy_unittest.cc b/chrome/browser/resource_coordinator/session_restore_policy_unittest.cc
index 978bd18..2f88f34d 100644
--- a/chrome/browser/resource_coordinator/session_restore_policy_unittest.cc
+++ b/chrome/browser/resource_coordinator/session_restore_policy_unittest.cc
@@ -352,13 +352,9 @@
   policy_->NotifyTabLoadStarted();
 }
 
-TEST_F(SessionRestorePolicyTest, ShouldLoadBackgroundDataExperimentEnabled) {
+TEST_F(SessionRestorePolicyTest, ShouldLoadBackgroundData) {
   using TabData = TestSessionRestorePolicy::TabData;
 
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(
-      features::kSessionRestorePrioritizesBackgroundUseCases);
-
   CreatePolicy(true);
   EXPECT_TRUE(policy_->policy_enabled());
   EXPECT_EQ(2u, policy_->simultaneous_tab_loads());
@@ -462,42 +458,9 @@
     ASSERT_GE(tab_data[i - 1].score, tab_data[i].score);
 }
 
-TEST_F(SessionRestorePolicyTest, ScoreTabExperimentDisabled) {
+TEST_F(SessionRestorePolicyTest, ScoreTab) {
   using TabData = TestSessionRestorePolicy::TabData;
 
-  TabData td_app;
-  td_app.is_app = true;
-  EXPECT_TRUE(TestSessionRestorePolicy::ScoreTab(&td_app));
-
-  TabData td_pinned;
-  td_pinned.is_pinned = true;
-  EXPECT_TRUE(TestSessionRestorePolicy::ScoreTab(&td_pinned));
-
-  TabData td_normal_young;
-  TabData td_normal_old;
-  td_normal_young.last_active = base::TimeDelta::FromSeconds(1);
-  td_normal_old.last_active = base::TimeDelta::FromDays(7);
-  EXPECT_TRUE(TestSessionRestorePolicy::ScoreTab(&td_normal_young));
-  EXPECT_TRUE(TestSessionRestorePolicy::ScoreTab(&td_normal_old));
-
-  TabData td_internal;
-  td_internal.is_internal = true;
-  EXPECT_TRUE(TestSessionRestorePolicy::ScoreTab(&td_internal));
-
-  // Check the score produces the expected ordering of tabs.
-  EXPECT_LT(td_internal.score, td_normal_old.score);
-  EXPECT_LT(td_normal_old.score, td_normal_young.score);
-  EXPECT_EQ(td_normal_young.score, td_pinned.score);
-  EXPECT_EQ(td_pinned.score, td_app.score);
-}
-
-TEST_F(SessionRestorePolicyTest, ScoreTabDefaultBehaviour) {
-  using TabData = TestSessionRestorePolicy::TabData;
-
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(
-      features::kSessionRestorePrioritizesBackgroundUseCases);
-
   TabData td_bg;
   td_bg.used_in_bg = true;
   td_bg.last_active = base::TimeDelta::FromDays(30);
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index d97137f..21f318a 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/resource_coordinator/intervention_policy_database.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
 #include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
 #include "chrome/browser/resource_coordinator/tab_activity_watcher.h"
@@ -1027,22 +1026,6 @@
         DecisionFailureReason::LIFECYCLES_ENTERPRISE_POLICY_OPT_OUT);
   }
 
-  // Apply intervention database opt-in/opt-out (policy is per origin).
-  auto intervention_policy =
-      GetTabSource()->intervention_policy_database()->GetFreezingPolicy(
-          url::Origin::Create(web_contents()->GetLastCommittedURL()));
-
-  switch (intervention_policy) {
-    case OriginInterventions::OPT_IN:
-      decision_details->AddReason(DecisionSuccessReason::GLOBAL_WHITELIST);
-      break;
-    case OriginInterventions::OPT_OUT:
-      decision_details->AddReason(DecisionFailureReason::GLOBAL_BLACKLIST);
-      break;
-    case OriginInterventions::DEFAULT:
-      break;
-  }
-
   // Apply origin trial opt-in/opt-out (policy is per page).
   switch (origin_trial_freeze_policy_) {
     case performance_manager::mojom::InterventionPolicy::kUnknown:
@@ -1070,21 +1053,6 @@
     decision_details->AddReason(
         DecisionFailureReason::LIFECYCLES_ENTERPRISE_POLICY_OPT_OUT);
   }
-
-  auto intervention_policy =
-      GetTabSource()->intervention_policy_database()->GetDiscardingPolicy(
-          url::Origin::Create(web_contents()->GetLastCommittedURL()));
-
-  switch (intervention_policy) {
-    case OriginInterventions::OPT_IN:
-      decision_details->AddReason(DecisionSuccessReason::GLOBAL_WHITELIST);
-      break;
-    case OriginInterventions::OPT_OUT:
-      decision_details->AddReason(DecisionFailureReason::GLOBAL_BLACKLIST);
-      break;
-    case OriginInterventions::DEFAULT:
-      break;
-  }
 }
 
 LifecycleUnitDiscardReason
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
index 806bb9d..7fad4df 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
@@ -173,16 +173,12 @@
 };
 
 TabLifecycleUnitSource::TabLifecycleUnitSource(
-    InterventionPolicyDatabase* intervention_policy_database,
     UsageClock* usage_clock)
     : browser_tab_strip_tracker_(this, nullptr, this),
-      intervention_policy_database_(intervention_policy_database),
       usage_clock_(usage_clock) {
   // In unit tests, tabs might already exist when TabLifecycleUnitSource is
   // instantiated. No TabLifecycleUnit is created for these tabs.
 
-  DCHECK(intervention_policy_database_);
-
   browser_tab_strip_tracker_.Init();
 }
 
@@ -233,8 +229,8 @@
   if (!g_browser_process->local_state())
     return;
 
-  tab_lifecycles_enterprise_preference_monitor_ =
-      std::make_unique<TabLifecylesEnterprisePreferenceMonitor>(
+  tab_freezing_enabled_enterprise_preference_monitor_ =
+      std::make_unique<TabFreezingEnabledPreferenceMonitor>(
           g_browser_process->local_state(),
           base::BindRepeating(
               &TabLifecycleUnitSource::SetTabLifecyclesEnterprisePolicy,
@@ -244,7 +240,7 @@
 void TabLifecycleUnitSource::OnAllLifecycleUnitsDestroyed() {
   // This needs to be freed before shutdown as PrefChangeRegistrars can't exist
   // at shutdown. Tear it down when there are no more tabs being monitored.
-  tab_lifecycles_enterprise_preference_monitor_.reset();
+  tab_freezing_enabled_enterprise_preference_monitor_.reset();
 }
 
 // static
@@ -456,36 +452,35 @@
 }
 
 void TabLifecycleUnitSource::SetTabLifecyclesEnterprisePolicy(bool enabled) {
-  tab_lifecycles_enterprise_policy_ = enabled;
+  tab_freezing_enabled_enterprise_policy_ = enabled;
 }
 
-TabLifecylesEnterprisePreferenceMonitor::
-    TabLifecylesEnterprisePreferenceMonitor(
-        PrefService* pref_service,
-        OnPreferenceChangedCallback callback)
+TabFreezingEnabledPreferenceMonitor::TabFreezingEnabledPreferenceMonitor(
+    PrefService* pref_service,
+    OnPreferenceChangedCallback callback)
     : pref_service_(pref_service), callback_(callback) {
   // Create a registrar to track changes to the setting.
   pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
   pref_change_registrar_->Init(pref_service_);
   pref_change_registrar_->Add(
-      prefs::kTabLifecyclesEnabled,
-      base::BindRepeating(&TabLifecylesEnterprisePreferenceMonitor::GetPref,
+      prefs::kTabFreezingEnabled,
+      base::BindRepeating(&TabFreezingEnabledPreferenceMonitor::GetPref,
                           base::Unretained(this)));
 
   // Do an initial check of the value.
   GetPref();
 }
 
-TabLifecylesEnterprisePreferenceMonitor::
-    ~TabLifecylesEnterprisePreferenceMonitor() = default;
+TabFreezingEnabledPreferenceMonitor::~TabFreezingEnabledPreferenceMonitor() =
+    default;
 
-void TabLifecylesEnterprisePreferenceMonitor::GetPref() {
+void TabFreezingEnabledPreferenceMonitor::GetPref() {
   bool enabled = true;
 
   // If the preference is set to false by enterprise policy then disable the
   // lifecycles feature.
   const PrefService::Preference* pref =
-      pref_service_->FindPreference(prefs::kTabLifecyclesEnabled);
+      pref_service_->FindPreference(prefs::kTabFreezingEnabled);
   if (pref->IsManaged() && !pref->GetValue()->GetBool())
     enabled = false;
 
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
index f26ead1..160961f 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
@@ -27,8 +27,7 @@
 
 namespace resource_coordinator {
 
-class InterventionPolicyDatabase;
-class TabLifecylesEnterprisePreferenceMonitor;
+class TabFreezingEnabledPreferenceMonitor;
 class TabLifecycleObserver;
 class TabLifecycleStateObserver;
 class TabLifecycleUnitExternal;
@@ -43,7 +42,6 @@
   class LifecycleStateObserver;
 
   TabLifecycleUnitSource(
-      InterventionPolicyDatabase* intervention_policy_database,
       UsageClock* usage_clock);
   ~TabLifecycleUnitSource() override;
 
@@ -64,14 +62,10 @@
   // Pretend that |tab_strip| is the TabStripModel of the focused window.
   void SetFocusedTabStripModelForTesting(TabStripModel* tab_strip);
 
-  InterventionPolicyDatabase* intervention_policy_database() const {
-    return intervention_policy_database_;
-  }
-
   // Returns the state of the tab lifecycles feature enterprise control. This
   // returns true if the feature should be enabled, false otherwise.
   bool tab_lifecycles_enterprise_policy() const {
-    return tab_lifecycles_enterprise_policy_;
+    return tab_freezing_enabled_enterprise_policy_;
   }
 
   // Returns the state of the MemoryLimitMbEnabled enterprise policy.
@@ -184,40 +178,36 @@
   // changes.
   base::ObserverList<TabLifecycleObserver>::Unchecked tab_lifecycle_observers_;
 
-  // The intervention policy database used to assist freezing/discarding
-  // decisions.
-  InterventionPolicyDatabase* intervention_policy_database_;
-
   // A clock that advances when Chrome is in use.
   UsageClock* const usage_clock_;
 
-  // The enterprise policy for overriding the tab lifecycles feature.
-  bool tab_lifecycles_enterprise_policy_ = true;
+  // The enterprise policy for overriding the tab freezing feature.
+  bool tab_freezing_enabled_enterprise_policy_ = true;
 
   // The enterprise policy for setting a limit on total physical memory usage.
   bool memory_limit_enterprise_policy_ = false;
 
   // In official production builds this monitors policy settings and reflects
-  // them in |tab_lifecycles_enterprise_policy_|.
-  std::unique_ptr<TabLifecylesEnterprisePreferenceMonitor>
-      tab_lifecycles_enterprise_preference_monitor_;
+  // them in |tab_freezing_enabled_enterprise_policy_|.
+  std::unique_ptr<TabFreezingEnabledPreferenceMonitor>
+      tab_freezing_enabled_enterprise_preference_monitor_;
 
   DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitSource);
 };
 
 // Helper class used for getting and monitoring enterprise-policy controlled
-// preferences that can control the tab lifecycles feature. Exposed for testing.
-class TabLifecylesEnterprisePreferenceMonitor {
+// preferences that can control the tab freezing feature. Exposed for testing.
+class TabFreezingEnabledPreferenceMonitor {
  public:
   using OnPreferenceChangedCallback = base::RepeatingCallback<void(bool)>;
 
   // Creates a preference monitor that monitors the provided PrefService. When
   // the preference is initially checked or changed its value is provided via
   // the provided callback.
-  TabLifecylesEnterprisePreferenceMonitor(PrefService* pref_service,
-                                          OnPreferenceChangedCallback callback);
+  TabFreezingEnabledPreferenceMonitor(PrefService* pref_service,
+                                      OnPreferenceChangedCallback callback);
 
-  ~TabLifecylesEnterprisePreferenceMonitor();
+  ~TabFreezingEnabledPreferenceMonitor();
 
  private:
   void GetPref();
@@ -226,7 +216,7 @@
   OnPreferenceChangedCallback callback_;
   std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
 
-  DISALLOW_COPY_AND_ASSIGN(TabLifecylesEnterprisePreferenceMonitor);
+  DISALLOW_COPY_AND_ASSIGN(TabFreezingEnabledPreferenceMonitor);
 };
 
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
index 5aede056..ddeac21 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
@@ -840,9 +840,9 @@
 
 }  // namespace
 
-TEST(TabLifecylesEnterprisePreferenceMonitor, ObservesChanges) {
+TEST(TabFreezingEnabledPreferenceMonitor, ObservesChanges) {
   TestingPrefServiceSimple pref_service;
-  pref_service.registry()->RegisterBooleanPref(prefs::kTabLifecyclesEnabled,
+  pref_service.registry()->RegisterBooleanPref(prefs::kTabFreezingEnabled,
                                                true);
 
   ::testing::StrictMock<MockOnPrefChanged> obs;
@@ -850,7 +850,7 @@
   // Create a monitor that dispatches to the mock. The constructor should have
   // checked the value and it should return the default.
   EXPECT_CALL(obs, OnPrefChanged(true));
-  TabLifecylesEnterprisePreferenceMonitor monitor(
+  TabFreezingEnabledPreferenceMonitor monitor(
       &pref_service, base::BindRepeating(&MockOnPrefChanged::OnPrefChanged,
                                          base::Unretained(&obs)));
   ::testing::Mock::VerifyAndClear(&obs);
@@ -858,19 +858,19 @@
   // Set the preference in an unmanaged way to false. The preference should
   // still be true.
   EXPECT_CALL(obs, OnPrefChanged(true));
-  pref_service.SetUserPref(prefs::kTabLifecyclesEnabled,
+  pref_service.SetUserPref(prefs::kTabFreezingEnabled,
                            std::make_unique<base::Value>(false));
   ::testing::Mock::VerifyAndClear(&obs);
 
   // Set the preference in a managed way to false.
   EXPECT_CALL(obs, OnPrefChanged(false));
-  pref_service.SetManagedPref(prefs::kTabLifecyclesEnabled,
+  pref_service.SetManagedPref(prefs::kTabFreezingEnabled,
                               std::make_unique<base::Value>(false));
   ::testing::Mock::VerifyAndClear(&obs);
 
   // Set the preference in a managed way to true.
   EXPECT_CALL(obs, OnPrefChanged(true));
-  pref_service.SetManagedPref(prefs::kTabLifecyclesEnabled,
+  pref_service.SetManagedPref(prefs::kTabFreezingEnabled,
                               std::make_unique<base::Value>(true));
   ::testing::Mock::VerifyAndClear(&obs);
 }
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 1d58d59..2991840 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
 #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#include "chrome/browser/resource_coordinator/intervention_policy_database.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
 #include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
 #include "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
@@ -595,108 +594,6 @@
       DecisionFailureReason::HEURISTIC_INSUFFICIENT_OBSERVATION, nullptr);
 }
 
-TEST_F(TabLifecycleUnitTest, CannotProactivelyDiscardTabIfOriginOptedOut) {
-  InterventionPolicyDatabase* policy_db =
-      GetTabLifecycleUnitSource()->intervention_policy_database();
-  policy_db->AddOriginPoliciesForTesting(
-      url::Origin::Create(web_contents_->GetLastCommittedURL()),
-      {OriginInterventions::OPT_OUT, OriginInterventions::DEFAULT});
-
-  TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_,
-                                      usage_clock_.get(), web_contents_,
-                                      tab_strip_model_.get());
-  // Advance time enough that the tab is urgent discardable.
-  test_clock_.Advance(kBackgroundUrgentProtectionTime);
-
-  // Proactive discarding shouldn't be possible, urgent and external discarding
-  // should still be possible.
-  {
-    DecisionDetails decision_details;
-    EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(
-        LifecycleUnitDiscardReason::PROACTIVE, &decision_details));
-    EXPECT_FALSE(decision_details.IsPositive());
-    EXPECT_EQ(DecisionFailureReason::GLOBAL_BLACKLIST,
-              decision_details.FailureReason());
-    EXPECT_EQ(1U, decision_details.reasons().size());
-  }
-  {
-    DecisionDetails decision_details;
-    EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(
-        LifecycleUnitDiscardReason::URGENT, &decision_details));
-    EXPECT_TRUE(decision_details.IsPositive());
-  }
-  {
-    DecisionDetails decision_details;
-    EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(
-        LifecycleUnitDiscardReason::EXTERNAL, &decision_details));
-    EXPECT_TRUE(decision_details.IsPositive());
-  }
-}
-
-TEST_F(TabLifecycleUnitTest, CannotFreezeTabIfOriginOptedOut) {
-  auto* policy_db = GetTabLifecycleUnitSource()->intervention_policy_database();
-  policy_db->AddOriginPoliciesForTesting(
-      url::Origin::Create(web_contents_->GetLastCommittedURL()),
-      InterventionPolicyDatabase::OriginInterventionPolicies(
-          OriginInterventions::DEFAULT, OriginInterventions::OPT_OUT));
-
-  TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_,
-                                      usage_clock_.get(), web_contents_,
-                                      tab_strip_model_.get());
-  TabLoadTracker::Get()->TransitionStateForTesting(web_contents_,
-                                                   LoadingState::LOADED);
-  DecisionDetails decision_details;
-  EXPECT_FALSE(tab_lifecycle_unit.CanFreeze(&decision_details));
-  EXPECT_FALSE(decision_details.IsPositive());
-  EXPECT_EQ(DecisionFailureReason::GLOBAL_BLACKLIST,
-            decision_details.FailureReason());
-}
-
-TEST_F(TabLifecycleUnitTest, OptInTabsGetsDiscarded) {
-  auto* policy_db = GetTabLifecycleUnitSource()->intervention_policy_database();
-  policy_db->AddOriginPoliciesForTesting(
-      url::Origin::Create(web_contents_->GetLastCommittedURL()),
-      InterventionPolicyDatabase::OriginInterventionPolicies(
-          OriginInterventions::OPT_IN, OriginInterventions::DEFAULT));
-
-  TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_,
-                                      usage_clock_.get(), web_contents_,
-                                      tab_strip_model_.get());
-
-  // Mark the tab as recently audible, this should protect it from being
-  // discarded.
-  tab_lifecycle_unit.SetRecentlyAudible(true);
-
-  DecisionDetails decision_details;
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(
-      LifecycleUnitDiscardReason::PROACTIVE, &decision_details));
-  EXPECT_TRUE(decision_details.IsPositive());
-  EXPECT_EQ(DecisionSuccessReason::GLOBAL_WHITELIST,
-            decision_details.SuccessReason());
-}
-
-TEST_F(TabLifecycleUnitTest, CanFreezeOptedInTabs) {
-  auto* policy_db = GetTabLifecycleUnitSource()->intervention_policy_database();
-  policy_db->AddOriginPoliciesForTesting(
-      url::Origin::Create(web_contents_->GetLastCommittedURL()),
-      {OriginInterventions::DEFAULT, OriginInterventions::OPT_IN});
-
-  TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_,
-                                      usage_clock_.get(), web_contents_,
-                                      tab_strip_model_.get());
-  TabLoadTracker::Get()->TransitionStateForTesting(web_contents_,
-                                                   LoadingState::LOADED);
-
-  // Mark the tab as recently audible, this should protect it from being frozen.
-  tab_lifecycle_unit.SetRecentlyAudible(true);
-
-  DecisionDetails decision_details;
-  EXPECT_TRUE(tab_lifecycle_unit.CanFreeze(&decision_details));
-  EXPECT_TRUE(decision_details.IsPositive());
-  EXPECT_EQ(DecisionSuccessReason::GLOBAL_WHITELIST,
-            decision_details.SuccessReason());
-}
-
 TEST_F(TabLifecycleUnitTest, CannotFreezeAFrozenTab) {
   TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_,
                                       usage_clock_.get(), web_contents_,
@@ -885,12 +782,9 @@
   EXPECT_TRUE(decision_details.IsPositive());
   decision_details.Clear();
 
-  // Use one of the heuristics on the tab to prevent it from being discarded.
-  InterventionPolicyDatabase* policy_db =
-      GetTabLifecycleUnitSource()->intervention_policy_database();
-  policy_db->AddOriginPoliciesForTesting(
-      url::Origin::Create(web_contents_->GetLastCommittedURL()),
-      {OriginInterventions::OPT_OUT, OriginInterventions::OPT_OUT});
+  // Use one of the heuristics on the tab to prevent it from being frozen or
+  // discarded.
+  ScopedEnterpriseOptOut enterprise_opt_out;
 
   EXPECT_FALSE(tab_lifecycle_unit.CanFreeze(&decision_details));
   EXPECT_FALSE(decision_details.IsPositive());
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index 1bc7596ee..302aa6e 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -67,7 +67,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/page_importance_signals.h"
 #include "net/base/network_change_notifier.h"
-#include "third_party/blink/public/platform/web_sudden_termination_disabler_type.h"
+#include "third_party/blink/public/common/sudden_termination_disabler_type.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h"
@@ -169,7 +169,6 @@
   proactive_freeze_discard_params_ =
       GetStaticProactiveTabFreezeAndDiscardParams();
   tab_load_tracker_->AddObserver(this);
-  intervention_policy_database_.reset(new InterventionPolicyDatabase());
 
   // TabManager works in the absence of DesktopSessionDurationTracker for tests.
   if (metrics::DesktopSessionDurationTracker::IsInitialized())
diff --git a/chrome/browser/resource_coordinator/tab_manager.h b/chrome/browser/resource_coordinator/tab_manager.h
index 680891d..779ab3d 100644
--- a/chrome/browser/resource_coordinator/tab_manager.h
+++ b/chrome/browser/resource_coordinator/tab_manager.h
@@ -20,7 +20,6 @@
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#include "chrome/browser/resource_coordinator/intervention_policy_database.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_source_observer.h"
@@ -159,10 +158,6 @@
   // non-zero only during session restore.
   int restored_tab_count() const { return restored_tab_count_; }
 
-  InterventionPolicyDatabase* intervention_policy_database() {
-    return intervention_policy_database_.get();
-  }
-
   UsageClock* usage_clock() { return &usage_clock_; }
 
   // Returns true if the tab was created by session restore and has not finished
@@ -481,10 +476,6 @@
   // session restore.
   std::unique_ptr<TabManagerStatsCollector> stats_collector_;
 
-  // The intervention policy database, should be initialized by
-  // InterventionPolicyDatabaseComponentInstallerPolicy.
-  std::unique_ptr<InterventionPolicyDatabase> intervention_policy_database_;
-
   // Last time at which a LifecycleUnit was temporarily unfrozen.
   base::TimeTicks last_unfreeze_time_;
 
diff --git a/chrome/browser/resource_coordinator/tab_manager_features.cc b/chrome/browser/resource_coordinator/tab_manager_features.cc
index 35fb02b..22dcc4f5 100644
--- a/chrome/browser/resource_coordinator/tab_manager_features.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_features.cc
@@ -28,12 +28,6 @@
     resource_coordinator::kProactiveTabFreezeAndDiscardFeatureName,
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables prioritization of sites that communicate with the user while in the
-// background (email, chat, calendar, etc) during session restore.
-const base::Feature kSessionRestorePrioritizesBackgroundUseCases{
-    "SessionRestorePrioritizesBackgroundUseCases",
-    base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables the site characteristics database.
 const base::Feature kSiteCharacteristicsDatabase{
     "SiteCharacteristicsDatabase", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/browser/resource_coordinator/tab_manager_features.h b/chrome/browser/resource_coordinator/tab_manager_features.h
index daef4bd..dc061ce 100644
--- a/chrome/browser/resource_coordinator/tab_manager_features.h
+++ b/chrome/browser/resource_coordinator/tab_manager_features.h
@@ -15,7 +15,6 @@
 
 extern const base::Feature kCustomizedTabLoadTimeout;
 extern const base::Feature kProactiveTabFreezeAndDiscard;
-extern const base::Feature kSessionRestorePrioritizesBackgroundUseCases;
 extern const base::Feature kSiteCharacteristicsDatabase;
 extern const base::Feature kStaggeredBackgroundTabOpening;
 extern const base::Feature kStaggeredBackgroundTabOpeningExperiment;
diff --git a/chrome/browser/resource_coordinator/tab_metrics_logger.cc b/chrome/browser/resource_coordinator/tab_metrics_logger.cc
index c9eadc15..490984df 100644
--- a/chrome/browser/resource_coordinator/tab_metrics_logger.cc
+++ b/chrome/browser/resource_coordinator/tab_metrics_logger.cc
@@ -32,7 +32,7 @@
 #include "content/public/common/page_importance_signals.h"
 #include "net/base/mime_util.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
-#include "third_party/blink/public/platform/web_sudden_termination_disabler_type.h"
+#include "third_party/blink/public/common/sudden_termination_disabler_type.h"
 #include "url/gurl.h"
 
 using metrics::TabMetricsEvent;
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index db6266e..8346f4f 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -365,11 +365,13 @@
     "$root_gen_dir/chrome/bluetooth_internals_resources.pak",
     "$root_gen_dir/chrome/omnibox_resources.pak",
     "$root_gen_dir/chrome/usb_internals_resources.pak",
+    "$root_gen_dir/components/sync_driver_resources.pak",
   ]
   deps = [
     "//chrome/browser/resources/bluetooth_internals:resources",
     "//chrome/browser/resources/omnibox:resources",
     "//chrome/browser/resources/usb_internals:resources",
+    "//components/sync/driver:resources",
   ]
 }
 
diff --git a/chrome/browser/resources/browser_switch/BUILD.gn b/chrome/browser/resources/browser_switch/BUILD.gn
index 3546ed2..7b25cc5 100644
--- a/chrome/browser/resources/browser_switch/BUILD.gn
+++ b/chrome/browser/resources/browser_switch/BUILD.gn
@@ -20,7 +20,6 @@
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:load_time_data.m",
   ]
-  extra_deps = [ ":app_module" ]
 }
 
 js_library("browser_switch_proxy") {
diff --git a/chrome/browser/resources/chromeos/camera/src/js/BUILD.gn b/chrome/browser/resources/chromeos/camera/src/js/BUILD.gn
index 64d48a3..7ea176a9 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/BUILD.gn
+++ b/chrome/browser/resources/chromeos/camera/src/js/BUILD.gn
@@ -19,6 +19,7 @@
   deps = [
     ":background",
     ":intent",
+    ":metrics",
     ":nav",
     ":resolution_event_broker",
     ":state",
@@ -34,6 +35,12 @@
   ]
 }
 
+js_library("metrics") {
+  deps = [
+    "externs:chrome_platform_analytics",
+  ]
+}
+
 js_library("resolution_event_broker") {
 }
 
diff --git a/chrome/browser/resources/chromeos/camera/src/js/device/constraints_preferrer.js b/chrome/browser/resources/chromeos/camera/src/js/device/constraints_preferrer.js
index 14e0f0b..14ef884 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/device/constraints_preferrer.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/device/constraints_preferrer.js
@@ -231,8 +231,12 @@
 
     devices.forEach(({deviceId, videoResols, videoMaxFps, fpsRanges}) => {
       this.deviceResolutions_[deviceId] = videoResols;
-      let {width = -1, height = -1} = this.prefResolution_[deviceId] || {};
-      if (!videoResols.find(([w, h]) => w === width && h === height)) {
+      const findResol = (width, height) =>
+          videoResols.find(([w, h]) => w == width && h == height) &&
+          {width, height};
+      let {width, height} = this.prefResolution_[deviceId] ||
+          findResol(1920, 1080) || findResol(1280, 720) || {};
+      if (findResol(width, height) === undefined) {
         [width, height] = videoResols.reduce(
             (maxR, R) => (maxR[0] * maxR[1] < R[0] * R[1] ? R : maxR), [0, 0]);
       }
@@ -326,7 +330,7 @@
     };
 
     const toConstraints = (width, height, fps) => ({
-      audio: true,
+      audio: {echoCancellation: false},
       video: {
         deviceId: {exact: deviceId},
         frameRate: fps ? {exact: fps} : {min: 24},
@@ -455,8 +459,13 @@
                 (captureR, r) => (r[0] > captureR[0] ? r : captureR), [0, -1]);
           }
 
+          const filterByScreenSize = ([w, h], index, arr) =>
+              index == arr.length - 1 ||
+              (w <= window.screen.width && h <= window.screen.height);
+
           const candidates = [...previewRs]
                                  .sort(([w, h], [w2, h2]) => w2 - w)
+                                 .filter(filterByScreenSize)
                                  .map(([width, height]) => ({
                                         audio: false,
                                         video: {
diff --git a/chrome/browser/resources/chromeos/camera/src/js/externs/BUILD.gn b/chrome/browser/resources/chromeos/camera/src/js/externs/BUILD.gn
new file mode 100644
index 0000000..e414990
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/js/externs/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_library("chrome_platform_analytics") {
+  sources = []
+
+  externs_list = [ "chrome_platform_analytics.js" ]
+}
diff --git a/chrome/browser/resources/chromeos/camera/src/js/externs/chrome_platform_analytics.js b/chrome/browser/resources/chromeos/camera/src/js/externs/chrome_platform_analytics.js
new file mode 100644
index 0000000..ee9ab920
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/js/externs/chrome_platform_analytics.js
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Externs for chrome-platform-analytics library.
+ * @see https://github.com/googlearchive/chrome-platform-analytics/wiki
+ * @externs
+ */
+
+/* eslint-disable valid-jsdoc */
+
+var analytics = {};
+
+/**
+ * @constructor
+ * @struct
+ */
+analytics.EventBuilder = function() {};
+
+/**
+ * @param {!analytics.EventBuilder.Dimension} dimension
+ * @return {!analytics.EventBuilder}
+ */
+analytics.EventBuilder.prototype.dimension = function(dimension) {};
+
+/**
+ * @param {string} category
+ * @return {!analytics.EventBuilder}
+ */
+analytics.EventBuilder.prototype.category = function(category) {};
+
+/**
+ * @param {string} action
+ * @return {!analytics.EventBuilder}
+ */
+analytics.EventBuilder.prototype.action = function(action) {};
+
+/**
+ * @param {string} label
+ * @return {!analytics.EventBuilder}
+ */
+analytics.EventBuilder.prototype.label = function(label) {};
+
+/**
+ * @param {number} value
+ * @return {!analytics.EventBuilder}
+ */
+analytics.EventBuilder.prototype.value = function(value) {};
+
+/**
+ * @typedef {{
+ *   index: number,
+ *   value: string
+ * }}
+ */
+analytics.EventBuilder.Dimension;
+
+/**
+ * @interface
+ */
+analytics.Tracker = function() {};
+
+/**
+ * @param {!analytics.EventBuilder} eventBuilder
+ */
+analytics.Tracker.prototype.send;
diff --git a/chrome/browser/resources/chromeos/camera/src/js/metrics.js b/chrome/browser/resources/chromeos/camera/src/js/metrics.js
index 6a257a8..601def3 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/metrics.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/metrics.js
@@ -16,21 +16,18 @@
 
 /**
  * Event builder for basic metrics.
- * @type {analytics.EventBuilder}
+ * @type {?analytics.EventBuilder}
  * @private
  */
 cca.metrics.base_ = null;
 
-/**
- * @type {analytics}
- */
 var analytics = window['analytics'] || {};
 
 /**
  * Fixes analytics.EventBuilder's dimension() method.
  * @param {number} i
  * @param {string} v
- * @return {analytics.EventBuilder}
+ * @return {!analytics.EventBuilder}
  */
 analytics.EventBuilder.prototype.dimen = function(i, v) {
   return this.dimension({index: i, value: v});
@@ -39,6 +36,7 @@
 /**
  * Promise for Google Analytics tracker.
  * @type {Promise<analytics.Tracker>}
+ * @suppress {checkTypes}
  * @private
  */
 cca.metrics.ga_ = (function() {
@@ -83,7 +81,7 @@
 /**
  * Returns event builder for the metrics type: launch.
  * @param {boolean} ackMigrate Whether acknowledged to migrate during launch.
- * @return {analytics.EventBuilder}
+ * @return {!analytics.EventBuilder}
  * @private
  */
 cca.metrics.launchType_ = function(ackMigrate) {
@@ -95,13 +93,12 @@
  * Returns event builder for the metrics type: capture.
  * @param {?string} facingMode Camera facing-mode of the capture.
  * @param {number} length Length of 1 minute buckets for captured video.
- * @param {number} width The width of the capture resolution.
- * @param {number} height The height of the capture resolution.
- * @return {analytics.EventBuilder}
+ * @param {!{width: number, height: number}} resolution Capture resolution.
+ * @return {!analytics.EventBuilder}
  * @private
  */
 cca.metrics.captureType_ = function(facingMode, length, {width, height}) {
-  var condState = (states, cond, strict) => {
+  var condState = (states, cond = undefined, strict = undefined) => {
     // Return the first existing state among the given states only if there is
     // no gate condition or the condition is met.
     const prerequisite = !cond || cca.state.get(cond);
@@ -130,7 +127,7 @@
 
 /**
  * Metrics types.
- * @enum {function(*=)}
+ * @enum {function(...): !analytics.EventBuilder}
  */
 cca.metrics.Type = {
   LAUNCH: cca.metrics.launchType_,
@@ -139,7 +136,7 @@
 
 /**
  * Logs the given metrics.
- * @param {cca.metrics.Type} type Metrics type.
+ * @param {!cca.metrics.Type} type Metrics type.
  * @param {...*} args Optional rest parameters for logging metrics.
  */
 cca.metrics.log = function(type, ...args) {
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
index 935ce7d..0bd566f 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
@@ -254,7 +254,10 @@
       // deviceId is set to null for requesting camera with default facing.
       constraint.facingMode = {exact: defaultFacing};
     }
-    return {audio: videoMode, video: constraint};
+    return {
+      audio: videoMode ? {echoCancellation: false} : false,
+      video: constraint,
+    };
   });
 };
 
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/preview.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/preview.js
index a75252a..679a0572 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/preview.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/preview.js
@@ -223,6 +223,31 @@
     element.textContent = val;
   };
 
+  const buildInverseTable = (obj, prefix) => {
+    const tbl = {};
+    for (const [key, val] of Object.entries(obj)) {
+      if (!key.startsWith(prefix)) {
+        continue;
+      }
+      if (tbl.hasOwnProperty(val)) {
+        console.error(`Duplicated value: ${val}`);
+        continue;
+      }
+      tbl[val] = key.slice(prefix.length);
+    }
+    return tbl;
+  };
+
+  const afStateName = buildInverseTable(
+      cros.mojom.AndroidControlAfState, 'ANDROID_CONTROL_AF_STATE_');
+  const aeStateName = buildInverseTable(
+      cros.mojom.AndroidControlAeState, 'ANDROID_CONTROL_AE_STATE_');
+  const awbStateName = buildInverseTable(
+      cros.mojom.AndroidControlAwbState, 'ANDROID_CONTROL_AWB_STATE_');
+  const aeAntibandingModeName = buildInverseTable(
+      cros.mojom.AndroidControlAeAntibandingMode,
+      'ANDROID_CONTROL_AE_ANTIBANDING_MODE_');
+
   const tag = cros.mojom.CameraMetadataTag;
   const metadataEntryHandlers = {
     [tag.ANDROID_LENS_FOCUS_DISTANCE]: ([value]) => {
@@ -233,32 +258,36 @@
       const focusDistance = (100 / value).toFixed(1);
       showValue('#preview-focus-distance', `${focusDistance} cm`);
     },
-    [tag.ANDROID_LENS_FOCAL_LENGTH]: ([value]) => {
-      const focalLength = value.toFixed(1);
-      showValue('#preview-focal-length', `${focalLength} mm`);
-    },
-    [tag.ANDROID_LENS_APERTURE]: ([value]) => {
-      const aperture = value.toFixed(1);
-      showValue('#preview-aperture', `f/${aperture}`);
+    [tag.ANDROID_CONTROL_AF_STATE]: ([value]) => {
+      showValue('#preview-af-state', afStateName[value]);
     },
     [tag.ANDROID_SENSOR_SENSITIVITY]: ([value]) => {
       const sensitivity = value;
       showValue('#preview-sensitivity', `ISO ${sensitivity}`);
     },
     [tag.ANDROID_SENSOR_EXPOSURE_TIME]: ([value]) => {
-      const shutterSpeed = Math.round(1e9 / value).toFixed(0);
+      const shutterSpeed = Math.round(1e9 / value);
       showValue('#preview-exposure-time', `1/${shutterSpeed}`);
     },
     [tag.ANDROID_SENSOR_FRAME_DURATION]: ([value]) => {
       const frameFrequency = Math.round(1e9 / value);
       showValue('#preview-frame-duration', `${frameFrequency} Hz`);
     },
+    [tag.ANDROID_CONTROL_AE_ANTIBANDING_MODE]: ([value]) => {
+      showValue('#preview-ae-antibanding-mode', aeAntibandingModeName[value]);
+    },
+    [tag.ANDROID_CONTROL_AE_STATE]: ([value]) => {
+      showValue('#preview-ae-state', aeStateName[value]);
+    },
     [tag.ANDROID_COLOR_CORRECTION_GAINS]: ([valueRed, , , valueBlue]) => {
       const wbGainRed = valueRed.toFixed(2);
       showValue('#preview-wb-gain-red', `${wbGainRed}x`);
       const wbGainBlue = valueBlue.toFixed(2);
       showValue('#preview-wb-gain-blue', `${wbGainBlue}x`);
     },
+    [tag.ANDROID_CONTROL_AWB_STATE]: ([value]) => {
+      showValue('#preview-awb-state', awbStateName[value]);
+    },
     [tag.ANDROID_CONTROL_AF_MODE]: ([value]) => {
       displayCategory('#preview-af', value !== tag.CONTROL_AF_MODE_OFF);
     },
diff --git a/chrome/browser/resources/chromeos/camera/src/views/main.html b/chrome/browser/resources/chromeos/camera/src/views/main.html
index e46d0af..81200bf 100644
--- a/chrome/browser/resources/chromeos/camera/src/views/main.html
+++ b/chrome/browser/resources/chromeos/camera/src/views/main.html
@@ -82,19 +82,21 @@
           <div id="preview-af" class="metadata-row">
             <span class="metadata-category">AF</span>
             <div id="preview-focus-distance" class="metadata-value"></div>
-            <div id="preview-focal-length" class="metadata-value"></div>
-            <div id="preview-aperture" class="metadata-value"></div>
+            <div id="preview-af-state" class="metadata-value"></div>
           </div>
           <div id="preview-ae" class="metadata-row">
             <span class="metadata-category">AE</span>
             <div id="preview-sensitivity" class="metadata-value"></div>
             <div id="preview-exposure-time" class="metadata-value"></div>
             <div id="preview-frame-duration" class="metadata-value"></div>
+            <div id="preview-ae-antibanding-mode" class="metadata-value"></div>
+            <div id="preview-ae-state" class="metadata-value"></div>
           </div>
           <div id="preview-awb" class="metadata-row">
             <span class="metadata-category">AWB</span>
             <div id="preview-wb-gain-red" class="metadata-value"></div>
             <div id="preview-wb-gain-blue" class="metadata-value"></div>
+            <div id="preview-awb-state" class="metadata-value"></div>
           </div>
         </div>
         <div id="preview-grid">
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
index cc9bb94..101523e 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
@@ -474,8 +474,27 @@
           // element, should be treated as a character offset.
           (!newNode.state[StateType.EDITABLE] ||
            newNode.state[StateType.RICHLY_EDITABLE]) &&
-          newNode.children[newIndex]) {
-        // Valid child node offset.
+          newIndex <= newNode.children.length) {
+        // Valid child node offset. Note that there is a special case where
+        // |newIndex == node.children.length|. In these cases, we actually want
+        // to position the cursor at the end of the text of
+        // |node.children[newIndex - 1]|.
+        // |newIndex| is assumed to be > 0.
+        if (newIndex == newNode.children.length) {
+          // Take the last child.
+          newNode = newNode.lastChild;
+
+          // The |newIndex| is either a text offset or a child offset.
+          if (newNode.role == RoleType.STATIC_TEXT) {
+            newIndex = newNode.name.length;
+            isTextIndex = true;
+            break;
+          }
+
+          // The last valid child index.
+          newIndex--;
+        }
+
         newNode = newNode.children[newIndex];
         newIndex = 0;
       } else {
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
index 5882679..4700ebd3 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
@@ -519,6 +519,20 @@
   });
 });
 
+TEST_F('ChromeVoxCursorsTest', 'DeepEquivalencyBeyondLastChild', function() {
+  this.runWithLoadedTree(function() {/*!
+    <p>test</p>
+  */}, function(root) {
+    var paragraph = root.find({role: RoleType.PARAGRAPH});
+    assertEquals(1, paragraph.children.length);
+    var cursor = new cursors.Cursor(paragraph, 1);
+
+    var deep = cursor.deepEquivalent;
+    assertEquals(RoleType.STATIC_TEXT, deep.node.role);
+    assertEquals(4, deep.index);
+  });
+});
+
 TEST_F('ChromeVoxCursorsTest', 'SelectionAdjustmentsRichText', function() {
   this.runWithLoadedTree(function() {/*!
     <div contenteditable><p>test</p><p>123</p></div>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
index 45ab186..3980ec9 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -2156,6 +2156,9 @@
     if (node.placeholder)
       this.append_(buff, node.placeholder);
 
+    if (node.tooltip)
+      this.append_(buff, node.tooltip);
+
     if (AutomationPredicate.checkable(node))
       this.format_(
           node, '@hint_checkable', buff, ruleStr, undefined, hintProperties);
diff --git a/chrome/browser/resources/chromeos/crostini_installer/BUILD.gn b/chrome/browser/resources/chromeos/crostini_installer/BUILD.gn
index b51117c..2ce6341 100644
--- a/chrome/browser/resources/chromeos/crostini_installer/BUILD.gn
+++ b/chrome/browser/resources/chromeos/crostini_installer/BUILD.gn
@@ -18,14 +18,10 @@
 }
 
 js_library("app") {
-  sources = [
-    "$root_gen_dir/chrome/browser/resources/chromeos/crostini_installer/app.js",
-  ]
   deps = [
     ":browser_proxy",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
-  extra_deps = [ ":app_module" ]
 }
 
 js_library("browser_proxy") {
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog.css b/chrome/browser/resources/chromeos/login/oobe_dialog.css
index a538999..81c59b8 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog.css
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog.css
@@ -90,3 +90,7 @@
   --iron-icon-width: 32px;
   --iron-icon-fill-color: var(--google-blue-600);
 }
+
+.slot-container {
+  position: relative;
+}
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog.html b/chrome/browser/resources/chromeos/login/oobe_dialog.html
index 2934fa87..95d5040 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog.html
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog.html
@@ -63,26 +63,26 @@
     <link rel="stylesheet" href="oobe_flex_layout.css">
     <style include="cr-shared-style"></style>
     <div id="header-container" hidden="[[noHeader]]">
-      <div id="oobe-icon-div">
+      <div id="oobe-icon-div" class="slot-container">
         <slot name="oobe-icon"></slot>
       </div>
-      <div id="oobe-title" class="layout vertical end-justified">
+      <div id="oobe-title" class="slot-container layout vertical end-justified">
         <slot name="title"></slot>
       </div>
-      <div id="oobe-subtitle" class="layout vertical">
+      <div id="oobe-subtitle" class="slot-container layout vertical">
         <slot name="subtitle"></slot>
       </div>
-      <div id="oobe-progress" class="layout vertical">
+      <div id="oobe-progress" class="slot-container layout vertical">
         <slot name="progress"></slot>
       </div>
     </div>
     <div id="footer-container" noFooterPadding$="[[noFooterPadding]]"
-        class="flex layout vertical">
+        class="slot-container flex layout vertical">
       <slot name="footer"></slot>
     </div>
     <template is="dom-if" if="[[hasButtons]]">
       <div id="oobe-bottom" hideShadow$="[[hideShadow]]"
-          class="layout horizontal center">
+          class="slot-container layout horizontal center">
         <slot name="bottom-buttons"></slot>
       </div>
     </template>
diff --git a/chrome/browser/resources/chromeos/set_time_dialog/BUILD.gn b/chrome/browser/resources/chromeos/set_time_dialog/BUILD.gn
index 7e924f7..ce57213 100644
--- a/chrome/browser/resources/chromeos/set_time_dialog/BUILD.gn
+++ b/chrome/browser/resources/chromeos/set_time_dialog/BUILD.gn
@@ -3,8 +3,10 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/polymer.gni")
 
 js_type_check("closure_compile") {
+  is_polymer3 = true
   deps = [
     ":set_time_browser_proxy",
     ":set_time_dialog",
@@ -14,17 +16,29 @@
 js_library("set_time_dialog") {
   deps = [
     ":set_time_browser_proxy",
-    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
-    "//ui/webui/resources/js:assert",
-    "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js:load_time_data",
-    "//ui/webui/resources/js:web_ui_listener_behavior",
+    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js:load_time_data.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
 }
 
 js_library("set_time_browser_proxy") {
   deps = [
-    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:cr.m",
   ]
   externs_list = [ "$externs_path/chrome_send.js" ]
 }
+
+polymer_modulizer("set_time_dialog") {
+  js_file = "set_time_dialog.js"
+  html_file = "set_time_dialog.html"
+  html_type = "v3-ready"
+}
+
+group("polymer3_elements") {
+  deps = [
+    ":set_time_dialog_module",
+  ]
+}
diff --git a/chrome/browser/resources/chromeos/set_time_dialog/set_time.html b/chrome/browser/resources/chromeos/set_time_dialog/set_time.html
new file mode 100644
index 0000000..a519d48
--- /dev/null
+++ b/chrome/browser/resources/chromeos/set_time_dialog/set_time.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<head>
+<meta charset="utf-8">
+<title>$i18n{setTimeTitle}</title>
+<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
+</head>
+
+<body>
+  <set-time-dialog></set-time-dialog>
+  <script type="module" src="set_time_dialog.js"></script>
+</body>
+</html>
diff --git a/chrome/browser/resources/chromeos/set_time_dialog/set_time_browser_proxy.html b/chrome/browser/resources/chromeos/set_time_dialog/set_time_browser_proxy.html
deleted file mode 100644
index 8cf05d3..0000000
--- a/chrome/browser/resources/chromeos/set_time_dialog/set_time_browser_proxy.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<link rel="import" href="chrome://resources/html/cr.html">
-<script src="set_time_browser_proxy.js"></script>
diff --git a/chrome/browser/resources/chromeos/set_time_dialog/set_time_browser_proxy.js b/chrome/browser/resources/chromeos/set_time_dialog/set_time_browser_proxy.js
index ce25318..48a94b44 100644
--- a/chrome/browser/resources/chromeos/set_time_dialog/set_time_browser_proxy.js
+++ b/chrome/browser/resources/chromeos/set_time_dialog/set_time_browser_proxy.js
@@ -3,61 +3,57 @@
 // found in the LICENSE file.
 
 /** @fileoverview A helper object used by the "Set Time" dialog. */
-cr.define('settime', function() {
-  /** @interface */
-  class SetTimeBrowserProxy {
-    /** Notifies C++ code that it's safe to call JS functions. */
-    sendPageReady() {}
 
-    /** @param {number} timeInSeconds */
-    setTimeInSeconds(timeInSeconds) {}
+import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
 
-    /** @param {string} timezone */
-    setTimezone(timezone) {}
+/** @interface */
+export class SetTimeBrowserProxy {
+  /** Notifies C++ code that it's safe to call JS functions. */
+  sendPageReady() {}
 
-    /** Closes the dialog. */
-    dialogClose() {}
+  /** @param {number} timeInSeconds */
+  setTimeInSeconds(timeInSeconds) {}
 
-    /**
-     * Notifies C++ code that done button was clicked.
-     * @param {number} timeInSeconds Seconds since epoch representing the date
-     *     on the dialog inputs.
-     */
-    doneClicked(timeInSeconds) {}
+  /** @param {string} timezone */
+  setTimezone(timezone) {}
+
+  /** Closes the dialog. */
+  dialogClose() {}
+
+  /**
+   * Notifies C++ code that done button was clicked.
+   * @param {number} timeInSeconds Seconds since epoch representing the date
+   *     on the dialog inputs.
+   */
+  doneClicked(timeInSeconds) {}
+}
+
+/** @implements {SetTimeBrowserProxy} */
+export class SetTimeBrowserProxyImpl {
+  /** @override */
+  sendPageReady() {
+    chrome.send('setTimePageReady');
   }
 
-  /** @implements {settime.SetTimeBrowserProxy} */
-  class SetTimeBrowserProxyImpl {
-    /** @override */
-    sendPageReady() {
-      chrome.send('setTimePageReady');
-    }
-
-    /** @override */
-    setTimeInSeconds(timeInSeconds) {
-      chrome.send('setTimeInSeconds', [timeInSeconds]);
-    }
-
-    /** @override */
-    setTimezone(timezone) {
-      chrome.send('setTimezone', [timezone]);
-    }
-
-    /** @override */
-    dialogClose() {
-      chrome.send('dialogClose');
-    }
-
-    /** @override */
-    doneClicked(timeInSeconds) {
-      chrome.send('doneClicked', [timeInSeconds]);
-    }
+  /** @override */
+  setTimeInSeconds(timeInSeconds) {
+    chrome.send('setTimeInSeconds', [timeInSeconds]);
   }
 
-  cr.addSingletonGetter(SetTimeBrowserProxyImpl);
+  /** @override */
+  setTimezone(timezone) {
+    chrome.send('setTimezone', [timezone]);
+  }
 
-  return {
-    SetTimeBrowserProxy: SetTimeBrowserProxy,
-    SetTimeBrowserProxyImpl: SetTimeBrowserProxyImpl,
-  };
-});
+  /** @override */
+  dialogClose() {
+    chrome.send('dialogClose');
+  }
+
+  /** @override */
+  doneClicked(timeInSeconds) {
+    chrome.send('doneClicked', [timeInSeconds]);
+  }
+}
+
+addSingletonGetter(SetTimeBrowserProxyImpl);
diff --git a/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html b/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html
index e74e069..ebe30c7 100644
--- a/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html
+++ b/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html
@@ -1,30 +1,3 @@
-<!doctype html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}">
-<head>
-<meta charset="utf-8">
-<title>$i18n{setTimeTitle}</title>
-
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/load_time_data.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_page_host_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="set_time_browser_proxy.html">
-
-<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
-
-<script src="strings.js"></script>
-
-</head>
-
-<body>
-  <dom-module id="set-time-dialog">
-    <template>
       <style include="cr-page-host-style cr-shared-style md-select">
         :host {
           user-select: none;
@@ -143,10 +116,3 @@
         </div>
       </cr-dialog>
     </template>
-    <script src="set_time_dialog.js"></script>
-  </dom-module>
-
-  <set-time-dialog></set-time-dialog>
-
-</body>
-</html>
diff --git a/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.js b/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.js
index 7f8c5d4b..272b528 100644
--- a/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.js
+++ b/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.js
@@ -12,8 +12,19 @@
  * when the user changes the time or timezone.
  */
 
-(function() {
-'use strict';
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import 'chrome://resources/cr_elements/cr_page_host_style_css.m.js';
+import 'chrome://resources/cr_elements/md_select_css.m.js';
+import 'chrome://resources/cr_elements/shared_style_css.m.js';
+import './strings.m.js';
+
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {SetTimeBrowserProxy, SetTimeBrowserProxyImpl} from './set_time_browser_proxy.js';
 
 /**
  * @return {!Array<!{id: string, name: string, selected: Boolean}>} Items for
@@ -103,6 +114,8 @@
 Polymer({
   is: 'set-time-dialog',
 
+  _template: html`{__html_template__}`,
+
   // Remove listeners on detach.
   behaviors: [WebUIListenerBehavior],
 
@@ -171,12 +184,12 @@
    */
   timeTimeoutId_: null,
 
-  /** @private {?settime.SetTimeBrowserProxy} */
+  /** @private {?SetTimeBrowserProxy} */
   browserProxy_: null,
 
   /** @override */
   created: function() {
-    this.browserProxy_ = settime.SetTimeBrowserProxyImpl.getInstance();
+    this.browserProxy_ = SetTimeBrowserProxyImpl.getInstance();
   },
 
   /** @override */
@@ -339,4 +352,3 @@
     this.browserProxy_.dialogClose();
   },
 });
-})();
diff --git a/chrome/browser/resources/dev_ui/dev_ui_loader_error.html b/chrome/browser/resources/dev_ui/dev_ui_loader_error.html
new file mode 100644
index 0000000..76ca63e
--- /dev/null
+++ b/chrome/browser/resources/dev_ui/dev_ui_loader_error.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>DevUI DFM Loader</title>
+  <style>
+body {
+  align-items: center;
+  display: flex;
+  flex-direction: column;
+  font-family: sans, Arial, sans-serif;
+  font-size: 93.75%;
+  justify-content: flex-start;
+}
+
+#failure-message {
+  display: inline-block;
+  padding-top: 100px;
+}
+
+h1.heading {
+  color: rgb(32, 33, 36);
+  font-size: 1.6em;
+  font-weight: normal;
+  line-height: 1.25em;
+  margin: 0 0 15px 0;
+}
+
+.list-header {
+  color: #2B2B2B;
+  line-height: 1.61em;
+  margin: 0 8px 0 0;
+}
+
+ul.suggestions-list {
+  margin-top: 0;
+}
+
+ul.suggestions-list li {
+  color: #2B2B2B;
+  line-height: 1.55;
+}
+  </style>
+</head>
+<body>
+<div id="failure-message">
+  <h1 class="heading">$i18n{h1}</h1>
+  <p class="list-header">$i18n{p}</p>
+  <ul class="suggestions-list">
+    <li>$i18n{li-1}</li>
+    <li>$i18n{li-2}</li>
+  </ul>
+</div>
+</body>
+</html>
diff --git a/chrome/browser/resources/dev_ui_loader/dev_ui_loader.css b/chrome/browser/resources/dev_ui_loader/dev_ui_loader.css
deleted file mode 100644
index aa35e0ac..0000000
--- a/chrome/browser/resources/dev_ui_loader/dev_ui_loader.css
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Copyright 2019 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-body {
-  align-items: center;
-  display: flex;
-  flex-direction: column;
-  font-family: sans, Arial, sans-serif;
-  font-size: 93.75%;
-  justify-content: flex-start;
-}
-
-/* This is needed to hide elements that have "display" set. */
-[hidden] {
-  display: none !important;
-}
-
-#failure-message {
-  display: inline-block;
-  padding-top: 100px;
-}
-
-h1.heading {
-  color: rgb(32, 33, 36);
-  font-size: 1.6em;
-  font-weight: normal;
-  line-height: 1.25em;
-  margin: 0 0 15px 0;
-}
-
-.list-header {
-  color: #2B2B2B;
-  line-height: 1.61em;
-  margin: 0 8px 0 0;
-}
-
-ul.suggestions-list {
-  margin-top: 0;
-}
-
-ul.suggestions-list li {
-  color: #2B2B2B;
-  line-height: 1.55;
-}
diff --git a/chrome/browser/resources/dev_ui_loader/dev_ui_loader.grdp b/chrome/browser/resources/dev_ui_loader/dev_ui_loader.grdp
deleted file mode 100644
index 547e30b..0000000
--- a/chrome/browser/resources/dev_ui_loader/dev_ui_loader.grdp
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<grit-part>
-  <include name="IDR_DEV_UI_LOADER_HTML" file="resources/dev_ui_loader/dev_ui_loader.html" compress="gzip" type="BINDATA" />
-  <include name="IDR_DEV_UI_LOADER_CSS" file="resources/dev_ui_loader/dev_ui_loader.css" compress="gzip" type="BINDATA" />
-  <include name="IDR_DEV_UI_LOADER_JS" file="resources/dev_ui_loader/dev_ui_loader.js" compress="gzip" type="BINDATA" />
-</grit-part>
diff --git a/chrome/browser/resources/dev_ui_loader/dev_ui_loader.html b/chrome/browser/resources/dev_ui_loader/dev_ui_loader.html
deleted file mode 100644
index fc95229..0000000
--- a/chrome/browser/resources/dev_ui_loader/dev_ui_loader.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta charset="utf-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>DevUI DFM Loader</title>
-  <link rel="stylesheet" href="dev_ui_loader.css">
-  <script src="chrome://resources/js/cr.js"></script>
-  <script src="chrome://resources/js/promise_resolver.js"></script>
-  <script src="dev_ui_loader.js"></script>
-</head>
-<body>
-<div id="failure-message" hidden>
-  <h1 class="heading">Something went wrong</h1>
-  <p class="list-header">Try:</p>
-  <ul class="suggestions-list">
-    <li>Reloading this page</li>
-    <li>Checking your internet connection</li>
-  </ul>
-</div>
-</body>
-</html>
diff --git a/chrome/browser/resources/dev_ui_loader/dev_ui_loader.js b/chrome/browser/resources/dev_ui_loader/dev_ui_loader.js
deleted file mode 100644
index 5c295a9..0000000
--- a/chrome/browser/resources/dev_ui_loader/dev_ui_loader.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(function() {
-'use strict';
-
-/** @return {!URL} */
-function getRedirectUrl() {
-  try {  // For potential TypeError from new URL().
-    const urlString = new URL(location).searchParams.get('url');
-    if (urlString) {
-      const url = new URL(decodeURIComponent(urlString));
-      // Perform basic filtering. Also skip 'dev-ui-loader' to avoid redirect
-      // cycle (benign but bizarre). Note that |url.host| is always lowercase.
-      if (url.protocol === 'chrome:' && url.host.match(/[a-z0-9_\-]+/) &&
-          url.host !== 'dev-ui-loader') {
-        return url;
-      }
-    }
-  } catch (e) {
-  }
-  return new URL('chrome://chrome-urls');
-}
-
-function redirectToChromePage() {
-  // Use replace() so the current page disappears from history.
-  location.replace(getRedirectUrl());
-}
-
-function doInstall() {
-  cr.sendWithPromise('installDevUiDfm').then((response) => {
-    const status = response[0];
-    if (status === 'success' || status === 'noop') {
-      redirectToChromePage();
-    } else if (status === 'failure') {
-      document.querySelector('#failure-message').hidden = false;
-    }
-  });
-}
-
-document.addEventListener('DOMContentLoaded', () => {
-  cr.sendWithPromise('getDevUiDfmState').then((state) => {
-    if (state === 'installed') {
-      redirectToChromePage();
-    } else if (state === 'not-installed') {
-      doInstall();
-    }
-  });
-});
-})();
diff --git a/chrome/browser/resources/extensions/extensions.html b/chrome/browser/resources/extensions/extensions.html
index 83ea4fe72..8324340 100644
--- a/chrome/browser/resources/extensions/extensions.html
+++ b/chrome/browser/resources/extensions/extensions.html
@@ -61,6 +61,6 @@
   </script>
   <extensions-manager></extensions-manager>
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
-  <link rel="import" href="chrome://extensions/manager.html">
+  <link rel="import" href="manager.html">
 </body>
 </html>
diff --git a/chrome/browser/resources/history/BUILD.gn b/chrome/browser/resources/history/BUILD.gn
index 7bf036c..042656b 100644
--- a/chrome/browser/resources/history/BUILD.gn
+++ b/chrome/browser/resources/history/BUILD.gn
@@ -87,6 +87,7 @@
     "//third_party/polymer/v1_0/components-chromium/iron-scroll-threshold:iron-scroll-threshold-extracted",
     "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu",
     "//ui/webui/resources/cr_elements/cr_lazy_render:cr_lazy_render",
+    "//ui/webui/resources/js:i18n_behavior",
     "//ui/webui/resources/js:load_time_data",
     "//ui/webui/resources/js:util",
   ]
diff --git a/chrome/browser/resources/history/history_list.html b/chrome/browser/resources/history/history_list.html
index cbd4f20..01e555c 100644
--- a/chrome/browser/resources/history/history_list.html
+++ b/chrome/browser/resources/history/history_list.html
@@ -2,6 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-scroll-threshold/iron-scroll-threshold.html">
diff --git a/chrome/browser/resources/history/history_list.js b/chrome/browser/resources/history/history_list.js
index 18805d5..35b256e 100644
--- a/chrome/browser/resources/history/history_list.js
+++ b/chrome/browser/resources/history/history_list.js
@@ -5,6 +5,8 @@
 Polymer({
   is: 'history-list',
 
+  behaviors: [I18nBehavior],
+
   properties: {
     // The search term for the current query. Set when the query returns.
     searchedTerm: {
@@ -60,6 +62,10 @@
     actionMenuModel_: Object,
   },
 
+  hostAttributes: {
+    role: 'application',
+  },
+
   listeners: {
     'history-checkbox-select': 'onItemSelected_',
     'open-menu': 'onOpenMenu_',
@@ -74,6 +80,7 @@
     /** @type {IronListElement} */ (this.$['infinite-list']).notifyResize();
     this.$['infinite-list'].scrollTarget = this;
     this.$['scroll-threshold'].scrollTarget = this;
+    this.setAttribute('aria-roledescription', this.i18n('ariaRoleDescription'));
   },
 
   /////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index 119a2d2..1abacac 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -221,6 +221,7 @@
   background-repeat: no-repeat;
   background-size: 24px;
   font-size: 16px;
+  outline: none;
   overflow: hidden;
   padding-bottom: 8px;
   padding-inline-end: 16px;
@@ -240,16 +241,11 @@
   padding-inline-end: 48px;
 }
 
-#realbox-matches .search-icon {
-  -webkit-mask-size: 16px;
-}
-
-#realbox-matches .clock-icon {
-  -webkit-mask-image: url(icons/clock.svg);
+.clock-icon,
+.search-icon {
   -webkit-mask-position: center;
   -webkit-mask-repeat: no-repeat;
-  -webkit-mask-size: 16px;
-  background: 24px 24px rgb(117, 117, 117);
+  background-color: rgb(117, 117, 117);
   bottom: 0;
   left: 16px;
   position: absolute;
@@ -257,15 +253,26 @@
   width: 24px;
 }
 
-html[dir=rtl] #realbox-matches .clock-icon {
+.clock-icon {
+  -webkit-mask-image: url(icons/clock.svg);
+  -webkit-mask-size: 16px;
+}
+
+.search-icon {
+  -webkit-mask-image:
+      url(../../../../ui/webui/resources/images/icon_search.svg);
+  -webkit-mask-size: 20px;
+}
+
+html[dir=rtl] #realbox-matches :-webkit-any(.clock-icon, .search-icon) {
   right: 16px;
 }
 
-#realbox-matches a:hover {
+#realbox-matches a:-webkit-any(:hover, .selected) {
   background-color: rgb(var(--GG100-rgb));
 }
 
-#realbox-matches a.selected {
+#realbox-matches a:focus-within {
   background-color: rgb(var(--GG200-rgb));
 }
 
@@ -332,24 +339,6 @@
   right: 0;
 }
 
-.search-icon {
-  -webkit-mask-image:
-      url(../../../../ui/webui/resources/images/icon_search.svg);
-  -webkit-mask-position: center;
-  -webkit-mask-repeat: no-repeat;
-  -webkit-mask-size: 100%;
-  background: 24px 24px rgb(117, 117, 117);
-  bottom: 0;
-  left: 16px;
-  position: absolute;
-  top: 0;
-  width: 24px;
-}
-
-html[dir=rtl] .search-icon {
-  right: 16px;
-}
-
 #fakebox-text {
   bottom: 0;
   color: rgb(117, 117, 117);
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index e8aca7b..dc1b8e1 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -1241,7 +1241,8 @@
     return;
   }
 
-  const matchEls = Array.from($(IDS.REALBOX_MATCHES).children);
+  const realboxMatchesEl = $(IDS.REALBOX_MATCHES);
+  const matchEls = Array.from(realboxMatchesEl.children);
   const selected = matchEls.findIndex(matchEl => {
     return matchEl.classList.contains(CLASSES.SELECTED);
   });
@@ -1290,6 +1291,11 @@
   }
   selectMatchEl(matchEls[newSelected]);
 
+  if (realboxMatchesEl.contains(document.activeElement)) {
+    // Selection should match focus if focus is currently in the matches.
+    matchEls[newSelected].focus();
+  }
+
   const newMatch = autocompleteMatches[newSelected];
   const newFill = newMatch.fillIntoEdit;
   let newInline = '';
diff --git a/chrome/browser/resources/print_preview/ui/icons.html b/chrome/browser/resources/print_preview/ui/icons.html
index 8cffcf2..1f5ce331 100644
--- a/chrome/browser/resources/print_preview/ui/icons.html
+++ b/chrome/browser/resources/print_preview/ui/icons.html
@@ -7,20 +7,15 @@
     <defs>
       <!-- Custom svgs (namratakannan). -->
       <g id="printer-shared" viewBox="0 0 106 96">
-        <defs>
-          <path d="M44 59H32v26h12v11H21.2V74.462H0V42.154C0 33.215 7.102 26 15.9 26h74.2c8.798 0 15.9 7.215 15.9 16.154V59H91.393A15.943 15.943 0 0 0 93 52c0-8.84-7.16-16-16-16s-16 7.16-16 16c0 2.51.578 4.886 1.607 7H44zM84 0H21v22h63V0z" id="a"></path>
-        </defs>
-        <g fill-rule="nonzero">
-          <use xlink:href="#a"></use>
-          <path d="M77 68c-9.679 0-29 6.253-29 18.667V96h58v-9.333C106 74.253 86.679 68 77 68zM77 64c6.63 0 12-5.37 12-12s-5.37-12-12-12-12 5.37-12 12 5.37 12 12 12z" fill="#4A93F9"></path>
-        </g>
+        <path d="M44 59H32v26h12v11H21.2V74.462H0V42.154C0 33.215 7.102 26 15.9 26h74.2c8.798 0 15.9 7.215 15.9 16.154V59H91.393A15.943 15.943 0 0 0 93 52c0-8.84-7.16-16-16-16s-16 7.16-16 16c0 2.51.578 4.886 1.607 7H44zM84 0H21v22h63V0z"></path>
+        <path d="M77 68c-9.679 0-29 6.253-29 18.667V96h58v-9.333C106 74.253 86.679 68 77 68zM77 64c6.63 0 12-5.37 12-12s-5.37-12-12-12-12 5.37-12 12 5.37 12 12 12z" fill="#4A93F9"></path>
       </g>
       <g id="short-edge" viewBox="0 0 26 20" fill-rule="evenodd">
-        <path d="M2 14v2h2v-2H2zm12 4v2h2v-2h-2zm4 0v2h2v-2h-2zm3.556-18H4.444C3.1 0 2 1.35 2 3v6h2V2h18v7h2V3c0-1.65-1.1-3-2.444-3zM24 18h-2v2c1.1 0 2-.9 2-2zM0 10v2h26v-2H0zm6 8v2h2v-2H6zm16-4v2h2v-2h-2zm-12 4v2h2v-2h-2zm-8 0c0 1.1.9 2 2 2v-2H2z" fill-rule="nonzero">
+        <path d="M2 14v2h2v-2H2zm12 4v2h2v-2h-2zm4 0v2h2v-2h-2zm3.556-18H4.444C3.1 0 2 1.35 2 3v6h2V2h18v7h2V3c0-1.65-1.1-3-2.444-3zM24 18h-2v2c1.1 0 2-.9 2-2zM0 10v2h26v-2H0zm6 8v2h2v-2H6zm16-4v2h2v-2h-2zm-12 4v2h2v-2h-2zm-8 0c0 1.1.9 2 2 2v-2H2z">
         <path d="M29-6v32H-3V-6z">
       </g>
       <g id="long-edge" viewBox="0 0 23 22" fill-rule="evenodd">
-        <path d="M17 20h2v-2h-2v2zm4-12h2V6h-2v2zM0 4v14c0 1.1 1.35 2 3 2h6v-2H2V4h7V2H3c-1.65 0-3 .9-3 2zm21-2v2h2c0-1.1-.9-2-2-2zM10 22h2V0h-2v22zm11-6h2v-2h-2v2zM17 4h2V2h-2v2zm-4 16h2v-2h-2v2zm0-16h2V2h-2v2zm8 8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2z" fill-rule="nonzero">
+        <path d="M17 20h2v-2h-2v2zm4-12h2V6h-2v2zM0 4v14c0 1.1 1.35 2 3 2h6v-2H2V4h7V2H3c-1.65 0-3 .9-3 2zm21-2v2h2c0-1.1-.9-2-2-2zM10 22h2V0h-2v22zm11-6h2v-2h-2v2zM17 4h2V2h-2v2zm-4 16h2v-2h-2v2zm0-16h2V2h-2v2zm8 8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2z">
         <path d="M-5-5h32v32H-5z">
       </g>
 
diff --git a/chrome/browser/resources/print_preview/ui/scaling_settings.html b/chrome/browser/resources/print_preview/ui/scaling_settings.html
index 5c3e8fcc..8da1200 100644
--- a/chrome/browser/resources/print_preview/ui/scaling_settings.html
+++ b/chrome/browser/resources/print_preview/ui/scaling_settings.html
@@ -24,6 +24,9 @@
           <option value="[[ScalingValue.FIT_TO_PAGE]]" hidden$="[[!isPdf]]">
             $i18n{optionFitToPage}
           </option>
+          <option value="[[ScalingValue.FIT_TO_PAPER]]" hidden$="[[!isPdf]]">
+            $i18n{optionFitToPaper}
+          </option>
           <option value="[[ScalingValue.CUSTOM]]">
             $i18n{optionCustomScaling}
           </option>
diff --git a/chrome/browser/resources/print_preview/ui/scaling_settings.js b/chrome/browser/resources/print_preview/ui/scaling_settings.js
index 3c537813a..500637a 100644
--- a/chrome/browser/resources/print_preview/ui/scaling_settings.js
+++ b/chrome/browser/resources/print_preview/ui/scaling_settings.js
@@ -5,12 +5,9 @@
 cr.exportPath('print_preview');
 
 /*
- * When fit to page is available, the checkbox and input interact as follows:
- * 1. When checkbox is checked, the fit to page scaling value is displayed in
- * the input. The error message is cleared if it was present.
- * 2. When checkbox is unchecked, the most recent valid scale value is restored.
- * 3. If the input is modified while the checkbox is checked, the checkbox will
- * be unchecked automatically, regardless of the validity of the new value.
+ * Fit to page and fit to paper options will only be displayed for PDF
+ * documents. If the custom option is selected, an additional input field will
+ * appear to enter the custom scale factor.
  */
 Polymer({
   is: 'print-preview-scaling-settings',
@@ -94,12 +91,14 @@
     }
 
     const valueAsNumber = parseInt(value, 10);
-    if (value !== print_preview.ScalingType.FIT_TO_PAGE.toString()) {
+    if (isCustom || value === print_preview.ScalingType.DEFAULT.toString()) {
       this.setSetting('scalingType', valueAsNumber);
     }
     if (this.isPdf ||
-        this.getSetting('scalingTypePdf').value !==
-            print_preview.ScalingType.FIT_TO_PAGE) {
+        this.getSetting('scalingTypePdf').value ===
+            print_preview.ScalingType.DEFAULT ||
+        this.getSetting('scalingTypePdf').value ===
+            print_preview.ScalingType.CUSTOM) {
       this.setSetting('scalingTypePdf', valueAsNumber);
     }
 
@@ -144,8 +143,8 @@
   },
 
   /**
-   * Updates scaling and fit to page settings based on the validity and current
-   * value of the scaling input.
+   * Updates scaling settings based on the validity and current value of the
+   * scaling input.
    * @private
    */
   onInputChanged_: function() {
diff --git a/chrome/browser/resources/safety_tips/safety_tips.asciipb b/chrome/browser/resources/safety_tips/safety_tips.asciipb
index a2f3029..a1536bd 100644
--- a/chrome/browser/resources/safety_tips/safety_tips.asciipb
+++ b/chrome/browser/resources/safety_tips/safety_tips.asciipb
@@ -8,10 +8,10 @@
 # locally, generate and push the protobuffer to the storage bucket but do not
 # check in the changes.
 
-version_id: 4
+version_id: 5
 
 # See chrome/browser/lookalikes/safety_tips.proto for the full format.
-# These entries must be sorted alphabetically by pattern.
+# flagged_page entries must be sorted alphabetically by pattern.
 
 # Example entry:
 # flagged_page {
@@ -21,6 +21,7 @@
 
 # A page that's marked to not show a UI for any heuristic (including lookalike
 # or edit distance).
+# allowed_pattern entries must be sorted alphabetically by pattern.
 # allowed_pattern:  {
 #   pattern: "good.test/some/path.html"
 # }
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html
index e00326d..04047695 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html
@@ -21,7 +21,8 @@
       </app-management-pin-to-shelf-item>
       <app-management-permission-item id="notifications-card"
         class="permission-card-row separated-row header-text"
-        permission-label="$i18n{notifications}" permission-type="NOTIFICATIONS"
+        permission-label="$i18n{appManagementNotificationsLabel}"
+        permission-type="NOTIFICATIONS"
         hidden$="[[!isArcSupported_]]">
       </app-management-permission-item>
       <div id="permissions-card"
@@ -30,32 +31,32 @@
         <div id="subpermission-expand-row"
           class="permission-section-header"
           hidden$="[[!isArcSupported_]]">
-          <div class="header-text">$i18n{permissions}</div>
+          <div class="header-text">$i18n{appManagementPermissionsLabel}</div>
         </div>
         <div class="permission-list indented-permission-block">
           <app-management-permission-item class="subpermission-row"
             icon="app-management:location"
-            permission-label="$i18n{location}"
+            permission-label="$i18n{appManagementLocationPermissionLabel}"
             permission-type="LOCATION">
           </app-management-permission-item>
           <app-management-permission-item class="subpermission-row"
             icon="app-management:camera"
-            permission-label="$i18n{camera}"
+            permission-label="$i18n{appManagementCameraPermissionLabel}"
             permission-type="CAMERA">
           </app-management-permission-item>
           <app-management-permission-item class="subpermission-row"
             icon="app-management:microphone"
-            permission-label="$i18n{microphone}"
+            permission-label="$i18n{appManagementMicrophonePermissionLabel}"
             permission-type="MICROPHONE">
           </app-management-permission-item>
           <app-management-permission-item class="subpermission-row"
             icon="app-management:contacts"
-            permission-label="$i18n{contacts}"
+            permission-label="$i18n{appManagementContactsPermissionLabel}"
             permission-type="CONTACTS">
           </app-management-permission-item>
           <app-management-permission-item class="subpermission-row"
             icon="app-management:storage"
-            permission-label="$i18n{storage}"
+            permission-label="$i18n{appManagementStoragePermissionLabel}"
             permission-type="STORAGE">
           </app-management-permission-item>
         </div>
@@ -63,7 +64,9 @@
       <div id="more-settings"
         class="permission-card-row separated-row header-text clickable"
         on-click="onClickNativeSettingsButton_">
-        <div id="label" class="header-text">$i18n{moreSettings}</div>
+        <div id="label" class="header-text">
+          $i18n{appManagementMoreSettingsLabel}
+        </div>
         <div class="permission-row-controls">
           <cr-icon-button class="native-settings-icon icon-external" role="link"
             tabindex="0" aria-labelledby="label">
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html
index ef7d836..0ee04ab0 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html
@@ -18,10 +18,6 @@
         border-top: none;
       }
 
-      #pin-to-shelf-setting {
-        border-top: var(--card-separator);
-      }
-
     </style>
     <div>
       <div class="permission-list">
@@ -33,12 +29,12 @@
 
         <div id="permissions-card" class="permission-card-row">
           <div class="permission-section-header">
-            <div class="header-text">$i18n{permissions}</div>
+            <div class="header-text">$i18n{appManagementPermissionsLabel}</div>
           </div>
           <template is="dom-if" if="[[!hasPermissions_(messages_)]]">
             <div id="no-permissions" class="indented-permission-block">
               <div class="permission-text-row">
-                $i18n{appNoPermission}
+                $i18n{appManagementNoPermissions}
               </div>
             </div>
           </template>
@@ -75,7 +71,9 @@
           class="permission-card-row separated-row header-text clickable"
           on-click="onClickExtensionsSettingsButton_"
           hidden$="[[app_.hideMoreSettings]]">
-          <div id="label" class="header-text">$i18n{moreSettings}</div>
+          <div id="label" class="header-text">
+            $i18n{appManagementMoreSettingsLabel}
+          </div>
           <div class="permission-row-controls">
             <cr-icon-button class="native-settings-icon icon-external" role="link"
               tabindex="0" aria-labelledby="label">
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/metadata_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/metadata_view.html
deleted file mode 100644
index 43948ae..0000000
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/metadata_view.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="browser_proxy.html">
-<link rel="import" href="shared_style.html">
-<link rel="import" href="store_client.html">
-
-<dom-module id="app-management-metadata-view">
-  <template>
-    <style include="app-management-shared-css">
-    #shelf-switch-row {
-      align-items: center;
-      display: flex;
-      justify-content: space-around;
-      padding-bottom: 20px;
-    }
-
-    #shelf-switch {
-      align-items: center;
-      display: flex;
-      justify-content: space-between;
-    }
-
-    cr-toggle {
-      margin-inline-start: 12px;
-    }
-
-    #metadata-overview {
-      display: flex;
-      flex-direction: column;
-    }
-
-    #metadata-overview > span {
-      text-align: center;
-    }
-
-    .metadata-row {
-      display: flex;
-      justify-content: space-around;
-    }
-
-    #policy-indicator {
-      fill: var(--google-grey-refresh-700);
-      margin-inline-end: 12px;
-    }
-    </style>
-    <template is="dom-if" if="[[pinToShelfToggleVisible_(app_)]]">
-      <div id="shelf-switch-row">
-        <span id="shelf-switch" class="header-text">
-          <template is="dom-if" if="[[isPolicyPinned_(app_)]]">
-            <iron-icon id="policy-indicator" icon="cr:domain" tabindex="0"
-              aria-describedby="tooltip">
-            </iron-icon>
-            <paper-tooltip id="tooltip" for="policy-indicator"
-              position="top" fit-to-visible-bounds>
-              $i18n{pinControlledByPolicy}
-            </paper-tooltip>
-          </template>
-          $i18n{pinToShelf}
-          <cr-toggle id="pin-to-shelf-toggle" checked="[[isPinned_(app_)]]"
-            on-change="togglePinned_" disabled$="[[isPolicyPinned_(app_)]]">
-          </cr-toggle>
-        </span>
-      </div>
-    </template>
-
-    <div id="metadata-overview" class="secondary-text">
-      <span>[[versionString_(app_)]]</span>
-      <span>[[sizeString_(app_)]]</span>
-    </div>
-  </template>
-  <script src="metadata_view.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/metadata_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/metadata_view.js
deleted file mode 100644
index d063cdc..0000000
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/metadata_view.js
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-Polymer({
-  is: 'app-management-metadata-view',
-
-  behaviors: [
-    app_management.StoreClient,
-  ],
-
-  properties: {
-    /** @type {App} */
-    app_: {
-      type: Object,
-    },
-  },
-
-  attached: function() {
-    this.watch('app_', state => app_management.util.getSelectedApp(state));
-    this.updateFromStore();
-  },
-
-  /**
-   * @param {App} app
-   * @return bool
-   * @private
-   */
-  pinToShelfToggleVisible_: function(app) {
-    return app.isPinned !== OptionalBool.kUnknown;
-  },
-
-  /**
-   * Returns a bool representation of the app's isPinned value, used to
-   * determine the position of the "Pin to Shelf" toggle.
-   * @param {App} app
-   * @return bool
-   * @private
-   */
-  isPinned_: function(app) {
-    return app.isPinned === OptionalBool.kTrue;
-  },
-
-  isPolicyPinned_: function(app) {
-    return app.isPolicyPinned === OptionalBool.kTrue;
-  },
-
-  /** @private */
-  togglePinned_: function() {
-    let newPinnedValue;
-
-    switch (this.app_.isPinned) {
-      case OptionalBool.kFalse:
-        newPinnedValue = OptionalBool.kTrue;
-        break;
-      case OptionalBool.kTrue:
-        newPinnedValue = OptionalBool.kFalse;
-        break;
-      default:
-        assertNotReached();
-    }
-
-    app_management.BrowserProxy.getInstance().handler.setPinned(
-        this.app_.id, assert(newPinnedValue));
-  },
-
-  /**
-   * @param {App} app
-   * @return {?string}
-   * @private
-   */
-  versionString_: function(app) {
-    if (!app.version) {
-      return null;
-    }
-
-    return loadTimeData.getStringF('version', assert(app.version));
-  },
-
-  /**
-   * @param {App} app
-   * @return {?string}
-   * @private
-   */
-  sizeString_: function(app) {
-    if (!app.size) {
-      return null;
-    }
-
-    return loadTimeData.getStringF('size', assert(app.size));
-  },
-});
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pin_to_shelf_item.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pin_to_shelf_item.html
index 11083f5..c1a8b00 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pin_to_shelf_item.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pin_to_shelf_item.html
@@ -10,7 +10,7 @@
     </style>
     <app-management-toggle-row
       id="toggle-row"
-      label="$i18n{pinToShelf}"
+      label="$i18n{appManagementPinToShelfLabel}"
       managed="[[isManaged_(app_)]]"
       value="[[getValue_(app_)]]">
     </app-management-toggle-row>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html
index a27943c..1ab2cf52 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html
@@ -21,26 +21,27 @@
       </app-management-pin-to-shelf-item>
       <app-management-permission-item id="notifications-card"
         class="permission-card-row separated-row header-text"
-        permission-label="$i18n{notifications}"
+        permission-label="$i18n{appManagementNotificationsLabel}"
         permission-type="CONTENT_SETTINGS_TYPE_NOTIFICATIONS">
       </app-management-permission-item>
       <div id="permissions-card" class="permission-card-row">
         <div class="permission-section-header">
-          <div class="header-text">$i18n{permissions}</div>
+          <div class="header-text">$i18n{appManagementPermissionsLabel}</div>
         </div>
         <div class="permission-list indented-permission-block">
           <app-management-permission-item id="location"
             class="subpermission-row" icon="app-management:location"
-            permission-label="$i18n{location}"
+            permission-label="$i18n{appManagementLocationPermissionLabel}"
             permission-type="CONTENT_SETTINGS_TYPE_GEOLOCATION">
           </app-management-permission-item>
           <app-management-permission-item id="camera" class="subpermission-row"
-            icon="app-management:camera" permission-label="$i18n{camera}"
+            icon="app-management:camera"
+            permission-label="$i18n{appManagementCameraPermissionLabel}"
             permission-type="CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA">
           </app-management-permission-item>
           <app-management-permission-item id="microphone"
             class="subpermission-row" icon="app-management:microphone"
-            permission-label="$i18n{microphone}"
+            permission-label="$i18n{appManagementMicrophonePermissionLabel}"
             permission-type="CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC">
           </app-management-permission-item>
         </div>
@@ -48,7 +49,9 @@
       <div id="more-settings"
         class="permission-card-row separated-row header-text clickable"
         on-click="onClickSiteSettingsButton_">
-        <div id="label" class="header-text">$i18n{moreSettings}</div>
+        <div id="label" class="header-text">
+          $i18n{appManagementMoreSettingsLabel}
+        </div>
         <div class="permission-row-controls">
           <cr-icon-button class="native-settings-icon icon-external" role="link"
             tabindex="0" aria-labelledby="label">
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.html
index 6711f4ee..c90d327b 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.html
@@ -25,15 +25,15 @@
       <cr-tooltip-icon
           id="policyIndicator"
           icon-class="cr20:domain"
-          tooltip-text="$i18n{policyAppUninstallPolicy}"
-          icon-aria-label="$i18n{policyAppUninstallPolicy}"
+          tooltip-text="$i18n{appManagementAppInstalledByPolicyLabel}"
+          icon-aria-label="$i18n{appManagementAppInstalledByPolicyLabel}"
           tooltip-position="bottom">
       </cr-tooltip-icon>
     </template>
     <template is="dom-if" if="[[showUninstallButton_(app_)]]">
       <cr-button id="uninstallButton" on-click="onClick_"
           disabled$="[[getDisableState_(app_)]]">
-        $i18n{uninstallApp}
+        $i18n{appManagementUninstallLabel}
       </cr-button>
     </template>
   </template>
diff --git a/chrome/browser/resources/tab_strip/BUILD.gn b/chrome/browser/resources/tab_strip/BUILD.gn
index faba4952..f6b8048 100644
--- a/chrome/browser/resources/tab_strip/BUILD.gn
+++ b/chrome/browser/resources/tab_strip/BUILD.gn
@@ -11,7 +11,7 @@
     ":custom_element",
     ":tab",
     ":tab_list",
-    ":tab_strip_view_proxy",
+    ":tab_strip_embedder_proxy",
     ":tabs_api_proxy",
   ]
 }
@@ -38,7 +38,7 @@
 js_library("tab_list") {
 }
 
-js_library("tab_strip_view_proxy") {
+js_library("tab_strip_embedder_proxy") {
   deps = [
     "//ui/webui/resources/js:cr.m",
   ]
diff --git a/chrome/browser/resources/tab_strip/tab.js b/chrome/browser/resources/tab_strip/tab.js
index b37952e..a6908f2 100644
--- a/chrome/browser/resources/tab_strip/tab.js
+++ b/chrome/browser/resources/tab_strip/tab.js
@@ -6,6 +6,7 @@
 import {getFavicon} from 'chrome://resources/js/icon.m.js';
 
 import {CustomElement} from './custom_element.js';
+import {TabStripEmbedderProxy} from './tab_strip_embedder_proxy.js';
 import {TabData, TabNetworkState, TabsApiProxy} from './tabs_api_proxy.js';
 
 export const DEFAULT_ANIMATION_DURATION = 125;
@@ -46,6 +47,9 @@
     /** @private {!TabsApiProxy} */
     this.tabsApi_ = TabsApiProxy.getInstance();
 
+    /** @private {!TabStripEmbedderProxy} */
+    this.embedderApi_ = TabStripEmbedderProxy.getInstance();
+
     /** @private {!HTMLElement} */
     this.titleTextEl_ = /** @type {!HTMLElement} */ (
         this.shadowRoot.querySelector('#titleText'));
@@ -123,6 +127,7 @@
     }
 
     this.tabsApi_.activateTab(this.tab_.id);
+    this.embedderApi_.closeContainer();
   }
 
   /** @private */
@@ -133,7 +138,7 @@
       return;
     }
 
-    this.tabsApi_.showTabContextMenu(
+    this.embedderApi_.showTabContextMenu(
         this.tab_.id, event.clientX, event.clientY);
   }
 
diff --git a/chrome/browser/resources/tab_strip/tab_list.js b/chrome/browser/resources/tab_strip/tab_list.js
index 5f0562c..4d747e65 100644
--- a/chrome/browser/resources/tab_strip/tab_list.js
+++ b/chrome/browser/resources/tab_strip/tab_list.js
@@ -9,7 +9,7 @@
 
 import {CustomElement} from './custom_element.js';
 import {TabElement} from './tab.js';
-import {TabStripViewProxy} from './tab_strip_view_proxy.js';
+import {TabStripEmbedderProxy} from './tab_strip_embedder_proxy.js';
 import {TabData, TabsApiProxy} from './tabs_api_proxy.js';
 
 /**
@@ -59,8 +59,8 @@
     /** @private {!Element} */
     this.scrollingParent_ = document.documentElement;
 
-    /** @private {!TabStripViewProxy} */
-    this.tabStripViewProxy_ = TabStripViewProxy.getInstance();
+    /** @private {!TabStripEmbedderProxy} */
+    this.tabStripEmbedderProxy_ = TabStripEmbedderProxy.getInstance();
 
     /** @private {!TabsApiProxy} */
     this.tabsApi_ = TabsApiProxy.getInstance();
@@ -71,7 +71,7 @@
             this.shadowRoot.querySelector('#tabsContainer'));
 
     addWebUIListener('theme-changed', () => this.fetchAndUpdateColors_());
-    this.tabStripViewProxy_.observeThemeChanges();
+    this.tabStripEmbedderProxy_.observeThemeChanges();
 
     addWebUIListener(
         'tab-thumbnail-updated', this.tabThumbnailUpdated_.bind(this));
@@ -133,7 +133,7 @@
 
   /** @private */
   fetchAndUpdateColors_() {
-    this.tabStripViewProxy_.getColors().then(colors => {
+    this.tabStripEmbedderProxy_.getColors().then(colors => {
       for (const [cssVariable, rgbaValue] of Object.entries(colors)) {
         this.style.setProperty(cssVariable, rgbaValue);
       }
@@ -178,7 +178,7 @@
       return;
     }
 
-    if (!this.tabStripViewProxy_.isVisible() && !activeTab.tab.pinned &&
+    if (!this.tabStripEmbedderProxy_.isVisible() && !activeTab.tab.pinned &&
         this.tabsContainerElement_.firstChild !== activeTab) {
       this.tabsApi_.moveTab(
           activeTab.tab.id, this.pinnedTabsContainerElement_.childElementCount);
diff --git a/chrome/browser/resources/tab_strip/tab_strip_view_proxy.js b/chrome/browser/resources/tab_strip/tab_strip_embedder_proxy.js
similarity index 63%
rename from chrome/browser/resources/tab_strip/tab_strip_view_proxy.js
rename to chrome/browser/resources/tab_strip/tab_strip_embedder_proxy.js
index 089baae..6852de8 100644
--- a/chrome/browser/resources/tab_strip/tab_strip_view_proxy.js
+++ b/chrome/browser/resources/tab_strip/tab_strip_embedder_proxy.js
@@ -4,7 +4,7 @@
 
 import {addSingletonGetter, addWebUIListener, sendWithPromise} from 'chrome://resources/js/cr.m.js';
 
-export class TabStripViewProxy {
+export class TabStripEmbedderProxy {
   /** @return {boolean} */
   isVisible() {
     return document.visibilityState === 'visible';
@@ -21,6 +21,19 @@
   observeThemeChanges() {
     chrome.send('observeThemeChanges');
   }
+
+  /**
+   * @param {number} tabId
+   * @param {number} locationX
+   * @param {number} locationY
+   */
+  showTabContextMenu(tabId, locationX, locationY) {
+    chrome.send('showTabContextMenu', [tabId, locationX, locationY]);
+  }
+
+  closeContainer() {
+    chrome.send('closeContainer');
+  }
 }
 
-addSingletonGetter(TabStripViewProxy);
+addSingletonGetter(TabStripEmbedderProxy);
diff --git a/chrome/browser/resources/tab_strip/tab_strip_resources.grd b/chrome/browser/resources/tab_strip/tab_strip_resources.grd
index a14c6bf1..97074da 100644
--- a/chrome/browser/resources/tab_strip/tab_strip_resources.grd
+++ b/chrome/browser/resources/tab_strip/tab_strip_resources.grd
@@ -46,8 +46,8 @@
           type="chrome_html"
           compress="gzip"/>
       <structure
-          name="IDR_TAB_STRIP_VIEW_PROXY_JS"
-          file="tab_strip_view_proxy.js"
+          name="IDR_TAB_STRIP_EMBEDDER_PROXY_JS"
+          file="tab_strip_embedder_proxy.js"
           type="chrome_html"
           compress="gzip"/>
     </structures>
diff --git a/chrome/browser/resources/tab_strip/tabs_api_proxy.js b/chrome/browser/resources/tab_strip/tabs_api_proxy.js
index 6217b60..34b13b9 100644
--- a/chrome/browser/resources/tab_strip/tabs_api_proxy.js
+++ b/chrome/browser/resources/tab_strip/tabs_api_proxy.js
@@ -85,15 +85,6 @@
   trackThumbnailForTab(tabId) {
     chrome.send('addTrackedTab', [tabId]);
   }
-
-  /**
-   * @param {number} tabId
-   * @param {number} locationX
-   * @param {number} locationY
-   */
-  showTabContextMenu(tabId, locationX, locationY) {
-    chrome.send('showTabContextMenu', [tabId, locationX, locationY]);
-  }
 }
 
 addSingletonGetter(TabsApiProxy);
diff --git a/chrome/browser/resources/welcome/BUILD.gn b/chrome/browser/resources/welcome/BUILD.gn
index a01d9fc..f82bd8f 100644
--- a/chrome/browser/resources/welcome/BUILD.gn
+++ b/chrome/browser/resources/welcome/BUILD.gn
@@ -28,9 +28,6 @@
 }
 
 js_library("landing_view") {
-  sources = [
-    "$root_gen_dir/chrome/browser/resources/welcome/landing_view.js",
-  ]
   deps = [
     ":landing_view_proxy",
     ":navigation_behavior",
@@ -38,7 +35,6 @@
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/js:load_time_data.m",
   ]
-  extra_deps = [ ":landing_view_module" ]
 }
 
 js_library("landing_view_proxy") {
@@ -49,16 +45,12 @@
 }
 
 js_library("signin_view") {
-  sources = [
-    "$root_gen_dir/chrome/browser/resources/welcome/signin_view.js",
-  ]
   deps = [
     ":navigation_behavior",
     ":signin_view_proxy",
     ":welcome_browser_proxy",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
-  extra_deps = [ ":signin_view_module" ]
 }
 
 js_library("signin_view_proxy") {
@@ -76,9 +68,6 @@
 }
 
 js_library("welcome_app") {
-  sources = [
-    "$root_gen_dir/chrome/browser/resources/welcome/welcome_app.js",
-  ]
   deps = [
     ":navigation_behavior",
     ":welcome_browser_proxy",
@@ -89,7 +78,6 @@
     "//ui/webui/resources/cr_elements/cr_view_manager:cr_view_manager.m",
     "//ui/webui/resources/js:load_time_data.m",
   ]
-  extra_deps = [ ":welcome_app_module" ]
 }
 
 js_library("welcome_browser_proxy") {
diff --git a/chrome/browser/resources/welcome/google_apps/BUILD.gn b/chrome/browser/resources/welcome/google_apps/BUILD.gn
index 8434c2f..f6f678ed 100644
--- a/chrome/browser/resources/welcome/google_apps/BUILD.gn
+++ b/chrome/browser/resources/welcome/google_apps/BUILD.gn
@@ -19,9 +19,6 @@
 }
 
 js_library("nux_google_apps") {
-  sources = [
-    "$root_gen_dir/chrome/browser/resources/welcome/google_apps/nux_google_apps.js",
-  ]
   deps = [
     ":google_app_proxy",
     ":google_apps_metrics_proxy",
@@ -35,7 +32,6 @@
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:util.m",
   ]
-  extra_deps = [ ":nux_google_apps_module" ]
 }
 
 js_library("google_app_proxy") {
diff --git a/chrome/browser/resources/welcome/ntp_background/BUILD.gn b/chrome/browser/resources/welcome/ntp_background/BUILD.gn
index 0500642d..09daeac 100644
--- a/chrome/browser/resources/welcome/ntp_background/BUILD.gn
+++ b/chrome/browser/resources/welcome/ntp_background/BUILD.gn
@@ -39,9 +39,6 @@
 }
 
 js_library("nux_ntp_background") {
-  sources = [
-    "$root_gen_dir/chrome/browser/resources/welcome/ntp_background/nux_ntp_background.js",
-  ]
   deps = [
     ":ntp_background_metrics_proxy",
     ":ntp_background_proxy",
@@ -52,5 +49,4 @@
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:util.m",
   ]
-  extra_deps = [ ":nux_ntp_background_module" ]
 }
diff --git a/chrome/browser/resources/welcome/set_as_default/BUILD.gn b/chrome/browser/resources/welcome/set_as_default/BUILD.gn
index d4fa01a..0c82491 100644
--- a/chrome/browser/resources/welcome/set_as_default/BUILD.gn
+++ b/chrome/browser/resources/welcome/set_as_default/BUILD.gn
@@ -17,9 +17,6 @@
 }
 
 js_library("nux_set_as_default") {
-  sources = [
-    "$root_gen_dir/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.js",
-  ]
   deps = [
     ":nux_set_as_default_proxy",
     "../:navigation_behavior",
@@ -28,7 +25,6 @@
     "//ui/webui/resources/js:load_time_data.m",
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
-  extra_deps = [ ":nux_set_as_default_module" ]
 }
 
 js_library("nux_set_as_default_proxy") {
diff --git a/chrome/browser/resources/welcome/shared/BUILD.gn b/chrome/browser/resources/welcome/shared/BUILD.gn
index 14b604fb..9ebc573 100644
--- a/chrome/browser/resources/welcome/shared/BUILD.gn
+++ b/chrome/browser/resources/welcome/shared/BUILD.gn
@@ -37,14 +37,10 @@
 }
 
 js_library("step_indicator") {
-  sources = [
-    "$root_gen_dir/chrome/browser/resources/welcome/shared/step_indicator.js",
-  ]
   deps = [
     ":nux_types",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
-  extra_deps = [ ":step_indicator_module" ]
 }
 
 polymer_modulizer("animations_css") {
diff --git a/chrome/browser/search/ntp_icon_source.cc b/chrome/browser/search/ntp_icon_source.cc
index 12e90faf..12d820426 100644
--- a/chrome/browser/search/ntp_icon_source.cc
+++ b/chrome/browser/search/ntp_icon_source.cc
@@ -63,7 +63,8 @@
 const char kIconSourceUmaClientName[] = "NtpIconSource";
 
 // The color of the letter drawn for a fallback icon.  Changing this may require
-// changing the algorithm in RenderIconBitmap() that guarantees contrast.
+// changing the algorithm in ReturnRenderedIconForRequest() that guarantees
+// contrast.
 constexpr SkColor kFallbackIconLetterColor = SK_ColorWHITE;
 
 const char kShowFallbackMonogramParam[] = "show_fallback_monogram";
@@ -222,50 +223,6 @@
   return SkColorSetRGB(hash[0], hash[1], hash[2]);
 }
 
-// For the given |icon_url|, will render |favicon|. If |favicon| is not
-// specified, will use a colored circular monogram instead.
-std::vector<unsigned char> RenderIconBitmap(const GURL& icon_url,
-                                            const SkBitmap& favicon,
-                                            int icon_size,
-                                            int fallback_size,
-                                            bool show_fallback_monogram,
-                                            float device_scale_factor) {
-  SkBitmap bitmap;
-  bitmap.allocN32Pixels(icon_size, icon_size, false);
-  cc::SkiaPaintCanvas paint_canvas(bitmap);
-  gfx::Canvas canvas(&paint_canvas, 1.f);
-  canvas.DrawColor(SK_ColorTRANSPARENT, SkBlendMode::kSrc);
-
-  // If necessary, draw the colored fallback monogram.
-  if (favicon.empty()) {
-    if (show_fallback_monogram) {
-      SkColor fallback_color =
-          color_utils::BlendForMinContrast(GetBackgroundColorForUrl(icon_url),
-                                           kFallbackIconLetterColor)
-              .color;
-
-      int offset = (icon_size - fallback_size) / 2;
-      DrawCircleInCanvas(&canvas, fallback_size, offset, fallback_color);
-      DrawFallbackIconLetter(icon_url, icon_size, &canvas);
-    } else {
-      const auto* default_favicon = favicon::GetDefaultFavicon().ToImageSkia();
-      const auto& rep = default_favicon->GetRepresentation(device_scale_factor);
-      gfx::ImageSkia scaled_image(rep);
-      const auto resized = gfx::ImageSkiaOperations::CreateResizedImage(
-          scaled_image, skia::ImageOperations::RESIZE_BEST,
-          gfx::Size(fallback_size, fallback_size));
-      DrawFavicon(*resized.bitmap(), &canvas, icon_size);
-    }
-  } else {
-    DrawFavicon(favicon, &canvas, icon_size);
-  }
-
-  std::vector<unsigned char> bitmap_data;
-  bool result = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_data);
-  DCHECK(result);
-  return bitmap_data;
-}
-
 }  // namespace
 
 struct NtpIconSource::NtpIconRequest {
@@ -384,7 +341,7 @@
     const favicon_base::FaviconRawBitmapResult& bitmap_result) {
   if (bitmap_result.is_valid()) {
     // A local favicon was found. Decode it to an SkBitmap so it can eventually
-    // be passed as valid image data to RenderIconBitmap.
+    // be passed as valid image data to ReturnRenderedIconForRequest.
     SkBitmap bitmap;
     bool result =
         gfx::PNGCodec::Decode(bitmap_result.bitmap_data.get()->front(),
@@ -470,7 +427,7 @@
     // The received server icon bitmap may still be bigger than our desired
     // size, so resize it.
     fetched_bitmap = skia::ImageOperations::Resize(
-        fetched_bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
+        fetched_bitmap, skia::ImageOperations::RESIZE_BEST,
         request.icon_size_in_pixels, request.icon_size_in_pixels);
   }
 
@@ -478,16 +435,47 @@
 }
 
 void NtpIconSource::ReturnRenderedIconForRequest(const NtpIconRequest& request,
-                                                 const SkBitmap& bitmap) {
+                                                 const SkBitmap& favicon) {
   // Only use even pixel sizes to avoid issues when centering the fallback
   // monogram.
-  int desired_overall_size_in_pixel =
+  const int icon_size =
       std::round(kIconSizeDip * request.device_scale_factor * 0.5) * 2.0;
-  int desired_fallback_size_in_pixel =
+  const int fallback_size =
       std::round(kFallbackSizeDip * request.device_scale_factor * 0.5) * 2.0;
-  std::vector<unsigned char> bitmap_data = RenderIconBitmap(
-      request.path, bitmap, desired_overall_size_in_pixel,
-      desired_fallback_size_in_pixel, request.show_fallback_monogram,
-      request.device_scale_factor);
+
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(icon_size, icon_size, false);
+  cc::SkiaPaintCanvas paint_canvas(bitmap);
+  gfx::Canvas canvas(&paint_canvas, 1.f);
+  canvas.DrawColor(SK_ColorTRANSPARENT, SkBlendMode::kSrc);
+
+  // If necessary, draw the colored fallback monogram.
+  if (favicon.empty()) {
+    if (request.show_fallback_monogram) {
+      SkColor fallback_color =
+          color_utils::BlendForMinContrast(
+              GetBackgroundColorForUrl(request.path), kFallbackIconLetterColor)
+              .color;
+
+      int offset = (icon_size - fallback_size) / 2;
+      DrawCircleInCanvas(&canvas, fallback_size, offset, fallback_color);
+      DrawFallbackIconLetter(request.path, icon_size, &canvas);
+    } else {
+      const auto* default_favicon = favicon::GetDefaultFavicon().ToImageSkia();
+      const auto& rep =
+          default_favicon->GetRepresentation(request.device_scale_factor);
+      gfx::ImageSkia scaled_image(rep);
+      const auto resized = gfx::ImageSkiaOperations::CreateResizedImage(
+          scaled_image, skia::ImageOperations::RESIZE_BEST,
+          gfx::Size(fallback_size, fallback_size));
+      DrawFavicon(*resized.bitmap(), &canvas, icon_size);
+    }
+  } else {
+    DrawFavicon(favicon, &canvas, icon_size);
+  }
+
+  std::vector<unsigned char> bitmap_data;
+  bool result = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_data);
+  DCHECK(result);
   request.callback.Run(base::RefCountedBytes::TakeVector(&bitmap_data));
 }
diff --git a/chrome/browser/sharing/ack_message_handler.cc b/chrome/browser/sharing/ack_message_handler.cc
index 5f93d0f..f4489185 100644
--- a/chrome/browser/sharing/ack_message_handler.cc
+++ b/chrome/browser/sharing/ack_message_handler.cc
@@ -13,7 +13,8 @@
 void AckMessageHandler::OnMessage(
     const chrome_browser_sharing::SharingMessage& message) {
   for (AckMessageObserver& observer : observers_)
-    observer.OnAckReceived(message.ack_message().original_message_id());
+    observer.OnAckReceived(message.ack_message().original_message_type(),
+                           message.ack_message().original_message_id());
 }
 
 void AckMessageHandler::AddObserver(AckMessageObserver* observer) {
diff --git a/chrome/browser/sharing/ack_message_handler.h b/chrome/browser/sharing/ack_message_handler.h
index 8e00dad..71ecc7f 100644
--- a/chrome/browser/sharing/ack_message_handler.h
+++ b/chrome/browser/sharing/ack_message_handler.h
@@ -11,15 +11,21 @@
 #include "base/observer_list.h"
 #include "chrome/browser/sharing/sharing_message_handler.h"
 
+namespace chrome_browser_sharing {
+enum MessageType : int;
+}  // namespace chrome_browser_sharing
+
 // Class to managae ack message and notify observers.
 class AckMessageHandler : public SharingMessageHandler {
  public:
   // Interface for objects observing ack message received events.
   class AckMessageObserver : public base::CheckedObserver {
    public:
-    // Called when an ack message is received, where the identifier of original
-    // message is |message_id|.
-    virtual void OnAckReceived(const std::string& message_id) = 0;
+    // Called when an ack message is received, where |message_type| is the type
+    // of the original message, and |message_id| is the identifier of the
+    // original message.
+    virtual void OnAckReceived(chrome_browser_sharing::MessageType message_type,
+                               const std::string& message_id) = 0;
   };
 
   AckMessageHandler();
diff --git a/chrome/browser/sharing/ack_message_handler_unittest.cc b/chrome/browser/sharing/ack_message_handler_unittest.cc
index 6addf3d..1a3cb395 100644
--- a/chrome/browser/sharing/ack_message_handler_unittest.cc
+++ b/chrome/browser/sharing/ack_message_handler_unittest.cc
@@ -11,13 +11,20 @@
 
 class TestObserver : public AckMessageHandler::AckMessageObserver {
  public:
-  void OnAckReceived(const std::string& message_id) override {
+  void OnAckReceived(chrome_browser_sharing::MessageType message_type,
+                     const std::string& message_id) override {
+    received_message_type_ = message_type;
     received_message_id_ = message_id;
   }
 
+  chrome_browser_sharing::MessageType received_message_type() const {
+    return received_message_type_;
+  }
+
   std::string received_message_id() const { return received_message_id_; }
 
  private:
+  chrome_browser_sharing::MessageType received_message_type_;
   std::string received_message_id_;
 };
 
@@ -37,8 +44,12 @@
   chrome_browser_sharing::SharingMessage sharing_message;
   sharing_message.mutable_ack_message()->set_original_message_id(
       kTestMessageId);
+  sharing_message.mutable_ack_message()->set_original_message_type(
+      chrome_browser_sharing::CLICK_TO_CALL_MESSAGE);
 
   ack_message_handler_.OnMessage(sharing_message);
 
   EXPECT_EQ(kTestMessageId, test_observer_.received_message_id());
+  EXPECT_EQ(chrome_browser_sharing::CLICK_TO_CALL_MESSAGE,
+            test_observer_.received_message_type());
 }
diff --git a/chrome/browser/sharing/proto/sharing_message.proto b/chrome/browser/sharing/proto/sharing_message.proto
index 29bda1a..d6cc93c 100644
--- a/chrome/browser/sharing/proto/sharing_message.proto
+++ b/chrome/browser/sharing/proto/sharing_message.proto
@@ -12,6 +12,18 @@
 // Required in Chrome.
 option optimize_for = LITE_RUNTIME;
 
+// Enum for identifying a message type.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. Keep this in sync with
+// SharingMessageType in enums.xml.
+enum MessageType {
+  UNKNOWN_MESSAGE = 0;
+  PING_MESSAGE = 1;
+  ACK_MESSAGE = 2;
+  CLICK_TO_CALL_MESSAGE = 3;
+  SHARED_CLIPBOARD_MESSAGE = 4;
+}
+
 // Message for sending between devices in Sharing.
 message SharingMessage {
   // Identifier of sender. required.
@@ -41,6 +53,9 @@
 message AckMessage {
   // required.
   string original_message_id = 1;
+
+  // The type of message that this is acknowledging. optional.
+  MessageType original_message_type = 2;
 }
 
 // Message for data necessary to send an AckMessage to the sender.
diff --git a/chrome/browser/sharing/sharing_fcm_handler.cc b/chrome/browser/sharing/sharing_fcm_handler.cc
index 477217a2..ccf11c78 100644
--- a/chrome/browser/sharing/sharing_fcm_handler.cc
+++ b/chrome/browser/sharing/sharing_fcm_handler.cc
@@ -105,7 +105,17 @@
   DCHECK(sharing_message.payload_case() != SharingMessage::PAYLOAD_NOT_SET)
       << "No payload set in SharingMessage received";
 
-  LogSharingMessageReceived(sharing_message.payload_case());
+  chrome_browser_sharing::MessageType message_type =
+      chrome_browser_sharing::UNKNOWN_MESSAGE;
+  if (sharing_message.payload_case() ==
+      chrome_browser_sharing::SharingMessage::kAckMessage) {
+    DCHECK(sharing_message.has_ack_message());
+    message_type = sharing_message.ack_message().original_message_type();
+  } else {
+    message_type =
+        SharingPayloadCaseToMessageType(sharing_message.payload_case());
+  }
+  LogSharingMessageReceived(message_type, sharing_message.payload_case());
 
   auto it = sharing_handlers_.find(sharing_message.payload_case());
   if (it == sharing_handlers_.end()) {
@@ -152,26 +162,33 @@
   SharingMessage ack_message;
   ack_message.mutable_ack_message()->set_original_message_id(
       original_message_id);
+  chrome_browser_sharing::MessageType original_message_type =
+      SharingPayloadCaseToMessageType(original_message.payload_case());
+  ack_message.mutable_ack_message()->set_original_message_type(
+      original_message_type);
 
   base::Optional<syncer::DeviceInfo::SharingInfo> sharing_info =
       GetSharingInfo(original_message);
   if (!sharing_info) {
     LOG(ERROR) << "Unable to find sharing info";
-    LogSendSharingAckMessageResult(SharingSendMessageResult::kDeviceNotFound);
+    LogSendSharingAckMessageResult(original_message_type,
+                                   SharingSendMessageResult::kDeviceNotFound);
     return;
   }
 
   sharing_fcm_sender_->SendMessageToDevice(
       std::move(*sharing_info), kAckTimeToLive, std::move(ack_message),
       base::BindOnce(&SharingFCMHandler::OnAckMessageSent,
-                     weak_ptr_factory_.GetWeakPtr(), original_message_id));
+                     weak_ptr_factory_.GetWeakPtr(), original_message_id,
+                     original_message_type));
 }
 
 void SharingFCMHandler::OnAckMessageSent(
     const std::string& original_message_id,
+    chrome_browser_sharing::MessageType original_message_type,
     SharingSendMessageResult result,
     base::Optional<std::string> message_id) {
-  LogSendSharingAckMessageResult(result);
+  LogSendSharingAckMessageResult(original_message_type, result);
   if (result != SharingSendMessageResult::kSuccessful)
     LOG(ERROR) << "Failed to send ack mesage for " << original_message_id;
 }
diff --git a/chrome/browser/sharing/sharing_fcm_handler.h b/chrome/browser/sharing/sharing_fcm_handler.h
index 6d0c3ee..c6e389da 100644
--- a/chrome/browser/sharing/sharing_fcm_handler.h
+++ b/chrome/browser/sharing/sharing_fcm_handler.h
@@ -76,9 +76,11 @@
   void SendAckMessage(const SharingMessage& original_message,
                       const std::string& original_message_id);
 
-  void OnAckMessageSent(const std::string& original_message_id,
-                        SharingSendMessageResult result,
-                        base::Optional<std::string> message_id);
+  void OnAckMessageSent(
+      const std::string& original_message_id,
+      chrome_browser_sharing::MessageType original_message_type,
+      SharingSendMessageResult result,
+      base::Optional<std::string> message_id);
 
   gcm::GCMDriver* const gcm_driver_;
   SharingFCMSender* sharing_fcm_sender_;
diff --git a/chrome/browser/sharing/sharing_fcm_handler_unittest.cc b/chrome/browser/sharing/sharing_fcm_handler_unittest.cc
index 3a32ac75..3aae1a10 100644
--- a/chrome/browser/sharing/sharing_fcm_handler_unittest.cc
+++ b/chrome/browser/sharing/sharing_fcm_handler_unittest.cc
@@ -50,7 +50,7 @@
   MOCK_METHOD4(SendMessageToDevice,
                void(syncer::DeviceInfo::SharingInfo target,
                     base::TimeDelta time_to_live,
-                    chrome_browser_sharing::SharingMessage message,
+                    SharingMessage message,
                     SendMessageCallback callback));
 };
 
@@ -142,6 +142,8 @@
   SharingMessage sharing_ack_message;
   sharing_ack_message.mutable_ack_message()->set_original_message_id(
       kTestMessageId);
+  sharing_ack_message.mutable_ack_message()->set_original_message_type(
+      chrome_browser_sharing::PING_MESSAGE);
 
   // Tests OnMessage flow in SharingFCMHandler when no handler is registered.
   EXPECT_CALL(mock_sharing_message_handler_, OnMessage(_)).Times(0);
@@ -183,6 +185,8 @@
   SharingMessage sharing_ack_message;
   sharing_ack_message.mutable_ack_message()->set_original_message_id(
       kTestMessageId);
+  sharing_ack_message.mutable_ack_message()->set_original_message_type(
+      chrome_browser_sharing::PING_MESSAGE);
 
   // Tests OnMessage flow in SharingFCMHandler after handler is added.
   EXPECT_CALL(mock_sharing_message_handler_,
@@ -212,6 +216,8 @@
   SharingMessage sharing_ack_message;
   sharing_ack_message.mutable_ack_message()->set_original_message_id(
       kTestMessageId);
+  sharing_ack_message.mutable_ack_message()->set_original_message_type(
+      chrome_browser_sharing::PING_MESSAGE);
 
   EXPECT_CALL(mock_sharing_message_handler_,
               OnMessage(ProtoEquals(sharing_message)));
diff --git a/chrome/browser/sharing/sharing_metrics.cc b/chrome/browser/sharing/sharing_metrics.cc
index f49fbd2..7ab85015 100644
--- a/chrome/browser/sharing/sharing_metrics.cc
+++ b/chrome/browser/sharing/sharing_metrics.cc
@@ -18,34 +18,6 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
 namespace {
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class SharingMessageType {
-  kUnknownMessage = 0,
-  kPingMessage = 1,
-  kAckMessage = 2,
-  kClickToCallMessage = 3,
-  kSharedClipboardMessage = 4,
-  kMaxValue = kSharedClipboardMessage,
-};
-
-SharingMessageType PayloadCaseToMessageType(
-    chrome_browser_sharing::SharingMessage::PayloadCase payload_case) {
-  switch (payload_case) {
-    case chrome_browser_sharing::SharingMessage::PAYLOAD_NOT_SET:
-      return SharingMessageType::kUnknownMessage;
-    case chrome_browser_sharing::SharingMessage::kPingMessage:
-      return SharingMessageType::kPingMessage;
-    case chrome_browser_sharing::SharingMessage::kAckMessage:
-      return SharingMessageType::kAckMessage;
-    case chrome_browser_sharing::SharingMessage::kClickToCallMessage:
-      return SharingMessageType::kClickToCallMessage;
-    case chrome_browser_sharing::SharingMessage::kSharedClipboardMessage:
-      return SharingMessageType::kSharedClipboardMessage;
-  }
-}
-
 const char* GetEnumStringValue(SharingFeatureName feature) {
   DCHECK(feature != SharingFeatureName::kUnknown)
       << "Feature needs to be specified for metrics logging.";
@@ -59,12 +31,54 @@
       return "SharedClipboard";
   }
 }
+
+const std::string& MessageTypeToMessageSuffix(
+    chrome_browser_sharing::MessageType message_type) {
+  // For proto3 enums unrecognized enum values are kept when parsing and their
+  // name is an empty string. We don't want to use that as a histogram suffix.
+  // The returned values must match the values of the SharingMessage suffixes
+  // defined in histograms.xml.
+  if (!chrome_browser_sharing::MessageType_IsValid(message_type)) {
+    return chrome_browser_sharing::MessageType_Name(
+        chrome_browser_sharing::UNKNOWN_MESSAGE);
+  }
+  return chrome_browser_sharing::MessageType_Name(message_type);
+}
 }  // namespace
 
-void LogSharingMessageReceived(
+chrome_browser_sharing::MessageType SharingPayloadCaseToMessageType(
     chrome_browser_sharing::SharingMessage::PayloadCase payload_case) {
-  base::UmaHistogramEnumeration("Sharing.MessageReceivedType",
-                                PayloadCaseToMessageType(payload_case));
+  switch (payload_case) {
+    case chrome_browser_sharing::SharingMessage::PAYLOAD_NOT_SET:
+      return chrome_browser_sharing::UNKNOWN_MESSAGE;
+    case chrome_browser_sharing::SharingMessage::kPingMessage:
+      return chrome_browser_sharing::PING_MESSAGE;
+    case chrome_browser_sharing::SharingMessage::kAckMessage:
+      return chrome_browser_sharing::ACK_MESSAGE;
+    case chrome_browser_sharing::SharingMessage::kClickToCallMessage:
+      return chrome_browser_sharing::CLICK_TO_CALL_MESSAGE;
+    case chrome_browser_sharing::SharingMessage::kSharedClipboardMessage:
+      return chrome_browser_sharing::SHARED_CLIPBOARD_MESSAGE;
+  }
+  // For proto3 enums unrecognized enum values are kept when parsing, and a new
+  // payload case received over the network would not default to
+  // PAYLOAD_NOT_SET. Explicitly return UNKNOWN_MESSAGE here to handle this
+  // case.
+  return chrome_browser_sharing::UNKNOWN_MESSAGE;
+}
+
+void LogSharingMessageReceived(
+    chrome_browser_sharing::MessageType original_message_type,
+    chrome_browser_sharing::SharingMessage::PayloadCase payload_case) {
+  chrome_browser_sharing::MessageType actual_message_type =
+      SharingPayloadCaseToMessageType(payload_case);
+  base::UmaHistogramExactLinear("Sharing.MessageReceivedType",
+                                actual_message_type,
+                                chrome_browser_sharing::MessageType_ARRAYSIZE);
+  base::UmaHistogramExactLinear(
+      base::StrCat({"Sharing.MessageReceivedType.",
+                    MessageTypeToMessageSuffix(original_message_type)}),
+      actual_message_type, chrome_browser_sharing::MessageType_ARRAYSIZE);
 }
 
 void LogSharingRegistrationResult(SharingDeviceRegistrationResult result) {
@@ -150,8 +164,13 @@
       /*value_max=*/20);
 }
 
-void LogSharingMessageAckTime(base::TimeDelta time) {
+void LogSharingMessageAckTime(chrome_browser_sharing::MessageType message_type,
+                              base::TimeDelta time) {
   base::UmaHistogramMediumTimes("Sharing.MessageAckTime", time);
+  base::UmaHistogramMediumTimes(
+      base::StrCat({"Sharing.MessageAckTime.",
+                    MessageTypeToMessageSuffix(message_type)}),
+      time);
 }
 
 void LogSharingDialogShown(SharingFeatureName feature, SharingDialogType type) {
@@ -164,12 +183,24 @@
   base::UmaHistogramEnumeration("Sharing.ClickToCallHelpTextClicked", type);
 }
 
-void LogSendSharingMessageResult(SharingSendMessageResult result) {
+void LogSendSharingMessageResult(
+    chrome_browser_sharing::MessageType message_type,
+    SharingSendMessageResult result) {
   base::UmaHistogramEnumeration("Sharing.SendMessageResult", result);
+  base::UmaHistogramEnumeration(
+      base::StrCat({"Sharing.SendMessageResult.",
+                    MessageTypeToMessageSuffix(message_type)}),
+      result);
 }
 
-void LogSendSharingAckMessageResult(SharingSendMessageResult result) {
+void LogSendSharingAckMessageResult(
+    chrome_browser_sharing::MessageType message_type,
+    SharingSendMessageResult result) {
   base::UmaHistogramEnumeration("Sharing.SendAckMessageResult", result);
+  base::UmaHistogramEnumeration(
+      base::StrCat({"Sharing.SendAckMessageResult.",
+                    MessageTypeToMessageSuffix(message_type)}),
+      result);
 }
 
 void LogClickToCallUKM(content::WebContents* web_contents,
diff --git a/chrome/browser/sharing/sharing_metrics.h b/chrome/browser/sharing/sharing_metrics.h
index f3fcb81c..efc6736f 100644
--- a/chrome/browser/sharing/sharing_metrics.h
+++ b/chrome/browser/sharing/sharing_metrics.h
@@ -5,9 +5,8 @@
 #ifndef CHROME_BROWSER_SHARING_SHARING_METRICS_H_
 #define CHROME_BROWSER_SHARING_SHARING_METRICS_H_
 
-#include "chrome/browser/sharing/proto/sharing_message.pb.h"
-
 #include "base/time/time.h"
+#include "chrome/browser/sharing/proto/sharing_message.pb.h"
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_send_message_result.h"
 
@@ -72,9 +71,15 @@
 const char kSharingClickToCallUiContextMenu[] = "ContextMenu";
 const char kSharingClickToCallUiDialog[] = "Dialog";
 
+chrome_browser_sharing::MessageType SharingPayloadCaseToMessageType(
+    chrome_browser_sharing::SharingMessage::PayloadCase payload_case);
+
 // Logs the |payload_case| to UMA. This should be called when a SharingMessage
-// is received.
+// is received. Additionally, a suffixed version of the histogram is logged
+// using |original_message_type| which is different from the actual message type
+// for ack messages.
 void LogSharingMessageReceived(
+    chrome_browser_sharing::MessageType original_message_type,
     chrome_browser_sharing::SharingMessage::PayloadCase payload_case);
 
 // Logs the |result| to UMA. This should be called after attempting register
@@ -126,7 +131,8 @@
 
 // Logs to UMA the time from sending a FCM message from the Sharing service
 // until an ack message is received for it.
-void LogSharingMessageAckTime(base::TimeDelta time);
+void LogSharingMessageAckTime(chrome_browser_sharing::MessageType message_type,
+                              base::TimeDelta time);
 
 // Logs to UMA the |type| of dialog shown for sharing feature.
 void LogSharingDialogShown(SharingFeatureName feature, SharingDialogType type);
@@ -137,10 +143,14 @@
 
 // Logs to UMA result of sending a SharingMessage. This should not be called for
 // sending ack messages.
-void LogSendSharingMessageResult(SharingSendMessageResult result);
+void LogSendSharingMessageResult(
+    chrome_browser_sharing::MessageType message_type,
+    SharingSendMessageResult result);
 
 // Logs to UMA result of sendin an ack of a SharingMessage.
-void LogSendSharingAckMessageResult(SharingSendMessageResult result);
+void LogSendSharingAckMessageResult(
+    chrome_browser_sharing::MessageType message_type,
+    SharingSendMessageResult result);
 
 // Records a Click to Call selection to UKM. This is logged after a completed
 // action like selecting an app or a device to send the phone number to.
diff --git a/chrome/browser/sharing/sharing_service.cc b/chrome/browser/sharing/sharing_service.cc
index 70a4007..85c94be0 100644
--- a/chrome/browser/sharing/sharing_service.cc
+++ b/chrome/browser/sharing/sharing_service.cc
@@ -67,67 +67,61 @@
   return l10n_util::GetStringUTF8(device_type_message_id);
 }
 
-// Util function to get name for a single device. |is_full_name| determines
-// whether we should add the model name for the device.
-std::string GetDeviceName(const syncer::DeviceInfo* device, bool is_full_name) {
+struct DeviceNames {
+  std::string full_name;
+  std::string short_name;
+};
+
+// Returns full and short names for |device|.
+DeviceNames GetDeviceNames(const syncer::DeviceInfo* device) {
   DCHECK(device);
+  DeviceNames device_names;
 
   base::SysInfo::HardwareInfo hardware_info = device->hardware_info();
 
   // The model might be empty if other device is still on M78 or lower with sync
   // turned on.
-  if (hardware_info.model.empty())
-    return device->client_name();
+  if (hardware_info.model.empty()) {
+    device_names.full_name = device_names.short_name = device->client_name();
+    return device_names;
+  }
 
   sync_pb::SyncEnums::DeviceType type = device->device_type();
 
   // For chromeOS, return manufacturer + model.
   if (type == sync_pb::SyncEnums::TYPE_CROS) {
-    return base::StrCat({device->hardware_info().manufacturer, " ",
-                         device->hardware_info().model});
+    device_names.short_name = device_names.full_name =
+        base::StrCat({device->hardware_info().manufacturer, " ",
+                      device->hardware_info().model});
+    return device_names;
   }
 
   if (hardware_info.manufacturer == "Apple Inc.") {
-    if (is_full_name)
-      return hardware_info.model;
-
-    // Returns a more readable hardware model. Internal names of Apple devices
-    // are formatted as MacbookPro2,3 or iPhone2,1 or Ipad4,1.
-    return hardware_info.model.substr(
+    // Internal names of Apple devices are formatted as MacbookPro2,3 or
+    // iPhone2,1 or Ipad4,1.
+    device_names.short_name = hardware_info.model.substr(
         0, hardware_info.model.find_first_of("0123456789,"));
+    device_names.full_name = hardware_info.model;
+    return device_names;
   }
 
-  std::string device_name =
+  device_names.short_name =
       base::StrCat({hardware_info.manufacturer, " ", GetDeviceType(type)});
-  if (is_full_name)
-    base::StrAppend(&device_name, {" ", hardware_info.model});
-
-  return device_name;
+  device_names.full_name =
+      base::StrCat({device_names.short_name, " ", hardware_info.model});
+  return device_names;
 }
 
-// Util function to rename devices for Sharing.
-std::vector<std::unique_ptr<syncer::DeviceInfo>> RenameDevices(
-    const std::vector<std::unique_ptr<syncer::DeviceInfo>>& devices) {
-  std::map<std::string, int> similar_devices_counter;
-  for (const auto& device : devices)
-    similar_devices_counter[GetDeviceName(device.get(),
-                                          /*is_full_name=*/false)]++;
-
-  std::vector<std::unique_ptr<syncer::DeviceInfo>> renamed_devices;
-  for (const auto& device : devices) {
-    bool is_full_name_required = similar_devices_counter[GetDeviceName(
-                                     device.get(), /*is_full_name=*/false)] > 1;
-    std::string device_name =
-        GetDeviceName(device.get(), is_full_name_required);
-
-    renamed_devices.push_back(std::make_unique<syncer::DeviceInfo>(
-        device->guid(), device_name, device->chrome_version(),
-        device->sync_user_agent(), device->device_type(),
-        device->signin_scoped_device_id(), device->hardware_info(),
-        device->last_updated_timestamp(),
-        device->send_tab_to_self_receiving_enabled(), device->sharing_info()));
-  }
-  return renamed_devices;
+// Clones device with new device name.
+std::unique_ptr<syncer::DeviceInfo> CloneDevice(
+    const syncer::DeviceInfo* device,
+    const std::string& device_name) {
+  return std::make_unique<syncer::DeviceInfo>(
+      device->guid(), device_name, device->chrome_version(),
+      device->sync_user_agent(), device->device_type(),
+      device->signin_scoped_device_id(), device->hardware_info(),
+      device->last_updated_timestamp(),
+      device->send_tab_to_self_receiving_enabled(), device->sharing_info());
 }
 }  // namespace
 
@@ -225,61 +219,16 @@
   return device_info_tracker_->GetDeviceInfo(guid);
 }
 
-// TODO(crbug.com/1014107) - Simplify this function. The function does a number
-// of things and some calls are duplicated at the moment.
-std::vector<std::unique_ptr<syncer::DeviceInfo>>
-SharingService::GetDeviceCandidates(
+SharingService::SharingDeviceList SharingService::GetDeviceCandidates(
     sync_pb::SharingSpecificFields::EnabledFeatures required_feature) const {
-  std::vector<std::unique_ptr<syncer::DeviceInfo>> device_candidates;
-  std::vector<std::unique_ptr<syncer::DeviceInfo>> all_devices =
+  if (IsSyncDisabled() || !local_device_info_provider_->GetLocalDeviceInfo())
+    return {};
+
+  SharingDeviceList device_candidates =
       device_info_tracker_->GetAllDeviceInfo();
-  const syncer::DeviceInfo* local_device_info =
-      local_device_info_provider_->GetLocalDeviceInfo();
-
-  if (IsSyncDisabled() || all_devices.empty() || !local_device_info)
-    return device_candidates;
-
-  const base::Time min_updated_time = base::Time::Now() - kDeviceExpiration;
-
-  // Sort the DeviceInfo vector so the most recently modified devices are first.
-  std::sort(all_devices.begin(), all_devices.end(),
-            [](const auto& device1, const auto& device2) {
-              return device1->last_updated_timestamp() >
-                     device2->last_updated_timestamp();
-            });
-
-  std::unordered_set<std::string> full_device_names;
-  // To prevent adding candidates with same name as local device.
-  full_device_names.insert(
-      GetDeviceName(local_device_info, /*is_full_name=*/true));
-
-  for (auto& device : all_devices) {
-    // If the current device is considered expired for our purposes, stop here
-    // since the next devices in the vector are at least as expired than this
-    // one.
-    if (device->last_updated_timestamp() < min_updated_time)
-      break;
-
-    base::Optional<syncer::DeviceInfo::SharingInfo> sharing_info =
-        sync_prefs_->GetSharingInfo(device.get());
-    if (!sharing_info ||
-        !sharing_info->enabled_features.count(required_feature)) {
-      continue;
-    }
-
-    // Only insert the first occurrence of each device name.
-    auto inserted = full_device_names.insert(
-        GetDeviceName(device.get(), /*is_full_name=*/true));
-    if (inserted.second)
-      device_candidates.push_back(std::move(device));
-  }
-
-  // TODO(knollr): Remove devices from |sync_prefs_| that are in
-  // |synced_devices| but not in |all_devices|?
-
-  device_candidates = RenameDevices(device_candidates);
-
-  return device_candidates;
+  device_candidates =
+      FilterDeviceCandidates(std::move(device_candidates), required_feature);
+  return RenameAndDeduplicateDevices(std::move(device_candidates));
 }
 
 void SharingService::AddDeviceCandidatesInitializedObserver(
@@ -319,18 +268,20 @@
     SendMessageCallback callback) {
   std::string message_guid = base::GenerateGUID();
   send_message_callbacks_.emplace(message_guid, std::move(callback));
+  chrome_browser_sharing::MessageType message_type =
+      SharingPayloadCaseToMessageType(message.payload_case());
 
   base::PostDelayedTask(
       FROM_HERE, {base::TaskPriority::USER_VISIBLE, content::BrowserThread::UI},
       base::BindOnce(&SharingService::InvokeSendMessageCallback,
-                     weak_ptr_factory_.GetWeakPtr(), message_guid,
+                     weak_ptr_factory_.GetWeakPtr(), message_guid, message_type,
                      SharingSendMessageResult::kAckTimeout),
       kSendMessageTimeout);
 
   base::Optional<syncer::DeviceInfo::SharingInfo> sharing_info =
       sync_prefs_->GetSharingInfo(device_guid);
   if (!sharing_info) {
-    InvokeSendMessageCallback(message_guid,
+    InvokeSendMessageCallback(message_guid, message_type,
                               SharingSendMessageResult::kDeviceNotFound);
     return;
   }
@@ -339,7 +290,7 @@
       std::move(*sharing_info), time_to_live, std::move(message),
       base::BindOnce(&SharingService::OnMessageSent,
                      weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(),
-                     message_guid));
+                     message_guid, message_type));
 }
 
 void SharingService::SetDeviceInfoTrackerForTesting(
@@ -347,12 +298,14 @@
   device_info_tracker_ = tracker;
 }
 
-void SharingService::OnMessageSent(base::TimeTicks start_time,
-                                   const std::string& message_guid,
-                                   SharingSendMessageResult result,
-                                   base::Optional<std::string> message_id) {
+void SharingService::OnMessageSent(
+    base::TimeTicks start_time,
+    const std::string& message_guid,
+    chrome_browser_sharing::MessageType message_type,
+    SharingSendMessageResult result,
+    base::Optional<std::string> message_id) {
   if (result != SharingSendMessageResult::kSuccessful) {
-    InvokeSendMessageCallback(message_guid, result);
+    InvokeSendMessageCallback(message_guid, message_type, result);
     return;
   }
 
@@ -360,10 +313,13 @@
   message_guids_.emplace(*message_id, message_guid);
 }
 
-void SharingService::OnAckReceived(const std::string& message_id) {
+void SharingService::OnAckReceived(
+    chrome_browser_sharing::MessageType message_type,
+    const std::string& message_id) {
   auto times_iter = send_message_times_.find(message_id);
   if (times_iter != send_message_times_.end()) {
-    LogSharingMessageAckTime(base::TimeTicks::Now() - times_iter->second);
+    LogSharingMessageAckTime(message_type,
+                             base::TimeTicks::Now() - times_iter->second);
     send_message_times_.erase(times_iter);
   }
 
@@ -373,12 +329,13 @@
 
   std::string message_guid = std::move(iter->second);
   message_guids_.erase(iter);
-  InvokeSendMessageCallback(message_guid,
+  InvokeSendMessageCallback(message_guid, message_type,
                             SharingSendMessageResult::kSuccessful);
 }
 
 void SharingService::InvokeSendMessageCallback(
     const std::string& message_guid,
+    chrome_browser_sharing::MessageType message_type,
     SharingSendMessageResult result) {
   auto iter = send_message_callbacks_.find(message_guid);
   if (iter == send_message_callbacks_.end())
@@ -387,7 +344,7 @@
   SendMessageCallback callback = std::move(iter->second);
   send_message_callbacks_.erase(iter);
   std::move(callback).Run(result);
-  LogSendSharingMessageResult(result);
+  LogSendSharingMessageResult(message_type, result);
 }
 
 void SharingService::OnDeviceInfoChange() {
@@ -590,3 +547,76 @@
 
   return required_data_types;
 }
+
+SharingService::SharingDeviceList SharingService::FilterDeviceCandidates(
+    SharingDeviceList devices,
+    sync_pb::SharingSpecificFields::EnabledFeatures required_feature) const {
+  const base::Time min_updated_time = base::Time::Now() - kDeviceExpiration;
+  SharingDeviceList filtered_devices;
+
+  for (auto& device : devices) {
+    // Checks if |last_updated_timestamp| is not too old.
+    if (device->last_updated_timestamp() < min_updated_time)
+      continue;
+
+    // Checks whether |device| supports |required_feature|.
+    base::Optional<syncer::DeviceInfo::SharingInfo> sharing_info =
+        sync_prefs_->GetSharingInfo(device.get());
+    if (!sharing_info ||
+        !sharing_info->enabled_features.count(required_feature)) {
+      continue;
+    }
+
+    filtered_devices.push_back(std::move(device));
+  }
+
+  return filtered_devices;
+}
+
+SharingService::SharingDeviceList SharingService::RenameAndDeduplicateDevices(
+    SharingDeviceList devices) const {
+  // Sort the devices so the most recently modified devices are first.
+  std::sort(devices.begin(), devices.end(),
+            [](const auto& device1, const auto& device2) {
+              return device1->last_updated_timestamp() >
+                     device2->last_updated_timestamp();
+            });
+
+  std::unordered_map<std::string, DeviceNames> device_candidate_names;
+  std::unordered_set<std::string> full_device_names;
+  std::unordered_map<std::string, int> short_device_name_counter;
+
+  // To prevent adding candidates with same full name as local device.
+  full_device_names.insert(
+      GetDeviceNames(local_device_info_provider_->GetLocalDeviceInfo())
+          .full_name);
+
+  for (const auto& device : devices) {
+    DeviceNames device_names = GetDeviceNames(device.get());
+
+    // Only insert the first occurrence of each device name.
+    auto inserted = full_device_names.insert(device_names.full_name);
+    if (!inserted.second)
+      continue;
+
+    short_device_name_counter[device_names.short_name]++;
+    device_candidate_names.insert({device->guid(), std::move(device_names)});
+  }
+
+  // Rename filtered devices.
+  SharingDeviceList device_candidates;
+  for (const auto& device : devices) {
+    auto it = device_candidate_names.find(device->guid());
+    if (it == device_candidate_names.end())
+      continue;
+
+    const DeviceNames& device_names = it->second;
+    bool is_short_name_unique =
+        short_device_name_counter[device_names.short_name] == 1;
+    device_candidates.push_back(CloneDevice(
+        device.get(), is_short_name_unique ? device_names.short_name
+                                           : device_names.full_name));
+  }
+
+  return device_candidates;
+}
diff --git a/chrome/browser/sharing/sharing_service.h b/chrome/browser/sharing/sharing_service.h
index ca435b4..5bb5b0e 100644
--- a/chrome/browser/sharing/sharing_service.h
+++ b/chrome/browser/sharing/sharing_service.h
@@ -61,6 +61,7 @@
  public:
   using SendMessageCallback =
       base::OnceCallback<void(SharingSendMessageResult)>;
+  using SharingDeviceList = std::vector<std::unique_ptr<syncer::DeviceInfo>>;
 
   enum class State {
     // Device is unregistered with FCM and Sharing is unavailable.
@@ -92,7 +93,7 @@
 
   // Returns a list of DeviceInfo that is available to receive messages.
   // All returned devices have the specified |required_feature|.
-  virtual std::vector<std::unique_ptr<syncer::DeviceInfo>> GetDeviceCandidates(
+  virtual SharingDeviceList GetDeviceCandidates(
       sync_pb::SharingSpecificFields::EnabledFeatures required_feature) const;
 
   // Register |callback| so it will be invoked after all dependencies of
@@ -133,7 +134,8 @@
   void OnSyncCycleCompleted(syncer::SyncService* sync) override;
 
   // AckMessageHandler::AckMessageObserver override.
-  void OnAckReceived(const std::string& message_id) override;
+  void OnAckReceived(chrome_browser_sharing::MessageType message_type,
+                     const std::string& message_id) override;
 
   // syncer::DeviceInfoTracker::Observer.
   void OnDeviceInfoChange() override;
@@ -148,10 +150,13 @@
 
   void OnMessageSent(base::TimeTicks start_time,
                      const std::string& message_guid,
+                     chrome_browser_sharing::MessageType message_type,
                      SharingSendMessageResult result,
                      base::Optional<std::string> message_id);
-  void InvokeSendMessageCallback(const std::string& message_guid,
-                                 SharingSendMessageResult result);
+  void InvokeSendMessageCallback(
+      const std::string& message_guid,
+      chrome_browser_sharing::MessageType message_type,
+      SharingSendMessageResult result);
 
   // Returns true if required sync feature is enabled.
   bool IsSyncEnabled() const;
@@ -163,6 +168,21 @@
   // Returns all required sync data types to enable Sharing feature.
   syncer::ModelTypeSet GetRequiredSyncDataTypes() const;
 
+  // Returns list of devices that have |required_feature| enabled. Also
+  // filters out devices which have not been online for more than
+  // |SharingConstants::kDeviceExpiration| time.
+  SharingDeviceList FilterDeviceCandidates(
+      SharingDeviceList devices,
+      sync_pb::SharingSpecificFields::EnabledFeatures required_feature) const;
+
+  // Deduplicates devices based on client name. For devices with duplicate
+  // client names, only the most recently updated device is filtered in.
+  // Returned devices are renamed using the RenameDevice function
+  // and are sorted in (not strictly) descending order by
+  // last_updated_timestamp.
+  SharingDeviceList RenameAndDeduplicateDevices(
+      SharingDeviceList devices) const;
+
   std::unique_ptr<SharingSyncPreference> sync_prefs_;
   std::unique_ptr<VapidKeyManager> vapid_key_manager_;
   std::unique_ptr<SharingDeviceRegistration> sharing_device_registration_;
diff --git a/chrome/browser/subresource_filter/subresource_filter_worker_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_worker_browsertest.cc
index 4875fe2..9c16663 100644
--- a/chrome/browser/subresource_filter/subresource_filter_worker_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_worker_browsertest.cc
@@ -4,20 +4,17 @@
 
 #include <vector>
 
-#include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -28,61 +25,83 @@
 class SubresourceFilterWorkerFetchBrowserTest
     : public SubresourceFilterBrowserTest {
  public:
-  SubresourceFilterWorkerFetchBrowserTest() {}
-
-  ~SubresourceFilterWorkerFetchBrowserTest() override {}
+  SubresourceFilterWorkerFetchBrowserTest() = default;
+  ~SubresourceFilterWorkerFetchBrowserTest() override = default;
 
  protected:
+  void RunTest(const std::string& document_path,
+               const std::string& filter_path) {
+    const base::string16 fetch_succeeded_title =
+        base::ASCIIToUTF16("FetchSucceeded");
+    const base::string16 fetch_failed_title = base::ASCIIToUTF16("FetchFailed");
+    const base::string16 fetch_partially_failed_title =
+        base::ASCIIToUTF16("FetchPartiallyFailed");
+
+    GURL url(GetTestUrl(document_path));
+    ConfigureAsPhishingURL(url);
+
+    // This unrelated rule shouldn't block fetch.
+    ASSERT_NO_FATAL_FAILURE(SetRulesetToDisallowURLsWithPathSuffix(
+        "suffix-that-does-not-match-anything"));
+    {
+      content::TitleWatcher title_watcher(
+          browser()->tab_strip_model()->GetActiveWebContents(),
+          fetch_succeeded_title);
+      title_watcher.AlsoWaitForTitle(fetch_failed_title);
+      title_watcher.AlsoWaitForTitle(fetch_partially_failed_title);
+      ui_test_utils::NavigateToURL(browser(), url);
+      EXPECT_EQ(fetch_succeeded_title, title_watcher.WaitAndGetTitle());
+    }
+    ClearTitle();
+
+    // This rule should block fetch.
+    ASSERT_NO_FATAL_FAILURE(
+        SetRulesetToDisallowURLsWithPathSuffix(filter_path));
+    {
+      content::TitleWatcher title_watcher(
+          browser()->tab_strip_model()->GetActiveWebContents(),
+          fetch_succeeded_title);
+      title_watcher.AlsoWaitForTitle(fetch_failed_title);
+      title_watcher.AlsoWaitForTitle(fetch_partially_failed_title);
+      ui_test_utils::NavigateToURL(browser(), url);
+      EXPECT_EQ(fetch_failed_title, title_watcher.WaitAndGetTitle());
+    }
+    ClearTitle();
+  }
+
   void ClearTitle() {
     ASSERT_TRUE(content::ExecuteScript(web_contents()->GetMainFrame(),
                                        "document.title = \"\";"));
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(SubresourceFilterWorkerFetchBrowserTest);
 };
 
-IN_PROC_BROWSER_TEST_F(SubresourceFilterWorkerFetchBrowserTest, WorkerFetch) {
-  const base::string16 fetch_succeeded_title =
-      base::ASCIIToUTF16("FetchSucceeded");
-  const base::string16 fetch_failed_title = base::ASCIIToUTF16("FetchFailed");
-  GURL url(GetTestUrl("subresource_filter/worker_fetch.html"));
-  ConfigureAsPhishingURL(url);
+// TODO(https://crbug.com/1011208): Add more tests for workers like top-level
+// worker script fetch and module script fetch.
 
-  ASSERT_NO_FATAL_FAILURE(SetRulesetToDisallowURLsWithPathSuffix(
-      "suffix-that-does-not-match-anything"));
-  {
-    content::TitleWatcher title_watcher(
-        browser()->tab_strip_model()->GetActiveWebContents(),
-        fetch_succeeded_title);
-    title_watcher.AlsoWaitForTitle(fetch_failed_title);
-    ui_test_utils::NavigateToURL(browser(), url);
-    EXPECT_EQ(fetch_succeeded_title, title_watcher.WaitAndGetTitle());
-  }
-  ClearTitle();
-  ASSERT_NO_FATAL_FAILURE(
-      SetRulesetToDisallowURLsWithPathSuffix("worker_fetch_data.txt"));
-  {
-    content::TitleWatcher title_watcher(
-        browser()->tab_strip_model()->GetActiveWebContents(),
-        fetch_succeeded_title);
-    title_watcher.AlsoWaitForTitle(fetch_failed_title);
-    ui_test_utils::NavigateToURL(browser(), url);
-    EXPECT_EQ(fetch_failed_title, title_watcher.WaitAndGetTitle());
-  }
-  ClearTitle();
-  // The main frame document should never be filtered.
-  SetRulesetToDisallowURLsWithPathSuffix("worker_fetch.html");
-  {
-    content::TitleWatcher title_watcher(
-        browser()->tab_strip_model()->GetActiveWebContents(),
-        fetch_succeeded_title);
-    title_watcher.AlsoWaitForTitle(fetch_failed_title);
-    ui_test_utils::NavigateToURL(browser(), url);
-    EXPECT_EQ(fetch_succeeded_title, title_watcher.WaitAndGetTitle());
-  }
+// Test if fetch() on dedicated workers is blocked by the subresource filter.
+IN_PROC_BROWSER_TEST_F(SubresourceFilterWorkerFetchBrowserTest, WorkerFetch) {
+  // This fetches "worklet_fetch_data.txt" by fetch().
+  RunTest("subresource_filter/worker_fetch.html", "worker_fetch_data.txt");
 }
 
+// Test if top-level worklet script fetch is blocked by the subresource filter.
+IN_PROC_BROWSER_TEST_F(SubresourceFilterWorkerFetchBrowserTest,
+                       WorkletScriptFetch) {
+  RunTest("subresource_filter/worklet_script_fetch.html",
+          "worklet_script_fetch.js");
+}
+
+// Test if static import on worklets is blocked by the subresource filter.
+IN_PROC_BROWSER_TEST_F(SubresourceFilterWorkerFetchBrowserTest,
+                       WorkletStaticImport) {
+  // This fetches "empty.js" by static import.
+  RunTest("subresource_filter/worklet_script_fetch.html", "empty.js");
+}
+
+// Any network APIs including dynamic import are disallowed on worklets, so we
+// don't have to test them.
+
 }  // namespace subresource_filter
diff --git a/chrome/browser/sync/test/integration/bookmarks_helper.cc b/chrome/browser/sync/test/integration/bookmarks_helper.cc
index 6fbfe7a..c9b43fc 100644
--- a/chrome/browser/sync/test/integration/bookmarks_helper.cc
+++ b/chrome/browser/sync/test/integration/bookmarks_helper.cc
@@ -1048,17 +1048,23 @@
   // Make a copy so we can remove bookmarks that were found.
   std::vector<ExpectedBookmark> expected = expected_bookmarks_;
   for (const sync_pb::SyncEntity& entity : entities) {
-    // If the cryptographer was provided, we expect the specifics to have
-    // encrypted data.
-    EXPECT_EQ(entity.specifics().has_encrypted(), cryptographer_ != nullptr);
-
     sync_pb::BookmarkSpecifics actual_specifics;
     if (entity.specifics().has_encrypted()) {
+      // If no cryptographer was provided, we expect the specifics to have
+      // unencrypted data.
+      if (!cryptographer_) {
+        return false;
+      }
       sync_pb::EntitySpecifics entity_specifics;
       EXPECT_TRUE(cryptographer_->Decrypt(entity.specifics().encrypted(),
                                           &entity_specifics));
       actual_specifics = entity_specifics.bookmark();
     } else {
+      // If the cryptographer was provided, we expect the specifics to have
+      // encrypted data.
+      if (cryptographer_) {
+        return false;
+      }
       actual_specifics = entity.specifics().bookmark();
     }
 
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 4e7ffc6..c7d871ca 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -1019,7 +1019,7 @@
   // Allows google.com as well as country-specific TLDs.
   host_resolver()->AllowDirectLookup("*.google.com");
   host_resolver()->AllowDirectLookup("accounts.google.*");
-
+  host_resolver()->AllowDirectLookup("*.googleusercontent.com");
   // Allow connection to googleapis.com for oauth token requests in E2E tests.
   host_resolver()->AllowDirectLookup("*.googleapis.com");
 
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc
index 3efa9b5..b3331da2 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc
@@ -22,6 +22,7 @@
   ~TwoClientWebAppsSyncTest() override = default;
 
   AppId InstallApp(const WebApplicationInfo& info, Profile* profile) {
+    DCHECK(info.app_url.is_valid());
     base::RunLoop run_loop;
     AppId app_id;
 
@@ -148,4 +149,65 @@
   EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
 }
 
+IN_PROC_BROWSER_TEST_F(TwoClientWebAppsSyncTest, AppFieldsChangeDoesNotSync) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
+
+  const AppRegistrar& registrar0 = GetRegistrar(GetProfile(0));
+  const AppRegistrar& registrar1 = GetRegistrar(GetProfile(1));
+
+  WebApplicationInfo info_a;
+  info_a.title = base::UTF8ToUTF16("Test name A");
+  info_a.description = base::UTF8ToUTF16("Description A");
+  info_a.app_url = GURL("http://www.chromium.org/path/to/start_url");
+  info_a.scope = GURL("http://www.chromium.org/path/to/");
+  info_a.theme_color = SK_ColorBLUE;
+  AppId app_id_a = InstallApp(info_a, GetProfile(0));
+
+  EXPECT_EQ(WebAppInstallObserver(GetProfile(1)).AwaitNextInstall(), app_id_a);
+  EXPECT_EQ(base::UTF8ToUTF16(registrar1.GetAppShortName(app_id_a)),
+            info_a.title);
+  EXPECT_EQ(base::UTF8ToUTF16(registrar1.GetAppDescription(app_id_a)),
+            info_a.description);
+  EXPECT_EQ(registrar1.GetAppScope(app_id_a), info_a.scope);
+  EXPECT_EQ(registrar1.GetAppThemeColor(app_id_a), info_a.theme_color);
+  ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
+
+  // Reinstall same app in Profile 0 with a different metadata aside from the
+  // name and start_url.
+  WebApplicationInfo info_b;
+  info_b.title = base::UTF8ToUTF16("Test name B");
+  info_b.description = base::UTF8ToUTF16("Description B");
+  info_b.app_url = GURL("http://www.chromium.org/path/to/start_url");
+  info_b.scope = GURL("http://www.chromium.org/path/to/");
+  info_b.theme_color = SK_ColorRED;
+  AppId app_id_b = InstallApp(info_b, GetProfile(0));
+  EXPECT_EQ(app_id_a, app_id_b);
+  EXPECT_EQ(base::UTF8ToUTF16(registrar0.GetAppShortName(app_id_a)),
+            info_b.title);
+  EXPECT_EQ(base::UTF8ToUTF16(registrar0.GetAppDescription(app_id_a)),
+            info_b.description);
+  EXPECT_EQ(registrar0.GetAppScope(app_id_a), info_b.scope);
+  EXPECT_EQ(registrar0.GetAppThemeColor(app_id_a), info_b.theme_color);
+
+  // Install a separate app just to have something to await on to ensure the
+  // sync has propagated to the other profile.
+  WebApplicationInfo infoC;
+  infoC.title = base::UTF8ToUTF16("Different test name");
+  infoC.app_url = GURL("http://www.notchromium.org/");
+  AppId app_id_c = InstallApp(infoC, GetProfile(0));
+  EXPECT_NE(app_id_a, app_id_c);
+  EXPECT_EQ(WebAppInstallObserver(GetProfile(1)).AwaitNextInstall(), app_id_c);
+
+  // After sync we should not see the metadata update in Profile 1.
+  EXPECT_EQ(base::UTF8ToUTF16(registrar1.GetAppShortName(app_id_a)),
+            info_a.title);
+  EXPECT_EQ(base::UTF8ToUTF16(registrar1.GetAppDescription(app_id_a)),
+            info_a.description);
+  EXPECT_EQ(registrar1.GetAppScope(app_id_a), info_a.scope);
+  EXPECT_EQ(registrar1.GetAppThemeColor(app_id_a), info_a.theme_color);
+
+  EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml
new file mode 100644
index 0000000..b628745
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:layout_marginBottom="16dp"
+    android:orientation="vertical"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp">
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:importantForAccessibility="no"
+        app:srcCompat="@drawable/touch_to_fill_header_image" />
+
+    <org.chromium.ui.widget.TextViewWithLeading
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:text="@string/touch_to_fill_sheet_title"
+        android:textAppearance="@style/TextAppearance.BlackHeadline" />
+
+    <org.chromium.ui.widget.TextViewWithLeading
+        android:id="@+id/touch_to_fill_sheet_subtitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:textAppearance="@style/TextAppearance.BlackHint1" />
+</LinearLayout>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml
index 3223dc1..d1b527b 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml
+++ b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml
@@ -9,6 +9,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
+    android:minHeight="340dp"
     android:orientation="vertical"
     android:paddingStart="16dp"
     android:paddingEnd="16dp">
@@ -23,27 +24,6 @@
         android:importantForAccessibility="no"
         app:srcCompat="@drawable/drag_handlebar" />
 
-    <ImageView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:importantForAccessibility="no"
-        app:srcCompat="@drawable/touch_to_fill_header_image" />
-
-    <org.chromium.ui.widget.TextViewWithLeading
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:text="@string/touch_to_fill_sheet_title"
-        android:textAppearance="@style/TextAppearance.BlackHeadline" />
-
-    <org.chromium.ui.widget.TextViewWithLeading
-        android:id="@+id/touch_to_fill_sheet_subtitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:textAppearance="@style/TextAppearance.BlackHint1" />
-
     <android.support.v7.widget.RecyclerView
         android:id="@+id/sheet_item_list"
         android:layout_width="match_parent"
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java
index de759c5..8947197 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java
@@ -20,7 +20,8 @@
  */
 public class TouchToFillCoordinator implements TouchToFillComponent {
     private final TouchToFillMediator mMediator = new TouchToFillMediator();
-    private final PropertyModel mModel = TouchToFillProperties.createDefaultModel(mMediator);
+    private final PropertyModel mModel =
+            TouchToFillProperties.createDefaultModel(mMediator::onDismissed);
 
     @Override
     public void initialize(Context context, BottomSheetController sheetController,
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java
index d0408d92..f7c46ef 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java
@@ -8,15 +8,18 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.FAVICON;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.FORMATTED_ORIGIN;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.ON_CLICK_LISTENER;
-import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FORMATTED_URL;
-import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.ORIGIN_SECURE;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.VISIBLE;
 
 import androidx.annotation.Px;
 
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties;
+import org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties;
 import org.chromium.chrome.browser.touch_to_fill.data.Credential;
+import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.StateChangeReason;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
@@ -28,10 +31,16 @@
  * Contains the logic for the TouchToFill component. It sets the state of the model and reacts to
  * events like clicks.
  */
-class TouchToFillMediator implements TouchToFillProperties.ViewEventListener {
+class TouchToFillMediator {
+    static final String UMA_TOUCH_TO_FILL_DISMISSAL_REASON =
+            "PasswordManager.TouchToFill.DismissalReason";
+    static final String UMA_TOUCH_TO_FILL_CREDENTIAL_INDEX =
+            "PasswordManager.TouchToFill.CredentialIndex";
+
     private TouchToFillComponent.Delegate mDelegate;
     private PropertyModel mModel;
     private @Px int mDesiredFaviconSize;
+    private List<Credential> mCredentials;
 
     void initialize(TouchToFillComponent.Delegate delegate, PropertyModel model,
             @Px int desiredFaviconSize) {
@@ -44,12 +53,18 @@
     void showCredentials(
             String formattedUrl, boolean isOriginSecure, List<Credential> credentials) {
         assert credentials != null;
-        mModel.set(FORMATTED_URL, formattedUrl);
-        mModel.set(ORIGIN_SECURE, isOriginSecure);
         mModel.set(VISIBLE, true);
 
         ListModel<ListItem> sheetItems = mModel.get(SHEET_ITEMS);
         sheetItems.clear();
+
+        sheetItems.add(new ListItem(TouchToFillProperties.ItemType.HEADER,
+                new PropertyModel.Builder(HeaderProperties.ALL_KEYS)
+                        .with(FORMATTED_URL, formattedUrl)
+                        .with(ORIGIN_SECURE, isOriginSecure)
+                        .build()));
+
+        mCredentials = credentials;
         for (Credential credential : credentials) {
             PropertyModel propertyModel =
                     new PropertyModel.Builder(CredentialProperties.ALL_KEYS)
@@ -68,12 +83,20 @@
     private void onSelectedCredential(Credential credential) {
         mModel.set(VISIBLE, false);
         mDelegate.onCredentialSelected(credential);
+        if (mCredentials.size() > 1) {
+            // We only record this histogram in case multiple credentials were shown to the user.
+            // Otherwise the single credential case where position should always be 0 will dominate
+            // the recording.
+            RecordHistogram.recordCount100Histogram(
+                    UMA_TOUCH_TO_FILL_CREDENTIAL_INDEX, mCredentials.indexOf(credential));
+        }
     }
 
-    @Override
-    public void onDismissed() {
+    public void onDismissed(@StateChangeReason int reason) {
         if (!mModel.get(VISIBLE)) return; // Dismiss only if not dismissed yet.
         mModel.set(VISIBLE, false);
+        RecordHistogram.recordEnumeratedHistogram(
+                UMA_TOUCH_TO_FILL_DISMISSAL_REASON, reason, StateChangeReason.MAX_VALUE + 1);
         mDelegate.onDismissed();
     }
 }
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java
index f838ca1b..4017518b 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java
@@ -24,23 +24,17 @@
 class TouchToFillProperties {
     static final PropertyModel.WritableBooleanPropertyKey VISIBLE =
             new PropertyModel.WritableBooleanPropertyKey("visible");
-    static final PropertyModel.WritableObjectPropertyKey<String> FORMATTED_URL =
-            new PropertyModel.WritableObjectPropertyKey<>("formatted_url");
-    static final PropertyModel.WritableBooleanPropertyKey ORIGIN_SECURE =
-            new PropertyModel.WritableBooleanPropertyKey("origin_secure");
     static final PropertyModel
             .ReadableObjectPropertyKey<ListModel<MVCListAdapter.ListItem>> SHEET_ITEMS =
             new PropertyModel.ReadableObjectPropertyKey<>("sheet_items");
-    static final PropertyModel.ReadableObjectPropertyKey<ViewEventListener> VIEW_EVENT_LISTENER =
-            new PropertyModel.ReadableObjectPropertyKey<>("view_event_listener");
+    static final PropertyModel.ReadableObjectPropertyKey<Callback<Integer>> DISMISS_HANDLER =
+            new PropertyModel.ReadableObjectPropertyKey<>("dismiss_handler");
 
-    static PropertyModel createDefaultModel(ViewEventListener listener) {
-        return new PropertyModel
-                .Builder(VISIBLE, FORMATTED_URL, ORIGIN_SECURE, SHEET_ITEMS, VIEW_EVENT_LISTENER)
+    static PropertyModel createDefaultModel(Callback<Integer> handler) {
+        return new PropertyModel.Builder(VISIBLE, SHEET_ITEMS, DISMISS_HANDLER)
                 .with(VISIBLE, false)
-                .with(ORIGIN_SECURE, false)
                 .with(SHEET_ITEMS, new ListModel<>())
-                .with(VIEW_EVENT_LISTENER, listener)
+                .with(DISMISS_HANDLER, handler)
                 .build();
     }
 
@@ -65,13 +59,17 @@
     }
 
     /**
-     * This interface is used by the view to communicate events back to the mediator. It abstracts
-     * from the view by stripping information like parents, id or context.
+     * Properties defined here reflect the visible state of the header in the TouchToFill sheet.
      */
-    interface ViewEventListener {
+    static class HeaderProperties {
+        static final PropertyModel.ReadableObjectPropertyKey<String> FORMATTED_URL =
+                new PropertyModel.ReadableObjectPropertyKey<>("formatted_url");
+        static final PropertyModel.ReadableBooleanPropertyKey ORIGIN_SECURE =
+                new PropertyModel.ReadableBooleanPropertyKey("origin_secure");
 
-        /** Called if the user dismissed the view. */
-        void onDismissed();
+        static final PropertyKey[] ALL_KEYS = {FORMATTED_URL, ORIGIN_SECURE};
+
+        private HeaderProperties() {}
     }
 
     @IntDef({ItemType.HEADER, ItemType.CREDENTIAL})
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
index 156e62e..de6a26f3 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
@@ -11,8 +11,8 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
+import org.chromium.base.Callback;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
@@ -28,14 +28,14 @@
     private final BottomSheetController mBottomSheetController;
     private final RecyclerView mSheetItemListView;
     private final LinearLayout mContentView;
-    private TouchToFillProperties.ViewEventListener mEventListener;
+    private Callback<Integer> mDismissHandler;
 
     private final BottomSheetObserver mBottomSheetObserver = new EmptyBottomSheetObserver() {
         @Override
-        public void onSheetClosed(int reason) {
+        public void onSheetClosed(@BottomSheet.StateChangeReason int reason) {
             super.onSheetClosed(reason);
-            assert mEventListener != null;
-            mEventListener.onDismissed();
+            assert mDismissHandler != null;
+            mDismissHandler.onResult(reason);
             mBottomSheetController.getBottomSheet().removeObserver(mBottomSheetObserver);
         }
     };
@@ -58,10 +58,10 @@
 
     /**
      * Sets a new listener that reacts to events like item selection or dismissal.
-     * @param viewEventListener A {@link TouchToFillProperties.ViewEventListener}.
+     * @param dismissHandler A {@link Callback<Integer>}.
      */
-    void setEventListener(TouchToFillProperties.ViewEventListener viewEventListener) {
-        mEventListener = viewEventListener;
+    void setDismissHandler(Callback<Integer> dismissHandler) {
+        mDismissHandler = dismissHandler;
     }
 
     /**
@@ -77,26 +77,6 @@
         }
     }
 
-    /**
-     * Renders the given secure url into the subtitle.
-     * @param formattedUrl A {@link String} containing a URL already formatted to display.
-     */
-    void setSecureSubtitle(String formattedUrl) {
-        TextView sheetSubtitleText = mContentView.findViewById(R.id.touch_to_fill_sheet_subtitle);
-        sheetSubtitleText.setText(formattedUrl);
-    }
-
-    /**
-     * Renders the given non-secure url into the subtitle.
-     * @param formattedUrl A {@link String} containing a URL already formatted to display.
-     */
-    void setNonSecureSubtitle(String formattedUrl) {
-        TextView sheetSubtitleText = mContentView.findViewById(R.id.touch_to_fill_sheet_subtitle);
-        String subtitleText = String.format(
-                mContext.getString(R.string.touch_to_fill_sheet_subtitle_not_secure), formattedUrl);
-        sheetSubtitleText.setText(subtitleText);
-    }
-
     void setSheetItemListAdapter(RecyclerView.Adapter adapter) {
         mSheetItemListView.setAdapter(adapter);
     }
@@ -123,7 +103,7 @@
 
     @Override
     public int getVerticalScrollOffset() {
-        return 0;
+        return mSheetItemListView.computeVerticalScrollOffset();
     }
 
     @Override
@@ -143,12 +123,13 @@
 
     @Override
     public int getPeekHeight() {
-        return BottomSheet.HeightMode.DISABLED;
+        return Math.min(mContentView.getMinimumHeight(),
+                (int) mBottomSheetController.getBottomSheet().getSheetContainerHeight());
     }
 
     @Override
     public boolean wrapContentEnabled() {
-        return true;
+        return false;
     }
 
     @Override
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
index 8c1b4b6..abc1871d 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
@@ -8,10 +8,10 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.FAVICON;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.FORMATTED_ORIGIN;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.ON_CLICK_LISTENER;
-import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FORMATTED_URL;
-import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.ORIGIN_SECURE;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.DISMISS_HANDLER;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.SHEET_ITEMS;
-import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.VIEW_EVENT_LISTENER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.VISIBLE;
 import static org.chromium.chrome.browser.util.UrlUtilities.stripScheme;
 
@@ -42,16 +42,10 @@
      */
     static void bindTouchToFillView(
             PropertyModel model, TouchToFillView view, PropertyKey propertyKey) {
-        if (propertyKey == VIEW_EVENT_LISTENER) {
-            view.setEventListener(model.get(VIEW_EVENT_LISTENER));
+        if (propertyKey == DISMISS_HANDLER) {
+            view.setDismissHandler(model.get(DISMISS_HANDLER));
         } else if (propertyKey == VISIBLE) {
             view.setVisible(model.get(VISIBLE));
-        } else if (propertyKey == FORMATTED_URL || propertyKey == ORIGIN_SECURE) {
-            if (model.get(ORIGIN_SECURE)) {
-                view.setSecureSubtitle(model.get(FORMATTED_URL));
-            } else {
-                view.setNonSecureSubtitle(model.get(FORMATTED_URL));
-            }
         } else if (propertyKey == SHEET_ITEMS) {
             view.setSheetItemListAdapter(
                     new RecyclerViewAdapter<>(new SimpleRecyclerViewMcp<>(model.get(SHEET_ITEMS),
@@ -72,7 +66,8 @@
             ViewGroup parent, @ItemType int itemType) {
         switch (itemType) {
             case ItemType.HEADER:
-                return null;
+                return new TouchToFillViewHolder(parent, R.layout.touch_to_fill_header_item,
+                        TouchToFillViewBinder::bindHeaderView);
             case ItemType.CREDENTIAL:
                 return new TouchToFillViewHolder(parent, R.layout.touch_to_fill_credential_item,
                         TouchToFillViewBinder::bindCredentialView);
@@ -133,5 +128,27 @@
         }
     }
 
+    /**
+     * Called whenever a property in the given model changes. It updates the given view accordingly.
+     * @param model The observed {@link PropertyModel}. Its data need to be reflected in the view.
+     * @param viewGroup The {@link ViewGroup} containing the header to update.
+     * @param key The {@link PropertyKey} which changed.
+     */
+    private static void bindHeaderView(PropertyModel model, ViewGroup viewGroup, PropertyKey key) {
+        if (key == FORMATTED_URL || key == ORIGIN_SECURE) {
+            TextView sheetSubtitleText = viewGroup.findViewById(R.id.touch_to_fill_sheet_subtitle);
+            if (model.get(ORIGIN_SECURE)) {
+                sheetSubtitleText.setText(model.get(FORMATTED_URL));
+            } else {
+                sheetSubtitleText.setText(
+                        String.format(viewGroup.getContext().getString(
+                                              R.string.touch_to_fill_sheet_subtitle_not_secure),
+                                model.get(FORMATTED_URL)));
+            }
+        } else {
+            assert false : "Unhandled update to property:" + key;
+        }
+    }
+
     private TouchToFillViewBinder() {}
 }
diff --git a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java
index 43dea74c..ee62fa3 100644
--- a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java
+++ b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java
@@ -82,7 +82,7 @@
         runOnUiThreadBlocking(() -> {
             mTouchToFill.showCredentials(EXAMPLE_URL, true, Collections.singletonList(ANA));
         });
-        pollUiThread(() -> getBottomSheetState() == SheetState.FULL);
+        pollUiThread(() -> getBottomSheetState() == SheetState.PEEK);
 
         pollUiThread(() -> getCredentials().getChildAt(0) != null);
         TouchCommon.singleClickView(getCredentials().getChildAt(0));
@@ -98,7 +98,7 @@
         runOnUiThreadBlocking(() -> {
             mTouchToFill.showCredentials(EXAMPLE_URL, true, Arrays.asList(ANA, BOB));
         });
-        pollUiThread(() -> getBottomSheetState() == SheetState.FULL);
+        pollUiThread(() -> getBottomSheetState() == SheetState.PEEK);
 
         Espresso.pressBack();
 
diff --git a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
index 5c020f5..6423f4b 100644
--- a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
+++ b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
@@ -15,8 +15,8 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.CREDENTIAL;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.FORMATTED_ORIGIN;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.ON_CLICK_LISTENER;
-import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FORMATTED_URL;
-import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.ORIGIN_SECURE;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.VISIBLE;
 import static org.chromium.content_public.browser.test.util.CriteriaHelper.pollUiThread;
@@ -41,8 +41,10 @@
 import org.chromium.base.test.util.ScalableTimeout;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties;
 import org.chromium.chrome.browser.touch_to_fill.data.Credential;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.SheetState;
+import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.StateChangeReason;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
@@ -66,7 +68,7 @@
             new Credential("Bob", "***", "Bob", "mobile.example.xyz", true);
 
     @Mock
-    private TouchToFillProperties.ViewEventListener mMockListener;
+    private Callback<Integer> mDismissHandler;
     @Mock
     private Callback<Credential> mCredentialCallback;
 
@@ -80,7 +82,7 @@
     public void setUp() throws InterruptedException {
         MockitoAnnotations.initMocks(this);
         mActivityTestRule.startMainActivityOnBlankPage();
-        mModel = TouchToFillProperties.createDefaultModel(mMockListener);
+        mModel = TouchToFillProperties.createDefaultModel(mDismissHandler);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mTouchToFillView =
                     new TouchToFillView(getActivity(), getActivity().getBottomSheetController());
@@ -93,7 +95,7 @@
     public void testVisibilityChangedByModel() {
         // After setting the visibility to true, the view should exist and be visible.
         TestThreadUtils.runOnUiThreadBlocking(() -> mModel.set(VISIBLE, true));
-        pollUiThread(() -> getBottomSheetState() == SheetState.FULL);
+        pollUiThread(() -> getBottomSheetState() == SheetState.PEEK);
         assertThat(mTouchToFillView.getContentView().isShown(), is(true));
 
         // After hiding the view, the view should still exist but be invisible.
@@ -104,22 +106,38 @@
 
     @Test
     @MediumTest
-    public void testSubtitleUrlChangedByModel() {
+    public void testSecureSubtitleUrlDisplayed() {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.set(FORMATTED_URL, "www.example.org");
-            mModel.set(ORIGIN_SECURE, true);
+            mModel.get(SHEET_ITEMS)
+                    .add(new MVCListAdapter.ListItem(TouchToFillProperties.ItemType.HEADER,
+                            new PropertyModel.Builder(HeaderProperties.ALL_KEYS)
+                                    .with(FORMATTED_URL, "www.example.org")
+                                    .with(ORIGIN_SECURE, true)
+                                    .build()));
             mModel.set(VISIBLE, true);
         });
-        pollUiThread(() -> getBottomSheetState() == SheetState.FULL);
+        pollUiThread(() -> getBottomSheetState() == SheetState.PEEK);
         TextView subtitle =
                 mTouchToFillView.getContentView().findViewById(R.id.touch_to_fill_sheet_subtitle);
 
         assertThat(subtitle.getText(), is("www.example.org"));
+    }
 
+    @Test
+    @MediumTest
+    public void testNonSecureSubtitleUrlDisplayed() {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.set(FORMATTED_URL, "m.example.org");
-            mModel.set(ORIGIN_SECURE, false);
+            mModel.get(SHEET_ITEMS)
+                    .add(new MVCListAdapter.ListItem(TouchToFillProperties.ItemType.HEADER,
+                            new PropertyModel.Builder(HeaderProperties.ALL_KEYS)
+                                    .with(FORMATTED_URL, "m.example.org")
+                                    .with(ORIGIN_SECURE, false)
+                                    .build()));
+            mModel.set(VISIBLE, true);
         });
+        pollUiThread(() -> getBottomSheetState() == SheetState.PEEK);
+        TextView subtitle =
+                mTouchToFillView.getContentView().findViewById(R.id.touch_to_fill_sheet_subtitle);
 
         assertThat(subtitle.getText(), is(getFormattedNotSecureSubtitle("m.example.org")));
     }
@@ -134,7 +152,7 @@
                             buildCredentialItem(BOB)));
         });
 
-        pollUiThread(() -> getBottomSheetState() == SheetState.FULL);
+        pollUiThread(() -> getBottomSheetState() == SheetState.PEEK);
         assertThat(getCredentials().getChildCount(), is(3));
         assertThat(getCredentialOriginAt(0).getVisibility(), is(View.GONE));
         assertThat(getCredentialNameAt(0).getText(), is(ANA.getFormattedUsername()));
@@ -162,7 +180,7 @@
             mModel.get(SHEET_ITEMS).addAll(Collections.singletonList(buildCredentialItem(ANA)));
             mModel.set(VISIBLE, true);
         });
-        pollUiThread(() -> getBottomSheetState() == SheetState.FULL);
+        pollUiThread(() -> getBottomSheetState() == SheetState.PEEK);
 
         assertNotNull(getCredentials().getChildAt(0));
 
@@ -175,10 +193,10 @@
     @MediumTest
     public void testDismissesWhenHidden() {
         TestThreadUtils.runOnUiThreadBlocking(() -> mModel.set(VISIBLE, true));
-        pollUiThread(() -> getBottomSheetState() == SheetState.FULL);
+        pollUiThread(() -> getBottomSheetState() == SheetState.PEEK);
         TestThreadUtils.runOnUiThreadBlocking(() -> mModel.set(VISIBLE, false));
         pollUiThread(() -> getBottomSheetState() == SheetState.HIDDEN);
-        verify(mMockListener).onDismissed();
+        verify(mDismissHandler).onResult(StateChangeReason.NONE);
     }
 
     private ChromeActivity getActivity() {
diff --git a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
index 53db2ba3..93d34963 100644
--- a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
+++ b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
@@ -18,10 +18,10 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.FAVICON;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.FORMATTED_ORIGIN;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.ON_CLICK_LISTENER;
-import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FORMATTED_URL;
-import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.ORIGIN_SECURE;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.DISMISS_HANDLER;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.SHEET_ITEMS;
-import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.VIEW_EVENT_LISTENER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.VISIBLE;
 
 import android.graphics.Bitmap;
@@ -36,12 +36,17 @@
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
 
 import org.chromium.base.Callback;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.RecordHistogramJni;
+import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.ItemType;
 import org.chromium.chrome.browser.touch_to_fill.data.Credential;
+import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.components.url_formatter.UrlFormatterJni;
 import org.chromium.ui.modelutil.ListModel;
@@ -56,6 +61,7 @@
  * properly.
  */
 @RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
 public class TouchToFillControllerTest {
     private static final String TEST_URL = "www.example.xyz";
     private static final String TEST_SUBDOMAIN_URL = "subdomain.example.xyz";
@@ -75,16 +81,21 @@
     @Mock
     private TouchToFillComponent.Delegate mMockDelegate;
 
+    @Mock
+    private RecordHistogram.Natives mMockRecordHistogram;
+
     // Can't be local, as it has to be initialized by initMocks.
     @Captor
     private ArgumentCaptor<Callback<Bitmap>> mCallbackArgumentCaptor;
 
     private final TouchToFillMediator mMediator = new TouchToFillMediator();
-    private final PropertyModel mModel = TouchToFillProperties.createDefaultModel(mMediator);
+    private final PropertyModel mModel =
+            TouchToFillProperties.createDefaultModel(mMediator::onDismissed);
 
     public TouchToFillControllerTest() {
         MockitoAnnotations.initMocks(this);
         mJniMocker.mock(UrlFormatterJni.TEST_HOOKS, mUrlFormatterJniMock);
+        mJniMocker.mock(RecordHistogramJni.TEST_HOOKS, mMockRecordHistogram);
         when(mUrlFormatterJniMock.formatUrlForDisplayOmitScheme(anyString()))
                 .then(inv -> format(inv.getArgument(0)));
     }
@@ -97,34 +108,33 @@
     @Test
     public void testCreatesValidDefaultModel() {
         assertNotNull(mModel.get(SHEET_ITEMS));
-        assertNotNull(mModel.get(VIEW_EVENT_LISTENER));
+        assertNotNull(mModel.get(DISMISS_HANDLER));
         assertThat(mModel.get(VISIBLE), is(false));
-        assertThat(mModel.get(FORMATTED_URL), is(nullValue()));
-        assertThat(mModel.get(ORIGIN_SECURE), is(false));
     }
 
     @Test
-    public void testShowCredentialsSetsFormattedUrl() {
+    public void testShowCredentialsCreatesHeader() {
         mMediator.showCredentials(TEST_URL, true, Arrays.asList(ANA, CARL, BOB));
-        assertThat(mModel.get(FORMATTED_URL), is(TEST_URL));
-        assertThat(mModel.get(ORIGIN_SECURE), is(true));
+        ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
+        assertThat(itemList.get(0).type, is(ItemType.HEADER));
+        assertThat(itemList.get(0).model.get(FORMATTED_URL), is(TEST_URL));
+        assertThat(itemList.get(0).model.get(ORIGIN_SECURE), is(true));
     }
 
     @Test
     public void testShowCredentialsSetsCredentialListAndRequestsFavicons() {
         mMediator.showCredentials(TEST_URL, true, Arrays.asList(ANA, CARL, BOB));
-        ListModel<MVCListAdapter.ListItem> credentialList = mModel.get(SHEET_ITEMS);
-        // TODO(https://crbug.com/1013209): Simplify this after adding equals to ModelList.
-        assertThat(credentialList.size(), is(3));
-        assertThat(credentialList.get(0).type, is(ItemType.CREDENTIAL));
-        assertThat(credentialList.get(0).model.get(CREDENTIAL), is(ANA));
-        assertThat(credentialList.get(0).model.get(FAVICON), is(nullValue()));
-        assertThat(credentialList.get(1).type, is(TouchToFillProperties.ItemType.CREDENTIAL));
-        assertThat(credentialList.get(1).model.get(CREDENTIAL), is(CARL));
-        assertThat(credentialList.get(1).model.get(FAVICON), is(nullValue()));
-        assertThat(credentialList.get(2).type, is(TouchToFillProperties.ItemType.CREDENTIAL));
-        assertThat(credentialList.get(2).model.get(CREDENTIAL), is(BOB));
-        assertThat(credentialList.get(2).model.get(FAVICON), is(nullValue()));
+        ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
+        assertThat(itemList.size(), is(4));
+        assertThat(itemList.get(1).type, is(ItemType.CREDENTIAL));
+        assertThat(itemList.get(1).model.get(CREDENTIAL), is(ANA));
+        assertThat(itemList.get(1).model.get(FAVICON), is(nullValue()));
+        assertThat(itemList.get(2).type, is(ItemType.CREDENTIAL));
+        assertThat(itemList.get(2).model.get(CREDENTIAL), is(CARL));
+        assertThat(itemList.get(2).model.get(FAVICON), is(nullValue()));
+        assertThat(itemList.get(3).type, is(ItemType.CREDENTIAL));
+        assertThat(itemList.get(3).model.get(CREDENTIAL), is(BOB));
+        assertThat(itemList.get(3).model.get(FAVICON), is(nullValue()));
 
         verify(mMockDelegate).fetchFavicon(eq("https://m.a.xyz/"), eq(DESIRED_FAVICON_SIZE), any());
         verify(mMockDelegate).fetchFavicon(eq(TEST_URL), eq(DESIRED_FAVICON_SIZE), any());
@@ -135,12 +145,11 @@
     @Test
     public void testFetchFaviconUpdatesModel() {
         mMediator.showCredentials(TEST_URL, true, Collections.singletonList(CARL));
-        ListModel<MVCListAdapter.ListItem> credentialList = mModel.get(SHEET_ITEMS);
-        assertThat(credentialList.size(), is(1));
-        // TODO(https://crbug.com/1013209): Simplify this after adding equals to ModelList.
-        assertThat(credentialList.get(0).type, is(TouchToFillProperties.ItemType.CREDENTIAL));
-        assertThat(credentialList.get(0).model.get(CREDENTIAL), is(CARL));
-        assertThat(credentialList.get(0).model.get(FAVICON), is(nullValue()));
+        ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
+        assertThat(itemList.size(), is(2));
+        assertThat(itemList.get(1).type, is(ItemType.CREDENTIAL));
+        assertThat(itemList.get(1).model.get(CREDENTIAL), is(CARL));
+        assertThat(itemList.get(1).model.get(FAVICON), is(nullValue()));
 
         // ANA and CARL both have TEST_URL as their origin URL
         verify(mMockDelegate)
@@ -150,39 +159,37 @@
         Bitmap bitmap = Bitmap.createBitmap(
                 DESIRED_FAVICON_SIZE, DESIRED_FAVICON_SIZE, Bitmap.Config.ARGB_8888);
         callback.onResult(bitmap);
-        assertThat(credentialList.get(0).model.get(FAVICON), is(bitmap));
+        assertThat(itemList.get(1).model.get(FAVICON), is(bitmap));
     }
 
     @Test
     public void testShowCredentialsFormatPslOrigins() {
         mMediator.showCredentials(TEST_URL, true, Arrays.asList(ANA, BOB));
-        assertThat(mModel.get(SHEET_ITEMS).size(), is(2));
-        assertThat(mModel.get(SHEET_ITEMS).get(0).type, is(ItemType.CREDENTIAL));
-        assertThat(mModel.get(SHEET_ITEMS).get(0).model.get(FORMATTED_ORIGIN),
-                is(format(ANA.getOriginUrl())));
+        assertThat(mModel.get(SHEET_ITEMS).size(), is(3));
         assertThat(mModel.get(SHEET_ITEMS).get(1).type, is(ItemType.CREDENTIAL));
         assertThat(mModel.get(SHEET_ITEMS).get(1).model.get(FORMATTED_ORIGIN),
+                is(format(ANA.getOriginUrl())));
+        assertThat(mModel.get(SHEET_ITEMS).get(2).type, is(ItemType.CREDENTIAL));
+        assertThat(mModel.get(SHEET_ITEMS).get(2).model.get(FORMATTED_ORIGIN),
                 is(format(BOB.getOriginUrl())));
     }
 
     @Test
     public void testClearsCredentialListWhenShowingAgain() {
         mMediator.showCredentials(TEST_URL, true, Collections.singletonList(ANA));
-        ListModel<MVCListAdapter.ListItem> credentialList = mModel.get(SHEET_ITEMS);
-        // TODO(https://crbug.com/1013209): Simplify this after adding equals to ModelList.
-        assertThat(credentialList.size(), is(1));
-        assertThat(credentialList.get(0).type, is(ItemType.CREDENTIAL));
-        assertThat(credentialList.get(0).model.get(CREDENTIAL), is(ANA));
-        assertThat(credentialList.get(0).model.get(FAVICON), is(nullValue()));
+        ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
+        assertThat(itemList.size(), is(2));
+        assertThat(itemList.get(1).type, is(ItemType.CREDENTIAL));
+        assertThat(itemList.get(1).model.get(CREDENTIAL), is(ANA));
+        assertThat(itemList.get(1).model.get(FAVICON), is(nullValue()));
 
         // Showing the sheet a second time should replace all changed credentials.
         mMediator.showCredentials(TEST_URL, true, Collections.singletonList(BOB));
-        credentialList = mModel.get(SHEET_ITEMS);
-        // TODO(https://crbug.com/1013209): Simplify this after adding equals to ModelList.
-        assertThat(credentialList.size(), is(1));
-        assertThat(credentialList.get(0).type, is(ItemType.CREDENTIAL));
-        assertThat(credentialList.get(0).model.get(CREDENTIAL), is(BOB));
-        assertThat(credentialList.get(0).model.get(FAVICON), is(nullValue()));
+        itemList = mModel.get(SHEET_ITEMS);
+        assertThat(itemList.size(), is(2));
+        assertThat(itemList.get(1).type, is(ItemType.CREDENTIAL));
+        assertThat(itemList.get(1).model.get(CREDENTIAL), is(BOB));
+        assertThat(itemList.get(1).model.get(FAVICON), is(nullValue()));
     }
 
     @Test
@@ -192,6 +199,20 @@
     }
 
     @Test
+    public void testCallsCallbackAndHidesOnSelectingItemDoesNotRecordIndexForSingleCredential() {
+        mMediator.showCredentials(TEST_URL, true, Arrays.asList(ANA));
+        assertThat(mModel.get(VISIBLE), is(true));
+        assertNotNull(mModel.get(SHEET_ITEMS).get(1).model.get(ON_CLICK_LISTENER));
+
+        mModel.get(SHEET_ITEMS).get(1).model.get(ON_CLICK_LISTENER).onResult(ANA);
+        verify(mMockDelegate).onCredentialSelected(ANA);
+        assertThat(mModel.get(VISIBLE), is(false));
+        assertThat(RecordHistogram.getHistogramTotalCountForTesting(
+                           TouchToFillMediator.UMA_TOUCH_TO_FILL_CREDENTIAL_INDEX),
+                is(0));
+    }
+
+    @Test
     public void testCallsCallbackAndHidesOnSelectingItem() {
         mMediator.showCredentials(TEST_URL, true, Arrays.asList(ANA, CARL));
         assertThat(mModel.get(VISIBLE), is(true));
@@ -200,14 +221,21 @@
         mModel.get(SHEET_ITEMS).get(1).model.get(ON_CLICK_LISTENER).onResult(CARL);
         verify(mMockDelegate).onCredentialSelected(CARL);
         assertThat(mModel.get(VISIBLE), is(false));
+        assertThat(RecordHistogram.getHistogramValueCountForTesting(
+                           TouchToFillMediator.UMA_TOUCH_TO_FILL_CREDENTIAL_INDEX, 1),
+                is(1));
     }
 
     @Test
     public void testCallsDelegateAndHidesOnDismiss() {
         mMediator.showCredentials(TEST_URL, true, Arrays.asList(ANA, CARL));
-        mMediator.onDismissed();
+        mMediator.onDismissed(BottomSheet.StateChangeReason.BACK_PRESS);
         verify(mMockDelegate).onDismissed();
         assertThat(mModel.get(VISIBLE), is(false));
+        assertThat(RecordHistogram.getHistogramValueCountForTesting(
+                           TouchToFillMediator.UMA_TOUCH_TO_FILL_DISMISSAL_REASON,
+                           BottomSheet.StateChangeReason.BACK_PRESS),
+                is(1));
     }
 
     /**
diff --git a/chrome/browser/tracing/background_tracing_field_trial.cc b/chrome/browser/tracing/background_tracing_field_trial.cc
index 3b192684..5293258f 100644
--- a/chrome/browser/tracing/background_tracing_field_trial.cc
+++ b/chrome/browser/tracing/background_tracing_field_trial.cc
@@ -70,7 +70,7 @@
       TraceEventMetadataSource::GetInstance()->GenerateLegacyMetadataDict();
 
   uploader->DoUpload(
-      file_contents->data(), content::TraceUploader::UNCOMPRESSED_UPLOAD,
+      *file_contents, content::TraceUploader::UNCOMPRESSED_UPLOAD,
       std::move(metadata), content::TraceUploader::UploadProgressCallback(),
       base::BindOnce(&OnBackgroundTracingUploadComplete, base::Owned(uploader),
                      std::move(callback)));
diff --git a/chrome/browser/tracing/navigation_tracing.cc b/chrome/browser/tracing/navigation_tracing.cc
index 8c8fdf05..437ec8ea 100644
--- a/chrome/browser/tracing/navigation_tracing.cc
+++ b/chrome/browser/tracing/navigation_tracing.cc
@@ -47,7 +47,7 @@
       TraceEventMetadataSource::GetInstance()->GenerateLegacyMetadataDict();
 
   uploader->DoUpload(
-      file_contents->data(), content::TraceUploader::UNCOMPRESSED_UPLOAD,
+      *file_contents, content::TraceUploader::UNCOMPRESSED_UPLOAD,
       std::move(metadata), content::TraceUploader::UploadProgressCallback(),
       base::BindOnce(&OnNavigationTracingUploadComplete, base::Owned(uploader),
                      std::move(callback)));
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index e0ddff6..99b66171 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -9,7 +9,6 @@
 import("//build/config/jumbo.gni")
 import("//build/config/linux/gtk/gtk.gni")
 import("//build/config/ui.gni")
-import("//build/split_static_library.gni")
 import("//chrome/common/features.gni")
 import("//chromeos/assistant/assistant.gni")
 import("//components/feature_engagement/features.gni")
@@ -28,17 +27,7 @@
 
 # Use a static library here because many test binaries depend on this but don't
 # require many files from it. This makes linking more efficient.
-jumbo_split_static_library("ui") {
-  # Split into multiple static libraries on Windows builds. We have hit size
-  # limits on Windows official builds and on goma builds when symbol_level = 2
-  # is selected. Always splitting on Windows builds is simpler than trying to
-  # perfectly calculate the scenarios where it is required.
-  if (is_win) {
-    split_count = 5
-  } else {
-    split_count = 1
-  }
-
+jumbo_static_library("ui") {
   sources = [
     "accelerator_utils.h",
     "app_list/app_list_util.cc",
@@ -514,6 +503,7 @@
     "//components/subresource_filter/content/browser",
     "//components/subresource_filter/core/browser",
     "//components/sync",
+    "//components/sync/driver:resources",
     "//components/sync_preferences",
     "//components/sync_sessions",
     "//components/tracing:startup_tracing",
diff --git a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
index 9dd0588..db29c54 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
@@ -72,6 +72,9 @@
   TabAndroid* tab = TabAndroid::GetNativeTab(env, jtab);
   if (tab)
     tab->SetWindowSessionID(GetSessionId());
+
+  if (IsOffTheRecord())
+    UMA_HISTOGRAM_COUNTS_100("Tab.Count.Incognito", GetTabCount());
 }
 
 int TabModelJniBridge::GetTabCount() const {
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RoundedCornerImageView.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RoundedCornerImageView.java
index 46241221..dad80cb 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RoundedCornerImageView.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RoundedCornerImageView.java
@@ -19,11 +19,11 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.shapes.RoundRectShape;
 import android.graphics.drawable.shapes.Shape;
+import android.support.annotation.ColorInt;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.widget.ImageView;
 
-import androidx.annotation.ColorRes;
 import androidx.annotation.Nullable;
 
 /**
@@ -41,20 +41,20 @@
  * corners are used.
  */
 public class RoundedCornerImageView extends ImageView {
-    private Shape mRoundedRectangle;
-    private BitmapShader mShader;
-    private Paint mPaint;
+    private final RectF mTmpRect = new RectF();
+    private final Matrix mTmpMatrix = new Matrix();
 
-    private Paint mFillPaint;
+    private final Paint mRoundedBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private Paint mRoundedContentPaint;
     private final Matrix mScaleMatrix = new Matrix();
     private boolean mRoundCorners;
+    // True, if constructor had a chance to run.
+    // This is needed, because ImageView's constructor may trigger updates on our end
+    // if certain attributes (eg. Drawable) are supplied via layout attributes.
+    private final boolean mIsInitialized;
 
-    // Object to avoid allocations during draw calls.
-    private final RectF mTmpRect = new RectF();
-
-    // Whether or not to apply the shader, if we have one. This might be set to false if the image
-    // is smaller than the view and does not need to have the corners rounded.
-    private boolean mApplyShader;
+    private Shape mRoundedRectangle;
+    private @ColorInt int mFillColor = Color.TRANSPARENT;
 
     public RoundedCornerImageView(Context context) {
         this(context, null, 0);
@@ -66,32 +66,69 @@
 
     public RoundedCornerImageView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+
+        // Set attribute indicating that all required objects are created.
+        mIsInitialized = true;
+
+        int radiusTopStart = 0;
+        int radiusTopEnd = 0;
+        int radiusBottomStart = 0;
+        int radiusBottomEnd = 0;
+        int color = Color.TRANSPARENT;
+
         if (attrs != null) {
             TypedArray a = getContext().obtainStyledAttributes(
                     attrs, R.styleable.RoundedCornerImageView, 0, 0);
-            int cornerRadiusTopStart = a.getDimensionPixelSize(
+            radiusTopStart = a.getDimensionPixelSize(
                     R.styleable.RoundedCornerImageView_cornerRadiusTopStart, 0);
-            int cornerRadiusTopEnd = a.getDimensionPixelSize(
+            radiusTopEnd = a.getDimensionPixelSize(
                     R.styleable.RoundedCornerImageView_cornerRadiusTopEnd, 0);
-            int cornerRadiusBottomStart = a.getDimensionPixelSize(
+            radiusBottomStart = a.getDimensionPixelSize(
                     R.styleable.RoundedCornerImageView_cornerRadiusBottomStart, 0);
-            int cornerRadiusBottomEnd = a.getDimensionPixelSize(
+            radiusBottomEnd = a.getDimensionPixelSize(
                     R.styleable.RoundedCornerImageView_cornerRadiusBottomEnd, 0);
-            if (a.hasValue(R.styleable.RoundedCornerImageView_roundedfillColor)) {
-                mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-                mFillPaint.setColor(a.getColor(
-                        R.styleable.RoundedCornerImageView_roundedfillColor, Color.WHITE));
-            }
-            a.recycle();
 
-            setRoundedCorners(cornerRadiusTopStart, cornerRadiusTopEnd, cornerRadiusBottomStart,
-                    cornerRadiusBottomEnd);
+            color = a.getColor(
+                    R.styleable.RoundedCornerImageView_roundedfillColor, Color.TRANSPARENT);
+            a.recycle();
         }
+
+        setRoundedCorners(radiusTopStart, radiusTopEnd, radiusBottomStart, radiusBottomEnd);
+        setRoundedFillColor(color);
+        refreshState();
     }
 
     /**
-     * Updates the rounded corners, using the radius set in the layout.
+     * Sets the rounded corner fill color to {@code color}.  This can be used to make sure the
+     * rounded shape shows even if the actual content isn't full-bleed (e.g. icon with transparency
+     * or too small to reach the edges).
+     * @param color The color to use.  Setting to {@link Color#TRANSPARENT} will remove the color.
      */
+    public void setRoundedFillColor(@ColorInt int color) {
+        mFillColor = color;
+        mRoundedBackgroundPaint.setColor(color);
+        invalidate();
+    }
+
+    // ImageView implementation.
+    @Override
+    public void setImageDrawable(@Nullable Drawable drawable) {
+        super.setImageDrawable(drawable);
+        refreshState();
+    }
+
+    @Override
+    public void setImageResource(int resId) {
+        super.setImageResource(resId);
+        refreshState();
+    }
+
+    @Override
+    public void setImageBitmap(Bitmap bm) {
+        super.setImageBitmap(bm);
+        refreshState();
+    }
+
     public void setRoundedCorners(int cornerRadiusTopStart, int cornerRadiusTopEnd,
             int cornerRadiusBottomStart, int cornerRadiusBottomEnd) {
         mRoundCorners = (cornerRadiusTopStart != 0 || cornerRadiusTopEnd != 0
@@ -110,154 +147,84 @@
         }
 
         mRoundedRectangle = new RoundRectShape(radii, null, null);
-        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     }
 
-    @Override
-    public void setImageDrawable(@Nullable Drawable drawable) {
-        super.setImageDrawable(drawable);
-        reset();
-    }
-
-    @Override
-    public void setImageResource(int res) {
-        super.setImageResource(res);
-        reset();
-    }
-
-    @Override
-    public void setImageBitmap(Bitmap bm) {
-        super.setImageBitmap(bm);
-        reset();
-    }
-
-    private void reset() {
-        // Reset shaders.  We will need to recalculate them.
-        mShader = null;
-        mApplyShader = false;
-
-        // Reset shader in Paint to avoid retaining the old Bitmap.
-        if (mPaint != null) mPaint.setShader(null);
-
-        maybeCreateShader();
-        updateApplyShader();
-    }
-
-    /**
-     * Set the fill color resource.
-     * @param id The color resource id.
-     */
-    public void setRoundedFillColor(@ColorRes int id) {
-        mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mFillPaint.setColor(getContext().getResources().getColor(id));
-    }
-
-    protected void maybeCreateShader() {
-        // Only create the shader if we have a rectangle to use as a mask.
+    private void refreshState() {
         Drawable drawable = getDrawable();
-        Bitmap bitmap = (drawable instanceof BitmapDrawable)
-                ? ((BitmapDrawable) drawable).getBitmap()
-                : null;
-        if (mRoundedRectangle != null && bitmap != null) {
-            mShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
-        }
-    }
 
-    @Override
-    protected boolean setFrame(int l, int t, int r, int b) {
-        boolean changed = super.setFrame(l, t, r, b);
-        updateApplyShader();
-        return changed;
-    }
-
-    @Override
-    public void setScaleType(ScaleType scaleType) {
-        super.setScaleType(scaleType);
-        updateApplyShader();
-    }
-
-    /**
-     * Updates the flag to tell whether or not to apply the shader that produces the rounded
-     * corners. We should not apply the shader if the final image is smaller than the view, because
-     * it will try to tile the image, which is not desirable. This should be called when the image
-     * is changed, or the view bounds change.
-     */
-    private void updateApplyShader() {
-        Drawable drawable = getDrawable();
-        if (!(drawable instanceof BitmapDrawable) || (mShader == null) || (mPaint == null)) {
-            // In this state we wouldn't use the shader anyway.
-            mApplyShader = false;
+        // Do not update state if we were invoked from the ImageView's constructor
+        // (before we had the chance to initialize our own private data).
+        if (!mIsInitialized) {
             return;
         }
 
-        // Default to using the shader.
-        mApplyShader = true;
+        if (drawable instanceof ColorDrawable) {
+            mRoundedBackgroundPaint.setColor(((ColorDrawable) getDrawable()).getColor());
+            mRoundedContentPaint = null;
+        } else if (drawable instanceof BitmapDrawable) {
+            mRoundedBackgroundPaint.setColor(mFillColor);
+            mRoundedContentPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+
+            mRoundedContentPaint.setShader(
+                    new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+        } else {
+            mRoundedBackgroundPaint.setColor(mFillColor);
+            mRoundedContentPaint = null;
+        }
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
-        final int width = getWidth() - getPaddingLeft() - getPaddingRight();
-        final int height = getHeight() - getPaddingTop() - getPaddingBottom();
-
-        Drawable drawable = getDrawable();
-        Shape localRoundedRect = mRoundedRectangle;
-        Paint localPaint = mPaint;
-
-        boolean drawFill = mFillPaint != null && localRoundedRect != null
-                && !(drawable instanceof ColorDrawable);
-        boolean drawContent = localPaint != null && localRoundedRect != null
-                && isSupportedDrawable(drawable) && mRoundCorners;
-
-        if (drawFill || drawContent) localRoundedRect.resize(width, height);
-
-        final int saveCount = canvas.save();
-        canvas.translate(getPaddingLeft(), getPaddingTop());
-
-        // First, fill the drawing area with the given fill paint.
-        if (drawFill) localRoundedRect.draw(canvas, mFillPaint);
-
-        if (!drawContent) {
-            // We probably have an unsupported drawable or we don't want rounded corners. Draw
-            // normally and return.
-            // Undo our modifications to canvas first. ImageView will re-apply these.
-            canvas.restoreToCount(saveCount);
+        if (!mRoundCorners) {
             super.onDraw(canvas);
             return;
         }
 
-        // We have a drawable to draw with rounded corners. Let's first set up the paint.
-        if (drawable instanceof ColorDrawable) {
-            ColorDrawable colorDrawable = (ColorDrawable) drawable;
-            localPaint.setColor(colorDrawable.getColor());
+        final int width = getWidth() - getPaddingLeft() - getPaddingRight();
+        final int height = getHeight() - getPaddingTop() - getPaddingBottom();
+        if (width <= 0 || height <= 0) return;
+
+        mRoundedRectangle.resize(width, height);
+
+        final int saveCount = canvas.save();
+        try {
+            canvas.translate(getPaddingLeft(), getPaddingTop());
+
+            if (mRoundedBackgroundPaint.getColor() != Color.TRANSPARENT) {
+                mRoundedRectangle.draw(canvas, mRoundedBackgroundPaint);
+                // Note: RoundedBackgroundPaint is also used as ColorDrawable.
+                if (getDrawable() instanceof ColorDrawable) {
+                    return;
+                }
+            }
+
+            if (mRoundedContentPaint == null) {
+                canvas.restoreToCount(saveCount);
+                super.onDraw(canvas);
+                return;
+            }
+
+            Shader shader = mRoundedContentPaint.getShader();
+            if (shader != null) {
+                Drawable drawable = getDrawable();
+
+                Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+                mTmpMatrix.set(getImageMatrix());
+                mTmpMatrix.preScale((float) drawable.getIntrinsicWidth() / bitmap.getWidth(),
+                        (float) drawable.getIntrinsicHeight() / bitmap.getHeight());
+
+                shader.setLocalMatrix(mTmpMatrix);
+
+                mTmpRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
+                mTmpMatrix.mapRect(mTmpRect);
+                canvas.clipRect(mTmpRect);
+            }
+
+            mRoundedRectangle.draw(canvas, mRoundedContentPaint);
+        } finally {
+            canvas.restoreToCount(saveCount);
         }
-
-        if (mApplyShader) {
-            assert mShader != null;
-
-            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
-            mScaleMatrix.set(getImageMatrix());
-            mScaleMatrix.preScale(1.f * drawable.getIntrinsicWidth() / bitmap.getWidth(),
-                    1.f * drawable.getIntrinsicHeight() / bitmap.getHeight());
-            mShader.setLocalMatrix(mScaleMatrix);
-            localPaint.setShader(mShader);
-
-            // Find the desired bounding box where the bitmap is to be shown.
-            mTmpRect.set(getDrawable().getBounds());
-            getImageMatrix().mapRect(mTmpRect);
-        }
-
-        // Clip the canvas to the desired bounding box so that the shader isn't applied anywhere
-        // outside the desired area.
-        if (mApplyShader) canvas.clipRect(mTmpRect);
-
-        // Draw the rounded rectangle.
-        localRoundedRect.draw(canvas, localPaint);
-
-        // Remove the clip.
-        canvas.restoreToCount(saveCount);
-    }
-
-    private boolean isSupportedDrawable(Drawable drawable) {
-        return (drawable instanceof ColorDrawable) || (drawable instanceof BitmapDrawable);
     }
 }
diff --git a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.cc b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.cc
index 9fec75b..4bc9d5c 100644
--- a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.cc
+++ b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h"
 
+#include "ash/public/cpp/app_list/app_list_features.h"
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/important_file_writer.h"
@@ -19,6 +21,18 @@
 namespace {
 
 constexpr int kSecondsPerDay = 86400;
+// If |CrOSActionRecorder::actions_| gets longer than this, force a Flush to
+// disk.
+constexpr int kActionLimitInMemory = 3600;
+// If current file already contains more record than this, skip the rest for
+// that day.
+constexpr int kActionLimitPerFile = 100000;
+
+enum CrOSActionRecorderType {
+  kDefault = 0,
+  kLogWithHash = 1,
+  kLogWithoutHash = 2,
+};
 
 void SaveToDiskOnWorkerThread(const CrOSActionHistoryProto actions,
                               const base::FilePath action_filepath) {
@@ -31,6 +45,9 @@
   if (!actions_to_write.ParseFromString(proto_str))
     actions_to_write.Clear();
 
+  if (actions_to_write.actions_size() > kActionLimitPerFile)
+    return;
+
   actions_to_write.MergeFrom(actions);
   const std::string proto_str_to_write = actions_to_write.SerializeAsString();
 
@@ -54,6 +71,9 @@
       should_hash_(true),
       last_save_timestamp_(base::Time::Now()) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  SetCrOSActionRecorderType();
+
   Profile* profile = ProfileManager::GetPrimaryUserProfile();
   if (profile) {
     profile_path_ = profile->GetPath();
@@ -101,8 +121,12 @@
 
 void CrOSActionRecorder::MaybeFlushToDisk() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (actions_.actions().empty())
+    return;
+
   const base::Time now = base::Time::Now();
-  if (now - last_save_timestamp_ >= kSaveInternal) {
+  if (now - last_save_timestamp_ >= kSaveInternal ||
+      actions_.actions_size() > kActionLimitInMemory) {
     last_save_timestamp_ = now;
 
     // Writes the predictor proto to disk asynchronously.
@@ -118,9 +142,24 @@
   }
 }
 
+void CrOSActionRecorder::SetCrOSActionRecorderType() {
+  if (!app_list_features::IsCrOSActionRecorderEnabled())
+    return;
+  const CrOSActionRecorderType type = static_cast<CrOSActionRecorderType>(
+      app_list_features::GetCrOSActionRecorderType());
+  if (type == CrOSActionRecorderType::kLogWithHash) {
+    should_log_ = true;
+    should_hash_ = true;
+  } else if (type == CrOSActionRecorderType::kLogWithoutHash) {
+    should_log_ = true;
+    should_hash_ = false;
+  }
+}
+
 std::string CrOSActionRecorder::MaybeHashed(const std::string& input,
                                             const bool should_hash) {
   return should_hash ? base::NumberToString(base::HashMetricName(input))
                      : input;
 }
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h
index b6a9a3fa..9ce2758 100644
--- a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h
+++ b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h
@@ -55,6 +55,11 @@
   // criteria is met.
   void MaybeFlushToDisk();
 
+  // Get CrOSActionRecorderType from
+  // app_list_features::kEnableCrOSActionRecorder and set |should_log_|,
+  // |should_hash_| accordingly.
+  void SetCrOSActionRecorderType();
+
   // Hashes the |input| if |should_hash| is true; otherwise return |input|.
   static std::string MaybeHashed(const std::string& input, bool should_hash);
 
diff --git a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc
index cc05f54..c61411e 100644
--- a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc
+++ b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc
@@ -3,10 +3,12 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h"
+#include "ash/public/cpp/app_list/app_list_features.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_mock_clock_override.h"
 #include "base/test/task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -49,12 +51,20 @@
     return CrOSActionRecorder::GetCrosActionRecorder()->actions_;
   }
 
-  void EnableLog() {
-    CrOSActionRecorder::GetCrosActionRecorder()->should_log_ = true;
+  void SetLogWithHash() {
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        app_list_features::kEnableCrOSActionRecorder,
+        {{"CrOSActionRecorderType", "1"}});
+
+    CrOSActionRecorder::GetCrosActionRecorder()->SetCrOSActionRecorderType();
   }
 
-  void DisableHash() {
-    CrOSActionRecorder::GetCrosActionRecorder()->should_hash_ = false;
+  void SetLogWithoutHash() {
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        app_list_features::kEnableCrOSActionRecorder,
+        {{"CrOSActionRecorderType", "2"}});
+
+    CrOSActionRecorder::GetCrosActionRecorder()->SetCrOSActionRecorderType();
   }
 
   // Read and Parse log from |day|-th file.
@@ -93,6 +103,7 @@
   base::FilePath profile_path_;
   int64_t save_internal_secs_ = 0;
   base::ScopedMockClockOverride time_;
+  base::test::ScopedFeatureList scoped_feature_list_;
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::MainThreadType::DEFAULT,
       base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED};
@@ -104,9 +115,9 @@
   EXPECT_TRUE(GetCrOSActionHistory().actions().empty());
 }
 
-// Log is hashed by default.
-TEST_F(CrOSActionRecorderTest, HashActionNameByDefault) {
-  EnableLog();
+// Log is hashed if CrOSActionRecorderType == 1.
+TEST_F(CrOSActionRecorderTest, HashActionNameAndConditionName) {
+  SetLogWithHash();
   CrOSActionRecorder::GetCrosActionRecorder()->RecordAction(
       {actions_[0]}, {{conditions_[0], kConditionValue}});
   const CrOSActionHistoryProto& action_history = GetCrOSActionHistory();
@@ -115,10 +126,10 @@
   ExpectCrOSAction(action_history.actions(0), 0, 0);
 }
 
-// DisableHash will log action name and condition name explicitly.
+// Log action name and condition name explicitly if
+// CrOSActionRecorderType == 2.
 TEST_F(CrOSActionRecorderTest, DisableHashToLogExplicitly) {
-  EnableLog();
-  DisableHash();
+  SetLogWithoutHash();
   CrOSActionRecorder::GetCrosActionRecorder()->RecordAction(
       {actions_[0]}, {{conditions_[0], kConditionValue}});
   const CrOSActionHistoryProto& action_history = GetCrOSActionHistory();
@@ -128,7 +139,7 @@
 
 // Check a new file is written every day for expected values.
 TEST_F(CrOSActionRecorderTest, WriteToNewFileEveryDay) {
-  EnableLog();
+  SetLogWithHash();
   time_.Advance(base::TimeDelta::FromSeconds(save_internal_secs_));
   CrOSActionRecorder::GetCrosActionRecorder()->RecordAction(
       {actions_[0]}, {{conditions_[0], kConditionValue}});
@@ -158,7 +169,7 @@
 
 // Check that the result is appended to previous log within a day.
 TEST_F(CrOSActionRecorderTest, AppendToFileEverySaveInAday) {
-  EnableLog();
+  SetLogWithHash();
   time_.Advance(base::TimeDelta::FromSeconds(save_internal_secs_));
   CrOSActionRecorder::GetCrosActionRecorder()->RecordAction(
       {actions_[0]}, {{conditions_[0], kConditionValue}});
diff --git a/chrome/browser/ui/app_list/search/search_controller.cc b/chrome/browser/ui/app_list/search/search_controller.cc
index 2bef832..9141e963 100644
--- a/chrome/browser/ui/app_list/search/search_controller.cc
+++ b/chrome/browser/ui/app_list/search/search_controller.cc
@@ -15,6 +15,8 @@
 #include "ash/public/cpp/tablet_mode.h"
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/metrics/metrics_hashes.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -23,6 +25,7 @@
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_model_updater.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
+#include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/histogram_util.h"
@@ -258,6 +261,14 @@
     }
   }
 
+  // CrOS action recorder.
+  CrOSActionRecorder::GetCrosActionRecorder()->RecordAction(
+      {base::StrCat(
+          {"SearchResultLaunched-", NormalizeId(app_launch_data.id)})},
+      {{"ResultType", static_cast<int>(app_launch_data.ranking_item_type)},
+       {"Query", static_cast<int>(
+                     base::HashMetricName(base::UTF16ToUTF8(last_query_)))}});
+
   for (const auto& provider : providers_)
     provider->Train(app_launch_data.id, app_launch_data.ranking_item_type);
   app_launch_data.query = base::UTF16ToUTF8(last_query_);
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
index 664e655..75b2f95 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
@@ -506,6 +506,16 @@
   } else if (model == Model::APPS && app_ranker_) {
     app_ranker_->Record(NormalizeAppId(app_launch_data.id));
   }
+
+  if (model == Model::MIXED_TYPES && app_launch_data.query.empty() &&
+      zero_state_group_ranker_) {
+    std::vector<std::string> weights;
+    for (const auto& pair : *zero_state_group_ranker_->GetTargetData())
+      weights.push_back(base::StrCat(
+          {pair.first, ":", base::NumberToString(pair.second.last_score)}));
+    VLOG(1) << "Zero state files model weights: ["
+            << base::JoinString(weights, ", ") << "]";
+  }
 }
 
 void SearchResultRanker::LogSearchResults(
diff --git a/chrome/browser/ui/app_list/search/tests/tokenized_string_fuzzer.cc b/chrome/browser/ui/app_list/search/tests/tokenized_string_fuzzer.cc
new file mode 100644
index 0000000..65fded65
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/tests/tokenized_string_fuzzer.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/app_list/tokenized_string.h"
+#include "base/strings/string16.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < 1 || size % 2 != 0)
+    return 0;
+
+  // Test for base::string16 if size is even.
+  base::string16 string_input16(reinterpret_cast<const base::char16*>(data),
+                                size / 2);
+  ash::TokenizedString tokenized_string_from_string16(string_input16);
+  return 0;
+}
diff --git a/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc
index 4fa6cdd..a85f1b8 100644
--- a/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc
@@ -9,6 +9,8 @@
 
 #include "ash/public/cpp/app_menu_constants.h"
 #include "ash/public/cpp/shelf_item.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/arc/app_shortcuts/arc_app_shortcuts_menu_builder.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
@@ -65,6 +67,14 @@
     return;
   }
   if (command_id == ash::UNINSTALL) {
+    if (base::FeatureList::IsEnabled(features::kAppServiceShelf)) {
+      apps::AppServiceProxy* proxy =
+          apps::AppServiceProxyFactory::GetForProfile(controller()->profile());
+      DCHECK(proxy);
+      proxy->Uninstall(item().id.app_id);
+      return;
+    }
+
     arc::ShowArcAppUninstallDialog(controller()->profile(),
                                    nullptr /* controller */, item().id.app_id);
     return;
diff --git a/chrome/browser/ui/ash/network/enrollment_dialog_view.cc b/chrome/browser/ui/ash/network/enrollment_dialog_view.cc
index f362917..b623fd0 100644
--- a/chrome/browser/ui/ash/network/enrollment_dialog_view.cc
+++ b/chrome/browser/ui/ash/network/enrollment_dialog_view.cc
@@ -54,7 +54,6 @@
 
   // views::DialogDelegateView overrides
   bool Accept() override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
 
   // views::WidgetDelegate overrides
   ui::ModalType GetModalType() const override;
@@ -92,6 +91,9 @@
       target_uri_(target_uri),
       connect_(connect),
       added_cert_(false) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(IDS_NETWORK_ENROLLMENT_HANDLER_BUTTON));
   set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
       views::TEXT, views::TEXT));
   chrome::RecordDialogCreation(chrome::DialogIdentifier::ENROLLMENT);
@@ -124,13 +126,6 @@
   return true;
 }
 
-base::string16 EnrollmentDialogView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (button == ui::DIALOG_BUTTON_OK)
-    return l10n_util::GetStringUTF16(IDS_NETWORK_ENROLLMENT_HANDLER_BUTTON);
-  return views::DialogDelegateView::GetDialogButtonLabel(button);
-}
-
 ui::ModalType EnrollmentDialogView::GetModalType() const {
   return ui::MODAL_TYPE_SYSTEM;
 }
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
index 756db08..7985bde 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
@@ -378,7 +378,6 @@
     case BubbleType::SIGN_IN_PROMO:
     case BubbleType::FAILURE:
     case BubbleType::INACTIVE:
-      NOTREACHED();
       return base::string16();
   }
 }
@@ -418,7 +417,6 @@
     case BubbleType::SIGN_IN_PROMO:
     case BubbleType::FAILURE:
     case BubbleType::INACTIVE:
-      NOTREACHED();
       return base::string16();
   }
 }
diff --git a/chrome/browser/ui/blocked_content/popup_blocker.cc b/chrome/browser/ui/blocked_content/popup_blocker.cc
index 1c1646b82..83ce973d 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker.cc
@@ -56,9 +56,8 @@
 
   // This is trusted user action (e.g. shift-click), so make sure it is not
   // blocked.
-  if (open_url_params &&
-      open_url_params->triggering_event_info !=
-          blink::WebTriggeringEventInfo::kFromUntrustedEvent) {
+  if (open_url_params && open_url_params->triggering_event_info !=
+                             blink::TriggeringEventInfo::kFromUntrustedEvent) {
     return PopupBlockType::kNotBlocked;
   }
 
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
index ce8a377..71a0e2e 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
@@ -19,8 +19,8 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
 
 namespace {
 
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
index cda6776..1064216 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
@@ -34,7 +34,7 @@
 #include "content/public/test/test_renderer_host.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/window_open_disposition.h"
 #include "url/gurl.h"
@@ -236,7 +236,7 @@
       ui::PAGE_TRANSITION_LINK, true /* is_renderer_initiated */);
   params.user_gesture = true;
   params.triggering_event_info =
-      blink::WebTriggeringEventInfo::kFromUntrustedEvent;
+      blink::TriggeringEventInfo::kFromUntrustedEvent;
 
   NavigateParams nav_params(profile(), popup_url, ui::PAGE_TRANSITION_LINK);
   nav_params.FillNavigateParamsFromOpenURLParams(params);
@@ -263,8 +263,7 @@
       popup_url, content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
       ui::PAGE_TRANSITION_LINK, true /* is_renderer_initiated */);
   params.user_gesture = true;
-  params.triggering_event_info =
-      blink::WebTriggeringEventInfo::kFromTrustedEvent;
+  params.triggering_event_info = blink::TriggeringEventInfo::kFromTrustedEvent;
 
   NavigateParams nav_params(profile(), popup_url, ui::PAGE_TRANSITION_LINK);
   nav_params.FillNavigateParamsFromOpenURLParams(params);
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 0572aa5..923f79e0a 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -87,7 +87,6 @@
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
-#include "chrome/browser/subresource_filter/chrome_subresource_filter_client.h"
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "chrome/browser/task_manager/web_contents_tags.h"
 #include "chrome/browser/themes/theme_service.h"
@@ -181,7 +180,6 @@
 #include "components/sessions/core/session_types.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
 #include "components/translate/core/browser/language_state.h"
 #include "components/user_manager/user_manager.h"
 #include "components/viz/common/surfaces/surface_id.h"
@@ -1387,15 +1385,6 @@
 #endif  // defined(OS_CHROMEOS)
 }
 
-bool Browser::IsFrameLowPriority(
-    const content::WebContents* web_contents,
-    const content::RenderFrameHost* render_frame_host) {
-  const auto* client =
-      ChromeSubresourceFilterClient::FromWebContents(web_contents);
-  return client &&
-         client->GetThrottleManager()->IsFrameTaggedAsAd(render_frame_host);
-}
-
 bool Browser::IsMouseLocked() const {
   return exclusive_access_manager_->mouse_lock_controller()->IsMouseLocked();
 }
@@ -1450,7 +1439,7 @@
     auto* tracker = PopupTracker::CreateForWebContents(
         nav_params.navigated_or_inserted_contents, source);
     tracker->set_is_trusted(params.triggering_event_info !=
-                            blink::WebTriggeringEventInfo::kFromUntrustedEvent);
+                            blink::TriggeringEventInfo::kFromUntrustedEvent);
   }
 
   return nav_params.navigated_or_inserted_contents;
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 8b8ec34..0b0b0cb0 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -579,9 +579,6 @@
       bool did_start_load,
       bool did_finish_load) override;
   bool ShouldShowStaleContentOnEviction(content::WebContents* source) override;
-  bool IsFrameLowPriority(
-      const content::WebContents* web_contents,
-      const content::RenderFrameHost* render_frame_host) override;
 
   bool is_type_normal() const { return type_ == TYPE_NORMAL; }
   bool is_type_popup() const { return type_ == TYPE_POPUP; }
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
index 99839e3..a8fb8b9b 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
 #include "chrome/browser/ui/content_settings/fake_owner.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/content_settings/core/common/content_settings_types.h"
@@ -35,8 +36,10 @@
 class ContentSettingBubbleModelMixedScriptTest : public InProcessBrowserTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    feature_list.InitAndDisableFeature(
-        blink::features::kMixedContentAutoupgrade);
+    feature_list.InitWithFeatures(
+        /* enabled_features */ {},
+        /* disabled_features */ {blink::features::kMixedContentAutoupgrade,
+                                 features::kMixedContentSiteSetting});
   }
 
  protected:
@@ -300,6 +303,7 @@
     : public ContentSettingBubbleModelMixedScriptTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
+    ContentSettingBubbleModelMixedScriptTest::SetUpCommandLine(command_line);
     content::IsolateAllSitesForTesting(command_line);
   }
 
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
index 110159af..c88432d 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -59,10 +59,8 @@
 void HostedAppBrowserController::SetAppPrefsForWebContents(
     web_app::AppBrowserController* controller,
     content::WebContents* web_contents) {
-  auto* rvh = web_contents->GetRenderViewHost();
-
   web_contents->GetMutableRendererPrefs()->can_accept_load_drops = false;
-  rvh->SyncRendererPrefs();
+  web_contents->SyncRendererPrefs();
 
   if (!controller)
     return;
@@ -80,10 +78,8 @@
 // static
 void HostedAppBrowserController::ClearAppPrefsForWebContents(
     content::WebContents* web_contents) {
-  auto* rvh = web_contents->GetRenderViewHost();
-
   web_contents->GetMutableRendererPrefs()->can_accept_load_drops = true;
-  rvh->SyncRendererPrefs();
+  web_contents->SyncRendererPrefs();
 
   extensions::TabHelper::FromWebContents(web_contents)
       ->SetExtensionApp(nullptr);
diff --git a/chrome/browser/ui/global_media_controls/media_notification_container_observer.h b/chrome/browser/ui/global_media_controls/media_notification_container_observer.h
index fa016041..e50a96a 100644
--- a/chrome/browser/ui/global_media_controls/media_notification_container_observer.h
+++ b/chrome/browser/ui/global_media_controls/media_notification_container_observer.h
@@ -15,6 +15,9 @@
   // Called when the metadata displayed in the container changes.
   virtual void OnContainerMetadataChanged() = 0;
 
+  // Called when the container is clicked.
+  virtual void OnContainerClicked(const std::string& id) = 0;
+
   // Called when the container is dismissed from the dialog.
   virtual void OnContainerDismissed(const std::string& id) = 0;
 
diff --git a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc
index 7b2f07c..349ffa79 100644
--- a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc
+++ b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc
@@ -213,6 +213,22 @@
                             IsWebContentsFocused(web_contents));
 }
 
+void MediaToolbarButtonController::OnContainerClicked(const std::string& id) {
+  auto it = sessions_.find(id);
+  if (it == sessions_.end())
+    return;
+
+  content::WebContents* web_contents = it->second.web_contents();
+  if (!web_contents)
+    return;
+
+  content::WebContentsDelegate* delegate = web_contents->GetDelegate();
+  if (!delegate)
+    return;
+
+  delegate->ActivateContents(web_contents);
+}
+
 void MediaToolbarButtonController::OnContainerDismissed(const std::string& id) {
   auto it = sessions_.find(id);
   if (it != sessions_.end())
diff --git a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h
index 7d15e25..092c3ab 100644
--- a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h
+++ b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h
@@ -64,6 +64,7 @@
   // MediaNotificationContainerObserver implementation.
   void OnContainerExpanded(bool expanded) override {}
   void OnContainerMetadataChanged() override {}
+  void OnContainerClicked(const std::string& id) override;
   void OnContainerDismissed(const std::string& id) override;
   void OnContainerDestroyed(const std::string& id) override;
 
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index 77324b5..3091208 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -686,17 +686,6 @@
       if (visible_security_state.cert_status & net::CERT_STATUS_IS_EV) {
         // EV HTTPS page.
         site_identity_status_ = SITE_IDENTITY_STATUS_EV_CERT;
-        DCHECK(!certificate_->subject().organization_names.empty());
-        organization_name_ =
-            UTF8ToUTF16(certificate_->subject().organization_names[0]);
-        // An EV Cert is required to have a city (localityName) and country but
-        // state is "if any".
-        DCHECK(!certificate_->subject().locality_name.empty());
-        DCHECK(!certificate_->subject().country_name.empty());
-        site_details_message_.assign(l10n_util::GetStringFUTF16(
-            IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_EV_VERIFIED,
-            organization_name_,
-            UTF8ToUTF16(certificate_->subject().country_name)));
       } else {
         // Non-EV OK HTTPS page.
         site_identity_status_ = SITE_IDENTITY_STATUS_CERT;
@@ -989,10 +978,7 @@
   DCHECK_NE(site_identity_status_, SITE_IDENTITY_STATUS_UNKNOWN);
   DCHECK_NE(site_connection_status_, SITE_CONNECTION_STATUS_UNKNOWN);
   PageInfoUI::IdentityInfo info;
-  if (site_identity_status_ == SITE_IDENTITY_STATUS_EV_CERT)
-    info.site_identity = UTF16ToUTF8(organization_name());
-  else
-    info.site_identity = UTF16ToUTF8(GetSimpleSiteName(site_url_));
+  info.site_identity = UTF16ToUTF8(GetSimpleSiteName(site_url_));
 
   info.connection_status = site_connection_status_;
   info.connection_status_description = UTF16ToUTF8(site_connection_details_);
diff --git a/chrome/browser/ui/page_info/page_info.h b/chrome/browser/ui/page_info/page_info.h
index 65deb97f..8ea537c5 100644
--- a/chrome/browser/ui/page_info/page_info.h
+++ b/chrome/browser/ui/page_info/page_info.h
@@ -208,8 +208,6 @@
     return site_details_message_;
   }
 
-  const base::string16& organization_name() const { return organization_name_; }
-
  private:
   FRIEND_TEST_ALL_PREFIXES(PageInfoTest,
                            NonFactoryDefaultAndRecentlyChangedPermissionsShown);
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index c8a7fb7..550f42c 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -510,7 +510,6 @@
             page_info()->site_connection_status());
   EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_NO_CERT,
             page_info()->site_identity_status());
-  EXPECT_EQ(base::string16(), page_info()->organization_name());
 }
 
 TEST_F(PageInfoTest, HTTPSConnection) {
@@ -530,7 +529,6 @@
             page_info()->site_connection_status());
   EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_CERT,
             page_info()->site_identity_status());
-  EXPECT_EQ(base::string16(), page_info()->organization_name());
 }
 
 // Define some dummy constants for Android-only resources.
@@ -745,7 +743,6 @@
         test.expected_connection_icon_id,
         PageInfoUI::GetConnectionIconID(page_info()->site_connection_status()));
 #endif
-    EXPECT_EQ(base::string16(), page_info()->organization_name());
   }
 }
 
@@ -772,9 +769,6 @@
             page_info()->site_connection_status());
   EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_EV_CERT,
             page_info()->site_identity_status());
-  EXPECT_EQ(base::UTF8ToUTF16("Google Inc"), page_info()->organization_name());
-  EXPECT_EQ(base::UTF8ToUTF16("Issued to: Google Inc [US]"),
-            page_info()->site_details_message());
 }
 
 TEST_F(PageInfoTest, HTTPSConnectionError) {
@@ -796,7 +790,6 @@
             page_info()->site_connection_status());
   EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_CERT,
             page_info()->site_identity_status());
-  EXPECT_EQ(base::string16(), page_info()->organization_name());
 }
 
 #if defined(OS_CHROMEOS)
@@ -817,7 +810,6 @@
             page_info()->site_connection_status());
   EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_ADMIN_PROVIDED_CERT,
             page_info()->site_identity_status());
-  EXPECT_EQ(base::string16(), page_info()->organization_name());
 }
 #endif
 
@@ -839,7 +831,6 @@
             page_info()->site_connection_status());
   EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_DEPRECATED_SIGNATURE_ALGORITHM,
             page_info()->site_identity_status());
-  EXPECT_EQ(base::string16(), page_info()->organization_name());
 #if defined(OS_ANDROID)
   EXPECT_EQ(IDR_PAGEINFO_WARNING_MINOR,
             PageInfoUI::GetIdentityIconID(page_info()->site_identity_status()));
@@ -958,7 +949,6 @@
             page_info()->site_connection_status());
   EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_NO_CERT,
             page_info()->site_identity_status());
-  EXPECT_EQ(base::string16(), page_info()->organization_name());
 }
 
 // On desktop, internal URLs aren't handled by PageInfo class. Instead, a
@@ -971,7 +961,6 @@
             page_info()->site_connection_status());
   EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_INTERNAL_PAGE,
             page_info()->site_identity_status());
-  EXPECT_EQ(base::string16(), page_info()->organization_name());
 }
 #endif
 
diff --git a/chrome/browser/ui/prefs/prefs_tab_helper.cc b/chrome/browser/ui/prefs/prefs_tab_helper.cc
index 1a90fe3..29d35b2 100644
--- a/chrome/browser/ui/prefs/prefs_tab_helper.cc
+++ b/chrome/browser/ui/prefs/prefs_tab_helper.cc
@@ -451,7 +451,7 @@
   blink::mojom::RendererPreferences* prefs =
       web_contents_->GetMutableRendererPrefs();
   renderer_preferences_util::UpdateFromSystemSettings(prefs, profile_);
-  web_contents_->GetRenderViewHost()->SyncRendererPrefs();
+  web_contents_->SyncRendererPrefs();
 }
 
 void PrefsTabHelper::OnFontFamilyPrefChanged(const std::string& pref_name) {
diff --git a/chrome/browser/ui/search/new_tab_page_navigation_throttle.cc b/chrome/browser/ui/search/new_tab_page_navigation_throttle.cc
index 6d8327fb..3243bb4f 100644
--- a/chrome/browser/ui/search/new_tab_page_navigation_throttle.cc
+++ b/chrome/browser/ui/search/new_tab_page_navigation_throttle.cc
@@ -4,11 +4,14 @@
 
 #include "chrome/browser/ui/search/new_tab_page_navigation_throttle.h"
 
+#include <utility>
+
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/common/url_constants.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/page_navigator.h"
 #include "content/public/browser/web_contents.h"
 #include "net/http/http_status_code.h"
 #include "url/gurl.h"
@@ -60,12 +63,10 @@
 
 content::NavigationThrottle::ThrottleCheckResult
 NewTabPageNavigationThrottle::OpenLocalNewTabPage() {
-  navigation_handle()->GetWebContents()->OpenURL(content::OpenURLParams(
-      GURL(chrome::kChromeSearchLocalNtpUrl),
-      content::Referrer(navigation_handle()->GetReferrer()),
-      navigation_handle()->GetFrameTreeNodeId(),
-      WindowOpenDisposition::CURRENT_TAB,
-      navigation_handle()->GetPageTransition(),
-      false /* is_renderer_initiated */));
+  content::OpenURLParams params =
+      content::OpenURLParams::FromNavigationHandle(navigation_handle());
+  params.url = GURL(chrome::kChromeSearchLocalNtpUrl);
+  params.is_renderer_initiated = false;
+  navigation_handle()->GetWebContents()->OpenURL(std::move(params));
   return content::NavigationThrottle::CANCEL_AND_IGNORE;
 }
diff --git a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
index 784e1679..a778dd7 100644
--- a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
+++ b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
@@ -58,7 +58,6 @@
   // Overridden from views::BubbleDialogDelegateView:
   std::unique_ptr<views::View> CreateExtraView() override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   void Init() override;
 
   // Overridden from views::WidgetDelegate:
@@ -86,6 +85,8 @@
       browser_(browser),
       high_contrast_(nullptr),
       dark_theme_(nullptr) {
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK,
+                                   l10n_util::GetStringUTF16(IDS_DONE));
   set_margins(gfx::Insets());
   chrome::RecordDialogCreation(chrome::DialogIdentifier::INVERT);
 }
@@ -106,12 +107,6 @@
   return ui::DIALOG_BUTTON_OK;
 }
 
-base::string16 InvertBubbleView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  DCHECK_EQ(button, ui::DialogButton::DIALOG_BUTTON_OK);
-  return l10n_util::GetStringUTF16(IDS_DONE);
-}
-
 void InvertBubbleView::Init() {
   const ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
   SetBorder(views::CreateEmptyBorder(
diff --git a/chrome/browser/ui/views/apps/app_uninstall_dialog_view.cc b/chrome/browser/ui/views/apps/app_uninstall_dialog_view.cc
index 349be48..75ee2a5 100644
--- a/chrome/browser/ui/views/apps/app_uninstall_dialog_view.cc
+++ b/chrome/browser/ui/views/apps/app_uninstall_dialog_view.cc
@@ -33,9 +33,10 @@
     Profile* profile,
     apps::mojom::AppType app_type,
     const std::string& app_id,
+    const std::string& app_name,
     gfx::ImageSkia image,
     apps::UninstallDialog* uninstall_dialog) {
-  new AppUninstallDialogView(profile, app_type, app_id, image,
+  new AppUninstallDialogView(profile, app_type, app_id, app_name, image,
                              uninstall_dialog);
 }
 
@@ -43,23 +44,14 @@
     Profile* profile,
     apps::mojom::AppType app_type,
     const std::string& app_id,
+    const std::string& app_name,
     gfx::ImageSkia image,
     apps::UninstallDialog* uninstall_dialog)
     : apps::UninstallDialog::UiBase(image, uninstall_dialog),
-      BubbleDialogDelegateView(nullptr, views::BubbleBorder::NONE) {
-  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
-  SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
-      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)));
-
-  // Add margins for the icon plus the icon-title padding so that the dialog
-  // contents align with the title text.
-  set_margins(
-      margins() +
-      gfx::Insets(0, margins().left() + apps::UninstallDialog::kSizeHintInDip,
-                  0, 0));
-
-  InitializeView(profile, app_type, app_id);
+      BubbleDialogDelegateView(nullptr, views::BubbleBorder::NONE),
+      app_type_(app_type),
+      app_name_(app_name) {
+  InitializeView(profile, app_id);
   constrained_window::CreateBrowserModalDialogViews(this, nullptr)->Show();
 }
 
@@ -91,12 +83,6 @@
   return gfx::Size(default_width, GetHeightForWidth(default_width));
 }
 
-base::string16 AppUninstallDialogView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return button == ui::DIALOG_BUTTON_CANCEL ? cancel_button_text_
-                                            : confirm_button_text_;
-}
-
 ui::ModalType AppUninstallDialogView::GetModalType() const {
   return ui::MODAL_TYPE_WINDOW;
 }
@@ -106,7 +92,28 @@
 }
 
 base::string16 AppUninstallDialogView::GetWindowTitle() const {
-  return window_title_;
+  switch (app_type_) {
+    case apps::mojom::AppType::kUnknown:
+    case apps::mojom::AppType::kBuiltIn:
+      NOTREACHED();
+      return base::string16();
+    case apps::mojom::AppType::kArc:
+      return l10n_util::GetStringUTF16(
+          shortcut_ ? IDS_EXTENSION_UNINSTALL_PROMPT_TITLE
+                    : IDS_APP_UNINSTALL_PROMPT_TITLE);
+    case apps::mojom::AppType::kCrostini:
+#if defined(OS_CHROMEOS)
+      return l10n_util::GetStringUTF16(
+          IDS_CROSTINI_APPLICATION_UNINSTALL_CONFIRM_TITLE);
+#else
+      NOTREACHED();
+      return base::string16();
+#endif
+    case apps::mojom::AppType::kExtension:
+    case apps::mojom::AppType::kWeb:
+      return l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_TITLE,
+                                        base::UTF8ToUTF16(app_name_));
+  }
 }
 
 bool AppUninstallDialogView::ShouldShowCloseButton() const {
@@ -114,7 +121,8 @@
 }
 
 bool AppUninstallDialogView::ShouldShowWindowIcon() const {
-  return true;
+  return app_type_ == apps::mojom::AppType::kExtension ||
+         app_type_ == apps::mojom::AppType::kWeb;
 }
 
 void AppUninstallDialogView::AddMultiLineLabel(
@@ -130,18 +138,25 @@
 void AppUninstallDialogView::InitializeViewForExtension(
     Profile* profile,
     const std::string& app_id) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON));
+
+  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
+      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)));
+
+  // Add margins for the icon plus the icon-title padding so that the dialog
+  // contents align with the title text.
+  set_margins(margins() +
+              gfx::Insets(0, margins().left() + image().size().height(), 0, 0));
+
   const extensions::Extension* extension =
       extensions::ExtensionRegistry::Get(profile)->GetInstalledExtension(
           app_id);
   DCHECK(extension);
 
-  window_title_ =
-      l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_TITLE,
-                                 base::UTF8ToUTF16(extension->name()));
-
-  confirm_button_text_ =
-      l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON);
-
   if (extensions::ManifestURL::UpdatesFromGallery(extension)) {
     auto report_abuse_checkbox = std::make_unique<views::Checkbox>(
         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_REPORT_ABUSE));
@@ -164,6 +179,12 @@
 void AppUninstallDialogView::InitializeViewForArcApp(
     Profile* profile,
     const std::string& app_id) {
+  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kHorizontal,
+      provider->GetDialogInsetsForContentType(views::TEXT, views::TEXT),
+      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL)));
+
   ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile);
   DCHECK(arc_prefs);
 
@@ -171,27 +192,37 @@
       arc_prefs->GetApp(app_id);
   DCHECK(arc_prefs);
 
-  window_title_ = l10n_util::GetStringUTF16(
-      app_info->shortcut ? IDS_EXTENSION_UNINSTALL_PROMPT_TITLE
-                         : IDS_APP_UNINSTALL_PROMPT_TITLE);
+  shortcut_ = app_info->shortcut;
 
   base::string16 heading_text = l10n_util::GetStringFUTF16(
-      app_info->shortcut ? IDS_EXTENSION_UNINSTALL_PROMPT_HEADING
-                         : IDS_NON_PLATFORM_APP_UNINSTALL_PROMPT_HEADING,
-      base::UTF8ToUTF16(app_info->name));
+      shortcut_ ? IDS_EXTENSION_UNINSTALL_PROMPT_HEADING
+                : IDS_NON_PLATFORM_APP_UNINSTALL_PROMPT_HEADING,
+      base::UTF8ToUTF16(app_name_));
   base::string16 subheading_text;
-  if (!app_info->shortcut) {
+  if (!shortcut_) {
     subheading_text = l10n_util::GetStringUTF16(
         IDS_ARC_APP_UNINSTALL_PROMPT_DATA_REMOVAL_WARNING);
   }
 
-  confirm_button_text_ = l10n_util::GetStringUTF16(
-      app_info->shortcut ? IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON
-                         : IDS_EXTENSION_PROMPT_UNINSTALL_APP_BUTTON);
+  if (!app_info->shortcut) {
+    DialogDelegate::set_button_label(
+        ui::DIALOG_BUTTON_OK,
+        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_APP_BUTTON));
+  } else {
+    DialogDelegate::set_button_label(
+        ui::DIALOG_BUTTON_OK,
+        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON));
+  }
+
+  auto* icon_view = AddChildView(std::make_unique<views::ImageView>());
+  constexpr int kArcImageViewSize = 64;
+  icon_view->SetPreferredSize(gfx::Size(kArcImageViewSize, kArcImageViewSize));
+  icon_view->SetImageSize(image().size());
+  icon_view->SetImage(image());
 
   auto* text_container = AddChildView(std::make_unique<views::View>());
   auto* text_container_layout =
-      SetLayoutManager(std::make_unique<views::BoxLayout>(
+      text_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::Orientation::kVertical));
   text_container_layout->set_main_axis_alignment(
       views::BoxLayout::MainAxisAlignment::kCenter);
@@ -202,14 +233,32 @@
   if (!subheading_text.empty())
     AddMultiLineLabel(text_container, subheading_text);
 }
+
+void AppUninstallDialogView::InitializeViewForCrostiniApp(
+    Profile* profile,
+    const std::string& app_id) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_APP_BUTTON));
+
+  views::LayoutProvider* provider = views::LayoutProvider::Get();
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical,
+      provider->GetDialogInsetsForContentType(views::TEXT, views::TEXT),
+      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)));
+
+  base::string16 message = l10n_util::GetStringFUTF16(
+      IDS_CROSTINI_APPLICATION_UNINSTALL_CONFIRM_BODY,
+      base::UTF8ToUTF16(app_name_));
+  auto* message_label = AddChildView(std::make_unique<views::Label>(message));
+  message_label->SetMultiLine(true);
+  message_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+}
 #endif
 
 void AppUninstallDialogView::InitializeView(Profile* profile,
-                                            apps::mojom::AppType app_type,
                                             const std::string& app_id) {
-  cancel_button_text_ = l10n_util::GetStringUTF16(IDS_CANCEL);
-
-  switch (app_type) {
+  switch (app_type_) {
     case apps::mojom::AppType::kUnknown:
     case apps::mojom::AppType::kBuiltIn:
       NOTREACHED();
@@ -217,10 +266,16 @@
     case apps::mojom::AppType::kArc:
 #if defined(OS_CHROMEOS)
       InitializeViewForArcApp(profile, app_id);
+#else
+      NOTREACHED();
 #endif
       break;
     case apps::mojom::AppType::kCrostini:
+#if defined(OS_CHROMEOS)
+      InitializeViewForCrostiniApp(profile, app_id);
+#else
       NOTREACHED();
+#endif
       break;
     case apps::mojom::AppType::kExtension:
     case apps::mojom::AppType::kWeb:
diff --git a/chrome/browser/ui/views/apps/app_uninstall_dialog_view.h b/chrome/browser/ui/views/apps/app_uninstall_dialog_view.h
index 2005972..b13a086 100644
--- a/chrome/browser/ui/views/apps/app_uninstall_dialog_view.h
+++ b/chrome/browser/ui/views/apps/app_uninstall_dialog_view.h
@@ -39,8 +39,7 @@
 // AppService, which transfers control to the publisher to uninstall the app.
 //
 // TODO(crbug.com/1009248):
-// 1. Add Crostini uninstall function.
-// 2. Add an interface to the uninstall, like what is done by
+// 1. Add an interface to the uninstall, like what is done by
 // extension_uninstall_dialog_->ConfirmUninstallByExtension.
 class AppUninstallDialogView : public apps::UninstallDialog::UiBase,
                                views::BubbleDialogDelegateView {
@@ -48,6 +47,7 @@
   AppUninstallDialogView(Profile* profile,
                          apps::mojom::AppType app_type,
                          const std::string& app_id,
+                         const std::string& app_name,
                          gfx::ImageSkia image,
                          apps::UninstallDialog* uninstall_dialog);
   ~AppUninstallDialogView() override = default;
@@ -57,7 +57,6 @@
   bool Accept() override;
   bool Close() override;
   gfx::Size CalculatePreferredSize() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   ui::ModalType GetModalType() const override;
   gfx::ImageSkia GetWindowIcon() override;
   base::string16 GetWindowTitle() const override;
@@ -69,20 +68,25 @@
   void InitializeViewForExtension(Profile* profile, const std::string& app_id);
 #if defined(OS_CHROMEOS)
   void InitializeViewForArcApp(Profile* profile, const std::string& app_id);
+  void InitializeViewForCrostiniApp(Profile* profile,
+                                    const std::string& app_id);
 #endif
   void InitializeView(Profile* profile,
-                      apps::mojom::AppType app_type,
                       const std::string& app_id);
 
+  // The type of apps, e.g. Extension-backed app, Android app.
+  apps::mojom::AppType app_type_;
+
+  // The name of apps, e.g. Camera.
+  const std::string app_name_;
+
+  // Whether app represents a shortcut. |shortcut_| is available for the ARC
+  // apps only.
+  bool shortcut_ = false;
+
   views::Checkbox* report_abuse_checkbox_ = nullptr;
   views::Checkbox* clear_site_data_checkbox_ = nullptr;
 
-  // TODO(crbug.com/1009248): Remove these fields to use the consistent title
-  // and button.
-  base::string16 window_title_;
-  base::string16 confirm_button_text_;
-  base::string16 cancel_button_text_;
-
   DISALLOW_COPY_AND_ASSIGN(AppUninstallDialogView);
 };
 
diff --git a/chrome/browser/ui/views/arc_app_dialog_view.cc b/chrome/browser/ui/views/arc_app_dialog_view.cc
index cf8d60a..14c2dc4 100644
--- a/chrome/browser/ui/views/arc_app_dialog_view.cc
+++ b/chrome/browser/ui/views/arc_app_dialog_view.cc
@@ -63,7 +63,6 @@
   bool ShouldShowCloseButton() const override;
 
   // views::DialogDelegate:
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool Accept() override;
   bool Cancel() override;
 
@@ -84,8 +83,6 @@
 
   const std::string app_id_;
   const base::string16 window_title_;
-  const base::string16 confirm_button_text_;
-  const base::string16 cancel_button_text_;
   ArcAppConfirmCallback confirm_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(ArcAppDialogView);
@@ -106,9 +103,11 @@
     : profile_(profile),
       app_id_(app_id),
       window_title_(window_title),
-      confirm_button_text_(confirm_button_text),
-      cancel_button_text_(cancel_button_text),
       confirm_callback_(std::move(confirm_callback)) {
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK, confirm_button_text);
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_CANCEL,
+                                   cancel_button_text);
+
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
 
   SetLayoutManager(std::make_unique<views::BoxLayout>(
@@ -184,12 +183,6 @@
   return false;
 }
 
-base::string16 ArcAppDialogView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return button == ui::DIALOG_BUTTON_CANCEL ? cancel_button_text_
-                                            : confirm_button_text_;
-}
-
 bool ArcAppDialogView::Accept() {
   if (confirm_callback_)
     std::move(confirm_callback_).Run(true);
diff --git a/chrome/browser/ui/views/arc_data_removal_dialog_view.cc b/chrome/browser/ui/views/arc_data_removal_dialog_view.cc
index e6e269d..9f7203a0 100644
--- a/chrome/browser/ui/views/arc_data_removal_dialog_view.cc
+++ b/chrome/browser/ui/views/arc_data_removal_dialog_view.cc
@@ -50,7 +50,6 @@
   ui::ModalType GetModalType() const override;
 
   // views::DialogDelegate:
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool Accept() override;
   bool Cancel() override;
 
@@ -83,6 +82,10 @@
     Profile* profile,
     DataRemovalConfirmationCallback confirm_callback)
     : profile_(profile), confirm_callback_(std::move(confirm_callback)) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(IDS_ARC_DATA_REMOVAL_CONFIRMATION_OK_BUTTON));
+
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
 
   std::unique_ptr<views::BoxLayout> layout = std::make_unique<views::BoxLayout>(
@@ -131,15 +134,6 @@
   return ui::MODAL_TYPE_WINDOW;
 }
 
-base::string16 DataRemovalConfirmationDialog::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (button == ui::DIALOG_BUTTON_OK) {
-    return l10n_util::GetStringUTF16(
-        IDS_ARC_DATA_REMOVAL_CONFIRMATION_OK_BUTTON);
-  }
-  return views::DialogDelegate::GetDialogButtonLabel(button);
-}
-
 bool DataRemovalConfirmationDialog::Accept() {
   if (confirm_callback_)
     std::move(confirm_callback_).Run(true);
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index 827c232e..dd1b565 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -335,7 +335,7 @@
   node_data->SetName(base::JoinString(text, base::ASCIIToUTF16(" ")));
 
   // Options are selectable.
-  node_data->role = ax::mojom::Role::kMenuItem;
+  node_data->role = ax::mojom::Role::kListBoxOption;
   node_data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected,
                               is_selected());
 
@@ -849,7 +849,7 @@
 
 void AutofillPopupViewNativeViews::GetAccessibleNodeData(
     ui::AXNodeData* node_data) {
-  node_data->role = ax::mojom::Role::kMenu;
+  node_data->role = ax::mojom::Role::kListBox;
   // If controller_ is valid, then the view is expanded.
   if (controller_) {
     node_data->AddState(ax::mojom::State::kExpanded);
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc
index 818e856..7e5f716 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc
@@ -174,7 +174,7 @@
   // Item 0.
   ui::AXNodeData node_data_0;
   view()->GetRowsForTesting()[0]->GetAccessibleNodeData(&node_data_0);
-  EXPECT_EQ(ax::mojom::Role::kMenuItem, node_data_0.role);
+  EXPECT_EQ(ax::mojom::Role::kListBoxOption, node_data_0.role);
   EXPECT_EQ(1, node_data_0.GetIntAttribute(ax::mojom::IntAttribute::kPosInSet));
   EXPECT_EQ(3, node_data_0.GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
   EXPECT_TRUE(
@@ -194,7 +194,7 @@
   view()->GetRowsForTesting()[2]->GetAccessibleNodeData(&node_data_2);
   EXPECT_EQ(2, node_data_2.GetIntAttribute(ax::mojom::IntAttribute::kPosInSet));
   EXPECT_EQ(3, node_data_2.GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
-  EXPECT_EQ(ax::mojom::Role::kMenuItem, node_data_2.role);
+  EXPECT_EQ(ax::mojom::Role::kListBoxOption, node_data_2.role);
   EXPECT_FALSE(
       node_data_2.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
 
@@ -203,7 +203,7 @@
   view()->GetRowsForTesting()[3]->GetAccessibleNodeData(&node_data_3);
   EXPECT_EQ(3, node_data_3.GetIntAttribute(ax::mojom::IntAttribute::kPosInSet));
   EXPECT_EQ(3, node_data_3.GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
-  EXPECT_EQ(ax::mojom::Role::kMenuItem, node_data_3.role);
+  EXPECT_EQ(ax::mojom::Role::kListBoxOption, node_data_3.role);
   EXPECT_FALSE(
       node_data_3.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
 }
diff --git a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
index 530ad98..d966e6b 100644
--- a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
@@ -71,6 +71,7 @@
     content::WebContents* web_contents)
     : controller_(controller), web_contents_(web_contents) {
   chrome::RecordDialogCreation(chrome::DialogIdentifier::CARD_UNMASK);
+  UpdateButtonLabels();
 }
 
 CardUnmaskPromptViews::~CardUnmaskPromptViews() {
@@ -92,6 +93,7 @@
   controls_container_->SetVisible(false);
   overlay_->SetVisible(true);
   progress_throbber_->Start();
+  UpdateButtonLabels();
   DialogModelChanged();
   Layout();
 }
@@ -153,6 +155,7 @@
       layout->AddView(std::move(error_icon));
       layout->AddView(std::move(error_label));
     }
+    UpdateButtonLabels();
     DialogModelChanged();
   }
 
@@ -172,6 +175,7 @@
   input_row_->InvalidateLayout();
   cvc_input_->SetInvalid(false);
   cvc_input_->SetText(base::string16());
+  UpdateButtonLabels();
   DialogModelChanged();
   GetWidget()->UpdateWindowTitle();
   instructions_->SetText(controller_->GetInstructionsMessage());
@@ -284,14 +288,6 @@
   return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
 }
 
-base::string16 CardUnmaskPromptViews::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (button == ui::DIALOG_BUTTON_OK)
-    return controller_->GetOkButtonLabel();
-
-  return DialogDelegateView::GetDialogButtonLabel(button);
-}
-
 bool CardUnmaskPromptViews::IsDialogButtonEnabled(
     ui::DialogButton button) const {
   if (button == ui::DIALOG_BUTTON_CANCEL)
@@ -339,6 +335,7 @@
   if (controller_->InputCvcIsValid(new_contents))
     cvc_input_->SetInvalid(false);
 
+  UpdateButtonLabels();
   DialogModelChanged();
 }
 
@@ -359,6 +356,7 @@
         IDS_AUTOFILL_CARD_UNMASK_INVALID_EXPIRATION_DATE));
   }
 
+  UpdateButtonLabels();
   DialogModelChanged();
 }
 
@@ -491,6 +489,11 @@
   GetWidget()->Close();
 }
 
+void CardUnmaskPromptViews::UpdateButtonLabels() {
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK,
+                                   controller_->GetOkButtonLabel());
+}
+
 CardUnmaskPromptView* CreateCardUnmaskPromptView(
     CardUnmaskPromptController* controller,
     content::WebContents* web_contents) {
diff --git a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.h b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.h
index ab7d494a..82505fe 100644
--- a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.h
+++ b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.h
@@ -61,7 +61,6 @@
   base::string16 GetWindowTitle() const override;
   void DeleteDelegate() override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   View* GetInitiallyFocusedView() override;
   bool ShouldShowCloseButton() const override;
@@ -88,6 +87,8 @@
   void ShowNewCardLink();
   void ClosePrompt();
 
+  void UpdateButtonLabels();
+
   CardUnmaskPromptController* controller_;
   content::WebContents* web_contents_;
 
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_bubble_views.cc b/chrome/browser/ui/views/autofill/payments/local_card_migration_bubble_views.cc
index 4dbaf0c..7112e4d2 100644
--- a/chrome/browser/ui/views/autofill/payments/local_card_migration_bubble_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_bubble_views.cc
@@ -50,6 +50,10 @@
     : LocationBarBubbleDelegateView(anchor_view, web_contents),
       controller_(controller) {
   DCHECK(controller);
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(
+          IDS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_BUTTON_LABEL));
 }
 
 void LocalCardMigrationBubbleViews::Show(DisplayReason reason) {
@@ -87,13 +91,6 @@
   return ui::DIALOG_BUTTON_OK;
 }
 
-base::string16 LocalCardMigrationBubbleViews::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  DCHECK_EQ(button, ui::DIALOG_BUTTON_OK);
-  return l10n_util::GetStringUTF16(
-      IDS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_BUTTON_LABEL);
-}
-
 gfx::Size LocalCardMigrationBubbleViews::CalculatePreferredSize() const {
   const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
                         DISTANCE_BUBBLE_PREFERRED_WIDTH) -
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_bubble_views.h b/chrome/browser/ui/views/autofill/payments/local_card_migration_bubble_views.h
index a8fac7c0..4fac675 100644
--- a/chrome/browser/ui/views/autofill/payments/local_card_migration_bubble_views.h
+++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_bubble_views.h
@@ -38,7 +38,6 @@
   bool Cancel() override;
   bool Close() override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   gfx::Size CalculatePreferredSize() const override;
   void AddedToWidget() override;
   bool ShouldShowCloseButton() const override;
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.cc b/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.cc
index b14bd531..90471075 100644
--- a/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.cc
+++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.cc
@@ -352,6 +352,9 @@
     LocalCardMigrationDialogController* controller,
     content::WebContents* web_contents)
     : controller_(controller), web_contents_(web_contents) {
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK, GetOkButtonLabel());
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_CANCEL,
+                                   GetCancelButtonLabel());
   set_close_on_deactivate(false);
   set_margins(gfx::Insets());
 }
@@ -395,12 +398,6 @@
   return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
 }
 
-base::string16 LocalCardMigrationDialogView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return button == ui::DIALOG_BUTTON_OK ? GetOkButtonLabel()
-                                        : GetCancelButtonLabel();
-}
-
 // TODO(crbug.com/867194): Update this method when adding feedback.
 bool LocalCardMigrationDialogView::IsDialogButtonEnabled(
     ui::DialogButton button) const {
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.h b/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.h
index c3b2e877..c7b2499 100644
--- a/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.h
+++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.h
@@ -38,7 +38,6 @@
   ui::ModalType GetModalType() const override;
   bool ShouldShowCloseButton() const override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   bool Accept() override;
   bool Cancel() override;
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.cc
index 691cda6..f1ca879 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.cc
@@ -55,6 +55,10 @@
                                          SaveCardBubbleController* controller)
     : LocationBarBubbleDelegateView(anchor_view, web_contents),
       controller_(controller) {
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK,
+                                   controller->GetAcceptButtonText());
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_CANCEL,
+                                   controller->GetDeclineButtonText());
   DCHECK(controller);
   chrome::RecordDialogCreation(chrome::DialogIdentifier::SAVE_CARD);
 }
@@ -75,12 +79,6 @@
   CloseBubble();
 }
 
-base::string16 SaveCardBubbleViews::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return button == ui::DIALOG_BUTTON_OK ? controller()->GetAcceptButtonText()
-                                        : controller()->GetDeclineButtonText();
-}
-
 std::unique_ptr<views::View> SaveCardBubbleViews::CreateFootnoteView() {
   return nullptr;
 }
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.h b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.h
index c4ee2d39..200b1af 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.h
+++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.h
@@ -39,7 +39,6 @@
   void Hide() override;
 
   // views::BubbleDialogDelegateView:
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   std::unique_ptr<views::View> CreateFootnoteView() override;
   bool Accept() override;
   bool Cancel() override;
diff --git a/chrome/browser/ui/views/autofill/payments/webauthn_offer_dialog_view_impl.cc b/chrome/browser/ui/views/autofill/payments/webauthn_offer_dialog_view_impl.cc
index 3f15eb52..217b042 100644
--- a/chrome/browser/ui/views/autofill/payments/webauthn_offer_dialog_view_impl.cc
+++ b/chrome/browser/ui/views/autofill/payments/webauthn_offer_dialog_view_impl.cc
@@ -30,6 +30,11 @@
   sheet_view_ =
       AddChildView(CreateSheetViewForAutofillWebAuthn(std::move(model)));
   sheet_view_->ReInitChildViews();
+
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK,
+                                   model_->GetAcceptButtonLabel());
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_CANCEL,
+                                   model_->GetCancelButtonLabel());
 }
 
 WebauthnOfferDialogViewImpl::~WebauthnOfferDialogViewImpl() {
@@ -104,12 +109,6 @@
              : ui::DIALOG_BUTTON_CANCEL;
 }
 
-base::string16 WebauthnOfferDialogViewImpl::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return button == ui::DIALOG_BUTTON_OK ? model_->GetAcceptButtonLabel()
-                                        : model_->GetCancelButtonLabel();
-}
-
 bool WebauthnOfferDialogViewImpl::IsDialogButtonEnabled(
     ui::DialogButton button) const {
   return button == ui::DIALOG_BUTTON_OK ? model_->IsAcceptButtonEnabled()
@@ -146,6 +145,10 @@
 void WebauthnOfferDialogViewImpl::RefreshContent() {
   sheet_view_->ReInitChildViews();
   sheet_view_->InvalidateLayout();
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK,
+                                   model_->GetAcceptButtonLabel());
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_CANCEL,
+                                   model_->GetCancelButtonLabel());
   DialogModelChanged();
   Layout();
 
diff --git a/chrome/browser/ui/views/autofill/payments/webauthn_offer_dialog_view_impl.h b/chrome/browser/ui/views/autofill/payments/webauthn_offer_dialog_view_impl.h
index 5a7bb5f..46839dc 100644
--- a/chrome/browser/ui/views/autofill/payments/webauthn_offer_dialog_view_impl.h
+++ b/chrome/browser/ui/views/autofill/payments/webauthn_offer_dialog_view_impl.h
@@ -40,7 +40,6 @@
   bool Cancel() override;
   bool Close() override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   ui::ModalType GetModalType() const override;
   base::string16 GetWindowTitle() const override;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
index 4b21555..c2e3a3f 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -193,11 +193,10 @@
   return true;
 }
 
-void BookmarkBubbleView::UpdateButton(views::LabelButton* button,
-                                      ui::DialogButton type) {
-  LocationBarBubbleDelegateView::UpdateButton(button, type);
-  if (type == ui::DIALOG_BUTTON_CANCEL)
-    button->AddAccelerator(ui::Accelerator(ui::VKEY_R, ui::EF_ALT_DOWN));
+void BookmarkBubbleView::OnDialogInitialized() {
+  views::Button* cancel = GetCancelButton();
+  if (cancel)
+    cancel->AddAccelerator(ui::Accelerator(ui::VKEY_R, ui::EF_ALT_DOWN));
 }
 
 // views::View -----------------------------------------------------------------
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
index 32ae8f7..8935b26 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
@@ -70,7 +70,7 @@
   bool Cancel() override;
   bool Accept() override;
   bool Close() override;
-  void UpdateButton(views::LabelButton* button, ui::DialogButton type) override;
+  void OnDialogInitialized() override;
   const char* GetClassName() const override;
 
   // views::ButtonListener:
diff --git a/chrome/browser/ui/views/confirm_bubble_views.cc b/chrome/browser/ui/views/confirm_bubble_views.cc
index 0fa4c2b..88d9fcf 100644
--- a/chrome/browser/ui/views/confirm_bubble_views.cc
+++ b/chrome/browser/ui/views/confirm_bubble_views.cc
@@ -28,6 +28,13 @@
 ConfirmBubbleViews::ConfirmBubbleViews(
     std::unique_ptr<ConfirmBubbleModel> model)
     : model_(std::move(model)), help_button_(nullptr) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      model_->GetButtonLabel(ConfirmBubbleModel::BUTTON_OK));
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_CANCEL,
+      model_->GetButtonLabel(ConfirmBubbleModel::BUTTON_CANCEL));
+
   set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
       views::TEXT, views::TEXT));
   views::GridLayout* layout =
@@ -57,19 +64,6 @@
 ConfirmBubbleViews::~ConfirmBubbleViews() {
 }
 
-base::string16 ConfirmBubbleViews::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  switch (button) {
-    case ui::DIALOG_BUTTON_OK:
-      return model_->GetButtonLabel(ConfirmBubbleModel::BUTTON_OK);
-    case ui::DIALOG_BUTTON_CANCEL:
-      return model_->GetButtonLabel(ConfirmBubbleModel::BUTTON_CANCEL);
-    default:
-      NOTREACHED();
-      return DialogDelegateView::GetDialogButtonLabel(button);
-  }
-}
-
 bool ConfirmBubbleViews::IsDialogButtonEnabled(ui::DialogButton button) const {
   switch (button) {
     case ui::DIALOG_BUTTON_OK:
diff --git a/chrome/browser/ui/views/confirm_bubble_views.h b/chrome/browser/ui/views/confirm_bubble_views.h
index c8ed94f..1d8565b 100644
--- a/chrome/browser/ui/views/confirm_bubble_views.h
+++ b/chrome/browser/ui/views/confirm_bubble_views.h
@@ -37,7 +37,6 @@
   ~ConfirmBubbleViews() override;
 
   // views::DialogDelegate implementation.
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   std::unique_ptr<views::View> CreateExtraView() override;
   bool Cancel() override;
diff --git a/chrome/browser/ui/views/crostini/crostini_app_uninstaller_view.cc b/chrome/browser/ui/views/crostini/crostini_app_uninstaller_view.cc
index e8955ed..eb993258 100644
--- a/chrome/browser/ui/views/crostini/crostini_app_uninstaller_view.cc
+++ b/chrome/browser/ui/views/crostini/crostini_app_uninstaller_view.cc
@@ -43,15 +43,6 @@
   return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
 }
 
-base::string16 CrostiniAppUninstallerView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (button == ui::DIALOG_BUTTON_OK)
-    return l10n_util::GetStringUTF16(
-        IDS_CROSTINI_APPLICATION_UNINSTALL_UNINSTALL_BUTTON);
-  DCHECK_EQ(button, ui::DIALOG_BUTTON_CANCEL);
-  return l10n_util::GetStringUTF16(IDS_APP_CANCEL);
-}
-
 base::string16 CrostiniAppUninstallerView::GetWindowTitle() const {
   return l10n_util::GetStringUTF16(
       IDS_CROSTINI_APPLICATION_UNINSTALL_CONFIRM_TITLE);
@@ -80,6 +71,11 @@
     Profile* profile,
     const std::string& app_id)
     : profile_(profile), app_id_(app_id) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(
+          IDS_CROSTINI_APPLICATION_UNINSTALL_UNINSTALL_BUTTON));
+
   views::LayoutProvider* provider = views::LayoutProvider::Get();
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical,
diff --git a/chrome/browser/ui/views/crostini/crostini_app_uninstaller_view.h b/chrome/browser/ui/views/crostini/crostini_app_uninstaller_view.h
index 8f353fa..74da0cd5 100644
--- a/chrome/browser/ui/views/crostini/crostini_app_uninstaller_view.h
+++ b/chrome/browser/ui/views/crostini/crostini_app_uninstaller_view.h
@@ -22,7 +22,6 @@
 
   // views::DialogDelegateView:
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   base::string16 GetWindowTitle() const override;
   bool ShouldShowCloseButton() const override;
   bool Accept() override;
diff --git a/chrome/browser/ui/views/crostini/crostini_force_close_view.cc b/chrome/browser/ui/views/crostini/crostini_force_close_view.cc
index a27640e..cb2c52a 100644
--- a/chrome/browser/ui/views/crostini/crostini_force_close_view.cc
+++ b/chrome/browser/ui/views/crostini/crostini_force_close_view.cc
@@ -65,14 +65,6 @@
   return false;
 }
 
-base::string16 CrostiniForceCloseView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (button == ui::DIALOG_BUTTON_OK)
-    return l10n_util::GetStringUTF16(IDS_CROSTINI_FORCE_CLOSE_ACCEPT_BUTTON);
-  DCHECK_EQ(button, ui::DIALOG_BUTTON_CANCEL);
-  return l10n_util::GetStringUTF16(IDS_APP_CANCEL);
-}
-
 base::string16 CrostiniForceCloseView::GetWindowTitle() const {
   return app_name_.empty()
              ? l10n_util::GetStringUTF16(IDS_CROSTINI_FORCE_CLOSE_TITLE_UNKNOWN)
@@ -92,6 +84,10 @@
     base::OnceClosure force_close_callback)
     : app_name_(base::UTF8ToUTF16(app_name)),
       force_close_callback_(std::move(force_close_callback)) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(IDS_CROSTINI_FORCE_CLOSE_ACCEPT_BUTTON));
+
   views::LayoutProvider* provider = views::LayoutProvider::Get();
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical,
diff --git a/chrome/browser/ui/views/crostini/crostini_force_close_view.h b/chrome/browser/ui/views/crostini/crostini_force_close_view.h
index 6df59a9..4a3912ef 100644
--- a/chrome/browser/ui/views/crostini/crostini_force_close_view.h
+++ b/chrome/browser/ui/views/crostini/crostini_force_close_view.h
@@ -36,7 +36,6 @@
   ui::ModalType GetModalType() const override;
   bool ShouldShowCloseButton() const override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   base::string16 GetWindowTitle() const override;
   gfx::Size CalculatePreferredSize() const override;
 
diff --git a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc
index b2b932ff..b269cb0b 100644
--- a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc
+++ b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc
@@ -63,14 +63,6 @@
   return 0;
 }
 
-base::string16 CrostiniUninstallerView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (button == ui::DIALOG_BUTTON_OK)
-    return l10n_util::GetStringUTF16(IDS_CROSTINI_UNINSTALLER_UNINSTALL_BUTTON);
-  DCHECK_EQ(button, ui::DIALOG_BUTTON_CANCEL);
-  return l10n_util::GetStringUTF16(IDS_APP_CANCEL);
-}
-
 base::string16 CrostiniUninstallerView::GetWindowTitle() const {
   const base::string16 device_type = ui::GetChromeOSDeviceName();
   return l10n_util::GetStringUTF16(IDS_CROSTINI_UNINSTALLER_TITLE);
@@ -121,6 +113,10 @@
 
 CrostiniUninstallerView::CrostiniUninstallerView(Profile* profile)
     : profile_(profile) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(IDS_CROSTINI_UNINSTALLER_UNINSTALL_BUTTON));
+
   views::LayoutProvider* provider = views::LayoutProvider::Get();
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical,
diff --git a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.h b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.h
index c47fca8..a33b0ed7 100644
--- a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.h
+++ b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.h
@@ -35,7 +35,6 @@
 
   // views::DialogDelegateView:
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   base::string16 GetWindowTitle() const override;
   bool ShouldShowCloseButton() const override;
   bool Accept() override;
diff --git a/chrome/browser/ui/views/download/download_in_progress_dialog_view.cc b/chrome/browser/ui/views/download/download_in_progress_dialog_view.cc
index 960ac6c..791b3be 100644
--- a/chrome/browser/ui/views/download/download_in_progress_dialog_view.cc
+++ b/chrome/browser/ui/views/download/download_in_progress_dialog_view.cc
@@ -36,6 +36,12 @@
       app_modal_(app_modal),
       callback_(callback) {
   DialogDelegate::set_default_button(ui::DIALOG_BUTTON_CANCEL);
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(IDS_ABANDON_DOWNLOAD_DIALOG_EXIT_BUTTON));
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_CANCEL,
+      l10n_util::GetStringUTF16(IDS_ABANDON_DOWNLOAD_DIALOG_CONTINUE_BUTTON));
   SetLayoutManager(std::make_unique<views::FillLayout>());
   set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
       views::TEXT, views::TEXT));
@@ -76,14 +82,6 @@
   return gfx::Size(width, GetHeightForWidth(width));
 }
 
-base::string16 DownloadInProgressDialogView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return l10n_util::GetStringUTF16(
-      button == ui::DIALOG_BUTTON_OK
-          ? IDS_ABANDON_DOWNLOAD_DIALOG_EXIT_BUTTON
-          : IDS_ABANDON_DOWNLOAD_DIALOG_CONTINUE_BUTTON);
-}
-
 bool DownloadInProgressDialogView::Cancel() {
   callback_.Run(false /* cancel_downloads */);
   return true;
diff --git a/chrome/browser/ui/views/download/download_in_progress_dialog_view.h b/chrome/browser/ui/views/download/download_in_progress_dialog_view.h
index ae595b91..176d0db 100644
--- a/chrome/browser/ui/views/download/download_in_progress_dialog_view.h
+++ b/chrome/browser/ui/views/download/download_in_progress_dialog_view.h
@@ -34,7 +34,6 @@
 
   // views::DialogDelegateView:
   gfx::Size CalculatePreferredSize() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool Cancel() override;
   bool Accept() override;
   ui::ModalType GetModalType() const override;
diff --git a/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller_dialog_browsertest.cc b/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller_dialog_browsertest.cc
index c91ff56..40f7ebc 100644
--- a/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller_dialog_browsertest.cc
@@ -49,7 +49,7 @@
   }
 
   void SetUpOnMainThread() override {
-    GetMediaToolbarButton()->SetNativeTheme(&test_theme_);
+    GetMediaToolbarButton()->SetNativeThemeForTesting(&test_theme_);
   }
 
   void ShowUi(const std::string& name) override { ShowPromo(); }
diff --git a/chrome/browser/ui/views/find_bar_view.cc b/chrome/browser/ui/views/find_bar_view.cc
index 12315db0..a5f2b18d 100644
--- a/chrome/browser/ui/views/find_bar_view.cc
+++ b/chrome/browser/ui/views/find_bar_view.cc
@@ -12,7 +12,6 @@
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "chrome/app/vector_icons/vector_icons.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
 #include "chrome/browser/ui/find_bar/find_bar_state.h"
@@ -312,16 +311,6 @@
   return size;
 }
 
-void FindBarView::AddedToWidget() {
-  // Since the find bar now works/looks like a location bar bubble, make sure it
-  // doesn't get dark themed in incognito mode.
-  if (find_bar_host_->browser_view()
-          ->browser()
-          ->profile()
-          ->IsIncognitoProfile())
-    SetNativeTheme(ui::NativeTheme::GetInstanceForNativeUi());
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // FindBarView, DropdownBarHostDelegate implementation:
 
diff --git a/chrome/browser/ui/views/find_bar_view.h b/chrome/browser/ui/views/find_bar_view.h
index edbeceb0..507139d 100644
--- a/chrome/browser/ui/views/find_bar_view.h
+++ b/chrome/browser/ui/views/find_bar_view.h
@@ -81,10 +81,6 @@
   void OnAfterUserAction(views::Textfield* sender) override;
   void OnAfterPaste() override;
 
- protected:
-  // views::View overrides:
-  void AddedToWidget() override;
-
  private:
   class FindBarButton;
   class MatchCountLabel;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 9af8cf9..10600aa 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -667,11 +667,7 @@
   if (immersive_mode_controller->IsEnabled())
     return immersive_mode_controller->IsRevealed();
 
-  if (frame()->IsFullscreen())
-    return false;
-
-  // Do not paint for V1 apps in overview mode.
-  return browser_view()->IsBrowserTypeNormal() || !IsInOverviewMode();
+  return !frame()->IsFullscreen();
 }
 
 void BrowserNonClientFrameViewAsh::OnAddedToOrRemovedFromOverview() {
@@ -679,14 +675,6 @@
   caption_button_container_->SetVisible(should_show_caption_buttons);
   if (web_app_frame_toolbar())
     web_app_frame_toolbar()->SetVisible(should_show_caption_buttons);
-
-  // The entire frame should be repainted for v1 apps, since its visibility can
-  // change (see also ShouldPaint()). Do not invoke this on normal browser
-  // windows since it does not have to repaint frame except for the caption
-  // buttons and repainting might cause stuttering of the animation. See
-  // https://crbug.com/949227.
-  if (!browser_view()->IsBrowserTypeNormal())
-    SchedulePaint();
 }
 
 std::unique_ptr<ash::FrameHeader>
diff --git a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
index 4352e407..26b2bb7 100644
--- a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
+++ b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
@@ -80,6 +80,13 @@
   return toggle_button;
 }
 
+void WebUITabStripContainerView::CloseContainer() {
+  if (!GetVisible())
+    return;
+  SetVisible(false);
+  InvalidateLayout();
+}
+
 void WebUITabStripContainerView::ShowContextMenuAtPoint(
     gfx::Point point,
     std::unique_ptr<ui::MenuModel> menu_model) {
diff --git a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h
index 586da13dd..6c4b6b6 100644
--- a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h
+++ b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h
@@ -43,6 +43,7 @@
 
  private:
   // TabStripUI::Embedder:
+  void CloseContainer() override;
   void ShowContextMenuAtPoint(
       gfx::Point point,
       std::unique_ptr<ui::MenuModel> menu_model) override;
diff --git a/chrome/browser/ui/views/global_error_bubble_view.cc b/chrome/browser/ui/views/global_error_bubble_view.cc
index 1253beb..ad9ec77 100644
--- a/chrome/browser/ui/views/global_error_bubble_view.cc
+++ b/chrome/browser/ui/views/global_error_bubble_view.cc
@@ -151,21 +151,6 @@
   set_close_on_deactivate(error_->ShouldCloseOnDeactivate());
 }
 
-void GlobalErrorBubbleView::UpdateButton(views::LabelButton* button,
-                                         ui::DialogButton type) {
-  if (error_) {
-    // UpdateButton can result in calls back in to GlobalErrorBubbleView,
-    // possibly accessing |error_|.
-    BubbleDialogDelegateView::UpdateButton(button, type);
-    if (type == ui::DIALOG_BUTTON_OK &&
-        error_->ShouldAddElevationIconToAcceptButton()) {
-      elevation_icon_setter_ = std::make_unique<ElevationIconSetter>(
-          button, base::BindOnce(&GlobalErrorBubbleView::SizeToContents,
-                                 base::Unretained(this)));
-    }
-  }
-}
-
 bool GlobalErrorBubbleView::ShouldShowCloseButton() const {
   return error_ && error_->ShouldShowCloseButton();
 }
@@ -198,6 +183,15 @@
   return view;
 }
 
+void GlobalErrorBubbleView::OnDialogInitialized() {
+  views::LabelButton* ok_button = GetOkButton();
+  if (ok_button && error_ && error_->ShouldAddElevationIconToAcceptButton()) {
+    elevation_icon_setter_ = std::make_unique<ElevationIconSetter>(
+        ok_button, base::BindOnce(&GlobalErrorBubbleView::SizeToContents,
+                                  base::Unretained(this)));
+  }
+}
+
 bool GlobalErrorBubbleView::Cancel() {
   if (error_)
     error_->BubbleViewCancelButtonPressed(browser_);
diff --git a/chrome/browser/ui/views/global_error_bubble_view.h b/chrome/browser/ui/views/global_error_bubble_view.h
index 254cd6e..961f053 100644
--- a/chrome/browser/ui/views/global_error_bubble_view.h
+++ b/chrome/browser/ui/views/global_error_bubble_view.h
@@ -36,10 +36,10 @@
   // views::BubbleDialogDelegateView implementation.
   void Init() override;
   bool ShouldShowCloseButton() const override;
-  void UpdateButton(views::LabelButton* button, ui::DialogButton type) override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   int GetDialogButtons() const override;
   std::unique_ptr<views::View> CreateExtraView() override;
+  void OnDialogInitialized() override;
   bool Cancel() override;
   bool Accept() override;
   bool Close() override;
diff --git a/chrome/browser/ui/views/global_error_bubble_view_unittest.cc b/chrome/browser/ui/views/global_error_bubble_view_unittest.cc
index b1030197..166b827 100644
--- a/chrome/browser/ui/views/global_error_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/global_error_bubble_view_unittest.cc
@@ -107,13 +107,6 @@
   view_->WindowClosing();
 
   EXPECT_CALL(*mock_global_error_with_standard_bubble_,
-              GetBubbleViewAcceptButtonLabel());
-  EXPECT_CALL(*mock_global_error_with_standard_bubble_,
-              ShouldAddElevationIconToAcceptButton())
-      .WillOnce(Return(false));
-  view_->UpdateButton(&button_, ui::DIALOG_BUTTON_OK);
-
-  EXPECT_CALL(*mock_global_error_with_standard_bubble_,
               ShouldShowCloseButton());
   view_->ShouldShowCloseButton();
 
@@ -142,7 +135,6 @@
   view_->GetWindowTitle();
   view_->WindowClosing();
 
-  view_->UpdateButton(&button_, ui::DIALOG_BUTTON_OK);
   view_->ShouldShowCloseButton();
 
   EXPECT_EQ(base::string16(),
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view.h b/chrome/browser/ui/views/global_media_controls/media_dialog_view.h
index cd72469..4dc7bb2 100644
--- a/chrome/browser/ui/views/global_media_controls/media_dialog_view.h
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view.h
@@ -50,6 +50,7 @@
   // MediaNotificationContainerObserver implementation.
   void OnContainerExpanded(bool expanded) override;
   void OnContainerMetadataChanged() override;
+  void OnContainerClicked(const std::string& id) override {}
   void OnContainerDismissed(const std::string& id) override {}
   void OnContainerDestroyed(const std::string& id) override;
 
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
index add4e1c..1b2c031b 100644
--- a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
@@ -201,19 +201,19 @@
   void StartPlayback() {
     // The test HTML files used in these tests contain "play()" functions that
     // play the video.
-    GetWebContents()->GetMainFrame()->ExecuteJavaScriptForTests(
+    GetActiveWebContents()->GetMainFrame()->ExecuteJavaScriptForTests(
         base::ASCIIToUTF16("play()"), base::NullCallback());
   }
 
   void WaitForStart() {
     content::MediaStartStopObserver observer(
-        GetWebContents(), content::MediaStartStopObserver::Type::kStart);
+        GetActiveWebContents(), content::MediaStartStopObserver::Type::kStart);
     observer.Wait();
   }
 
   void WaitForStop() {
     content::MediaStartStopObserver observer(
-        GetWebContents(), content::MediaStartStopObserver::Type::kStop);
+        GetActiveWebContents(), content::MediaStartStopObserver::Type::kStop);
     observer.Wait();
   }
 
@@ -240,6 +240,18 @@
     ClickButton(GetButtonForAction(MediaSessionAction::kPlay));
   }
 
+  void ClickNotificationByTitle(const base::string16& title) {
+    ASSERT_TRUE(MediaDialogView::IsShowing());
+    MediaNotificationContainerImplView* notification =
+        GetNotificationByTitle(title);
+    ASSERT_NE(nullptr, notification);
+    ClickButton(notification);
+  }
+
+  content::WebContents* GetActiveWebContents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
  private:
   void ClickButton(views::Button* button) {
     base::RunLoop closure_loop;
@@ -275,8 +287,18 @@
     return nullptr;
   }
 
-  content::WebContents* GetWebContents() {
-    return browser()->tab_strip_model()->GetActiveWebContents();
+  // Finds a MediaNotificationContainerImplView by title.
+  MediaNotificationContainerImplView* GetNotificationByTitle(
+      const base::string16& title) {
+    for (const auto notification_pair :
+         MediaDialogView::GetDialogViewForTesting()
+             ->GetNotificationsForTesting()) {
+      const media_message_center::MediaNotificationView* view =
+          notification_pair.second->view_for_testing();
+      if (view->title_label_for_testing()->GetText() == title)
+        return notification_pair.second;
+    }
+    return nullptr;
   }
 
   base::test::ScopedFeatureList feature_list_;
@@ -350,3 +372,39 @@
   WaitForDialogToContainText(base::ASCIIToUTF16("Different Title"));
   WaitForDialogToContainText(base::ASCIIToUTF16("Another Artist"));
 }
+
+IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,
+                       ClickingOnNotificationGoesBackToTab) {
+  // Open a tab and play media.
+  OpenTestURL();
+  StartPlayback();
+  WaitForStart();
+
+  // Pointer to the first tab.
+  content::WebContents* first_web_contents = GetActiveWebContents();
+
+  // Open another tab and play different media.
+  OpenDifferentMetadataURLInNewTab();
+  StartPlayback();
+  WaitForStart();
+
+  // Now the active web contents is the second tab.
+  content::WebContents* second_web_contents = GetActiveWebContents();
+  ASSERT_NE(first_web_contents, second_web_contents);
+
+  // Open the media dialog.
+  ClickToolbarIcon();
+  WaitForDialogOpened();
+  EXPECT_TRUE(IsDialogVisible());
+
+  // Wait for the dialog to be populated.
+  WaitForDialogToContainText(base::ASCIIToUTF16("Big Buck Bunny"));
+  WaitForDialogToContainText(base::ASCIIToUTF16("Different Title"));
+
+  // The second tab should be the active tab.
+  EXPECT_EQ(second_web_contents, GetActiveWebContents());
+
+  // Clicking the first notification should make the first tab active.
+  ClickNotificationByTitle(base::ASCIIToUTF16("Big Buck Bunny"));
+  EXPECT_EQ(first_web_contents, GetActiveWebContents());
+}
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc
index b2f20a9e..4ecb230d 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc
@@ -57,12 +57,16 @@
 MediaNotificationContainerImplView::MediaNotificationContainerImplView(
     const std::string& id,
     base::WeakPtr<media_message_center::MediaNotificationItem> item)
-    : id_(id),
+    : views::Button(this),
+      id_(id),
       foreground_color_(kDefaultForegroundColor),
       background_color_(kDefaultBackgroundColor) {
   SetLayoutManager(std::make_unique<views::FillLayout>());
-
   SetPreferredSize(kNormalSize);
+  set_notify_enter_exit_on_child(true);
+  SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
+  SetTooltipText(
+      l10n_util::GetStringUTF16(IDS_GLOBAL_MEDIA_CONTROLS_BACK_TO_TAB));
 
   swipeable_container_ = std::make_unique<views::View>();
   swipeable_container_->set_owned_by_client();
@@ -76,7 +80,7 @@
   dismiss_button_container_->SetPreferredSize(kDismissButtonSize);
   dismiss_button_container_->SetLayoutManager(
       std::make_unique<views::FillLayout>());
-  dismiss_button_container_->SetVisible(true);
+  dismiss_button_container_->SetVisible(false);
 
   auto dismiss_button = std::make_unique<DismissButton>(this);
   dismiss_button->SetPreferredSize(kDismissButtonSize);
@@ -88,7 +92,8 @@
   UpdateDismissButtonIcon();
 
   view_ = std::make_unique<media_message_center::MediaNotificationView>(
-      this, std::move(item), dismiss_button_container_.get(), base::string16());
+      this, std::move(item), dismiss_button_container_.get(), base::string16(),
+      kWidth);
   view_->set_owned_by_client();
   ForceExpandedState();
 
@@ -103,6 +108,32 @@
     observer.OnContainerDestroyed(id_);
 }
 
+void MediaNotificationContainerImplView::AddedToWidget() {
+  if (GetFocusManager())
+    GetFocusManager()->AddFocusChangeListener(this);
+}
+
+void MediaNotificationContainerImplView::RemovedFromWidget() {
+  if (GetFocusManager())
+    GetFocusManager()->RemoveFocusChangeListener(this);
+}
+
+void MediaNotificationContainerImplView::OnMouseEntered(
+    const ui::MouseEvent& event) {
+  UpdateDismissButtonVisibility();
+}
+
+void MediaNotificationContainerImplView::OnMouseExited(
+    const ui::MouseEvent& event) {
+  UpdateDismissButtonVisibility();
+}
+
+void MediaNotificationContainerImplView::OnDidChangeFocus(
+    views::View* focused_before,
+    views::View* focused_now) {
+  UpdateDismissButtonVisibility();
+}
+
 void MediaNotificationContainerImplView::OnExpanded(bool expanded) {
   SetPreferredSize(expanded ? kExpandedSize : kNormalSize);
   PreferredSizeChanged();
@@ -111,13 +142,6 @@
     observer.OnContainerExpanded(expanded);
 }
 
-void MediaNotificationContainerImplView::OnMediaSessionInfoChanged(
-    const media_session::mojom::MediaSessionInfoPtr& session_info) {
-  dismiss_button_container_->SetVisible(
-      !session_info || session_info->playback_state !=
-                           media_session::mojom::MediaPlaybackState::kPlaying);
-}
-
 void MediaNotificationContainerImplView::OnMediaSessionMetadataChanged() {
   for (auto& observer : observers_)
     observer.OnContainerMetadataChanged();
@@ -159,8 +183,14 @@
 
 void MediaNotificationContainerImplView::ButtonPressed(views::Button* sender,
                                                        const ui::Event& event) {
-  DCHECK_EQ(dismiss_button_, sender);
-  DismissNotification();
+  if (sender == dismiss_button_) {
+    DismissNotification();
+  } else if (sender == this) {
+    for (auto& observer : observers_)
+      observer.OnContainerClicked(id_);
+  } else {
+    NOTREACHED();
+  }
 }
 
 void MediaNotificationContainerImplView::AddObserver(
@@ -194,6 +224,17 @@
       background_color_, kDismissButtonBackgroundRadius));
 }
 
+void MediaNotificationContainerImplView::UpdateDismissButtonVisibility() {
+  bool has_focus = false;
+  if (GetFocusManager()) {
+    views::View* focused_view = GetFocusManager()->GetFocusedView();
+    if (focused_view)
+      has_focus = Contains(focused_view);
+  }
+
+  dismiss_button_container_->SetVisible(IsMouseHovered() || has_focus);
+}
+
 void MediaNotificationContainerImplView::DismissNotification() {
   for (auto& observer : observers_)
     observer.OnContainerDismissed(id_);
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h
index 840c7fca..439ce004 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h
@@ -14,7 +14,7 @@
 #include "components/media_message_center/media_notification_view.h"
 #include "ui/views/animation/slide_out_controller_delegate.h"
 #include "ui/views/controls/button/button.h"
-#include "ui/views/view.h"
+#include "ui/views/focus/focus_manager.h"
 
 namespace media_message_center {
 class MediaNotificationItem;
@@ -30,21 +30,34 @@
 // within the MediaDialogView. The media notification shows metadata for a media
 // session and can control playback.
 class MediaNotificationContainerImplView
-    : public views::View,
+    : public views::Button,
       public media_message_center::MediaNotificationContainer,
       public MediaNotificationContainerImpl,
       public views::SlideOutControllerDelegate,
-      public views::ButtonListener {
+      public views::ButtonListener,
+      public views::FocusChangeListener {
  public:
   MediaNotificationContainerImplView(
       const std::string& id,
       base::WeakPtr<media_message_center::MediaNotificationItem> item);
   ~MediaNotificationContainerImplView() override;
 
+  // views::Button:
+  void AddedToWidget() override;
+  void RemovedFromWidget() override;
+  void OnMouseEntered(const ui::MouseEvent& event) override;
+  void OnMouseExited(const ui::MouseEvent& event) override;
+
+  // views::FocusChangeListener:
+  void OnWillChangeFocus(views::View* focused_before,
+                         views::View* focused_now) override {}
+  void OnDidChangeFocus(views::View* focused_before,
+                        views::View* focused_now) override;
+
   // media_message_center::MediaNotificationContainer:
   void OnExpanded(bool expanded) override;
   void OnMediaSessionInfoChanged(
-      const media_session::mojom::MediaSessionInfoPtr& session_info) override;
+      const media_session::mojom::MediaSessionInfoPtr& session_info) override {}
   void OnMediaSessionMetadataChanged() override;
   void OnVisibleActionsChanged(
       const std::set<media_session::mojom::MediaSessionAction>& actions)
@@ -78,6 +91,8 @@
 
   void UpdateDismissButtonBackground();
 
+  void UpdateDismissButtonVisibility();
+
   void DismissNotification();
 
   // Updates the forced expanded state of |view_|.
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view_unittest.cc b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view_unittest.cc
index 2a3a04b..70221b7 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view_unittest.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view_unittest.cc
@@ -5,12 +5,17 @@
 #include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "chrome/browser/ui/global_media_controls/media_notification_container_observer.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "ui/display/test/scoped_screen_override.h"
+#include "ui/display/test/test_screen.h"
 #include "ui/events/base_event_utils.h"
+#include "ui/events/test/event_generator.h"
 #include "ui/views/test/button_test_api.h"
 #include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget_utils.h"
 
 using media_session::mojom::MediaPlaybackState;
 using media_session::mojom::MediaSessionAction;
@@ -30,6 +35,7 @@
   // MediaNotificationContainerObserver implementation.
   MOCK_METHOD1(OnContainerExpanded, void(bool expanded));
   MOCK_METHOD0(OnContainerMetadataChanged, void());
+  MOCK_METHOD1(OnContainerClicked, void(const std::string& id));
   MOCK_METHOD1(OnContainerDismissed, void(const std::string& id));
   MOCK_METHOD1(OnContainerDestroyed, void(const std::string& id));
 
@@ -37,11 +43,28 @@
   DISALLOW_COPY_AND_ASSIGN(MockMediaNotificationContainerObserver);
 };
 
+// Fake display::Screen implementation that allows us to set a cursor location.
+class FakeCursorLocationScreen : public display::test::TestScreen {
+ public:
+  FakeCursorLocationScreen() = default;
+  ~FakeCursorLocationScreen() override = default;
+
+  void SetCursorScreenPoint(gfx::Point point) { cursor_position_ = point; }
+
+  // display::test::TestScreen implementation.
+  gfx::Point GetCursorScreenPoint() override { return cursor_position_; }
+
+ private:
+  gfx::Point cursor_position_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeCursorLocationScreen);
+};
+
 }  // anonymous namespace
 
 class MediaNotificationContainerImplViewTest : public views::ViewsTestBase {
  public:
-  MediaNotificationContainerImplViewTest() = default;
+  MediaNotificationContainerImplViewTest() : screen_override_(&fake_screen_) {}
   ~MediaNotificationContainerImplViewTest() override = default;
 
   // ViewsTestBase:
@@ -53,14 +76,12 @@
     params.bounds = gfx::Rect(400, 300);
     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     widget_.Init(std::move(params));
-    views::View* container_view = new views::View();
-    widget_.SetContentsView(container_view);
 
     auto notification_container =
         std::make_unique<MediaNotificationContainerImplView>(
             kTestNotificationId, nullptr);
-    notification_container_ =
-        container_view->AddChildView(std::move(notification_container));
+    notification_container_ = notification_container.get();
+    widget_.SetContentsView(notification_container.release());
 
     observer_ = std::make_unique<MockMediaNotificationContainerObserver>();
     notification_container_->AddObserver(observer_.get());
@@ -84,12 +105,53 @@
 
   bool IsDismissButtonVisible() { return GetDismissButton()->IsDrawn(); }
 
+  void SimulateHoverOverContainer() {
+    fake_screen_.SetCursorScreenPoint(
+        notification_container_->GetBoundsInScreen().CenterPoint());
+
+    ui::MouseEvent event(ui::ET_MOUSE_ENTERED, gfx::Point(), gfx::Point(),
+                         ui::EventTimeForNow(), 0, 0);
+    notification_container_->OnMouseEntered(event);
+  }
+
+  void SimulateNotHoveringOverContainer() {
+    gfx::Rect container_bounds = notification_container_->GetBoundsInScreen();
+    gfx::Point point_outside_container =
+        container_bounds.bottom_right() + gfx::Vector2d(1, 1);
+    fake_screen_.SetCursorScreenPoint(point_outside_container);
+
+    ui::MouseEvent event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
+                         ui::EventTimeForNow(), 0, 0);
+    notification_container_->OnMouseExited(event);
+  }
+
+  void SimulateContainerClicked() {
+    ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                         ui::EventTimeForNow(), 0, 0);
+    views::test::ButtonTestApi(notification_container_).NotifyClick(event);
+  }
+
   void SimulateDismissButtonClicked() {
     ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                          ui::EventTimeForNow(), 0, 0);
     views::test::ButtonTestApi(GetDismissButton()).NotifyClick(event);
   }
 
+  void SimulatePressingDismissButtonWithKeyboard() {
+    GetFocusManager()->SetFocusedView(
+        notification_container_->GetDismissButtonForTesting());
+
+// On Mac OS, we need to use the space bar to press a button.
+#if defined(OS_MACOSX)
+    ui::KeyboardCode button_press_keycode = ui::VKEY_SPACE;
+#else
+    ui::KeyboardCode button_press_keycode = ui::VKEY_RETURN;
+#endif  // defined(OS_MACOSX)
+
+    ui::test::EventGenerator generator(GetRootWindow(&widget_));
+    generator.PressKey(button_press_keycode, 0);
+  }
+
   void SimulateSessionPlaying() { SimulateSessionInfo(true); }
 
   void SimulateSessionPaused() { SimulateSessionInfo(false); }
@@ -133,6 +195,10 @@
     GetView()->UpdateWithMediaArtwork(gfx::ImageSkia());
   }
 
+  views::FocusManager* GetFocusManager() {
+    return notification_container_->GetFocusManager();
+  }
+
   MockMediaNotificationContainerObserver& observer() { return *observer_; }
 
   MediaNotificationContainerImplView* notification_container() {
@@ -179,6 +245,9 @@
   // Set of actions currently enabled.
   std::set<MediaSessionAction> actions_;
 
+  FakeCursorLocationScreen fake_screen_;
+  display::test::ScopedScreenOverride screen_override_;
+
   DISALLOW_COPY_AND_ASSIGN(MediaNotificationContainerImplViewTest);
 };
 
@@ -188,17 +257,57 @@
 }
 
 TEST_F(MediaNotificationContainerImplViewTest, ClickToDismiss) {
-  // We don't show the dismiss button when playing.
-  SimulateSessionPlaying();
+  // Ensure that the mouse is not over the container and that nothing is
+  // focused. The dismiss button should not be visible.
+  SimulateNotHoveringOverContainer();
+  ASSERT_EQ(nullptr, GetFocusManager()->GetFocusedView());
+  ASSERT_FALSE(notification_container()->IsMouseHovered());
   EXPECT_FALSE(IsDismissButtonVisible());
 
-  // We only show it when paused.
-  SimulateSessionPaused();
+  // Hovering over the notification should show the dismiss button.
+  SimulateHoverOverContainer();
+  EXPECT_TRUE(IsDismissButtonVisible());
+
+  // Moving the mouse away from the notification should hide the dismiss button.
+  SimulateNotHoveringOverContainer();
+  EXPECT_FALSE(IsDismissButtonVisible());
+
+  // Moving the mouse back over the notification should re-show it.
+  SimulateHoverOverContainer();
   EXPECT_TRUE(IsDismissButtonVisible());
 
   // Clicking it should inform observers that we've been dismissed.
   EXPECT_CALL(observer(), OnContainerDismissed(kTestNotificationId));
   SimulateDismissButtonClicked();
+  testing::Mock::VerifyAndClearExpectations(&observer());
+}
+
+TEST_F(MediaNotificationContainerImplViewTest, KeyboardToDismiss) {
+  // Ensure that the mouse is not over the container and that nothing is
+  // focused. The dismiss button should not be visible.
+  SimulateNotHoveringOverContainer();
+  ASSERT_EQ(nullptr, GetFocusManager()->GetFocusedView());
+  ASSERT_FALSE(notification_container()->IsMouseHovered());
+  EXPECT_FALSE(IsDismissButtonVisible());
+
+  // When the notification receives keyboard focus, the dismiss button should be
+  // visible.
+  GetFocusManager()->SetFocusedView(notification_container());
+  EXPECT_TRUE(IsDismissButtonVisible());
+
+  // When the notification loses keyboard focus, the dismiss button should be
+  // hidden.
+  GetFocusManager()->SetFocusedView(nullptr);
+  EXPECT_FALSE(IsDismissButtonVisible());
+
+  // If it gets focus again, it should re-show the dismiss button.
+  GetFocusManager()->SetFocusedView(notification_container());
+  EXPECT_TRUE(IsDismissButtonVisible());
+
+  // Clicking it should inform observers that we've been dismissed.
+  EXPECT_CALL(observer(), OnContainerDismissed(kTestNotificationId));
+  SimulatePressingDismissButtonWithKeyboard();
+  testing::Mock::VerifyAndClearExpectations(&observer());
 }
 
 TEST_F(MediaNotificationContainerImplViewTest, ForceExpandedState) {
@@ -244,3 +353,8 @@
   container.reset();
   testing::Mock::VerifyAndClearExpectations(&observer);
 }
+
+TEST_F(MediaNotificationContainerImplViewTest, SendsClicks) {
+  EXPECT_CALL(observer(), OnContainerClicked(kTestNotificationId));
+  SimulateContainerClicked();
+}
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_list_view.cc b/chrome/browser/ui/views/global_media_controls/media_notification_list_view.cc
index 3957aba..96b8e74 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_list_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_list_view.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/global_media_controls/media_notification_list_view.h"
 
 #include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
 #include "ui/views/layout/box_layout.h"
 
@@ -12,6 +13,19 @@
 
 constexpr int kMediaListMaxHeight = 478;
 
+// Color for border between notifications.
+constexpr SkColor kMediaListSeparatorColor = SkColorSetA(SK_ColorBLACK, 31);
+
+// Thickness of separator border.
+constexpr int kMediaListSeparatorThickness = 2;
+
+std::unique_ptr<views::Border> CreateMediaListSeparatorBorder() {
+  return views::CreateSolidSidedBorder(/*top=*/kMediaListSeparatorThickness,
+                                       /*left=*/0,
+                                       /*bottom=*/0,
+                                       /*right=*/0, kMediaListSeparatorColor);
+}
+
 }  // anonymous namespace
 
 MediaNotificationListView::MediaNotificationListView() {
@@ -35,6 +49,11 @@
   DCHECK(!base::Contains(notifications_, id));
   DCHECK_NE(nullptr, notification.get());
 
+  // If this isn't the first notification, then create a top-sided separator
+  // border.
+  if (!notifications_.empty())
+    notification->SetBorder(CreateMediaListSeparatorBorder());
+
   notifications_[id] = contents()->AddChildView(std::move(notification));
 
   contents()->InvalidateLayout();
@@ -45,7 +64,18 @@
   if (!base::Contains(notifications_, id))
     return;
 
+  // If we're removing the topmost notification and there are others, then we
+  // need to remove the top-sided separator border from the new topmost
+  // notification.
+  if (contents()->children().size() > 1 &&
+      contents()->children().at(0) == notifications_[id]) {
+    contents()->children().at(1)->SetBorder(nullptr);
+  }
+
+  // Remove the notification. Note that since |RemoveChildView()| does not
+  // delete the notification, we must do so manually here.
   contents()->RemoveChildView(notifications_[id]);
+  delete notifications_[id];
   notifications_.erase(id);
 
   contents()->InvalidateLayout();
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_list_view_unittest.cc b/chrome/browser/ui/views/global_media_controls/media_notification_list_view_unittest.cc
new file mode 100644
index 0000000..d9c757b
--- /dev/null
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_list_view_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/global_media_controls/media_notification_list_view.h"
+
+#include <string>
+
+#include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h"
+#include "ui/views/test/views_test_base.h"
+
+namespace {
+
+// Test IDs for notifications.
+const char kTestNotificationId1[] = "testid1";
+const char kTestNotificationId2[] = "testid2";
+const char kTestNotificationId3[] = "testid3";
+
+}  // anonymous namespace
+
+class MediaNotificationListViewTest : public views::ViewsTestBase {
+ public:
+  MediaNotificationListViewTest() = default;
+  ~MediaNotificationListViewTest() override = default;
+
+  // ViewsTestBase:
+  void SetUp() override {
+    ViewsTestBase::SetUp();
+
+    views::Widget::InitParams params =
+        CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+    params.bounds = gfx::Rect(400, 400);
+    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+    widget_.Init(std::move(params));
+
+    list_view_ = new MediaNotificationListView();
+    widget_.SetContentsView(list_view_);
+
+    widget_.Show();
+  }
+
+  void TearDown() override {
+    widget_.Close();
+    ViewsTestBase::TearDown();
+  }
+
+  void ShowNotification(const std::string& id) {
+    list_view_->ShowNotification(
+        id, std::make_unique<MediaNotificationContainerImplView>(id, nullptr));
+  }
+
+  void HideNotification(const std::string& id) {
+    list_view_->HideNotification(id);
+  }
+
+  MediaNotificationListView* list_view() { return list_view_; }
+
+ private:
+  views::Widget widget_;
+  MediaNotificationListView* list_view_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaNotificationListViewTest);
+};
+
+TEST_F(MediaNotificationListViewTest, NoSeparatorForOneNotification) {
+  // Show a single notification.
+  ShowNotification(kTestNotificationId1);
+
+  // There should be just one notification.
+  EXPECT_EQ(1u, list_view()->notifications_for_testing().size());
+
+  // Since there's only one, there should be no separator line.
+  EXPECT_EQ(nullptr, list_view()
+                         ->notifications_for_testing()
+                         .at(kTestNotificationId1)
+                         ->border());
+}
+
+TEST_F(MediaNotificationListViewTest, SeparatorBetweenNotifications) {
+  // Show two notifications.
+  ShowNotification(kTestNotificationId1);
+  ShowNotification(kTestNotificationId2);
+
+  // There should be two notifications.
+  EXPECT_EQ(2u, list_view()->notifications_for_testing().size());
+
+  // There should be a separator between them. Since the separators are
+  // top-sided, the bottom notification should have one.
+  EXPECT_EQ(nullptr, list_view()
+                         ->notifications_for_testing()
+                         .at(kTestNotificationId1)
+                         ->border());
+  EXPECT_NE(nullptr, list_view()
+                         ->notifications_for_testing()
+                         .at(kTestNotificationId2)
+                         ->border());
+}
+
+TEST_F(MediaNotificationListViewTest, SeparatorRemovedWhenNotificationRemoved) {
+  // Show three notifications.
+  ShowNotification(kTestNotificationId1);
+  ShowNotification(kTestNotificationId2);
+  ShowNotification(kTestNotificationId3);
+
+  // There should be three notifications.
+  EXPECT_EQ(3u, list_view()->notifications_for_testing().size());
+
+  // There should be separators.
+  EXPECT_EQ(nullptr, list_view()
+                         ->notifications_for_testing()
+                         .at(kTestNotificationId1)
+                         ->border());
+  EXPECT_NE(nullptr, list_view()
+                         ->notifications_for_testing()
+                         .at(kTestNotificationId2)
+                         ->border());
+  EXPECT_NE(nullptr, list_view()
+                         ->notifications_for_testing()
+                         .at(kTestNotificationId3)
+                         ->border());
+
+  // Remove the topmost notification.
+  HideNotification(kTestNotificationId1);
+
+  // There should be two notifications.
+  EXPECT_EQ(2u, list_view()->notifications_for_testing().size());
+
+  // The new top notification should have lost its top separator.
+  EXPECT_EQ(nullptr, list_view()
+                         ->notifications_for_testing()
+                         .at(kTestNotificationId2)
+                         ->border());
+  EXPECT_NE(nullptr, list_view()
+                         ->notifications_for_testing()
+                         .at(kTestNotificationId3)
+                         ->border());
+}
diff --git a/chrome/browser/ui/views/importer/import_lock_dialog_view.cc b/chrome/browser/ui/views/importer/import_lock_dialog_view.cc
index 287d11e5..b6d00e9 100644
--- a/chrome/browser/ui/views/importer/import_lock_dialog_view.cc
+++ b/chrome/browser/ui/views/importer/import_lock_dialog_view.cc
@@ -46,6 +46,9 @@
 ImportLockDialogView::ImportLockDialogView(
     const base::Callback<void(bool)>& callback)
     : callback_(callback) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK, l10n_util::GetStringUTF16(IDS_IMPORTER_LOCK_OK));
+
   SetLayoutManager(std::make_unique<views::FillLayout>());
   views::Label* description_label =
       new views::Label(l10n_util::GetStringUTF16(IDS_IMPORTER_LOCK_TEXT));
@@ -67,13 +70,6 @@
   return gfx::Size(width, GetHeightForWidth(width));
 }
 
-base::string16 ImportLockDialogView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (button == ui::DIALOG_BUTTON_OK)
-    return l10n_util::GetStringUTF16(IDS_IMPORTER_LOCK_OK);
-  return DialogDelegateView::GetDialogButtonLabel(button);
-}
-
 base::string16 ImportLockDialogView::GetWindowTitle() const {
   return l10n_util::GetStringUTF16(IDS_IMPORTER_LOCK_TITLE);
 }
diff --git a/chrome/browser/ui/views/importer/import_lock_dialog_view.h b/chrome/browser/ui/views/importer/import_lock_dialog_view.h
index bef55da..23aeebc 100644
--- a/chrome/browser/ui/views/importer/import_lock_dialog_view.h
+++ b/chrome/browser/ui/views/importer/import_lock_dialog_view.h
@@ -25,7 +25,6 @@
   gfx::Size CalculatePreferredSize() const override;
 
   // views::DialogDelegate:
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   base::string16 GetWindowTitle() const override;
   bool Accept() override;
   bool Cancel() override;
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.cc b/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.cc
index 607bf2a5..a524aec 100644
--- a/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.cc
+++ b/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.cc
@@ -49,15 +49,6 @@
       IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_TITLE);
 }
 
-base::string16
-NativeFileSystemDirectoryAccessConfirmationView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (button == ui::DIALOG_BUTTON_OK)
-    return l10n_util::GetStringUTF16(
-        IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_ALLOW_BUTTON);
-  return l10n_util::GetStringUTF16(IDS_APP_CANCEL);
-}
-
 bool NativeFileSystemDirectoryAccessConfirmationView::ShouldShowCloseButton()
     const {
   return false;
@@ -99,6 +90,10 @@
         const base::FilePath& path,
         base::OnceCallback<void(PermissionAction result)> callback)
     : callback_(std::move(callback)) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(
+          IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_ALLOW_BUTTON));
   const views::LayoutProvider* provider = ChromeLayoutProvider::Get();
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical,
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.h b/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.h
index da659e55..fef9b6a 100644
--- a/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.h
+++ b/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.h
@@ -44,7 +44,6 @@
 
   // views::DialogDelegateView:
   base::string16 GetWindowTitle() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool ShouldShowCloseButton() const override;
   bool Accept() override;
   bool Cancel() override;
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.cc b/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.cc
index c56112d..cc3299e 100644
--- a/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.cc
+++ b/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.cc
@@ -26,6 +26,11 @@
     bool is_directory,
     base::OnceCallback<void(PermissionAction result)> callback)
     : path_(path), callback_(std::move(callback)) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(
+          IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_ALLOW_TEXT));
+
   const views::LayoutProvider* provider = ChromeLayoutProvider::Get();
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical,
@@ -62,14 +67,6 @@
       path_.BaseName().LossyDisplayName());
 }
 
-base::string16 NativeFileSystemPermissionView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (button == ui::DIALOG_BUTTON_OK)
-    return l10n_util::GetStringUTF16(
-        IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_ALLOW_TEXT);
-  return l10n_util::GetStringUTF16(IDS_APP_CANCEL);
-}
-
 bool NativeFileSystemPermissionView::ShouldShowCloseButton() const {
   return false;
 }
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.h b/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.h
index 80104c1..dfa827b 100644
--- a/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.h
+++ b/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.h
@@ -46,7 +46,6 @@
 
   // views::DialogDelegateView:
   base::string16 GetWindowTitle() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool ShouldShowCloseButton() const override;
   bool Accept() override;
   bool Cancel() override;
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.cc b/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.cc
index e870069..e5e21e1 100644
--- a/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.cc
+++ b/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.cc
@@ -48,16 +48,6 @@
   return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
 }
 
-base::string16
-NativeFileSystemRestrictedDirectoryDialogView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (button == ui::DIALOG_BUTTON_OK)
-    return l10n_util::GetStringUTF16(
-        is_directory_ ? IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_BUTTON
-                      : IDS_NATIVE_FILE_SYSTEM_RESTRICTED_FILE_BUTTON);
-  return l10n_util::GetStringUTF16(IDS_APP_CANCEL);
-}
-
 bool NativeFileSystemRestrictedDirectoryDialogView::ShouldShowCloseButton()
     const {
   return false;
@@ -97,6 +87,12 @@
         bool is_directory,
         base::OnceCallback<void(SensitiveDirectoryResult)> callback)
     : is_directory_(is_directory), callback_(std::move(callback)) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(
+          is_directory_ ? IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_BUTTON
+                        : IDS_NATIVE_FILE_SYSTEM_RESTRICTED_FILE_BUTTON));
+
   SetLayoutManager(std::make_unique<views::FillLayout>());
   set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
       views::TEXT, views::TEXT));
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.h b/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.h
index 59962db..dbbf8d8 100644
--- a/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.h
+++ b/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.h
@@ -48,7 +48,6 @@
   // views::DialogDelegateView:
   base::string16 GetWindowTitle() const override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool ShouldShowCloseButton() const override;
   bool Accept() override;
   bool Cancel() override;
@@ -62,7 +61,7 @@
       bool is_directory,
       base::OnceCallback<void(SensitiveDirectoryResult)> callback);
 
-  bool is_directory_;
+  const bool is_directory_;
   base::OnceCallback<void(SensitiveDirectoryResult)> callback_;
 
   DISALLOW_COPY_AND_ASSIGN(NativeFileSystemRestrictedDirectoryDialogView);
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_usage_bubble_view.cc b/chrome/browser/ui/views/native_file_system/native_file_system_usage_bubble_view.cc
index ee39794..05382a6f 100644
--- a/chrome/browser/ui/views/native_file_system/native_file_system_usage_bubble_view.cc
+++ b/chrome/browser/ui/views/native_file_system/native_file_system_usage_bubble_view.cc
@@ -297,7 +297,10 @@
       origin_(origin),
       usage_(std::move(usage)),
       writable_paths_model_(usage_.writable_files, usage_.writable_directories),
-      readable_paths_model_({}, usage_.readable_directories) {}
+      readable_paths_model_({}, usage_.readable_directories) {
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK,
+                                   l10n_util::GetStringUTF16(IDS_DONE));
+}
 
 NativeFileSystemUsageBubbleView::~NativeFileSystemUsageBubbleView() = default;
 
@@ -318,11 +321,6 @@
   return ui::DIALOG_BUTTON_OK;
 }
 
-base::string16 NativeFileSystemUsageBubbleView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return l10n_util::GetStringUTF16(IDS_DONE);
-}
-
 bool NativeFileSystemUsageBubbleView::ShouldShowCloseButton() const {
   return true;
 }
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_usage_bubble_view.h b/chrome/browser/ui/views/native_file_system/native_file_system_usage_bubble_view.h
index 41d2ff2..5665429 100644
--- a/chrome/browser/ui/views/native_file_system/native_file_system_usage_bubble_view.h
+++ b/chrome/browser/ui/views/native_file_system/native_file_system_usage_bubble_view.h
@@ -64,7 +64,6 @@
   // LocationBarBubbleDelegateView:
   base::string16 GetAccessibleWindowTitle() const override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool ShouldShowCloseButton() const override;
   void Init() override;
   void WindowClosing() override;
diff --git a/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc b/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc
index 6362a19..73afcdc 100644
--- a/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc
+++ b/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc
@@ -146,12 +146,6 @@
   return ui::DIALOG_BUTTON_OK;
 }
 
-base::string16 OutdatedUpgradeBubbleView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return l10n_util::GetStringUTF16(auto_update_enabled_ ? IDS_REINSTALL_APP
-                                                        : IDS_REENABLE_UPDATES);
-}
-
 void OutdatedUpgradeBubbleView::Init() {
   SetLayoutManager(std::make_unique<views::FillLayout>());
   auto text_label = std::make_unique<views::Label>(
@@ -174,5 +168,9 @@
     : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
       auto_update_enabled_(auto_update_enabled),
       navigator_(navigator) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(auto_update_enabled_ ? IDS_REINSTALL_APP
+                                                     : IDS_REENABLE_UPDATES));
   chrome::RecordDialogCreation(chrome::DialogIdentifier::OUTDATED_UPGRADE);
 }
diff --git a/chrome/browser/ui/views/outdated_upgrade_bubble_view.h b/chrome/browser/ui/views/outdated_upgrade_bubble_view.h
index 19831dca..a38d6aa 100644
--- a/chrome/browser/ui/views/outdated_upgrade_bubble_view.h
+++ b/chrome/browser/ui/views/outdated_upgrade_bubble_view.h
@@ -29,7 +29,6 @@
   bool Accept() override;
   bool Close() override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   void Init() override;
 
  private:
@@ -43,7 +42,7 @@
   bool uma_recorded_ = false;
 
   // Identifies if auto-update is enabled or not.
-  bool auto_update_enabled_;
+  const bool auto_update_enabled_;
 
   // The PageNavigator to use for opening the Download Chrome URL.
   content::PageNavigator* navigator_;
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index a470642..b7f5ff8 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -787,8 +787,16 @@
               PageInfo::SITE_IDENTITY_STATUS_EV_CERT &&
           identity_info.connection_status ==
               PageInfo::SITE_CONNECTION_STATUS_ENCRYPTED) {
-        subtitle_text =
-            base::UTF8ToUTF16(identity_info.identity_status_description);
+        // An EV cert is required to have an organization name, a city
+        // (localityName), and country, but state is "if any".
+        if (!certificate_->subject().organization_names.empty() &&
+            !certificate_->subject().locality_name.empty() &&
+            !certificate_->subject().country_name.empty()) {
+          subtitle_text = l10n_util::GetStringFUTF16(
+              IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_EV_VERIFIED,
+              base::UTF8ToUTF16(certificate_->subject().organization_names[0]),
+              base::UTF8ToUTF16(certificate_->subject().country_name));
+        }
       }
     }
 
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
index 971583c3..640710d4 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
@@ -204,7 +204,6 @@
       // the certificate button subtitle.
       identity.identity_status = PageInfo::SITE_IDENTITY_STATUS_EV_CERT;
       identity.connection_status = PageInfo::SITE_CONNECTION_STATUS_ENCRYPTED;
-      identity.identity_status_description = "Issued to: Thawte Inc [US]";
       scoped_refptr<net::X509Certificate> ev_cert =
           net::X509Certificate::CreateFromBytes(
               reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der));
@@ -367,6 +366,13 @@
     return page_info_bubble_view->certificate_button_->title()->GetText();
   }
 
+  base::string16 GetCertificateButtonSubtitle() const {
+    PageInfoBubbleView* page_info_bubble_view =
+        static_cast<PageInfoBubbleView*>(
+            PageInfoBubbleView::GetPageInfoBubbleForTesting());
+    return page_info_bubble_view->certificate_button_->subtitle()->GetText();
+  }
+
   const base::string16 GetPageInfoBubbleViewDetailText() {
     PageInfoBubbleView* page_info_bubble_view =
         static_cast<PageInfoBubbleView*>(
@@ -745,6 +751,48 @@
                                        invalid_parens));
 }
 
+// Ensure a page that has an EV certificate *and* is blocked by Safe Browsing
+// shows the correct PageInfo UI. Regression test for crbug.com/1014240.
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, MalwareAndEvCert) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.AddDefaultHandlers(
+      base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
+  ASSERT_TRUE(https_server.Start());
+
+  ui_test_utils::NavigateToURL(browser(), https_server.GetURL("/simple.html"));
+
+  // Generate a valid mock EV HTTPS identity, with an EV certificate. Must
+  // match conditions in PageInfoBubbleView::SetIdentityInfo() for setting
+  // the certificate button subtitle.
+  PageInfoUI::IdentityInfo identity;
+  identity.identity_status = PageInfo::SITE_IDENTITY_STATUS_EV_CERT;
+  identity.connection_status = PageInfo::SITE_CONNECTION_STATUS_ENCRYPTED;
+  scoped_refptr<net::X509Certificate> ev_cert =
+      net::X509Certificate::CreateFromBytes(
+          reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der));
+  ASSERT_TRUE(ev_cert);
+  identity.certificate = ev_cert;
+
+  // Have the page also trigger an SB malware warning.
+  identity.safe_browsing_status = PageInfo::SAFE_BROWSING_STATUS_MALWARE;
+
+  OpenPageInfoBubble(browser());
+  SetPageInfoBubbleIdentityInfo(identity);
+
+  views::BubbleDialogDelegateView* page_info =
+      PageInfoBubbleView::GetPageInfoBubbleForTesting();
+
+  // Verify bubble complains of malware...
+  EXPECT_EQ(page_info->GetWindowTitle(),
+            l10n_util::GetStringUTF16(IDS_PAGE_INFO_MALWARE_SUMMARY));
+
+  // ...and has the correct organization details in the Certificate button.
+  EXPECT_EQ(GetCertificateButtonSubtitle(),
+            l10n_util::GetStringFUTF16(
+                IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_EV_VERIFIED,
+                base::UTF8ToUTF16("Thawte Inc"), base::UTF8ToUTF16("US")));
+}
+
 namespace {
 
 // Tracks focus of an arbitrary UI element.
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index de55789..817f97f 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/hover_button.h"
 #include "chrome/browser/ui/views/page_info/chosen_object_view.h"
+#include "chrome/browser/ui/views/page_info/page_info_hover_button.h"
 #include "chrome/browser/ui/views/page_info/permission_selector_row.h"
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
@@ -31,8 +32,10 @@
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_web_contents_factory.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "net/cert/cert_status_flags.h"
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "net/test/cert_test_util.h"
+#include "net/test/test_certificate_data.h"
 #include "net/test/test_data_directory.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
@@ -129,6 +132,12 @@
     CreateView();
   }
 
+  base::string16 GetCertificateButtonSubtitleText() const {
+    EXPECT_TRUE(view_->certificate_button_);
+    EXPECT_TRUE(view_->certificate_button_->subtitle());
+    return view_->certificate_button_->subtitle()->GetText();
+  }
+
   void WaitForBubbleClose() { run_loop_.Run(); }
 
  private:
@@ -711,3 +720,41 @@
             api_->closed_reason());
 }
 
+TEST_F(PageInfoBubbleViewTest, CertificateButtonShowsEvCertDetails) {
+  content::BrowserSideNavigationSetUp();
+  SecurityStateTabHelper::CreateForWebContents(
+      web_contents_helper_.web_contents());
+  std::unique_ptr<content::NavigationSimulator> navigation =
+      content::NavigationSimulator::CreateRendererInitiated(
+          GURL(kSecureUrl),
+          web_contents_helper_.web_contents()->GetMainFrame());
+  navigation->Start();
+  api_->CreateView();
+
+  // Set up a test SSLInfo so that Page Info sees the connection as secure and
+  // using an EV certificate.
+  uint16_t cipher_suite = 0xc02f;  // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+  int connection_status = 0;
+  net::SSLConnectionStatusSetCipherSuite(cipher_suite, &connection_status);
+  net::SSLConnectionStatusSetVersion(net::SSL_CONNECTION_VERSION_TLS1_2,
+                                     &connection_status);
+  net::SSLInfo ssl_info;
+  ssl_info.connection_status = connection_status;
+  ssl_info.cert = net::X509Certificate::CreateFromBytes(
+      reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der));
+  ASSERT_TRUE(ssl_info.cert);
+  ssl_info.cert_status = net::CERT_STATUS_IS_EV;
+
+  navigation->SetSSLInfo(ssl_info);
+
+  navigation->Commit();
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURE_SUMMARY),
+            api_->GetWindowTitle());
+
+  // The certificate button subtitle should show the EV certificate organization
+  // name and country of incorporation.
+  EXPECT_EQ(l10n_util::GetStringFUTF16(
+                IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_EV_VERIFIED,
+                base::UTF8ToUTF16("Thawte Inc"), base::UTF8ToUTF16("US")),
+            api_->GetCertificateButtonSubtitleText());
+}
diff --git a/chrome/browser/ui/views/page_info/page_info_hover_button.h b/chrome/browser/ui/views/page_info/page_info_hover_button.h
index 03508824..24539d539 100644
--- a/chrome/browser/ui/views/page_info/page_info_hover_button.h
+++ b/chrome/browser/ui/views/page_info/page_info_hover_button.h
@@ -13,6 +13,10 @@
 class ImageSkia;
 }  // namespace gfx
 
+namespace test {
+class PageInfoBubbleViewTestApi;
+}  // namespace test
+
 namespace views {
 class ButtonListener;
 class Label;
@@ -66,6 +70,7 @@
 
  private:
   friend class PageInfoBubbleViewBrowserTest;
+  friend class test::PageInfoBubbleViewTestApi;
 
   void UpdateAccessibleName();
 
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc
index d56289355..7fec7bed 100644
--- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc
@@ -32,6 +32,25 @@
 
 using security_state::SafetyTipStatus;
 
+namespace {
+
+int GetSafetyTipBannerId(security_state::SafetyTipStatus safety_tip_status) {
+  switch (safety_tip_status) {
+    case security_state::SafetyTipStatus::kBadReputation:
+      return IDR_SAFETY_TIP_SUSPICIOUS_ILLUSTRATION;
+    case security_state::SafetyTipStatus::kLookalike:
+      return IDR_SAFETY_TIP_LOOKALIKE_ILLUSTRATION;
+    case security_state::SafetyTipStatus::kBadKeyword:
+    case security_state::SafetyTipStatus::kUnknown:
+    case security_state::SafetyTipStatus::kNone:
+      NOTREACHED();
+  }
+  NOTREACHED();
+  return IDR_SAFETY_TIP_SUSPICIOUS_ILLUSTRATION;
+}
+
+}  // namespace
+
 SafetyTipPageInfoBubbleView::SafetyTipPageInfoBubbleView(
     views::View* anchor_view,
     const gfx::Rect& anchor_rect,
@@ -91,7 +110,15 @@
   bubble_col_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL,
                             1.0, views::GridLayout::USE_PREF, 0, 0);
 
-  // TODO(crbug/996731): Add banner once available. See crrev/c/1816805/7.
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  const gfx::ImageSkia* image =
+      rb.GetNativeImageNamed(GetSafetyTipBannerId(safety_tip_status))
+          .ToImageSkia();
+  auto image_view = std::make_unique<NonAccessibleImageView>();
+  image_view->SetImage(*image);
+  views::BubbleFrameView* frame_view = GetBubbleFrameView();
+  CHECK(frame_view);
+  frame_view->SetHeaderView(std::move(image_view));
 
   auto bottom_view = std::make_unique<views::View>();
   views::GridLayout* bottom_layout =
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc
index c2fa4b7..0a61cfe 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -464,7 +464,7 @@
     return gfx::ImageSkia();
 
   if (!identity_manager->HasPrimaryAccount())
-    return ColoredImageForMenu(kSyncPausedCircleIcon, gfx::kGoogleGrey400);
+    return ColoredImageForMenu(kSyncPausedIcon, gfx::kGoogleGrey500);
 
   const gfx::VectorIcon* icon = nullptr;
   ui::NativeTheme::ColorId color_id;
@@ -472,11 +472,11 @@
   switch (
       sync_ui_util::GetMessagesForAvatarSyncError(profile, &unused, &unused)) {
     case sync_ui_util::NO_SYNC_ERROR:
-      icon = &kSyncCircleIcon;
+      icon = &kSyncIcon;
       color_id = ui::NativeTheme::kColorId_AlertSeverityLow;
       break;
     case sync_ui_util::AUTH_ERROR:
-      icon = &kSyncPausedCircleIcon;
+      icon = &kSyncPausedIcon;
       color_id = ui::NativeTheme::kColorId_ProminentButtonColor;
       break;
     case sync_ui_util::MANAGED_USER_UNRECOVERABLE_ERROR:
@@ -484,7 +484,7 @@
     case sync_ui_util::UPGRADE_CLIENT_ERROR:
     case sync_ui_util::PASSPHRASE_ERROR:
     case sync_ui_util::SETTINGS_UNCONFIRMED_ERROR:
-      icon = &kSyncPausedCircleIcon;
+      icon = &kSyncPausedIcon;
       color_id = ui::NativeTheme::kColorId_AlertSeverityHigh;
       break;
   }
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index d2c9a476..00762b8 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -55,6 +55,7 @@
 constexpr int kIconSize = 16;
 constexpr int kIdentityImageSize = 64;
 constexpr int kMaxImageSize = kIdentityImageSize;
+constexpr int kDefaultVerticalMargin = 12;
 
 // If the bubble is too large to fit on the screen, it still needs to be at
 // least this tall to show one row.
@@ -135,21 +136,6 @@
   return layout;
 }
 
-std::unique_ptr<views::View> CreateBorderedBoxView() {
-  constexpr int kBorderThickness = 1;
-
-  auto bordered_box = std::make_unique<views::View>();
-  bordered_box->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical));
-  bordered_box->SetBorder(views::CreateRoundedRectBorder(
-      kBorderThickness,
-      views::LayoutProvider::Get()->GetCornerRadiusMetric(views::EMPHASIS_HIGH),
-      GetDefaultSeparatorColor()));
-  bordered_box->SetProperty(views::kMarginsKey, gfx::Insets(kMenuEdgeMargin));
-
-  return bordered_box;
-}
-
 std::unique_ptr<views::Button> CreateCircularImageButton(
     views::ButtonListener* listener,
     const gfx::ImageSkia& image,
@@ -356,55 +342,62 @@
                                       const base::string16& description,
                                       const base::string16& clickable_text,
                                       base::RepeatingClosure action) {
-  constexpr int kVerticalSpacing = 8;
   constexpr int kIconSize = 16;
+  const int kDescriptionIconSpacing =
+      ChromeLayoutProvider::Get()->GetDistanceMetric(
+          views::DISTANCE_RELATED_LABEL_HORIZONTAL);
+  constexpr int kBorderThickness = 1;
+  const int kBorderCornerRadius =
+      views::LayoutProvider::Get()->GetCornerRadiusMetric(views::EMPHASIS_HIGH);
 
   sync_info_container_->RemoveAllChildViews(/*delete_children=*/true);
   sync_info_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical));
-
-  views::LabelButton* button = nullptr;
+      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
+      kDefaultVerticalMargin));
 
   if (description.empty()) {
-    button = sync_info_container_->AddChildView(std::make_unique<HoverButton>(
-        this, SizeImage(icon, kIconSize), clickable_text));
-  } else {
-    // Add icon + description on top.
-    views::View* description_container =
-        sync_info_container_->AddChildView(std::make_unique<views::View>());
-    views::BoxLayout* description_layout =
-        description_container->SetLayoutManager(
-            std::make_unique<views::BoxLayout>(
-                views::BoxLayout::Orientation::kHorizontal,
-                gfx::Insets(kVerticalSpacing, kMenuEdgeMargin),
-                /*between_child_spacing=*/
-                ChromeLayoutProvider::Get()->GetDistanceMetric(
-                    views::DISTANCE_RELATED_LABEL_HORIZONTAL)));
-
-    if (icon.isNull()) {
-      // If there is no image, the description text should be centered.
-      description_layout->set_main_axis_alignment(
-          views::BoxLayout::MainAxisAlignment::kCenter);
-    } else {
-      views::ImageView* icon_view = description_container->AddChildView(
-          std::make_unique<views::ImageView>());
-      icon_view->SetImage(SizeImage(icon, kIconSize));
-    }
-
-    views::Label* label = description_container->AddChildView(
-        std::make_unique<views::Label>(description));
-    label->SetMultiLine(true);
-    label->SetHandlesTooltips(false);
-
-    // Add blue button at the bottom.
-    button = sync_info_container_->AddChildView(
-        views::MdTextButton::CreateSecondaryUiBlueButton(this, clickable_text));
-    button->SetProperty(
-        views::kMarginsKey,
-        gfx::Insets(/*top=*/0, /*left=*/kMenuEdgeMargin,
-                    /*bottom=*/kVerticalSpacing, /*right=*/kMenuEdgeMargin));
+    views::Button* button =
+        sync_info_container_->AddChildView(std::make_unique<HoverButton>(
+            this, SizeImage(icon, kIconSize), clickable_text));
+    RegisterClickAction(button, std::move(action));
+    return;
   }
 
+  // Add padding, rounded border and margins.
+  sync_info_container_->SetBorder(views::CreatePaddedBorder(
+      views::CreateRoundedRectBorder(kBorderThickness, kBorderCornerRadius,
+                                     GetDefaultSeparatorColor()),
+      /*padding=*/gfx::Insets(kDefaultVerticalMargin, kMenuEdgeMargin)));
+  sync_info_container_->SetProperty(
+      views::kMarginsKey, gfx::Insets(kDefaultVerticalMargin, kMenuEdgeMargin));
+
+  // Add icon + description at the top.
+  views::View* description_container =
+      sync_info_container_->AddChildView(std::make_unique<views::View>());
+  views::BoxLayout* description_layout =
+      description_container->SetLayoutManager(
+          std::make_unique<views::BoxLayout>(
+              views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
+              kDescriptionIconSpacing));
+
+  if (icon.isNull()) {
+    // If there is no image, the description is centered.
+    description_layout->set_main_axis_alignment(
+        views::BoxLayout::MainAxisAlignment::kCenter);
+  } else {
+    views::ImageView* icon_view = description_container->AddChildView(
+        std::make_unique<views::ImageView>());
+    icon_view->SetImage(SizeImage(icon, kIconSize));
+  }
+
+  views::Label* label = description_container->AddChildView(
+      std::make_unique<views::Label>(description));
+  label->SetMultiLine(true);
+  label->SetHandlesTooltips(false);
+
+  // Add blue button at the bottom.
+  views::Button* button = sync_info_container_->AddChildView(
+      views::MdTextButton::CreateSecondaryUiBlueButton(this, clickable_text));
   RegisterClickAction(button, std::move(action));
 }
 
@@ -456,27 +449,26 @@
       /*delete_children=*/true);
   profile_mgmt_heading_container_->SetLayoutManager(
       std::make_unique<views::FillLayout>());
+  profile_mgmt_heading_container_->SetBorder(views::CreateEmptyBorder(
+      gfx::Insets(kDefaultVerticalMargin, kMenuEdgeMargin)));
 
   views::Label* label = profile_mgmt_heading_container_->AddChildView(
       std::make_unique<views::Label>(heading, views::style::CONTEXT_LABEL,
                                      STYLE_HINT));
   label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   label->SetHandlesTooltips(false);
-  label->SetBorder(views::CreateEmptyBorder(gfx::Insets(0, kMenuEdgeMargin)));
 }
 
 void ProfileMenuViewBase::AddSelectableProfile(const gfx::ImageSkia& image,
                                                const base::string16& name,
                                                base::RepeatingClosure action) {
-  constexpr int kTopMargin = 8;
   constexpr int kImageSize = 22;
 
   // Initialize layout if this is the first time a button is added.
   if (!selectable_profiles_container_->GetLayoutManager()) {
     selectable_profiles_container_->SetLayoutManager(
         std::make_unique<views::BoxLayout>(
-            views::BoxLayout::Orientation::kVertical,
-            gfx::Insets(kTopMargin, 0, 0, 0)));
+            views::BoxLayout::Orientation::kVertical));
   }
 
   gfx::ImageSkia sized_image = CropCircle(SizeImage(image, kImageSize));
@@ -635,23 +627,23 @@
       views::BoxLayout::Orientation::kVertical));
 
   // Create and add new component containers in the correct order.
-  // First, add the bordered box with the identity and feature buttons.
-  views::View* bordered_box = components->AddChildView(CreateBorderedBoxView());
+  // First, add the parts of the current profile.
   heading_container_ =
-      bordered_box->AddChildView(std::make_unique<views::View>());
+      components->AddChildView(std::make_unique<views::View>());
   identity_info_container_ =
-      bordered_box->AddChildView(std::make_unique<views::View>());
+      components->AddChildView(std::make_unique<views::View>());
   shortcut_features_container_ =
-      bordered_box->AddChildView(std::make_unique<views::View>());
+      components->AddChildView(std::make_unique<views::View>());
   sync_info_container_ =
-      bordered_box->AddChildView(std::make_unique<views::View>());
+      components->AddChildView(std::make_unique<views::View>());
   features_container_ =
-      bordered_box->AddChildView(std::make_unique<views::View>());
-  // Second, add the profile header.
+      components->AddChildView(std::make_unique<views::View>());
+  // Second, add the profile management header.
+  components->AddChildView(new views::Separator());
   auto profile_header = std::make_unique<views::View>();
-  views::BoxLayout* profile_header_layout =
-      profile_header->SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kHorizontal));
+  views::BoxLayout* profile_header_layout = profile_header->SetLayoutManager(
+      CreateBoxLayout(views::BoxLayout::Orientation::kHorizontal,
+                      views::BoxLayout::CrossAxisAlignment::kCenter));
   profile_mgmt_heading_container_ =
       profile_header->AddChildView(std::make_unique<views::View>());
   profile_header_layout->SetFlexForView(profile_mgmt_heading_container_, 1);
@@ -660,7 +652,7 @@
   profile_header_layout->SetFlexForView(
       profile_mgmt_shortcut_features_container_, 0);
   components->AddChildView(std::move(profile_header));
-  // Third, add the profile buttons at the bottom.
+  // Third, add the profile management buttons.
   selectable_profiles_container_ =
       components->AddChildView(std::make_unique<views::View>());
   profile_mgmt_features_container_ =
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
index 56e86e35..a5e7860a 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/secondary_account_helper.h"
+#include "chrome/browser/sync/test/integration/status_change_checker.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
@@ -64,6 +65,37 @@
 
 namespace {
 
+class UnconsentedPrimaryAccountChecker
+    : public StatusChangeChecker,
+      public signin::IdentityManager::Observer {
+ public:
+  explicit UnconsentedPrimaryAccountChecker(
+      signin::IdentityManager* identity_manager)
+      : identity_manager_(identity_manager) {
+    identity_manager_->AddObserver(this);
+  }
+  ~UnconsentedPrimaryAccountChecker() override {
+    identity_manager_->RemoveObserver(this);
+  }
+
+  // StatusChangeChecker overrides:
+  bool IsExitConditionSatisfied() override {
+    return identity_manager_->HasUnconsentedPrimaryAccount();
+  }
+  std::string GetDebugMessage() const override {
+    return "Unconsented primary account checker";
+  }
+
+  // signin::IdentityManager::Observer overrides:
+  void OnUnconsentedPrimaryAccountChanged(
+      const CoreAccountInfo& unconsented_primary_account_info) override {
+    CheckExitCondition();
+  }
+
+ private:
+  signin::IdentityManager* identity_manager_;
+};
+
 Profile* CreateTestingProfile(const base::FilePath& path) {
   base::ScopedAllowBlockingForTesting allow_blocking;
   ProfileManager* profile_manager = g_browser_process->profile_manager();
@@ -856,13 +888,12 @@
         // there are no other buttons at the end.
         ProfileMenuViewBase::ActionableItem::kEditProfileButton};
 
-// TODO(crbug.com/1012167): Flaky.
-PROFILE_MENU_CLICK_TEST(
-    kActionableItems_WithUnconsentedPrimaryAccount,
-    DISABLED_ProfileMenuClickTest_WithUnconsentedPrimaryAccount) {
+PROFILE_MENU_CLICK_TEST(kActionableItems_WithUnconsentedPrimaryAccount,
+                        ProfileMenuClickTest_WithUnconsentedPrimaryAccount) {
   signin::MakeAccountAvailableWithCookies(identity_manager(),
                                           &test_url_loader_factory_,
                                           "user@example.com", "gaia_id");
+  UnconsentedPrimaryAccountChecker(identity_manager()).Wait();
   // Check that the setup was successful.
   ASSERT_FALSE(identity_manager()->HasPrimaryAccount());
   ASSERT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount());
diff --git a/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc b/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc
index c10f544..8fe7d846 100644
--- a/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc
+++ b/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc
@@ -102,8 +102,7 @@
     case safe_browsing::ReusedPasswordAccountType::NON_GAIA_ENTERPRISE:
       return l10n_util::GetStringUTF16(IDS_PAGE_INFO_CHANGE_PASSWORD_BUTTON);
     case safe_browsing::ReusedPasswordAccountType::SAVED_PASSWORD:
-      return l10n_util::GetStringUTF16(
-          IDS_PAGE_INFO_DISMISS_PASSWORD_WARNING_BUTTON);
+      return l10n_util::GetStringUTF16(IDS_CLOSE);
     default:
       return l10n_util::GetStringUTF16(IDS_PAGE_INFO_PROTECT_ACCOUNT_BUTTON);
   }
diff --git a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
index ad1f705..6018692 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
+++ b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
@@ -74,6 +74,13 @@
       confirmed_callback_(std::move(confirmed_callback)),
       advanced_link_(nullptr),
       learn_more_link_(nullptr) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_OK_BUTTON));
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_CANCEL,
+      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_UNDO_BUTTON));
+
   DCHECK(!confirmed_callback_.is_null());
   set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
       views::TEXT, views::TEXT));
@@ -140,13 +147,6 @@
   return advanced_link;
 }
 
-base::string16 OneClickSigninDialogView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return l10n_util::GetStringUTF16(
-      button == ui::DIALOG_BUTTON_OK ? IDS_ONE_CLICK_SIGNIN_DIALOG_OK_BUTTON
-                                     : IDS_ONE_CLICK_SIGNIN_DIALOG_UNDO_BUTTON);
-}
-
 void OneClickSigninDialogView::LinkClicked(views::Link* source,
                                            int event_flags) {
   if (source == learn_more_link_) {
diff --git a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h
index f28f3dc4..13ad21f 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h
+++ b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h
@@ -63,7 +63,6 @@
   ui::ModalType GetModalType() const override;
   void WindowClosing() override;
   std::unique_ptr<views::View> CreateExtraView() override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool Accept() override;
 
   // Overridden from views::LinkListener:
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
index d692a98..6782c6d 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -4,12 +4,15 @@
 
 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
 
+#include <limits>
 #include <utility>
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/command_line.h"
 #include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "build/build_config.h"
 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
@@ -45,6 +48,7 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/peak_gpu_memory_tracker.h"
 #include "content/public/browser/web_contents.h"
 #include "ipc/ipc_message.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
@@ -239,6 +243,14 @@
 
 void BrowserTabStripController::SelectTab(int model_index,
                                           const ui::Event& event) {
+  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);
+          }));
+
   TabStripModel::UserGestureDetails gesture_detail(
       TabStripModel::GestureType::kOther, event.time_stamp());
   TabStripModel::GestureType type = TabStripModel::GestureType::kOther;
@@ -248,6 +260,16 @@
     type = TabStripModel::GestureType::kTouch;
   gesture_detail.type = type;
   model_->ActivateTabAt(model_index, gesture_detail);
+
+  tabstrip_->GetWidget()->GetCompositor()->RequestPresentationTimeForNextFrame(
+      base::BindOnce(
+          [](std::unique_ptr<content::PeakGpuMemoryTracker> tracker,
+             const gfx::PresentationFeedback& feedback) {
+            // This callback will be ran once the ui::Compositor presents the
+            // next frame for the |tabstrip_|. The destruction of |tracker| will
+            // get the peak GPU memory and record a histogram.
+          },
+          std::move(tracker)));
 }
 
 void BrowserTabStripController::ExtendSelectionTo(int model_index) {
diff --git a/chrome/browser/ui/views/tabs/tab_close_button.cc b/chrome/browser/ui/views/tabs/tab_close_button.cc
index 0c99bea..cda83d0 100644
--- a/chrome/browser/ui/views/tabs/tab_close_button.cc
+++ b/chrome/browser/ui/views/tabs/tab_close_button.cc
@@ -24,9 +24,9 @@
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/animation/ink_drop.h"
 #include "ui/views/animation/ink_drop_mask.h"
+#include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/rect_based_targeting_utils.h"
-#include "ui/views/view_class_properties.h"
 
 #if defined(USE_AURA)
 #include "ui/aura/env.h"
@@ -35,6 +35,25 @@
 namespace {
 constexpr int kGlyphWidth = 16;
 constexpr int kTouchGlyphWidth = 24;
+
+class TabCloseButtonHighlightPathGenerator
+    : public views::HighlightPathGenerator {
+ public:
+  TabCloseButtonHighlightPathGenerator() = default;
+
+  // views::HighlightPathGenerator:
+  SkPath GetHighlightPath(const views::View* view) override {
+    const gfx::Rect bounds = view->GetContentsBounds();
+    const gfx::Point center = bounds.CenterPoint();
+    const int radius = views::LayoutProvider::Get()->GetCornerRadiusMetric(
+        views::EMPHASIS_MAXIMUM, bounds.size());
+    return SkPath().addCircle(center.x(), center.y(), radius);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TabCloseButtonHighlightPathGenerator);
+};
+
 }  //  namespace
 
 TabCloseButton::TabCloseButton(views::ButtonListener* listener,
@@ -55,6 +74,8 @@
   GetInkDrop()->SetHoverHighlightFadeDuration(base::TimeDelta());
 
   SetInstallFocusRingOnFocus(true);
+  views::HighlightPathGenerator::Install(
+      this, std::make_unique<TabCloseButtonHighlightPathGenerator>());
 }
 
 TabCloseButton::~TabCloseButton() {}
@@ -120,17 +141,6 @@
   return size;
 }
 
-void TabCloseButton::OnBoundsChanged(const gfx::Rect& previous_bounds) {
-  ImageButton::OnBoundsChanged(previous_bounds);
-  auto path = std::make_unique<SkPath>();
-  const gfx::Rect bounds = GetContentsBounds();
-  const gfx::Point center = bounds.CenterPoint();
-  const int radius = views::LayoutProvider::Get()->GetCornerRadiusMetric(
-      views::EMPHASIS_MAXIMUM, bounds.size());
-  path->addCircle(center.x(), center.y(), radius);
-  SetProperty(views::kHighlightPathKey, path.release());
-}
-
 std::unique_ptr<views::InkDropMask> TabCloseButton::CreateInkDropMask() const {
   const gfx::Rect bounds = GetContentsBounds();
   const int radius = views::LayoutProvider::Get()->GetCornerRadiusMetric(
diff --git a/chrome/browser/ui/views/tabs/tab_close_button.h b/chrome/browser/ui/views/tabs/tab_close_button.h
index ccf1e6b..eda7d11 100644
--- a/chrome/browser/ui/views/tabs/tab_close_button.h
+++ b/chrome/browser/ui/views/tabs/tab_close_button.h
@@ -49,7 +49,6 @@
  protected:
   // views::ImageButton:
   gfx::Size CalculatePreferredSize() const override;
-  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   void PaintButtonContents(gfx::Canvas* canvas) override;
 
  private:
diff --git a/chrome/browser/ui/views/tabs/tab_drag_context.h b/chrome/browser/ui/views/tabs/tab_drag_context.h
index 24f2f98a..e484434 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_context.h
+++ b/chrome/browser/ui/views/tabs/tab_drag_context.h
@@ -52,10 +52,6 @@
   // operation.
   virtual void DestroyDragController() = 0;
 
-  // Return true if this tab strip is compatible with the provided tab strip.
-  // Compatible tab strips can transfer tabs during drag and drop.
-  virtual bool IsCompatibleWith(TabDragContext* other) const = 0;
-
   // Returns true if a drag session is currently active.
   virtual bool IsDragSessionActive() const = 0;
 
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 62cf0ff..0e64ded 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -665,7 +665,7 @@
   // Detaching and attaching can be suppresed temporarily to suppress attaching
   // to incorrect window on changing bounds. We should prevent Drag() itself,
   // otherwise it can clear deferred attaching tab.
-  if (!CanDetachFromTabStrip(GetContextForWindow(widget->GetNativeWindow())))
+  if (!CanDetachFromTabStrip(attached_context_))
     return;
 #if defined(USE_AURA)
   aura::Env* env = aura::Env::GetInstance();
@@ -1100,10 +1100,11 @@
   if (state == Liveness::DELETED)
     return Liveness::DELETED;
 
-  // Do not allow dragging into a window with a modal dialog, it causes a weird
-  // behavior.  See crbug.com/336691
-  if (local_window && !ShouldDisallowDrag(local_window)) {
-    TabDragContext* destination_tab_strip = GetContextForWindow(local_window);
+  if (local_window && CanAttachTo(local_window)) {
+    TabDragContext* destination_tab_strip =
+        BrowserView::GetBrowserViewForNativeWindow(local_window)
+            ->tabstrip()
+            ->GetDragContext();
     if (ShouldAttachOnEnd(destination_tab_strip)) {
       // No need to check if the specified screen point is within the bounds of
       // the tabstrip as arriving here we know that the window is currently
@@ -1124,25 +1125,6 @@
   return Liveness::ALIVE;
 }
 
-TabDragContext* TabDragController::GetContextForWindow(
-    gfx::NativeWindow window) {
-  if (!window)
-    return NULL;
-  BrowserView* browser_view =
-      BrowserView::GetBrowserViewForNativeWindow(window);
-  // We don't allow drops on windows that don't have tabstrips.
-  if (!browser_view || !browser_view->browser()->SupportsWindowFeature(
-                           Browser::FEATURE_TABSTRIP))
-    return NULL;
-
-  TabDragContext* other_context = browser_view->tabstrip()->GetDragContext();
-  TabDragContext* context =
-      attached_context_ ? attached_context_ : source_context_;
-  DCHECK(context);
-
-  return other_context->IsCompatibleWith(context) ? other_context : nullptr;
-}
-
 bool TabDragController::DoesTabStripContain(
     TabDragContext* context,
     const gfx::Point& point_in_screen) const {
@@ -1773,7 +1755,7 @@
 
   // Only bring browser windows to front - only windows with a
   // TabDragContext can be tab drag targets.
-  if (!GetContextForWindow(window))
+  if (!CanAttachTo(window))
     return;
 
   if (window) {
@@ -2181,17 +2163,42 @@
   return base::nullopt;
 }
 
-bool TabDragController::ShouldDisallowDrag(gfx::NativeWindow window) {
+bool TabDragController::CanAttachTo(gfx::NativeWindow window) {
+  if (!window)
+    return false;
+
+  BrowserView* other_browser_view =
+      BrowserView::GetBrowserViewForNativeWindow(window);
+  if (!other_browser_view)
+    return false;
+  Browser* other_browser = other_browser_view->browser();
+
+  // Do not allow dragging into a window with a modal dialog, it causes a
+  // weird behavior.  See crbug.com/336691
 #if defined(USE_AURA)
-  return wm::GetModalTransient(window) != nullptr;
+  if (wm::GetModalTransient(window) != nullptr)
+    return false;
 #else
-  TabDragContext* context = GetContextForWindow(window);
-  if (!context)
-    return true;
-  TabStripModel* model = context->GetTabStripModel();
+  TabStripModel* model = other_browser->tab_strip_model();
   DCHECK(model);
-  return model->IsTabBlocked(model->active_index());
+  if (model->IsTabBlocked(model->active_index()))
+    return false;
 #endif
+
+  // We don't allow drops on windows that don't have tabstrips.
+  if (!other_browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP))
+    return false;
+
+  Browser* browser = BrowserView::GetBrowserViewForNativeWindow(
+                         GetAttachedBrowserWidget()->GetNativeWindow())
+                         ->browser();
+
+  // Profiles must be the same.
+  if (other_browser->profile() != browser->profile())
+    return false;
+
+  // Browser type (e.g. NORMAL vs APP) must be the same.
+  return other_browser->type() == browser->type();
 }
 
 void TabDragController::SetDeferredTargetTabstrip(
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.h b/chrome/browser/ui/views/tabs/tab_drag_controller.h
index 8cf1c81..8453b5f9 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.h
@@ -319,10 +319,6 @@
   void StartMoveStackedTimerIfNecessary(const gfx::Point& point_in_screen,
                                         base::TimeDelta delay);
 
-  // Returns the TabDragContext for the specified window, or NULL if
-  // one doesn't exist or isn't compatible.
-  TabDragContext* GetContextForWindow(gfx::NativeWindow window);
-
   // Returns the compatible TabDragContext to drag to at the
   // specified point (screen coordinates), or nullptr if there is none.
   Liveness GetTargetTabStripForPoint(const gfx::Point& point_in_screen,
@@ -499,9 +495,11 @@
 
   DragState current_state_;
 
-  // Whether a drag to |window| should be blocked (for example, if the window
-  // is showing a modal).
-  bool ShouldDisallowDrag(gfx::NativeWindow window);
+  // Tests whether a drag can be attached to a |window|.  Drags may be
+  // disallowed for reasons such as the target: does not support tabs, is
+  // showing a modal, has a different profile, is a different browser type
+  // (NORMAL vs APP).
+  bool CanAttachTo(gfx::NativeWindow window);
 
   // Helper method for TabDragController::MoveAttached to update the tab group
   // membership of selected tabs. UpdateGroupForDraggedTabs should be called
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index c9b21ad..f7de5a6f 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -235,20 +235,22 @@
 }
 
 Browser* TabDragControllerTest::CreateAnotherBrowserAndResize() {
-  // Create another browser.
   Browser* browser2 = CreateBrowser(browser()->profile());
   ResetIDs(browser2->tab_strip_model(), 100);
+  Resize(browser(), browser2);
+  return browser2;
+}
 
+void TabDragControllerTest::Resize(Browser* browser1, Browser* browser2) {
   // Resize the two windows so they're right next to each other.
-  const gfx::NativeWindow window = browser()->window()->GetNativeWindow();
+  const gfx::NativeWindow window = browser1->window()->GetNativeWindow();
   gfx::Rect work_area =
       display::Screen::GetScreen()->GetDisplayNearestWindow(window).work_area();
   const gfx::Size size(work_area.width() / 3, work_area.height() / 2);
   gfx::Rect browser_rect(work_area.origin(), size);
-  browser()->window()->SetBounds(browser_rect);
+  browser1->window()->SetBounds(browser_rect);
   browser_rect.set_x(browser_rect.right());
   browser2->window()->SetBounds(browser_rect);
-  return browser2;
 }
 
 void TabDragControllerTest::SetWindowFinderForTabStrip(
@@ -603,16 +605,16 @@
     observer.Wait();
   }
 
-  std::string InstallWebApp(
+  web_app::AppId InstallWebApp(
       std::unique_ptr<WebApplicationInfo>&& web_app_info) {
-    std::string app_id;
+    web_app::AppId app_id;
     base::RunLoop run_loop;
     auto* provider = web_app::WebAppProvider::Get(browser()->profile());
     DCHECK(provider);
     provider->install_manager().InstallWebAppFromInfo(
         std::move(web_app_info), web_app::ForInstallableSite::kYes,
         WebappInstallSource::OMNIBOX_INSTALL_ICON,
-        base::BindLambdaForTesting([&](const std::string& installed_app_id,
+        base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
                                        web_app::InstallResultCode code) {
           EXPECT_EQ(web_app::InstallResultCode::kSuccessNewInstall, code);
           app_id = installed_app_id;
@@ -624,26 +626,30 @@
   }
 
   Browser* GetTerminalAppBrowser() {
-    GURL app_url = embedded_test_server()->GetURL("app.com", "/simple.html");
-    auto web_app_info = std::make_unique<WebApplicationInfo>();
-    web_app_info->app_url = app_url;
-    web_app_info->scope = app_url.GetWithoutFilename();
-    web_app_info->open_as_window = true;
-    web_app::AppId app_id = InstallWebApp(std::move(web_app_info));
+    // Install the app (but only once per session).
+    if (!terminal_app_extension_) {
+      GURL app_url = embedded_test_server()->GetURL("app.com", "/simple.html");
+      auto web_app_info = std::make_unique<WebApplicationInfo>();
+      web_app_info->app_url = app_url;
+      web_app_info->scope = app_url.GetWithoutFilename();
+      web_app_info->open_as_window = true;
+      web_app::AppId app_id = InstallWebApp(std::move(web_app_info));
 
-    auto* provider = web_app::WebAppProvider::Get(browser()->profile());
-    provider->system_web_app_manager().SetSystemAppsForTesting(
-        {{web_app::SystemAppType::TERMINAL, web_app::SystemAppInfo(app_url)}});
-    web_app::ExternallyInstalledWebAppPrefs(browser()->profile()->GetPrefs())
-        .Insert(app_url, app_id,
-                web_app::ExternalInstallSource::kInternalDefault);
+      auto* provider = web_app::WebAppProvider::Get(browser()->profile());
+      provider->system_web_app_manager().SetSystemAppsForTesting(
+          {{web_app::SystemAppType::TERMINAL,
+            web_app::SystemAppInfo(app_url)}});
+      web_app::ExternallyInstalledWebAppPrefs(browser()->profile()->GetPrefs())
+          .Insert(app_url, app_id,
+                  web_app::ExternalInstallSource::kInternalDefault);
 
-    const extensions::Extension* extension =
-        extensions::ExtensionRegistry::Get(browser()->profile())
-            ->GetInstalledExtension(app_id);
+      terminal_app_extension_ =
+          extensions::ExtensionRegistry::Get(browser()->profile())
+              ->GetInstalledExtension(app_id);
+    }
 
-    return extensions::browsertest_util::LaunchAppBrowser(browser()->profile(),
-                                                          extension);
+    return extensions::browsertest_util::LaunchAppBrowser(
+        browser()->profile(), terminal_app_extension_);
   }
 
   Browser* browser() const { return InProcessBrowserTest::browser(); }
@@ -653,6 +659,7 @@
   // The root window for the event generator.
   aura::Window* root_ = nullptr;
 #endif
+  const extensions::Extension* terminal_app_extension_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(DetachToBrowserTabDragControllerTest);
 };
@@ -2783,6 +2790,39 @@
   EXPECT_EQ(Browser::Type::TYPE_APP, browser_list->get(1)->type());
 }
 
+// Move tab from TYPE_APP Browser to another TYPE_APP Browser.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
+                       DragAppToAppWindow) {
+  // Start the embedded server, and get 2 browsers with Terminal System App.
+  ASSERT_TRUE(embedded_test_server()->Start());
+  Browser* app_browser1 = GetTerminalAppBrowser();
+  Browser* app_browser2 = GetTerminalAppBrowser();
+  ASSERT_EQ(3u, browser_list->size());
+  ResetIDs(app_browser2->tab_strip_model(), 100);
+  Resize(app_browser1, app_browser2);
+
+  // Close normal browser since other code expects only 1 browser to start.
+  CloseBrowserSynchronously(browser());
+  ASSERT_EQ(2u, browser_list->size());
+  SelectFirstBrowser();
+  ASSERT_EQ(app_browser1, browser());
+
+  AddTabsAndResetBrowser(browser(), 1);
+  TabStrip* tab_strip1 = GetTabStripForBrowser(app_browser1);
+  TabStrip* tab_strip2 = GetTabStripForBrowser(app_browser2);
+
+  // Move to the first tab and drag it enough so that it detaches, but not
+  // enough that it attaches to browser2.
+  DragTabAndNotify(tab_strip1, base::BindOnce(&DragToSeparateWindowStep2, this,
+                                              tab_strip1, tab_strip2));
+
+  // Should now be attached to tab_strip2.
+  // Release mouse or touch, stopping the drag session.
+  ASSERT_TRUE(ReleaseInput());
+  EXPECT_EQ("100 0", IDString(app_browser2->tab_strip_model()));
+  EXPECT_EQ("1", IDString(app_browser1->tab_strip_model()));
+}
+
 // Subclass of DetachToBrowserTabDragControllerTest that
 // creates multiple displays.
 class DetachToBrowserInSeparateDisplayTabDragControllerTest
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h
index bfc48e9..d86eb58d 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h
@@ -38,6 +38,9 @@
   // the tabs in |browser|.
   void AddTabsAndResetBrowser(Browser* browser, int additional_tabs);
 
+  // Resizes browser1 and browser2 to be side by side.
+  void Resize(Browser* browser1, Browser* browser2);
+
   // Creates a new Browser and resizes browser() and the new browser to be side
   // by side.
   Browser* CreateAnotherBrowserAndResize();
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 6dbce64..db98ca4 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -444,12 +444,6 @@
     return drag_controller_.release();
   }
 
-  bool IsCompatibleWith(TabDragContext* other) const override {
-    return static_cast<TabDragContextImpl*>(other)
-               ->tab_strip_->controller()
-               ->GetProfile() == tab_strip_->controller()->GetProfile();
-  }
-
   bool IsDragSessionActive() const override {
     return drag_controller_ != nullptr;
   }
diff --git a/chrome/browser/ui/views/task_manager_view.cc b/chrome/browser/ui/views/task_manager_view.cc
index 03955be..1274b1d 100644
--- a/chrome/browser/ui/views/task_manager_view.cc
+++ b/chrome/browser/ui/views/task_manager_view.cc
@@ -213,11 +213,6 @@
   return ui::DIALOG_BUTTON_OK;
 }
 
-base::string16 TaskManagerView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_KILL);
-}
-
 bool TaskManagerView::IsDialogButtonEnabled(ui::DialogButton button) const {
   const ui::ListSelectionModel::SelectedIndices& selections(
       tab_table_->selection_model().selected_indices());
@@ -298,6 +293,9 @@
       tab_table_parent_(nullptr),
       is_always_on_top_(false) {
   DialogDelegate::set_use_custom_frame(false);
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK, l10n_util::GetStringUTF16(IDS_TASK_MANAGER_KILL));
+
   Init();
   chrome::RecordDialogCreation(chrome::DialogIdentifier::TASK_MANAGER);
 }
diff --git a/chrome/browser/ui/views/task_manager_view.h b/chrome/browser/ui/views/task_manager_view.h
index 502197766..f9673e2 100644
--- a/chrome/browser/ui/views/task_manager_view.h
+++ b/chrome/browser/ui/views/task_manager_view.h
@@ -65,7 +65,6 @@
   bool Accept() override;
   bool Close() override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   void WindowClosing() override;
 
diff --git a/chrome/browser/ui/web_applications/web_app_launch_manager.cc b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
index 81839ca0..95c3849 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_manager.cc
+++ b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
@@ -47,7 +47,7 @@
 
 void SetWebAppPrefsForWebContents(content::WebContents* web_contents) {
   web_contents->GetMutableRendererPrefs()->can_accept_load_drops = false;
-  web_contents->GetRenderViewHost()->SyncRendererPrefs();
+  web_contents->SyncRendererPrefs();
   web_contents->NotifyPreferencesChanged();
 }
 
diff --git a/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_message_handler.cc b/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_message_handler.cc
deleted file mode 100644
index bccb1f3..0000000
--- a/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_message_handler.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_message_handler.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/values.h"
-#include "chrome/android/modules/dev_ui/provider/dev_ui_module_provider.h"
-
-DevUiLoaderMessageHandler::DevUiLoaderMessageHandler() = default;
-
-DevUiLoaderMessageHandler::~DevUiLoaderMessageHandler() = default;
-
-void DevUiLoaderMessageHandler::RegisterMessages() {
-  web_ui()->RegisterMessageCallback(
-      "getDevUiDfmState",
-      base::BindRepeating(&DevUiLoaderMessageHandler::HandleGetDevUiDfmState,
-                          weak_ptr_factory_.GetWeakPtr()));
-  web_ui()->RegisterMessageCallback(
-      "installDevUiDfm",
-      base::BindRepeating(&DevUiLoaderMessageHandler::HandleInstallDevUiDfm,
-                          weak_ptr_factory_.GetWeakPtr()));
-}
-
-void DevUiLoaderMessageHandler::HandleGetDevUiDfmState(
-    const base::ListValue* args) {
-  const base::Value* callback_id = nullptr;
-  CHECK(args->Get(0, &callback_id));
-  const char* response =
-      dev_ui::DevUiModuleProvider::GetInstance()->ModuleInstalled()
-          ? "installed"
-          : "not-installed";
-  AllowJavascript();
-  ResolveJavascriptCallback(*callback_id, base::Value(response));
-}
-
-void DevUiLoaderMessageHandler::ReplyToJavaScript(
-    const base::Value& callback_id,
-    const char* return_value) {
-  AllowJavascript();
-  base::ListValue response;
-  response.Append(base::Value(return_value));
-  ResolveJavascriptCallback(callback_id, response);
-}
-
-void DevUiLoaderMessageHandler::HandleInstallDevUiDfm(
-    const base::ListValue* args) {
-  const base::Value* callback_id = nullptr;
-  CHECK(args->Get(0, &callback_id));
-
-  if (!dev_ui::DevUiModuleProvider::GetInstance()->ModuleInstalled()) {
-    dev_ui::DevUiModuleProvider::GetInstance()->InstallModule(base::BindOnce(
-        &DevUiLoaderMessageHandler::OnDevUiDfmInstallWithStatus,
-        weak_ptr_factory_.GetWeakPtr(), callback_id->GetString()));
-  } else {
-    ReplyToJavaScript(*callback_id, "noop");
-  }
-}
-
-void DevUiLoaderMessageHandler::OnDevUiDfmInstallWithStatus(
-    std::string callback_id_string,
-    bool success) {
-  ReplyToJavaScript(base::Value(callback_id_string),
-                    success ? "success" : "failure");
-}
-
diff --git a/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_message_handler.h b/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_message_handler.h
deleted file mode 100644
index 1dda914..0000000
--- a/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_message_handler.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_ANDROID_DEV_UI_LOADER_DEV_UI_LOADER_MESSAGE_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_ANDROID_DEV_UI_LOADER_DEV_UI_LOADER_MESSAGE_HANDLER_H_
-
-#include <string>
-
-#include "base/memory/weak_ptr.h"
-#include "build/build_config.h"
-#include "chrome/android/features/dev_ui/buildflags.h"
-#include "content/public/browser/web_ui_message_handler.h"
-
-#if !defined(OS_ANDROID) || !BUILDFLAG(DFMIFY_DEV_UI)
-#error Unsupported platform.
-#endif
-
-namespace base {
-class ListValue;
-class Value;
-}  // namespace base
-
-class DevUiLoaderMessageHandler : public content::WebUIMessageHandler {
- public:
-  DevUiLoaderMessageHandler();
-  ~DevUiLoaderMessageHandler() override;
-
- private:
-  DevUiLoaderMessageHandler(const DevUiLoaderMessageHandler&) = delete;
-  void operator=(const DevUiLoaderMessageHandler&) = delete;
-
-  // WebUIMessageHandler
-  void RegisterMessages() override;
-
-  // Called from JavaScript. |args| specifies id for callback, which receives
-  // one of the following responses:
-  // * "not-installed" if the DevUI DFM is not installed.
-  // * "installed" if the DevUI DFM is installed.
-  void HandleGetDevUiDfmState(const base::ListValue* args);
-
-  // Helper for HandleInstallAndLoadDevUiDfm().
-  void ReplyToJavaScript(const base::Value& callback_id,
-                         const char* return_value);
-
-  // Called from JavaScript. |args| specifies id for callback, which receives
-  // one of the following responses:
-  // * "noop" if the DevUI DFM is already installed and loaded.
-  // * "success" if DevUI DFM install takes place, and succeeds.
-  // * "failure" if DevUI DFM install takes place, but fails.
-  void HandleInstallDevUiDfm(const base::ListValue* args);
-
-  // Callback for dev_ui::DevUiModuleProvider::InstallModule().
-  void OnDevUiDfmInstallWithStatus(std::string callback_id_string,
-                                   bool success);
-
-  // Factory for creating references in callbacks.
-  base::WeakPtrFactory<DevUiLoaderMessageHandler> weak_ptr_factory_{this};
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_ANDROID_DEV_UI_LOADER_DEV_UI_LOADER_MESSAGE_HANDLER_H_
diff --git a/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_ui.cc b/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_ui.cc
deleted file mode 100644
index 52622ee..0000000
--- a/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_ui.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_ui.h"
-
-#include <memory>
-#include <utility>
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_message_handler.h"
-#include "chrome/grit/browser_resources.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "url/gurl.h"
-
-DevUiLoaderUI::DevUiLoaderUI(content::WebUI* web_ui_in, const GURL& url)
-    : WebUIController(web_ui_in) {
-  std::unique_ptr<content::WebUIDataSource> html_source;
-  html_source.reset(content::WebUIDataSource::Create(url.host()));
-  html_source->SetDefaultResource(IDR_DEV_UI_LOADER_HTML);
-  html_source->AddResourcePath("dev_ui_loader.html", IDR_DEV_UI_LOADER_HTML);
-  html_source->AddResourcePath("dev_ui_loader.js", IDR_DEV_UI_LOADER_JS);
-  html_source->AddResourcePath("dev_ui_loader.css", IDR_DEV_UI_LOADER_CSS);
-
-  Profile* profile = Profile::FromWebUI(web_ui());
-  content::WebUIDataSource::Add(profile, html_source.release());
-  web_ui()->AddMessageHandler(std::make_unique<DevUiLoaderMessageHandler>());
-}
-
-DevUiLoaderUI::~DevUiLoaderUI() = default;
diff --git a/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_ui.h b/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_ui.h
deleted file mode 100644
index d8b70e7..0000000
--- a/chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_ui.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_ANDROID_DEV_UI_LOADER_DEV_UI_LOADER_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_ANDROID_DEV_UI_LOADER_DEV_UI_LOADER_UI_H_
-
-#include "base/memory/weak_ptr.h"
-#include "build/build_config.h"
-#include "chrome/android/features/dev_ui/buildflags.h"
-#include "content/public/browser/web_ui_controller.h"
-
-#if !defined(OS_ANDROID) || !BUILDFLAG(DFMIFY_DEV_UI)
-#error Unsupported platform.
-#endif
-
-class GURL;
-
-class DevUiLoaderUI : public content::WebUIController {
- public:
-  DevUiLoaderUI(content::WebUI* web_ui_in, const GURL& url);
-  ~DevUiLoaderUI() override;
-
- private:
-  DevUiLoaderUI(const DevUiLoaderUI&) = delete;
-  void operator=(const DevUiLoaderUI&) = delete;
-
-  // Factory for creating references in callbacks.
-  base::WeakPtrFactory<DevUiLoaderUI> weak_ptr_factory_{this};
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_ANDROID_DEV_UI_LOADER_DEV_UI_LOADER_UI_H_
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index aee567a..49a65a4 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -118,7 +118,6 @@
 #endif
 
 #if defined(OS_ANDROID)
-#include "chrome/android/features/dev_ui/buildflags.h"
 #include "chrome/browser/ui/webui/explore_sites_internals/explore_sites_internals_ui.h"
 #include "chrome/browser/ui/webui/offline/offline_internals_ui.h"
 #include "chrome/browser/ui/webui/snippets_internals/snippets_internals_ui.h"
@@ -128,9 +127,6 @@
 #if BUILDFLAG(ENABLE_FEED_IN_CHROME)
 #include "chrome/browser/ui/webui/feed_internals/feed_internals_ui.h"
 #endif  // BUILDFLAG(ENABLE_FEED_IN_CHROME)
-#if BUILDFLAG(DFMIFY_DEV_UI)
-#include "chrome/browser/ui/webui/android/dev_ui_loader/dev_ui_loader_ui.h"
-#endif  // BUILDFLAG(DFMIFY_DEV_UI)
 #else   // defined(OS_ANDROID)
 #include "chrome/browser/ui/webui/bookmarks/bookmarks_ui.h"
 #include "chrome/browser/ui/webui/devtools_ui.h"
@@ -263,15 +259,6 @@
   return new T(web_ui);
 }
 
-#if defined(OS_ANDROID)
-#if BUILDFLAG(DFMIFY_DEV_UI)
-template <>
-WebUIController* NewWebUI<DevUiLoaderUI>(WebUI* web_ui, const GURL& url) {
-  return new DevUiLoaderUI(web_ui, url);
-}
-#endif  // BUILDFLAG(DFMIFY_DEV_UI)
-#endif  // defined(OS_ANDROID)
-
 #if !defined(OS_ANDROID)
 template <>
 WebUIController* NewWebUI<PageNotAvailableForGuestUI>(WebUI* web_ui,
@@ -344,13 +331,6 @@
     return nullptr;
   }
 
-#if defined(OS_ANDROID)
-#if BUILDFLAG(DFMIFY_DEV_UI)
-  if (url.host_piece() == chrome::kChromeUIDevUiLoaderHost)
-    return &NewWebUI<DevUiLoaderUI>;
-#endif  // BUILDFLAG(DFMIFY_DEV_UI)
-#endif  // defined(OS_ANDROID)
-
   // Please keep this in alphabetical order. If #ifs or special logics are
   // required, add it below in the appropriate section.
   //
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 66d22f2..ce27b780 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -560,7 +560,7 @@
 
   if (public_saml_url_fetcher_) {
     params.SetBoolean("startsOnSamlPage", true);
-    CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
+    DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
         switches::kPublicAccountsSamlAclUrl));
     std::string saml_acl_url =
         base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
diff --git a/chrome/browser/ui/webui/chromeos/set_time_ui.cc b/chrome/browser/ui/webui/chromeos/set_time_ui.cc
index 1376878..f5ec12c 100644
--- a/chrome/browser/ui/webui/chromeos/set_time_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/set_time_ui.cc
@@ -33,6 +33,7 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/browser/web_ui_message_handler.h"
+#include "ui/resources/grit/webui_resources.h"
 
 namespace chromeos {
 
@@ -171,6 +172,8 @@
   // Set up the chrome://set-time source.
   content::WebUIDataSource* source =
       content::WebUIDataSource::Create(chrome::kChromeUISetTimeHost);
+  source->OverrideContentSecurityPolicyScriptSrc(
+      "script-src chrome://resources chrome://test 'self';");
 
   static constexpr LocalizedString kStrings[] = {
       {"setTimeTitle", IDS_SET_TIME_TITLE},
@@ -196,13 +199,15 @@
 
   source->AddLocalizedStrings(values);
   source->UseStringsJs();
+  source->EnableReplaceI18nInJS();
 
-  source->AddResourcePath("set_time_browser_proxy.html",
-                          IDR_SET_TIME_BROWSER_PROXY_HTML);
   source->AddResourcePath("set_time_browser_proxy.js",
                           IDR_SET_TIME_BROWSER_PROXY_JS);
   source->AddResourcePath("set_time_dialog.js", IDR_SET_TIME_DIALOG_JS);
-  source->SetDefaultResource(IDR_SET_TIME_DIALOG_HTML);
+  source->SetDefaultResource(IDR_SET_TIME_HTML);
+
+  source->AddResourcePath("test_loader.js", IDR_WEBUI_JS_TEST_LOADER);
+  source->AddResourcePath("test_loader.html", IDR_WEBUI_HTML_TEST_LOADER);
 
   content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source);
 }
diff --git a/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc b/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc
index d38705d..0fd891b5 100644
--- a/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc
+++ b/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc
@@ -49,7 +49,7 @@
   renderer_preferences_util::UpdateFromSystemSettings(
       prefs, Profile::FromBrowserContext(browser_context));
 
-  web_contents_->GetRenderViewHost()->SyncRendererPrefs();
+  web_contents_->SyncRendererPrefs();
 
   // Set |this| as a delegate so the ConstrainedWebDialogUI can retrieve it.
   ConstrainedWebDialogUI::SetConstrainedDelegate(web_contents_, this);
diff --git a/chrome/browser/ui/webui/history_ui.cc b/chrome/browser/ui/webui/history_ui.cc
index 5f900c0..4826cb8 100644
--- a/chrome/browser/ui/webui/history_ui.cc
+++ b/chrome/browser/ui/webui/history_ui.cc
@@ -58,6 +58,7 @@
   static constexpr LocalizedString kStrings[] = {
       // Localized strings (alphabetical order).
       {"actionMenuDescription", IDS_HISTORY_ACTION_MENU_DESCRIPTION},
+      {"ariaRoleDescription", IDS_HISTORY_ARIA_ROLE_DESCRIPTION},
       {"bookmarked", IDS_HISTORY_ENTRY_BOOKMARKED},
       {"cancel", IDS_CANCEL},
       {"clearBrowsingData", IDS_CLEAR_BROWSING_DATA_TITLE},
diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
index 0d3d7ecf..4591b88 100644
--- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -283,8 +283,8 @@
                                                                   : "";
   replacements["cookieControlsTitle"] =
       l10n_util::GetStringUTF8(IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIE);
-  replacements["cookieControlsDescription"] =
-      l10n_util::GetStringUTF8(IDS_NEW_TAB_OTR_COOKIE_CONTROLS_DESCRIPTION);
+  replacements["cookieControlsDescription"] = l10n_util::GetStringUTF8(
+      IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIE_SUBLABEL);
   replacements["cookieControlsToggleChecked"] =
       CookieControlsHandler::GetToggleCheckedValue(profile_) ? "checked" : "";
 
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index dcf8913b..2ecb8cd 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -147,6 +147,7 @@
   DEFAULT_DPI,
   NON_DEFAULT_DPI,
   PIN,
+  FIT_TO_PAPER,
   PRINT_SETTINGS_BUCKET_BOUNDARY
 };
 
@@ -358,8 +359,11 @@
     ReportPrintSettingHistogram(SCALING);
   }
 
-  if (is_pdf && scaling_type == ScalingType::FIT_TO_PAGE) {
-    ReportPrintSettingHistogram(FIT_TO_PAGE);
+  if (is_pdf) {
+    if (scaling_type == ScalingType::FIT_TO_PAGE)
+      ReportPrintSettingHistogram(FIT_TO_PAGE);
+    else if (scaling_type == ScalingType::FIT_TO_PAPER)
+      ReportPrintSettingHistogram(FIT_TO_PAPER);
   }
 
   if (print_settings.FindIntKey(kSettingDpiHorizontal).value_or(0) > 0 &&
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index 1b73c3c..8208206 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -228,6 +228,7 @@
     {"optionCustomScaling", IDS_PRINT_PREVIEW_OPTION_CUSTOM_SCALING},
     {"optionDefaultScaling", IDS_PRINT_PREVIEW_OPTION_DEFAULT_SCALING},
     {"optionFitToPage", IDS_PRINT_PREVIEW_OPTION_FIT_TO_PAGE},
+    {"optionFitToPaper", IDS_PRINT_PREVIEW_OPTION_FIT_TO_PAPER},
     {"optionHeaderFooter", IDS_PRINT_PREVIEW_OPTION_HEADER_FOOTER},
     {"optionLandscape", IDS_PRINT_PREVIEW_OPTION_LANDSCAPE},
     {"optionLongEdge", IDS_PRINT_PREVIEW_OPTION_LONG_EDGE},
diff --git a/chrome/browser/ui/webui/profile_helper_browsertest.cc b/chrome/browser/ui/webui/profile_helper_browsertest.cc
index 3e338e5..c6f2e95 100644
--- a/chrome/browser/ui/webui/profile_helper_browsertest.cc
+++ b/chrome/browser/ui/webui/profile_helper_browsertest.cc
@@ -69,6 +69,31 @@
   base::RunLoop loop_;
 };
 
+// An observer that returns back to test code after a new browser is added to
+// the BrowserList.
+class BrowserAddedObserver : public BrowserListObserver {
+ public:
+  BrowserAddedObserver() { BrowserList::AddObserver(this); }
+
+  ~BrowserAddedObserver() override { BrowserList::RemoveObserver(this); }
+
+  Browser* Wait() {
+    run_loop_.Run();
+    return browser_;
+  }
+
+ protected:
+  // BrowserListObserver:
+  void OnBrowserAdded(Browser* browser) override {
+    browser_ = browser;
+    run_loop_.Quit();
+  }
+
+ private:
+  Browser* browser_;
+  base::RunLoop run_loop_;
+};
+
 }  // namespace
 
 using ProfileHelperTest = InProcessBrowserTest;
@@ -114,13 +139,7 @@
 #endif
 }
 
-#if (defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)) && \
-    defined(NDEBUG)
-#define MAYBE_DeleteSoleProfile DISABLED_DeleteSoleProfile
-#else
-#define MAYBE_DeleteSoleProfile DeleteSoleProfile
-#endif
-IN_PROC_BROWSER_TEST_F(ProfileHelperTest, MAYBE_DeleteSoleProfile) {
+IN_PROC_BROWSER_TEST_F(ProfileHelperTest, DeleteSoleProfile) {
   content::TestWebUI web_ui;
   Browser* original_browser = browser();
   ProfileAttributesStorage& storage =
@@ -132,12 +151,15 @@
   EXPECT_EQ(1u, storage.GetNumberOfProfiles());
 
   // Original browser will be closed, and browser with the new profile created.
+  BrowserAddedObserver added_observer;
   webui::DeleteProfileAtPath(original_browser->profile()->GetPath(),
                              ProfileMetrics::DELETE_PROFILE_SETTINGS);
   ui_test_utils::WaitForBrowserToClose(original_browser);
+  Browser* new_browser = added_observer.Wait();
 
   EXPECT_EQ(1u, browser_list->size());
   EXPECT_FALSE(base::Contains(*browser_list, original_browser));
+  EXPECT_NE(new_browser, original_browser);
   EXPECT_EQ(1u, storage.GetNumberOfProfiles());
 }
 
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index a30278ea..465410f 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -648,33 +648,22 @@
 
 void AddAppManagementStrings(content::WebUIDataSource* html_source) {
   static constexpr LocalizedString localized_strings[] = {
-      {"appListTitle", IDS_APP_MANAGEMENT_APP_LIST_TITLE},
-      {"appManagementSearchPrompt", IDS_APP_MANAGEMENT_SEARCH_PROMPT},
-      {"appNoPermission", IDS_APPLICATION_INFO_APP_NO_PERMISSIONS_TEXT},
-      {"camera", IDS_APP_MANAGEMENT_CAMERA},
-      {"contacts", IDS_APP_MANAGEMENT_CONTACTS},
-      {"controlledByPolicy", IDS_CONTROLLED_SETTING_POLICY},
-      {"lessApps", IDS_APP_MANAGEMENT_LESS_APPS},
-      {"location", IDS_APP_MANAGEMENT_LOCATION},
-      {"microphone", IDS_APP_MANAGEMENT_MICROPHONE},
-      {"moreApps", IDS_APP_MANAGEMENT_MORE_APPS},
-      {"moreSettings", IDS_APP_MANAGEMENT_MORE_SETTINGS},
+      {"appManagementAppInstalledByPolicyLabel",
+       IDS_APP_MANAGEMENT_POLICY_APP_POLICY_STRING},
+      {"appManagementCameraPermissionLabel", IDS_APP_MANAGEMENT_CAMERA},
+      {"appManagementContactsPermissionLabel", IDS_APP_MANAGEMENT_CONTACTS},
+      {"appManagementLocationPermissionLabel", IDS_APP_MANAGEMENT_LOCATION},
+      {"appManagementMicrophonePermissionLabel", IDS_APP_MANAGEMENT_MICROPHONE},
+      {"appManagementMoreSettingsLabel", IDS_APP_MANAGEMENT_MORE_SETTINGS},
       {"appManagementNoAppsFound", IDS_APP_MANAGEMENT_NO_APPS_FOUND},
-      {"notifications", IDS_APP_MANAGEMENT_NOTIFICATIONS},
-      {"notificationSublabel", IDS_APP_MANAGEMENT_NOTIFICATIONS_SUBLABEL},
-      {"openAndroidSettings", IDS_APP_MANAGEMENT_ANDROID_SETTINGS},
-      {"openExtensionsSettings", IDS_APP_MANAGEMENT_EXTENSIONS_SETTINGS},
-      {"openSiteSettings", IDS_APP_MANAGEMENT_SITE_SETTING},
-      {"permissions", IDS_APP_MANAGEMENT_PERMISSIONS},
-      {"pinControlledByPolicy", IDS_APP_MANAGEMENT_PIN_ENFORCED_BY_POLICY},
-      {"pinToShelf", IDS_APP_MANAGEMENT_PIN_TO_SHELF},
-      {"policyAppUninstallPolicy", IDS_APP_MANAGEMENT_POLICY_APP_POLICY_STRING},
-      {"size", IDS_APP_MANAGEMENT_SIZE},
-      {"storage", IDS_APP_MANAGEMENT_STORAGE},
-      {"thisAppCan", IDS_APP_MANAGEMENT_THIS_APP_CAN},
-      {"title", IDS_APP_MANAGEMENT_TITLE},
-      {"uninstallApp", IDS_APP_MANAGEMENT_UNINSTALL_APP},
-      {"version", IDS_APP_MANAGEMENT_VERSION},
+      {"appManagementNoPermissions",
+       IDS_APPLICATION_INFO_APP_NO_PERMISSIONS_TEXT},
+      {"appManagementNotificationsLabel", IDS_APP_MANAGEMENT_NOTIFICATIONS},
+      {"appManagementPermissionsLabel", IDS_APP_MANAGEMENT_PERMISSIONS},
+      {"appManagementPinToShelfLabel", IDS_APP_MANAGEMENT_PIN_TO_SHELF},
+      {"appManagementSearchPrompt", IDS_APP_MANAGEMENT_SEARCH_PROMPT},
+      {"appManagementStoragePermissionLabel", IDS_APP_MANAGEMENT_STORAGE},
+      {"appManagementUninstallLabel", IDS_APP_MANAGEMENT_UNINSTALL_APP},
   };
   AddLocalizedStringsBulk(html_source, localized_strings,
                           base::size(localized_strings));
diff --git a/chrome/browser/ui/webui/sync_internals_ui.cc b/chrome/browser/ui/webui/sync_internals_ui.cc
index d4d1835..ed5f6af 100644
--- a/chrome/browser/ui/webui/sync_internals_ui.cc
+++ b/chrome/browser/ui/webui/sync_internals_ui.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/sync_internals_message_handler.h"
 #include "chrome/common/url_constants.h"
-#include "components/grit/components_resources.h"
+#include "components/grit/sync_driver_resources.h"
 #include "components/sync/driver/about_sync_util.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
index ad525a9b..98fa0fb 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
@@ -205,6 +205,9 @@
         "removeTrackedTab", base::Bind(&TabStripUIHandler::RemoveTrackedTab,
                                        base::Unretained(this)));
     web_ui()->RegisterMessageCallback(
+        "closeContainer", base::Bind(&TabStripUIHandler::HandleCloseContainer,
+                                     base::Unretained(this)));
+    web_ui()->RegisterMessageCallback(
         "showTabContextMenu",
         base::Bind(&TabStripUIHandler::HandleShowTabContextMenu,
                    base::Unretained(this)));
@@ -306,6 +309,11 @@
     ResolveJavascriptCallback(callback_id, colors);
   }
 
+  void HandleCloseContainer(const base::ListValue* args) {
+    DCHECK(embedder_);
+    embedder_->CloseContainer();
+  }
+
   void HandleShowTabContextMenu(const base::ListValue* args) {
     int tab_id = 0;
     args->GetInteger(0, &tab_id);
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
index e7b7818..6f126eeb 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
@@ -32,6 +32,8 @@
     Embedder() = default;
     virtual ~Embedder() {}
 
+    virtual void CloseContainer() = 0;
+
     virtual void ShowContextMenuAtPoint(
         gfx::Point point,
         std::unique_ptr<ui::MenuModel> menu_model) = 0;
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui_browsertest.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui_browsertest.cc
index d612277..5df49d7 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui_browsertest.cc
@@ -8,6 +8,7 @@
 
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
+#include "base/strings/string_piece.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
@@ -31,6 +32,7 @@
 
 class MockTabStripUIEmbedder : public TabStripUI::Embedder {
  public:
+  MOCK_METHOD(void, CloseContainer, (), (override));
   MOCK_METHOD(void,
               ShowContextMenuAtPoint,
               (gfx::Point point, std::unique_ptr<ui::MenuModel> menu_model),
@@ -74,6 +76,8 @@
   void TearDownOnMainThread() override { webui_contents_.reset(); }
 
  protected:
+  static const std::string tab_query_js;
+
   ::testing::NiceMock<MockTabStripUIEmbedder> mock_embedder_;
   std::unique_ptr<content::WebContents> webui_contents_;
 
@@ -81,6 +85,20 @@
   base::test::ScopedFeatureList feature_override_;
 };
 
+// static
+const std::string TabStripUIBrowserTest::tab_query_js(
+    "document.querySelector('tabstrip-tab-list')"
+    "    .shadowRoot.querySelector('tabstrip-tab')");
+
+IN_PROC_BROWSER_TEST_F(TabStripUIBrowserTest, ActivatingTabClosesEmbedder) {
+  const std::string activate_tab_js = tab_query_js + ".click()";
+
+  EXPECT_CALL(mock_embedder_, CloseContainer()).Times(1);
+  ASSERT_TRUE(content::ExecJs(webui_contents_.get(), activate_tab_js,
+                              content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
+                              ISOLATED_WORLD_ID_CHROME_INTERNAL));
+}
+
 // Checks that the contextmenu event on a tab gets forwarded to the
 // TabStripUI::Embedder.
 IN_PROC_BROWSER_TEST_F(TabStripUIBrowserTest,
@@ -89,10 +107,8 @@
 
   const std::string invoke_menu_js =
       "const event ="
-      "    new MouseEvent('contextmenu', { clientX: 100, clientY: 50 });"
-      "document.querySelector('tabstrip-tab-list')"
-      "    .shadowRoot.querySelector('tabstrip-tab')"
-      "    .dispatchEvent(event)";
+      "    new MouseEvent('contextmenu', { clientX: 100, clientY: 50 });" +
+      tab_query_js + ".dispatchEvent(event)";
 
   EXPECT_CALL(mock_embedder_, ShowContextMenuAtPoint(gfx::Point(100, 50), _))
       .Times(1);
diff --git a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
index 27f147d..adb4444 100644
--- a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
+++ b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
@@ -173,6 +173,12 @@
   if (g_frame_timeout_ui_disabled_for_testing_)
     return;
 
+  // TODO(crbug.com/1014764): If we try to re-start the timeouts after UI has
+  // already been shown (e.g. a user takes their headset off for a permissions
+  // prompt). Then the prompt UI doesn't seem to be dismissed immediately.
+  if (!waiting_for_webxr_frame_)
+    return;
+
   if (frames_throttled_) {
     StopWebXrTimeout();
 
diff --git a/chrome/browser/web_applications/components/install_finalizer.h b/chrome/browser/web_applications/components/install_finalizer.h
index 5b4a6f2..44e92689 100644
--- a/chrome/browser/web_applications/components/install_finalizer.h
+++ b/chrome/browser/web_applications/components/install_finalizer.h
@@ -44,6 +44,10 @@
                                const FinalizeOptions& options,
                                InstallFinalizedCallback callback) = 0;
 
+  // Write the new WebApp data to disk and update the app.
+  virtual void FinalizeUpdate(const WebApplicationInfo& web_app_info,
+                              InstallFinalizedCallback callback) = 0;
+
   // Removes the external app for |app_url| from disk and registrar. Fails if
   // there is no installed external app for |app_url|.
   virtual void UninstallExternalWebApp(const GURL& app_url,
@@ -71,10 +75,6 @@
   virtual bool CanRevealAppShim() const = 0;
   virtual void RevealAppShim(const AppId& app_id) = 0;
 
-  virtual bool CanSkipAppUpdateForSync(
-      const AppId& app_id,
-      const WebApplicationInfo& web_app_info) const = 0;
-
   virtual bool CanUserUninstallFromSync(const AppId& app_id) const = 0;
 
   void SetSubsystems(AppRegistrar* registrar, WebAppUiManager* ui_manager);
diff --git a/chrome/browser/web_applications/components/install_manager.h b/chrome/browser/web_applications/components/install_manager.h
index e46c6ba..69ebcee 100644
--- a/chrome/browser/web_applications/components/install_manager.h
+++ b/chrome/browser/web_applications/components/install_manager.h
@@ -12,7 +12,6 @@
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/components/web_app_install_utils.h"
 #include "chrome/browser/web_applications/components/web_app_url_loader.h"
-#include "third_party/blink/public/common/manifest/manifest.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
 
 enum class WebappInstallSource;
@@ -109,15 +108,17 @@
   // Starts background installation or an update of a web app from the sync
   // system. |web_application_info| contains received sync data. Icons will be
   // downloaded from the icon URLs provided in |web_application_info|.
-  virtual void InstallOrUpdateWebAppFromSync(
+  virtual void InstallWebAppFromSync(
       const AppId& app_id,
       std::unique_ptr<WebApplicationInfo> web_application_info,
       OnceInstallCallback callback) = 0;
 
-  // Reinstall an existing web app with an updated manifest.
-  virtual void UpdateWebAppFromManifest(const AppId& app_id,
-                                        blink::Manifest manifest,
-                                        OnceInstallCallback callback) = 0;
+  // Reinstall an existing web app, will redownload icons and update them on
+  // disk.
+  virtual void UpdateWebAppFromInfo(
+      const AppId& app_id,
+      std::unique_ptr<WebApplicationInfo> web_application_info,
+      OnceInstallCallback callback) = 0;
 
   virtual void Shutdown() = 0;
 
diff --git a/chrome/browser/web_applications/components/manifest_update_manager.cc b/chrome/browser/web_applications/components/manifest_update_manager.cc
index 1bedfdb..63936805 100644
--- a/chrome/browser/web_applications/components/manifest_update_manager.cc
+++ b/chrome/browser/web_applications/components/manifest_update_manager.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/web_applications/components/manifest_update_manager.h"
 
+#include "base/metrics/histogram_macros.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/common/chrome_features.h"
 
@@ -101,6 +102,11 @@
 
 void ManifestUpdateManager::NotifyResult(const GURL& url,
                                          ManifestUpdateResult result) {
+  // Don't log kNoAppInScope because it will be far too noisy (most page loads
+  // will hit it).
+  if (result != ManifestUpdateResult::kNoAppInScope) {
+    UMA_HISTOGRAM_ENUMERATION("Webapp.Update.ManifestUpdateResult", result);
+  }
   if (result_callback_for_testing_)
     std::move(result_callback_for_testing_).Run(url, result);
 }
diff --git a/chrome/browser/web_applications/components/manifest_update_manager_browsertest.cc b/chrome/browser/web_applications/components/manifest_update_manager_browsertest.cc
index 6a9c2581..40c87289 100644
--- a/chrome/browser/web_applications/components/manifest_update_manager_browsertest.cc
+++ b/chrome/browser/web_applications/components/manifest_update_manager_browsertest.cc
@@ -9,6 +9,7 @@
 
 #include "base/strings/string_util.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/time/time.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/installable/installable_metrics.h"
@@ -33,7 +34,9 @@
 
 namespace {
 
-const char* kInstallableIconList = R"(
+constexpr char kUpdateHistogramName[] = "Webapp.Update.ManifestUpdateResult";
+
+constexpr char kInstallableIconList[] = R"(
   [
     {
       "src": "launcher-icon-4x.png",
@@ -43,7 +46,7 @@
   ]
 )";
 
-const char* kAnotherInstallableIconList = R"(
+constexpr char kAnotherInstallableIconList[] = R"(
   [
     {
       "src": "/banners/image-512px.png",
@@ -169,6 +172,28 @@
     return app_id;
   }
 
+  AppId InstallPolicyApp() {
+    const GURL app_url = GetAppURL();
+    base::RunLoop run_loop;
+    ExternalInstallOptions install_options(
+        app_url, blink::mojom::DisplayMode::kStandalone,
+        ExternalInstallSource::kExternalPolicy);
+    install_options.add_to_applications_menu = false;
+    install_options.add_to_desktop = false;
+    install_options.add_to_quick_launch_bar = false;
+    install_options.install_placeholder = true;
+    GetProvider().pending_app_manager().Install(
+        std::move(install_options),
+        base::BindLambdaForTesting(
+            [&](const GURL& installed_app_url, InstallResultCode code) {
+              EXPECT_EQ(installed_app_url, app_url);
+              EXPECT_EQ(code, InstallResultCode::kSuccessNewInstall);
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+    return GetProvider().registrar().LookupExternalAppId(app_url).value();
+  }
+
   void SetTimeOverride(base::Time time_override) {
     GetManifestUpdateManager(browser()).set_time_override_for_testing(
         time_override);
@@ -187,6 +212,8 @@
 
   net::EmbeddedTestServer::HandleRequestCallback request_override_;
 
+  base::HistogramTester histogram_tester_;
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 
@@ -204,6 +231,8 @@
 
   EXPECT_EQ(GetResultAfterPageLoad(GURL("http://example.org"), nullptr),
             ManifestUpdateResult::kNoAppInScope);
+
+  histogram_tester_.ExpectTotalCount(kUpdateHistogramName, 0);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, CheckIsThrottled) {
@@ -234,6 +263,11 @@
   SetTimeOverride(time_override);
   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
             ManifestUpdateResult::kAppUpToDate);
+
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kThrottled, 2);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppUpToDate, 3);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
@@ -247,6 +281,8 @@
   chrome::CloseTab(browser());
   EXPECT_EQ(std::move(awaiter).AwaitNextResult(),
             ManifestUpdateResult::kWebContentsDestroyed);
+  histogram_tester_.ExpectBucketCount(
+      kUpdateHistogramName, ManifestUpdateResult::kWebContentsDestroyed, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
@@ -260,11 +296,13 @@
   GetProvider().install_finalizer().UninstallWebApp(app_id, base::DoNothing());
   EXPECT_EQ(std::move(awaiter).AwaitNextResult(),
             ManifestUpdateResult::kAppUninstalled);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppUninstalled, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
                        CheckIgnoresWhitespaceDifferences) {
-  const char* manifest_template = R"(
+  constexpr char kManifestTemplate[] = R"(
     {
       "name": "Test app",
       "start_url": ".",
@@ -274,17 +312,19 @@
       $2
     }
   )";
-  OverrideManifest(manifest_template, {kInstallableIconList, ""});
+  OverrideManifest(kManifestTemplate, {kInstallableIconList, ""});
   AppId app_id = InstallWebApp();
 
-  OverrideManifest(manifest_template, {kInstallableIconList, "\n\n\n\n"});
+  OverrideManifest(kManifestTemplate, {kInstallableIconList, "\n\n\n\n"});
   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
             ManifestUpdateResult::kAppUpToDate);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppUpToDate, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
                        CheckIgnoresNameChange) {
-  const char* manifest_template = R"(
+  constexpr char kManifestTemplate[] = R"(
     {
       "name": "$1",
       "start_url": ".",
@@ -293,18 +333,20 @@
       "icons": $2
     }
   )";
-  OverrideManifest(manifest_template, {"Test app name", kInstallableIconList});
+  OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList});
   AppId app_id = InstallWebApp();
 
-  OverrideManifest(manifest_template,
+  OverrideManifest(kManifestTemplate,
                    {"Different app name", kInstallableIconList});
   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
             ManifestUpdateResult::kAppUpToDate);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppUpToDate, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
                        CheckIgnoresShortNameChange) {
-  const char* manifest_template = R"(
+  constexpr char kManifestTemplate[] = R"(
     {
       "name": "Test app name",
       "short_name": "$1",
@@ -314,19 +356,21 @@
       "icons": $2
     }
   )";
-  OverrideManifest(manifest_template,
+  OverrideManifest(kManifestTemplate,
                    {"Short test app name", kInstallableIconList});
   AppId app_id = InstallWebApp();
 
-  OverrideManifest(manifest_template,
+  OverrideManifest(kManifestTemplate,
                    {"Different short test app name", kInstallableIconList});
   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
             ManifestUpdateResult::kAppUpToDate);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppUpToDate, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
                        CheckIgnoresStartUrlChange) {
-  const char* manifest_template = R"(
+  constexpr char kManifestTemplate[] = R"(
     {
       "name": "Test app name",
       "start_url": "$1",
@@ -335,17 +379,19 @@
       "icons": $2
     }
   )";
-  OverrideManifest(manifest_template, {"a.html", kInstallableIconList});
+  OverrideManifest(kManifestTemplate, {"a.html", kInstallableIconList});
   AppId app_id = InstallWebApp();
 
-  OverrideManifest(manifest_template, {"b.html", kInstallableIconList});
+  OverrideManifest(kManifestTemplate, {"b.html", kInstallableIconList});
   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
             ManifestUpdateResult::kAppUpToDate);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppUpToDate, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
                        CheckIgnoresNoManifestChange) {
-  const char* manifest_template = R"(
+  constexpr char kManifestTemplate[] = R"(
     {
       "name": "Test app name",
       "start_url": ".",
@@ -354,15 +400,17 @@
       "icons": $1
     }
   )";
-  OverrideManifest(manifest_template, {kInstallableIconList});
+  OverrideManifest(kManifestTemplate, {kInstallableIconList});
   AppId app_id = InstallWebApp();
   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
             ManifestUpdateResult::kAppUpToDate);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppUpToDate, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
                        CheckIgnoresInvalidManifest) {
-  const char* manifest_template = R"(
+  constexpr char kManifestTemplate[] = R"(
     {
       "name": "Test app name",
       "start_url": ".",
@@ -372,17 +420,19 @@
       $2
     }
   )";
-  OverrideManifest(manifest_template, {kInstallableIconList, ""});
+  OverrideManifest(kManifestTemplate, {kInstallableIconList, ""});
   AppId app_id = InstallWebApp();
-  OverrideManifest(manifest_template, {kInstallableIconList,
+  OverrideManifest(kManifestTemplate, {kInstallableIconList,
                                        "invalid manifest syntax !@#$%^*&()"});
   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
             ManifestUpdateResult::kAppDataInvalid);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppDataInvalid, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
                        CheckIgnoresNonLocalApps) {
-  const char* manifest_template = R"(
+  constexpr char kManifestTemplate[] = R"(
     {
       "name": "Test app name",
       "start_url": ".",
@@ -392,16 +442,17 @@
       "theme_color": "$2"
     }
   )";
-  OverrideManifest(manifest_template, {kInstallableIconList, "blue"});
+  OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"});
   AppId app_id = InstallWebApp();
 
   GetProvider().registry_controller().SetAppIsLocallyInstalledForTesting(app_id,
                                                                          false);
   EXPECT_FALSE(GetProvider().registrar().IsLocallyInstalled(app_id));
 
-  OverrideManifest(manifest_template, {kInstallableIconList, "red"});
+  OverrideManifest(kManifestTemplate, {kInstallableIconList, "red"});
   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
             ManifestUpdateResult::kNoAppInScope);
+  histogram_tester_.ExpectTotalCount(kUpdateHistogramName, 0);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
@@ -423,29 +474,12 @@
 
   // Install via PendingAppManager, the redirect should cause it to install a
   // placeholder app.
-  base::RunLoop run_loop;
-  ExternalInstallOptions install_options(
-      app_url, blink::mojom::DisplayMode::kStandalone,
-      ExternalInstallSource::kExternalPolicy);
-  install_options.add_to_applications_menu = false;
-  install_options.add_to_desktop = false;
-  install_options.add_to_quick_launch_bar = false;
-  install_options.install_placeholder = true;
-  GetProvider().pending_app_manager().Install(
-      std::move(install_options),
-      base::BindLambdaForTesting(
-          [&](const GURL& installed_app_url, InstallResultCode code) {
-            EXPECT_EQ(installed_app_url, app_url);
-            EXPECT_EQ(code, InstallResultCode::kSuccessNewInstall);
-            run_loop.Quit();
-          }));
-  run_loop.Run();
-  AppId app_id = GetProvider().registrar().LookupExternalAppId(app_url).value();
+  AppId app_id = InstallPolicyApp();
   EXPECT_TRUE(GetProvider().registrar().IsPlaceholderApp(app_id));
 
   // Manifest updating should ignore non-redirect loads for placeholder apps
   // because the PendingAppManager will handle these.
-  const char* manifest_template = R"(
+  constexpr char kManifestTemplate[] = R"(
     {
       "name": "Test app name",
       "start_url": ".",
@@ -454,14 +488,16 @@
       "icons": $1
     }
   )";
-  OverrideManifest(manifest_template, {kInstallableIconList});
+  OverrideManifest(kManifestTemplate, {kInstallableIconList});
   EXPECT_EQ(GetResultAfterPageLoad(app_url, &app_id),
             ManifestUpdateResult::kAppIsPlaceholder);
+  histogram_tester_.ExpectBucketCount(
+      kUpdateHistogramName, ManifestUpdateResult::kAppIsPlaceholder, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
                        CheckFindsThemeColorChange) {
-  const char* manifest_template = R"(
+  constexpr char kManifestTemplate[] = R"(
     {
       "name": "Test app name",
       "start_url": ".",
@@ -471,19 +507,50 @@
       "theme_color": "$2"
     }
   )";
-  OverrideManifest(manifest_template, {kInstallableIconList, "blue"});
+  OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"});
   AppId app_id = InstallWebApp();
+  EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorBLUE);
 
-  OverrideManifest(manifest_template, {kInstallableIconList, "red"});
-  // TODO(crbug.com/926083): Implement successful updating.
+  OverrideManifest(kManifestTemplate, {kInstallableIconList, "red"});
   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
-            ManifestUpdateResult::kAppUpdateFailed);
+            ManifestUpdateResult::kAppUpdated);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppUpdated, 1);
+  EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorRED);
+}
+
+IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, CheckKeepsSameName) {
+  constexpr char kManifestTemplate[] = R"(
+    {
+      "name": "$1",
+      "start_url": ".",
+      "scope": "/",
+      "display": "standalone",
+      "icons": $2,
+      "theme_color": "$3"
+    }
+  )";
+  OverrideManifest(kManifestTemplate,
+                   {"App name 1", kInstallableIconList, "blue"});
+  AppId app_id = InstallWebApp();
+  EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorBLUE);
+  EXPECT_EQ(GetProvider().registrar().GetAppShortName(app_id), "App name 1");
+
+  OverrideManifest(kManifestTemplate,
+                   {"App name 2", kInstallableIconList, "red"});
+  EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
+            ManifestUpdateResult::kAppUpdated);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppUpdated, 1);
+  EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorRED);
+  // The app name must not change without user confirmation.
+  EXPECT_EQ(GetProvider().registrar().GetAppShortName(app_id), "App name 1");
 }
 
 // TODO(crbug.com/926083): Implement icon URL change detection.
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
                        DISABLED_CheckFindsIconUrlChange) {
-  const char* manifest_template = R"(
+  constexpr char kManifestTemplate[] = R"(
     {
       "name": "Test app name",
       "start_url": ".",
@@ -492,13 +559,44 @@
       "icons": $1
     }
   )";
-  OverrideManifest(manifest_template, {kInstallableIconList});
+  OverrideManifest(kManifestTemplate, {kInstallableIconList});
   AppId app_id = InstallWebApp();
 
-  OverrideManifest(manifest_template, {kAnotherInstallableIconList});
-  // TODO(crbug.com/926083): Implement successful updating.
+  OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList});
+  // TODO(crbug.com/926083): Implement icon updating.
   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
             ManifestUpdateResult::kAppUpdateFailed);
+  histogram_tester_.ExpectBucketCount(
+      kUpdateHistogramName, ManifestUpdateResult::kAppUpdateFailed, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
+                       CheckUpdatedPolicyAppsNotUninstallable) {
+  constexpr char kManifestTemplate[] = R"(
+    {
+      "name": "Test app name",
+      "start_url": ".",
+      "scope": "/",
+      "display": "standalone",
+      "theme_color": "$1",
+      "icons": $2
+    }
+  )";
+  OverrideManifest(kManifestTemplate, {"blue", kInstallableIconList});
+  AppId app_id = InstallPolicyApp();
+  EXPECT_FALSE(
+      GetProvider().install_finalizer().CanUserUninstallFromSync(app_id));
+
+  OverrideManifest(kManifestTemplate, {"red", kInstallableIconList});
+  EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
+            ManifestUpdateResult::kAppUpdated);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppUpdated, 1);
+
+  // Policy installed apps should continue to be not uninstallable by the user
+  // after updating.
+  EXPECT_FALSE(
+      GetProvider().install_finalizer().CanUserUninstallFromSync(app_id));
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/manifest_update_task.cc b/chrome/browser/web_applications/components/manifest_update_task.cc
index 49d1beed..3641d501 100644
--- a/chrome/browser/web_applications/components/manifest_update_task.cc
+++ b/chrome/browser/web_applications/components/manifest_update_task.cc
@@ -8,7 +8,9 @@
 #include "chrome/browser/installable/installable_manager.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/install_manager.h"
+#include "chrome/browser/web_applications/components/web_app_install_utils.h"
 #include "chrome/browser/web_applications/components/web_app_ui_manager.h"
+#include "chrome/common/web_application_info.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
 
 namespace web_app {
@@ -112,44 +114,51 @@
 void ManifestUpdateTask::OnAllAppWindowsClosed(blink::Manifest manifest) {
   DCHECK_EQ(stage_, Stage::kPendingWindowsClosed);
 
-  // The app's name must not change due to an automatic update.
-  // TODO: Support name/short_name distinction.
-  manifest.name = base::NullableString16(
-      base::UTF8ToUTF16(registrar_.GetAppShortName(app_id_)));
-  manifest.short_name = base::NullableString16();
+  auto web_application_info = std::make_unique<WebApplicationInfo>();
+  UpdateWebAppInfoFromManifest(manifest, web_application_info.get(),
+                               ForInstallableSite::kYes);
 
-  // Preserve the user's choice of launch container (excluding full screen).
-  // TODO(crbug.com/1009909): Test kMinimalUi.
-  auto display_mode = registrar_.GetAppDisplayMode(app_id_);
-  switch (display_mode) {
+  // The app's name must not change due to an automatic update.
+  web_application_info->title =
+      base::UTF8ToUTF16(registrar_.GetAppShortName(app_id_));
+
+  // Preserve the user's choice of launch container (excluding fullscreen).
+  switch (registrar_.GetAppDisplayMode(app_id_)) {
     case blink::mojom::DisplayMode::kBrowser:
+      web_application_info->open_as_window = false;
+      break;
     case blink::mojom::DisplayMode::kMinimalUi:
     case blink::mojom::DisplayMode::kStandalone:
-      manifest.display = display_mode;
-      break;
     case blink::mojom::DisplayMode::kFullscreen:
-      // Fall back to a windowed mode.
-      manifest.display = blink::mojom::DisplayMode::kStandalone;
+      web_application_info->open_as_window = true;
       break;
     case blink::mojom::DisplayMode::kUndefined:
+      NOTREACHED();
       break;
   }
 
   stage_ = Stage::kPendingInstallation;
-  install_manager_.UpdateWebAppFromManifest(
-      app_id_, std::move(manifest),
-      base::Bind(&ManifestUpdateTask::OnInstallationComplete, AsWeakPtr()));
+  install_manager_.UpdateWebAppFromInfo(
+      app_id_, std::move(web_application_info),
+      base::Bind(&ManifestUpdateTask::OnInstallationComplete, AsWeakPtr(),
+                 std::move(manifest)));
 }
 
-void ManifestUpdateTask::OnInstallationComplete(const AppId& app_id,
+void ManifestUpdateTask::OnInstallationComplete(blink::Manifest manifest,
+                                                const AppId& app_id,
                                                 InstallResultCode code) {
   DCHECK_EQ(stage_, Stage::kPendingInstallation);
-  DCHECK_EQ(app_id_, app_id);
-  DCHECK(!IsSuccess(code) ||
-         code == InstallResultCode::kSuccessAlreadyInstalled);
 
-  DestroySelf(IsSuccess(code) ? ManifestUpdateResult::kAppUpdated
-                              : ManifestUpdateResult::kAppUpdateFailed);
+  if (!IsSuccess(code)) {
+    DestroySelf(ManifestUpdateResult::kAppUpdateFailed);
+    return;
+  }
+
+  DCHECK_EQ(app_id_, app_id);
+  DCHECK(!IsUpdateNeededForManifest(manifest));
+  DCHECK_EQ(code, InstallResultCode::kSuccessAlreadyInstalled);
+
+  DestroySelf(ManifestUpdateResult::kAppUpdated);
 }
 
 void ManifestUpdateTask::DestroySelf(ManifestUpdateResult result) {
diff --git a/chrome/browser/web_applications/components/manifest_update_task.h b/chrome/browser/web_applications/components/manifest_update_task.h
index 5d9e783..874e1ecf 100644
--- a/chrome/browser/web_applications/components/manifest_update_task.h
+++ b/chrome/browser/web_applications/components/manifest_update_task.h
@@ -20,16 +20,18 @@
 class InstallManager;
 enum class InstallResultCode;
 
+// This enum is recorded by UMA, the numeric values must not change.
 enum ManifestUpdateResult {
-  kNoAppInScope,
-  kThrottled,
-  kWebContentsDestroyed,
-  kAppUninstalled,
-  kAppIsPlaceholder,
-  kAppUpToDate,
-  kAppDataInvalid,
-  kAppUpdateFailed,
-  kAppUpdated,
+  kNoAppInScope = 0,
+  kThrottled = 1,
+  kWebContentsDestroyed = 2,
+  kAppUninstalled = 3,
+  kAppIsPlaceholder = 4,
+  kAppUpToDate = 5,
+  kAppDataInvalid = 6,
+  kAppUpdateFailed = 7,
+  kAppUpdated = 8,
+  kMaxValue = kAppUpdated,
 };
 
 // Used by UpdateManager on a per web app basis for checking and performing
@@ -71,7 +73,9 @@
   void OnDidGetInstallableData(const InstallableData& data);
   bool IsUpdateNeededForManifest(const blink::Manifest& manifest) const;
   void OnAllAppWindowsClosed(blink::Manifest manifest);
-  void OnInstallationComplete(const AppId& app_id, InstallResultCode code);
+  void OnInstallationComplete(blink::Manifest manifest,
+                              const AppId& app_id,
+                              InstallResultCode code);
   void DestroySelf(ManifestUpdateResult result);
 
   const AppRegistrar& registrar_;
diff --git a/chrome/browser/web_applications/components/web_app_icon_downloader.cc b/chrome/browser/web_applications/components/web_app_icon_downloader.cc
index 917b022..5bbd3bd8 100644
--- a/chrome/browser/web_applications/components/web_app_icon_downloader.cc
+++ b/chrome/browser/web_applications/components/web_app_icon_downloader.cc
@@ -121,6 +121,9 @@
       case Histogram::kForSync:
         histogram_name = "WebApp.Icon.HttpStatusCodeClassOnSync";
         break;
+      case Histogram::kForUpdate:
+        histogram_name = "WebApp.Icon.HttpStatusCodeClassOnUpdate";
+        break;
     }
     DCHECK_LE(100, http_status_code);
     DCHECK_GT(600, http_status_code);
diff --git a/chrome/browser/web_applications/components/web_app_icon_downloader.h b/chrome/browser/web_applications/components/web_app_icon_downloader.h
index a0141bb9..9954c08 100644
--- a/chrome/browser/web_applications/components/web_app_icon_downloader.h
+++ b/chrome/browser/web_applications/components/web_app_icon_downloader.h
@@ -35,6 +35,7 @@
   enum class Histogram {
     kForCreate,
     kForSync,
+    kForUpdate,
   };
 
   using WebAppIconDownloaderCallback =
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
index b9d8db9..0b1be89 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
@@ -11,7 +11,6 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/optional.h"
-#include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/launch_util.h"
@@ -23,6 +22,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/web_application_info.h"
+#include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/uninstall_reason.h"
@@ -102,6 +102,35 @@
   crx_installer->InstallWebApp(web_app_info);
 }
 
+void BookmarkAppInstallFinalizer::FinalizeUpdate(
+    const WebApplicationInfo& web_app_info,
+    InstallFinalizedCallback callback) {
+  web_app::AppId expected_app_id =
+      web_app::GenerateAppIdFromURL(web_app_info.app_url);
+
+  const Extension* existing_extension = GetEnabledExtension(expected_app_id);
+  if (!existing_extension) {
+    DCHECK(ExtensionRegistry::Get(profile_)->GetInstalledExtension(
+        expected_app_id));
+    std::move(callback).Run(web_app::AppId(),
+                            web_app::InstallResultCode::kWebAppDisabled);
+    return;
+  }
+  DCHECK(existing_extension->from_bookmark());
+
+  scoped_refptr<CrxInstaller> crx_installer =
+      crx_installer_factory_.Run(profile_);
+  crx_installer->set_installer_callback(
+      base::BindOnce(&BookmarkAppInstallFinalizer::OnExtensionUpdated,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(expected_app_id),
+                     std::move(callback), crx_installer));
+  crx_installer->InitializeCreationFlagsForUpdate(existing_extension,
+                                                  Extension::NO_FLAGS);
+  crx_installer->set_install_source(existing_extension->location());
+
+  crx_installer->InstallWebApp(web_app_info);
+}
+
 void BookmarkAppInstallFinalizer::UninstallExternalWebApp(
     const GURL& app_url,
     UninstallWebAppCallback callback) {
@@ -175,31 +204,6 @@
 #endif  // defined(OS_MACOSX)
 }
 
-bool BookmarkAppInstallFinalizer::CanSkipAppUpdateForSync(
-    const web_app::AppId& app_id,
-    const WebApplicationInfo& web_app_info) const {
-  ExtensionRegistry* extension_registry = ExtensionRegistry::Get(profile_);
-  DCHECK(extension_registry);
-
-  const Extension* extension =
-      extension_registry->GetInstalledExtension(app_id);
-  if (!extension)
-    return false;
-
-  // We can skip if there are no bookmark app details that need updating.
-  // TODO(loyso): We need to check more data fields. crbug.com/949427.
-  const std::string extension_sync_data_name =
-      base::UTF16ToUTF8(web_app_info.title);
-  const std::string bookmark_app_description =
-      base::UTF16ToUTF8(web_app_info.description);
-  if (extension->non_localized_name() == extension_sync_data_name &&
-      extension->description() == bookmark_app_description) {
-    return true;
-  }
-
-  return false;
-}
-
 bool BookmarkAppInstallFinalizer::CanUserUninstallFromSync(
     const web_app::AppId& app_id) const {
   const Extension* app = GetEnabledExtension(app_id);
@@ -225,7 +229,7 @@
     const GURL& app_url,
     LaunchType launch_type,
     bool is_locally_installed,
-    web_app::InstallFinalizer::InstallFinalizedCallback callback,
+    InstallFinalizedCallback callback,
     scoped_refptr<CrxInstaller> crx_installer,
     const base::Optional<CrxInstallError>& error) {
   if (error) {
@@ -238,6 +242,9 @@
   DCHECK(extension);
 
   if (extension != GetEnabledExtension(extension->id())) {
+    LOG(ERROR) << "Installed extension was disabled: "
+               << ExtensionPrefs::Get(profile_)->GetDisableReasons(
+                      extension->id());
     std::move(callback).Run(web_app::AppId(),
                             web_app::InstallResultCode::kWebAppDisabled);
     return;
@@ -255,4 +262,29 @@
                           web_app::InstallResultCode::kSuccessNewInstall);
 }
 
+void BookmarkAppInstallFinalizer::OnExtensionUpdated(
+    const web_app::AppId& expected_app_id,
+    InstallFinalizedCallback callback,
+    scoped_refptr<CrxInstaller> crx_installer,
+    const base::Optional<CrxInstallError>& error) {
+  if (error) {
+    std::move(callback).Run(web_app::AppId(),
+                            web_app::InstallResultCode::kFailedUnknownReason);
+    return;
+  }
+
+  const Extension* extension = crx_installer->extension();
+  DCHECK(extension);
+  DCHECK_EQ(extension->id(), expected_app_id);
+
+  if (extension != GetEnabledExtension(extension->id())) {
+    std::move(callback).Run(web_app::AppId(),
+                            web_app::InstallResultCode::kWebAppDisabled);
+    return;
+  }
+
+  std::move(callback).Run(extension->id(),
+                          web_app::InstallResultCode::kSuccessAlreadyInstalled);
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
index c7d5a26..65ca7c2 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
@@ -35,6 +35,8 @@
   void FinalizeInstall(const WebApplicationInfo& web_app_info,
                        const FinalizeOptions& options,
                        InstallFinalizedCallback callback) override;
+  void FinalizeUpdate(const WebApplicationInfo& web_app_info,
+                      InstallFinalizedCallback callback) override;
   void UninstallExternalWebApp(const GURL& app_url,
                                UninstallWebAppCallback callback) override;
   void UninstallWebApp(const web_app::AppId& app_id,
@@ -45,9 +47,6 @@
                          CreateOsShortcutsCallback callback) override;
   bool CanRevealAppShim() const override;
   void RevealAppShim(const web_app::AppId& app_id) override;
-  bool CanSkipAppUpdateForSync(
-      const web_app::AppId& app_id,
-      const WebApplicationInfo& web_app_info) const override;
   bool CanUserUninstallFromSync(const web_app::AppId& app_id) const override;
 
   using CrxInstallerFactory =
@@ -59,13 +58,17 @@
   // May return nullptr if app_id is not found or extension is disabled.
   const Extension* GetEnabledExtension(const web_app::AppId& app_id) const;
 
-  void OnExtensionInstalled(
-      const GURL& app_url,
-      LaunchType launch_type,
-      bool is_locally_installed,
-      web_app::InstallFinalizer::InstallFinalizedCallback callback,
-      scoped_refptr<CrxInstaller> crx_installer,
-      const base::Optional<CrxInstallError>& error);
+  void OnExtensionInstalled(const GURL& app_url,
+                            LaunchType launch_type,
+                            bool is_locally_installed,
+                            InstallFinalizedCallback callback,
+                            scoped_refptr<CrxInstaller> crx_installer,
+                            const base::Optional<CrxInstallError>& error);
+
+  void OnExtensionUpdated(const web_app::AppId& expected_app_id,
+                          InstallFinalizedCallback callback,
+                          scoped_refptr<CrxInstaller> crx_installer,
+                          const base::Optional<CrxInstallError>& error);
 
   CrxInstallerFactory crx_installer_factory_;
   web_app::ExternallyInstalledWebAppPrefs externally_installed_app_prefs_;
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
index 4887ee74..3405e0db 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
@@ -368,43 +368,6 @@
   run_loop.Run();
 }
 
-TEST_F(BookmarkAppInstallFinalizerTest, CanSkipAppUpdateForSync) {
-  auto info = std::make_unique<WebApplicationInfo>();
-  info->app_url = kWebAppUrl;
-  info->title = base::ASCIIToUTF16("Title1");
-  info->description = base::ASCIIToUTF16("Description1");
-
-  const web_app::AppId app_id = web_app::GenerateAppIdFromURL(info->app_url);
-
-  EXPECT_FALSE(finalizer().CanSkipAppUpdateForSync(app_id, *info));
-
-  base::RunLoop run_loop;
-  web_app::InstallFinalizer::FinalizeOptions options;
-  options.install_source = WebappInstallSource::SYNC;
-
-  finalizer().FinalizeInstall(
-      *info, options,
-      base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
-                                     web_app::InstallResultCode code) {
-        EXPECT_EQ(web_app::InstallResultCode::kSuccessNewInstall, code);
-        EXPECT_EQ(app_id, installed_app_id);
-        run_loop.Quit();
-      }));
-  run_loop.Run();
-
-  EXPECT_TRUE(finalizer().CanSkipAppUpdateForSync(app_id, *info));
-
-  WebApplicationInfo info_with_diff_title = *info;
-  info_with_diff_title.title = base::ASCIIToUTF16("Title2");
-  EXPECT_FALSE(
-      finalizer().CanSkipAppUpdateForSync(app_id, info_with_diff_title));
-
-  WebApplicationInfo info_with_diff_description = *info;
-  info_with_diff_description.title = base::ASCIIToUTF16("Description2");
-  EXPECT_FALSE(
-      finalizer().CanSkipAppUpdateForSync(app_id, info_with_diff_description));
-}
-
 TEST_F(BookmarkAppInstallFinalizerTest, UninstallExternalWebApp_Successful) {
   InstallExternalApp(kWebAppUrl);
   ASSERT_EQ(1u, enabled_extensions().size());
diff --git a/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc b/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
index 0de9001..8490e0a24 100644
--- a/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
+++ b/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
@@ -635,7 +635,7 @@
                    .empty());
 }
 
-TEST_F(InstallManagerBookmarkAppTest, InstallOrUpdateWebAppFromSync) {
+TEST_F(InstallManagerBookmarkAppTest, InstallWebAppFromSync) {
   CreateEmptyDataRetriever();
 
   EXPECT_EQ(0u, registry()->enabled_extensions().size());
@@ -663,7 +663,7 @@
   {
     base::RunLoop run_loop;
 
-    provider->install_manager().InstallOrUpdateWebAppFromSync(
+    provider->install_manager().InstallWebAppFromSync(
         app_id, std::move(web_app_info),
         base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
                                        web_app::InstallResultCode code) {
@@ -709,11 +709,11 @@
   {
     base::RunLoop run_loop;
 
-    provider->install_manager().InstallOrUpdateWebAppFromSync(
+    provider->install_manager().InstallWebAppFromSync(
         app_id, std::move(web_app_info2),
         base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
                                        web_app::InstallResultCode code) {
-          EXPECT_EQ(web_app::InstallResultCode::kSuccessNewInstall, code);
+          EXPECT_EQ(web_app::InstallResultCode::kSuccessAlreadyInstalled, code);
           EXPECT_EQ(app_id, installed_app_id);
           run_loop.Quit();
         }));
@@ -722,15 +722,16 @@
   }
 
   {
+    // New fields from sync are not deployed as they are now managed by the
+    // ManifestUpdateManager.
     EXPECT_EQ(1u, registry()->enabled_extensions().size());
     const Extension* extension =
         registry()->enabled_extensions().begin()->get();
     EXPECT_TRUE(extension->from_bookmark());
-    EXPECT_EQ(kAlternativeAppTitle, extension->name());
+    EXPECT_EQ(kAppTitle, extension->name());
     EXPECT_EQ(kAppDescription, extension->description());
     EXPECT_EQ(kAppUrl, AppLaunchInfo::GetLaunchWebURL(extension));
-    EXPECT_EQ(GURL(kAppAlternativeScope),
-              GetScopeURLFromBookmarkApp(extension));
+    EXPECT_EQ(GURL(kAppScope), GetScopeURLFromBookmarkApp(extension));
     EXPECT_FALSE(extensions::IconsInfo::GetIconResource(
                      extension, kIconSizeSmall, ExtensionIconSet::MATCH_EXACTLY)
                      .empty());
diff --git a/chrome/browser/web_applications/extensions/pending_app_install_task_unittest.cc b/chrome/browser/web_applications/extensions/pending_app_install_task_unittest.cc
index 6ea9823..509f479 100644
--- a/chrome/browser/web_applications/extensions/pending_app_install_task_unittest.cc
+++ b/chrome/browser/web_applications/extensions/pending_app_install_task_unittest.cc
@@ -174,6 +174,11 @@
             }));
   }
 
+  void FinalizeUpdate(const WebApplicationInfo& web_app_info,
+                      InstallFinalizedCallback callback) override {
+    NOTREACHED();
+  }
+
   void UninstallExternalWebApp(const GURL& app_url,
                                UninstallWebAppCallback callback) override {
     DCHECK(base::Contains(next_uninstall_external_web_app_results_, app_url));
@@ -239,13 +244,6 @@
     ++num_reveal_appshim_calls_;
   }
 
-  bool CanSkipAppUpdateForSync(
-      const AppId& app_id,
-      const WebApplicationInfo& web_app_info) const override {
-    NOTIMPLEMENTED();
-    return true;
-  }
-
   bool CanUserUninstallFromSync(const AppId& app_id) const override {
     NOTIMPLEMENTED();
     return false;
diff --git a/chrome/browser/web_applications/test/test_install_finalizer.cc b/chrome/browser/web_applications/test/test_install_finalizer.cc
index 5a6d45b..87fbe20c 100644
--- a/chrome/browser/web_applications/test/test_install_finalizer.cc
+++ b/chrome/browser/web_applications/test/test_install_finalizer.cc
@@ -29,24 +29,16 @@
     const WebApplicationInfo& web_app_info,
     const FinalizeOptions& options,
     InstallFinalizedCallback callback) {
-  AppId app_id = GetAppIdForUrl(web_app_info.app_url);
-  if (next_app_id_.has_value()) {
-    app_id = next_app_id_.value();
-    next_app_id_.reset();
-  }
-
-  InstallResultCode code = InstallResultCode::kSuccessNewInstall;
-  if (next_result_code_.has_value()) {
-    code = next_result_code_.value();
-    next_result_code_.reset();
-  }
-
-  // Store input data copies for inspecting in tests.
-  web_app_info_copy_ = std::make_unique<WebApplicationInfo>(web_app_info);
   finalize_options_list_.push_back(options);
+  Finalize(web_app_info, InstallResultCode::kSuccessNewInstall,
+           std::move(callback));
+}
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), app_id, code));
+void TestInstallFinalizer::FinalizeUpdate(
+    const WebApplicationInfo& web_app_info,
+    InstallFinalizedCallback callback) {
+  Finalize(web_app_info, InstallResultCode::kSuccessAlreadyInstalled,
+           std::move(callback));
 }
 
 void TestInstallFinalizer::UninstallExternalWebApp(
@@ -109,12 +101,6 @@
   ++num_reveal_appshim_calls_;
 }
 
-bool TestInstallFinalizer::CanSkipAppUpdateForSync(
-    const AppId& app_id,
-    const WebApplicationInfo& web_app_info) const {
-  return next_can_skip_app_update_for_sync_;
-}
-
 bool TestInstallFinalizer::CanUserUninstallFromSync(const AppId& app_id) const {
   NOTIMPLEMENTED();
   return false;
@@ -134,9 +120,25 @@
   next_uninstall_external_web_app_results_[app_url] = uninstalled;
 }
 
-void TestInstallFinalizer::SetNextCanSkipAppUpdateForSync(
-    bool can_skip_app_update_for_sync) {
-  next_can_skip_app_update_for_sync_ = can_skip_app_update_for_sync;
+void TestInstallFinalizer::Finalize(const WebApplicationInfo& web_app_info,
+                                    InstallResultCode code,
+                                    InstallFinalizedCallback callback) {
+  AppId app_id = GetAppIdForUrl(web_app_info.app_url);
+  if (next_app_id_.has_value()) {
+    app_id = next_app_id_.value();
+    next_app_id_.reset();
+  }
+
+  if (next_result_code_.has_value()) {
+    code = next_result_code_.value();
+    next_result_code_.reset();
+  }
+
+  // Store input data copies for inspecting in tests.
+  web_app_info_copy_ = std::make_unique<WebApplicationInfo>(web_app_info);
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), app_id, code));
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/test/test_install_finalizer.h b/chrome/browser/web_applications/test/test_install_finalizer.h
index 36eeb7e..e0410b0 100644
--- a/chrome/browser/web_applications/test/test_install_finalizer.h
+++ b/chrome/browser/web_applications/test/test_install_finalizer.h
@@ -28,6 +28,8 @@
   void FinalizeInstall(const WebApplicationInfo& web_app_info,
                        const FinalizeOptions& options,
                        InstallFinalizedCallback callback) override;
+  void FinalizeUpdate(const WebApplicationInfo& web_app_info,
+                      InstallFinalizedCallback callback) override;
   void UninstallExternalWebApp(const GURL& app_url,
                                UninstallWebAppCallback callback) override;
   void UninstallWebApp(const AppId& app_id,
@@ -45,16 +47,12 @@
                    content::WebContents* web_contents) override;
   bool CanRevealAppShim() const override;
   void RevealAppShim(const AppId& app_id) override;
-  bool CanSkipAppUpdateForSync(
-      const AppId& app_id,
-      const WebApplicationInfo& web_app_info) const override;
   bool CanUserUninstallFromSync(const AppId& app_id) const override;
 
   void SetNextFinalizeInstallResult(const AppId& app_id,
                                     InstallResultCode code);
   void SetNextUninstallExternalWebAppResult(const GURL& app_url,
                                             bool uninstalled);
-  void SetNextCanSkipAppUpdateForSync(bool can_skip_app_update_for_sync);
 
   std::unique_ptr<WebApplicationInfo> web_app_info() {
     return std::move(web_app_info_copy_);
@@ -76,6 +74,10 @@
   }
 
  private:
+  void Finalize(const WebApplicationInfo& web_app_info,
+                InstallResultCode code,
+                InstallFinalizedCallback callback);
+
   std::unique_ptr<WebApplicationInfo> web_app_info_copy_;
   std::vector<FinalizeOptions> finalize_options_list_;
   std::vector<GURL> uninstall_external_web_app_urls_;
@@ -83,7 +85,6 @@
   base::Optional<AppId> next_app_id_;
   base::Optional<InstallResultCode> next_result_code_;
   std::map<GURL, bool> next_uninstall_external_web_app_results_;
-  bool next_can_skip_app_update_for_sync_ = false;
 
   int num_create_os_shortcuts_calls_ = 0;
   int num_reparent_tab_calls_ = 0;
diff --git a/chrome/browser/web_applications/web_app_icon_manager.cc b/chrome/browser/web_applications/web_app_icon_manager.cc
index f983eb9..3c5b064 100644
--- a/chrome/browser/web_applications/web_app_icon_manager.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/web_applications/file_utils_wrapper.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/common/web_application_info.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/codec/png_codec.h"
@@ -84,14 +85,14 @@
 
 bool WriteIcons(FileUtilsWrapper* utils,
                 const base::FilePath& app_dir,
-                const WebApplicationInfo& web_app_info) {
+                const std::vector<WebApplicationIconInfo>& icon_infos) {
   const base::FilePath icons_dir = app_dir.Append(kIconsDirectoryName);
   if (!utils->CreateDirectory(icons_dir)) {
     LOG(ERROR) << "Could not create icons directory.";
     return false;
   }
 
-  for (const WebApplicationIconInfo& icon_info : web_app_info.icons) {
+  for (const WebApplicationIconInfo& icon_info : icon_infos) {
     // Skip unfetched bitmaps.
     if (icon_info.data.colorType() == kUnknown_SkColorType)
       continue;
@@ -104,11 +105,11 @@
 }
 
 // Performs blocking I/O. May be called on another thread.
-// Returns true if no errors occured.
+// Returns true if no errors occurred.
 bool WriteDataBlocking(std::unique_ptr<FileUtilsWrapper> utils,
                        base::FilePath web_apps_directory,
                        AppId app_id,
-                       std::unique_ptr<WebApplicationInfo> web_app_info) {
+                       std::vector<WebApplicationIconInfo> icon_infos) {
   const base::FilePath temp_dir = GetTempDir(utils.get(), web_apps_directory);
   if (temp_dir.empty()) {
     LOG(ERROR)
@@ -122,7 +123,7 @@
     return false;
   }
 
-  if (!WriteIcons(utils.get(), app_temp_dir.GetPath(), *web_app_info))
+  if (!WriteIcons(utils.get(), app_temp_dir.GetPath(), icon_infos))
     return false;
 
   // Commit: move whole app data dir to final destination in one mv operation.
@@ -137,7 +138,7 @@
 }
 
 // Performs blocking I/O. May be called on another thread.
-// Returns empty SkBitmap if any errors occured.
+// Returns empty SkBitmap if any errors occurred.
 SkBitmap ReadIconBlocking(std::unique_ptr<FileUtilsWrapper> utils,
                           base::FilePath web_apps_directory,
                           AppId app_id,
@@ -182,14 +183,14 @@
 
 void WebAppIconManager::WriteData(
     AppId app_id,
-    std::unique_ptr<WebApplicationInfo> web_app_info,
+    std::vector<WebApplicationIconInfo> icon_infos,
     WriteDataCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   base::PostTaskAndReplyWithResult(
       FROM_HERE, kTaskTraits,
       base::BindOnce(WriteDataBlocking, utils_->Clone(), web_apps_directory_,
-                     std::move(app_id), std::move(web_app_info)),
+                     std::move(app_id), std::move(icon_infos)),
       std::move(callback));
 }
 
diff --git a/chrome/browser/web_applications/web_app_icon_manager.h b/chrome/browser/web_applications/web_app_icon_manager.h
index 3e7be1b..99ead85 100644
--- a/chrome/browser/web_applications/web_app_icon_manager.h
+++ b/chrome/browser/web_applications/web_app_icon_manager.h
@@ -6,13 +6,14 @@
 #define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_ICON_MANAGER_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "chrome/browser/web_applications/components/app_icon_manager.h"
-#include "chrome/common/web_application_info.h"
 
 class Profile;
+struct WebApplicationIconInfo;
 
 namespace web_app {
 
@@ -30,7 +31,7 @@
   // Writes all data (icons) for an app.
   using WriteDataCallback = base::OnceCallback<void(bool success)>;
   void WriteData(AppId app_id,
-                 std::unique_ptr<WebApplicationInfo> web_app_info,
+                 std::vector<WebApplicationIconInfo> icon_infos,
                  WriteDataCallback callback);
 
   // AppIconManager:
diff --git a/chrome/browser/web_applications/web_app_icon_manager_unittest.cc b/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
index a18b8598..f8e875ae 100644
--- a/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
+#include "chrome/common/web_application_info.h"
 #include "chrome/test/base/testing_profile.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
@@ -48,17 +49,18 @@
                   const std::vector<int>& sizes_px,
                   const std::vector<SkColor>& colors) {
     DCHECK_EQ(sizes_px.size(), colors.size());
-    auto web_app_info = std::make_unique<WebApplicationInfo>();
-    web_app_info->icons.reserve(sizes_px.size());
+
+    std::vector<WebApplicationIconInfo> icon_infos;
+    icon_infos.reserve(sizes_px.size());
+
     for (size_t i = 0; i < sizes_px.size(); ++i) {
       std::string icon_name = base::StringPrintf("app-%d.ico", sizes_px[i]);
       GURL icon_url = app_url.Resolve(icon_name);
-      web_app_info->icons.push_back(
-          GenerateIconInfo(icon_url, sizes_px[i], colors[i]));
+      icon_infos.push_back(GenerateIconInfo(icon_url, sizes_px[i], colors[i]));
     }
 
     base::RunLoop run_loop;
-    icon_manager_->WriteData(app_id, std::move(web_app_info),
+    icon_manager_->WriteData(app_id, std::move(icon_infos),
                              base::BindLambdaForTesting([&](bool success) {
                                EXPECT_TRUE(success);
                                run_loop.Quit();
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index 5cf695d..dee2234 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -64,10 +64,11 @@
   }
 }
 
-void SetIcons(const WebApplicationInfo& web_app_info, WebApp* web_app) {
+void SetWebAppIcons(const std::vector<WebApplicationIconInfo>& icon_infos,
+                    WebApp* web_app) {
   WebApp::Icons web_app_icons;
 
-  for (const WebApplicationIconInfo& icon_info : web_app_info.icons) {
+  for (const WebApplicationIconInfo& icon_info : icon_infos) {
     // Skip unfetched bitmaps.
     if (icon_info.data.colorType() == kUnknown_SkColorType)
       continue;
@@ -128,7 +129,7 @@
                               : blink::mojom::DisplayMode::kBrowser);
   web_app->SetIsLocallyInstalled(options.locally_installed);
 
-  SetIcons(web_app_info, web_app.get());
+  SetWebAppIcons(web_app_info.icons, web_app.get());
 
   web_app->SetIsInSyncInstall(false);
   WebApp::SyncData sync_data;
@@ -137,7 +138,7 @@
   web_app->SetSyncData(std::move(sync_data));
 
   icon_manager_->WriteData(
-      std::move(app_id), std::make_unique<WebApplicationInfo>(web_app_info),
+      std::move(app_id), web_app_info.icons,
       base::BindOnce(&WebAppInstallFinalizer::OnIconsDataWritten,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      std::move(web_app)));
@@ -149,6 +150,14 @@
   NOTIMPLEMENTED();
 }
 
+void WebAppInstallFinalizer::FinalizeUpdate(
+    const WebApplicationInfo& web_app_info,
+    InstallFinalizedCallback callback) {
+  // TODO(crbug.com/926083): Implement update logic, this requires updating
+  // WebAppIconManager to clean out the existing icons and write new ones.
+  NOTIMPLEMENTED();
+}
+
 void WebAppInstallFinalizer::UninstallWebApp(const AppId& app_id,
                                              UninstallWebAppCallback) {
   // TODO(loyso): Implement The Unified Uninstall API. Expose Source as an
@@ -221,13 +230,6 @@
   NOTIMPLEMENTED();
 }
 
-bool WebAppInstallFinalizer::CanSkipAppUpdateForSync(
-    const AppId& app_id,
-    const WebApplicationInfo& web_app_info) const {
-  NOTIMPLEMENTED();
-  return true;
-}
-
 bool WebAppInstallFinalizer::CanUserUninstallFromSync(
     const AppId& app_id) const {
   // TODO(crbug.com/901226): Implement it.
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h
index 206b38a..24dba87 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.h
+++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -29,6 +29,8 @@
   void FinalizeInstall(const WebApplicationInfo& web_app_info,
                        const FinalizeOptions& options,
                        InstallFinalizedCallback callback) override;
+  void FinalizeUpdate(const WebApplicationInfo& web_app_info,
+                      InstallFinalizedCallback callback) override;
   void UninstallExternalWebApp(const GURL& app_url,
                                UninstallWebAppCallback callback) override;
   void UninstallWebApp(const AppId& app_id, UninstallWebAppCallback) override;
@@ -38,9 +40,6 @@
                          CreateOsShortcutsCallback callback) override;
   bool CanRevealAppShim() const override;
   void RevealAppShim(const AppId& app_id) override;
-  bool CanSkipAppUpdateForSync(
-      const AppId& app_id,
-      const WebApplicationInfo& web_app_info) const override;
   bool CanUserUninstallFromSync(const AppId& app_id) const override;
 
  private:
diff --git a/chrome/browser/web_applications/web_app_install_manager.cc b/chrome/browser/web_applications/web_app_install_manager.cc
index d49ab92..ca5bab2 100644
--- a/chrome/browser/web_applications/web_app_install_manager.cc
+++ b/chrome/browser/web_applications/web_app_install_manager.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
@@ -100,11 +101,14 @@
   tasks_.insert(std::move(task));
 }
 
-void WebAppInstallManager::InstallOrUpdateWebAppFromSync(
+void WebAppInstallManager::InstallWebAppFromSync(
     const AppId& app_id,
     std::unique_ptr<WebApplicationInfo> web_application_info,
     OnceInstallCallback callback) {
-  if (finalizer()->CanSkipAppUpdateForSync(app_id, *web_application_info)) {
+  // Skip sync update if app exists.
+  // All manifest fields will be set locally via update (see crbug.com/926083)
+  // so we must not sync them in order to avoid a device-to-device sync war.
+  if (registrar()->IsInstalled(app_id)) {
     std::move(callback).Run(app_id,
                             InstallResultCode::kSuccessAlreadyInstalled);
     return;
@@ -130,12 +134,20 @@
   EnqueueTask(std::move(task), std::move(start_task));
 }
 
-void WebAppInstallManager::UpdateWebAppFromManifest(
+void WebAppInstallManager::UpdateWebAppFromInfo(
     const AppId& app_id,
-    blink::Manifest manifest,
+    std::unique_ptr<WebApplicationInfo> web_application_info,
     OnceInstallCallback callback) {
-  // TODO(crbug.com/926083): Implement this.
-  std::move(callback).Run(app_id, InstallResultCode::kFailedUnknownReason);
+  auto task = std::make_unique<WebAppInstallTask>(
+      profile(), finalizer(), data_retriever_factory_.Run());
+
+  base::OnceClosure start_task = base::BindOnce(
+      &WebAppInstallTask::UpdateWebAppFromInfo, base::Unretained(task.get()),
+      EnsureWebContentsCreated(), app_id, std::move(web_application_info),
+      base::BindOnce(&WebAppInstallManager::OnQueuedTaskCompleted,
+                     base::Unretained(this), task.get(), std::move(callback)));
+
+  EnqueueTask(std::move(task), std::move(start_task));
 }
 
 void WebAppInstallManager::Shutdown() {
diff --git a/chrome/browser/web_applications/web_app_install_manager.h b/chrome/browser/web_applications/web_app_install_manager.h
index 50d5e886..6043d16 100644
--- a/chrome/browser/web_applications/web_app_install_manager.h
+++ b/chrome/browser/web_applications/web_app_install_manager.h
@@ -57,13 +57,14 @@
                                const InstallParams& install_params,
                                WebappInstallSource install_source,
                                OnceInstallCallback callback) override;
-  void InstallOrUpdateWebAppFromSync(
+  void InstallWebAppFromSync(
       const AppId& app_id,
       std::unique_ptr<WebApplicationInfo> web_application_info,
       OnceInstallCallback callback) override;
-  void UpdateWebAppFromManifest(const AppId& app_id,
-                                blink::Manifest manifest,
-                                OnceInstallCallback callback) override;
+  void UpdateWebAppFromInfo(
+      const AppId& app_id,
+      std::unique_ptr<WebApplicationInfo> web_application_info,
+      OnceInstallCallback callback) override;
   void Shutdown() override;
 
   // For the new USS-based system only. SyncInstallDelegate:
diff --git a/chrome/browser/web_applications/web_app_install_manager_unittest.cc b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
index 7c242806..930aa7e 100644
--- a/chrome/browser/web_applications/web_app_install_manager_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
@@ -87,7 +87,7 @@
 };
 
 TEST_F(WebAppInstallManagerTest,
-       InstallOrUpdateWebAppFromSync_TwoConcurrentInstallsAreRunInOrder) {
+       InstallWebAppFromSync_TwoConcurrentInstallsAreRunInOrder) {
   const GURL url1{"https://example.com/path"};
   const AppId app1_id = GenerateAppIdFromURL(url1);
 
@@ -154,7 +154,7 @@
   EXPECT_FALSE(install_manager().has_web_contents_for_testing());
 
   // Enqueue a request to install the 1st app.
-  install_manager().InstallOrUpdateWebAppFromSync(
+  install_manager().InstallWebAppFromSync(
       app1_id, CreateWebAppInfo(url1),
       base::BindLambdaForTesting(
           [&](const AppId& installed_app_id, InstallResultCode code) {
@@ -170,7 +170,7 @@
 
   // Immediately enqueue a request to install the 2nd app, WebContents is not
   // ready.
-  install_manager().InstallOrUpdateWebAppFromSync(
+  install_manager().InstallWebAppFromSync(
       app2_id, CreateWebAppInfo(url2),
       base::BindLambdaForTesting(
           [&](const AppId& installed_app_id, InstallResultCode code) {
@@ -206,7 +206,7 @@
 }
 
 TEST_F(WebAppInstallManagerTest,
-       InstallOrUpdateWebAppFromSync_InstallManagerDestroyed) {
+       InstallWebAppFromSync_InstallManagerDestroyed) {
   const GURL app_url("https://example.com/path");
   const AppId app_id = GenerateAppIdFromURL(app_url);
   NavigateAndCommit(app_url);
@@ -232,7 +232,7 @@
         return std::unique_ptr<WebAppDataRetriever>(std::move(data_retriever));
       }));
 
-  install_manager().InstallOrUpdateWebAppFromSync(
+  install_manager().InstallWebAppFromSync(
       app_id, CreateWebAppInfo(app_url),
       base::BindLambdaForTesting([](const web_app::AppId& installed_app_id,
                                     web_app::InstallResultCode code) {
@@ -248,24 +248,4 @@
   DestroyManagers();
 }
 
-TEST_F(WebAppInstallManagerTest,
-       InstallOrUpdateWebAppFromSync_CanSkipAppUpdateForSync) {
-  const GURL app_url("https://example.com/path");
-  const AppId app_id = GenerateAppIdFromURL(app_url);
-  NavigateAndCommit(app_url);
-
-  finalizer().SetNextCanSkipAppUpdateForSync(true);
-
-  base::RunLoop run_loop;
-  install_manager().InstallOrUpdateWebAppFromSync(
-      app_id, CreateWebAppInfo(app_url),
-      base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
-                                     web_app::InstallResultCode code) {
-        EXPECT_EQ(InstallResultCode::kSuccessAlreadyInstalled, code);
-        EXPECT_EQ(app_id, installed_app_id);
-        run_loop.Quit();
-      }));
-  run_loop.Run();
-}
-
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_install_task.cc b/chrome/browser/web_applications/web_app_install_task.cc
index 84807cf..6118dd35 100644
--- a/chrome/browser/web_applications/web_app_install_task.cc
+++ b/chrome/browser/web_applications/web_app_install_task.cc
@@ -180,6 +180,25 @@
                      is_locally_installed));
 }
 
+void WebAppInstallTask::UpdateWebAppFromInfo(
+    content::WebContents* web_contents,
+    const AppId& app_id,
+    std::unique_ptr<WebApplicationInfo> web_application_info,
+    InstallManager::OnceInstallCallback callback) {
+  Observe(web_contents);
+  install_callback_ = std::move(callback);
+  background_installation_ = true;
+
+  std::vector<GURL> icon_urls =
+      GetValidIconUrlsToDownload(*web_application_info, /*data=*/nullptr);
+
+  data_retriever_->GetIcons(
+      web_contents, std::move(icon_urls),
+      /*skip_page_fav_icons=*/true, WebAppIconDownloader::Histogram::kForUpdate,
+      base::BindOnce(&WebAppInstallTask::OnIconsRetrievedFinalizeUpdate,
+                     base::Unretained(this), std::move(web_application_info)));
+}
+
 void WebAppInstallTask::WebContentsDestroyed() {
   CallInstallCallback(AppId(), InstallResultCode::kWebContentsDestroyed);
 }
@@ -212,7 +231,6 @@
   Observe(nullptr);
   dialog_callback_.Reset();
 
-  DCHECK(install_source_ != kNoInstallSource);
   install_source_ = kNoInstallSource;
 
   DCHECK(install_callback_);
@@ -394,9 +412,9 @@
   DCHECK(web_app_info);
 
   // Installing from sync should not change icon links.
-  // TODO(loyso): Limit this only to WebappInstallSource::SYNC.
-  FilterAndResizeIconsGenerateMissing(web_app_info.get(), &icons_map,
-                                      /*is_for_sync*/ true);
+  FilterAndResizeIconsGenerateMissing(
+      web_app_info.get(), &icons_map,
+      /*is_for_sync=*/install_source_ == WebappInstallSource::SYNC);
 
   InstallFinalizer::FinalizeOptions options;
   options.install_source = install_source_;
@@ -417,8 +435,9 @@
 
   DCHECK(web_app_info);
 
-  FilterAndResizeIconsGenerateMissing(web_app_info.get(), &icons_map,
-                                      /*is_for_sync*/ false);
+  FilterAndResizeIconsGenerateMissing(
+      web_app_info.get(), &icons_map,
+      /*is_for_sync=*/install_source_ == WebappInstallSource::SYNC);
 
   if (background_installation_) {
     DCHECK(!dialog_callback_);
@@ -434,6 +453,23 @@
   }
 }
 
+void WebAppInstallTask::OnIconsRetrievedFinalizeUpdate(
+    std::unique_ptr<WebApplicationInfo> web_app_info,
+    IconsMap icons_map) {
+  if (ShouldStopInstall())
+    return;
+
+  DCHECK(web_app_info);
+
+  // TODO(crbug.com/926083): Abort update if icons fail to download.
+  FilterAndResizeIconsGenerateMissing(web_app_info.get(), &icons_map,
+                                      /*is_for_sync=*/false);
+
+  install_finalizer_->FinalizeUpdate(
+      *web_app_info, base::BindOnce(&WebAppInstallTask::OnInstallFinalized,
+                                    weak_ptr_factory_.GetWeakPtr()));
+}
+
 void WebAppInstallTask::OnDialogCompleted(
     ForInstallableSite for_installable_site,
     bool user_accepted,
diff --git a/chrome/browser/web_applications/web_app_install_task.h b/chrome/browser/web_applications/web_app_install_task.h
index 8045411d..8c38d1ae 100644
--- a/chrome/browser/web_applications/web_app_install_task.h
+++ b/chrome/browser/web_applications/web_app_install_task.h
@@ -90,6 +90,12 @@
       WebappInstallSource install_source,
       InstallManager::OnceInstallCallback callback);
 
+  void UpdateWebAppFromInfo(
+      content::WebContents* web_contents,
+      const AppId& app_id,
+      std::unique_ptr<WebApplicationInfo> web_application_info,
+      InstallManager::OnceInstallCallback callback);
+
   // WebContentsObserver:
   void WebContentsDestroyed() override;
 
@@ -146,6 +152,9 @@
       std::unique_ptr<WebApplicationInfo> web_app_info,
       ForInstallableSite for_installable_site,
       IconsMap icons_map);
+  void OnIconsRetrievedFinalizeUpdate(
+      std::unique_ptr<WebApplicationInfo> web_app_info,
+      IconsMap icons_map);
   void OnDialogCompleted(ForInstallableSite for_installable_site,
                          bool user_accepted,
                          std::unique_ptr<WebApplicationInfo> web_app_info);
@@ -163,7 +172,8 @@
   base::Optional<InstallManager::InstallParams> install_params_;
   bool background_installation_ = false;
 
-  // The mechanism via which the app creation was triggered.
+  // The mechanism via which the app creation was triggered, will stay as
+  // kNoInstallSource for updates.
   static constexpr WebappInstallSource kNoInstallSource =
       WebappInstallSource::COUNT;
   WebappInstallSource install_source_ = kNoInstallSource;
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 87a8864..4b8205a 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -749,13 +749,6 @@
 const base::Feature kHeavyAdBlocklist{"HeavyAdBlocklist",
                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
-#if defined(OS_CHROMEOS)
-// Enables or disables usage time state notifier for supervised accounts on
-// Chrome OS.
-const base::Feature kUsageTimeStateNotifier{"UsageTimeStateNotifier",
-                                            base::FEATURE_ENABLED_BY_DEFAULT};
-#endif
-
 #if defined(OS_ANDROID)
 const base::Feature kUseDisplayWideColorGamut{"UseDisplayWideColorGamut",
                                               base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 54b189c..6a3e6c2 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -470,11 +470,6 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kHeavyAdBlocklist;
 
-#if defined(OS_CHROMEOS)
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kUsageTimeStateNotifier;
-#endif
-
 #if defined(OS_ANDROID)
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kUseDisplayWideColorGamut;
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index dc07467..69e57cb4 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -63,7 +63,7 @@
   // set in the future.
   enum WMEventType {
     WMEventNormal,
-    WMEventMaxmize,
+    WMEventMaximize,
     WMEventMinimize,
     WMEventFullscreen,
     WMEventSnapLeft,
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 0a1f468a..30db34b27 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1303,6 +1303,10 @@
 #endif  // !OS_CHROMEOS && !OS_ANDROID
 
 #if defined(OS_CHROMEOS)
+// List of print servers ids that are allowed. List of strings.
+const char kExternalPrintServersWhitelist[] =
+    "native_printing.external_print_servers_whitelist";
+
 // List of printers configured by policy.
 const char kRecommendedNativePrinters[] =
     "native_printing.recommended_printers";
@@ -2753,9 +2757,8 @@
 const char kNotificationNextTriggerTime[] =
     "persistent_notifications.next_trigger";
 
-// Preference for controlling whether tab lifecycles
-// (throttling/freezing/discarding) are enabled.
-const char kTabLifecyclesEnabled[] = "tab_lifecycles_enabled";
+// Preference for controlling whether tab freezing is enabled.
+const char kTabFreezingEnabled[] = "tab_freezing_enabled";
 
 // Boolean that enables the Enterprise Hardware Platform Extension API for
 // extensions installed by enterprise policy.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index f0b5d5b..a6375e92 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -418,6 +418,7 @@
 #endif
 
 #if defined(OS_CHROMEOS)
+extern const char kExternalPrintServersWhitelist[];
 extern const char kRecommendedNativePrinters[];
 extern const char kRecommendedNativePrintersAccessMode[];
 extern const char kRecommendedNativePrintersBlacklist[];
@@ -962,7 +963,7 @@
 extern const char kNotificationNextPersistentId[];
 extern const char kNotificationNextTriggerTime[];
 
-extern const char kTabLifecyclesEnabled[];
+extern const char kTabFreezingEnabled[];
 
 extern const char kEnterpriseHardwarePlatformAPIEnabled[];
 
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index fce988c..542bc07 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -55,7 +55,6 @@
 const char kChromeUIDeviceLogHost[] = "device-log";
 const char kChromeUIDevicesHost[] = "devices";
 const char kChromeUIDevicesURL[] = "chrome://devices/";
-const char kChromeUIDevUiLoaderHost[] = "dev-ui-loader";
 const char kChromeUIDevUiLoaderURL[] = "chrome://dev-ui-loader/";
 const char kChromeUIDomainReliabilityInternalsHost[] =
     "domain-reliability-internals";
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 1c17c6e..f15bbd3f 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -62,7 +62,6 @@
 extern const char kChromeUIDeviceLogHost[];
 extern const char kChromeUIDevicesHost[];
 extern const char kChromeUIDevicesURL[];
-extern const char kChromeUIDevUiLoaderHost[];
 extern const char kChromeUIDevUiLoaderURL[];
 extern const char kChromeUIDomainReliabilityInternalsHost[];
 extern const char kChromeUIDownloadInternalsHost[];
diff --git a/chrome/renderer/resources/extensions/media_router_bindings.js b/chrome/renderer/resources/extensions/media_router_bindings.js
index a3ac84d..cabbf8a 100644
--- a/chrome/renderer/resources/extensions/media_router_bindings.js
+++ b/chrome/renderer/resources/extensions/media_router_bindings.js
@@ -744,8 +744,8 @@
     MirroringSessionObserverPtr: mirroring.mojom.SessionObserverPtr,
     MirroringSessionParameters: mirroring.mojom.SessionParameters,
     MirroringSessionType: mirroring.mojom.SessionType,
-    MirroringRemotingNamespace: mirroring.mojom.kRemotingNamespace,
-    MirroringWebRtcNamespace: mirroring.mojom.kWebRtcNamespace,
+    MirroringRemotingNamespace: mirroring.mojom.REMOTING_NAMESPACE,
+    MirroringWebRtcNamespace: mirroring.mojom.WEB_RTC_NAMESPACE,
     MirrorServiceRemoter: MirrorServiceRemoterAdapter,
     MirrorServiceRemoterPtr: MirrorServiceRemoterPtrAdapter,
     MirrorServiceRemotingSourcePtr: MirrorServiceRemotingSourcePtrAdapter,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index cfeeefc..dde2eaf 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3073,8 +3073,6 @@
     "../browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/amp_page_load_metrics_observer_unittest.cc",
-    "../browser/page_load_metrics/observers/click_input_tracker_unittest.cc",
-    "../browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc",
     "../browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_test_utils.cc",
     "../browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_test_utils.h",
@@ -3102,6 +3100,7 @@
     "../browser/password_manager/chrome_password_manager_client_unittest.cc",
     "../browser/password_manager/password_store_x_unittest.cc",
     "../browser/payments/payment_handler_permission_context_unittest.cc",
+    "../browser/performance_manager/decorators/frame_priority_decorator_unittest.cc",
     "../browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc",
     "../browser/performance_manager/decorators/page_aggregator_unittest.cc",
     "../browser/performance_manager/decorators/page_almost_idle_decorator_unittest.cc",
@@ -3598,7 +3597,7 @@
     sources += [
       "../browser/offline_pages/offline_page_request_handler_unittest.cc",
       "../browser/offline_pages/offline_page_utils_unittest.cc",
-      "../browser/offline_pages/prefetch/prefetch_instance_id_proxy_unittest.cc",
+      "../browser/offline_pages/prefetch/gcm_token_unittest.cc",
     ]
   }
 
@@ -3651,6 +3650,10 @@
       deps += [ "//v8:v8_external_startup_data_assets" ]
     }
     if (dfmify_dev_ui) {
+      sources += [
+        "//chrome/browser/dev_ui/android/dev_ui_loader_throttle_unittest.cc",
+      ]
+
       # If DevUI DFM is on, include Java classes and native resource split so
       # that DevUI page tests would work.
       deps += [
@@ -3736,7 +3739,6 @@
       "../browser/resource_coordinator/background_tab_navigation_throttle_unittest.cc",
       "../browser/resource_coordinator/decision_details_unittest.cc",
       "../browser/resource_coordinator/discard_metrics_lifecycle_unit_observer_unittest.cc",
-      "../browser/resource_coordinator/intervention_policy_database_unittest.cc",
       "../browser/resource_coordinator/leveldb_site_characteristics_database_unittest.cc",
       "../browser/resource_coordinator/lifecycle_unit_base_unittest.cc",
       "../browser/resource_coordinator/lifecycle_unit_unittest.cc",
@@ -4960,7 +4962,7 @@
     sources += [
       "../browser/media/webrtc/native_desktop_media_list_unittest.cc",
       "../browser/metrics/upgrade_metrics_provider_unittest.cc",
-      "../browser/policy/machine_level_user_cloud_policy_register_watcher_unittest.cc",
+      "../browser/policy/chrome_browser_cloud_management_register_watcher_unittest.cc",
       "../browser/profiles/profile_activity_metrics_recorder_unittest.cc",
       "../browser/signin/force_signin_verifier_unittest.cc",
       "../browser/signin/signin_global_error_unittest.cc",
@@ -5011,6 +5013,7 @@
       "../browser/ui/views/fullscreen_control/fullscreen_control_popup_unittest.cc",
       "../browser/ui/views/global_error_bubble_view_unittest.cc",
       "../browser/ui/views/global_media_controls/media_notification_container_impl_view_unittest.cc",
+      "../browser/ui/views/global_media_controls/media_notification_list_view_unittest.cc",
       "../browser/ui/views/hover_button_unittest.cc",
       "../browser/ui/views/infobars/infobar_view_unittest.cc",
       "../browser/ui/views/intent_picker_bubble_view_unittest.cc",
@@ -6465,6 +6468,18 @@
   }
 }
 
+if (is_chromeos) {
+  fuzzer_test("tokenized_string_fuzzer") {
+    sources = [
+      "../browser/ui/app_list/search/tests/tokenized_string_fuzzer.cc",
+    ]
+    deps = [
+      "//ash/public/cpp",
+      "//base",
+    ]
+  }
+}
+
 if (is_win) {
   test("pixel_browser_tests") {
     sources = [
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index d439726b..13ec18c 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -7,8 +7,9 @@
 android_library("chrome_java_test_pagecontroller") {
   testonly = true
   java_files = [
-    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/android/PermissionDialog.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ElementController.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/PageController.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/android/PermissionDialog.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/DataSaverController.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/SyncController.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/TOSController.java",
@@ -19,7 +20,6 @@
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/IncognitoNewTabPageController.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/NewTabPageController.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/SuggestionTileController.java",
-    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/PageController.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherMenuController.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/urlpage/UrlPage.java",
@@ -70,6 +70,22 @@
   }
 }
 
+instrumentation_test_apk("chrome_java_test_pagecontroller_codelab") {
+  apk_name = "ChromePageControllerCodelab"
+  apk_under_test = "//chrome/android:chrome_public_apk"
+  android_manifest = "javatests/src/org/chromium/chrome/test/pagecontroller/tests/AndroidManifest.xml"
+  java_files = [ "javatests/src/org/chromium/chrome/test/pagecontroller/tests/codelab/SettingsForCodelabTest.java" ]
+  deps = [
+    ":chrome_java_test_pagecontroller",
+    "//third_party/junit",
+  ]
+
+  if (!is_java_debug) {
+    proguard_enabled = true
+    proguard_configs = [ "//chrome/android/java/apk_for_test.flags" ]
+  }
+}
+
 junit_binary("chrome_java_test_pagecontroller_junit_tests") {
   testonly = true
   java_files = [
@@ -94,6 +110,7 @@
   testonly = true
   java_files = [
     "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorTestUtils.java",
+    "javatests/src/org/chromium/chrome/browser/tab/MockTab.java",
     "javatests/src/org/chromium/chrome/test/BottomSheetTestRule.java",
     "javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java",
     "javatests/src/org/chromium/chrome/test/ChromeBrowserTestRule.java",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java b/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java
new file mode 100644
index 0000000..daf831f
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java
@@ -0,0 +1,46 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tab;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.chrome.browser.tab.TabUma.TabCreationState;
+import org.chromium.chrome.browser.tabmodel.TabLaunchType;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * Tab used for various testing purposes.
+ */
+public class MockTab extends Tab {
+    /**
+     * Create a new Tab for testing and initializes Tab UserData objects.
+     */
+    public static Tab createAndInitialize(int id, boolean incognito) {
+        Tab tab = new MockTab(id, incognito);
+        tab.initialize(null, null, null, null, null, false, null, false);
+        return tab;
+    }
+
+    /**
+     * Constructor for id and incognito attribute. Tests often need to initialize
+     * these two fields only.
+     */
+    public MockTab(int id, boolean incognito) {
+        super(id, null, incognito, null);
+    }
+
+    public MockTab(int id, boolean incognito, @TabLaunchType Integer type) {
+        super(id, null, incognito, type);
+    }
+
+    @Override
+    public void initialize(Tab parent, @Nullable @TabCreationState Integer creationState,
+            LoadUrlParams loadUrlParams, WebContents webContents,
+            @Nullable TabDelegateFactory delegateFactory, boolean initiallyHidden,
+            TabState tabState, boolean unfreeze) {
+        TabHelpers.initTabHelpers(this, parent, creationState);
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/README.md b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/README.md
index 30b3cb2..730e380 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/README.md
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/README.md
@@ -7,17 +7,79 @@
 into re-usable classes.  Learn how to write and use Page Controllers to solve
 your testing needs.
 
+
 ## File Organization
 
+[codelabsolution](codelabsolution]: Solutions for the [code lab](#code-lab).
 [controllers](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/): Contains all the Page Controllers.<br/>
 [rules](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/): Junit Test Rules that provide access to the Page Controllers in a
 test case.<br/>
 [tests](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/): Tests for the Page Controllers themselves.<br/>
 [utils](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/): Utility classes that are useful for writing Page Controllers.<br/>
 
+
+## Using IDEs
+IDEs will make writing tests and maintaining Page Controllers much easier with online documentation and code completion.  See the [setup
+guide](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/README.md#integrated-development-environment-ide_set-up-guides).
+
+
+## Code Lab
+The code lab is an introduction to writing new Page Controllers and using them in tests.
+
+1) Create a new branch to go through the exercise, ex:
+```
+git checkout -b pagecontroller_codelab
+```
+
+2) Add code lab files to the build by adding the following lines to chrome/test/android/BUILD.gn.
+```
+...
+android_library("chrome_java_test_pagecontroller") {
+  testonly = true
+  java_files = [
+  ...
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/android/PermissionDialog.java",
++   "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/codelab/SearchEngineSelectionControllerForCodelab.java",
++   "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/codelab/SettingsControllerForCodelab.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/DataSaverController.java",
+  ...
+  ]
+```
+
+3) Modify the following files according to TODO hints contained therein.  It's
+best to build and test frequently iteratively as you complete the TODO items.
+- Start here: [SettingsForCodelabTest.java](tests/codelab/SettingsForCodelabTest.java)
+- Complete TODOs in: [controllers/ntp/ChromeMenu.java](controllers/ntp/ChromeMenu.java)
+  (Hint, Read the ["Where to find the resource and string
+  ids"](#where-to-find-the-resource-and-string-ids) section to find resource and
+  string ids to use in locators.)
+- Complete TODOs to pass SettingsForCodelabTest.testOpenCodelabSettings: [controllers/codelab/SettingsControllerForCodelab.java](controllers/codelab/SettingsControllerForCodelab.java)
+- Complete TODOs to pass SettingsForCodelabTest.testSwitchSearchEngine:
+  [controllers/codelab/SearchEngineSelectionControllerForCodelab.java](controllers/codelab/SearchEngineSelectionControllerForCodelab.java)
+
+4) Build the codelab.
+```
+autoninja -C out/Debug chrome_java_test_pagecontroller_codelab
+```
+
+5) Run codelab tests (repeat from step 3 until all the tests pass).
+```
+# Open the html results link to view logcat to debug errors.  There will be an
+# xml hierarchy dump of the UI in case of test failures in the logcat.
+# Screenshot is also available in the test results page.
+
+# Run the tests one at a time (to aid in troubleshooting) by using the -f option:
+out/Debug/bin/run_chrome_java_test_pagecontroller_codelab --num-retries 0 --local-output -f "*SettingsForCodelabTest.testOpenSettings"
+
+out/Debug/bin/run_chrome_java_test_pagecontroller_codelab --num-retries 0 --local-output -f "*SettingsForCodelabTest.testSwitchSearchEngine"
+```
+
+6) Sample answers are [here](codelabsolution/README.md) in case you get stuck.
+
+
 ## Writing Testcases
 
-See the [ExampleTest](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/ExampleTest.java)
+See the [ExampleTest](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/ExampleTest.java).
 
 ## Creating + Updating Page Controllers
 
@@ -60,3 +122,40 @@
     }
 }
 ```
+
+## Where to find the resource and string ids.
+
+If you're working on a new UI change or are familiar with the UI that you want
+to implement a Page Controller for, then you already should know what R.id.* and
+R.string.* entries to use in the Page Controllers.  Prefer to construct locators
+with these resources, for example:
+
+```
+import org.chromium.chrome.R;
+...
+IUi2Locator LOCATOR_TAB_SWITCHER_BUTTON = Ui2Locators.withResEntries(R.id.tab_switcher_button);
+
+IUi2Locator LOCATOR_NEW_TAB_MENU_ITEM =
+Ui2Locators.withPath(Ui2Locators.withResEntries(R.id.menu_item_text),
+                     Ui2Locators.withText(R.string.menu_new_tab));
+```
+
+**It is highly recommended that unique R.id entries are present for important UI elements in an activity, add if they're missing.**
+
+**It is also highly recommended that R.string.\* entries be used in Page Controllers and tests instead of hard-coding them.  This will keep tests in sync with the code-base and allow them to work under different language settings.**
+
+If you are not too sure about the correct resource / string ids to use, see if you can find the developer or maintainer for the UI.  If they're not available, here are some tips:
+
+ - [Android Layout inspector](https://developer.android.com/studio/debug/layout-inspector): This is useful to find out the resource ids (click an element, then expand the properties list, under mID).  To see strings, expand the text folder, see the mText field.  Note that this is the literal string, not the string resource id (see strings grd file below).
+
+ - The res directory: Search in [chrome/android/java/res/](https://cs.chromium.org/chromium/src/chrome/android/java/res/) directory for resource ids and string ids.  They are defined there and then auto generated (@+id/xyz -> R.id.xyz)
+
+ - Search the strings grd file with
+   [search_strings.py](https://cs.chromium.org/chromium/src/tools/android/pagecontroller/search_strings.py).
+
+   [android_chrome_strings.grd](https://cs.chromium.org/chromium/src/chrome/android/java/strings/android_chrome_strings.grd) contains the list of strings used in Clank.
+   The entries are transformed into android strings using the name attribute by dropping the IDS_ prefix and converting the rest into lower case.  For example: IDS_MENU_BOOKMARKS -> R.string.menu_bookmarks.  There may be several string matching what's displayed but with different ids, be sure to read the "desc" field in the grd file and also the java source code for the UI to pick the right one.
+
+ - The Clank java source code: [src/chrome/android/java/src/org/chromium/chrome/browser/](https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser/)
+
+ Search for ids and strings discovered in the previous steps to see how they are used.
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/codelabsolution/README.md b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/codelabsolution/README.md
new file mode 100644
index 0000000..71c1d2a
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/codelabsolution/README.md
@@ -0,0 +1,274 @@
+# Solutions for Chrome Page Controller Code Lab
+
+## These are sample solutions for the Code Lab, but not the only way to do it.
+
+***Paths in steps 1-4 are relative to src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/,
+   steps 5 is relative the chromium/src.***
+
+1) tests/codelab/SettingsForCodelabTest.java:
+``` java
+package org.chromium.chrome.test.pagecontroller.tests.codelab;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.chrome.test.pagecontroller.controllers.codelab.SearchEngineSelectionControllerForCodelab;
+import org.chromium.chrome.test.pagecontroller.controllers.ntp.ChromeMenu;
+import org.chromium.chrome.test.pagecontroller.rules.ChromeUiApplicationTestRule;
+import org.chromium.chrome.test.pagecontroller.rules.ChromeUiAutomatorTestRule;
+
+/**
+ * Test for Page Controllers created in the code lab.
+ */
+@SmallTest
+@RunWith(BaseJUnit4ClassRunner.class)
+public class SettingsForCodelabTest {
+    // ChromeUiAutomatorTestRule will capture a screen shot and UI Hierarchy info in the event
+    // of a test failure to aid test debugging.
+    public ChromeUiAutomatorTestRule mUiAutomatorRule = new ChromeUiAutomatorTestRule();
+
+    // ChromeUiApplicationTestRule provides a way to launch the Chrome Application under test
+    // and access to the Page Controllers.
+    public ChromeUiApplicationTestRule mChromeUiRule = new ChromeUiApplicationTestRule();
+
+    // The rule chain allows deterministic ordering of test rules so that the
+    // UiAutomatorRule will print debugging information in case of errors
+    // before the application is shut-down.
+    @Rule
+    public final TestRule mChain = RuleChain.outerRule(mChromeUiRule).around(mUiAutomatorRule);
+
+    private ChromeMenu mChromeMenu;
+
+    @Before
+    public void setUp() {
+        // TODO: Obtain a ChromeMenu instance.  Hint, start with
+        //       mChromeUiRule.launchIntoNewTabPageOnFirstRun().
+        mChromeMenu = mChromeUiRule.launchIntoNewTabPageOnFirstRun().openChromeMenu();
+    }
+
+    @Test
+    public void testOpenSettingsControllerForCodelab() {
+        // TODO: Modify ChromeMenu.java to add a method that returns an
+        //       instance of CodeLabSettings Page Controller.
+        mChromeMenu.openSettingsForCodelab();
+    }
+
+    @Test
+    public void testSwitchSearchEngine() {
+        // TODO: Write a test to verify ability to change the default search
+        //       engine.
+        SearchEngineSelectionControllerForCodelab engineSelection =
+                mChromeMenu.openSettingsForCodelab().clickSearchEngine();
+        Assert.assertEquals(engineSelection.getEngineChoice(), "Google");
+        engineSelection.chooseSearchEngine("Bing");
+        Assert.assertEquals(engineSelection.getEngineChoice(), "Bing");
+    }
+}
+```
+
+2) controllers/codelab/SettingsControllerForCodelab.java:
+``` java
+package org.chromium.chrome.test.pagecontroller.controllers.codelab;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
+import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
+
+// TODO: Read documentation in the PageController class.  Refer to implemented
+// Page Controllers in the pagecontroller directory for examples.
+/**
+ * Settings Menu Page Controller for the Code Lab.
+ */
+public class SettingsControllerForCodelab extends PageController {
+    // TODO: Replace null with a an actual locator.  (Hint, see Ui2Locators.with*.)
+    private static final IUi2Locator LOCATOR_SETTINGS =
+            Ui2Locators.withTextString(R.string.preferences);
+
+    // TODO: (Hint, you may need more IUi2Locators than just LOCATOR_SETTINGS,
+    // add them here.
+    private static final IUi2Locator LOCATOR_SEARCH_ENGINE =
+            Ui2Locators.withTextString(R.string.prefs_search_engine);
+
+    // The next 5 lines are boilerplate, no need to modify.
+    private static final SettingsControllerForCodelab sInstance =
+            new SettingsControllerForCodelab();
+    private SettingsControllerForCodelab() {}
+    public static SettingsControllerForCodelab getInstance() {
+        return sInstance;
+    }
+
+    @Override
+    public SettingsControllerForCodelab verifyActive() {
+        // TODO: See PageController.verifyActive documentation on what this
+        // method should do.  (Hint, PageController has a mLocatorHelper field.)
+        mLocatorHelper.verifyOnScreen(LOCATOR_SETTINGS);
+
+        return this;
+    }
+
+    /**
+     * Click on the Search Engine option.
+     * @returns SearchEngineSelectionControllerForCodelab PageController.
+     */
+    public SearchEngineSelectionControllerForCodelab clickSearchEngine() {
+        // TODO: Perform a click on the Search engine option
+        // in the Settings menu.  (Hint, PageController has a mUtils field.)
+        mUtils.click(LOCATOR_SEARCH_ENGINE);
+
+        // TODO: Replace null with an instance of
+        // SearchEngineSelectionControllerForCodelab.  (Hint, all PageController
+        // subclasses have a verifyActive method.)
+        return SearchEngineSelectionControllerForCodelab.getInstance().verifyActive();
+    }
+}
+```
+
+3) controllers/codelab/SearchEngineSelectionControllerForCodelab.java:
+``` java
+package org.chromium.chrome.test.pagecontroller.controllers.codelab;
+
+import android.support.test.uiautomator.UiObject2;
+import android.util.Pair;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
+import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
+import org.chromium.chrome.test.pagecontroller.utils.UiLocationException;
+import org.chromium.chrome.test.pagecontroller.utils.UiLocatorHelper;
+
+import java.util.List;
+
+/**
+ * Search Engine Selection Page Controller for the Code Lab, corresponds to
+ * {@link org.chromium.chrome.browser.preferences.SearchEnginePreference}.
+ */
+public class SearchEngineSelectionControllerForCodelab extends PageController {
+    // TODO: Put locators here.
+    private static final IUi2Locator LOCATOR_SEARCH_ENGINE =
+            Ui2Locators.withPath(Ui2Locators.withAnyResEntry(R.id.action_bar),
+                    Ui2Locators.withTextString(R.string.prefs_search_engine));
+    private static final IUi2Locator LOCATOR_ALL_ENGINES = Ui2Locators.withPath(
+            Ui2Locators.withAnyResEntry(R.id.name), Ui2Locators.withTextRegex("^(.+)$"));
+    private class EngineSelectionIndicator
+            implements UiLocatorHelper.CustomElementMaker<Pair<String, Boolean>> {
+        @Override
+        public Pair<String, Boolean> makeElement(UiObject2 root, boolean isLastAttempt) {
+            String engine = mLocatorHelper.getOneTextImmediate(LOCATOR_ALL_ENGINES, root);
+            Boolean isChecked = mLocatorHelper.getOneCheckedImmediate(LOCATOR_ALL_ENGINES, root);
+            if (engine == null) {
+                throw new UiLocationException("Engine not found.", LOCATOR_ALL_ENGINES, root);
+            } else if (isChecked == null) {
+                throw new UiLocationException(
+                        "Checked status not found.", LOCATOR_ALL_ENGINES, root);
+            } else {
+                return new Pair<String, Boolean>(engine, isChecked);
+            }
+        }
+    }
+    private EngineSelectionIndicator mEngineSelectionIndicator = new EngineSelectionIndicator();
+    // The next 5 lines are boilerplate, no need to modify.
+    private static final SearchEngineSelectionControllerForCodelab sInstance =
+            new SearchEngineSelectionControllerForCodelab();
+    private SearchEngineSelectionControllerForCodelab() {}
+    public static SearchEngineSelectionControllerForCodelab getInstance() {
+        return sInstance;
+    }
+    @Override
+    public SearchEngineSelectionControllerForCodelab verifyActive() {
+        // TODO: Implement this method.
+        mLocatorHelper.verifyOnScreen(LOCATOR_SEARCH_ENGINE);
+        return this;
+    }
+    /**
+     * Choose the Omnibox default search engine.
+     * @param   engine The engine to choose.
+     */
+    public SearchEngineSelectionControllerForCodelab chooseSearchEngine(String engineName) {
+        // TODO: Perform a click on the engine choice.
+        mUtils.click(getEngineLocator(engineName));
+        // Returning this instead of void facilitates chaining.
+        return this;
+    }
+    /**
+     * @return The current engine choice.
+     */
+    public String getEngineChoice() {
+        // TODO: Determine which engine option is selected and return it.
+        String engineSelection = null;
+        List<Pair<String, Boolean>> engineSelections =
+                mLocatorHelper.getCustomElements(LOCATOR_ALL_ENGINES, mEngineSelectionIndicator);
+        for (Pair<String, Boolean> pair : engineSelections) {
+            if (pair.second) {
+                if (engineSelection == null) {
+                    engineSelection = pair.first;
+                } else {
+                    throw new UiLocationException("More than 1 engine chosen: " + engineSelection
+                            + "," + pair.first + "!");
+                }
+            }
+        }
+        if (engineSelection == null) {
+            throw new UiLocationException("No engines chosen!");
+        }
+        return engineSelection;
+    }
+    /**
+     * @return IUi2Locator to find the engine choice.  Since the engine name
+     *         varies across languages, this couldn't be hard-coded.
+     */
+    private IUi2Locator getEngineLocator(String engineName) {
+        return Ui2Locators.withPath(LOCATOR_ALL_ENGINES, Ui2Locators.withText(engineName));
+    }
+}
+```
+
+4) controllers/ntp/ChromeMenu.java, add lines marked with "+":
+``` java
+...
+
++ import org.chromium.chrome.test.pagecontroller.controllers.codelab.SettingsControllerForCodelab;
+...
+public class ChromeMenu extends PageController {
+...
++    private static final IUi2Locator LOCATOR_SETTINGS_FOR_CODELAB =
++            Ui2Locators.withPath(Ui2Locators.withAnyResEntry(R.id.menu_item_text),
++                    Ui2Locators.withTextString(R.string.menu_preferences));
+...
++    public SettingsControllerForCodelab openSettingsForCodelab() {
++        mUtils.click(LOCATOR_SETTINGS_FOR_CODELAB);
++        return SettingsControllerForCodelab.getInstance().verifyActive();
++    }
+...
+}
+```
+
+5) Add lines marked with '+' to chrome/test/android/BUILD.gn:
+```
+android_library("chrome_java_test_pagecontroller") {
+  testonly = true
+  java_files = [
+...
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/android/PermissionDialog.java",
++    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/codelab/SettingsControllerForCodelab.java",
++    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/codelab/SearchEngineSelectionControllerForCodelab.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/TOSController.java",
+...
+
+```
+
+6) Build and run.
+``` bash
+autoninja -C out/Debug chrome_java_test_pagecontroller_codelab
+
+out/Debug/bin/run_chrome_java_test_pagecontroller_codelab --num-retries 0 --local-output
+```
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/codelab/SearchEngineSelectionControllerForCodelab.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/codelab/SearchEngineSelectionControllerForCodelab.java
new file mode 100644
index 0000000..cf1c355
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/codelab/SearchEngineSelectionControllerForCodelab.java
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.test.pagecontroller.controllers.codelab;
+
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+
+// TODO: Implement page controller for SearchEnginePreferences.java.
+
+/**
+ * Search Engine Selection Page Controller for the Code Lab, representing
+ * SearchEnginePreferences.java.
+ *
+ * @see <a
+ *         href="https://chromium.googlesource.com/chromium/src/+/HEAD/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEnginePreference.java">SearchEnginePreference.java</a>
+ */
+
+public class SearchEngineSelectionControllerForCodelab extends PageController {
+    // The next 5 lines are boilerplate, no need to modify.
+    private static final SearchEngineSelectionControllerForCodelab sInstance =
+            new SearchEngineSelectionControllerForCodelab();
+    private SearchEngineSelectionControllerForCodelab() {}
+    public static SearchEngineSelectionControllerForCodelab getInstance() {
+        return sInstance;
+    }
+
+    @Override
+    public SearchEngineSelectionControllerForCodelab verifyActive() {
+        // TODO: Implement this method to verify that the UI is displaying the
+        // search engine selection activity then return this (otherwise throw).
+
+        return this;
+    }
+
+    /**
+     * Choose the Omnibox default search engine.
+     * @param  engine The engine to choose.
+     * @return        SearchEngineSelectionControllerForCodelab, after verification that
+     *                the page has transitioned to it.
+     */
+    public SearchEngineSelectionControllerForCodelab chooseSearchEngine(String engineName) {
+        // TODO: Construct a IUi2Locator for the element corresponding to the
+        // given engineName and perform a click on it.
+        // (Hint, the resource id entry for search engine choices along with the
+        // engineName can be used by Ui2Locators.withPath(...).)
+
+        return this;
+    }
+
+    /**
+     * @return The current engine choice.
+     */
+    public String getEngineChoice() {
+        // TODO: Determine which engine option is selected and return it.
+        // (Hint, there are various get*Checked methods in UILocatorHelper
+        // to find out if something is checked.)
+
+        return null;
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/codelab/SettingsControllerForCodelab.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/codelab/SettingsControllerForCodelab.java
new file mode 100644
index 0000000..0cebdcae
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/codelab/SettingsControllerForCodelab.java
@@ -0,0 +1,57 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.test.pagecontroller.controllers.codelab;
+
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
+
+// TODO: Implement page controller for MainPreferences.java.  Read
+// documentation in the PageController class and refer to implemented Page
+// Controllers in the pagecontroller directory for examples.
+
+/**
+ * Settings Menu Page Controller for the Code Lab, representing
+ * MainPreferences.java.
+ *
+ * @see <a
+ *         href="https://chromium.googlesource.com/chromium/src/+/HEAD/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java">MainPreferences.java</a>
+ */
+public class SettingsControllerForCodelab extends PageController {
+    // TODO: Replace null with a an actual locator.  Hint, see
+    // {@link ../../README.md#where-to-find-the-resource-and-string-ids}, and
+    // {@link ../../utils/Ui2Locators#withTextString}.
+    static final IUi2Locator LOCATOR_SETTINGS = null;
+
+    // TODO: (Hint, you may need more IUi2Locators, add them here.
+
+    // The next 5 lines are boilerplate, no need to modify.
+    private static final SettingsControllerForCodelab sInstance =
+            new SettingsControllerForCodelab();
+    private SettingsControllerForCodelab() {}
+    public static SettingsControllerForCodelab getInstance() {
+        return sInstance;
+    }
+
+    @Override
+    public SettingsControllerForCodelab verifyActive() {
+        // TODO: See PageController.verifyActive documentation on what this
+        // method should do.  (Hint, PageController has a mLocatorHelper field.)
+
+        return this;
+    }
+
+    /**
+     * Click on the Search Engine option.
+     * @returns SearchEngineSelectionControllerForCodelab PageController.
+     */
+    public SearchEngineSelectionControllerForCodelab clickSearchEngine() {
+        // TODO: Perform a click on the Search engine option
+        // in the Settings menu.  (Hint, PageController has a mUtils field.)
+
+        // TODO: Replace null with an instance of CodelabSearchEngine.  (Hint,
+        // all PageController subclasses have a verifyActive method.)
+        return null;
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/codelab/SettingsForCodelabTest.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/codelab/SettingsForCodelabTest.java
new file mode 100644
index 0000000..b012c51a
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/codelab/SettingsForCodelabTest.java
@@ -0,0 +1,70 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.test.pagecontroller.tests.codelab;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.chrome.test.pagecontroller.controllers.ntp.ChromeMenu;
+import org.chromium.chrome.test.pagecontroller.rules.ChromeUiApplicationTestRule;
+import org.chromium.chrome.test.pagecontroller.rules.ChromeUiAutomatorTestRule;
+
+/**
+ * Test for Page Controller Code Lab.
+ */
+@SmallTest
+@RunWith(BaseJUnit4ClassRunner.class)
+public class SettingsForCodelabTest {
+    // ChromeUiAutomatorTestRule will capture a screen shot and UI Hierarchy info in the event
+    // of a test failure to aid test debugging.
+    public ChromeUiAutomatorTestRule mUiAutomatorRule = new ChromeUiAutomatorTestRule();
+
+    // ChromeUiApplicationTestRule provides a way to launch the Chrome Application under test
+    // and access to the Page Controllers.
+    public ChromeUiApplicationTestRule mChromeUiRule = new ChromeUiApplicationTestRule();
+
+    // The rule chain allows deterministic ordering of test rules so that the
+    // UiAutomatorRule will print debugging information in case of errors
+    // before the application is shut-down.
+    @Rule
+    public final TestRule mChain = RuleChain.outerRule(mChromeUiRule).around(mUiAutomatorRule);
+
+    private ChromeMenu mChromeMenu;
+
+    @Before
+    public void setUp() {
+        // TODO: Obtain a ChromeMenu instance.  Hint, start with
+        //       mChromeUiRule.launchIntoNewTabPageOnFirstRun().
+        mChromeMenu = null;
+    }
+
+    @Test
+    public void testOpenCodelabSettings() {
+        // TODO: Uncomment and add a method to ChromeMenu that returns an
+        // instance of SettingsControllerForCodelab to pass this test.
+
+        // mChromeMenu.openSettingsForCodelab();
+    }
+
+    @Test
+    public void testSwitchSearchEngine() {
+        // TODO: Uncomment and implement clickSearchEngine that verifies the
+        // page has changed to the default search engine selection page.
+
+        // SearchEngineSelectionControllerForCodelab engineSelection =
+        //         mChromeMenu.openSettingsForCodelab().clickSearchEngine();
+        // Assert.assertEquals(engineSelection.getEngineChoice(), "Google");
+
+        // TODO: Change the search engine to something else, then verify that
+        // the change is reflected in the UI.
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java
index d376f86..d2f982e 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java
@@ -4,8 +4,8 @@
 
 package org.chromium.chrome.test.util.browser.tabmodel;
 
+import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabBuilder;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModel;
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -43,7 +43,7 @@
     }
 
     public Tab addTab(int id) {
-        Tab tab = mDelegate == null ? new TabBuilder().setId(id).setIncognito(isIncognito()).build()
+        Tab tab = mDelegate == null ? new MockTab(id, isIncognito())
                                     : mDelegate.createTab(id, isIncognito());
         mTabs.add(tab);
         return tab;
diff --git a/chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_avatar128.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_avatar128.Nexus_5-19.png.sha1
new file mode 100644
index 0000000..26b042c
--- /dev/null
+++ b/chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_avatar128.Nexus_5-19.png.sha1
@@ -0,0 +1 @@
+3cf76bf7cb229c327498c29a0bd9267b4408ca6c
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_avatar.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_avatar64.Nexus_5-19.png.sha1
similarity index 100%
rename from chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_avatar.Nexus_5-19.png.sha1
rename to chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_avatar64.Nexus_5-19.png.sha1
diff --git a/chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_placeholder128.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_placeholder128.Nexus_5-19.png.sha1
new file mode 100644
index 0000000..8c8530c9
--- /dev/null
+++ b/chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_placeholder128.Nexus_5-19.png.sha1
@@ -0,0 +1 @@
+2e062f07f87adf32dc4c8849904d345fc2c0dacb
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_placeholder.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_placeholder64.Nexus_5-19.png.sha1
similarity index 100%
rename from chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_placeholder.Nexus_5-19.png.sha1
rename to chrome/test/data/android/render_tests/ProfileDataCacheRenderTest.profile_data_cache_placeholder64.Nexus_5-19.png.sha1
diff --git a/chrome/test/data/extensions/api_test/webrequest/cors/fetch.html b/chrome/test/data/extensions/api_test/webrequest/cors/fetch.html
index ac81c202..32cf477 100644
--- a/chrome/test/data/extensions/api_test/webrequest/cors/fetch.html
+++ b/chrome/test/data/extensions/api_test/webrequest/cors/fetch.html
@@ -4,10 +4,13 @@
  * found in the LICENSE file.
 -->
 <script>
+const params = (new URL(location.href)).searchParams;
 const hostname = 'cors.example.com';
 const origin = location.protocol + '//' + hostname + ':' + location.port;
-const path = location.search.substr('?path='.length);
+const path = params.get('path');
 const url = origin + '/extensions/api_test/webrequest/cors/' + path;
+const withPreflight = params.has('with-preflight');
+const headers = withPreflight? {'x-foo': 'x-bar'} : undefined;
 
-fetch(url);
+fetch(url, { headers, cache: 'no-store' });
 </script>
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_cors.js b/chrome/test/data/extensions/api_test/webrequest/test_cors.js
index 64805b3..b6e3fb7 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_cors.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_cors.js
@@ -4,6 +4,7 @@
 
 const callbackPass = chrome.test.callbackPass;
 const listeningUrlPattern = '*://cors.example.com/*';
+const BASE = 'extensions/api_test/webrequest/cors/';
 
 function getCorsMode() {
   const query = location.search;
@@ -14,6 +15,196 @@
   return mode;
 }
 
+function setExpectationsForNonObservablePreflight() {
+  // In this case the preflight request is not observable.
+  chrome.test.assertTrue(getCorsMode() == 'network_service');
+
+  const url = getServerURL(BASE + 'accept', 'cors.example.com');
+  const method = 'GET';
+  const initiator = getServerURL('').slice(0, -1);
+  const type = 'xmlhttprequest';
+  const frameUrl = 'unknown frame URL';
+
+  expect(
+      [  // events
+        { label: 'onBeforeRequest',
+          event: 'onBeforeRequest',
+          details: {
+            url,
+            method,
+            initiator,
+            type,
+            frameUrl,
+          },
+        },
+        { label: 'onBeforeSendHeaders',
+          event: 'onBeforeSendHeaders',
+          details: {
+            url,
+            method,
+            initiator,
+            type,
+          },
+        },
+        { label: 'onSendHeaders',
+          event: 'onSendHeaders',
+          details: {
+            url,
+            method,
+            initiator,
+            type,
+          },
+        },
+        { // CORS fails due to lack of 'access-control-allow-headers' header.
+          label: 'onErrorOccurred',
+          event: 'onErrorOccurred',
+          details: {
+            url,
+            method,
+            error: 'net::ERR_FAILED',
+            initiator,
+            type,
+            fromCache: false,
+          }
+        }
+      ],
+      [ // event order
+        ['onBeforeRequest', 'onBeforeSendHeaders', 'onSendHeaders',
+         'onErrorOccurred']
+      ],
+      {urls: [url]},  // filter
+      []  // extraInfoSpec
+  );
+}
+
+function setExpectationsForObservablePreflight(extraInfoSpec) {
+  const url = getServerURL(BASE + 'accept', 'cors.example.com');
+  const initiator = getServerURL('').slice(0, -1);
+  const frameUrl = 'unknown frame URL';
+  const type = 'xmlhttprequest';
+
+  const eventsForPreflight = [
+    { label: 'onBeforeRequest-P',
+      event: 'onBeforeRequest',
+      details: {
+        url,
+        method: 'OPTIONS',
+        initiator,
+        type,
+        frameUrl,
+      },
+    },
+    { label: 'onBeforeSendHeaders-P',
+      event: 'onBeforeSendHeaders',
+      details: {
+        url,
+        method: 'OPTIONS',
+        initiator,
+        type,
+      },
+    },
+    { label: 'onSendHeaders-P',
+      event: 'onSendHeaders',
+      details: {
+        url,
+        method: 'OPTIONS',
+        initiator,
+        type,
+      },
+    },
+    { label: 'onHeadersReceived-P',
+      event: 'onHeadersReceived',
+      details: {
+        url,
+        method: 'OPTIONS',
+        statusCode: 200,
+        statusLine: 'HTTP/1.1 200 OK',
+        initiator,
+        type,
+      },
+    },
+    { label: 'onResponseStarted-P',
+      event: 'onResponseStarted',
+      details: {
+        url,
+        method: 'OPTIONS',
+        ip: '127.0.0.1',
+        fromCache: false,
+        statusCode: 200,
+        statusLine: 'HTTP/1.1 200 OK',
+        initiator,
+        type,
+      },
+    },
+    { label: 'onCompleted-P',
+      event: 'onCompleted',
+      details: {
+        url,
+        method: 'OPTIONS',
+        ip: '127.0.0.1',
+        fromCache: false,
+        statusCode: 200,
+        statusLine: 'HTTP/1.1 200 OK',
+        initiator,
+        type,
+      },
+    },
+  ];
+  const eventOrderForPreflight = [
+    'onBeforeRequest-P', 'onBeforeSendHeaders-P', 'onSendHeaders-P',
+    'onHeadersReceived-P', 'onResponseStarted-P', 'onCompleted-P',
+  ];
+
+  let events;
+  let eventsOrder;
+  if (getCorsMode() == 'network_service') {
+    // When the CORS module is in the network process, onBeforeRequest is called
+    // for the actual request first, and then the preflight request is made.
+    // As there is no 'access-control-allow-headers' header in the preflight
+    // response, the actual request fails whereas the preflight request
+    // succeeds.
+    events = [
+      { label: 'onBeforeRequest',
+        event: 'onBeforeRequest',
+        details: {
+          url: url,
+          method: 'GET',
+          initiator,
+          type: 'xmlhttprequest',
+          frameUrl: 'unknown frame URL',
+        },
+      },
+    ].concat(eventsForPreflight, [
+      { label: 'onErrorOccurred',
+        event: 'onErrorOccurred',
+        details: {
+          url: url,
+          method: 'GET',
+          error: 'net::ERR_FAILED',
+          initiator: initiator,
+          type: 'xmlhttprequest',
+          fromCache: false,
+        }
+      },
+    ]);
+    eventOrder = ['onBeforeRequest'].concat(
+        eventOrderForPreflight, ['onErrorOccurred']);
+  } else {
+    // In this case, the preflight request is made first, and blink will not
+    // make the actual request because of the lack of an
+    // 'access-control-allow-headers' header in the preflight response.
+    events = eventsForPreflight;
+    eventOrder = eventOrderForPreflight;
+  }
+
+  expect(
+      events,
+      [eventOrder],
+      {urls: [url]},  // filter
+      extraInfoSpec,
+  );
+}
+
 function registerOriginListeners(
     requiredNames, disallowedNames, extraInfoSpec) {
   let observed = false;
@@ -93,6 +284,282 @@
       onCompletedOrErrorOccurredListener, {urls: [listeningUrlPattern]});
 }
 
+function setExpectationsForSuccessfulPreflight() {
+  const url = getServerURL(BASE + 'accept', 'cors.example.com');
+  const initiator = getServerURL('').slice(0, -1);
+  const frameUrl = 'unknown frame URL';
+  const type = 'xmlhttprequest';
+
+  const events = [
+    { label: 'onBeforeRequest-P',
+      event: 'onBeforeRequest',
+      details: {
+        url,
+        method: 'OPTIONS',
+        initiator,
+        type,
+        frameUrl,
+      },
+    },
+    { label: 'onBeforeSendHeaders-P',
+      event: 'onBeforeSendHeaders',
+      details: {
+        url,
+        method: 'OPTIONS',
+        initiator,
+        type,
+      },
+    },
+    { label: 'onSendHeaders-P',
+      event: 'onSendHeaders',
+      details: {
+        url,
+        method: 'OPTIONS',
+        initiator,
+        type,
+      },
+    },
+    { label: 'onHeadersReceived-P',
+      event: 'onHeadersReceived',
+      details: {
+        url,
+        method: 'OPTIONS',
+        statusCode: 200,
+        statusLine: 'HTTP/1.1 200 OK',
+        initiator,
+        type,
+        responseHeadersExist: true,
+      },
+      retval_function: (name, details) => {
+        // Allow the 'x-foo' header, so that the preflight succeeds.
+        details.responseHeaders.push(
+            {name: 'access-control-allow-headers', value: 'x-foo'});
+        // Prevent the CORS preflight cache from reusing this preflight
+        // response.
+        details.responseHeaders.push(
+            {name: 'access-control-max-age', value: '0'});
+        return {responseHeaders: details.responseHeaders};
+      },
+    },
+    { label: 'onResponseStarted-P',
+      event: 'onResponseStarted',
+      details: {
+        url,
+        method: 'OPTIONS',
+        ip: '127.0.0.1',
+        fromCache: false,
+        statusCode: 200,
+        statusLine: 'HTTP/1.1 200 OK',
+        initiator,
+        type,
+        responseHeadersExist: true,
+      },
+    },
+    { label: 'onCompleted-P',
+      event: 'onCompleted',
+      details: {
+        url,
+        method: 'OPTIONS',
+        ip: '127.0.0.1',
+        fromCache: false,
+        statusCode: 200,
+        statusLine: 'HTTP/1.1 200 OK',
+        initiator,
+        type,
+        responseHeadersExist: true,
+      },
+    },
+    { label: 'onBeforeRequest',
+        event: 'onBeforeRequest',
+        details: {
+          url: url,
+          method: 'GET',
+          initiator,
+          type: 'xmlhttprequest',
+          frameUrl: 'unknown frame URL',
+        },
+      },
+    { label: 'onBeforeSendHeaders',
+      event: 'onBeforeSendHeaders',
+      details: {
+        url,
+        method: 'GET',
+        initiator,
+        type,
+      },
+    },
+    { label: 'onSendHeaders',
+      event: 'onSendHeaders',
+      details: {
+        url,
+        method: 'GET',
+        initiator,
+        type,
+      },
+    },
+    { label: 'onHeadersReceived',
+      event: 'onHeadersReceived',
+      details: {
+        url,
+        method: 'GET',
+        statusCode: 200,
+        statusLine: 'HTTP/1.1 200 OK',
+        initiator,
+        type,
+        responseHeadersExist: true,
+      },
+    },
+    { label: 'onResponseStarted',
+      event: 'onResponseStarted',
+      details: {
+        url,
+        method: 'GET',
+        ip: '127.0.0.1',
+        fromCache: false,
+        statusCode: 200,
+        statusLine: 'HTTP/1.1 200 OK',
+        initiator,
+        type,
+        responseHeadersExist: true,
+      },
+    },
+    { label: 'onCompleted',
+      event: 'onCompleted',
+      details: {
+        url,
+        method: 'GET',
+        ip: '127.0.0.1',
+        fromCache: false,
+        statusCode: 200,
+        statusLine: 'HTTP/1.1 200 OK',
+        initiator,
+        type,
+        responseHeadersExist: true,
+      },
+    },
+  ];
+  let eventOrder;
+  if (getCorsMode() == 'network_service') {
+    eventOrder = [
+      'onBeforeRequest',
+      'onBeforeRequest-P',
+      'onBeforeSendHeaders-P',
+      'onSendHeaders-P',
+      'onHeadersReceived-P',
+      'onResponseStarted-P',
+      'onCompleted-P',
+      'onBeforeSendHeaders',
+      'onSendHeaders',
+      'onHeadersReceived',
+      'onResponseStarted',
+      'onCompleted',
+    ];
+  } else {
+    eventOrder = [
+      'onBeforeRequest-P',
+      'onBeforeSendHeaders-P',
+      'onSendHeaders-P',
+      'onHeadersReceived-P',
+      'onResponseStarted-P',
+      'onCompleted-P',
+      'onBeforeRequest',
+      'onBeforeSendHeaders',
+      'onSendHeaders',
+      'onHeadersReceived',
+      'onResponseStarted',
+      'onCompleted',
+    ];
+  }
+  expect(
+      events,
+      [eventOrder],
+      {urls: [url]},  // filter
+      ['blocking', 'responseHeaders', 'extraHeaders'] // extraInfoSpec
+  );
+}
+
+function registerPreflightBlockingListener() {
+  const url = getServerURL(BASE + 'accept', 'cors.example.com');
+
+  const onHeadersReceivedCalledForPreflight = callbackPass(() => {});
+  chrome.webRequest.onHeadersReceived.addListener(
+      function onHeadersReceived(details) {
+        if (details.method === 'OPTIONS') {
+          onHeadersReceivedCalledForPreflight();
+          // Synchronously removing the listener breaks the behavior.
+          setTimeout(() => {
+            chrome.webRequest.onHeadersReceived.removeListener(
+                onHeadersReceived)
+          }, 0);
+          return {cancel: true};
+        }
+      }, {urls: [url]}, ['blocking', 'extraHeaders']);
+
+  const done = callbackPass(() => {});
+  let hasSeenPreflightError = false;
+  chrome.webRequest.onErrorOccurred.addListener(
+      function onErrorOccurred(details) {
+        if (details.method === 'OPTIONS') {
+          hasSeenPreflightError = true;
+        }
+
+        // We see an error event for the actual request only when OOR-CORS
+        // is enabled; otherwise the CORS module in blink doesn't make a network
+        // request for the actual request.
+        if (details.method === 'GET' || getCorsMode() == 'blink') {
+          chrome.webRequest.onErrorOccurred.removeListener(onErrorOccurred);
+          chrome.test.assertTrue(hasSeenPreflightError);
+          done();
+        }
+      }, {urls: [url]});
+}
+
+function registerPreflightRedirectingListener() {
+  const url = getServerURL(BASE + 'accept', 'cors.example.com');
+
+  // This is needed to observe preflight requests when OOR-CORS is enabled.
+  chrome.webRequest.onHeadersReceived.addListener(
+      () => {},
+      {urls: [url]}, ['extraHeaders']);
+
+  const onBeforeRequestCalledForPreflight = callbackPass(() => {});
+  chrome.webRequest.onBeforeRequest.addListener(
+      function onBeforeRequest(details) {
+        if (details.method === 'OPTIONS') {
+          onBeforeRequestCalledForPreflight();
+          // Synchronously removing the listener breaks the behavior.
+          setTimeout(() => {
+            chrome.webRequest.onBeforeRequest.removeListener(onBeforeRequest)
+          }, 0);
+          return {redirectUrl: url + '?redirected'};
+        }
+      }, {urls: [url]}, ['blocking']);
+
+  if (getCorsMode() == 'network_service') {
+    // When CORS is implemented in the network service, we see failures on both
+    // the preflight and the actual request.
+    const done = callbackPass(() => {});
+    let hasSeenPreflightError = false;
+    chrome.webRequest.onErrorOccurred.addListener(
+        function onErrorOccurred(details) {
+          if (details.method === 'OPTIONS') {
+            hasSeenPreflightError = true;
+          }
+
+          if (details.method === 'GET') {
+            chrome.webRequest.onErrorOccurred.removeListener(onErrorOccurred);
+            chrome.test.assertTrue(hasSeenPreflightError);
+            done();
+          }
+        }, {urls: [url]});
+  } else {
+    // In this case we see no completion events nor error events - The renderer
+    // cancels the preflight request in the redirect handling logic, and
+    // WebRequestProxyingURLLoaderFactory suppresses events in such a case.
+    // See https://crbug.com/1014816.
+  }
+}
+
 runTests([
   function testOriginHeader() {
     // Register two sets of listener. One with extraHeaders and the second one
@@ -140,4 +607,33 @@
     navigateAndWait(getServerURL(
         'extensions/api_test/webrequest/cors/fetch.html?path=reject'));
   },
+  function testCorsPreflightWithoutExtraHeaders() {
+    if (getCorsMode() == 'network_service') {
+      setExpectationsForNonObservablePreflight();
+    } else {
+      setExpectationsForObservablePreflight([]);
+    }
+    navigateAndWait(getServerURL(
+        BASE + 'fetch.html?path=accept&with-preflight'));
+  },
+  function testCorsPreflightWithExtraHeaders() {
+    setExpectationsForObservablePreflight(['extraHeaders']);
+    navigateAndWait(getServerURL(
+        BASE + 'fetch.html?path=accept&with-preflight'));
+  },
+  function testCorsPreflightModificationWithExtraHeaders() {
+    setExpectationsForSuccessfulPreflight();
+    navigateAndWait(getServerURL(
+        BASE + 'fetch.html?path=accept&with-preflight'));
+  },
+  function testCorsPreflightBlockIsBlocked() {
+    registerPreflightBlockingListener();
+    navigateAndWait(getServerURL(
+        BASE + 'fetch.html?path=accept&with-preflight'));
+  },
+  function testCorsPreflightRedirect() {
+    registerPreflightRedirectingListener();
+    navigateAndWait(getServerURL(
+        BASE + 'fetch.html?path=accept&with-preflight'));
+  },
 ]);
diff --git a/chrome/test/data/local_ntp/realbox_browsertest.js b/chrome/test/data/local_ntp/realbox_browsertest.js
index 376214f..af1f56e 100644
--- a/chrome/test/data/local_ntp/realbox_browsertest.js
+++ b/chrome/test/data/local_ntp/realbox_browsertest.js
@@ -669,3 +669,52 @@
 
   assertTrue(clicked);
 };
+
+test.realbox.testArrowDownMovesFocus = function() {
+  test.realbox.realboxEl.value = 'hello ';
+  test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
+
+  chrome.embeddedSearch.searchBox.onqueryautocompletedone({
+    input: test.realbox.realboxEl.value,
+    matches: [
+      test.realbox.getSearchMatch({contents: 'hello fresh'}),
+      test.realbox.getSearchMatch({contents: 'hello kitty'}),
+      test.realbox.getSearchMatch({contents: 'hello dolly'}),
+      test.realbox.getUrlMatch(),
+    ],
+  });
+
+  test.realbox.realboxEl.focus();
+
+  const arrowDown = new KeyboardEvent('keydown', {
+    bubbles: true,
+    cancelable: true,
+    key: 'ArrowDown',
+  });
+  test.realbox.realboxEl.dispatchEvent(arrowDown);
+  assertTrue(arrowDown.defaultPrevented);
+
+  const matchEls = $(test.realbox.IDS.REALBOX_MATCHES).children;
+  assertEquals(4, matchEls.length);
+
+  // Arrow up/down while focus is in realbox should not focus matches.
+  assertTrue(matchEls[1].classList.contains(test.realbox.CLASSES.SELECTED));
+  assertEquals(document.activeElement, test.realbox.realboxEl);
+
+  // Pretend that user moved focus to first match via Tab.
+  matchEls[0].focus();
+  matchEls[0].dispatchEvent(new Event('focusin', {bubbles: true}));
+  assertTrue(matchEls[0].classList.contains(test.realbox.CLASSES.SELECTED));
+
+  const arrowDown2 = new KeyboardEvent('keydown', {
+    bubbles: true,
+    cancelable: true,
+    key: 'ArrowDown',
+  });
+  matchEls[0].dispatchEvent(arrowDown2);
+  assertTrue(arrowDown2.defaultPrevented);
+
+  // Arrow up/down while focus is in the matches should change focus.
+  assertTrue(matchEls[1].classList.contains(test.realbox.CLASSES.SELECTED));
+  assertEquals(document.activeElement, matchEls[1])
+};
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index c402b88c..8856bcc 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -31,12 +31,12 @@
     "indicator_selector": "A CSS selector that locates the controlled setting indicator directly affected by the policy. This is appended to the selector 'span.controlled-setting-indicator'."
   },
 
-  "TabLifecyclesEnabled": {
+  "TabFreezingEnabled": {
     "os": ["win"],
     "official_only": true,
-    "test_policy": { "TabLifecyclesEnabled": false },
+    "test_policy": { "TabFreezingEnabled": false },
     "pref_mappings": [
-      { "pref": "tab_lifecycles_enabled",
+      { "pref": "tab_freezing_enabled",
         "local_state": true
       }
     ]
@@ -431,12 +431,23 @@
     "os": ["chromeos"],
     "test_policy": {
       "ExternalPrintServers": {
+        "id": "id123",
         "url": "https://example.com/policyfile",
         "hash": "deadbeefdeadbeefdeadbeef"
       }
     }
   },
 
+  "ExternalPrintServersWhitelist": {
+    "os": ["chromeos"],
+    "test_policy": {
+      "ExternalPrintServersWhitelist":  ["id4", "id7", "id10"]
+    },
+    "pref_mappings": [
+      { "pref": "native_printing.external_print_servers_whitelist" }
+    ]
+  },
+
   "NativePrinters": {
     "os": ["chromeos"],
     "test_policy": {
diff --git a/chrome/test/data/subresource_filter/empty.js b/chrome/test/data/subresource_filter/empty.js
new file mode 100644
index 0000000..73f00f7
--- /dev/null
+++ b/chrome/test/data/subresource_filter/empty.js
@@ -0,0 +1,5 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Do nothing.
diff --git a/chrome/test/data/subresource_filter/worklet_script_fetch.html b/chrome/test/data/subresource_filter/worklet_script_fetch.html
new file mode 100644
index 0000000..0c2c752
--- /dev/null
+++ b/chrome/test/data/subresource_filter/worklet_script_fetch.html
@@ -0,0 +1,25 @@
+<script>
+const scriptURL = 'worklet_script_fetch.js';
+
+// Test AudioWorklet.
+const audioContext = new OfflineAudioContext(2,44100*40,44100);
+const audioWorkletPromise = audioContext.audioWorklet.addModule(scriptURL);
+
+// Test PaintWorklet.
+const paintWorkletPromise = CSS.paintWorklet.addModule(scriptURL);
+
+// TODO(https://crbug.com/1011208): Test AnimationWorklet and LayoutWorklet.
+
+const promises = [audioWorkletPromise, paintWorkletPromise];
+Promise.allSettled(promises)
+  .then(results => {
+    // The subresource filter should pass or block them all.
+    const numOfSuccess = results.filter(r => r.status === 'fulfilled').length;
+    if (numOfSuccess === promises.length)
+      document.title = 'FetchSucceeded';
+    else if (numOfSuccess === 0)
+      document.title = 'FetchFailed';
+    else
+      document.title = 'FetchPartiallyFailed';
+  });
+</script>
diff --git a/chrome/test/data/subresource_filter/worklet_script_fetch.js b/chrome/test/data/subresource_filter/worklet_script_fetch.js
new file mode 100644
index 0000000..5bcce97
--- /dev/null
+++ b/chrome/test/data/subresource_filter/worklet_script_fetch.js
@@ -0,0 +1,5 @@
+// Copyright 2019 The Chromium 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 './empty.js';
diff --git a/chrome/test/data/webui/print_preview/preview_generation_test.js b/chrome/test/data/webui/print_preview/preview_generation_test.js
index 5f86e58..331490b 100644
--- a/chrome/test/data/webui/print_preview/preview_generation_test.js
+++ b/chrome/test/data/webui/print_preview/preview_generation_test.js
@@ -509,6 +509,36 @@
               expectedScalingType: print_preview.ScalingType.DEFAULT,
             });
             nativeLayer.resetResolver('getPreview');
+            // DEFAULT -> FIT_TO_PAPER
+            page.setSetting(
+                'scalingTypePdf', print_preview.ScalingType.FIT_TO_PAPER);
+            return nativeLayer.whenCalled('getPreview');
+          })
+          .then(function(args) {
+            validateScalingChange({
+              printTicket: args.printTicket,
+              scalingTypeKey: 'scalingTypePdf',
+              expectedTicketId: 5,
+              expectedTicketScaleFactor: 100,
+              expectedScalingValue: '100',
+              expectedScalingType: print_preview.ScalingType.FIT_TO_PAPER,
+            });
+            nativeLayer.resetResolver('getPreview');
+            // FIT_TO_PAPER -> DEFAULT
+            page.setSetting(
+                'scalingTypePdf', print_preview.ScalingType.DEFAULT);
+            return nativeLayer.whenCalled('getPreview');
+          })
+          .then(function(args) {
+            validateScalingChange({
+              printTicket: args.printTicket,
+              scalingTypeKey: 'scalingTypePdf',
+              expectedTicketId: 6,
+              expectedTicketScaleFactor: 100,
+              expectedScalingValue: '100',
+              expectedScalingType: print_preview.ScalingType.DEFAULT,
+            });
+            nativeLayer.resetResolver('getPreview');
             // DEFAULT -> CUSTOM
             page.setSetting('scalingTypePdf', print_preview.ScalingType.CUSTOM);
             // Need to set custom value != 100 for preview to regenerate.
@@ -522,7 +552,7 @@
             validateScalingChange({
               printTicket: args.printTicket,
               scalingTypeKey: 'scalingTypePdf',
-              expectedTicketId: 5,
+              expectedTicketId: 7,
               expectedTicketScaleFactor: 120,
               expectedScalingValue: '120',
               expectedScalingType: print_preview.ScalingType.CUSTOM,
@@ -537,7 +567,7 @@
             validateScalingChange({
               printTicket: args.printTicket,
               scalingTypeKey: 'scalingTypePdf',
-              expectedTicketId: 6,
+              expectedTicketId: 8,
               expectedTicketScaleFactor: 100,
               expectedScalingValue: '120',
               expectedScalingType: print_preview.ScalingType.DEFAULT,
diff --git a/chrome/test/data/webui/print_preview/scaling_settings_test.js b/chrome/test/data/webui/print_preview/scaling_settings_test.js
index 42cdaa80..6dbce92 100644
--- a/chrome/test/data/webui/print_preview/scaling_settings_test.js
+++ b/chrome/test/data/webui/print_preview/scaling_settings_test.js
@@ -33,20 +33,24 @@
     });
 
     test(assert(TestNames.ShowCorrectDropdownOptions), function() {
-      // Fit to page unavailable -> No fit to page option.
+      // Not a PDF document -> No fit to page or fit to paper options.
       const fitToPageOption = scalingSection.$$(
           `[value="${scalingSection.ScalingValue.FIT_TO_PAGE}"]`);
+      const fitToPaperOption = scalingSection.$$(
+          `[value="${scalingSection.ScalingValue.FIT_TO_PAPER}"]`);
       const defaultOption =
           scalingSection.$$(`[value="${scalingSection.ScalingValue.DEFAULT}"]`);
       const customOption =
           scalingSection.$$(`[value="${scalingSection.ScalingValue.CUSTOM}"]`);
       assertTrue(fitToPageOption.hidden);
+      assertTrue(fitToPaperOption.hidden);
       assertFalse(defaultOption.hidden);
       assertFalse(customOption.hidden);
 
-      // Fit to page available -> All 3 options.
+      // Fit to page and paper available -> All 4 options.
       setDocumentPdf(true);
       assertFalse(fitToPageOption.hidden);
+      assertFalse(fitToPaperOption.hidden);
       assertFalse(defaultOption.hidden);
       assertFalse(customOption.hidden);
     });
@@ -103,7 +107,7 @@
               .$.userValue.inputElement;
       const scalingDropdown = scalingSection.$$('.md-select');
 
-      // Make fit to page available.
+      // Make fit to page and fit to paper available.
       setDocumentPdf(true);
 
       // Default is 100
@@ -137,6 +141,13 @@
           '105', true, print_preview.ScalingType.CUSTOM,
           print_preview.ScalingType.FIT_TO_PAGE, '105');
 
+      // Change to fit to paper.
+      await print_preview_test_utils.selectOption(
+          scalingSection, scalingSection.ScalingValue.FIT_TO_PAPER.toString());
+      validateState(
+          '105', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.FIT_TO_PAPER, '105');
+
       // Go back to custom. Restores 105 value.
       await print_preview_test_utils.selectOption(
           scalingSection, scalingSection.ScalingValue.CUSTOM.toString());
@@ -189,7 +200,7 @@
           print_preview.ScalingType.CUSTOM, '105');
 
       // Enter a blank value in the scaling field. This should not
-      // change the stored value of scaling or fit to page, to avoid an
+      // change the stored value of scaling or scaling type, to avoid an
       // unnecessary preview regeneration.
       await print_preview_test_utils.triggerInputEvent(
           scalingInput, '', scalingSection);
diff --git a/chrome/test/data/webui/set_time_dialog_browsertest.js b/chrome/test/data/webui/set_time_dialog_browsertest.js
index aee76dd..34f51af 100644
--- a/chrome/test/data/webui/set_time_dialog_browsertest.js
+++ b/chrome/test/data/webui/set_time_dialog_browsertest.js
@@ -3,244 +3,22 @@
 // found in the LICENSE file.
 
 GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN('#include "services/network/public/cpp/features.h"');
 
-/**
- * SetTimeDialogBrowserTest tests the "Set Time" web UI dialog.
- * @constructor
- * @extends {PolymerTest}
- */
-function SetTimeDialogBrowserTest() {}
+// SetTimeDialogBrowserTest tests the "Set Time" web UI dialog.
+// eslint-disable-next-line no-var
+var SetTimeDialogBrowserTest = class extends PolymerTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://set-time/test_loader.html?module=set_time_dialog_test.js';
+  }
 
-SetTimeDialogBrowserTest.prototype = {
-  __proto__: PolymerTest.prototype,
-
-  browsePreload: 'chrome://set-time/',
-
-  extraLibraries: [
-    ...PolymerTest.prototype.extraLibraries,
-    '//chrome/test/data/webui/test_browser_proxy.js',
-  ],
+  /** @override */
+  get featureList() {
+    return {enabled: ['network::features::kOutOfBlinkCors']};
+  }
 };
 
 TEST_F('SetTimeDialogBrowserTest', 'All', function() {
-  suite('SetTimeDialog', function() {
-    let setTimeElement = null;
-    let testBrowserProxy = null;
-
-    /** @implements {settime.SetTimeBrowserProxy} */
-    class TestSetTimeBrowserProxy extends TestBrowserProxy {
-      constructor() {
-        super([
-          'sendPageReady', 'setTimeInSeconds', 'setTimezone', 'dialogClose',
-          'doneClicked'
-        ]);
-      }
-
-      /** @override */
-      sendPageReady() {
-        this.methodCalled('sendPageReady');
-      }
-
-      /** @override */
-      setTimeInSeconds(timeInSeconds) {
-        this.methodCalled('setTimeInSeconds', timeInSeconds);
-      }
-
-      /** @override */
-      setTimezone(timezone) {
-        this.methodCalled('setTimezone', timezone);
-      }
-
-      /** @override */
-      dialogClose() {
-        this.methodCalled('dialogClose');
-      }
-
-      /** @override */
-      doneClicked() {
-        this.methodCalled('doneClicked');
-        cr.webUIListenerCallback('validation-complete');
-      }
-    }
-
-    suiteSetup(function() {
-      // Must use existing timezones in the test.
-      loadTimeData.overrideValues({
-        currentTimezoneId: 'America/Sao_Paulo',
-        timezoneList: [
-          [
-            'America/Los_Angeles',
-            '(GMT-7:00) Pacific Daylight Time (Los Angeles)'
-          ],
-          [
-            'America/Sao_Paulo', '(GMT-3:00) Brasilis Standard Time (Sao Paulo)'
-          ],
-          ['Asia/Seoul', '(GMT+9:00) Korean Standard Time (Seoul)'],
-        ],
-      });
-    });
-
-    setup(function() {
-      testBrowserProxy = new TestSetTimeBrowserProxy();
-      settime.SetTimeBrowserProxyImpl.instance_ = testBrowserProxy;
-      PolymerTest.clearBody();
-      setTimeElement = document.createElement('set-time-dialog');
-      document.body.appendChild(setTimeElement);
-      Polymer.dom.flush();
-    });
-
-    teardown(function() {
-      setTimeElement.remove();
-    });
-
-    test('PageReady', () => {
-      // Verify the page sends the ready message.
-      assertEquals(1, testBrowserProxy.getCallCount('sendPageReady'));
-    });
-
-    test('DateRangeContainsNow', () => {
-      const dateInput = setTimeElement.$$('#dateInput');
-
-      // Input element attributes min and max are strings like '2019-03-01'.
-      const minDate = new Date(dateInput.min);
-      const maxDate = new Date(dateInput.max);
-      const now = new Date();
-
-      // Verify min <= now <= max.
-      assertLE(minDate, now);
-      assertLE(now, maxDate);
-    });
-
-    test('SetDate', () => {
-      const dateInput = setTimeElement.$$('#dateInput');
-      assertTrue(!!dateInput);
-
-      // Simulate the user changing the date picker forward by a week.
-      const today = dateInput.valueAsDate;
-      const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);
-      dateInput.focus();
-      dateInput.valueAsDate = nextWeek;
-      setTimeElement.$$('#doneButton').click();
-
-      // Verify the page sends a request to move time forward.
-      return testBrowserProxy.whenCalled('setTimeInSeconds')
-          .then(timeInSeconds => {
-            const todaySeconds = today.getTime() / 1000;
-            // The exact value isn't important (it depends on the current time).
-            assertGT(timeInSeconds, todaySeconds);
-          });
-    });
-
-    test('Revert invalid date on blur', () => {
-      const dateInput = setTimeElement.$$('#dateInput');
-      dateInput.focus();
-      dateInput.value = '9999-99-99';
-      dateInput.blur();
-      // The exact value isn't important (it depends on the current date, and
-      // the date could change in the middle of the test).
-      assertNotEquals('9999-99-99', dateInput.value);
-    });
-
-    test('SystemTimezoneChanged', () => {
-      const timezoneSelect = setTimeElement.$$('#timezoneSelect');
-      assertTrue(!!timezoneSelect);
-      expectEquals('America/Sao_Paulo', timezoneSelect.value);
-
-      cr.webUIListenerCallback(
-          'system-timezone-changed', 'America/Los_Angeles');
-      expectEquals('America/Los_Angeles', timezoneSelect.value);
-
-      cr.webUIListenerCallback('system-timezone-changed', 'Asia/Seoul');
-      expectEquals('Asia/Seoul', timezoneSelect.value);
-    });
-
-    test('SetDateAndTimezone', () => {
-      const dateInput = setTimeElement.$$('#dateInput');
-      assertTrue(!!dateInput);
-
-      const timeInput = setTimeElement.$$('#timeInput');
-      assertTrue(!!timeInput);
-
-      const timezoneSelect = setTimeElement.$$('#timezoneSelect');
-      assertTrue(!!timezoneSelect);
-      expectEquals('America/Sao_Paulo', timezoneSelect.value);
-
-      // Simulate the user changing the time by forwarding it 15 minutes.
-      const originalTime = dateInput.valueAsDate;
-      originalTime.setMilliseconds(timeInput.valueAsNumber);
-      const updatedTime = new Date(originalTime.getTime() + 15 * 60 * 1000);
-      dateInput.focus();
-      dateInput.valueAsDate = updatedTime;
-      setTimeElement.$$('#doneButton').click();
-
-      // Simulate timezone change.
-      cr.webUIListenerCallback(
-          'system-timezone-changed', 'America/Los_Angeles');
-      expectEquals('America/Los_Angeles', timezoneSelect.value);
-
-      // Make sure that time on input field was updated.
-      const updatedTimeAndTimezone = dateInput.valueAsDate;
-      updatedTimeAndTimezone.setMilliseconds(timeInput.valueAsNumber);
-      // updatedTimeAndTimezone reflects the new timezone so it should be
-      // smaller, because it is more to the west than the original
-      // one, therefore even with the 15 minutes forwarded it should be smaller.
-      expectGT(updatedTime.getTime(), updatedTimeAndTimezone.getTime());
-
-      assertEquals(1, testBrowserProxy.getCallCount('setTimezone'));
-
-      return testBrowserProxy.whenCalled('setTimeInSeconds')
-          .then(timeInSeconds => {
-            const todaySeconds = originalTime.getTime() / 1000;
-            // The exact value isn't important (it depends on the current time).
-            // timeInSeconds should be bigger, because this timestamp is seconds
-            // since epoch and it does not hold any information regarding the
-            // current timezone.
-            assertGT(timeInSeconds, todaySeconds);
-          });
-    });
-
-    suite('NullTimezone', () => {
-      suiteSetup(() => {
-        loadTimeData.overrideValues({
-          currentTimezoneId: '',
-          timezoneList: [],
-        });
-      });
-
-      test('SetDateNullTimezone', () => {
-        const dateInput = setTimeElement.$$('#dateInput');
-        assertTrue(!!dateInput);
-
-        assertEquals(null, setTimeElement.$$('#timezoneSelect'));
-
-        // Simulates the user changing the date picker backward by two days. We
-        // are changing the date to make the test simpler. Changing the time
-        // would require timezone manipulation and handling corner cases over
-        // midnight. valuesAsDate return the time in UTC, therefore the amount
-        // of days here must be bigger than one to avoid situations where the
-        // new time and old time are in the same day.
-        const today = dateInput.valueAsDate;
-        const twoDaysAgo = new Date(today.getTime() - 2 * 24 * 60 * 60 * 1000);
-        dateInput.focus();
-        dateInput.valueAsDate = twoDaysAgo;
-        setTimeElement.$$('#doneButton').click();
-
-        assertEquals(0, testBrowserProxy.getCallCount('setTimezone'));
-
-        // Verify the page sends a request to move time backward.
-        return testBrowserProxy.whenCalled('setTimeInSeconds')
-            .then(newTimeSeconds => {
-              const todaySeconds = today.getTime() / 1000;
-              // Check that the current time is bigger than the new time, which
-              // is supposed to be two days ago. The exact value isn't
-              // important, checking it is difficult because it depends on the
-              // current time, which is constantly updated, therefore we only
-              // assert that one is bigger than the other.
-              assertGT(todaySeconds, newTimeSeconds);
-            });
-      });
-    });
-  });
-
   mocha.run();
 });
diff --git a/chrome/test/data/webui/set_time_dialog_test.js b/chrome/test/data/webui/set_time_dialog_test.js
new file mode 100644
index 0000000..cca927d
--- /dev/null
+++ b/chrome/test/data/webui/set_time_dialog_test.js
@@ -0,0 +1,226 @@
+// Copyright 2019 The Chromium 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 'chrome://set-time/set_time_dialog.js';
+
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {SetTimeBrowserProxyImpl} from 'chrome://set-time/set_time_browser_proxy.js';
+
+import {TestBrowserProxy} from './test_browser_proxy.m.js';
+
+suite('SetTimeDialog', function() {
+  let setTimeElement = null;
+  let testBrowserProxy = null;
+
+  /** @implements {SetTimeBrowserProxy} */
+  class TestSetTimeBrowserProxy extends TestBrowserProxy {
+    constructor() {
+      super([
+        'sendPageReady', 'setTimeInSeconds', 'setTimezone', 'dialogClose',
+        'doneClicked'
+      ]);
+    }
+
+    /** @override */
+    sendPageReady() {
+      this.methodCalled('sendPageReady');
+    }
+
+    /** @override */
+    setTimeInSeconds(timeInSeconds) {
+      this.methodCalled('setTimeInSeconds', timeInSeconds);
+    }
+
+    /** @override */
+    setTimezone(timezone) {
+      this.methodCalled('setTimezone', timezone);
+    }
+
+    /** @override */
+    dialogClose() {
+      this.methodCalled('dialogClose');
+    }
+
+    /** @override */
+    doneClicked() {
+      this.methodCalled('doneClicked');
+      cr.webUIListenerCallback('validation-complete');
+    }
+  }
+
+  suiteSetup(function() {
+    // Must use existing timezones in the test.
+    loadTimeData.overrideValues({
+      currentTimezoneId: 'America/Sao_Paulo',
+      timezoneList: [
+        [
+          'America/Los_Angeles',
+          '(GMT-7:00) Pacific Daylight Time (Los Angeles)'
+        ],
+        ['America/Sao_Paulo', '(GMT-3:00) Brasilis Standard Time (Sao Paulo)'],
+        ['Asia/Seoul', '(GMT+9:00) Korean Standard Time (Seoul)'],
+      ],
+    });
+  });
+
+  setup(function() {
+    testBrowserProxy = new TestSetTimeBrowserProxy();
+    SetTimeBrowserProxyImpl.instance_ = testBrowserProxy;
+    PolymerTest.clearBody();
+    setTimeElement = document.createElement('set-time-dialog');
+    document.body.appendChild(setTimeElement);
+    flush();
+  });
+
+  teardown(function() {
+    setTimeElement.remove();
+  });
+
+  test('PageReady', () => {
+    // Verify the page sends the ready message.
+    assertEquals(1, testBrowserProxy.getCallCount('sendPageReady'));
+  });
+
+  test('DateRangeContainsNow', () => {
+    const dateInput = setTimeElement.$$('#dateInput');
+
+    // Input element attributes min and max are strings like '2019-03-01'.
+    const minDate = new Date(dateInput.min);
+    const maxDate = new Date(dateInput.max);
+    const now = new Date();
+
+    // Verify min <= now <= max.
+    assertLE(minDate, now);
+    assertLE(now, maxDate);
+  });
+
+  test('SetDate', () => {
+    const dateInput = setTimeElement.$$('#dateInput');
+    assertTrue(!!dateInput);
+
+    // Simulate the user changing the date picker forward by a week.
+    const today = dateInput.valueAsDate;
+    const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);
+    dateInput.focus();
+    dateInput.valueAsDate = nextWeek;
+    setTimeElement.$$('#doneButton').click();
+
+    // Verify the page sends a request to move time forward.
+    return testBrowserProxy.whenCalled('setTimeInSeconds')
+        .then(timeInSeconds => {
+          const todaySeconds = today.getTime() / 1000;
+          // The exact value isn't important (it depends on the current time).
+          assertGT(timeInSeconds, todaySeconds);
+        });
+  });
+
+  test('Revert invalid date on blur', () => {
+    const dateInput = setTimeElement.$$('#dateInput');
+    dateInput.focus();
+    dateInput.value = '9999-99-99';
+    dateInput.blur();
+    // The exact value isn't important (it depends on the current date, and
+    // the date could change in the middle of the test).
+    assertNotEquals('9999-99-99', dateInput.value);
+  });
+
+  test('SystemTimezoneChanged', () => {
+    const timezoneSelect = setTimeElement.$$('#timezoneSelect');
+    assertTrue(!!timezoneSelect);
+    expectEquals('America/Sao_Paulo', timezoneSelect.value);
+
+    cr.webUIListenerCallback('system-timezone-changed', 'America/Los_Angeles');
+    expectEquals('America/Los_Angeles', timezoneSelect.value);
+
+    cr.webUIListenerCallback('system-timezone-changed', 'Asia/Seoul');
+    expectEquals('Asia/Seoul', timezoneSelect.value);
+  });
+
+  test('SetDateAndTimezone', () => {
+    const dateInput = setTimeElement.$$('#dateInput');
+    assertTrue(!!dateInput);
+
+    const timeInput = setTimeElement.$$('#timeInput');
+    assertTrue(!!timeInput);
+
+    const timezoneSelect = setTimeElement.$$('#timezoneSelect');
+    assertTrue(!!timezoneSelect);
+    expectEquals('America/Sao_Paulo', timezoneSelect.value);
+
+    // Simulate the user changing the time by forwarding it 15 minutes.
+    const originalTime = dateInput.valueAsDate;
+    originalTime.setMilliseconds(timeInput.valueAsNumber);
+    const updatedTime = new Date(originalTime.getTime() + 15 * 60 * 1000);
+    dateInput.focus();
+    dateInput.valueAsDate = updatedTime;
+    setTimeElement.$$('#doneButton').click();
+
+    // Simulate timezone change.
+    cr.webUIListenerCallback('system-timezone-changed', 'America/Los_Angeles');
+    expectEquals('America/Los_Angeles', timezoneSelect.value);
+
+    // Make sure that time on input field was updated.
+    const updatedTimeAndTimezone = dateInput.valueAsDate;
+    updatedTimeAndTimezone.setMilliseconds(timeInput.valueAsNumber);
+    // updatedTimeAndTimezone reflects the new timezone so it should be
+    // smaller, because it is more to the west than the original
+    // one, therefore even with the 15 minutes forwarded it should be smaller.
+    expectGT(updatedTime.getTime(), updatedTimeAndTimezone.getTime());
+
+    assertEquals(1, testBrowserProxy.getCallCount('setTimezone'));
+
+    return testBrowserProxy.whenCalled('setTimeInSeconds')
+        .then(timeInSeconds => {
+          const todaySeconds = originalTime.getTime() / 1000;
+          // The exact value isn't important (it depends on the current time).
+          // timeInSeconds should be bigger, because this timestamp is seconds
+          // since epoch and it does not hold any information regarding the
+          // current timezone.
+          assertGT(timeInSeconds, todaySeconds);
+        });
+  });
+
+  suite('NullTimezone', () => {
+    suiteSetup(() => {
+      loadTimeData.overrideValues({
+        currentTimezoneId: '',
+        timezoneList: [],
+      });
+    });
+
+    test('SetDateNullTimezone', () => {
+      const dateInput = setTimeElement.$$('#dateInput');
+      assertTrue(!!dateInput);
+
+      assertEquals(null, setTimeElement.$$('#timezoneSelect'));
+
+      // Simulates the user changing the date picker backward by two days. We
+      // are changing the date to make the test simpler. Changing the time
+      // would require timezone manipulation and handling corner cases over
+      // midnight. valuesAsDate return the time in UTC, therefore the amount
+      // of days here must be bigger than one to avoid situations where the
+      // new time and old time are in the same day.
+      const today = dateInput.valueAsDate;
+      const twoDaysAgo = new Date(today.getTime() - 2 * 24 * 60 * 60 * 1000);
+      dateInput.focus();
+      dateInput.valueAsDate = twoDaysAgo;
+      setTimeElement.$$('#doneButton').click();
+
+      assertEquals(0, testBrowserProxy.getCallCount('setTimezone'));
+
+      // Verify the page sends a request to move time backward.
+      return testBrowserProxy.whenCalled('setTimeInSeconds')
+          .then(newTimeSeconds => {
+            const todaySeconds = today.getTime() / 1000;
+            // Check that the current time is bigger than the new time, which
+            // is supposed to be two days ago. The exact value isn't
+            // important, checking it is difficult because it depends on the
+            // current time, which is constantly updated, therefore we only
+            // assert that one is bigger than the other.
+            assertGT(todaySeconds, newTimeSeconds);
+          });
+    });
+  });
+});
diff --git a/chrome/test/data/webui/signin/sync_confirmation_test.js b/chrome/test/data/webui/signin/sync_confirmation_test.js
index c843fed..c21318ad 100644
--- a/chrome/test/data/webui/signin/sync_confirmation_test.js
+++ b/chrome/test/data/webui/signin/sync_confirmation_test.js
@@ -10,16 +10,14 @@
 suite('SigninSyncConfirmationTest', function() {
   let app;
   setup(function() {
+    const browserProxy = new TestSyncConfirmationBrowserProxy();
+    SyncConfirmationBrowserProxyImpl.instance_ = browserProxy;
     PolymerTest.clearBody();
     app = document.createElement('sync-confirmation-app');
-    let accountImageRequested = false;
-    registerMessageCallback('accountImageRequest', this, function() {
-      accountImageRequested = true;
-    });
     document.body.append(app);
     // Check that the account image is requested when the app element is
     // attached to the document.
-    assertTrue(accountImageRequested);
+    return browserProxy.whenCalled('requestAccountImage');
   });
 
   // Tests that no DCHECKS are thrown during initialization of the UI.
diff --git a/chrome/test/data/webui/signin/test_sync_confirmation_browser_proxy.js b/chrome/test/data/webui/signin/test_sync_confirmation_browser_proxy.js
index 799c1ab..1086fb22 100644
--- a/chrome/test/data/webui/signin/test_sync_confirmation_browser_proxy.js
+++ b/chrome/test/data/webui/signin/test_sync_confirmation_browser_proxy.js
@@ -38,6 +38,6 @@
 
   /** @override */
   requestAccountImage() {
-    // Don't request image in the test browser proxy.
+    this.methodCalled('requestAccountImage');
   }
 }
diff --git a/chrome/test/data/webui/tab_strip/tab_list_test.js b/chrome/test/data/webui/tab_strip/tab_list_test.js
index 33b06432..9b5e39ca 100644
--- a/chrome/test/data/webui/tab_strip/tab_list_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_list_test.js
@@ -5,10 +5,10 @@
 import 'chrome://tab-strip/tab_list.js';
 
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
-import {TabStripViewProxy} from 'chrome://tab-strip/tab_strip_view_proxy.js';
+import {TabStripEmbedderProxy} from 'chrome://tab-strip/tab_strip_embedder_proxy.js';
 import {TabsApiProxy} from 'chrome://tab-strip/tabs_api_proxy.js';
 
-import {TestTabStripViewProxy} from './test_tab_strip_view_proxy.js';
+import {TestTabStripEmbedderProxy} from './test_tab_strip_embedder_proxy.js';
 import {TestTabsApiProxy} from './test_tabs_api_proxy.js';
 
 class MockDataTransfer extends DataTransfer {
@@ -52,7 +52,7 @@
   let callbackRouter;
   let optionsCalled;
   let tabList;
-  let testTabStripViewProxy;
+  let testTabStripEmbedderProxy;
   let testTabsApiProxy;
 
   const tabs = [
@@ -99,12 +99,12 @@
     TabsApiProxy.instance_ = testTabsApiProxy;
     callbackRouter = testTabsApiProxy.callbackRouter;
 
-    testTabStripViewProxy = new TestTabStripViewProxy();
-    testTabStripViewProxy.setColors({
+    testTabStripEmbedderProxy = new TestTabStripEmbedderProxy();
+    testTabStripEmbedderProxy.setColors({
       '--background-color': 'white',
       '--foreground-color': 'black',
     });
-    TabStripViewProxy.instance_ = testTabStripViewProxy;
+    TabStripEmbedderProxy.instance_ = testTabStripEmbedderProxy;
 
     tabList = document.createElement('tabstrip-tab-list');
     document.body.appendChild(tabList);
@@ -114,22 +114,22 @@
 
   teardown(() => {
     testTabsApiProxy.reset();
-    testTabStripViewProxy.reset();
+    testTabStripEmbedderProxy.reset();
   });
 
   test('sets theme colors on init', async () => {
-    await testTabStripViewProxy.whenCalled('getColors');
+    await testTabStripEmbedderProxy.whenCalled('getColors');
     assertEquals(tabList.style.getPropertyValue('--background-color'), 'white');
     assertEquals(tabList.style.getPropertyValue('--foreground-color'), 'black');
   });
 
   test('updates theme colors when theme changes', async () => {
-    testTabStripViewProxy.setColors({
+    testTabStripEmbedderProxy.setColors({
       '--background-color': 'pink',
       '--foreground-color': 'blue',
     });
     webUIListenerCallback('theme-changed');
-    await testTabStripViewProxy.whenCalled('getColors');
+    await testTabStripEmbedderProxy.whenCalled('getColors');
     assertEquals(tabList.style.getPropertyValue('--background-color'), 'pink');
     assertEquals(tabList.style.getPropertyValue('--foreground-color'), 'blue');
   });
@@ -250,7 +250,7 @@
   });
 
   test('activating a tab off-screen scrolls to it', async () => {
-    testTabStripViewProxy.setVisible(true);
+    testTabStripEmbedderProxy.setVisible(true);
 
     const scrollPadding = 32;
 
@@ -351,7 +351,7 @@
         testTabsApiProxy.resetResolver('moveTab');
 
         // Mock tab strip going from visible to hidden
-        testTabStripViewProxy.setVisible(false);
+        testTabStripEmbedderProxy.setVisible(false);
         document.dispatchEvent(new Event('visibilitychange'));
 
         const [moveId, newIndex] = await testTabsApiProxy.whenCalled('moveTab');
diff --git a/chrome/test/data/webui/tab_strip/tab_test.js b/chrome/test/data/webui/tab_strip/tab_test.js
index b6dcbb5..4e1b95dc 100644
--- a/chrome/test/data/webui/tab_strip/tab_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_test.js
@@ -5,12 +5,15 @@
 import 'chrome://tab-strip/tab.js';
 
 import {getFavicon} from 'chrome://resources/js/icon.m.js';
+import {TabStripEmbedderProxy} from 'chrome://tab-strip/tab_strip_embedder_proxy.js';
 import {TabNetworkState, TabsApiProxy} from 'chrome://tab-strip/tabs_api_proxy.js';
 
+import {TestTabStripEmbedderProxy} from './test_tab_strip_embedder_proxy.js';
 import {TestTabsApiProxy} from './test_tabs_api_proxy.js';
 
 suite('Tab', function() {
   let testTabsApiProxy;
+  let testTabStripEmbedderProxy;
   let tabElement;
 
   const tab = {
@@ -22,6 +25,9 @@
   setup(() => {
     document.body.innerHTML = '';
 
+    testTabStripEmbedderProxy = new TestTabStripEmbedderProxy();
+    TabStripEmbedderProxy.instance_ = testTabStripEmbedderProxy;
+
     testTabsApiProxy = new TestTabsApiProxy();
     TabsApiProxy.instance_ = testTabsApiProxy;
 
@@ -251,9 +257,15 @@
     tabElement.dispatchEvent(event);
 
     const contextMenuArgs =
-        await testTabsApiProxy.whenCalled('showTabContextMenu');
+        await testTabStripEmbedderProxy.whenCalled('showTabContextMenu');
     assertEquals(contextMenuArgs[0], tabElement.tab.id);
     assertEquals(contextMenuArgs[1], 1);
     assertEquals(contextMenuArgs[2], 2);
   });
+
+  test('activating closes WebUI container', () => {
+    assertEquals(testTabStripEmbedderProxy.getCallCount('closeContainer'), 0);
+    tabElement.click();
+    assertEquals(testTabStripEmbedderProxy.getCallCount('closeContainer'), 1);
+  });
 });
diff --git a/chrome/test/data/webui/tab_strip/test_tab_strip_view_proxy.js b/chrome/test/data/webui/tab_strip/test_tab_strip_embedder_proxy.js
similarity index 68%
rename from chrome/test/data/webui/tab_strip/test_tab_strip_view_proxy.js
rename to chrome/test/data/webui/tab_strip/test_tab_strip_embedder_proxy.js
index a074244f..fd84cd4 100644
--- a/chrome/test/data/webui/tab_strip/test_tab_strip_view_proxy.js
+++ b/chrome/test/data/webui/tab_strip/test_tab_strip_embedder_proxy.js
@@ -4,12 +4,14 @@
 
 import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
 
-export class TestTabStripViewProxy extends TestBrowserProxy {
+export class TestTabStripEmbedderProxy extends TestBrowserProxy {
   constructor() {
     super([
+      'closeContainer',
       'getColors',
       'isVisible',
       'observeThemeChanges',
+      'showTabContextMenu',
     ]);
 
     this.visible_ = false;
@@ -36,4 +38,13 @@
   observeThemeChanges() {
     this.methodCalled('observeThemeChanges');
   }
+
+  closeContainer() {
+    this.methodCalled('closeContainer');
+    return Promise.resolve();
+  }
+
+  showTabContextMenu(tabId, locationX, locationY) {
+    this.methodCalled('showTabContextMenu', [tabId, locationX, locationY]);
+  }
 }
diff --git a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
index 55a573c..4dfeafe 100644
--- a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
+++ b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
@@ -12,7 +12,6 @@
       'getTabs',
       'moveTab',
       'trackThumbnailForTab',
-      'showTabContextMenu',
     ]);
 
     this.tabs_;
@@ -42,10 +41,6 @@
     this.tabs_ = tabs;
   }
 
-  showTabContextMenu(tabId, locationX, locationY) {
-    this.methodCalled('showTabContextMenu', [tabId, locationX, locationY]);
-  }
-
   trackThumbnailForTab(tabId) {
     this.methodCalled('trackThumbnailForTab', tabId);
   }
diff --git a/chrome/updater/win/installer/BUILD.gn b/chrome/updater/win/installer/BUILD.gn
index b7ae096..ad8cf56 100644
--- a/chrome/updater/win/installer/BUILD.gn
+++ b/chrome/updater/win/installer/BUILD.gn
@@ -79,7 +79,9 @@
       "--setup_runtime_deps",
       rebase_path(updater_runtime_deps, root_build_dir),
       "--output_name=updater",
-      "--verbose",
+
+      # Optional argument for verbose archiving output.
+      #"--verbose",
     ]
 
     deps = [
diff --git a/chrome/updater/win/installer/create_installer_archive.py b/chrome/updater/win/installer/create_installer_archive.py
index c8dbb9c..3fd6da8 100644
--- a/chrome/updater/win/installer/create_installer_archive.py
+++ b/chrome/updater/win/installer/create_installer_archive.py
@@ -71,11 +71,9 @@
     if src_paths and not os.path.exists(dst_dir):
       os.makedirs(dst_dir)
     for src_path in src_paths:
-      print(src_path)
       dst_path = os.path.join(dst_dir, os.path.basename(src_path))
       if not os.path.exists(dst_path):
         g_archive_inputs.append(src_path)
-        print('paths src_path={0}, dest_dir={1}'.format(src_path, dst_dir))
         shutil.copy(src_path, dst_dir)
 
 def GetLZMAExec(build_dir):
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 63b7d07..1d80155 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -76,6 +76,7 @@
 #include <fontconfig/fontconfig.h>
 #include <signal.h>
 #include <sys/prctl.h>
+#include "ui/gfx/linux/fontconfig_util.h"
 #endif
 
 #if defined(OS_ANDROID)
@@ -440,8 +441,15 @@
   base::PathService::Get(base::DIR_MODULE, &dir_module);
   base::FilePath dir_font = dir_module.Append("fonts");
 
+  // Setting rescan interval to 0 will disable re-scan. More details in
+  // b/141204302#comment41.
+  // TODO(crbug/1015146): move re-scan disable logic to GetGlobalFontConfig().
+  if (!FcConfigSetRescanInterval(gfx::GetGlobalFontConfig(), 0)) {
+    LOG(WARNING) << "Cannot disable fontconfig rescan.";
+  }
+
   const FcChar8 *dir_font_char8 = reinterpret_cast<const FcChar8*>(dir_font.value().data());
-  if (FcConfigAppFontAddDir(nullptr, dir_font_char8) == FcFalse) {
+  if (!FcConfigAppFontAddDir(gfx::GetGlobalFontConfig(), dir_font_char8)) {
     LOG(ERROR) << "Cannot load fonts from " << dir_font_char8;
   }
 #endif
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 7501a8c..fced831 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -85,11 +85,11 @@
 #include "ui/display/screen.h"
 #include "ui/gl/gl_switches.h"
 
-#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
+#if BUILDFLAG(ENABLE_CAST_RENDERER)
 #include "chromecast/media/service/cast_mojo_media_client.h"
 #include "media/mojo/mojom/constants.mojom.h"  // nogncheck
-#include "media/mojo/services/media_service.h"      // nogncheck
-#endif  // ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS
+#include "media/mojo/services/media_service.h"  // nogncheck
+#endif  // BUILDFLAG(ENABLE_CAST_RENDERER)
 
 #if defined(OS_LINUX) || defined(OS_ANDROID)
 #include "components/crash/content/browser/crash_handler_host_linux.h"
@@ -102,7 +102,6 @@
 #if defined(OS_ANDROID)
 #include "components/cdm/browser/cdm_message_filter_android.h"
 #include "components/crash/content/app/crashpad.h"
-#include "media/mojo/services/android_mojo_media_client.h"
 #if !BUILDFLAG(USE_CHROMECAST_CDMS)
 #include "components/cdm/browser/media_drm_storage_impl.h"
 #include "url/origin.h"
@@ -146,11 +145,10 @@
 namespace shell {
 
 namespace {
-#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
+#if BUILDFLAG(ENABLE_CAST_RENDERER)
 static void CreateMediaService(CastContentBrowserClient* browser_client,
                                service_manager::mojom::ServiceRequest request) {
   std::unique_ptr<::media::MediaService> service;
-#if BUILDFLAG(ENABLE_CAST_RENDERER)
   auto mojo_media_client = std::make_unique<media::CastMojoMediaClient>(
       browser_client->GetCmaBackendFactory(),
       base::Bind(&CastContentBrowserClient::CreateCdmFactory,
@@ -160,15 +158,9 @@
       browser_client->media_resource_tracker());
   service = std::make_unique<::media::MediaService>(
       std::move(mojo_media_client), std::move(request));
-#elif defined(OS_ANDROID)
-  service = std::make_unique<::media::MediaService>(
-      std::make_unique<::media::AndroidMojoMediaClient>(), std::move(request));
-#else
-#error "Unsupported configuration."
-#endif  // defined(ENABLE_CAST_RENDERER)
   service_manager::Service::RunAsyncUntilTermination(std::move(service));
 }
-#endif  // BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
+#endif  // BUILDFLAG(ENABLE_CAST_RENDERER)
 
 #if defined(OS_ANDROID) && !BUILDFLAG(USE_CHROMECAST_CDMS)
 void CreateOriginId(cdm::MediaDrmStorageImpl::OriginIdObtainedCB callback) {
@@ -348,11 +340,8 @@
 #if BUILDFLAG(USE_CHROMECAST_CDMS)
 std::unique_ptr<::media::CdmFactory> CastContentBrowserClient::CreateCdmFactory(
     service_manager::mojom::InterfaceProvider* host_interfaces) {
-#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
   return std::make_unique<media::CastCdmFactory>(GetMediaTaskRunner(),
                                                  media_resource_tracker());
-#endif  // BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
-  return nullptr;
 }
 #endif  // BUILDFLAG(USE_CHROMECAST_CDMS)
 
@@ -792,15 +781,15 @@
 void CastContentBrowserClient::RunServiceInstance(
     const service_manager::Identity& identity,
     mojo::PendingReceiver<service_manager::mojom::Service>* receiver) {
-#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
-  if (identity.name() == ::media::mojom::kMediaServiceName) {
+#if BUILDFLAG(ENABLE_CAST_RENDERER)
+  if (identity.name() == ::media::mojom::kMediaRendererServiceName) {
     service_manager::mojom::ServiceRequest request(std::move(*receiver));
     GetMediaTaskRunner()->PostTask(
         FROM_HERE,
         base::BindOnce(&CreateMediaService, this, std::move(request)));
     return;
   }
-#endif  // BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
+#endif  // BUILDFLAG(ENABLE_CAST_RENDERER)
 
 #if BUILDFLAG(ENABLE_EXTERNAL_MOJO_SERVICES)
   if (identity.name() == external_mojo::BrokerService::kServiceName) {
diff --git a/chromecast/media/audio/capture_service/capture_service_receiver.cc b/chromecast/media/audio/capture_service/capture_service_receiver.cc
index 2c0422d..687a8aa 100644
--- a/chromecast/media/audio/capture_service/capture_service_receiver.cc
+++ b/chromecast/media/audio/capture_service/capture_service_receiver.cc
@@ -13,7 +13,6 @@
 #include "base/logging.h"
 #include "base/message_loop/message_pump_type.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "base/timer/timer.h"
 #include "chromecast/media/audio/audio_buildflags.h"
 #include "chromecast/media/audio/capture_service/constants.h"
 #include "chromecast/media/audio/capture_service/message_parsing_util.h"
@@ -66,7 +65,6 @@
   const int channels_;
 
   ::media::AudioInputStream::AudioInputCallback* input_callback_;
-  base::OneShotTimer inactivity_timer_;
 
   DISALLOW_COPY_AND_ASSIGN(Socket);
 };
@@ -86,25 +84,16 @@
 void CaptureServiceReceiver::Socket::Start(
     ::media::AudioInputStream::AudioInputCallback* input_callback) {
   input_callback_ = input_callback;
-  inactivity_timer_.Start(FROM_HERE, CaptureServiceReceiver::kInactivityTimeout,
-                          this,
-                          &CaptureServiceReceiver::Socket::OnInactivityTimeout);
   ReceiveMessages();
 }
 
 void CaptureServiceReceiver::Socket::ReportErrorAndStop() {
-  inactivity_timer_.Stop();
   if (input_callback_) {
     input_callback_->OnError();
   }
   input_callback_ = nullptr;
 }
 
-void CaptureServiceReceiver::Socket::OnInactivityTimeout() {
-  LOG(ERROR) << "Timed out " << this << " due to inactivity";
-  ReportErrorAndStop();
-}
-
 void CaptureServiceReceiver::Socket::OnError(int error) {
   LOG(INFO) << "Socket error from " << this << ": " << error;
   ReportErrorAndStop();
@@ -125,11 +114,6 @@
     ReportErrorAndStop();
     return false;
   }
-
-  if (input_callback_) {
-    inactivity_timer_.Reset();
-  }
-
   return HandleAudio(std::move(audio.value()), timestamp);
 }
 
@@ -152,7 +136,6 @@
 
 // static
 constexpr base::TimeDelta CaptureServiceReceiver::kConnectTimeout;
-constexpr base::TimeDelta CaptureServiceReceiver::kInactivityTimeout;
 
 CaptureServiceReceiver::CaptureServiceReceiver(
     const ::media::AudioParameters& audio_params)
diff --git a/chromecast/media/audio/capture_service/capture_service_receiver.h b/chromecast/media/audio/capture_service/capture_service_receiver.h
index 6d5e988..9fe39c8 100644
--- a/chromecast/media/audio/capture_service/capture_service_receiver.h
+++ b/chromecast/media/audio/capture_service/capture_service_receiver.h
@@ -31,10 +31,6 @@
   static constexpr base::TimeDelta kConnectTimeout =
       base::TimeDelta::FromSeconds(1);
 
-  // The timeout for a connected socket to disconnect due to inactivity.
-  static constexpr base::TimeDelta kInactivityTimeout =
-      base::TimeDelta::FromSeconds(5);
-
   explicit CaptureServiceReceiver(const ::media::AudioParameters& audio_params);
   ~CaptureServiceReceiver();
 
diff --git a/chromecast/media/audio/capture_service/capture_service_receiver_unittest.cc b/chromecast/media/audio/capture_service/capture_service_receiver_unittest.cc
index 906dd7cb..46aa475 100644
--- a/chromecast/media/audio/capture_service/capture_service_receiver_unittest.cc
+++ b/chromecast/media/audio/capture_service/capture_service_receiver_unittest.cc
@@ -108,9 +108,13 @@
 
   receiver_.StartWithSocket(&audio_, std::move(socket));
   task_environment_.RunUntilIdle();
+  // Stop receiver to disconnect socket, since receiver doesn't own the IO
+  // task runner in unittests.
+  receiver_.Stop();
+  task_environment_.RunUntilIdle();
 }
 
-TEST_F(CaptureServiceReceiverTest, ReceiveInvalidMessage) {
+TEST_F(CaptureServiceReceiverTest, ReceiveEmptyMessage) {
   auto socket = std::make_unique<MockStreamSocket>();
   EXPECT_CALL(*socket, Connect(_)).WillOnce(Return(net::OK));
   EXPECT_CALL(*socket, Read(_, _, _))
@@ -128,6 +132,29 @@
   task_environment_.RunUntilIdle();
 }
 
+TEST_F(CaptureServiceReceiverTest, ReceiveInvalidMessage) {
+  auto socket = std::make_unique<MockStreamSocket>();
+  EXPECT_CALL(*socket, Connect(_)).WillOnce(Return(net::OK));
+  EXPECT_CALL(*socket, Read(_, _, _))
+      .WillOnce(Invoke([](net::IOBuffer* buf, int,
+                          net::CompletionOnceCallback) {
+        std::vector<char> header(16, 0);
+        base::BigEndianWriter data_writer(header.data(), header.size());
+        data_writer.WriteU16(334);  // 160 frames + header - data[0], in bytes.
+        data_writer.WriteU16(1);    // Mono channels.
+        data_writer.WriteU16(6);    // Invalid format.
+        data_writer.WriteU16(0);    // Padding zero.
+        data_writer.WriteU64(0);    // Timestamp.
+        std::copy(header.data(), header.data() + header.size(), buf->data());
+        // No need to fill audio frames.
+        return 336;
+      }));
+  EXPECT_CALL(audio_, OnError());
+
+  receiver_.StartWithSocket(&audio_, std::move(socket));
+  task_environment_.RunUntilIdle();
+}
+
 TEST_F(CaptureServiceReceiverTest, ReceiveError) {
   auto socket = std::make_unique<MockStreamSocket>();
   EXPECT_CALL(*socket, Connect(_)).WillOnce(Return(net::OK));
@@ -149,16 +176,6 @@
   task_environment_.RunUntilIdle();
 }
 
-TEST_F(CaptureServiceReceiverTest, ReceiveTimeout) {
-  auto socket = std::make_unique<MockStreamSocket>();
-  EXPECT_CALL(*socket, Connect(_)).WillOnce(Return(net::OK));
-  EXPECT_CALL(*socket, Read(_, _, _)).WillOnce(Return(net::ERR_IO_PENDING));
-  EXPECT_CALL(audio_, OnError());
-
-  receiver_.StartWithSocket(&audio_, std::move(socket));
-  task_environment_.FastForwardBy(CaptureServiceReceiver::kInactivityTimeout);
-}
-
 }  // namespace
 }  // namespace capture_service
 }  // namespace media
diff --git a/chromeos/components/drivefs/fake_drivefs_launcher_client.cc b/chromeos/components/drivefs/fake_drivefs_launcher_client.cc
index 4cefa0f..4e568e8 100644
--- a/chromeos/components/drivefs/fake_drivefs_launcher_client.cc
+++ b/chromeos/components/drivefs/fake_drivefs_launcher_client.cc
@@ -14,6 +14,7 @@
 #include "chromeos/components/drivefs/pending_connection_manager.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_cros_disks_client.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
@@ -24,7 +25,7 @@
 namespace drivefs {
 namespace {
 
-void ConnectAsync(mojom::FakeDriveFsLauncherRequest request,
+void ConnectAsync(mojo::PendingReceiver<mojom::FakeDriveFsLauncher> receiver,
                   mojo::NamedPlatformChannel::ServerName server_name) {
   mojo::PlatformChannelEndpoint endpoint =
       mojo::NamedPlatformChannel::ConnectToServer(server_name);
@@ -33,7 +34,7 @@
 
   mojo::OutgoingInvitation invitation;
   mojo::FuseMessagePipes(invitation.AttachMessagePipe("drivefs-launcher"),
-                         request.PassMessagePipe());
+                         receiver.PassPipe());
   mojo::OutgoingInvitation::Send(std::move(invitation),
                                  base::kNullProcessHandle, std::move(endpoint));
 }
@@ -59,7 +60,7 @@
   base::PostTask(
       FROM_HERE,
       {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT},
-      base::BindOnce(&ConnectAsync, mojo::MakeRequest(&launcher_),
+      base::BindOnce(&ConnectAsync, launcher_.BindNewPipeAndPassReceiver(),
                      socket_path_.value()));
 
   chromeos::DBusThreadManager* dbus_thread_manager =
diff --git a/chromeos/components/drivefs/fake_drivefs_launcher_client.h b/chromeos/components/drivefs/fake_drivefs_launcher_client.h
index 80935da..ddedec2 100644
--- a/chromeos/components/drivefs/fake_drivefs_launcher_client.h
+++ b/chromeos/components/drivefs/fake_drivefs_launcher_client.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "chromeos/components/drivefs/mojom/fake_drivefs_launcher.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace drivefs {
 
@@ -33,7 +34,7 @@
   const base::FilePath chroot_path_;
   const base::FilePath socket_path_;
 
-  mojom::FakeDriveFsLauncherPtr launcher_;
+  mojo::Remote<mojom::FakeDriveFsLauncher> launcher_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeDriveFsLauncherClient);
 };
diff --git a/chromeos/dbus/cicerone_client.cc b/chromeos/dbus/cicerone_client.cc
index e6b9096e..8e5928a 100644
--- a/chromeos/dbus/cicerone_client.cc
+++ b/chromeos/dbus/cicerone_client.cc
@@ -94,6 +94,10 @@
     return is_apply_ansible_playbook_progress_signal_connected_;
   }
 
+  bool IsUpgradeContainerProgressSignalConnected() override {
+    return is_upgrade_container_progress_signal_connected_;
+  }
+
   void LaunchContainerApplication(
       const vm_tools::cicerone::LaunchContainerApplicationRequest& request,
       DBusMethodCallback<vm_tools::cicerone::LaunchContainerApplicationResponse>
@@ -440,6 +444,51 @@
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
+  void UpgradeContainer(
+      const vm_tools::cicerone::UpgradeContainerRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::UpgradeContainerResponse> callback)
+      override {
+    dbus::MethodCall method_call(vm_tools::cicerone::kVmCiceroneInterface,
+                                 vm_tools::cicerone::kUpgradeContainerMethod);
+    dbus::MessageWriter writer(&method_call);
+
+    if (!writer.AppendProtoAsArrayOfBytes(request)) {
+      LOG(ERROR) << "Failed to encode UpgradeContainerRequest protobuf";
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
+      return;
+    }
+
+    cicerone_proxy_->CallMethod(
+        &method_call, kDefaultTimeout.InMilliseconds(),
+        base::BindOnce(&CiceroneClientImpl::OnDBusProtoResponse<
+                           vm_tools::cicerone::UpgradeContainerResponse>,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  void CancelUpgradeContainer(
+      const vm_tools::cicerone::CancelUpgradeContainerRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::CancelUpgradeContainerResponse>
+          callback) override {
+    dbus::MethodCall method_call(
+        vm_tools::cicerone::kVmCiceroneInterface,
+        vm_tools::cicerone::kCancelUpgradeContainerMethod);
+    dbus::MessageWriter writer(&method_call);
+
+    if (!writer.AppendProtoAsArrayOfBytes(request)) {
+      LOG(ERROR) << "Failed to encode CancelUpgradeContainerRequest protobuf";
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
+      return;
+    }
+
+    cicerone_proxy_->CallMethod(
+        &method_call, kDefaultTimeout.InMilliseconds(),
+        base::BindOnce(&CiceroneClientImpl::OnDBusProtoResponse<
+                           vm_tools::cicerone::CancelUpgradeContainerResponse>,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   void WaitForServiceToBeAvailable(
       dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback)
       override {
@@ -552,6 +601,15 @@
             weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&CiceroneClientImpl::OnSignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
+
+    cicerone_proxy_->ConnectToSignal(
+        vm_tools::cicerone::kVmCiceroneInterface,
+        vm_tools::cicerone::kUpgradeContainerProgressSignal,
+        base::BindRepeating(
+            &CiceroneClientImpl::OnUpgradeContainerProgressSignal,
+            weak_ptr_factory_.GetWeakPtr()),
+        base::BindOnce(&CiceroneClientImpl::OnSignalConnected,
+                       weak_ptr_factory_.GetWeakPtr()));
   }
 
  private:
@@ -728,12 +786,24 @@
     }
   }
 
+  void OnUpgradeContainerProgressSignal(dbus::Signal* signal) {
+    vm_tools::cicerone::UpgradeContainerProgressSignal proto;
+    dbus::MessageReader reader(signal);
+    if (!reader.PopArrayOfBytesAsProto(&proto)) {
+      LOG(ERROR) << "Failed to parse proto from DBus Signal";
+      return;
+    }
+    for (auto& observer : observer_list_) {
+      observer.OnUpgradeContainerProgress(proto);
+    }
+  }
+
   void OnSignalConnected(const std::string& interface_name,
                          const std::string& signal_name,
                          bool is_connected) {
     if (!is_connected) {
       LOG(ERROR)
-          << "Failed to connect to Signal. Async StartContainer will not work";
+          << "Failed to connect to Signal. Async container ops may not work";
     }
     DCHECK_EQ(interface_name, vm_tools::cicerone::kVmCiceroneInterface);
     if (signal_name == vm_tools::cicerone::kContainerStartedSignal) {
@@ -769,6 +839,9 @@
     } else if (signal_name ==
                vm_tools::cicerone::kApplyAnsiblePlaybookProgressSignal) {
       is_apply_ansible_playbook_progress_signal_connected_ = is_connected;
+    } else if (signal_name ==
+               vm_tools::cicerone::kUpgradeContainerProgressSignal) {
+      is_upgrade_container_progress_signal_connected_ = is_connected;
     } else {
       NOTREACHED();
     }
@@ -791,6 +864,7 @@
   bool is_import_lxd_container_progress_signal_connected_ = false;
   bool is_pending_app_list_updates_signal_connected_ = false;
   bool is_apply_ansible_playbook_progress_signal_connected_ = false;
+  bool is_upgrade_container_progress_signal_connected_ = false;
 
   // Note: This should remain the last member so it'll be destroyed and
   // invalidate its weak pointers before any other members are destroyed.
diff --git a/chromeos/dbus/cicerone_client.h b/chromeos/dbus/cicerone_client.h
index bacf22b..3fdce4a 100644
--- a/chromeos/dbus/cicerone_client.h
+++ b/chromeos/dbus/cicerone_client.h
@@ -89,6 +89,11 @@
         const vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal&
             signal) = 0;
 
+    // This is signaled from Cicerone while a container is being upgraded
+    // via UpgradeContainer.
+    virtual void OnUpgradeContainerProgress(
+        const vm_tools::cicerone::UpgradeContainerProgressSignal& signal) = 0;
+
    protected:
     virtual ~Observer() = default;
   };
@@ -146,6 +151,9 @@
   // This should be true prior to calling ApplyAnsiblePlaybook.
   virtual bool IsApplyAnsiblePlaybookProgressSignalConnected() = 0;
 
+  // This should be true prior to calling UpgradeContainer.
+  virtual bool IsUpgradeContainerProgressSignalConnected() = 0;
+
   // Launches an application inside a running Container.
   // |callback| is called after the method call finishes.
   virtual void LaunchContainerApplication(
@@ -254,6 +262,20 @@
       DBusMethodCallback<vm_tools::cicerone::ApplyAnsiblePlaybookResponse>
           callback) = 0;
 
+  // Upgrades the container.
+  // |callback| is called when the method completes.
+  virtual void UpgradeContainer(
+      const vm_tools::cicerone::UpgradeContainerRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::UpgradeContainerResponse>
+          callback) = 0;
+
+  // Cancels the in progress container upgrade.
+  // |callback| is called when the method completes.
+  virtual void CancelUpgradeContainer(
+      const vm_tools::cicerone::CancelUpgradeContainerRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::CancelUpgradeContainerResponse>
+          callback) = 0;
+
   // Registers |callback| to run when the Cicerone service becomes available.
   // If the service is already available, or if connecting to the name-owner-
   // changed signal fails, |callback| will be run once asynchronously.
diff --git a/chromeos/dbus/dlcservice/dlcservice_client.cc b/chromeos/dbus/dlcservice/dlcservice_client.cc
index 24fc7745..7b00aa6 100644
--- a/chromeos/dbus/dlcservice/dlcservice_client.cc
+++ b/chromeos/dbus/dlcservice/dlcservice_client.cc
@@ -44,25 +44,25 @@
 class DlcserviceErrorResponseHandler {
  public:
   explicit DlcserviceErrorResponseHandler(dbus::ErrorResponse* err_response)
-      : err(dlcservice::kErrorInternal) {
-    if (err_response && dbus::MessageReader(err_response).PopString(&err_msg) &&
-        DlcserviceErrorFromString(err_msg, &err)) {
-      VLOG(1) << "Handling error response err=" << err
-              << " err_msg=" << err_msg;
-    } else {
-      LOG(ERROR) << "Failed to set err based on error response "
-                 << "defaulted to kErrorInternal.";
+      : err_(dlcservice::kErrorInternal) {
+    if (!err_response) {
+      LOG(ERROR) << "Failed to set err since ErrorResponse is null.";
+      return;
     }
+    VerifyAndSetError(err_response);
+    VerifyAndSetErrorMessage(err_response);
+    VLOG(1) << "Handling err=" << err_ << " err_msg=" << err_msg_;
   }
 
   ~DlcserviceErrorResponseHandler() = default;
 
-  std::string get_err() { return err; }
+  std::string get_err() { return err_; }
 
-  std::string get_err_msg() { return err_msg; }
+  std::string get_err_msg() { return err_msg_; }
 
  private:
-  bool DlcserviceErrorFromString(const std::string& err_msg, std::string* err) {
+  void VerifyAndSetError(dbus::ErrorResponse* err_response) {
+    const std::string& err = err_response->GetErrorName();
     static const base::NoDestructor<std::unordered_set<std::string>> err_set({
         dlcservice::kErrorNone,
         dlcservice::kErrorInternal,
@@ -70,40 +70,28 @@
         dlcservice::kErrorNeedReboot,
         dlcservice::kErrorInvalidDlc,
     });
-    static const std::pair<std::string, std::string> delims = {"dlcservice/",
-                                                               ":"};
-
-    if (!err) {
-      LOG(ERROR) << "err passed is nullptr.";
-      return false;
+    // Lookup the dlcservice error code and provide default on invalid.
+    auto itr = err_set->find(err);
+    if (itr == err_set->end()) {
+      LOG(ERROR) << "Failed to set error based on ErrorResponse "
+                    "defaulted to kErrorInternal, was:" << err;
+      err_ = dlcservice::kErrorInternal;
+      return;
     }
+    err_ = *itr;
+  }
 
-    // Clear to empty out |err|.
-    err->clear();
-
-    // Verify dlcservice error code.
-    if (err_msg.find(delims.first) == std::string::npos) {
-      LOG(ERROR) << "Dlcservice did not send valid error message: " << err_msg;
-      *err = dlcservice::kErrorInternal;
-      return true;
+  void VerifyAndSetErrorMessage(dbus::ErrorResponse* err_response) {
+    if (!dbus::MessageReader(err_response).PopString(&err_msg_)) {
+      LOG(ERROR) << "Failed to set error message from ErrorResponse.";
     }
-
-    // Extract the dlcservice error code.
-    size_t padding = delims.first.size();
-    size_t second_idx = err_msg.find(delims.second, padding);
-    *err = err_msg.substr(padding, second_idx - padding);
-
-    // Lookup the dlcservice error code and provide default on failure.
-    auto itr = err_set->find(*err);
-    *err = itr != err_set->end() ? *itr : dlcservice::kErrorInternal;
-    return true;
   }
 
   // Holds the dlcservice specific error.
-  std::string err;
+  std::string err_;
 
   // Holds the entire error message from error response.
-  std::string err_msg;
+  std::string err_msg_;
 
   DISALLOW_COPY_AND_ASSIGN(DlcserviceErrorResponseHandler);
 };
diff --git a/chromeos/dbus/fake_cicerone_client.cc b/chromeos/dbus/fake_cicerone_client.cc
index 456a415b..2ba8b76f 100644
--- a/chromeos/dbus/fake_cicerone_client.cc
+++ b/chromeos/dbus/fake_cicerone_client.cc
@@ -38,6 +38,12 @@
 
   import_lxd_container_response_.set_status(
       vm_tools::cicerone::ImportLxdContainerResponse::IMPORTING);
+
+  upgrade_container_response_.set_status(
+      vm_tools::cicerone::UpgradeContainerResponse::STARTED);
+
+  cancel_upgrade_container_response_.set_status(
+      vm_tools::cicerone::CancelUpgradeContainerResponse::CANCELLED);
 }
 
 FakeCiceroneClient::~FakeCiceroneClient() = default;
@@ -98,6 +104,10 @@
   return is_apply_ansible_playbook_progress_signal_connected_;
 }
 
+bool FakeCiceroneClient::IsUpgradeContainerProgressSignalConnected() {
+  return is_upgrade_container_progress_signal_connected_;
+}
+
 // Currently no tests need to change the output of this method. If you want to
 // add one, make it return a variable like the above examples.
 bool FakeCiceroneClient::IsPendingAppListUpdatesSignalConnected() {
@@ -291,6 +301,23 @@
       base::BindOnce(std::move(callback), apply_ansible_playbook_response_));
 }
 
+void FakeCiceroneClient::UpgradeContainer(
+    const vm_tools::cicerone::UpgradeContainerRequest& request,
+    DBusMethodCallback<vm_tools::cicerone::UpgradeContainerResponse> callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback), upgrade_container_response_));
+}
+
+void FakeCiceroneClient::CancelUpgradeContainer(
+    const vm_tools::cicerone::CancelUpgradeContainerRequest& request,
+    DBusMethodCallback<vm_tools::cicerone::CancelUpgradeContainerResponse>
+        callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback), cancel_upgrade_container_response_));
+}
+
 void FakeCiceroneClient::NotifyLxdContainerCreated(
     const vm_tools::cicerone::LxdContainerCreatedSignal& proto) {
   for (auto& observer : observer_list_) {
@@ -361,4 +388,11 @@
   }
 }
 
+void FakeCiceroneClient::NotifyUpgradeContainerProgress(
+    const vm_tools::cicerone::UpgradeContainerProgressSignal& signal) {
+  for (auto& observer : observer_list_) {
+    observer.OnUpgradeContainerProgress(signal);
+  }
+}
+
 }  // namespace chromeos
diff --git a/chromeos/dbus/fake_cicerone_client.h b/chromeos/dbus/fake_cicerone_client.h
index 47f6d0c7..2614ce0a 100644
--- a/chromeos/dbus/fake_cicerone_client.h
+++ b/chromeos/dbus/fake_cicerone_client.h
@@ -38,6 +38,7 @@
   bool IsImportLxdContainerProgressSignalConnected() override;
   bool IsPendingAppListUpdatesSignalConnected() override;
   bool IsApplyAnsiblePlaybookProgressSignalConnected() override;
+  bool IsUpgradeContainerProgressSignalConnected() override;
   void LaunchContainerApplication(
       const vm_tools::cicerone::LaunchContainerApplicationRequest& request,
       DBusMethodCallback<vm_tools::cicerone::LaunchContainerApplicationResponse>
@@ -105,6 +106,14 @@
       const vm_tools::cicerone::ApplyAnsiblePlaybookRequest& request,
       DBusMethodCallback<vm_tools::cicerone::ApplyAnsiblePlaybookResponse>
           callback) override;
+  void UpgradeContainer(
+      const vm_tools::cicerone::UpgradeContainerRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::UpgradeContainerResponse> callback)
+      override;
+  void CancelUpgradeContainer(
+      const vm_tools::cicerone::CancelUpgradeContainerRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::CancelUpgradeContainerResponse>
+          callback) override;
   void WaitForServiceToBeAvailable(
       dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback) override;
 
@@ -152,6 +161,9 @@
   void set_import_lxd_container_progress_signal_connected(bool connected) {
     is_import_lxd_container_progress_signal_connected_ = connected;
   }
+  void set_upgrade_container_progress_signal_connected(bool connected) {
+    is_upgrade_container_progress_signal_connected_ = connected;
+  }
   void set_launch_container_application_response(
       const vm_tools::cicerone::LaunchContainerApplicationResponse&
           launch_container_application_response) {
@@ -245,6 +257,16 @@
           apply_ansible_playbook_response) {
     apply_ansible_playbook_response_ = apply_ansible_playbook_response;
   }
+  void set_upgrade_container_response(
+      vm_tools::cicerone::UpgradeContainerResponse upgrade_container_response) {
+    upgrade_container_response_ = std::move(upgrade_container_response);
+  }
+  void set_cancel_upgrade_container_response(
+      vm_tools::cicerone::CancelUpgradeContainerResponse
+          cancel_upgrade_container_response) {
+    cancel_upgrade_container_response_ =
+        std::move(cancel_upgrade_container_response);
+  }
 
   // Additional functions to allow tests to trigger Signals.
   void NotifyLxdContainerCreated(
@@ -269,6 +291,8 @@
       const vm_tools::cicerone::PendingAppListUpdatesSignal& signal);
   void NotifyApplyAnsiblePlaybookProgress(
       const vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal& signal);
+  void NotifyUpgradeContainerProgress(
+      const vm_tools::cicerone::UpgradeContainerProgressSignal& signal);
 
  protected:
   void Init(dbus::Bus* bus) override {}
@@ -286,6 +310,7 @@
   bool is_export_lxd_container_progress_signal_connected_ = true;
   bool is_import_lxd_container_progress_signal_connected_ = true;
   bool is_apply_ansible_playbook_progress_signal_connected_ = true;
+  bool is_upgrade_container_progress_signal_connected_ = true;
 
   std::string last_container_username_;
   bool send_container_started_signal_ = true;
@@ -327,6 +352,9 @@
       cancel_import_lxd_container_response_;
   vm_tools::cicerone::ApplyAnsiblePlaybookResponse
       apply_ansible_playbook_response_;
+  vm_tools::cicerone::UpgradeContainerResponse upgrade_container_response_;
+  vm_tools::cicerone::CancelUpgradeContainerResponse
+      cancel_upgrade_container_response_;
 
   vm_tools::cicerone::OsRelease lxd_container_os_release_;
 
diff --git a/chromeos/profiles/airmont.afdo.newest.txt b/chromeos/profiles/airmont.afdo.newest.txt
index 666b64b..f38b480 100644
--- a/chromeos/profiles/airmont.afdo.newest.txt
+++ b/chromeos/profiles/airmont.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-airmont-79-3904.19-1569838411-benchmark-79.0.3923.0-r1-redacted.afdo.xz
\ No newline at end of file
+chromeos-chrome-amd64-airmont-79-3931.2-1571054549-benchmark-79.0.3940.0-r1-redacted.afdo.xz
\ No newline at end of file
diff --git a/chromeos/profiles/broadwell.afdo.newest.txt b/chromeos/profiles/broadwell.afdo.newest.txt
index d40f708..e955673 100644
--- a/chromeos/profiles/broadwell.afdo.newest.txt
+++ b/chromeos/profiles/broadwell.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-broadwell-79-3903.0-1569840015-benchmark-79.0.3923.0-r1-redacted.afdo.xz
\ No newline at end of file
+chromeos-chrome-amd64-broadwell-79-3904.41-1571046112-benchmark-79.0.3940.0-r1-redacted.afdo.xz
\ No newline at end of file
diff --git a/chromeos/profiles/silvermont.afdo.newest.txt b/chromeos/profiles/silvermont.afdo.newest.txt
index 62b0132..0420563f 100644
--- a/chromeos/profiles/silvermont.afdo.newest.txt
+++ b/chromeos/profiles/silvermont.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-silvermont-79-3904.19-1569836348-benchmark-79.0.3923.0-r1-redacted.afdo.xz
\ No newline at end of file
+chromeos-chrome-amd64-silvermont-79-3928.0-1571049528-benchmark-79.0.3940.0-r1-redacted.afdo.xz
\ No newline at end of file
diff --git a/chromeos/services/device_sync/fake_device_sync.cc b/chromeos/services/device_sync/fake_device_sync.cc
index b79f44be..e028c44 100644
--- a/chromeos/services/device_sync/fake_device_sync.cc
+++ b/chromeos/services/device_sync/fake_device_sync.cc
@@ -49,7 +49,7 @@
 
 void FakeDeviceSync::InvokePendingGetDevicesActivityStatusCallback(
     mojom::NetworkRequestResult result_code,
-    std::vector<mojom::DeviceActivityStatusPtr>
+    base::Optional<std::vector<mojom::DeviceActivityStatusPtr>>
         get_devices_activity_status_response) {
   std::move(get_devices_activity_status_callback_queue_.front())
       .Run(result_code, std::move(get_devices_activity_status_response));
diff --git a/chromeos/services/device_sync/fake_device_sync.h b/chromeos/services/device_sync/fake_device_sync.h
index 436705f..f7ab85f 100644
--- a/chromeos/services/device_sync/fake_device_sync.h
+++ b/chromeos/services/device_sync/fake_device_sync.h
@@ -46,7 +46,7 @@
       mojom::FindEligibleDevicesResponsePtr find_eligible_devices_response_ptr);
   void InvokePendingGetDevicesActivityStatusCallback(
       mojom::NetworkRequestResult result_code,
-      std::vector<mojom::DeviceActivityStatusPtr>
+      base::Optional<std::vector<mojom::DeviceActivityStatusPtr>>
           get_devices_activity_status_response);
   void InvokePendingGetDebugInfoCallback(mojom::DebugInfoPtr debug_info_ptr);
 
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client.h b/chromeos/services/device_sync/public/cpp/device_sync_client.h
index 91b22e6..0203c94 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client.h
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client.h
@@ -87,6 +87,8 @@
   virtual void FindEligibleDevices(
       multidevice::SoftwareFeature software_feature,
       FindEligibleDevicesCallback callback) = 0;
+  virtual void GetDevicesActivityStatus(
+      mojom::DeviceSync::GetDevicesActivityStatusCallback callback) = 0;
   virtual void GetDebugInfo(
       mojom::DeviceSync::GetDebugInfoCallback callback) = 0;
 
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
index 21105aa1..bfd9f77 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
@@ -126,6 +126,11 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+void DeviceSyncClientImpl::GetDevicesActivityStatus(
+    mojom::DeviceSync::GetDevicesActivityStatusCallback callback) {
+  device_sync_ptr_->GetDevicesActivityStatus(std::move(callback));
+}
+
 void DeviceSyncClientImpl::GetDebugInfo(
     mojom::DeviceSync::GetDebugInfoCallback callback) {
   device_sync_ptr_->GetDebugInfo(std::move(callback));
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
index d3b4b81..5380278 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
@@ -68,6 +68,8 @@
       mojom::DeviceSync::SetSoftwareFeatureStateCallback callback) override;
   void FindEligibleDevices(multidevice::SoftwareFeature software_feature,
                            FindEligibleDevicesCallback callback) override;
+  void GetDevicesActivityStatus(
+      mojom::DeviceSync::GetDevicesActivityStatusCallback callback) override;
   void GetDebugInfo(mojom::DeviceSync::GetDebugInfoCallback callback) override;
 
   // mojom::DeviceSyncObserver:
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc b/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
index b99977b..44ed49f 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
@@ -373,6 +373,42 @@
         expected_ineligible_devices);
   }
 
+  void CallGetDevicesActivityStatus(
+      mojom::NetworkRequestResult expected_result_code,
+      base::Optional<std::vector<mojom::DeviceActivityStatusPtr>>
+          expected_activity_statuses) {
+    base::RunLoop run_loop;
+
+    client_->GetDevicesActivityStatus(
+        base::BindOnce(&DeviceSyncClientImplTest::OnGetDevicesActivityStatus,
+                       base::Unretained(this), run_loop.QuitClosure()));
+
+    SendPendingMojoMessages();
+
+    base::Optional<std::vector<mojom::DeviceActivityStatusPtr>>
+        device_activity_statuses_optional;
+    if (expected_activity_statuses != base::nullopt) {
+      std::vector<mojom::DeviceActivityStatusPtr> device_activity_statuses;
+      for (const mojom::DeviceActivityStatusPtr& device_activity_status :
+           *expected_activity_statuses) {
+        device_activity_statuses.emplace_back(mojom::DeviceActivityStatus::New(
+            device_activity_status->device_id,
+            device_activity_status->last_activity_time,
+            device_activity_status->connectivity_status));
+      }
+      device_activity_statuses_optional =
+          base::make_optional(std::move(device_activity_statuses));
+    }
+    fake_device_sync_->InvokePendingGetDevicesActivityStatusCallback(
+        expected_result_code, std::move(device_activity_statuses_optional));
+    run_loop.Run();
+
+    EXPECT_EQ(expected_result_code,
+              std::get<0>(get_devices_activity_status_code_and_response_));
+    EXPECT_EQ(expected_activity_statuses,
+              std::get<1>(get_devices_activity_status_code_and_response_));
+  }
+
   void CallGetDebugInfo() {
     EXPECT_FALSE(debug_info_received_);
 
@@ -444,6 +480,9 @@
              multidevice::RemoteDeviceRefList,
              multidevice::RemoteDeviceRefList>
       find_eligible_devices_error_code_and_response_;
+  std::tuple<mojom::NetworkRequestResult,
+             base::Optional<std::vector<mojom::DeviceActivityStatusPtr>>>
+      get_devices_activity_status_code_and_response_;
   bool debug_info_received_ = false;
 
  private:
@@ -475,6 +514,16 @@
     std::move(callback).Run();
   }
 
+  void OnGetDevicesActivityStatus(
+      base::OnceClosure callback,
+      mojom::NetworkRequestResult result_code,
+      base::Optional<std::vector<mojom::DeviceActivityStatusPtr>>
+          device_activity_status) {
+    get_devices_activity_status_code_and_response_ =
+        std::make_tuple(result_code, std::move(device_activity_status));
+    std::move(callback).Run();
+  }
+
   void OnGetDebugInfoCompleted(base::OnceClosure callback,
                                mojom::DebugInfoPtr debug_info_ptr) {
     debug_info_received_ = true;
@@ -681,6 +730,23 @@
                           multidevice::RemoteDeviceList());
 }
 
+TEST_F(DeviceSyncClientImplTest, TestGetDevicesActivityStatus_NoErrorCode) {
+  SetupClient();
+  std::vector<mojom::DeviceActivityStatusPtr> expected_activity_statuses;
+  expected_activity_statuses.emplace_back(mojom::DeviceActivityStatus::New(
+      "deviceid", base::Time(), cryptauthv2::ConnectivityStatus::ONLINE));
+
+  CallGetDevicesActivityStatus(mojom::NetworkRequestResult::kSuccess,
+                               std::move(expected_activity_statuses));
+}
+
+TEST_F(DeviceSyncClientImplTest, TestGetDevicesActivityStatus_ErrorCode) {
+  SetupClient();
+
+  CallGetDevicesActivityStatus(mojom::NetworkRequestResult::kEndpointNotFound,
+                               base::nullopt);
+}
+
 TEST_F(DeviceSyncClientImplTest, TestGetDebugInfo) {
   SetupClient();
 
diff --git a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.cc b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.cc
index f756216..f039adf 100644
--- a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.cc
+++ b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.cc
@@ -49,6 +49,11 @@
   find_eligible_devices_callback_queue_.push(std::move(callback));
 }
 
+void FakeDeviceSyncClient::GetDevicesActivityStatus(
+    mojom::DeviceSync::GetDevicesActivityStatusCallback callback) {
+  get_devices_activity_status_callback_queue_.push(std::move(callback));
+}
+
 void FakeDeviceSyncClient::GetDebugInfo(
     mojom::DeviceSync::GetDebugInfoCallback callback) {
   get_debug_info_callback_queue_.push(std::move(callback));
@@ -105,6 +110,16 @@
   find_eligible_devices_callback_queue_.pop();
 }
 
+void FakeDeviceSyncClient::InvokePendingGetDevicesActivityStatusCallback(
+    mojom::NetworkRequestResult result_code,
+    base::Optional<std::vector<mojom::DeviceActivityStatusPtr>>
+        device_activity_status) {
+  DCHECK(get_devices_activity_status_callback_queue_.size() > 0);
+  std::move(get_devices_activity_status_callback_queue_.front())
+      .Run(result_code, std::move(device_activity_status));
+  get_devices_activity_status_callback_queue_.pop();
+}
+
 void FakeDeviceSyncClient::InvokePendingGetDebugInfoCallback(
     mojom::DebugInfoPtr debug_info_ptr) {
   DCHECK(get_debug_info_callback_queue_.size() > 0);
diff --git a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
index 7ed7cd5..323c5b3 100644
--- a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
+++ b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
@@ -42,6 +42,10 @@
       mojom::NetworkRequestResult result_code,
       multidevice::RemoteDeviceRefList eligible_devices,
       multidevice::RemoteDeviceRefList ineligible_devices);
+  void InvokePendingGetDevicesActivityStatusCallback(
+      mojom::NetworkRequestResult result_code,
+      base::Optional<std::vector<mojom::DeviceActivityStatusPtr>>
+          device_activity_status);
   void InvokePendingGetDebugInfoCallback(mojom::DebugInfoPtr debug_info_ptr);
 
   void set_synced_devices(multidevice::RemoteDeviceRefList synced_devices) {
@@ -73,6 +77,8 @@
       mojom::DeviceSync::SetSoftwareFeatureStateCallback callback) override;
   void FindEligibleDevices(multidevice::SoftwareFeature software_feature,
                            FindEligibleDevicesCallback callback) override;
+  void GetDevicesActivityStatus(
+      mojom::DeviceSync::GetDevicesActivityStatusCallback callback) override;
   void GetDebugInfo(mojom::DeviceSync::GetDebugInfoCallback callback) override;
 
   multidevice::RemoteDeviceRefList synced_devices_;
@@ -85,6 +91,8 @@
   std::queue<mojom::DeviceSync::SetSoftwareFeatureStateCallback>
       set_software_feature_state_callback_queue_;
   std::queue<FindEligibleDevicesCallback> find_eligible_devices_callback_queue_;
+  std::queue<mojom::DeviceSync::GetDevicesActivityStatusCallback>
+      get_devices_activity_status_callback_queue_;
   std::queue<mojom::DeviceSync::GetDebugInfoCallback>
       get_debug_info_callback_queue_;
 
diff --git a/chromeos/services/ime/ime_service_unittest.cc b/chromeos/services/ime/ime_service_unittest.cc
index ca9077b3..572789ed 100644
--- a/chromeos/services/ime/ime_service_unittest.cc
+++ b/chromeos/services/ime/ime_service_unittest.cc
@@ -303,5 +303,60 @@
   EXPECT_EQ(response.operations, expected_operations);
 }
 
+// Tests escapable characters. See https://crbug.com/1014384.
+TEST_F(ImeServiceTest, RuleBasedDoesNotEscapeCharacters) {
+  bool success = false;
+  TestClientChannel test_channel;
+  mojo::Remote<mojom::InputChannel> to_engine_remote;
+
+  remote_manager_->ConnectToImeEngine(
+      "m17n:deva_phone", to_engine_remote.BindNewPipeAndPassReceiver(),
+      test_channel.CreatePendingRemote(), extra,
+      base::BindOnce(&ConnectCallback, &success));
+  remote_manager_.FlushForTesting();
+  EXPECT_TRUE(success);
+
+  mojom::KeypressResponseForRulebased response;
+
+  // Test Shift+Quote ('"').
+  to_engine_remote->ProcessKeypressForRulebased(
+      mojom::KeypressInfoForRulebased::New("keydown", "Quote", true, false,
+                                           false, false, false),
+      base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
+  to_engine_remote.FlushForTesting();
+
+  EXPECT_EQ(response.result, true);
+  ASSERT_EQ(1U, response.operations.size());
+  EXPECT_EQ(mojom::OperationMethodForRulebased::COMMIT_TEXT,
+            response.operations[0]->method);
+  EXPECT_EQ("\"", response.operations[0]->arguments);
+
+  // Backslash.
+  to_engine_remote->ProcessKeypressForRulebased(
+      mojom::KeypressInfoForRulebased::New("keydown", "Backslash", false, false,
+                                           false, false, false),
+      base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
+  to_engine_remote.FlushForTesting();
+
+  EXPECT_EQ(response.result, true);
+  ASSERT_EQ(1U, response.operations.size());
+  EXPECT_EQ(mojom::OperationMethodForRulebased::COMMIT_TEXT,
+            response.operations[0]->method);
+  EXPECT_EQ("\\", response.operations[0]->arguments);
+
+  // Shift+Comma ('<')
+  to_engine_remote->ProcessKeypressForRulebased(
+      mojom::KeypressInfoForRulebased::New("keydown", "Comma", true, false,
+                                           false, false, false),
+      base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
+  to_engine_remote.FlushForTesting();
+
+  EXPECT_EQ(response.result, true);
+  ASSERT_EQ(1U, response.operations.size());
+  EXPECT_EQ(mojom::OperationMethodForRulebased::COMMIT_TEXT,
+            response.operations[0]->method);
+  EXPECT_EQ("<", response.operations[0]->arguments);
+}
+
 }  // namespace ime
 }  // namespace chromeos
diff --git a/chromeos/services/ime/input_engine.cc b/chromeos/services/ime/input_engine.cc
index 6577bb3..1b1e46ac 100644
--- a/chromeos/services/ime/input_engine.cc
+++ b/chromeos/services/ime/input_engine.cc
@@ -4,8 +4,6 @@
 
 #include "chromeos/services/ime/input_engine.h"
 
-#include "base/json/json_reader.h"
-#include "base/json/string_escape.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -41,10 +39,9 @@
       mojom::KeypressResponseForRulebased::New();
   keypress_response->result = process_key_result.key_handled;
   if (!process_key_result.commit_text.empty()) {
-    std::string commit_text;
-    base::EscapeJSONString(process_key_result.commit_text, false, &commit_text);
     keypress_response->operations.push_back(mojom::OperationForRulebased::New(
-        mojom::OperationMethodForRulebased::COMMIT_TEXT, commit_text));
+        mojom::OperationMethodForRulebased::COMMIT_TEXT,
+        process_key_result.commit_text));
   }
   // Need to add the setComposition operation to the result when the key is
   // handled and commit_text and composition_text are both empty.
@@ -53,11 +50,9 @@
   if (!process_key_result.composition_text.empty() ||
       (process_key_result.key_handled &&
        process_key_result.commit_text.empty())) {
-    std::string composition_text;
-    base::EscapeJSONString(process_key_result.composition_text, false,
-                           &composition_text);
     keypress_response->operations.push_back(mojom::OperationForRulebased::New(
-        mojom::OperationMethodForRulebased::SET_COMPOSITION, composition_text));
+        mojom::OperationMethodForRulebased::SET_COMPOSITION,
+        process_key_result.composition_text));
   }
   return keypress_response;
 }
diff --git a/chromeos/services/ime/public/cpp/BUILD.gn b/chromeos/services/ime/public/cpp/BUILD.gn
index 7205512..8f1a3a4 100644
--- a/chromeos/services/ime/public/cpp/BUILD.gn
+++ b/chromeos/services/ime/public/cpp/BUILD.gn
@@ -124,6 +124,7 @@
     deps = [
       ":rulebased",
       ":rulebased_fuzzer_proto",
+      "//base",
       "//third_party/libprotobuf-mutator",
     ]
   }
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index 9e3dfbe..eca2ce0 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -740,6 +740,9 @@
       GetStringList(dict, ::onc::ipconfig::kSearchDomains);
   ip_config->routing_prefix = GetInt32(dict, ::onc::ipconfig::kRoutingPrefix);
   ip_config->type = GetString(dict, ::onc::ipconfig::kType);
+  // Shill may omit the IP Config type for VPNs. The type should be IPv4.
+  if (!ip_config->type || ip_config->type->empty())
+    ip_config->type = ::onc::ipconfig::kIPv4;
   ip_config->web_proxy_auto_discovery_url =
       GetString(dict, ::onc::ipconfig::kWebProxyAutoDiscoveryUrl);
   return ip_config;
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 9ba9f3b..d77f858 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -243,6 +243,7 @@
       "//components/page_load_metrics/renderer:unit_tests",
       "//components/paint_preview/browser:unit_tests",
       "//components/paint_preview/common:unit_tests",
+      "//components/paint_preview/renderer:unit_tests",
       "//components/password_manager/content/browser:unit_tests",
       "//components/payments/content:unit_tests",
       "//components/payments/content/utility:unit_tests",
@@ -510,6 +511,7 @@
       "dom_distiller/content/browser/distiller_page_web_contents_browsertest.cc",
       "dom_distiller/content/browser/test/dom_distiller_js_browsertest.cc",
       "offline_pages/content/renovations/test/page_renovator_browsertest.cc",
+      "paint_preview/renderer/paint_preview_recorder_browsertest.cc",
       "security_state/content/content_utils_browsertest.cc",
       "ukm/content/source_url_recorder_browsertest.cc",
     ]
@@ -536,6 +538,7 @@
       "//components/dom_distiller/core:test_support",
       "//components/offline_pages/content/renovations",
       "//components/offline_pages/core/renovations",
+      "//components/paint_preview/renderer",
       "//components/password_manager/content/browser",
       "//components/security_state/content",
       "//components/security_state/core",
diff --git a/components/arc/session/arc_vm_client_adapter.cc b/components/arc/session/arc_vm_client_adapter.cc
index 8f67698..db803e3b 100644
--- a/components/arc/session/arc_vm_client_adapter.cc
+++ b/components/arc/session/arc_vm_client_adapter.cc
@@ -16,6 +16,7 @@
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -30,6 +31,7 @@
 #include "base/threading/scoped_blocking_call.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
+#include "base/values.h"
 #include "chromeos/dbus/concierge_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
@@ -42,6 +44,7 @@
 namespace arc {
 namespace {
 
+constexpr const char kArcVmConfigJsonPath[] = "/usr/share/arcvm/config.json";
 constexpr const char kArcVmServerProxyJobName[] = "arcvm_2dserver_2dproxy";
 constexpr const char kBuiltinPath[] = "/opt/google/vms/android";
 constexpr const char kCrosSystemPath[] = "/usr/bin/crossystem";
@@ -59,6 +62,7 @@
   ~FileSystemStatus() = default;
   FileSystemStatus& operator=(FileSystemStatus&& rhs) = default;
 
+  bool is_android_debuggable() const { return is_android_debuggable_; }
   bool is_host_rootfs_writable() const { return is_host_rootfs_writable_; }
   const base::FilePath& system_image_path() const { return system_image_path_; }
   const base::FilePath& vendor_image_path() const { return vendor_image_path_; }
@@ -68,15 +72,57 @@
   static FileSystemStatus GetFileSystemStatusBlocking() {
     return FileSystemStatus();
   }
+  static bool IsAndroidDebuggableForTesting(const base::FilePath& json_path) {
+    return IsAndroidDebuggable(json_path);
+  }
 
  private:
   FileSystemStatus()
-      : is_host_rootfs_writable_(IsHostRootfsWritable()),
+      : is_android_debuggable_(
+            IsAndroidDebuggable(base::FilePath(kArcVmConfigJsonPath))),
+        is_host_rootfs_writable_(IsHostRootfsWritable()),
         system_image_path_(SelectDlcOrBuiltin(base::FilePath(kRootFs))),
         vendor_image_path_(SelectDlcOrBuiltin(base::FilePath(kVendorImage))),
         guest_kernel_path_(SelectDlcOrBuiltin(base::FilePath(kKernel))),
         fstab_path_(SelectDlcOrBuiltin(base::FilePath(kFstab))) {}
 
+  // Parse a JSON file which is like the following and returns a result:
+  //   {
+  //     "ANDROID_DEBUGGABLE": false
+  //   }
+  static bool IsAndroidDebuggable(const base::FilePath& json_path) {
+    // TODO(yusukes): Remove this fallback after adding the json file.
+    if (!base::PathExists(json_path))
+      return true;
+
+    std::string content;
+    if (!base::ReadFileToString(json_path, &content))
+      return false;
+
+    base::JSONReader::ValueWithError result(
+        base::JSONReader::ReadAndReturnValueWithError(content,
+                                                      base::JSON_PARSE_RFC));
+    if (!result.value) {
+      LOG(ERROR) << "Error parsing " << json_path
+                 << ": code=" << result.error_code
+                 << ", message=" << result.error_message << ": " << content;
+      return false;
+    }
+    if (!result.value->is_dict()) {
+      LOG(ERROR) << "Error parsing " << json_path << ": " << *(result.value);
+      return false;
+    }
+
+    const base::Value* debuggable = result.value->FindKeyOfType(
+        "ANDROID_DEBUGGABLE", base::Value::Type::BOOLEAN);
+    if (!debuggable) {
+      LOG(ERROR) << "ANDROID_DEBUGGABLE is not found in " << json_path;
+      return false;
+    }
+
+    return debuggable->GetBool();
+  }
+
   static bool IsHostRootfsWritable() {
     base::ScopedBlockingCall scoped_blocking_call(
         FROM_HERE, base::BlockingType::MAY_BLOCK);
@@ -99,6 +145,7 @@
     return base::FilePath(kBuiltinPath).Append(file);
   }
 
+  bool is_android_debuggable_;
   bool is_host_rootfs_writable_;
   base::FilePath system_image_path_;
   base::FilePath vendor_image_path_;
@@ -170,6 +217,7 @@
 std::vector<std::string> GenerateKernelCmdline(
     int32_t lcd_density,
     const base::Optional<bool>& play_store_auto_update,
+    const FileSystemStatus& file_system_status,
     bool is_dev_mode,
     bool is_host_on_vm) {
   const std::string release_channel = GetReleaseChannel();
@@ -185,8 +233,8 @@
       base::StringPrintf("androidboot.dev_mode=%d", is_dev_mode),
       base::StringPrintf("androidboot.disable_runas=%d", !is_dev_mode),
       base::StringPrintf("androidboot.vm=%d", is_host_on_vm),
-      // TODO(yusukes): get this from arc-setup config or equivalent.
-      "androidboot.debuggable=1",
+      base::StringPrintf("androidboot.debuggable=%d",
+                         file_system_status.is_android_debuggable()),
       base::StringPrintf("androidboot.lcd_density=%d", lcd_density),
       base::StringPrintf(
           "androidboot.arc_file_picker=%d",
@@ -458,7 +506,8 @@
     VLOG(2) << "Got file system status";
     DCHECK(is_dev_mode_);
     std::vector<std::string> kernel_cmdline = GenerateKernelCmdline(
-        lcd_density_, play_store_auto_update_, *is_dev_mode_, is_host_on_vm_);
+        lcd_density_, play_store_auto_update_, file_system_status,
+        *is_dev_mode_, is_host_on_vm_);
     auto start_request =
         CreateStartArcVmRequest(user_id_hash_, cpus_, data_disk_path,
                                 file_system_status, std::move(kernel_cmdline));
@@ -563,4 +612,8 @@
   return std::make_unique<ArcVmClientAdapter>();
 }
 
+bool IsAndroidDebuggableForTesting(const base::FilePath& json_path) {
+  return FileSystemStatus::IsAndroidDebuggableForTesting(json_path);
+}
+
 }  // namespace arc
diff --git a/components/arc/session/arc_vm_client_adapter.h b/components/arc/session/arc_vm_client_adapter.h
index 4c79773..6e521bf6 100644
--- a/components/arc/session/arc_vm_client_adapter.h
+++ b/components/arc/session/arc_vm_client_adapter.h
@@ -9,11 +9,18 @@
 
 #include "components/arc/session/arc_client_adapter.h"
 
+namespace base {
+class FilePath;
+}  // namespace base
+
 namespace arc {
 
 // Returns an adapter for arcvm.
 std::unique_ptr<ArcClientAdapter> CreateArcVmClientAdapter();
 
+// Function(s) below are for testing.
+bool IsAndroidDebuggableForTesting(const base::FilePath& json_path);
+
 }  // namespace arc
 
 #endif  // COMPONENTS_ARC_SESSION_ARC_VM_CLIENT_ADAPTER_H_
diff --git a/components/arc/session/arc_vm_client_adapter_unittest.cc b/components/arc/session/arc_vm_client_adapter_unittest.cc
index 1b4a38c..fbe33af 100644
--- a/components/arc/session/arc_vm_client_adapter_unittest.cc
+++ b/components/arc/session/arc_vm_client_adapter_unittest.cc
@@ -8,6 +8,8 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
@@ -98,6 +100,7 @@
     adapter_ = CreateArcVmClientAdapter();
     arc_instance_stopped_called_ = false;
     adapter_->AddObserver(this);
+    ASSERT_TRUE(dir_.CreateUniqueTempDir());
 
     // The fake client returns VM_STATUS_STARTING by default. Change it
     // to VM_STATUS_RUNNING which is used by ARCVM.
@@ -177,6 +180,8 @@
 
   base::RunLoop* run_loop() { return run_loop_.get(); }
   ArcClientAdapter* adapter() { return adapter_.get(); }
+  const base::FilePath& GetTempDir() const { return dir_.GetPath(); }
+
   bool arc_instance_stopped_called() const {
     return arc_instance_stopped_called_;
   }
@@ -199,6 +204,7 @@
   bool arc_instance_stopped_called_;
 
   content::BrowserTaskEnvironment browser_task_environment_;
+  base::ScopedTempDir dir_;
 
   DISALLOW_COPY_AND_ASSIGN(ArcVmClientAdapterTest);
 };
@@ -438,5 +444,50 @@
   EXPECT_FALSE(arc_instance_stopped_called());
 }
 
+// Tests if androidboot.debuggable is set properly.
+TEST_F(ArcVmClientAdapterTest, IsAndroidDebuggable) {
+  constexpr const char kAndroidDebuggableTrueJson[] = R"json({
+    "ANDROID_DEBUGGABLE": true
+  })json";
+  constexpr const char kAndroidDebuggableFalseJson[] = R"json({
+    "ANDROID_DEBUGGABLE": false
+  })json";
+  constexpr const char kInvalidTypeJson[] = R"json([
+    42
+  ])json";
+  constexpr const char kInvalidJson[] = R"json({
+    "ANDROID_DEBUGGABLE": true,
+  })json";
+  constexpr const char kKeyNotFoundJson[] = R"json({
+    "BADKEY": "a"
+  })json";
+  constexpr const char kNonBooleanValue[] = R"json({
+    "ANDROID_DEBUGGABLE": "a"
+  })json";
+  constexpr const char kBadKeyType[] = R"json({
+    42: true
+  })json";
+
+  auto test = [](const base::FilePath& dir, const std::string& str) {
+    base::FilePath path;
+    if (!CreateTemporaryFileInDir(dir, &path))
+      return false;
+    base::WriteFile(path, str.data(), str.size());
+    return IsAndroidDebuggableForTesting(path);
+  };
+
+  EXPECT_TRUE(test(GetTempDir(), kAndroidDebuggableTrueJson));
+  EXPECT_FALSE(test(GetTempDir(), kAndroidDebuggableFalseJson));
+  EXPECT_FALSE(test(GetTempDir(), kInvalidTypeJson));
+  EXPECT_FALSE(test(GetTempDir(), kInvalidJson));
+  EXPECT_FALSE(test(GetTempDir(), kKeyNotFoundJson));
+  EXPECT_FALSE(test(GetTempDir(), kNonBooleanValue));
+  EXPECT_FALSE(test(GetTempDir(), kBadKeyType));
+
+  // TODO(yusukes): Change this to _FALSE later.
+  EXPECT_TRUE(
+      IsAndroidDebuggableForTesting(base::FilePath("/nonexistent-path")));
+}
+
 }  // namespace
 }  // namespace arc
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 68239ae..9d2e0b7 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -176,10 +176,10 @@
 }
 
 void ContentAutofillDriver::RendererShouldSetSuggestionAvailability(
-    bool available) {
+    const mojom::AutofillState state) {
   if (!RendererIsAvailable())
     return;
-  GetAutofillAgent()->SetSuggestionAvailability(available);
+  GetAutofillAgent()->SetSuggestionAvailability(state);
 }
 
 void ContentAutofillDriver::PopupHidden() {
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index 91df17d..abe86a9 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -78,7 +78,8 @@
   void RendererShouldFillFieldWithValue(const base::string16& value) override;
   void RendererShouldPreviewFieldWithValue(
       const base::string16& value) override;
-  void RendererShouldSetSuggestionAvailability(bool available) override;
+  void RendererShouldSetSuggestionAvailability(
+      const mojom::AutofillState state) override;
   void PopupHidden() override;
   gfx::RectF TransformBoundingBoxToViewportCoordinates(
       const gfx::RectF& bounding_box) override;
diff --git a/components/autofill/content/browser/content_autofill_driver_unittest.cc b/components/autofill/content/browser/content_autofill_driver_unittest.cc
index 24b76fe..9815b85 100644
--- a/components/autofill/content/browser/content_autofill_driver_unittest.cc
+++ b/components/autofill/content/browser/content_autofill_driver_unittest.cc
@@ -188,8 +188,11 @@
     CallDone();
   }
 
-  void SetSuggestionAvailability(bool value) override {
-    suggestions_available_ = value;
+  void SetSuggestionAvailability(const mojom::AutofillState state) override {
+    if (state == mojom::AutofillState::kAutofillAvailable)
+      suggestions_available_ = true;
+    else if (state == mojom::AutofillState::kNoSuggestions)
+      suggestions_available_ = false;
     CallDone();
   }
 
diff --git a/components/autofill/content/common/mojom/autofill_agent.mojom b/components/autofill/content/common/mojom/autofill_agent.mojom
index 6bfd655..e84430a 100644
--- a/components/autofill/content/common/mojom/autofill_agent.mojom
+++ b/components/autofill/content/common/mojom/autofill_agent.mojom
@@ -36,8 +36,8 @@
   PreviewFieldWithValue(mojo_base.mojom.String16 value);
 
   // Sets the currently selected node's corresponding accessibility node's
-  // suggestion availability.
-  SetSuggestionAvailability(bool available);
+  // autofill/autocomplete suggestion availability.
+  SetSuggestionAvailability(AutofillState type);
 
   // Sets the currently selected node's value to be the given data list value.
   AcceptDataListSuggestion(mojo_base.mojom.String16 value);
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index 35d207a..e372068 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -49,6 +49,7 @@
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/platform/web_keyboard_event.h"
 #include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/web/web_ax_enums.h"
 #include "third_party/blink/public/web/web_ax_object.h"
 #include "third_party/blink/public/web/web_console_message.h"
 #include "third_party/blink/public/web/web_document.h"
@@ -527,14 +528,32 @@
     DoPreviewFieldWithValue(value, input_element);
 }
 
-void AutofillAgent::SetSuggestionAvailability(bool available) {
+void AutofillAgent::SetSuggestionAvailability(
+    const mojom::AutofillState state) {
   if (element_.IsNull())
     return;
 
   WebInputElement* input_element = ToWebInputElement(&element_);
-  if (input_element)
-    WebAXObject::FromWebNode(*input_element)
-        .HandleAutofillStateChanged(available);
+  if (input_element) {
+    switch (state) {
+      case autofill::mojom::AutofillState::kAutofillAvailable:
+        WebAXObject::FromWebNode(*input_element)
+            .HandleAutofillStateChanged(
+                blink::WebAXAutofillState::kAutofillAvailable);
+        return;
+      case autofill::mojom::AutofillState::kAutocompleteAvailable:
+        WebAXObject::FromWebNode(*input_element)
+            .HandleAutofillStateChanged(
+                blink::WebAXAutofillState::kAutocompleteAvailable);
+        return;
+      case autofill::mojom::AutofillState::kNoSuggestions:
+        WebAXObject::FromWebNode(*input_element)
+            .HandleAutofillStateChanged(
+                blink::WebAXAutofillState::kNoSuggestions);
+        return;
+    }
+    NOTREACHED();
+  }
 }
 
 void AutofillAgent::AcceptDataListSuggestion(const base::string16& value) {
diff --git a/components/autofill/content/renderer/autofill_agent.h b/components/autofill/content/renderer/autofill_agent.h
index cdf54f0..0ea12f07 100644
--- a/components/autofill/content/renderer/autofill_agent.h
+++ b/components/autofill/content/renderer/autofill_agent.h
@@ -81,7 +81,7 @@
   void ClearPreviewedForm() override;
   void FillFieldWithValue(const base::string16& value) override;
   void PreviewFieldWithValue(const base::string16& value) override;
-  void SetSuggestionAvailability(bool available) override;
+  void SetSuggestionAvailability(const mojom::AutofillState state) override;
   void AcceptDataListSuggestion(const base::string16& value) override;
   void FillPasswordSuggestion(const base::string16& username,
                               const base::string16& password) override;
diff --git a/components/autofill/core/browser/autofill_driver.h b/components/autofill/core/browser/autofill_driver.h
index b9e20570..e8998205 100644
--- a/components/autofill/core/browser/autofill_driver.h
+++ b/components/autofill/core/browser/autofill_driver.h
@@ -107,9 +107,9 @@
       const base::string16& value) = 0;
 
   // Tells the renderer to set the currently focused node's corresponding
-  // accessibility node to |autofill_suggestions_available|.
+  // accessibility node's autofill state to |state|.
   virtual void RendererShouldSetSuggestionAvailability(
-      bool autofill_suggestions_available) = 0;
+      const mojom::AutofillState state) = 0;
 
   // Informs the renderer that the popup has been hidden.
   virtual void PopupHidden() = 0;
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index 52e162d9..a5507ab 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -153,10 +153,10 @@
 }
 
 void AutofillExternalDelegate::OnAutofillAvailabilityEvent(
-    bool has_suggestions) {
+    const mojom::AutofillState state) {
   // Availability of suggestions should be communicated to Blink because
   // accessibility objects live in both the renderer and browser processes.
-  driver_->RendererShouldSetSuggestionAvailability(has_suggestions);
+  driver_->RendererShouldSetSuggestionAvailability(state);
 }
 
 void AutofillExternalDelegate::SetCurrentDataListValues(
diff --git a/components/autofill/core/browser/autofill_external_delegate.h b/components/autofill/core/browser/autofill_external_delegate.h
index 2e2b813..7cde61e 100644
--- a/components/autofill/core/browser/autofill_external_delegate.h
+++ b/components/autofill/core/browser/autofill_external_delegate.h
@@ -86,9 +86,9 @@
   // Returns true if there is a screen reader installed on the machine.
   virtual bool HasActiveScreenReader() const;
 
-  // Indicates on focus changed if autofill is available or unavailable, so
-  // state can be announced by screen readers.
-  virtual void OnAutofillAvailabilityEvent(bool has_suggestions);
+  // Indicates on focus changed if autofill/autocomplete is available or
+  // unavailable, so state can be announced by screen readers.
+  virtual void OnAutofillAvailabilityEvent(const mojom::AutofillState state);
 
   // Set the data list value associated with the current field.
   void SetCurrentDataListValues(
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 0d04fbe6..a23e3c53 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -1008,10 +1008,13 @@
 #if defined(OS_CHROMEOS)
   // There is no way of determining whether ChromeVox is in use, so assume it's
   // being used.
-  external_delegate_->OnAutofillAvailabilityEvent(false);
+  external_delegate_->OnAutofillAvailabilityEvent(
+      mojom::AutofillState::kNoSuggestions);
 #else
-  if (external_delegate_->HasActiveScreenReader())
-    external_delegate_->OnAutofillAvailabilityEvent(false);
+  if (external_delegate_->HasActiveScreenReader()) {
+    external_delegate_->OnAutofillAvailabilityEvent(
+        mojom::AutofillState::kNoSuggestions);
+  }
 #endif
 }
 
@@ -1033,8 +1036,10 @@
   GetAvailableSuggestions(form, field, &suggestions, &context);
 
   external_delegate_->OnAutofillAvailabilityEvent(
-      context.suppress_reason == SuppressReason::kNotSuppressed &&
-      !suggestions.empty());
+      (context.suppress_reason == SuppressReason::kNotSuppressed &&
+       !suggestions.empty())
+          ? mojom::AutofillState::kAutofillAvailable
+          : mojom::AutofillState::kNoSuggestions);
 }
 
 void AutofillManager::OnSelectControlDidChangeImpl(
@@ -1370,6 +1375,9 @@
     int query_id,
     bool autoselect_first_suggestion,
     const std::vector<Suggestion>& suggestions) {
+  external_delegate_->OnAutofillAvailabilityEvent(
+      !suggestions.empty() ? mojom::AutofillState::kAutocompleteAvailable
+                           : mojom::AutofillState::kNoSuggestions);
   external_delegate_->OnSuggestionsReturned(query_id, suggestions,
                                             autoselect_first_suggestion);
 }
diff --git a/components/autofill/core/browser/autofill_type.cc b/components/autofill/core/browser/autofill_type.cc
index 7b673f6..4d0d4241 100644
--- a/components/autofill/core/browser/autofill_type.cc
+++ b/components/autofill/core/browser/autofill_type.cc
@@ -56,6 +56,10 @@
     case ADDRESS_HOME_STREET_ADDRESS:
     case ADDRESS_HOME_SORTING_CODE:
     case ADDRESS_HOME_DEPENDENT_LOCALITY:
+    case ADDRESS_HOME_STREET:
+    case ADDRESS_HOME_HOUSE_NUMBER:
+    case ADDRESS_HOME_FLOOR:
+    case ADDRESS_HOME_OTHER_SUBUNIT:
       return ADDRESS_HOME;
 
     case ADDRESS_BILLING_LINE1:
@@ -716,6 +720,14 @@
       return "NOT_USERNAME";
     case UPI_VPA:
       return "UPI_VPA";
+    case ADDRESS_HOME_STREET:
+      return "ADDRESS_HOME_STREET";
+    case ADDRESS_HOME_HOUSE_NUMBER:
+      return "ADDRESS_HOME_HOUSE_NUMBER";
+    case ADDRESS_HOME_FLOOR:
+      return "ADDRESS_HOME_FLOOR";
+    case ADDRESS_HOME_OTHER_SUBUNIT:
+      return "ADDRESS_HOME_OTHER_SUBUNIT";
     case AMBIGUOUS_TYPE:
       return "AMBIGUOUS_TYPE";
     case MAX_VALID_FIELD_TYPE:
diff --git a/components/autofill/core/browser/field_types.h b/components/autofill/core/browser/field_types.h
index 3080ba1..e874de0c 100644
--- a/components/autofill/core/browser/field_types.h
+++ b/components/autofill/core/browser/field_types.h
@@ -189,9 +189,26 @@
   // https://en.wikipedia.org/wiki/Unified_Payments_Interface
   UPI_VPA = 102,
 
+  // Just the street name of an address, no house number.
+  // Currently not used by Chrome.
+  ADDRESS_HOME_STREET = 103,
+
+  // House number of an address, may be alphanumeric.
+  // Currently not used by Chrome.
+  ADDRESS_HOME_HOUSE_NUMBER = 104,
+
+  // Floor within in a building, may be alphanumeric.
+  // Currently not used by Chrome.
+  ADDRESS_HOME_FLOOR = 105,
+
+  // A catch-all for other type of subunits (only used until something more
+  // precise is defined).
+  // Currently not used by Chrome.
+  ADDRESS_HOME_OTHER_SUBUNIT = 106,
+
   // No new types can be added without a corresponding change to the Autofill
   // server.
-  MAX_VALID_FIELD_TYPE = 103,
+  MAX_VALID_FIELD_TYPE = 107,
 };
 
 // The list of all HTML autocomplete field type hints supported by Chrome.
diff --git a/components/autofill/core/browser/test_autofill_driver.cc b/components/autofill/core/browser/test_autofill_driver.cc
index dd51b96..e7ce7623 100644
--- a/components/autofill/core/browser/test_autofill_driver.cc
+++ b/components/autofill/core/browser/test_autofill_driver.cc
@@ -79,7 +79,7 @@
 }
 
 void TestAutofillDriver::RendererShouldSetSuggestionAvailability(
-    bool available) {}
+    const mojom::AutofillState state) {}
 
 void TestAutofillDriver::PopupHidden() {
 }
diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h
index d65c2b2..f524de90 100644
--- a/components/autofill/core/browser/test_autofill_driver.h
+++ b/components/autofill/core/browser/test_autofill_driver.h
@@ -46,7 +46,8 @@
   void RendererShouldFillFieldWithValue(const base::string16& value) override;
   void RendererShouldPreviewFieldWithValue(
       const base::string16& value) override;
-  void RendererShouldSetSuggestionAvailability(bool available) override;
+  void RendererShouldSetSuggestionAvailability(
+      const mojom::AutofillState state) override;
   void PopupHidden() override;
   gfx::RectF TransformBoundingBoxToViewportCoordinates(
       const gfx::RectF& bounding_box) override;
diff --git a/components/autofill/core/browser/test_autofill_external_delegate.cc b/components/autofill/core/browser/test_autofill_external_delegate.cc
index f38acc7..d97cfcc 100644
--- a/components/autofill/core/browser/test_autofill_external_delegate.cc
+++ b/components/autofill/core/browser/test_autofill_external_delegate.cc
@@ -68,8 +68,11 @@
 }
 
 void TestAutofillExternalDelegate::OnAutofillAvailabilityEvent(
-    bool has_suggestions) {
-  has_suggestions_available_on_field_focus_ = has_suggestions;
+    const mojom::AutofillState state) {
+  if (state == mojom::AutofillState::kAutofillAvailable)
+    has_suggestions_available_on_field_focus_ = true;
+  else if (state == mojom::AutofillState::kNoSuggestions)
+    has_suggestions_available_on_field_focus_ = false;
 }
 
 void TestAutofillExternalDelegate::WaitForPopupHidden() {
diff --git a/components/autofill/core/browser/test_autofill_external_delegate.h b/components/autofill/core/browser/test_autofill_external_delegate.h
index 7757ba96..2aa5039 100644
--- a/components/autofill/core/browser/test_autofill_external_delegate.h
+++ b/components/autofill/core/browser/test_autofill_external_delegate.h
@@ -31,7 +31,7 @@
                              bool autoselect_first_suggestion,
                              bool is_all_server_suggestions) override;
   bool HasActiveScreenReader() const override;
-  void OnAutofillAvailabilityEvent(bool has_suggestions) override;
+  void OnAutofillAvailabilityEvent(const mojom::AutofillState state) override;
 
   // Functions unique to TestAutofillExternalDelegate.
 
diff --git a/components/autofill/core/common/mojom/autofill_types.mojom b/components/autofill/core/common/mojom/autofill_types.mojom
index 716bc5f..4ab37ae 100644
--- a/components/autofill/core/common/mojom/autofill_types.mojom
+++ b/components/autofill/core/common/mojom/autofill_types.mojom
@@ -303,3 +303,16 @@
   uint32 new_password_renderer_id;
   uint32 confirm_password_renderer_id;
 };
+
+// Represents the autofill state.
+enum AutofillState {
+  // There are no available suggestions, neither autofill nor autocomplete, for
+  // the input.
+  kNoSuggestions,
+  // There are available autofill suggestions for the input. Autofill fills in
+  // an entire form.
+  kAutofillAvailable,
+  // There are available autocomplete suggestions for the input. Autocomplete
+  // only fills in a single input.
+  kAutocompleteAvailable,
+};
diff --git a/components/autofill/core/common/save_password_progress_logger.cc b/components/autofill/core/common/save_password_progress_logger.cc
index 1aadf99..f08820c 100644
--- a/components/autofill/core/common/save_password_progress_logger.cc
+++ b/components/autofill/core/common/save_password_progress_logger.cc
@@ -456,6 +456,8 @@
       return "Navigation to New Tab page";
     case STRING_SERVER_PREDICTIONS:
       return "Server predictions";
+    case STRING_USERNAME_FIRST_FLOW_VOTE:
+      return "Username first flow vote";
     case SavePasswordProgressLogger::STRING_INVALID:
       return "INVALID";
       // Intentionally no default: clause here -- all IDs need to get covered.
diff --git a/components/autofill/core/common/save_password_progress_logger.h b/components/autofill/core/common/save_password_progress_logger.h
index 1ff14586..c8d7339 100644
--- a/components/autofill/core/common/save_password_progress_logger.h
+++ b/components/autofill/core/common/save_password_progress_logger.h
@@ -162,6 +162,7 @@
     STRING_DID_NAVIGATE_MAIN_FRAME,
     STRING_NAVIGATION_NTP,
     STRING_SERVER_PREDICTIONS,
+    STRING_USERNAME_FIRST_FLOW_VOTE,
     STRING_INVALID,  // Represents a string returned in a case of an error.
     STRING_MAX = STRING_INVALID
   };
diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h
index 9783f364..08226a74 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/components/autofill/ios/browser/autofill_driver_ios.h
@@ -60,7 +60,8 @@
   void RendererShouldFillFieldWithValue(const base::string16& value) override;
   void RendererShouldPreviewFieldWithValue(
       const base::string16& value) override;
-  void RendererShouldSetSuggestionAvailability(bool available) override;
+  void RendererShouldSetSuggestionAvailability(
+      const mojom::AutofillState state) override;
   void PopupHidden() override;
   gfx::RectF TransformBoundingBoxToViewportCoordinates(
       const gfx::RectF& bounding_box) override;
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm
index 7c18d5f3..c9bce04 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -131,7 +131,7 @@
 }
 
 void AutofillDriverIOS::RendererShouldSetSuggestionAvailability(
-    bool available) {}
+    const mojom::AutofillState state) {}
 
 void AutofillDriverIOS::PopupHidden() {
 }
diff --git a/components/bookmarks/browser/BUILD.gn b/components/bookmarks/browser/BUILD.gn
index e3eaa926..a28dfd2 100644
--- a/components/bookmarks/browser/BUILD.gn
+++ b/components/bookmarks/browser/BUILD.gn
@@ -75,7 +75,9 @@
   ]
 
   if (toolkit_views) {
-    deps += [ "//ui/views" ]
+    # This code has TOOLKIT_VIEWS ifdefs, but doesn't actually rely on anything
+    # else in views.
+    all_dependent_configs = [ "//ui/views:flags" ]
   }
 
   if (toolkit_views && !is_mac) {
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index eafb903..53a7f7d 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -973,10 +973,10 @@
   state_changed_ = window_state->GetStateType() != pending_window_state_;
   if (!state_changed_) {
     // Animate PIP window movement unless it is being dragged.
-    if (window_state->IsPip() && !window_state->is_dragged()) {
-      client_controlled_state_->set_next_bounds_change_animation_type(
-          ash::ClientControlledState::kAnimationAnimated);
-    }
+    client_controlled_state_->set_next_bounds_change_animation_type(
+        window_state->IsPip() && !window_state->is_dragged()
+            ? ash::ClientControlledState::kAnimationAnimated
+            : ash::ClientControlledState::kAnimationNone);
     return true;
   }
 
diff --git a/components/exo/client_controlled_shell_surface_unittest.cc b/components/exo/client_controlled_shell_surface_unittest.cc
index 6c721c8b..8fef635 100644
--- a/components/exo/client_controlled_shell_surface_unittest.cc
+++ b/components/exo/client_controlled_shell_surface_unittest.cc
@@ -2013,6 +2013,39 @@
 }
 
 TEST_F(ClientControlledShellSurfaceTest,
+       PipWindowDragDoesNotAnimateWithExtraCommit) {
+  const gfx::Size buffer_size(256, 256);
+  std::unique_ptr<Buffer> buffer(
+      new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+  std::unique_ptr<Surface> surface(new Surface());
+  auto shell_surface =
+      exo_test_helper()->CreateClientControlledShellSurface(surface.get());
+  shell_surface->SetGeometry(gfx::Rect(buffer_size));
+  surface->Attach(buffer.get());
+  surface->Commit();
+  shell_surface->SetPip();
+  surface->Commit();
+  shell_surface->GetWidget()->Show();
+
+  // Making an extra commit may set the next bounds change animation type
+  // wrongly.
+  surface->Commit();
+
+  aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
+  EXPECT_EQ(gfx::Rect(0, 0, 256, 256), window->layer()->GetTargetBounds());
+  EXPECT_EQ(gfx::Rect(0, 0, 256, 256), window->layer()->bounds());
+  ui::ScopedAnimationDurationScaleMode animation_scale_mode(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+  std::unique_ptr<ash::WindowResizer> resizer(ash::CreateWindowResizer(
+      window, gfx::Point(), HTCAPTION, ::wm::WINDOW_MOVE_SOURCE_MOUSE));
+  resizer->Drag(gfx::Point(10, 10), 0);
+  EXPECT_EQ(gfx::Rect(10, 10, 256, 256), window->layer()->GetTargetBounds());
+  EXPECT_EQ(gfx::Rect(10, 10, 256, 256), window->layer()->bounds());
+  EXPECT_FALSE(window->layer()->GetAnimator()->is_animating());
+  resizer->CompleteDrag();
+}
+
+TEST_F(ClientControlledShellSurfaceTest,
        ExpandingPipInTabletModeEndsSplitView) {
   EnableTabletMode(true);
 
diff --git a/components/favicon_base/BUILD.gn b/components/favicon_base/BUILD.gn
index f1057b17..fb71b75e0 100644
--- a/components/favicon_base/BUILD.gn
+++ b/components/favicon_base/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//testing/libfuzzer/fuzzer_test.gni")
+
 if (is_android) {
   import("//build/config/android/rules.gni")
 }
@@ -54,6 +56,18 @@
   ]
 }
 
+fuzzer_test("favicon_url_parser_fuzzer") {
+  sources = [
+    "favicon_url_parser_fuzzer.cc",
+  ]
+
+  deps = [
+    ":favicon_base",
+    "//base",
+    "//base:i18n",
+  ]
+}
+
 if (is_android) {
   java_cpp_enum("favicon_base_enums_java") {
     sources = [
diff --git a/components/favicon_base/favicon_url_parser_fuzzer.cc b/components/favicon_base/favicon_url_parser_fuzzer.cc
new file mode 100644
index 0000000..94af291
--- /dev/null
+++ b/components/favicon_base/favicon_url_parser_fuzzer.cc
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/i18n/icu_util.h"
+#include "components/favicon_base/favicon_url_parser.h"
+
+struct IcuEnvironment {
+  IcuEnvironment() { CHECK(base::i18n::InitializeICU()); }
+  // used by ICU integration.
+  base::AtExitManager at_exit_manager;
+};
+
+IcuEnvironment* env = new IcuEnvironment();
+
+chrome::FaviconUrlFormat GetFaviconUrlFormatFromUint8(uint8_t value) {
+  // Dummy switch to detect changes to the enum definition.
+  switch (chrome::FaviconUrlFormat()) {
+    case chrome::FaviconUrlFormat::kFaviconLegacy:
+    case chrome::FaviconUrlFormat::kFavicon2:
+      break;
+  }
+
+  return (value % 2) == 0 ? chrome::FaviconUrlFormat::kFaviconLegacy
+                          : chrome::FaviconUrlFormat::kFavicon2;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < 2)
+    return 0;
+
+  // The first byte is used to determine the FaviconUrlFormat, and the rest of
+  // the data is used in the input string to parse.
+  const chrome::FaviconUrlFormat url_format =
+      GetFaviconUrlFormatFromUint8(data[0]);
+
+  const std::string string_input(reinterpret_cast<const char*>(data + 1),
+                                 size - 1);
+  chrome::ParsedFaviconPath parsed;
+  chrome::ParseFaviconPath(string_input, url_format, &parsed);
+  return 0;
+}
diff --git a/components/history_strings.grdp b/components/history_strings.grdp
index 463ebd9..48d5f1e 100644
--- a/components/history_strings.grdp
+++ b/components/history_strings.grdp
@@ -5,6 +5,9 @@
   <message name="IDS_HISTORY_ACTION_MENU_DESCRIPTION" desc="Text used to identify the history entry drop-down menu for screen readers">
     Actions
   </message>
+  <message name="IDS_HISTORY_ARIA_ROLE_DESCRIPTION" desc="Text used for accessibility to describe the list of history entries.">
+    List of history entries
+  </message>
   <message name="IDS_HISTORY_CANCEL_EDITING_BUTTON" desc="Text for the button to exit editing mode [Length: 7em].">
     Cancel
   </message>
diff --git a/components/media_message_center/media_notification_background_unittest.cc b/components/media_message_center/media_notification_background_unittest.cc
index e929337..53712fe3 100644
--- a/components/media_message_center/media_notification_background_unittest.cc
+++ b/components/media_message_center/media_notification_background_unittest.cc
@@ -140,7 +140,7 @@
 TEST_F(MediaNotificationBackgroundTest, GetBackgroundColorRespectsTheme) {
   TestDarkTheme dark_theme;
   views::View owner;
-  owner.SetNativeTheme(&dark_theme);
+  owner.SetNativeThemeForTesting(&dark_theme);
   EXPECT_EQ(kDarkBackgroundColor, background()->GetBackgroundColor(owner));
 }
 
diff --git a/components/media_message_center/media_notification_view.cc b/components/media_message_center/media_notification_view.cc
index 4b8cca4..2672c7b 100644
--- a/components/media_message_center/media_notification_view.cc
+++ b/components/media_message_center/media_notification_view.cc
@@ -19,7 +19,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/font_list.h"
-#include "ui/message_center/public/cpp/message_center_constants.h"
 #include "ui/message_center/views/notification_header_view.h"
 #include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/layout/box_layout.h"
@@ -32,13 +31,6 @@
 
 namespace {
 
-// The right padding is 1/5th the size of the notification.
-constexpr int kRightMarginSize = message_center::kNotificationWidth / 5;
-
-// The right padding is 1/3rd the size of the notification when the
-// notification is expanded.
-constexpr int kRightMarginExpandedSize = message_center::kNotificationWidth / 4;
-
 // The number of actions supported when the notification is expanded or not.
 constexpr size_t kMediaNotificationActionsCount = 3;
 constexpr size_t kMediaNotificationExpandedActionsCount = 5;
@@ -107,11 +99,13 @@
     MediaNotificationContainer* container,
     base::WeakPtr<MediaNotificationItem> item,
     views::View* header_row_controls_view,
-    const base::string16& default_app_name)
+    const base::string16& default_app_name,
+    int notification_width)
     : container_(container),
       item_(std::move(item)),
       header_row_controls_view_(header_row_controls_view),
-      default_app_name_(default_app_name) {
+      default_app_name_(default_app_name),
+      notification_width_(notification_width) {
   DCHECK(container_);
 
   SetLayoutManager(std::make_unique<views::BoxLayout>(
@@ -407,7 +401,9 @@
             views::BoxLayout::Orientation::kVertical,
             gfx::Insets(
                 kDefaultMarginSize, kDefaultMarginSize, kDefaultMarginSize,
-                has_artwork_ ? kRightMarginExpandedSize : kDefaultMarginSize),
+                has_artwork_
+                    ? (notification_width_ * kMediaImageMaxWidthExpandedPct)
+                    : kDefaultMarginSize),
             kDefaultMarginSize))
         ->SetDefaultFlex(1);
   } else {
@@ -418,7 +414,9 @@
         ->SetLayoutManager(std::make_unique<views::BoxLayout>(
             views::BoxLayout::Orientation::kHorizontal,
             gfx::Insets(0, kDefaultMarginSize, 14,
-                        has_artwork_ ? kRightMarginSize : kDefaultMarginSize),
+                        has_artwork_
+                            ? (notification_width_ * kMediaImageMaxWidthPct)
+                            : kDefaultMarginSize),
             kDefaultMarginSize, true))
         ->SetFlexForView(title_artist_row_, 1);
   }
diff --git a/components/media_message_center/media_notification_view.h b/components/media_message_center/media_notification_view.h
index ab07f0d..6e1abc3 100644
--- a/components/media_message_center/media_notification_view.h
+++ b/components/media_message_center/media_notification_view.h
@@ -65,7 +65,8 @@
   MediaNotificationView(MediaNotificationContainer* container,
                         base::WeakPtr<MediaNotificationItem> item,
                         views::View* header_row_controls_view,
-                        const base::string16& default_app_name);
+                        const base::string16& default_app_name,
+                        int notification_width);
   ~MediaNotificationView() override;
 
   void SetExpanded(bool expanded);
@@ -127,6 +128,9 @@
   // String to set as the app name of the header when there is no source title.
   base::string16 default_app_name_;
 
+  // Width of the notification in pixels. Used for calculating artwork bounds.
+  int notification_width_;
+
   bool has_artwork_ = false;
 
   // Whether this notification is expanded or not.
diff --git a/components/media_message_center/media_notification_view_unittest.cc b/components/media_message_center/media_notification_view_unittest.cc
index 1cf2384c..188dd7f68 100644
--- a/components/media_message_center/media_notification_view_unittest.cc
+++ b/components/media_message_center/media_notification_view_unittest.cc
@@ -54,7 +54,10 @@
 const char kTestAppName[] = "app name";
 
 const gfx::Size kWidgetSize(500, 500);
-const gfx::Size kViewSize(400, 400);
+
+constexpr int kViewWidth = 400;
+constexpr int kViewArtworkWidth = kViewWidth * 0.4;
+const gfx::Size kViewSize(kViewWidth, 400);
 
 // Checks if the view class name is used by a media button.
 bool IsMediaButtonType(const char* class_name) {
@@ -303,7 +306,7 @@
     auto view = std::make_unique<MediaNotificationView>(
         &container_, item_->GetWeakPtr(),
         nullptr /* header_row_controls_view */,
-        base::ASCIIToUTF16(kTestDefaultAppName));
+        base::ASCIIToUTF16(kTestDefaultAppName), kViewWidth);
     view->SetSize(kViewSize);
     view->set_owned_by_client();
 
@@ -792,6 +795,9 @@
   // have artwork.
   EXPECT_GT(title_artist_width, title_artist_row()->width());
 
+  // Ensure that the title artist row does not extend into the artwork bounds.
+  EXPECT_LE(kViewWidth - kViewArtworkWidth, title_artist_row()->width());
+
   // Ensure that the image is displayed in the background artwork and that the
   // size of the notification was not affected.
   EXPECT_FALSE(GetArtworkImage().isNull());
diff --git a/components/new_or_sad_tab_strings.grdp b/components/new_or_sad_tab_strings.grdp
index 23930bd..4771a59 100644
--- a/components/new_or_sad_tab_strings.grdp
+++ b/components/new_or_sad_tab_strings.grdp
@@ -160,8 +160,5 @@
           <ph name="LIST_ITEM">&lt;li&gt;</ph>Your internet service provider
         <ph name="END_LIST">&lt;/ul&gt;</ph>
       </message>
-      <message name="IDS_NEW_TAB_OTR_COOKIE_CONTROLS_DESCRIPTION" desc="Description of the cookie controls box shown in the incognito ntp.">
-        When turned on, sites can’t use cookies that track you across the web
-      </message>
 
 </grit-part>
diff --git a/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.cc b/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.cc
index 1d30402d..90aa1866 100644
--- a/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.cc
+++ b/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.cc
@@ -14,21 +14,13 @@
 const char kPrefetchingOfflinePagesAppId[] =
     "com.google.chrome.OfflinePagePrefetch";
 
-PrefetchGCMAppHandler::PrefetchGCMAppHandler(
-    std::unique_ptr<TokenFactory> token_factory)
-    : token_factory_(std::move(token_factory)) {}
-
+PrefetchGCMAppHandler::PrefetchGCMAppHandler() {}
 PrefetchGCMAppHandler::~PrefetchGCMAppHandler() = default;
 
 void PrefetchGCMAppHandler::SetService(PrefetchService* service) {
   prefetch_service_ = service;
 }
 
-void PrefetchGCMAppHandler::GetGCMToken(
-    instance_id::InstanceID::GetTokenCallback callback) {
-  token_factory_->GetGCMToken(std::move(callback));
-}
-
 void PrefetchGCMAppHandler::ShutdownHandler() {
   NOTIMPLEMENTED();
 }
diff --git a/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h b/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h
index 3b5ca1f..5abf4fd 100644
--- a/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h
+++ b/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h
@@ -22,15 +22,7 @@
 class PrefetchGCMAppHandler : public gcm::GCMAppHandler,
                               public PrefetchGCMHandler {
  public:
-  class TokenFactory {
-   public:
-    virtual ~TokenFactory() = default;
-
-    virtual void GetGCMToken(
-        instance_id::InstanceID::GetTokenCallback callback) = 0;
-  };
-
-  explicit PrefetchGCMAppHandler(std::unique_ptr<TokenFactory> token_factory);
+  explicit PrefetchGCMAppHandler();
   ~PrefetchGCMAppHandler() override;
 
   // gcm::GCMAppHandler implementation.
@@ -50,12 +42,10 @@
   void SetService(PrefetchService* service) override;
   gcm::GCMAppHandler* AsGCMAppHandler() override;
   std::string GetAppId() const override;
-  void GetGCMToken(instance_id::InstanceID::GetTokenCallback callback) override;
 
  private:
   // Not owned, PrefetchService owns |this.
-  PrefetchService* prefetch_service_;
-  std::unique_ptr<TokenFactory> token_factory_;
+  PrefetchService* prefetch_service_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(PrefetchGCMAppHandler);
 };
diff --git a/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc b/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc
index 0fa6de7..2833391 100644
--- a/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc
@@ -9,40 +9,20 @@
 #include "base/bind.h"
 #include "base/test/task_environment.h"
 #include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/offline_pages/core/prefetch/prefetch_service_impl.h"
 #include "components/offline_pages/core/prefetch/prefetch_service_test_taco.h"
 #include "components/offline_pages/core/prefetch/test_prefetch_dispatcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
 
-class TestTokenFactory : public PrefetchGCMAppHandler::TokenFactory {
- public:
-  TestTokenFactory() = default;
-  ~TestTokenFactory() override = default;
-
-  void GetGCMToken(
-      instance_id::InstanceID::GetTokenCallback callback) override {
-    std::move(callback).Run(token, result);
-  }
-
-  instance_id::InstanceID::Result result = instance_id::InstanceID::SUCCESS;
-  std::string token = "default_token";
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestTokenFactory);
-};
-
 class PrefetchGCMAppHandlerTest : public testing::Test {
  public:
   PrefetchGCMAppHandlerTest() {
     auto dispatcher = std::make_unique<TestPrefetchDispatcher>();
     test_dispatcher_ = dispatcher.get();
 
-    auto token_factory = std::make_unique<TestTokenFactory>();
-    token_factory_ = token_factory.get();
-
-    auto gcm_app_handler =
-        std::make_unique<PrefetchGCMAppHandler>(std::move(token_factory));
+    auto gcm_app_handler = std::make_unique<PrefetchGCMAppHandler>();
     handler_ = gcm_app_handler.get();
 
     prefetch_service_taco_.reset(new PrefetchServiceTestTaco);
@@ -59,7 +39,6 @@
 
   TestPrefetchDispatcher* dispatcher() { return test_dispatcher_; }
   PrefetchGCMAppHandler* handler() { return handler_; }
-  TestTokenFactory* token_factory() { return token_factory_; }
 
  private:
   base::test::SingleThreadTaskEnvironment task_environment_;
@@ -69,8 +48,6 @@
   TestPrefetchDispatcher* test_dispatcher_;
   // Owned by the taco.
   PrefetchGCMAppHandler* handler_;
-  // Owned by the PrefetchGCMAppHandler.
-  TestTokenFactory* token_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PrefetchGCMAppHandlerTest);
 };
@@ -94,14 +71,4 @@
   EXPECT_EQ(0U, dispatcher()->operation_list.size());
 }
 
-TEST_F(PrefetchGCMAppHandlerTest, TestGetToken) {
-  std::string result_token;
-
-  handler()->GetGCMToken(base::BindOnce(
-      [](std::string* result_token, const std::string& token,
-         instance_id::InstanceID::Result result) { *result_token = token; },
-      &result_token));
-  EXPECT_EQ(token_factory()->token, result_token);
-}
-
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_gcm_handler.h b/components/offline_pages/core/prefetch/prefetch_gcm_handler.h
index a3f529b..fb1e179a 100644
--- a/components/offline_pages/core/prefetch/prefetch_gcm_handler.h
+++ b/components/offline_pages/core/prefetch/prefetch_gcm_handler.h
@@ -15,11 +15,9 @@
 
 namespace offline_pages {
 
-class PrefetchGCMHandler;
 class PrefetchService;
 
-// Main class and entry point for the Offline Pages Prefetching feature, that
-// controls the lifetime of all major subcomponents of the prefetching system.
+// Provides a GCM interface for PrefetchService.
 class PrefetchGCMHandler {
  public:
   PrefetchGCMHandler() = default;
@@ -34,11 +32,6 @@
 
   // The app ID to register with at the GCM layer.
   virtual std::string GetAppId() const = 0;
-
-  // Gets a token suitable for sending to Offline Page Service for notifications
-  // when work is completed.
-  virtual void GetGCMToken(
-      instance_id::InstanceID::GetTokenCallback callback) = 0;
 };
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_service_impl.cc b/components/offline_pages/core/prefetch/prefetch_service_impl.cc
index 53f246e..dbdbc91 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_impl.cc
+++ b/components/offline_pages/core/prefetch/prefetch_service_impl.cc
@@ -36,6 +36,7 @@
     std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer,
     std::unique_ptr<PrefetchDownloader> prefetch_downloader,
     std::unique_ptr<PrefetchImporter> prefetch_importer,
+    std::unique_ptr<PrefetchGCMHandler> gcm_handler,
     std::unique_ptr<PrefetchBackgroundTaskHandler>
         prefetch_background_task_handler,
     std::unique_ptr<ThumbnailFetcher> thumbnail_fetcher,
@@ -48,6 +49,7 @@
       prefetch_store_(std::move(prefetch_store)),
       prefetch_downloader_(std::move(prefetch_downloader)),
       prefetch_importer_(std::move(prefetch_importer)),
+      prefetch_gcm_handler_(std::move(gcm_handler)),
       prefetch_background_task_handler_(
           std::move(prefetch_background_task_handler)),
       prefs_(prefs),
@@ -56,6 +58,7 @@
       image_fetcher_(image_fetcher) {
   prefetch_dispatcher_->SetService(this);
   prefetch_downloader_->SetPrefetchService(this);
+  prefetch_gcm_handler_->SetService(this);
   if (suggested_articles_observer_)
     suggested_articles_observer_->SetPrefetchService(this);
 }
@@ -84,13 +87,7 @@
   return prefetch_prefs::GetCachedPrefetchGCMToken(prefs_);
 }
 
-void PrefetchServiceImpl::RefreshGCMToken() {
-  DCHECK(prefetch_gcm_handler_);
-  prefetch_gcm_handler_->GetGCMToken(base::AdaptCallbackForRepeating(
-      base::BindOnce(&PrefetchServiceImpl::OnGCMTokenReceived, GetWeakPtr())));
-}
-
-void PrefetchServiceImpl::OnGCMTokenReceived(
+void PrefetchServiceImpl::GCMTokenReceived(
     const std::string& gcm_token,
     instance_id::InstanceID::Result result) {
   // TODO(dimich): Add UMA reporting on instance_id::InstanceID::Result.
@@ -159,13 +156,6 @@
   return prefetch_gcm_handler_.get();
 }
 
-void PrefetchServiceImpl::SetPrefetchGCMHandler(
-    std::unique_ptr<PrefetchGCMHandler> handler) {
-  DCHECK(!prefetch_gcm_handler_);
-  prefetch_gcm_handler_ = std::move(handler);
-  prefetch_gcm_handler_->SetService(this);
-}
-
 PrefetchNetworkRequestFactory*
 PrefetchServiceImpl::GetPrefetchNetworkRequestFactory() {
   return network_request_factory_.get();
diff --git a/components/offline_pages/core/prefetch/prefetch_service_impl.h b/components/offline_pages/core/prefetch/prefetch_service_impl.h
index 643afa0..599552f 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_impl.h
+++ b/components/offline_pages/core/prefetch/prefetch_service_impl.h
@@ -31,6 +31,7 @@
       std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer,
       std::unique_ptr<PrefetchDownloader> prefetch_downloader,
       std::unique_ptr<PrefetchImporter> prefetch_importer,
+      std::unique_ptr<PrefetchGCMHandler> gcm_handler,
       std::unique_ptr<PrefetchBackgroundTaskHandler> background_task_handler,
       std::unique_ptr<ThumbnailFetcher> thumbnail_fetcher,
       image_fetcher::ImageFetcher* image_fetcher_,
@@ -62,8 +63,8 @@
   PrefetchImporter* GetPrefetchImporter() override;
   PrefetchBackgroundTaskHandler* GetPrefetchBackgroundTaskHandler() override;
 
-  void SetPrefetchGCMHandler(std::unique_ptr<PrefetchGCMHandler> handler);
-  void RefreshGCMToken();
+  void GCMTokenReceived(const std::string& gcm_token,
+                        instance_id::InstanceID::Result result);
 
   // Thumbnail fetchers. With Feed, GetImageFetcher() is available
   // and GetThumbnailFetcher() is null.
@@ -83,11 +84,8 @@
   void ReplaceImageFetcher(image_fetcher::ImageFetcher* image_fetcher);
 
  private:
-  void OnGCMTokenReceived(const std::string& gcm_token,
-                          instance_id::InstanceID::Result result);
 
   OfflineEventLogger logger_;
-  std::unique_ptr<PrefetchGCMHandler> prefetch_gcm_handler_;
 
   std::unique_ptr<OfflineMetricsCollector> offline_metrics_collector_;
   std::unique_ptr<PrefetchDispatcher> prefetch_dispatcher_;
@@ -96,6 +94,7 @@
   std::unique_ptr<PrefetchStore> prefetch_store_;
   std::unique_ptr<PrefetchDownloader> prefetch_downloader_;
   std::unique_ptr<PrefetchImporter> prefetch_importer_;
+  std::unique_ptr<PrefetchGCMHandler> prefetch_gcm_handler_;
   std::unique_ptr<PrefetchBackgroundTaskHandler>
       prefetch_background_task_handler_;
   PrefService* prefs_;
diff --git a/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc b/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
index ff94385..6fd99a42 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
+++ b/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
@@ -121,7 +121,6 @@
 void PrefetchServiceTestTaco::SetPrefetchGCMHandler(
     std::unique_ptr<PrefetchGCMHandler> gcm_handler) {
   CHECK(!prefetch_service_);
-  CHECK(gcm_handler);
   gcm_handler_ = std::move(gcm_handler);
 }
 
@@ -199,10 +198,9 @@
       std::move(network_request_factory_), offline_page_model_.get(),
       std::move(prefetch_store_), std::move(suggested_articles_observer_),
       std::move(prefetch_downloader_), std::move(prefetch_importer_),
-      std::move(prefetch_background_task_handler_),
+      std::move(gcm_handler_), std::move(prefetch_background_task_handler_),
       std::move(thumbnail_fetcher_), thumbnail_image_fetcher_.get(),
       pref_service_.get());
-  service->SetPrefetchGCMHandler(std::move(gcm_handler_));
   prefetch_service_ = std::move(service);
 }
 
diff --git a/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.cc b/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.cc
index 50f77c3..594ed04 100644
--- a/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.cc
+++ b/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.cc
@@ -5,9 +5,6 @@
 #include "components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h"
 
 namespace offline_pages {
-namespace {
-const char kToken[] = "an_instance_id_token";
-}
 
 TestPrefetchGCMHandler::TestPrefetchGCMHandler() = default;
 TestPrefetchGCMHandler::~TestPrefetchGCMHandler() = default;
@@ -20,10 +17,5 @@
   return "com.google.test.PrefetchAppId";
 }
 
-void TestPrefetchGCMHandler::GetGCMToken(
-    instance_id::InstanceID::GetTokenCallback callback) {
-  std::move(callback).Run(kToken, instance_id::InstanceID::Result::SUCCESS);
-}
-
 void TestPrefetchGCMHandler::SetService(PrefetchService* service) {}
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h b/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h
index 8dae52b..79754968 100644
--- a/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h
+++ b/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h
@@ -21,7 +21,6 @@
 
   gcm::GCMAppHandler* AsGCMAppHandler() override;
   std::string GetAppId() const override;
-  void GetGCMToken(instance_id::InstanceID::GetTokenCallback callback) override;
   void SetService(PrefetchService* service) override;
 };
 
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index aa0cfc1..8992ebf6 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -7,6 +7,7 @@
 import("//build/config/ui.gni")
 import("//components/vector_icons/vector_icons.gni")
 import("//device/vr/buildflags/buildflags.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
 import("//third_party/protobuf/proto_library.gni")
 
 if (is_android) {
@@ -453,3 +454,16 @@
     "//url",
   ]
 }
+
+fuzzer_test("autocomplete_input_fuzzer") {
+  sources = [
+    "autocomplete_input_fuzzer.cc",
+  ]
+  deps = [
+    ":browser",
+    ":test_support",
+    "//base",
+    "//base:i18n",
+    "//third_party/metrics_proto:metrics_proto",
+  ]
+}
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/SuggestionAnswer.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/SuggestionAnswer.java
index ba6acfb..7d618dba 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/SuggestionAnswer.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/SuggestionAnswer.java
@@ -7,6 +7,7 @@
 import android.support.v4.util.ObjectsCompat;
 import android.text.TextUtils;
 
+import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 
 import java.util.ArrayList;
@@ -22,7 +23,8 @@
     private final ImageLine mFirstLine;
     private final ImageLine mSecondLine;
 
-    private SuggestionAnswer(@AnswerType int type, ImageLine firstLine, ImageLine secondLine) {
+    @VisibleForTesting
+    public SuggestionAnswer(@AnswerType int type, ImageLine firstLine, ImageLine secondLine) {
         mType = type;
         mFirstLine = firstLine;
         mSecondLine = secondLine;
@@ -69,8 +71,9 @@
         private final TextField mStatusText;
         private final String mImage;
 
-        private ImageLine(List<TextField> textFields, TextField additionalText,
-                TextField statusText, String imageUrl) {
+        @VisibleForTesting
+        public ImageLine(List<TextField> textFields, TextField additionalText, TextField statusText,
+                String imageUrl) {
             mTextFields = textFields;
             mAdditionalText = additionalText;
             mStatusText = statusText;
@@ -160,7 +163,8 @@
         private final int mStyle;
         private final int mNumLines;
 
-        private TextField(
+        @VisibleForTesting
+        public TextField(
                 @AnswerTextType int type, String text, @AnswerTextStyle int style, int numLines) {
             mType = type;
             mText = text;
diff --git a/components/omnibox/browser/autocomplete_input_fuzzer.cc b/components/omnibox/browser/autocomplete_input_fuzzer.cc
new file mode 100644
index 0000000..b5f1319
--- /dev/null
+++ b/components/omnibox/browser/autocomplete_input_fuzzer.cc
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/omnibox/browser/autocomplete_input.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/at_exit.h"
+#include "base/i18n/icu_util.h"
+#include "base/strings/string16.h"
+#include "components/omnibox/browser/test_scheme_classifier.h"
+#include "third_party/metrics_proto/omnibox_event.pb.h"
+
+// From crbug.com/774858
+struct IcuEnvironment {
+  IcuEnvironment() { CHECK(base::i18n::InitializeICU()); }
+  // Used by ICU integration.
+  base::AtExitManager at_exit_manager;
+};
+
+IcuEnvironment icu_env;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // This fuzzer creates a random UTF16 string, for testing primarily against
+  // AutocompleteInput::Parse().
+  base::string16 s(reinterpret_cast<const base::string16::value_type*>(data),
+                   size / sizeof(base::string16::value_type));
+  AutocompleteInput input(s, metrics::OmniboxEventProto::OTHER,
+                          TestSchemeClassifier());
+  return 0;
+}
diff --git a/components/omnibox/browser/autocomplete_provider_unittest.cc b/components/omnibox/browser/autocomplete_provider_unittest.cc
index d41218b..79ee090 100644
--- a/components/omnibox/browser/autocomplete_provider_unittest.cc
+++ b/components/omnibox/browser/autocomplete_provider_unittest.cc
@@ -95,7 +95,7 @@
  public:
   TestProvider(int relevance,
                const base::string16& prefix,
-               const base::string16 match_keyword,
+               const base::string16& match_keyword,
                AutocompleteProviderClient* client)
       : AutocompleteProvider(AutocompleteProvider::TYPE_SEARCH),
         listener_(nullptr),
@@ -249,7 +249,7 @@
   };
 
   // Registers a test TemplateURL under the given keyword.
-  void RegisterTemplateURL(const base::string16 keyword,
+  void RegisterTemplateURL(const base::string16& keyword,
                            const std::string& template_url,
                            const std::string& image_url,
                            const std::string& image_url_post_params);
@@ -335,7 +335,7 @@
 }
 
 void AutocompleteProviderTest::RegisterTemplateURL(
-    const base::string16 keyword,
+    const base::string16& keyword,
     const std::string& template_url,
     const std::string& image_url = "",
     const std::string& image_url_post_params = "") {
diff --git a/components/omnibox/browser/history_quick_provider_unittest.cc b/components/omnibox/browser/history_quick_provider_unittest.cc
index b082587..6753641 100644
--- a/components/omnibox/browser/history_quick_provider_unittest.cc
+++ b/components/omnibox/browser/history_quick_provider_unittest.cc
@@ -146,21 +146,21 @@
   // Runs an autocomplete query on |text| and checks to see that the returned
   // results' destination URLs match those provided. |expected_urls| does not
   // need to be in sorted order.
-  void RunTest(const base::string16 text,
+  void RunTest(const base::string16& text,
                bool prevent_inline_autocomplete,
-               std::vector<std::string> expected_urls,
+               const std::vector<std::string>& expected_urls,
                bool can_inline_top_result,
-               base::string16 expected_fill_into_edit,
-               base::string16 autocompletion);
+               const base::string16& expected_fill_into_edit,
+               const base::string16& autocompletion);
 
   // As above, simply with a cursor position specified.
-  void RunTestWithCursor(const base::string16 text,
+  void RunTestWithCursor(const base::string16& text,
                          const size_t cursor_position,
                          bool prevent_inline_autocomplete,
-                         std::vector<std::string> expected_urls,
+                         const std::vector<std::string>& expected_urls,
                          bool can_inline_top_result,
-                         base::string16 expected_fill_into_edit,
-                         base::string16 autocompletion);
+                         const base::string16& expected_fill_into_edit,
+                         const base::string16& autocompletion);
 
   // TODO(shess): From history_service.h in reference to history_backend:
   // > This class has most of the implementation and runs on the 'thread_'.
@@ -297,25 +297,25 @@
 }
 
 void HistoryQuickProviderTest::RunTest(
-    const base::string16 text,
+    const base::string16& text,
     bool prevent_inline_autocomplete,
-    std::vector<std::string> expected_urls,
+    const std::vector<std::string>& expected_urls,
     bool can_inline_top_result,
-    base::string16 expected_fill_into_edit,
-    base::string16 expected_autocompletion) {
+    const base::string16& expected_fill_into_edit,
+    const base::string16& expected_autocompletion) {
   RunTestWithCursor(text, base::string16::npos, prevent_inline_autocomplete,
                     expected_urls, can_inline_top_result,
                     expected_fill_into_edit, expected_autocompletion);
 }
 
 void HistoryQuickProviderTest::RunTestWithCursor(
-    const base::string16 text,
+    const base::string16& text,
     const size_t cursor_position,
     bool prevent_inline_autocomplete,
-    std::vector<std::string> expected_urls,
+    const std::vector<std::string>& expected_urls,
     bool can_inline_top_result,
-    base::string16 expected_fill_into_edit,
-    base::string16 expected_autocompletion) {
+    const base::string16& expected_fill_into_edit,
+    const base::string16& expected_autocompletion) {
   SCOPED_TRACE(text);  // Minimal hint to query being run.
   base::RunLoop().RunUntilIdle();
   AutocompleteInput input(text, cursor_position,
diff --git a/components/omnibox/browser/history_url_provider_unittest.cc b/components/omnibox/browser/history_url_provider_unittest.cc
index 8094c4f3..fbff269 100644
--- a/components/omnibox/browser/history_url_provider_unittest.cc
+++ b/components/omnibox/browser/history_url_provider_unittest.cc
@@ -759,7 +759,7 @@
 
   auto TestAutocompletion =
       [this](std::string input_text, bool input_prevent_inline_autocomplete,
-             std::vector<AutocompletionExpectation> expectations) {
+             const std::vector<AutocompletionExpectation>& expectations) {
         const std::string debug = base::StringPrintf(
             "input text [%s], prevent inline [%d]", input_text.c_str(),
             input_prevent_inline_autocomplete);
diff --git a/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc b/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc
index b8dffca..495bb4bc 100644
--- a/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc
+++ b/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc
@@ -101,7 +101,7 @@
                              std::string zero_suggest_variant_value);
 
   // Fills the URLDatabase with search URLs using the provided information.
-  void LoadURLs(std::vector<TestURLData> url_data_list);
+  void LoadURLs(const std::vector<TestURLData>& url_data_list);
 
   // Waits for history::HistoryService's async operations.
   void WaitForHistoryService();
@@ -112,7 +112,7 @@
                                      PageClassification page_classification);
 
   // Verifies that provider matches are as expected.
-  void ExpectMatches(std::vector<TestMatchData> match_data_list);
+  void ExpectMatches(const std::vector<TestMatchData>& match_data_list);
 
   const TemplateURL* default_search_provider() {
     return client_->GetTemplateURLService()->GetDefaultSearchProvider();
@@ -141,7 +141,7 @@
 }
 
 void LocalHistoryZeroSuggestProviderTest::LoadURLs(
-    std::vector<TestURLData> url_data_list) {
+    const std::vector<TestURLData>& url_data_list) {
   const Time now = Time::Now();
   for (const auto& entry : url_data_list) {
     TemplateURLRef::SearchTermsArgs search_terms_args(
@@ -194,7 +194,7 @@
 }
 
 void LocalHistoryZeroSuggestProviderTest::ExpectMatches(
-    std::vector<TestMatchData> match_data_list) {
+    const std::vector<TestMatchData>& match_data_list) {
   ASSERT_EQ(match_data_list.size(), provider_->matches().size());
   size_t index = 0;
   for (const auto& expected : match_data_list) {
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp
index c833f32..888da30 100644
--- a/components/page_info_strings.grdp
+++ b/components/page_info_strings.grdp
@@ -424,7 +424,7 @@
           You could lose access to your Google Account. Chrome recommends changing your password now. You'll be asked to sign in.
         </message>
         <message name="IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED" desc="A short paragraph explaining to a Chrome user that they have reused their saved password on the current website.">
-          Chrome has detected that you have just entered your password into a site that is suspicious. Please reset your password now!
+          You just entered your password on a deceptive site. Chrome recommends changing your password now.
         </message>
         <message name="IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SYNC" desc="A short paragraph explaining to a Chrome sync user that they have reused their Google password on the current website.">
           Chrome can help you protect your Google Account and change your password.
@@ -444,8 +444,7 @@
           You could lose access to your Google Account. Chromium recommends changing your password now. You'll be asked to sign in.
         </message>
         <message name="IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED" desc="A short paragraph explaining to a Chromium user that they have reused their saved password on the current website.">
-          Chromium has detected that you have just entered your password into a site that is
-          suspicious. Please reset your password now!
+          You just entered your password on a deceptive site. Chromium recommends changing your password now.
         </message>
         <message name="IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SYNC" desc="A short paragraph explaining to a Chrome sync user that they have reused their Google password on the current website.">
           Chromium can help you protect your Google Account and change your password.
@@ -471,9 +470,6 @@
         <message name="IDS_PAGE_INFO_IGNORE_PASSWORD_WARNING_BUTTON" desc="In Title Case: The string used in the page info ignore password warning button.">
           Ignore
         </message>
-        <message name="IDS_PAGE_INFO_DISMISS_PASSWORD_WARNING_BUTTON" desc="In Title Case: The string used in the page info dismiss password warning button.">
-          Dismiss
-        </message>
       </if>
       <if expr="not use_titlecase">
         <message name="IDS_PAGE_INFO_CHANGE_PASSWORD_BUTTON" desc="The string used in the page info change password button.">
@@ -485,9 +481,6 @@
         <message name="IDS_PAGE_INFO_IGNORE_PASSWORD_WARNING_BUTTON" desc="The string used in the page info ignore password warning button.">
           Ignore
         </message>
-        <message name="IDS_PAGE_INFO_DISMISS_PASSWORD_WARNING_BUTTON" desc="The string used in the page info dismiss password warning button.">
-          Dismiss
-        </message>
        </if>
       <message name="IDS_PAGE_INFO_WHITELIST_PASSWORD_REUSE_BUTTON" desc="The string used in the page info whitelist password reuse button.">
         Site is legitimate
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED.png.sha1
new file mode 100644
index 0000000..233ec2c
--- /dev/null
+++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED.png.sha1
@@ -0,0 +1 @@
+8ae40b0d4a078fcdffbbff767a56efa0b6399483
\ No newline at end of file
diff --git a/components/page_load_metrics/browser/BUILD.gn b/components/page_load_metrics/browser/BUILD.gn
index 3dae2fc..3532a93 100644
--- a/components/page_load_metrics/browser/BUILD.gn
+++ b/components/page_load_metrics/browser/BUILD.gn
@@ -10,6 +10,12 @@
     "metrics_navigation_throttle.h",
     "metrics_web_contents_observer.cc",
     "metrics_web_contents_observer.h",
+    "observers/click_input_tracker.cc",
+    "observers/click_input_tracker.h",
+    "observers/core_page_load_metrics_observer.cc",
+    "observers/core_page_load_metrics_observer.h",
+    "observers/largest_contentful_paint_handler.cc",
+    "observers/largest_contentful_paint_handler.h",
     "observers/use_counter/ukm_features.cc",
     "observers/use_counter_page_load_metrics_observer.cc",
     "observers/use_counter_page_load_metrics_observer.h",
@@ -73,6 +79,8 @@
   testonly = true
   sources = [
     "metrics_web_contents_observer_unittest.cc",
+    "observers/click_input_tracker_unittest.cc",
+    "observers/core_page_load_metrics_observer_unittest.cc",
     "observers/page_load_metrics_observer_content_test_harness.cc",
     "observers/page_load_metrics_observer_content_test_harness.h",
     "observers/use_counter_page_load_metrics_observer_unittest.cc",
@@ -85,9 +93,11 @@
     "//base/test:test_support",
     "//components/page_load_metrics/common:page_load_metrics_mojom",
     "//components/page_load_metrics/common:test_support",
+    "//components/ukm:test_support",
     "//components/ukm/content",
     "//content/public/browser",
     "//content/test:test_support",
+    "//services/metrics/public/cpp:ukm_builders",
     "//testing/gtest",
     "//url",
   ]
diff --git a/chrome/browser/page_load_metrics/observers/click_input_tracker.cc b/components/page_load_metrics/browser/observers/click_input_tracker.cc
similarity index 97%
rename from chrome/browser/page_load_metrics/observers/click_input_tracker.cc
rename to components/page_load_metrics/browser/observers/click_input_tracker.cc
index 85505e8..81617b3e 100644
--- a/chrome/browser/page_load_metrics/observers/click_input_tracker.cc
+++ b/components/page_load_metrics/browser/observers/click_input_tracker.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/page_load_metrics/observers/click_input_tracker.h"
+#include "components/page_load_metrics/browser/observers/click_input_tracker.h"
 
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
diff --git a/chrome/browser/page_load_metrics/observers/click_input_tracker.h b/components/page_load_metrics/browser/observers/click_input_tracker.h
similarity index 91%
rename from chrome/browser/page_load_metrics/observers/click_input_tracker.h
rename to components/page_load_metrics/browser/observers/click_input_tracker.h
index d45d108..01d313d9 100644
--- a/chrome/browser/page_load_metrics/observers/click_input_tracker.h
+++ b/components/page_load_metrics/browser/observers/click_input_tracker.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_CLICK_INPUT_TRACKER_H_
-#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_CLICK_INPUT_TRACKER_H_
+#ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_CLICK_INPUT_TRACKER_H_
+#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_CLICK_INPUT_TRACKER_H_
 
 #include "base/feature_list.h"
 #include "base/time/time.h"
@@ -72,4 +72,4 @@
 
 }  // namespace page_load_metrics
 
-#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_CLICK_INPUT_TRACKER_H_
+#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_CLICK_INPUT_TRACKER_H_
diff --git a/chrome/browser/page_load_metrics/observers/click_input_tracker_unittest.cc b/components/page_load_metrics/browser/observers/click_input_tracker_unittest.cc
similarity index 98%
rename from chrome/browser/page_load_metrics/observers/click_input_tracker_unittest.cc
rename to components/page_load_metrics/browser/observers/click_input_tracker_unittest.cc
index d805a64b..fd29b7f 100644
--- a/chrome/browser/page_load_metrics/observers/click_input_tracker_unittest.cc
+++ b/components/page_load_metrics/browser/observers/click_input_tracker_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/page_load_metrics/observers/click_input_tracker.h"
+#include "components/page_load_metrics/browser/observers/click_input_tracker.h"
 
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_simple_task_runner.h"
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
similarity index 99%
rename from chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
rename to components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
index 1e0cd58c..fc70b13 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
+++ b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h"
+#include "components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h"
 
 #include <stddef.h>
 #include <stdint.h>
@@ -11,8 +11,6 @@
 
 #include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h"
 #include "components/page_load_metrics/browser/page_load_metrics_util.h"
 #include "content/public/common/process_type.h"
 #include "net/http/http_response_headers.h"
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h
similarity index 95%
rename from chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
rename to components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h
index 633853b7..c72f596 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
+++ b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_CORE_PAGE_LOAD_METRICS_OBSERVER_H_
-#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_CORE_PAGE_LOAD_METRICS_OBSERVER_H_
+#ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_CORE_PAGE_LOAD_METRICS_OBSERVER_H_
+#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_CORE_PAGE_LOAD_METRICS_OBSERVER_H_
 
-#include "chrome/browser/page_load_metrics/observers/click_input_tracker.h"
-#include "chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h"
+#include "components/page_load_metrics/browser/observers/click_input_tracker.h"
+#include "components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h"
 #include "components/page_load_metrics/browser/page_load_metrics_observer.h"
 #include "services/metrics/public/cpp/ukm_source.h"
 
@@ -268,4 +268,4 @@
   DISALLOW_COPY_AND_ASSIGN(CorePageLoadMetricsObserver);
 };
 
-#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_CORE_PAGE_LOAD_METRICS_OBSERVER_H_
+#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_CORE_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
similarity index 98%
rename from chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
rename to components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
index f24cc96b..fc0b80e0 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
+++ b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h"
+#include "components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h"
 
 #include <memory>
 
-#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
-#include "chrome/test/base/testing_browser_process.h"
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
+#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h"
 #include "components/page_load_metrics/browser/page_load_metrics_util.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 #include "components/page_load_metrics/common/test/page_load_metrics_test_util.h"
@@ -33,14 +32,14 @@
 }  // namespace
 
 class CorePageLoadMetricsObserverTest
-    : public page_load_metrics::PageLoadMetricsObserverTestHarness {
+    : public page_load_metrics::PageLoadMetricsObserverContentTestHarness {
  protected:
   void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
     tracker->AddObserver(std::make_unique<CorePageLoadMetricsObserver>());
   }
 
   void SetUp() override {
-    page_load_metrics::PageLoadMetricsObserverTestHarness::SetUp();
+    page_load_metrics::PageLoadMetricsObserverContentTestHarness::SetUp();
     page_load_metrics::LargestContentfulPaintHandler::SetTestMode(true);
   }
 };
diff --git a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.cc b/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc
similarity index 98%
rename from chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.cc
rename to components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc
index 68dc053..b8918a9 100644
--- a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.cc
+++ b/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h"
+#include "components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h"
 
 #include "components/page_load_metrics/browser/page_load_metrics_observer_delegate.h"
 #include "components/page_load_metrics/common/page_load_metrics.mojom.h"
diff --git a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h b/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
similarity index 93%
rename from chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h
rename to components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
index d574a60..8251eb1 100644
--- a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h
+++ b/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
@@ -1,8 +1,8 @@
 // Copyright 2019 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_LARGEST_CONTENTFUL_PAINT_HANDLER_H_
-#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_LARGEST_CONTENTFUL_PAINT_HANDLER_H_
+#ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_LARGEST_CONTENTFUL_PAINT_HANDLER_H_
+#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_LARGEST_CONTENTFUL_PAINT_HANDLER_H_
 
 #include <map>
 
@@ -119,4 +119,4 @@
 
 }  // namespace page_load_metrics
 
-#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_LARGEST_CONTENTFUL_PAINT_HANDLER_H_
+#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_LARGEST_CONTENTFUL_PAINT_HANDLER_H_
diff --git a/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc b/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc
index 64a3ea4..5772fea 100644
--- a/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc
+++ b/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc
@@ -5,6 +5,8 @@
 #include "components/page_load_metrics/browser/page_load_metrics_embedder_base.h"
 
 #include "base/timer/timer.h"
+
+#include "components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h"
 #include "components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 
@@ -18,8 +20,10 @@
 
 void PageLoadMetricsEmbedderBase::RegisterObservers(PageLoadTracker* tracker) {
   // Register observers used by all embedders
-  if (!IsPrerendering())
+  if (!IsPrerendering()) {
+    tracker->AddObserver(std::make_unique<CorePageLoadMetricsObserver>());
     tracker->AddObserver(std::make_unique<UseCounterPageLoadMetricsObserver>());
+  }
   // Allow the embedder to register any embedder-specific observers
   RegisterEmbedderObservers(tracker);
 }
diff --git a/components/paint_preview/DEPS b/components/paint_preview/DEPS
index dc3e39f..e05a98f 100644
--- a/components/paint_preview/DEPS
+++ b/components/paint_preview/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+mojo/public/cpp",
   "+third_party/harfbuzz-ng/src/src",
   "+third_party/skia/include/core",
   "+ui/gfx/geometry",
diff --git a/components/paint_preview/README.md b/components/paint_preview/README.md
index 3d4da3d..3215801 100644
--- a/components/paint_preview/README.md
+++ b/components/paint_preview/README.md
@@ -10,9 +10,9 @@
 
 ## Why //components?
 
-This directory facilitates sharing code between Blink and the browser
-process. This has the additional benefit of keeping most of the code
-centralized to this directory. Parts of the code are consumed in;
+This directory facilitates sharing code between Blink and the browser process.
+This has the additional benefit of keeping most of the code centralized to this
+directory. Parts of the code are consumed in;
 
 * `//cc`
 * `//chrome`
@@ -24,4 +24,6 @@
 
 ## Directory Structure (WIP)
 
+* `browser/` - Code related to managing and requesting paint previews.
 * `common/` - Shared code; mojo, protos, and C++ code.
+* `renderer/` - Code related to capturing paint previews within the renderer.
diff --git a/components/paint_preview/common/BUILD.gn b/components/paint_preview/common/BUILD.gn
index a2297fc..bcdeb8a 100644
--- a/components/paint_preview/common/BUILD.gn
+++ b/components/paint_preview/common/BUILD.gn
@@ -31,6 +31,19 @@
     ]
   }
 
+  source_set("test_utils") {
+    testonly = true
+
+    sources = [
+      "test_utils.h",
+    ]
+
+    deps = [
+      "//testing/gmock",
+      "//testing/gtest",
+    ]
+  }
+
   source_set("unit_tests") {
     testonly = true
 
diff --git a/components/paint_preview/common/mojom/BUILD.gn b/components/paint_preview/common/mojom/BUILD.gn
new file mode 100644
index 0000000..a688b673
--- /dev/null
+++ b/components/paint_preview/common/mojom/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojom") {
+  sources = [
+    "paint_preview_recorder.mojom",
+  ]
+
+  public_deps = [
+    "//components/discardable_memory/public/mojom",
+    "//mojo/public/mojom/base",
+    "//ui/gfx/geometry/mojom",
+  ]
+}
diff --git a/components/paint_preview/common/mojom/OWNERS b/components/paint_preview/common/mojom/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/components/paint_preview/common/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/components/paint_preview/common/mojom/paint_preview_recorder.mojom b/components/paint_preview/common/mojom/paint_preview_recorder.mojom
new file mode 100644
index 0000000..c0ae083
--- /dev/null
+++ b/components/paint_preview/common/mojom/paint_preview_recorder.mojom
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module paint_preview.mojom;
+
+import "components/discardable_memory/public/mojom/discardable_shared_memory_manager.mojom";
+import "mojo/public/mojom/base/file.mojom";
+import "mojo/public/mojom/base/shared_memory.mojom";
+import "mojo/public/mojom/base/unguessable_token.mojom";
+import "ui/gfx/geometry/mojom/geometry.mojom";
+
+// Status codes for the PaintPreviewRecorder.
+enum PaintPreviewStatus {
+  // Everything worked as intended.
+  kOk,
+
+  // The service was already performing a capture of the frame.
+  kAlreadyCapturing,
+
+  // Capturing the SkPicture for the frame failed or the file provided was bad.
+  kCaptureFailed,
+
+  // Serializing the proto to shared memory failed. Either the data was invalid,
+  // or the memory could not be allocated.
+  kProtoSerializationFailed,
+};
+
+struct PaintPreviewCaptureParams {
+  // GUID for the Paint Preview (used to associate subframes to main frame).
+  mojo_base.mojom.UnguessableToken guid;
+
+  // Clip rect for the capture. An empty |clip_rect| will be treated as
+  // unclipped and will default to the frame (document) size.
+  gfx.mojom.Rect clip_rect;
+
+  // Used to identify if the capture request is for the main frame.
+  bool is_main_frame;
+
+  // File to write the SkPicture to (write-only). A separate file should be
+  // created for each RenderFrame.
+  mojo_base.mojom.File file;
+};
+
+// Service for capturing a paint preview of a RenderFrame's contents. This
+// includes both the visual contents (as an SkPicture) and hyperlinks
+// for the frame.
+interface PaintPreviewRecorder {
+  // Captures a paint preview of the RenderFrame that receives the request.
+  //
+  // This interface is used for both the main frame and sub frames.
+  // Out-of-process subframes are handled by making requests back to the browser
+  // via the RenderFrameProxy. The browser handles dispatching these requests to
+  // the correct RenderFrame and aggregating all the outputs.
+  //
+  // Returns a status. If |status| == kOk then proto contains a serialized
+  // PaintPreviewFrameProto.
+  CapturePaintPreview(PaintPreviewCaptureParams params) =>
+    (PaintPreviewStatus status,
+     mojo_base.mojom.ReadOnlySharedMemoryRegion? proto);
+};
diff --git a/components/paint_preview/common/paint_preview_tracker.cc b/components/paint_preview/common/paint_preview_tracker.cc
index 32edc94..12a6271 100644
--- a/components/paint_preview/common/paint_preview_tracker.cc
+++ b/components/paint_preview/common/paint_preview_tracker.cc
@@ -44,7 +44,10 @@
 
 }  // namespace
 
-PaintPreviewTracker::PaintPreviewTracker() = default;
+PaintPreviewTracker::PaintPreviewTracker(const base::UnguessableToken& guid,
+                                         int routing_id,
+                                         bool is_main_frame)
+    : guid_(guid), routing_id_(routing_id), is_main_frame_(is_main_frame) {}
 PaintPreviewTracker::~PaintPreviewTracker() = default;
 
 uint32_t PaintPreviewTracker::CreateContentForRemoteFrame(const gfx::Rect& rect,
diff --git a/components/paint_preview/common/paint_preview_tracker.h b/components/paint_preview/common/paint_preview_tracker.h
index 84f2dba4..f1ebd34 100644
--- a/components/paint_preview/common/paint_preview_tracker.h
+++ b/components/paint_preview/common/paint_preview_tracker.h
@@ -23,17 +23,22 @@
 
 namespace paint_preview {
 
-// Tracks metadata for a Paint Preview.
+// Tracks metadata for a Paint Preview. Contains all the data required to
+// produce a PaintPreviewFrameProto.
 class PaintPreviewTracker {
  public:
-  PaintPreviewTracker();
+  PaintPreviewTracker(const base::UnguessableToken& guid,
+                      int routing_id,
+                      bool is_main_frame);
   ~PaintPreviewTracker();
 
-  // Data Collection ----------------------------------------------------------
+  // Getters ------------------------------------------------------------------
 
-  // Use base::UnguessableToken as a GUID that identifies the paint preview.
-  void SetGuid(base::UnguessableToken guid) { guid_ = guid; }
   base::UnguessableToken Guid() const { return guid_; }
+  int RoutingId() const { return routing_id_; }
+  bool IsMainFrame() const { return is_main_frame_; }
+
+  // Data Collection ----------------------------------------------------------
 
   // Creates a placeholder SkPicture for an OOP subframe located at |rect|
   // mapped to the |routing_id| of OOP RenderFrame. Returns the content id of
@@ -56,6 +61,7 @@
   void CustomDataToSkPictureCallback(SkCanvas* canvas, uint32_t content_id);
 
   // Expose internal maps for use in MakeSerialProcs().
+  // NOTE: Cannot be const due to how SkPicture procs work.
   PictureSerializationContext* GetPictureSerializationContext() {
     return &content_id_to_proxy_id_;
   }
@@ -65,7 +71,10 @@
   const std::vector<LinkDataProto>& GetLinks() const { return links_; }
 
  private:
-  base::UnguessableToken guid_;
+  const base::UnguessableToken guid_;
+  const int routing_id_;
+  const bool is_main_frame_;
+
   std::vector<LinkDataProto> links_;
   PictureSerializationContext content_id_to_proxy_id_;
   TypefaceUsageMap typeface_glyph_usage_;
diff --git a/components/paint_preview/common/paint_preview_tracker_unittest.cc b/components/paint_preview/common/paint_preview_tracker_unittest.cc
index 6ec528bd..e2c1d0af 100644
--- a/components/paint_preview/common/paint_preview_tracker_unittest.cc
+++ b/components/paint_preview/common/paint_preview_tracker_unittest.cc
@@ -18,6 +18,8 @@
 
 namespace {
 
+constexpr int32_t kRoutingId = 1;
+
 struct TestContext {
   const gfx::Rect* rect;
   bool was_called;
@@ -25,15 +27,17 @@
 
 }  // namespace
 
-TEST(PaintPreviewTrackerTest, TestGuid) {
+TEST(PaintPreviewTrackerTest, TestGetters) {
   auto token = base::UnguessableToken::Create();
-  PaintPreviewTracker tracker;
-  tracker.SetGuid(token);
+  PaintPreviewTracker tracker(token, kRoutingId, true);
   EXPECT_EQ(tracker.Guid(), token);
+  EXPECT_EQ(tracker.RoutingId(), kRoutingId);
+  EXPECT_TRUE(tracker.IsMainFrame());
 }
 
 TEST(PaintPreviewTrackerTest, TestRemoteFramePlaceholderPicture) {
-  PaintPreviewTracker tracker;
+  PaintPreviewTracker tracker(base::UnguessableToken::Create(), kRoutingId,
+                              true);
   const int kRoutingId = 50;
   gfx::Rect rect(50, 40, 30, 20);
   uint32_t content_id = tracker.CreateContentForRemoteFrame(rect, kRoutingId);
@@ -53,7 +57,8 @@
 }
 
 TEST(PaintPreviewTrackerTest, TestGlyphRunList) {
-  PaintPreviewTracker tracker;
+  PaintPreviewTracker tracker(base::UnguessableToken::Create(), kRoutingId,
+                              true);
   std::string unichars = "abc";
   auto typeface = SkTypeface::MakeDefault();
   SkFont font(typeface);
@@ -70,7 +75,8 @@
 }
 
 TEST(PaintPreviewTrackerTest, TestAnnotateLinks) {
-  PaintPreviewTracker tracker;
+  PaintPreviewTracker tracker(base::UnguessableToken::Create(), kRoutingId,
+                              true);
   const std::string url_1 = "https://www.chromium.org";
   const gfx::Rect rect_1(10, 20, 30, 40);
   tracker.AnnotateLink(url_1, rect_1);
diff --git a/components/paint_preview/common/proto/paint_preview.proto b/components/paint_preview/common/proto/paint_preview.proto
index fb4a2ea..fc1c3c04 100644
--- a/components/paint_preview/common/proto/paint_preview.proto
+++ b/components/paint_preview/common/proto/paint_preview.proto
@@ -33,7 +33,7 @@
 
   // Originates in renderer as Routing ID.
   // Converted to (Process ID || Routing ID) once processed in browser.
-  required int64 id = 3;
+  required uint64 id = 3;
 
   // Boolean indicating if the frame is the main frame.
   required bool is_main_frame = 4;
diff --git a/components/paint_preview/common/test_utils.h b/components/paint_preview/common/test_utils.h
new file mode 100644
index 0000000..2224f06
--- /dev/null
+++ b/components/paint_preview/common/test_utils.h
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAINT_PREVIEW_COMMON_TEST_UTILS_H_
+#define COMPONENTS_PAINT_PREVIEW_COMMON_TEST_UTILS_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+MATCHER_P(EqualsProto, message, "") {
+  std::string expected_serialized, actual_serialized;
+  message.SerializeToString(&expected_serialized);
+  arg.SerializeToString(&actual_serialized);
+  return expected_serialized == actual_serialized;
+}
+
+#endif  // COMPONENTS_PAINT_PREVIEW_COMMON_TEST_UTILS_H_
diff --git a/components/paint_preview/renderer/BUILD.gn b/components/paint_preview/renderer/BUILD.gn
new file mode 100644
index 0000000..e4c33f67
--- /dev/null
+++ b/components/paint_preview/renderer/BUILD.gn
@@ -0,0 +1,58 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/test.gni")
+
+if (!is_ios) {
+  static_library("renderer") {
+    sources = [
+      "paint_preview_recorder_impl.cc",
+      "paint_preview_recorder_impl.h",
+      "paint_preview_recorder_utils.cc",
+      "paint_preview_recorder_utils.h",
+    ]
+
+    deps = [
+      "//base",
+      "//cc/paint",
+      "//content/public/renderer",
+      "//mojo/public/cpp/base",
+      "//third_party/blink/public:blink_headers",
+      "//third_party/blink/public/common",
+    ]
+
+    public_deps = [
+      "//components/paint_preview/common",
+      "//components/paint_preview/common/mojom",
+      "//components/paint_preview/common/proto",
+    ]
+  }
+
+  source_set("unit_tests") {
+    testonly = true
+
+    sources = [
+      "paint_preview_recorder_utils_unittest.cc",
+    ]
+
+    deps = [
+      ":renderer",
+      "//base",
+      "//base/test:test_support",
+      "//cc/paint",
+      "//components/paint_preview/common:test_utils",
+      "//testing/gmock",
+      "//testing/gtest",
+    ]
+  }
+
+  test("paint_preview_renderer_unit_tests") {
+    deps = [
+      ":unit_tests",
+      "//base",
+      "//base/test:test_support",
+      "//components/test:run_all_unittests",
+    ]
+  }
+}
diff --git a/components/paint_preview/renderer/DEPS b/components/paint_preview/renderer/DEPS
new file mode 100644
index 0000000..53de874
--- /dev/null
+++ b/components/paint_preview/renderer/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  "+cc/paint",
+  "+content/public/renderer",
+  "+content/public/test",
+  "+third_party/blink/public",
+]
diff --git a/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc b/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc
new file mode 100644
index 0000000..fd2e004f
--- /dev/null
+++ b/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc
@@ -0,0 +1,117 @@
+// Copyright 2019 The Chromium 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/paint_preview/renderer/paint_preview_recorder_impl.h"
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "components/paint_preview/common/mojom/paint_preview_recorder.mojom.h"
+#include "components/paint_preview/common/proto/paint_preview.pb.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_view.h"
+#include "content/public/test/render_view_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace paint_preview {
+
+namespace {
+
+// Checks that |status| == |expected_status| and loads |region| into |proto| if
+// |expected_status| == kOk. If |expected_status| != kOk |proto| can safely be
+// nullptr.
+void OnCaptureFinished(mojom::PaintPreviewStatus expected_status,
+                       PaintPreviewFrameProto* proto,
+                       mojom::PaintPreviewStatus status,
+                       base::ReadOnlySharedMemoryRegion region) {
+  EXPECT_EQ(status, expected_status);
+  if (expected_status == mojom::PaintPreviewStatus::kOk) {
+    EXPECT_TRUE(region.IsValid());
+    auto mapping = region.Map();
+    EXPECT_TRUE(proto->ParseFromArray(mapping.memory(), mapping.size()));
+  }
+}
+
+}  // namespace
+
+class PaintPreviewRecorderRenderViewTest : public content::RenderViewTest {
+ public:
+  PaintPreviewRecorderRenderViewTest() {}
+  ~PaintPreviewRecorderRenderViewTest() override {}
+
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    RenderViewTest::SetUp();
+  }
+
+  content::RenderFrame* GetFrame() { return view_->GetMainRenderFrame(); }
+
+  base::FilePath MakeTestFilePath(const std::string& filename) {
+    return temp_dir_.GetPath().AppendASCII(filename);
+  }
+
+ private:
+  base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrame) {
+  LoadHTML(
+      "<body style='min-height:1000px;'>"
+      "  <div style='width: 100px; height: 100px; "
+      "              background-color: #000000'>&nbsp;</div>"
+      "  <p><a href='https://www.foo.com'>Foo</a></p>"
+      "</body>");
+  base::FilePath skp_path = MakeTestFilePath("test.skp");
+
+  mojom::PaintPreviewCaptureParamsPtr params =
+      mojom::PaintPreviewCaptureParams::New();
+  auto token = base::UnguessableToken::Create();
+  params->guid = token;
+  params->clip_rect = gfx::Rect();
+  params->is_main_frame = true;
+  base::File skp_file(skp_path,
+                      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  params->file = std::move(skp_file);
+
+  PaintPreviewFrameProto out_proto;
+  content::RenderFrame* frame = GetFrame();
+  int routing_id = frame->GetRoutingID();
+  PaintPreviewRecorderImpl paint_preview_recorder(frame);
+  paint_preview_recorder.CapturePaintPreview(
+      std::move(params),
+      base::BindOnce(&OnCaptureFinished, mojom::PaintPreviewStatus::kOk,
+                     &out_proto));
+  // Here id() is just the routing ID.
+  EXPECT_EQ(static_cast<int>(out_proto.id()), routing_id);
+  EXPECT_TRUE(out_proto.is_main_frame());
+  EXPECT_EQ(out_proto.unguessable_token_low(), token.GetLowForSerialization());
+  EXPECT_EQ(out_proto.unguessable_token_high(),
+            token.GetHighForSerialization());
+  EXPECT_EQ(out_proto.content_id_proxy_id_map_size(), 0);
+
+  // NOTE: should be non-zero once the Blink implementation is hooked up.
+  EXPECT_EQ(out_proto.links_size(), 0);
+}
+
+TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureInvalidFile) {
+  LoadHTML("<body></body>");
+
+  mojom::PaintPreviewCaptureParamsPtr params =
+      mojom::PaintPreviewCaptureParams::New();
+  auto token = base::UnguessableToken::Create();
+  params->guid = token;
+  params->clip_rect = gfx::Rect();
+  params->is_main_frame = true;
+  base::File skp_file;  // Invalid file.
+  params->file = std::move(skp_file);
+
+  content::RenderFrame* frame = GetFrame();
+  PaintPreviewRecorderImpl paint_preview_recorder(frame);
+  paint_preview_recorder.CapturePaintPreview(
+      std::move(params),
+      base::BindOnce(&OnCaptureFinished,
+                     mojom::PaintPreviewStatus::kCaptureFailed, nullptr));
+}
+
+}  // namespace paint_preview
diff --git a/components/paint_preview/renderer/paint_preview_recorder_impl.cc b/components/paint_preview/renderer/paint_preview_recorder_impl.cc
new file mode 100644
index 0000000..36eabb28
--- /dev/null
+++ b/components/paint_preview/renderer/paint_preview_recorder_impl.cc
@@ -0,0 +1,120 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/paint_preview/renderer/paint_preview_recorder_impl.h"
+
+#include <utility>
+
+#include "base/auto_reset.h"
+#include "base/memory/shared_memory.h"
+#include "base/task_runner.h"
+#include "cc/paint/paint_record.h"
+#include "cc/paint/paint_recorder.h"
+#include "components/paint_preview/renderer/paint_preview_recorder_utils.h"
+#include "content/public/renderer/render_frame.h"
+#include "mojo/public/cpp/base/shared_memory_utils.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+
+namespace paint_preview {
+
+namespace {
+
+mojom::PaintPreviewStatus FinishRecording(
+    sk_sp<const cc::PaintRecord> recording,
+    const gfx::Rect& bounds,
+    PaintPreviewTracker* tracker,
+    base::File skp_file,
+    base::ReadOnlySharedMemoryRegion* region) {
+  ParseGlyphs(recording.get(), tracker);
+  if (!SerializeAsSkPicture(recording, tracker, bounds, std::move(skp_file)))
+    return mojom::PaintPreviewStatus::kCaptureFailed;
+
+  if (!BuildAndSerializeProto(tracker, region))
+    return mojom::PaintPreviewStatus::kProtoSerializationFailed;
+  return mojom::PaintPreviewStatus::kOk;
+}
+
+}  // namespace
+
+PaintPreviewRecorderImpl::PaintPreviewRecorderImpl(
+    content::RenderFrame* render_frame)
+    : content::RenderFrameObserver(render_frame),
+      is_painting_preview_(false),
+      is_main_frame_(render_frame->IsMainFrame()),
+      routing_id_(render_frame->GetRoutingID()) {
+  render_frame->GetAssociatedInterfaceRegistry()->AddInterface(
+      base::BindRepeating(&PaintPreviewRecorderImpl::BindPaintPreviewRecorder,
+                          weak_ptr_factory_.GetWeakPtr()));
+}
+
+PaintPreviewRecorderImpl::~PaintPreviewRecorderImpl() = default;
+
+void PaintPreviewRecorderImpl::CapturePaintPreview(
+    mojom::PaintPreviewCaptureParamsPtr params,
+    CapturePaintPreviewCallback callback) {
+  mojom::PaintPreviewStatus status = mojom::PaintPreviewStatus::kOk;
+  base::ReadOnlySharedMemoryRegion region;
+  // This should not be called recursively or multiple times while unfinished
+  // (Blink can only run one capture per RenderFrame at a time).
+  DCHECK(!is_painting_preview_);
+  // DCHECK, but fallback safely as it is difficult to reason about whether this
+  // might happen due to it being tied to a RenderFrame rather than
+  // RenderWidget and we don't want to crash the renderer as this is
+  // recoverable.
+  if (is_painting_preview_) {
+    status = mojom::PaintPreviewStatus::kAlreadyCapturing;
+    std::move(callback).Run(status, std::move(region));
+    return;
+  }
+  base::AutoReset<bool>(&is_painting_preview_, true);
+
+  CapturePaintPreviewInternal(params, &region, &status);
+  std::move(callback).Run(status, std::move(region));
+}
+
+void PaintPreviewRecorderImpl::OnDestruct() {
+  paint_preview_recorder_receiver_.reset();
+  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+}
+
+void PaintPreviewRecorderImpl::BindPaintPreviewRecorder(
+    mojo::PendingAssociatedReceiver<mojom::PaintPreviewRecorder> receiver) {
+  paint_preview_recorder_receiver_.Bind(std::move(receiver));
+}
+
+void PaintPreviewRecorderImpl::CapturePaintPreviewInternal(
+    const mojom::PaintPreviewCaptureParamsPtr& params,
+    base::ReadOnlySharedMemoryRegion* region,
+    mojom::PaintPreviewStatus* status) {
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+  // Warm up paint for an out-of-lifecycle paint phase.
+  frame->DispatchBeforePrintEvent();
+
+  DCHECK_EQ(is_main_frame_, params->is_main_frame);
+  gfx::Rect bounds;
+  if (is_main_frame_ || params->clip_rect == gfx::Rect(0, 0, 0, 0)) {
+    auto size = frame->DocumentSize();
+    bounds = gfx::Rect(0, 0, size.width, size.height);
+  } else {
+    bounds = gfx::Rect(params->clip_rect.size());
+  }
+
+  cc::PaintRecorder recorder;
+  recorder.beginRecording(bounds.width(), bounds.height());
+  PaintPreviewTracker tracker(params->guid, routing_id_, is_main_frame_);
+  // TODO(crbug/1008885): Create a method on |canvas| to inject |tracker_| to
+  // propagate to graphics contexts and inner canvases
+  // TODO(crbug/1001109): Create a method on |frame| to execute the capture
+  // within Blink.
+
+  // Restore to before out-of-lifecycle paint phase.
+  frame->DispatchAfterPrintEvent();
+
+  // TODO(crbug/1011896): Determine if making this async would be beneficial.
+  *status = FinishRecording(recorder.finishRecordingAsPicture(), bounds,
+                            &tracker, std::move(params->file), region);
+}
+
+}  // namespace paint_preview
diff --git a/components/paint_preview/renderer/paint_preview_recorder_impl.h b/components/paint_preview/renderer/paint_preview_recorder_impl.h
new file mode 100644
index 0000000..2bb2d0a
--- /dev/null
+++ b/components/paint_preview/renderer/paint_preview_recorder_impl.h
@@ -0,0 +1,68 @@
+// Copyright 2019 The Chromium 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_PAINT_PREVIEW_RENDERER_PAINT_PREVIEW_RECORDER_IMPL_H_
+#define COMPONENTS_PAINT_PREVIEW_RENDERER_PAINT_PREVIEW_RECORDER_IMPL_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/paint_preview/common/mojom/paint_preview_recorder.mojom.h"
+#include "components/paint_preview/common/paint_preview_tracker.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+
+namespace base {
+class ReadOnlySharedMemoryRegion;
+}  // namespace base
+
+namespace content {
+class RenderFrame;
+}  // namespace content
+
+namespace paint_preview {
+
+// PaintPreviewRecorderImpl handles the majority of the grunt work for capturing
+// a paint preview of a RenderFrame.
+class PaintPreviewRecorderImpl : public content::RenderFrameObserver,
+                                 mojom::PaintPreviewRecorder {
+ public:
+  PaintPreviewRecorderImpl(content::RenderFrame* render_frame);
+  ~PaintPreviewRecorderImpl() override;
+
+  void CapturePaintPreview(mojom::PaintPreviewCaptureParamsPtr params,
+                           CapturePaintPreviewCallback callback) override;
+
+ private:
+  // RenderFrameObserver implementation --------------------------------------
+
+  void OnDestruct() override;
+
+  // Helpers ------------------------------------------------------------------
+
+  void BindPaintPreviewRecorder(
+      mojo::PendingAssociatedReceiver<mojom::PaintPreviewRecorder> receiver);
+
+  // Handles the bulk of the capture.
+  void CapturePaintPreviewInternal(
+      const mojom::PaintPreviewCaptureParamsPtr& params,
+      base::ReadOnlySharedMemoryRegion* region,
+      mojom::PaintPreviewStatus* status);
+
+  bool is_painting_preview_;
+  const bool is_main_frame_;
+  const int32_t routing_id_;
+  mojo::AssociatedReceiver<mojom::PaintPreviewRecorder>
+      paint_preview_recorder_receiver_{this};
+
+  base::WeakPtrFactory<PaintPreviewRecorderImpl> weak_ptr_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(PaintPreviewRecorderImpl);
+};
+
+}  // namespace paint_preview
+
+#endif  // COMPONENTS_PAINT_PREVIEW_RENDERER_PAINT_PREVIEW_RECORDER_IMPL_H_
diff --git a/components/paint_preview/renderer/paint_preview_recorder_utils.cc b/components/paint_preview/renderer/paint_preview_recorder_utils.cc
new file mode 100644
index 0000000..0cac96f
--- /dev/null
+++ b/components/paint_preview/renderer/paint_preview_recorder_utils.cc
@@ -0,0 +1,89 @@
+// Copyright 2019 The Chromium 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/paint_preview/renderer/paint_preview_recorder_utils.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/shared_memory.h"
+#include "components/paint_preview/common/file_stream.h"
+#include "components/paint_preview/common/paint_preview_tracker.h"
+#include "components/paint_preview/common/proto/paint_preview.pb.h"
+#include "mojo/public/cpp/base/shared_memory_utils.h"
+
+namespace paint_preview {
+
+void ParseGlyphs(const cc::PaintOpBuffer* buffer,
+                 PaintPreviewTracker* tracker) {
+  for (cc::PaintOpBuffer::Iterator it(buffer); it; ++it) {
+    if (it->GetType() == cc::PaintOpType::DrawTextBlob) {
+      auto* text_blob_op = static_cast<cc::DrawTextBlobOp*>(*it);
+      tracker->AddGlyphs(text_blob_op->blob.get());
+    } else if (it->GetType() == cc::PaintOpType::DrawRecord) {
+      // Recurse into nested records if they contain text blobs (equivalent to
+      // nested SkPictures).
+      auto* record_op = static_cast<cc::DrawRecordOp*>(*it);
+      if (record_op->HasText())
+        ParseGlyphs(record_op->record.get(), tracker);
+    }
+  }
+}
+
+bool SerializeAsSkPicture(sk_sp<const cc::PaintRecord> record,
+                          PaintPreviewTracker* tracker,
+                          const gfx::Rect& dimensions,
+                          base::File file) {
+  if (!file.IsValid())
+    return false;
+
+  // base::Unretained is safe as |tracker| outlives the usage of
+  // |custom_callback|.
+  cc::PlaybackParams::CustomDataRasterCallback custom_callback =
+      base::BindRepeating(&PaintPreviewTracker::CustomDataToSkPictureCallback,
+                          base::Unretained(tracker));
+  auto skp = ToSkPicture(
+      record, SkRect::MakeWH(dimensions.width(), dimensions.height()), nullptr,
+      custom_callback);
+  if (!skp)
+    return false;
+
+  TypefaceSerializationContext typeface_context(tracker->GetTypefaceUsageMap());
+  auto serial_procs = MakeSerialProcs(tracker->GetPictureSerializationContext(),
+                                      &typeface_context);
+  FileWStream stream(std::move(file));
+  skp->serialize(&stream, &serial_procs);
+  stream.flush();
+  stream.Close();
+  return true;
+}
+
+bool BuildAndSerializeProto(PaintPreviewTracker* tracker,
+                            base::ReadOnlySharedMemoryRegion* region) {
+  PaintPreviewFrameProto proto;
+  proto.set_unguessable_token_high(tracker->Guid().GetHighForSerialization());
+  proto.set_unguessable_token_low(tracker->Guid().GetLowForSerialization());
+  proto.set_id(tracker->RoutingId());
+  proto.set_is_main_frame(tracker->IsMainFrame());
+
+  auto* proto_content_proxy_map = proto.mutable_content_id_proxy_id_map();
+  for (const auto& id_pair : *(tracker->GetPictureSerializationContext()))
+    proto_content_proxy_map->insert(
+        google::protobuf::MapPair<uint32_t, int64_t>(id_pair.first,
+                                                     id_pair.second));
+  for (const auto& link : tracker->GetLinks())
+    *proto.add_links() = link;
+
+  base::MappedReadOnlyRegion region_mapping =
+      mojo::CreateReadOnlySharedMemoryRegion(proto.ByteSizeLong());
+  if (!region_mapping.IsValid())
+    return false;
+  bool success = proto.SerializeToArray(region_mapping.mapping.memory(),
+                                        proto.ByteSizeLong());
+  if (success)
+    *region = std::move(region_mapping.region);
+  return success;
+}
+
+}  // namespace paint_preview
diff --git a/components/paint_preview/renderer/paint_preview_recorder_utils.h b/components/paint_preview/renderer/paint_preview_recorder_utils.h
new file mode 100644
index 0000000..2b28511
--- /dev/null
+++ b/components/paint_preview/renderer/paint_preview_recorder_utils.h
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAINT_PREVIEW_RENDERER_PAINT_PREVIEW_RECORDER_UTILS_H_
+#define COMPONENTS_PAINT_PREVIEW_RENDERER_PAINT_PREVIEW_RECORDER_UTILS_H_
+
+#include "base/files/file.h"
+#include "cc/paint/paint_record.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "ui/gfx/geometry/rect.h"
+
+// These utilities are used by the PaintPreviewRecorderImpl. They are separate
+// for testing purposes and to enforce restrictions caused by the lifetime of
+// PaintPreviewServiceImpl being tied to it's associated RenderFrame.
+
+namespace base {
+class ReadOnlySharedMemoryRegion;
+}  // namespace base
+
+namespace paint_preview {
+
+class PaintPreviewTracker;
+
+// Walks |buffer| to extract all the glyphs from its text blobs and writes
+// them to |tracker|.
+void ParseGlyphs(const cc::PaintOpBuffer* buffer, PaintPreviewTracker* tracker);
+
+// Serializes |record| to |file| as an SkPicture of size |dimensions|. |tracker|
+// supplies metadata required during serialization.
+bool SerializeAsSkPicture(sk_sp<const cc::PaintRecord> record,
+                          PaintPreviewTracker* tracker,
+                          const gfx::Rect& dimensions,
+                          base::File file);
+
+// Builds and serializes a proto to |region| using the data contained in
+// |tracker|. Returns true on success.
+// NOTE: |tracker| is effectively const here despite being passed by pointer.
+bool BuildAndSerializeProto(PaintPreviewTracker* tracker,
+                            base::ReadOnlySharedMemoryRegion* region);
+
+}  // namespace paint_preview
+
+#endif  // COMPONENTS_PAINT_PREVIEW_RENDERER_PAINT_PREVIEW_RECORDER_UTILS_H_
diff --git a/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc b/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc
new file mode 100644
index 0000000..4cbdfbb5
--- /dev/null
+++ b/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc
@@ -0,0 +1,161 @@
+// Copyright 2019 The Chromium 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/paint_preview/renderer/paint_preview_recorder_utils.h"
+
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/files/file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/unguessable_token.h"
+#include "cc/paint/paint_canvas.h"
+#include "cc/paint/paint_flags.h"
+#include "cc/paint/paint_recorder.h"
+#include "components/paint_preview/common/file_stream.h"
+#include "components/paint_preview/common/paint_preview_tracker.h"
+#include "components/paint_preview/common/proto/paint_preview.pb.h"
+#include "components/paint_preview/common/serial_utils.h"
+#include "components/paint_preview/common/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkFont.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+namespace paint_preview {
+
+namespace {
+
+constexpr int32_t kRoutingId = 1;
+
+}  // namespace
+
+TEST(PaintPreviewServiceUtilsTest, TestParseGlyphs) {
+  auto typeface = SkTypeface::MakeDefault();
+  SkFont font(typeface);
+  std::string unichars_1 = "abc";
+  std::string unichars_2 = "efg";
+  auto blob_1 = SkTextBlob::MakeFromString(unichars_1.c_str(), font);
+  auto blob_2 = SkTextBlob::MakeFromString(unichars_2.c_str(), font);
+
+  cc::PaintFlags flags;
+  cc::PaintRecorder outer_recorder;
+  cc::PaintCanvas* outer_canvas = outer_recorder.beginRecording(100, 100);
+  outer_canvas->drawTextBlob(blob_1, 10, 10, flags);
+  cc::PaintRecorder inner_recorder;
+  cc::PaintCanvas* inner_canvas = inner_recorder.beginRecording(50, 50);
+  inner_canvas->drawTextBlob(blob_2, 15, 20, flags);
+  outer_canvas->drawPicture(inner_recorder.finishRecordingAsPicture());
+  auto record = outer_recorder.finishRecordingAsPicture();
+
+  PaintPreviewTracker tracker(base::UnguessableToken::Create(), kRoutingId,
+                              true);
+  ParseGlyphs(record.get(), &tracker);
+  auto* usage_map = tracker.GetTypefaceUsageMap();
+  EXPECT_TRUE(usage_map->count(typeface->uniqueID()));
+  EXPECT_TRUE(
+      (*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('a')));
+  EXPECT_TRUE(
+      (*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('b')));
+  EXPECT_TRUE(
+      (*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('c')));
+  EXPECT_TRUE(
+      (*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('e')));
+  EXPECT_TRUE(
+      (*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('f')));
+  EXPECT_TRUE(
+      (*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('g')));
+}
+
+TEST(PaintPreviewServiceUtilsTest, TestSerializeAsSkPicture) {
+  PaintPreviewTracker tracker(base::UnguessableToken::Create(), kRoutingId,
+                              true);
+
+  gfx::Rect dimensions(100, 100);
+  cc::PaintRecorder recorder;
+  cc::PaintCanvas* canvas =
+      recorder.beginRecording(dimensions.width(), dimensions.width());
+  cc::PaintFlags flags;
+  canvas->drawRect(SkRect::MakeWH(dimensions.width(), dimensions.height()),
+                   flags);
+
+  base::flat_set<uint32_t> ctx;
+  uint32_t content_id =
+      tracker.CreateContentForRemoteFrame(gfx::Rect(10, 10), kRoutingId + 1);
+  canvas->recordCustomData(content_id);
+  ctx.insert(content_id);
+  content_id =
+      tracker.CreateContentForRemoteFrame(gfx::Rect(20, 20), kRoutingId + 2);
+  canvas->recordCustomData(content_id);
+  ctx.insert(content_id);
+
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath file_path = temp_dir.GetPath().AppendASCII("test_file");
+  base::File write_file(
+      file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+
+  auto record = recorder.finishRecordingAsPicture();
+  EXPECT_TRUE(SerializeAsSkPicture(record, &tracker, dimensions,
+                                   std::move(write_file)));
+  base::File read_file(file_path, base::File::FLAG_OPEN |
+                                      base::File::FLAG_READ |
+                                      base::File::FLAG_EXCLUSIVE_READ);
+  FileRStream rstream(std::move(read_file));
+  SkDeserialProcs procs;
+  procs.fPictureProc = [](const void* data, size_t length, void* ctx) {
+    uint32_t content_id;
+    if (length < sizeof(content_id))
+      return MakeEmptyPicture();
+    memcpy(&content_id, data, sizeof(content_id));
+    auto* context = reinterpret_cast<base::flat_set<uint32_t>*>(ctx);
+    EXPECT_TRUE(context->count(content_id));
+    context->erase(content_id);
+    return MakeEmptyPicture();
+  };
+  procs.fPictureCtx = &ctx;
+  SkPicture::MakeFromStream(&rstream, &procs);
+  EXPECT_TRUE(ctx.empty());
+}
+
+TEST(PaintPreviewServiceUtilsTest, TestBuildAndSerializeProto) {
+  auto token = base::UnguessableToken::Create();
+  PaintPreviewTracker tracker(token, kRoutingId, true);
+  tracker.AnnotateLink("www.google.com", gfx::Rect(1, 2, 3, 4));
+  tracker.AnnotateLink("www.chromium.org", gfx::Rect(10, 20, 10, 20));
+  tracker.CreateContentForRemoteFrame(gfx::Rect(1, 1, 1, 1), kRoutingId + 1);
+  tracker.CreateContentForRemoteFrame(gfx::Rect(1, 2, 4, 8), kRoutingId + 2);
+
+  base::ReadOnlySharedMemoryRegion region;
+  EXPECT_TRUE(BuildAndSerializeProto(&tracker, &region));
+  PaintPreviewFrameProto proto;
+  EXPECT_TRUE(region.IsValid());
+  auto mapping = region.Map();
+  EXPECT_TRUE(mapping.IsValid());
+  EXPECT_TRUE(proto.ParseFromArray(mapping.memory(), mapping.size()));
+
+  EXPECT_TRUE(proto.is_main_frame());
+  EXPECT_EQ(static_cast<int>(proto.id()), kRoutingId);
+  EXPECT_EQ(proto.unguessable_token_low(),
+            tracker.Guid().GetLowForSerialization());
+  EXPECT_EQ(proto.unguessable_token_high(),
+            tracker.Guid().GetHighForSerialization());
+  EXPECT_EQ(proto.links_size(), 2);
+  EXPECT_EQ(static_cast<size_t>(proto.links_size()), tracker.GetLinks().size());
+  for (int i = 0; i < proto.links_size(); ++i)
+    EXPECT_THAT(proto.links(i), EqualsProto(tracker.GetLinks()[i]));
+  auto* content_map = tracker.GetPictureSerializationContext();
+  for (const auto& id_pair : proto.content_id_proxy_id_map()) {
+    auto it = content_map->find(id_pair.first);
+    EXPECT_NE(it, content_map->end());
+    EXPECT_EQ(id_pair.first, it->first);
+    EXPECT_EQ(id_pair.second, it->second);
+  }
+}
+
+}  // namespace paint_preview
diff --git a/components/password_manager/core/browser/form_parsing/fuzzer/form_data_producer.cc b/components/password_manager/core/browser/form_parsing/fuzzer/form_data_producer.cc
index 47a64f3f..1566f40 100644
--- a/components/password_manager/core/browser/form_parsing/fuzzer/form_data_producer.cc
+++ b/components/password_manager/core/browser/form_parsing/fuzzer/form_data_producer.cc
@@ -118,7 +118,8 @@
   if (predictions) {
     predictions->driver_id = static_cast<int>(accessor->ConsumeNumber(32));
     predictions->form_signature =
-        static_cast<uint64_t>(accessor->ConsumeNumber(64));
+        (static_cast<uint64_t>(accessor->ConsumeNumber(32)) << 32) +
+        accessor->ConsumeNumber(32);
   }
 
   // And finally do the same for all the fields.
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index 03dcae0..b8d868b 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -22,6 +22,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/pickle.h"
 #include "base/stl_util.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -222,34 +223,22 @@
                             DATABASE_INIT_ERROR_COUNT);
 }
 
-// UMA_* macros assume that the name never changes. This is a helper function
-// where this assumption doesn't hold.
-void LogDynamicUMAStat(const std::string& name,
-                       int sample,
-                       int min,
-                       int max,
-                       int bucket_count) {
-  base::HistogramBase* counter = base::Histogram::FactoryGet(
-      name, min, max, bucket_count,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  counter->Add(sample);
-}
-
 void LogAccountStat(const std::string& name, int sample) {
-  LogDynamicUMAStat(name, sample, 0, 32, 6);
+  base::UmaHistogramCustomCounts(name, sample, 0, 32, 6);
 }
 
 void LogAccountStatHiRes(const std::string& name, int sample) {
-  LogDynamicUMAStat(name, sample, 0, 1000, 100);
+  base::UmaHistogramCustomCounts(name, sample, 0, 1000, 100);
 }
 
 void LogTimesUsedStat(const std::string& name, int sample) {
-  LogDynamicUMAStat(name, sample, 0, 100, 10);
+  base::UmaHistogramCustomCounts(name, sample, 0, 100, 10);
 }
 
 void LogNumberOfAccountsForScheme(const std::string& scheme, int sample) {
-  LogDynamicUMAStat("PasswordManager.TotalAccountsHiRes.WithScheme." + scheme,
-                    sample, 1, 1000, 100);
+  base::UmaHistogramCustomCounts(
+      "PasswordManager.TotalAccountsHiRes.WithScheme." + scheme, sample, 1,
+      1000, 100);
 }
 
 void LogNumberOfAccountsReusingPassword(const std::string& suffix,
@@ -257,8 +246,9 @@
                                         HistogramSize histogram_size) {
   int max = histogram_size == HistogramSize::LARGE ? 500 : 100;
   int bucket_count = histogram_size == HistogramSize::LARGE ? 50 : 20;
-  LogDynamicUMAStat("PasswordManager.AccountsReusingPassword." + suffix, sample,
-                    1, max, bucket_count);
+  base::UmaHistogramCustomCounts(
+      "PasswordManager.AccountsReusingPassword." + suffix, sample, 1, max,
+      bucket_count);
 }
 
 // Records password reuse metrics given the |signon_realms| corresponding to a
@@ -811,54 +801,67 @@
   if (!s.is_valid())
     return;
 
-  std::string custom_passphrase = custom_passphrase_sync_enabled
-                                      ? "WithCustomPassphrase"
-                                      : "WithoutCustomPassphrase";
+  // Need to stay in sync with the PasswordGenerated suffix in histograms.xml.
+  static constexpr base::StringPiece kAutoGeneratedSuffix = ".AutoGenerated";
+  static constexpr base::StringPiece kUserCreatedSuffix = ".UserCreated";
+  static constexpr base::StringPiece kOverallSuffix = ".Overall";
+
+  // Need to stay in sync with the PasswordCustomPassphrase suffix in
+  // histograms.xml.
+  base::StringPiece custom_passphrase_suffix = custom_passphrase_sync_enabled
+                                                   ? ".WithCustomPassphrase"
+                                                   : ".WithoutCustomPassphrase";
 
   int total_user_created_accounts = 0;
   int total_generated_accounts = 0;
   int blacklisted_sites = 0;
   while (s.Step()) {
-    PasswordForm::Type password_type =
-        static_cast<PasswordForm::Type>(s.ColumnInt(1));
+    auto password_type = static_cast<PasswordForm::Type>(s.ColumnInt(1));
     int blacklisted = s.ColumnInt(2);
     int accounts_per_site = s.ColumnInt(3);
     if (blacklisted) {
       ++blacklisted_sites;
-    } else if (password_type == PasswordForm::Type::kGenerated) {
+      continue;
+    }
+
+    static constexpr base::StringPiece kAccountsPerSite =
+        "PasswordManager.AccountsPerSiteHiRes";
+
+    if (password_type == PasswordForm::Type::kGenerated) {
       total_generated_accounts += accounts_per_site;
-      LogAccountStat(
-          base::StringPrintf("PasswordManager.AccountsPerSite.AutoGenerated.%s",
-                             custom_passphrase.c_str()),
-          accounts_per_site);
+      LogAccountStatHiRes(base::StrCat({kAccountsPerSite, kAutoGeneratedSuffix,
+                                        custom_passphrase_suffix}),
+                          accounts_per_site);
     } else {
       total_user_created_accounts += accounts_per_site;
-      LogAccountStat(
-          base::StringPrintf("PasswordManager.AccountsPerSite.UserCreated.%s",
-                             custom_passphrase.c_str()),
-          accounts_per_site);
+      LogAccountStatHiRes(base::StrCat({kAccountsPerSite, kUserCreatedSuffix,
+                                        custom_passphrase_suffix}),
+                          accounts_per_site);
     }
-    // PasswordManager.AccountsPerSite.Overall is not recorded here even though
-    // the histogram suffixes suggest it. We may add it if we need it.
+
+    LogAccountStatHiRes(base::StrCat({kAccountsPerSite, kOverallSuffix,
+                                      custom_passphrase_suffix}),
+                        accounts_per_site);
   }
-  LogAccountStatHiRes(
-      base::StringPrintf(
-          "PasswordManager.TotalAccountsHiRes.ByType.UserCreated.%s",
-          custom_passphrase.c_str()),
-      total_user_created_accounts);
-  LogAccountStatHiRes(
-      base::StringPrintf(
-          "PasswordManager.TotalAccountsHiRes.ByType.AutoGenerated.%s",
-          custom_passphrase.c_str()),
-      total_generated_accounts);
-  LogAccountStatHiRes(
-      base::StringPrintf("PasswordManager.TotalAccountsHiRes.ByType.Overall.%s",
-                         custom_passphrase.c_str()),
-      total_user_created_accounts + total_generated_accounts);
-  LogAccountStatHiRes(
-      base::StringPrintf("PasswordManager.BlacklistedSitesHiRes.%s",
-                         custom_passphrase.c_str()),
-      blacklisted_sites);
+
+  static constexpr base::StringPiece kTotalAccountsByType =
+      "PasswordManager.TotalAccountsHiRes.ByType";
+
+  LogAccountStatHiRes(base::StrCat({kTotalAccountsByType, kUserCreatedSuffix,
+                                    custom_passphrase_suffix}),
+                      total_user_created_accounts);
+
+  LogAccountStatHiRes(base::StrCat({kTotalAccountsByType, kAutoGeneratedSuffix,
+                                    custom_passphrase_suffix}),
+                      total_generated_accounts);
+
+  LogAccountStatHiRes(base::StrCat({kTotalAccountsByType, kOverallSuffix,
+                                    custom_passphrase_suffix}),
+                      total_user_created_accounts + total_generated_accounts);
+
+  LogAccountStatHiRes(base::StrCat({"PasswordManager.BlacklistedSitesHiRes",
+                                    custom_passphrase_suffix}),
+                      blacklisted_sites);
 
   sql::Statement usage_statement(db_.GetCachedStatement(
       SQL_FROM_HERE, "SELECT password_type, times_used FROM logins"));
@@ -867,22 +870,24 @@
     return;
 
   while (usage_statement.Step()) {
-    PasswordForm::Type type =
-        static_cast<PasswordForm::Type>(usage_statement.ColumnInt(0));
+    auto type = static_cast<PasswordForm::Type>(usage_statement.ColumnInt(0));
+    const int times_used = usage_statement.ColumnInt(1);
+
+    static constexpr base::StringPiece kTimesPasswordUsed =
+        "PasswordManager.TimesPasswordUsed";
 
     if (type == PasswordForm::Type::kGenerated) {
-      LogTimesUsedStat(base::StringPrintf(
-                           "PasswordManager.TimesPasswordUsed.AutoGenerated.%s",
-                           custom_passphrase.c_str()),
-                       usage_statement.ColumnInt(1));
+      LogTimesUsedStat(base::StrCat({kTimesPasswordUsed, kAutoGeneratedSuffix,
+                                     custom_passphrase_suffix}),
+                       times_used);
     } else {
-      LogTimesUsedStat(
-          base::StringPrintf("PasswordManager.TimesPasswordUsed.UserCreated.%s",
-                             custom_passphrase.c_str()),
-          usage_statement.ColumnInt(1));
+      LogTimesUsedStat(base::StrCat({kTimesPasswordUsed, kUserCreatedSuffix,
+                                     custom_passphrase_suffix}),
+                       times_used);
     }
-    // PasswordManager.TimesPasswordUsed.Overall is not implemented even though
-    // the histogram suffixes forsee it. We can add it if necessary.
+    LogTimesUsedStat(base::StrCat({kTimesPasswordUsed, kOverallSuffix,
+                                   custom_passphrase_suffix}),
+                     times_used);
   }
 
   bool syncing_account_saved = false;
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index 75705fd..aed9a70 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -1475,28 +1475,41 @@
   db().ReportMetrics("", false);
 
   histogram_tester.ExpectUniqueSample(
-      "PasswordManager.TotalAccountsHiRes.ByType.UserCreated."
+      "PasswordManager.AccountsPerSiteHiRes.AutoGenerated."
       "WithoutCustomPassphrase",
-      7, 1);
+      1, 2);
+
   histogram_tester.ExpectBucketCount(
-      "PasswordManager.AccountsPerSite.UserCreated.WithoutCustomPassphrase", 1,
-      3);
+      "PasswordManager.AccountsPerSiteHiRes.UserCreated."
+      "WithoutCustomPassphrase",
+      1, 3);
   histogram_tester.ExpectBucketCount(
-      "PasswordManager.AccountsPerSite.UserCreated.WithoutCustomPassphrase", 2,
+      "PasswordManager.AccountsPerSiteHiRes.UserCreated."
+      "WithoutCustomPassphrase",
+      2, 2);
+
+  histogram_tester.ExpectBucketCount(
+      "PasswordManager.AccountsPerSiteHiRes.Overall.WithoutCustomPassphrase", 1,
+      5);
+  histogram_tester.ExpectBucketCount(
+      "PasswordManager.AccountsPerSiteHiRes.Overall.WithoutCustomPassphrase", 2,
       2);
-  histogram_tester.ExpectBucketCount(
-      "PasswordManager.TimesPasswordUsed.UserCreated.WithoutCustomPassphrase",
-      0, 1);
-  histogram_tester.ExpectBucketCount(
-      "PasswordManager.TimesPasswordUsed.UserCreated.WithoutCustomPassphrase",
-      1, 1);
-  histogram_tester.ExpectBucketCount(
-      "PasswordManager.TimesPasswordUsed.UserCreated.WithoutCustomPassphrase",
-      3, 1);
+
   histogram_tester.ExpectUniqueSample(
       "PasswordManager.TotalAccountsHiRes.ByType.AutoGenerated."
       "WithoutCustomPassphrase",
       2, 1);
+
+  histogram_tester.ExpectUniqueSample(
+      "PasswordManager.TotalAccountsHiRes.ByType.UserCreated."
+      "WithoutCustomPassphrase",
+      7, 1);
+
+  histogram_tester.ExpectUniqueSample(
+      "PasswordManager.TotalAccountsHiRes.ByType.Overall."
+      "WithoutCustomPassphrase",
+      9, 1);
+
   histogram_tester.ExpectUniqueSample(
       "PasswordManager.TotalAccountsHiRes.WithScheme.Android", 2, 1);
   histogram_tester.ExpectUniqueSample(
@@ -1507,15 +1520,38 @@
       "PasswordManager.TotalAccountsHiRes.WithScheme.Https", 1, 1);
   histogram_tester.ExpectUniqueSample(
       "PasswordManager.TotalAccountsHiRes.WithScheme.Other", 0, 1);
-  histogram_tester.ExpectUniqueSample(
-      "PasswordManager.AccountsPerSite.AutoGenerated.WithoutCustomPassphrase",
-      1, 2);
+
   histogram_tester.ExpectBucketCount(
       "PasswordManager.TimesPasswordUsed.AutoGenerated.WithoutCustomPassphrase",
       2, 1);
   histogram_tester.ExpectBucketCount(
       "PasswordManager.TimesPasswordUsed.AutoGenerated.WithoutCustomPassphrase",
       4, 1);
+
+  histogram_tester.ExpectBucketCount(
+      "PasswordManager.TimesPasswordUsed.UserCreated.WithoutCustomPassphrase",
+      0, 1);
+  histogram_tester.ExpectBucketCount(
+      "PasswordManager.TimesPasswordUsed.UserCreated.WithoutCustomPassphrase",
+      1, 1);
+  histogram_tester.ExpectBucketCount(
+      "PasswordManager.TimesPasswordUsed.UserCreated.WithoutCustomPassphrase",
+      3, 1);
+
+  histogram_tester.ExpectBucketCount(
+      "PasswordManager.TimesPasswordUsed.Overall.WithoutCustomPassphrase", 0,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "PasswordManager.TimesPasswordUsed.Overall.WithoutCustomPassphrase", 1,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "PasswordManager.TimesPasswordUsed.Overall.WithoutCustomPassphrase", 2,
+      1);
+  // The bucket for 3 and 4 is the same. Thus we expect two samples here.
+  histogram_tester.ExpectBucketCount(
+      "PasswordManager.TimesPasswordUsed.Overall.WithoutCustomPassphrase", 3,
+      2);
+
   histogram_tester.ExpectUniqueSample(
       "PasswordManager.EmptyUsernames.CountInDatabase", 1, 1);
   histogram_tester.ExpectUniqueSample("PasswordManager.InaccessiblePasswords",
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 9d3887c..89e52f9 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -30,6 +30,7 @@
 #include "components/password_manager/core/browser/password_manager_util.h"
 #include "components/password_manager/core/browser/possible_username_data.h"
 #include "components/password_manager/core/browser/statistics_table.h"
+#include "components/password_manager/core/common/password_manager_features.h"
 
 using autofill::FormData;
 using autofill::FormFieldData;
@@ -145,6 +146,10 @@
   return false;
 }
 
+bool IsUsernameFirstFlowFeatureEnabled() {
+  return base::FeatureList::IsEnabled(features::kUsernameFirstFlow);
+}
+
 }  // namespace
 
 PasswordFormManager::PasswordFormManager(
@@ -424,6 +429,8 @@
   votes_uploader_.UploadPasswordVote(*parsed_submitted_form_,
                                      *parsed_submitted_form_,
                                      autofill::UNKNOWN_TYPE, std::string());
+
+  votes_uploader_.MaybeSendSingleUsernameVote(false /* credentials_saved */);
   PermanentlyBlacklist();
 }
 
@@ -434,6 +441,8 @@
       *parsed_submitted_form_, *parsed_submitted_form_,
       is_update ? autofill::PROBABLY_NEW_PASSWORD : autofill::UNKNOWN_TYPE,
       std::string());
+
+  votes_uploader_.MaybeSendSingleUsernameVote(false /* credentials_saved */);
 }
 
 void PasswordFormManager::PermanentlyBlacklist() {
@@ -685,13 +694,17 @@
   is_submitted_ = true;
   CalculateFillingAssistanceMetric(submitted_form);
   metrics_recorder_->set_possible_username_used(false);
+  votes_uploader_.clear_single_username_vote_data();
 
-  if (parsed_submitted_form_->username_value.empty() && possible_username &&
+  if (IsUsernameFirstFlowFeatureEnabled() &&
+      parsed_submitted_form_->username_value.empty() && possible_username &&
       IsPossibleUsernameValid(*possible_username,
                               parsed_submitted_form_->signon_realm,
                               base::Time::Now())) {
     parsed_submitted_form_->username_value = possible_username->value;
     metrics_recorder_->set_possible_username_used(true);
+    votes_uploader_.set_single_username_vote_data(
+        possible_username->renderer_id, possible_username->form_predictions);
   }
   CreatePendingCredentials();
   return true;
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index 0521dd8..8e90471 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_download_manager.h"
@@ -29,6 +30,7 @@
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
 #include "components/password_manager/core/browser/stub_password_manager_driver.h"
 #include "components/password_manager/core/browser/vote_uploads_test_matchers.h"
+#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -2045,6 +2047,9 @@
 // Tests that username is taken during username first flow.
 TEST_F(PasswordFormManagerTest, UsernameFirstFlow) {
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
+
   CreateFormManager(observed_form_only_password_fields_);
   fetcher_->NotifyFetchCompleted();
   const base::string16 possible_username = ASCIIToUTF16("possible_username");
@@ -2066,6 +2071,9 @@
 // Tests that username is not taken when a possible username is not valid.
 TEST_F(PasswordFormManagerTest, UsernameFirstFlowDifferentDomains) {
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
+
   CreateFormManager(observed_form_only_password_fields_);
   fetcher_->NotifyFetchCompleted();
   base::string16 possible_username = ASCIIToUTF16("possible_username");
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index f9aac53a..fd9c9ce 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -3254,6 +3254,8 @@
 
 // Checks that username is saved on username first flow.
 TEST_F(PasswordManagerTest, UsernameFirstFlow) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
   EXPECT_CALL(*store_, GetLogins(_, _))
       .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
 
diff --git a/components/password_manager/core/browser/possible_username_data.h b/components/password_manager/core/browser/possible_username_data.h
index eb87b04..6415fad0 100644
--- a/components/password_manager/core/browser/possible_username_data.h
+++ b/components/password_manager/core/browser/possible_username_data.h
@@ -9,6 +9,7 @@
 
 #include "base/strings/string16.h"
 #include "base/time/time.h"
+#include "components/password_manager/core/browser/form_parsing/password_field_prediction.h"
 
 namespace password_manager {
 
@@ -31,6 +32,9 @@
   int32_t renderer_id;
   base::string16 value;
   base::Time last_change;
+
+  // Predictions for the form which contains a field with |renderer_id|.
+  FormPredictions* form_predictions = nullptr;
 };
 
 // Checks that |possible_username| might represent an username:
diff --git a/components/password_manager/core/browser/vote_uploads_test_matchers.h b/components/password_manager/core/browser/vote_uploads_test_matchers.h
index 7ee81d7..ac652f22 100644
--- a/components/password_manager/core/browser/vote_uploads_test_matchers.h
+++ b/components/password_manager/core/browser/vote_uploads_test_matchers.h
@@ -25,6 +25,17 @@
   return false;
 }
 
+MATCHER_P(SignatureIs,
+          signature,
+          std::string(negation ? "signature isn't " : "signature is ") +
+              base::NumberToString(signature)) {
+  if (signature == arg.form_signature())
+    return true;
+
+  *result_listener << "signature is " << arg.form_signature() << " instead";
+  return false;
+}
+
 MATCHER_P(SubmissionEventIsSameAs,
           expected_submission_event,
           std::string(negation ? "submission event isn't "
diff --git a/components/password_manager/core/browser/votes_uploader.cc b/components/password_manager/core/browser/votes_uploader.cc
index 15f8d35..7a2fa2c 100644
--- a/components/password_manager/core/browser/votes_uploader.cc
+++ b/components/password_manager/core/browser/votes_uploader.cc
@@ -11,6 +11,7 @@
 #include "base/hash/hash.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/optional.h"
 #include "base/rand_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_download_manager.h"
@@ -26,6 +27,7 @@
 using autofill::AutofillDownloadManager;
 using autofill::AutofillField;
 using autofill::AutofillUploadContents;
+using autofill::FieldSignature;
 using autofill::FormData;
 using autofill::FormFieldData;
 using autofill::FormStructure;
@@ -171,6 +173,19 @@
 
 }  // namespace
 
+SingleUsernameVoteData::SingleUsernameVoteData(
+    uint32_t renderer_id,
+    const FormPredictions& form_predictions)
+    : renderer_id(renderer_id), form_predictions(form_predictions) {}
+
+SingleUsernameVoteData::SingleUsernameVoteData(
+    const SingleUsernameVoteData& other) = default;
+SingleUsernameVoteData& SingleUsernameVoteData::operator=(
+    const SingleUsernameVoteData&) = default;
+SingleUsernameVoteData::SingleUsernameVoteData(SingleUsernameVoteData&& other) =
+    default;
+SingleUsernameVoteData::~SingleUsernameVoteData() = default;
+
 VotesUploader::VotesUploader(PasswordManagerClient* client,
                              bool is_possible_change_password_form)
     : client_(client),
@@ -201,6 +216,7 @@
                          autofill::USERNAME,
                          FormStructure(observed).FormSignatureAsStr());
     }
+    MaybeSendSingleUsernameVote(true /* credentials_saved */);
   } else {
     SendVoteOnCredentialsReuse(observed, submitted_form, pending_credentials);
   }
@@ -357,6 +373,8 @@
   form_structure.set_randomized_encoder(
       RandomizedEncoder::Create(client_->GetPrefs()));
 
+  // TODO(crbug.com/875768): Use VotesUploader::StartUploadRequest for avoiding
+  // code duplication.
   bool success = download_manager->StartUploadRequest(
       form_structure, false /* was_autofilled */, available_field_types,
       login_form_signature, true /* observed_submission */,
@@ -417,6 +435,8 @@
     logger.LogFormStructure(Logger::STRING_FIRSTUSE_FORM_VOTE, form_structure);
   }
 
+  // TODO(crbug.com/875768): Use VotesUploader::StartUploadRequest for avoiding
+  // code duplication.
   download_manager->StartUploadRequest(
       form_structure, false /* was_autofilled */, available_field_types,
       std::string(), true /* observed_submission */, nullptr /* prefs */);
@@ -441,6 +461,48 @@
   }
 }
 
+void VotesUploader::MaybeSendSingleUsernameVote(bool credentials_saved) {
+  if (!single_username_vote_data_)
+    return;
+
+  const FormPredictions& predictions =
+      single_username_vote_data_->form_predictions;
+  std::vector<FieldSignature> field_signatures;
+  for (const auto& field : predictions.fields)
+    field_signatures.push_back(field.signature);
+
+  std::unique_ptr<FormStructure> form_to_upload =
+      FormStructure::CreateForPasswordManagerUpload(predictions.form_signature,
+                                                    field_signatures);
+
+  // Label the username field with a SINGLE_USERNAME or NOT_USERNAME vote.
+  // TODO(crbug.com/552420): Use LabelFields() when cl/1667453 is landed.
+  ServerFieldTypeSet available_field_types;
+  for (size_t i = 0; i < form_to_upload->field_count(); ++i) {
+    AutofillField* field = form_to_upload->field(i);
+
+    ServerFieldType type = autofill::UNKNOWN_TYPE;
+    uint32_t field_renderer_id = predictions.fields[i].renderer_id;
+    if (field_renderer_id == single_username_vote_data_->renderer_id) {
+      type = credentials_saved && !has_username_edited_vote_
+                 ? autofill::SINGLE_USERNAME
+                 : autofill::NOT_USERNAME;
+      if (has_username_edited_vote_)
+        field->set_vote_type(AutofillUploadContents::Field::USERNAME_EDITED);
+      available_field_types.insert(type);
+    }
+    field->set_possible_types({type});
+  }
+
+  if (password_manager_util::IsLoggingActive(client_)) {
+    BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
+    logger.LogFormStructure(Logger::STRING_USERNAME_FIRST_FLOW_VOTE,
+                            *form_to_upload);
+  }
+
+  StartUploadRequest(std::move(form_to_upload), available_field_types);
+}
+
 void VotesUploader::AddGeneratedVote(FormStructure* form_structure) {
   DCHECK(form_structure);
   DCHECK(generation_popup_was_shown_);
@@ -609,4 +671,25 @@
   }
 }
 
+bool VotesUploader::StartUploadRequest(
+    std::unique_ptr<autofill::FormStructure> form_to_upload,
+    const ServerFieldTypeSet& available_field_types) {
+  AutofillDownloadManager* download_manager =
+      client_->GetAutofillDownloadManager();
+  if (!download_manager)
+    return false;
+
+  // Force uploading as these events are relatively rare and we want to make
+  // sure to receive them.
+  form_to_upload->set_upload_required(UPLOAD_REQUIRED);
+
+  // Attach the Randomized Encoder.
+  form_to_upload->set_randomized_encoder(
+      RandomizedEncoder::Create(client_->GetPrefs()));
+
+  return download_manager->StartUploadRequest(
+      *form_to_upload, false /* was_autofilled */, available_field_types,
+      std::string(), true /* observed_submission */, nullptr /* prefs */);
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/votes_uploader.h b/components/password_manager/core/browser/votes_uploader.h
index b0ab27fd..e7966020 100644
--- a/components/password_manager/core/browser/votes_uploader.h
+++ b/components/password_manager/core/browser/votes_uploader.h
@@ -14,6 +14,7 @@
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/proto/server.pb.h"
 #include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/form_parsing/password_field_prediction.h"
 
 namespace autofill {
 struct FormData;
@@ -30,6 +31,23 @@
 using VoteTypeMap =
     std::map<base::string16, autofill::AutofillUploadContents::Field::VoteType>;
 
+// Contains information for sending a SINGLE_USERNAME vote.
+struct SingleUsernameVoteData {
+  SingleUsernameVoteData(uint32_t renderer_id,
+                         const FormPredictions& form_predictions);
+  SingleUsernameVoteData(const SingleUsernameVoteData&);
+  SingleUsernameVoteData& operator=(const SingleUsernameVoteData&);
+  SingleUsernameVoteData(SingleUsernameVoteData&& other);
+  ~SingleUsernameVoteData();
+
+  // Renderer id of an input element, for which the SINGLE_USERNAME vote will be
+  // sent.
+  uint32_t renderer_id;
+
+  // Predictions for the form which contains a field with |renderer_id|.
+  FormPredictions form_predictions;
+};
+
 // This class manages vote uploads for password forms.
 class VotesUploader {
  public:
@@ -93,6 +111,15 @@
       uint32_t username_element_renderer_id,
       autofill::FormStructure* form_structure);
 
+  // Sends single a username vote if |single_username_vote_data_| is set.
+  // |credentials_saved| equals true if credentials with a single username form
+  // were saved, false if they were not saved.
+  // If |single_username_vote_data| is set, the vote sent is either
+  // SINGLE_USERNAME (if the user saved the credential with the username
+  // captured from |single_username_vote_data|) or NOT_USERNAME (if the user did
+  // not save the credential or modified the username).
+  void MaybeSendSingleUsernameVote(bool credentials_saved);
+
   void set_generation_popup_was_shown(bool generation_popup_was_shown) {
     generation_popup_was_shown_ = generation_popup_was_shown;
   }
@@ -133,6 +160,14 @@
     generated_password_changed_ = generated_password_changed;
   }
 
+  void clear_single_username_vote_data() { single_username_vote_data_.reset(); }
+
+  void set_single_username_vote_data(int renderer_id,
+                                     const FormPredictions* form_predictions) {
+    if (form_predictions)
+      single_username_vote_data_.emplace(renderer_id, *form_predictions);
+  }
+
  private:
   // The outcome of the form classifier.
   enum FormClassifierOutcome {
@@ -157,6 +192,10 @@
   bool FindUsernameInOtherPossibleUsernames(const autofill::PasswordForm& match,
                                             const base::string16& username);
 
+  bool StartUploadRequest(
+      std::unique_ptr<autofill::FormStructure> form_to_upload,
+      const autofill::ServerFieldTypeSet& available_field_types);
+
   // The client which implements embedder-specific PasswordManager operations.
   PasswordManagerClient* client_;
 
@@ -200,6 +239,8 @@
   // Maps an |unique_renderer_id| to the initial value of the fields of an
   // observed form.
   std::map<uint32_t, base::string16> initial_values_;
+
+  base::Optional<SingleUsernameVoteData> single_username_vote_data_;
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/votes_uploader_unittest.cc b/components/password_manager/core/browser/votes_uploader_unittest.cc
index bfb3b03..f967cc6 100644
--- a/components/password_manager/core/browser/votes_uploader_unittest.cc
+++ b/components/password_manager/core/browser/votes_uploader_unittest.cc
@@ -30,11 +30,13 @@
 using autofill::FormFieldData;
 using autofill::FormStructure;
 using autofill::NEW_PASSWORD;
+using autofill::NOT_USERNAME;
 using autofill::PASSWORD;
 using autofill::PasswordAttribute;
 using autofill::PasswordForm;
 using autofill::ServerFieldType;
 using autofill::ServerFieldTypeSet;
+using autofill::SINGLE_USERNAME;
 using autofill::mojom::SubmissionIndicatorEvent;
 using base::ASCIIToUTF16;
 using testing::_;
@@ -374,4 +376,46 @@
   }
 }
 
+TEST_F(VotesUploaderTest, NoSingleUsernameDataNoUpload) {
+  VotesUploader votes_uploader(&client_, false);
+  EXPECT_CALL(mock_autofill_download_manager_,
+              StartUploadRequest(_, _, _, _, _, _))
+      .Times(0);
+  votes_uploader.MaybeSendSingleUsernameVote(true /* credentials_saved */);
+}
+
+TEST_F(VotesUploaderTest, UploadSingleUsername) {
+  for (bool credentials_saved : {false, true}) {
+    SCOPED_TRACE(testing::Message("credentials_saved = ") << credentials_saved);
+    VotesUploader votes_uploader(&client_, false);
+    constexpr uint32_t kUsernameRendererId = 101;
+    constexpr uint32_t kUsernameFieldSignature = 1234;
+    constexpr uint64_t kFormSignature = 1000;
+
+    FormPredictions form_predictions;
+    form_predictions.form_signature = kFormSignature;
+    // Add a non-username field.
+    form_predictions.fields.emplace_back();
+    form_predictions.fields.back().renderer_id = kUsernameRendererId - 1;
+    form_predictions.fields.back().signature = kUsernameFieldSignature - 1;
+
+    // Add the username field.
+    form_predictions.fields.emplace_back();
+    form_predictions.fields.back().renderer_id = kUsernameRendererId;
+    form_predictions.fields.back().signature = kUsernameFieldSignature;
+
+    votes_uploader.set_single_username_vote_data(kUsernameRendererId,
+                                                 &form_predictions);
+
+    ServerFieldTypeSet expected_types = {credentials_saved ? SINGLE_USERNAME
+                                                           : NOT_USERNAME};
+    EXPECT_CALL(mock_autofill_download_manager_,
+                StartUploadRequest(SignatureIs(kFormSignature), false,
+                                   expected_types, std::string(), true,
+                                   /* pref_service= */ nullptr));
+
+    votes_uploader.MaybeSendSingleUsernameVote(credentials_saved);
+  }
+}
+
 }  // namespace password_manager
diff --git a/components/performance_manager/BUILD.gn b/components/performance_manager/BUILD.gn
index bcfb174..6d618818 100644
--- a/components/performance_manager/BUILD.gn
+++ b/components/performance_manager/BUILD.gn
@@ -88,8 +88,6 @@
     "frame_priority/frame_priority_unittest.cc",
     "frame_priority/max_vote_aggregator_unittest.cc",
     "frame_priority/override_vote_aggregator_unittest.cc",
-    "frame_priority/unittest_util.cc",
-    "frame_priority/unittest_util.h",
     "graph/frame_node_impl_unittest.cc",
     "graph/graph_impl_operations_unittest.cc",
     "graph/graph_impl_unittest.cc",
diff --git a/components/performance_manager/frame_priority/frame_priority_unittest.cc b/components/performance_manager/frame_priority/frame_priority_unittest.cc
index 05a23301..5db3a6c 100644
--- a/components/performance_manager/frame_priority/frame_priority_unittest.cc
+++ b/components/performance_manager/frame_priority/frame_priority_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "components/performance_manager/public/frame_priority/frame_priority.h"
 
-#include "components/performance_manager/frame_priority/unittest_util.h"
+#include "components/performance_manager/test_support/frame_priority.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace performance_manager {
@@ -93,12 +93,12 @@
 
   // Comparison with identical priorities, and different reason strings.
   PriorityAndReason par4(base::TaskPriority::LOWEST, kReason2);
-  EXPECT_FALSE(par1 == par2);
-  EXPECT_TRUE(par1 != par2);
-  EXPECT_TRUE(par1 <= par2);
-  EXPECT_FALSE(par1 >= par2);
-  EXPECT_TRUE(par1 < par2);
-  EXPECT_FALSE(par1 > par2);
+  EXPECT_FALSE(par1 == par4);
+  EXPECT_TRUE(par1 != par4);
+  EXPECT_TRUE(par1 <= par4);
+  EXPECT_FALSE(par1 >= par4);
+  EXPECT_TRUE(par1 < par4);
+  EXPECT_FALSE(par1 > par4);
 
   // Copy constructor.
   PriorityAndReason par5(par3);
diff --git a/components/performance_manager/frame_priority/max_vote_aggregator_unittest.cc b/components/performance_manager/frame_priority/max_vote_aggregator_unittest.cc
index db07363..7db79c9 100644
--- a/components/performance_manager/frame_priority/max_vote_aggregator_unittest.cc
+++ b/components/performance_manager/frame_priority/max_vote_aggregator_unittest.cc
@@ -5,7 +5,7 @@
 #include "components/performance_manager/public/frame_priority/max_vote_aggregator.h"
 
 #include "base/rand_util.h"
-#include "components/performance_manager/frame_priority/unittest_util.h"
+#include "components/performance_manager/test_support/frame_priority.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace performance_manager {
diff --git a/components/performance_manager/frame_priority/override_vote_aggregator_unittest.cc b/components/performance_manager/frame_priority/override_vote_aggregator_unittest.cc
index 3dfbd85..abec4ba 100644
--- a/components/performance_manager/frame_priority/override_vote_aggregator_unittest.cc
+++ b/components/performance_manager/frame_priority/override_vote_aggregator_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "components/performance_manager/public/frame_priority/override_vote_aggregator.h"
 
-#include "components/performance_manager/frame_priority/unittest_util.h"
+#include "components/performance_manager/test_support/frame_priority.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace performance_manager {
diff --git a/components/performance_manager/graph/frame_node_impl.cc b/components/performance_manager/graph/frame_node_impl.cc
index 8b68840c..c4e427f 100644
--- a/components/performance_manager/graph/frame_node_impl.cc
+++ b/components/performance_manager/graph/frame_node_impl.cc
@@ -14,6 +14,10 @@
 
 namespace performance_manager {
 
+// static
+constexpr char FrameNodeImpl::kDefaultPriorityReason[] =
+    "default frame priority";
+
 using PriorityAndReason = frame_priority::PriorityAndReason;
 
 FrameNodeImpl::FrameNodeImpl(GraphImpl* graph,
diff --git a/components/performance_manager/graph/frame_node_impl.h b/components/performance_manager/graph/frame_node_impl.h
index 58bbe1ca..5bd35ac 100644
--- a/components/performance_manager/graph/frame_node_impl.h
+++ b/components/performance_manager/graph/frame_node_impl.h
@@ -50,6 +50,7 @@
       public TypedNodeBase<FrameNodeImpl, FrameNode, FrameNodeObserver>,
       public mojom::DocumentCoordinationUnit {
  public:
+  static const char kDefaultPriorityReason[];
   static constexpr NodeTypeEnum Type() { return NodeTypeEnum::kFrame; }
 
   // Construct a frame node associated with a |process_node|, a |page_node| and
@@ -120,6 +121,7 @@
   void SetPriorityAndReason(const PriorityAndReason& priority_and_reason);
 
  private:
+  friend class FramePriorityAccess;
   friend class PageNodeImpl;
   friend class ProcessNodeImpl;
 
@@ -247,11 +249,15 @@
   // The child workers of this frame.
   base::flat_set<WorkerNodeImpl*> child_worker_nodes_;
 
-  // Frame priority information.
+  // Frame priority information. Set via FramePriorityDecorator.
   ObservedProperty::NotifiesOnlyOnChanges<
       PriorityAndReason,
       &FrameNodeObserver::OnPriorityAndReasonChanged>
-      priority_and_reason_;
+      priority_and_reason_{PriorityAndReason(base::TaskPriority::LOWEST,
+                                             kDefaultPriorityReason)};
+
+  // Inline storage for FramePriorityDecorator data.
+  frame_priority::AcceptedVote accepted_vote_;
 
   DISALLOW_COPY_AND_ASSIGN(FrameNodeImpl);
 };
diff --git a/components/performance_manager/graph/frame_node_impl_unittest.cc b/components/performance_manager/graph/frame_node_impl_unittest.cc
index 70ae828c7..7cc3883b 100644
--- a/components/performance_manager/graph/frame_node_impl_unittest.cc
+++ b/components/performance_manager/graph/frame_node_impl_unittest.cc
@@ -270,10 +270,9 @@
   MockObserver obs;
   graph()->AddFrameNodeObserver(&obs);
 
-  // By default the priority should be "lowest", and there should be no
-  // reason.
-  EXPECT_EQ(PriorityAndReason(base::TaskPriority::LOWEST, nullptr),
-            frame_node->priority_and_reason());
+  // By default the priority should be "lowest".
+  EXPECT_EQ(base::TaskPriority::LOWEST,
+            frame_node->priority_and_reason().priority());
 
   // Changed the reason only.
   static const char kDummyReason[] = "this is a reason!";
diff --git a/components/performance_manager/public/frame_priority/frame_priority.h b/components/performance_manager/public/frame_priority/frame_priority.h
index 259352d..7a33e80 100644
--- a/components/performance_manager/public/frame_priority/frame_priority.h
+++ b/components/performance_manager/public/frame_priority/frame_priority.h
@@ -84,7 +84,7 @@
 class PriorityAndReason {
  public:
   PriorityAndReason() = default;
-  PriorityAndReason(base::TaskPriority priority, const char* reason)
+  constexpr PriorityAndReason(base::TaskPriority priority, const char* reason)
       : priority_(priority), reason_(reason) {}
   PriorityAndReason(const PriorityAndReason&) = default;
   PriorityAndReason& operator=(const PriorityAndReason&) = default;
diff --git a/components/performance_manager/test_support/BUILD.gn b/components/performance_manager/test_support/BUILD.gn
index 729e16f..d53dd1f1 100644
--- a/components/performance_manager/test_support/BUILD.gn
+++ b/components/performance_manager/test_support/BUILD.gn
@@ -8,6 +8,8 @@
   testonly = true
 
   sources = [
+    "frame_priority.cc",
+    "frame_priority.h",
     "graph_impl.h",
     "graph_test_harness.cc",
     "graph_test_harness.h",
diff --git a/components/performance_manager/frame_priority/unittest_util.cc b/components/performance_manager/test_support/frame_priority.cc
similarity index 97%
rename from components/performance_manager/frame_priority/unittest_util.cc
rename to components/performance_manager/test_support/frame_priority.cc
index bc2db91..865712f 100644
--- a/components/performance_manager/frame_priority/unittest_util.cc
+++ b/components/performance_manager/test_support/frame_priority.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/performance_manager/frame_priority/unittest_util.h"
+#include "components/performance_manager/test_support/frame_priority.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/performance_manager/frame_priority/unittest_util.h b/components/performance_manager/test_support/frame_priority.h
similarity index 90%
rename from components/performance_manager/frame_priority/unittest_util.h
rename to components/performance_manager/test_support/frame_priority.h
index 1891c229..01882a95 100644
--- a/components/performance_manager/frame_priority/unittest_util.h
+++ b/components/performance_manager/test_support/frame_priority.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_PERFORMANCE_MANAGER_FRAME_PRIORITY_UNITTEST_UTIL_H_
-#define COMPONENTS_PERFORMANCE_MANAGER_FRAME_PRIORITY_UNITTEST_UTIL_H_
+#ifndef COMPONENTS_PERFORMANCE_MANAGER_TEST_SUPPORT_FRAME_PRIORITY_H_
+#define COMPONENTS_PERFORMANCE_MANAGER_TEST_SUPPORT_FRAME_PRIORITY_H_
 
 #include "components/performance_manager/public/frame_priority/frame_priority.h"
 
@@ -72,4 +72,4 @@
 }  // namespace frame_priority
 }  // namespace performance_manager
 
-#endif  // COMPONENTS_PERFORMANCE_MANAGER_FRAME_PRIORITY_UNITTEST_UTIL_H_
+#endif  // COMPONENTS_PERFORMANCE_MANAGER_TEST_SUPPORT_FRAME_PRIORITY_H_
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index ea68a2b..0e0a95fb 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -987,6 +987,7 @@
         'PrintPreviewUseSystemDefaultPrinter',
         'UserNativePrintersAllowed',
         'ExternalPrintServers',
+        'ExternalPrintServersWhitelist',
       ]
     },
     {
@@ -5476,7 +5477,6 @@
     },
     {
       'name': 'LegacySameSiteCookieBehaviorEnabled',
-      'future': True,
       'owners': [ 'chlily@chromium.org', ],
       'type': 'int-enum',
       'schema': {
@@ -5514,7 +5514,6 @@
     },
     {
       'name': 'LegacySameSiteCookieBehaviorEnabledForDomainList',
-      'future': True,
       'owners': [ 'chlily@chromium.org', ],
       'type': 'list',
       'schema': {
@@ -15763,25 +15762,24 @@
       This policy does not apply for channel switches.''',
     },
     {
-      'name': 'TabLifecyclesEnabled',
-      'owners': ['chrisha@chromium.org', 'pastarmovj@chromium.org'],
+      'name': 'TabFreezingEnabled',
+      'owners': ['catan-team@chromium.org'],
       'type': 'main',
       'schema': { 'type': 'boolean' },
-      'supported_on': ['chrome.win:69-'],
-      'future': True,
+      'supported_on': ['chrome.win:79-'],
       'features': {
         'dynamic_refresh': False,
         'per_profile': False,
       },
       'example_value': False,
       'id': 460,
-      'caption': '''Enables or disables tab lifecycles''',
+      'caption': '''Allow background tabs freeze''',
       'tags': [],
-      'desc': '''The tab lifecyles feature reclaims CPU and eventually memory associated with running tabs that have not been used in a long period of time, by first throttling them, then freezing them and finally discarding them.
+      'desc': '''Controls whether <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> can freeze tabs that have been in the background for at least 5 minutes.
 
-      If the policy is set to false then tab lifecycles are disabled, and all tabs will be left running normally.
+      If the policy is set to true, tabs that have been in the background for at least 5 minutes may be frozen. Tab freezing reduces CPU, battery and memory usage. <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> uses heuristics to avoid freezing tabs that do useful work in the background (e.g. display notifications, play sound, stream video). Web developers can also opt-out their site from freezing (https://chromium.googlesource.com/chromium/src/+/HEAD/chrome/browser/performance_manager/docs/freezing_opt_out_opt_in.md).
 
-      If the policy is set to true or left unspecified then tab lifecycles are enabled.''',
+      If the policy is set to false, no tabs will be frozen.''',
     },
     {
       'name': 'UrlKeyedAnonymizedDataCollectionEnabled',
@@ -17811,7 +17809,7 @@
 
       This policy allows you to provide configuration of external print servers to <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> devices as JSON file.
 
-      The size of the file must not exceed 1MB and must contain an array of records (JSON objects). Each record must contain fields "url" and "display_name".
+      The size of the file must not exceed 1MB and must contain an array of records (JSON objects). Each record must contain fields "id", "url" and "display_name" with strings as values. Values of "id" fields must be unique.
 
       The file is downloaded and cached. The cryptographic hash is used to verify the integrity of the download. The file will be re-downloaded whenever the URL or the hash changes.
 
@@ -18471,6 +18469,31 @@
       This policy will be removed after a couple of milestones.
 
       For details on <ph name="CORS">CORS</ph>, visit: <ph name="CORS_HELP_URL">https://www.chromestatus.com/feature/5768642492891136</ph>.'''
+    },
+    {
+      'name': 'ExternalPrintServersWhitelist',
+      'owners': ['file://chromeos/printing/OWNERS'],
+      'supported_on': ['chrome_os:79-'],
+      'id': 631,
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'type': 'list',
+      'schema': {
+        'type': 'array',
+        'items': { 'type': 'string' },
+      },
+      'example_value':  ["id1", "id2", "id3"],
+      'caption': '''Enabled external print servers''',
+      'tags': [],
+      'desc': '''Specifies the subset of print servers that will be queried for server printers.
+
+      If this policy is used, only the server printers with ids matching the values in this policy are available to the user.
+
+      The ids must correspond to the "id" field in the file specified in <ph name="EXTERNAL_PRINT_SERVERS_POLICY">ExternalPrintServers</ph>.
+
+      If this policy is not set, filtering is omitted and all print servers are taken into account.'''
     }
   ],
 
@@ -19293,6 +19316,6 @@
   ],
   'placeholders': [],
   'deleted_policy_ids': [412, 546, 562, 569],
-  'highest_id_currently_used': 630,
+  'highest_id_currently_used': 631,
   'highest_atomic_group_id_currently_used': 38
 }
diff --git a/components/resources/components_resources.grd b/components/resources/components_resources.grd
index adff924..78700d9 100644
--- a/components/resources/components_resources.grd
+++ b/components/resources/components_resources.grd
@@ -22,7 +22,6 @@
       <part file="safe_browsing_resources.grdp" />
       <part file="security_interstitials_resources.grdp" />
       <part file="signin_resources.grdp" />
-      <part file="sync_driver_resources.grdp" />
       <part file="translate_resources.grdp" />
       <part file="user_actions_ui_resources.grdp" />
       <part file="version_ui_resources.grdp" />
diff --git a/components/resources/sync_driver_resources.grdp b/components/resources/sync_driver_resources.grdp
deleted file mode 100644
index 9c322f5a..0000000
--- a/components/resources/sync_driver_resources.grdp
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<grit-part>
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_INDEX_HTML" file="../sync/driver/resources/index.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_INDEX_JS" file="../sync/driver/resources/sync_index.js" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_CHROME_SYNC_JS" file="../sync/driver/resources/chrome_sync.js" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_TYPES_JS" file="../sync/driver/resources/types.js" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_LOG_JS" file="../sync/driver/resources/sync_log.js" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_NODE_BROWSER_JS" file="../sync/driver/resources/sync_node_browser.js" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_SEARCH_JS" file="../sync/driver/resources/sync_search.js" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_ABOUT_JS" file="../sync/driver/resources/about.js" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_DATA_JS" file="../sync/driver/resources/data.js" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_EVENTS_JS" file="../sync/driver/resources/events.js" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SEARCH_JS" file="../sync/driver/resources/search.js" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_USER_EVENTS_JS" file="../sync/driver/resources/user_events.js" type="BINDATA" compress="gzip" />
-  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_TRAFFIC_LOG_JS"  file="../sync/driver/resources/traffic_log.js" type="BINDATA" compress="gzip" />
-</grit-part>
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc
index b282c32b..a99385f 100644
--- a/components/security_state/core/security_state.cc
+++ b/components/security_state/core/security_state.cc
@@ -44,6 +44,9 @@
     if (parameter == features::kMarkHttpAsParameterDangerous) {
       return DANGEROUS;
     }
+    if (parameter == features::kMarkHttpAsParameterDangerWarning) {
+      return WARNING;
+    }
   }
 
   // Default to dangerous on editing form fields and otherwise
diff --git a/components/security_state/core/security_state_unittest.cc b/components/security_state/core/security_state_unittest.cc
index 8927fcda..6579081 100644
--- a/components/security_state/core/security_state_unittest.cc
+++ b/components/security_state/core/security_state_unittest.cc
@@ -347,6 +347,24 @@
   EXPECT_EQ(DANGEROUS, helper.GetSecurityLevel());
 }
 
+// Tests that WARNING is set on normal http pages regardless of form edits
+// when kMarkHttpAsFeature is set to mark non-secure connections with grey
+// triangle icon.
+TEST(SecurityStateTest, AlwaysWarningWhenFeatureMarksWithTriangleWarning) {
+  TestSecurityStateHelper helper;
+  helper.SetUrl(GURL(kHttpUrl));
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      security_state::features::kMarkHttpAsFeature,
+      {{security_state::features::kMarkHttpAsFeatureParameterName,
+        security_state::features::kMarkHttpAsParameterDangerWarning}});
+
+  EXPECT_EQ(WARNING, helper.GetSecurityLevel());
+
+  helper.set_insecure_field_edit(true);
+  EXPECT_EQ(WARNING, helper.GetSecurityLevel());
+}
+
 // Tests that DANGEROUS is set on normal http pages regardless of form edits
 // when kMarkHttpAsFeature is set to always DANGEROUS
 TEST(SecurityStateTest, AlwaysDangerousWhenFeatureMarksAllAsDangerous) {
diff --git a/components/send_tab_to_self/BUILD.gn b/components/send_tab_to_self/BUILD.gn
index 1450990..162d558 100644
--- a/components/send_tab_to_self/BUILD.gn
+++ b/components/send_tab_to_self/BUILD.gn
@@ -63,6 +63,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "features_unittest.cc",
     "send_tab_to_self_bridge_unittest.cc",
     "send_tab_to_self_entry_unittest.cc",
   ]
@@ -74,6 +75,7 @@
     "//components/send_tab_to_self/proto:send_tab_to_self_proto",
     "//components/sync:test_support",
     "//components/sync_device_info:test_support",
+    "//components/sync_preferences:test_support",
     "//testing/gtest",
     "//url",
   ]
diff --git a/components/send_tab_to_self/DEPS b/components/send_tab_to_self/DEPS
index de5e9a8..153f0a6 100644
--- a/components/send_tab_to_self/DEPS
+++ b/components/send_tab_to_self/DEPS
@@ -1,10 +1,11 @@
 include_rules = [
+  "+components/history",
   "+components/infobars",
   "+components/keyed_service/core",
   "+components/sync",
   "+components/sync_device_info",
+  "+components/sync_preferences",
   "+components/version_info",
-  "+components/history",
   "+content/public/browser",
   "+google_apis",
 ]
diff --git a/components/send_tab_to_self/features.cc b/components/send_tab_to_self/features.cc
index 22784f9..0f744f7a4 100644
--- a/components/send_tab_to_self/features.cc
+++ b/components/send_tab_to_self/features.cc
@@ -16,8 +16,16 @@
     "SendTabToSelfWhenSignedIn", base::FEATURE_DISABLED_BY_DEFAULT};
 
 bool IsReceivingEnabledByUserOnThisDevice(PrefService* prefs) {
+  // TODO(crbug.com/1015322): SyncPrefs is used directly instead of methods in
+  // SyncService due to a dependency of ProfileSyncService on
+  // DeviceInfoSyncService. IsReceivingEnabledByUserOnThisDevice is ultimately
+  // used by DeviceInfoSyncClient which is owend by DeviceInfoSyncService.
   syncer::SyncPrefs sync_prefs(prefs);
-  return sync_prefs.GetSelectedTypes().Has(syncer::UserSelectableType::kTabs);
+  // As per documentation in SyncUserSettings, IsSyncRequested indicates user
+  // wants Sync to run, when combined with IsFirstSetupComplete, indicates
+  // whether user has consented to Sync.
+  return sync_prefs.IsSyncRequested() && sync_prefs.IsFirstSetupComplete() &&
+         sync_prefs.GetSelectedTypes().Has(syncer::UserSelectableType::kTabs);
 }
 
 bool EnabledOnSignIn() {
diff --git a/components/send_tab_to_self/features_unittest.cc b/components/send_tab_to_self/features_unittest.cc
new file mode 100644
index 0000000..3b25346
--- /dev/null
+++ b/components/send_tab_to_self/features_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/send_tab_to_self/features.h"
+
+#include <memory>
+#include "base/test/task_environment.h"
+
+#include "components/sync/base/sync_prefs.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace send_tab_to_self {
+
+namespace {
+
+class SendTabToSelfFeaturesTest : public testing::Test {
+ public:
+  SendTabToSelfFeaturesTest() {
+    syncer::SyncPrefs::RegisterProfilePrefs(prefs_.registry());
+    sync_prefs_ = std::make_unique<syncer::SyncPrefs>(&prefs_);
+  }
+
+ protected:
+  sync_preferences::TestingPrefServiceSyncable prefs_;
+  std::unique_ptr<syncer::SyncPrefs> sync_prefs_;
+  base::test::TaskEnvironment task_environment_;
+};
+
+TEST_F(SendTabToSelfFeaturesTest,
+       IsReceivingEnabledByUserOnThisDevice_Enabled) {
+  sync_prefs_->SetSyncRequested(true);
+  sync_prefs_->SetFirstSetupComplete();
+  sync_prefs_->SetSelectedTypes(
+      /*keep_everything_synced=*/false,
+      /*registered_types=*/syncer::UserSelectableTypeSet::All(),
+      /*selected_types=*/{syncer::UserSelectableType::kTabs});
+
+  EXPECT_TRUE(IsReceivingEnabledByUserOnThisDevice(&prefs_));
+}
+
+TEST_F(SendTabToSelfFeaturesTest,
+       IsReceivingEnabledByUserOnThisDevice_SyncNotRequested) {
+  sync_prefs_->SetSyncRequested(false);
+  sync_prefs_->SetFirstSetupComplete();
+  sync_prefs_->SetSelectedTypes(
+      /*keep_everything_synced=*/false,
+      /*registered_types=*/syncer::UserSelectableTypeSet::All(),
+      /*selected_types=*/{syncer::UserSelectableType::kTabs});
+
+  EXPECT_FALSE(IsReceivingEnabledByUserOnThisDevice(&prefs_));
+}
+
+TEST_F(SendTabToSelfFeaturesTest,
+       IsReceivingEnabledByUserOnThisDevice_FirstSetupNotCompleted) {
+  sync_prefs_->SetSyncRequested(true);
+  // Skip setting FirstSetupComplete.
+  sync_prefs_->SetSelectedTypes(
+      /*keep_everything_synced=*/false,
+      /*registered_types=*/syncer::UserSelectableTypeSet::All(),
+      /*selected_types=*/{syncer::UserSelectableType::kTabs});
+
+  EXPECT_FALSE(IsReceivingEnabledByUserOnThisDevice(&prefs_));
+}
+
+TEST_F(SendTabToSelfFeaturesTest,
+       IsReceivingEnabledByUserOnThisDevice_TabsNotSelected) {
+  sync_prefs_->SetSyncRequested(true);
+  sync_prefs_->SetFirstSetupComplete();
+  sync_prefs_->SetSelectedTypes(
+      /*keep_everything_synced=*/false,
+      /*registered_types=*/syncer::UserSelectableTypeSet::All(),
+      /*selected_types=*/{});
+
+  EXPECT_FALSE(IsReceivingEnabledByUserOnThisDevice(&prefs_));
+}
+
+}  // namespace
+
+}  // namespace send_tab_to_self
diff --git a/components/services/leveldb/leveldb_database_impl.cc b/components/services/leveldb/leveldb_database_impl.cc
index bfc8c85..14e056aaf 100644
--- a/components/services/leveldb/leveldb_database_impl.cc
+++ b/components/services/leveldb/leveldb_database_impl.cc
@@ -66,7 +66,7 @@
       base::BindOnce(
           [](const std::vector<uint8_t>& key, const std::vector<uint8_t>& value,
              const storage::DomStorageDatabase& db) {
-            return LeveldbStatusToError(db.Put(key, value));
+            return db.Put(key, value);
           },
           key, value),
       std::move(callback));
@@ -74,13 +74,12 @@
 
 void LevelDBDatabaseImpl::Delete(const std::vector<uint8_t>& key,
                                  StatusCallback callback) {
-  RunDatabaseTask(base::BindOnce(
-                      [](const std::vector<uint8_t>& key,
-                         const storage::DomStorageDatabase& db) {
-                        return LeveldbStatusToError(db.Delete(key));
-                      },
-                      key),
-                  std::move(callback));
+  RunDatabaseTask(
+      base::BindOnce(
+          [](const std::vector<uint8_t>& key,
+             const storage::DomStorageDatabase& db) { return db.Delete(key); },
+          key),
+      std::move(callback));
 }
 
 void LevelDBDatabaseImpl::DeletePrefixed(const std::vector<uint8_t>& key_prefix,
@@ -91,8 +90,8 @@
                         WriteBatch batch;
                         Status status = db.DeletePrefixed(prefix, &batch);
                         if (!status.ok())
-                          return LeveldbStatusToError(status);
-                        return LeveldbStatusToError(db.Commit(&batch));
+                          return status;
+                        return db.Commit(&batch);
                       },
                       key_prefix),
                   std::move(callback));
@@ -108,8 +107,7 @@
              storage::DomStorageDatabase* db) {
             callback_task_runner->PostTask(
                 FROM_HERE,
-                base::BindOnce(std::move(callback),
-                               LeveldbStatusToError(db->RewriteDB())));
+                base::BindOnce(std::move(callback), db->RewriteDB()));
           },
           std::move(callback), base::SequencedTaskRunnerHandle::Get()));
 }
@@ -148,7 +146,7 @@
                 }
               }
             }
-            return LeveldbStatusToError(db.Commit(&batch));
+            return db.Commit(&batch);
           },
           std::move(operations)),
       std::move(callback));
@@ -170,8 +168,7 @@
                       key),
                   base::BindOnce(
                       [](GetCallback callback, GetResult result) {
-                        std::move(callback).Run(
-                            LeveldbStatusToError(result.status), result.value);
+                        std::move(callback).Run(result.status, result.value);
                       },
                       std::move(callback)));
 }
@@ -197,8 +194,7 @@
             std::vector<mojom::KeyValuePtr> entries;
             for (auto& entry : result.entries)
               entries.push_back(mojom::KeyValue::New(entry.key, entry.value));
-            std::move(callback).Run(LeveldbStatusToError(result.status),
-                                    std::move(entries));
+            std::move(callback).Run(result.status, std::move(entries));
           },
           std::move(callback)));
 }
@@ -215,8 +211,8 @@
                         Status status =
                             db.CopyPrefixed(prefix, new_prefix, &batch);
                         if (!status.ok())
-                          return LeveldbStatusToError(status);
-                        return LeveldbStatusToError(db.Commit(&batch));
+                          return status;
+                        return db.Commit(&batch);
                       },
                       source_key_prefix, destination_key_prefix),
                   std::move(callback));
@@ -233,7 +229,7 @@
     for (auto& task : tasks)
       database_.PostTaskWithThisObject(FROM_HERE, std::move(task));
   }
-  std::move(callback).Run(LeveldbStatusToError(status));
+  std::move(callback).Run(status);
 }
 
 }  // namespace leveldb
diff --git a/components/services/leveldb/leveldb_database_impl.h b/components/services/leveldb/leveldb_database_impl.h
index f5dde2a..6ddee8265 100644
--- a/components/services/leveldb/leveldb_database_impl.h
+++ b/components/services/leveldb/leveldb_database_impl.h
@@ -27,7 +27,7 @@
 // TODO(https://crbug.com/1000959): Delete this class.
 class LevelDBDatabaseImpl {
  public:
-  using StatusCallback = base::OnceCallback<void(mojom::DatabaseError)>;
+  using StatusCallback = base::OnceCallback<void(Status)>;
 
   ~LevelDBDatabaseImpl();
 
@@ -69,13 +69,12 @@
   void Write(std::vector<mojom::BatchedOperationPtr> operations,
              StatusCallback callback);
 
-  using GetCallback = base::OnceCallback<void(mojom::DatabaseError status,
-                                              const std::vector<uint8_t>&)>;
+  using GetCallback =
+      base::OnceCallback<void(Status status, const std::vector<uint8_t>&)>;
   void Get(const std::vector<uint8_t>& key, GetCallback callback);
 
   using GetPrefixedCallback =
-      base::OnceCallback<void(mojom::DatabaseError status,
-                              std::vector<mojom::KeyValuePtr>)>;
+      base::OnceCallback<void(Status status, std::vector<mojom::KeyValuePtr>)>;
   void GetPrefixed(const std::vector<uint8_t>& key_prefix,
                    GetPrefixedCallback callback);
 
diff --git a/components/services/leveldb/public/cpp/util.cc b/components/services/leveldb/public/cpp/util.cc
index 0bcdcfe..0b61b11 100644
--- a/components/services/leveldb/public/cpp/util.cc
+++ b/components/services/leveldb/public/cpp/util.cc
@@ -10,63 +10,6 @@
 
 namespace leveldb {
 
-mojom::DatabaseError LeveldbStatusToError(const leveldb::Status& s) {
-  if (s.ok())
-    return mojom::DatabaseError::OK;
-  if (s.IsNotFound())
-    return mojom::DatabaseError::NOT_FOUND;
-  if (s.IsCorruption())
-    return mojom::DatabaseError::CORRUPTION;
-  if (s.IsNotSupportedError())
-    return mojom::DatabaseError::NOT_SUPPORTED;
-  if (s.IsIOError())
-    return mojom::DatabaseError::IO_ERROR;
-  return mojom::DatabaseError::INVALID_ARGUMENT;
-}
-
-leveldb::Status DatabaseErrorToStatus(mojom::DatabaseError e,
-                                      const Slice& msg,
-                                      const Slice& msg2) {
-  switch (e) {
-    case mojom::DatabaseError::OK:
-      return leveldb::Status::OK();
-    case mojom::DatabaseError::NOT_FOUND:
-      return leveldb::Status::NotFound(msg, msg2);
-    case mojom::DatabaseError::CORRUPTION:
-      return leveldb::Status::Corruption(msg, msg2);
-    case mojom::DatabaseError::NOT_SUPPORTED:
-      return leveldb::Status::NotSupported(msg, msg2);
-    case mojom::DatabaseError::INVALID_ARGUMENT:
-      return leveldb::Status::InvalidArgument(msg, msg2);
-    case mojom::DatabaseError::IO_ERROR:
-      return leveldb::Status::IOError(msg, msg2);
-  }
-
-  // This will never be reached, but we still have configurations which don't
-  // do switch enum checking.
-  return leveldb::Status::InvalidArgument(msg, msg2);
-}
-
-leveldb_env::LevelDBStatusValue GetLevelDBStatusUMAValue(
-    mojom::DatabaseError status) {
-  switch (status) {
-    case mojom::DatabaseError::OK:
-      return leveldb_env::LEVELDB_STATUS_OK;
-    case mojom::DatabaseError::NOT_FOUND:
-      return leveldb_env::LEVELDB_STATUS_NOT_FOUND;
-    case mojom::DatabaseError::CORRUPTION:
-      return leveldb_env::LEVELDB_STATUS_CORRUPTION;
-    case mojom::DatabaseError::NOT_SUPPORTED:
-      return leveldb_env::LEVELDB_STATUS_NOT_SUPPORTED;
-    case mojom::DatabaseError::INVALID_ARGUMENT:
-      return leveldb_env::LEVELDB_STATUS_INVALID_ARGUMENT;
-    case mojom::DatabaseError::IO_ERROR:
-      return leveldb_env::LEVELDB_STATUS_IO_ERROR;
-  }
-  NOTREACHED();
-  return leveldb_env::LEVELDB_STATUS_OK;
-}
-
 leveldb::Slice GetSliceFor(const std::vector<uint8_t>& key) {
   if (key.size() == 0)
     return leveldb::Slice();
diff --git a/components/services/leveldb/public/cpp/util.h b/components/services/leveldb/public/cpp/util.h
index b45b2fa..a3044e7 100644
--- a/components/services/leveldb/public/cpp/util.h
+++ b/components/services/leveldb/public/cpp/util.h
@@ -9,27 +9,11 @@
 #include <vector>
 
 #include "base/strings/string16.h"
-#include "components/services/leveldb/public/mojom/leveldb.mojom.h"
 #include "third_party/leveldatabase/env_chromium.h"
 
 namespace leveldb {
 
 class Slice;
-class Status;
-
-// Builds a mojo mojom::DatabaseError from a leveldb::Status object.
-mojom::DatabaseError LeveldbStatusToError(const leveldb::Status& s);
-
-// Creates a leveldb Status object form a database error and two optional
-// messages. A mojoification of the various static leveldb::Status
-// constructors.
-leveldb::Status DatabaseErrorToStatus(mojom::DatabaseError e,
-                                      const Slice& msg,
-                                      const Slice& msg2);
-
-// Returns an UMA value for a mojom::DatabaseError.
-leveldb_env::LevelDBStatusValue GetLevelDBStatusUMAValue(
-    mojom::DatabaseError status);
 
 // Builds a Slice pointing to the data inside |a|. This is not a type-converter
 // as it is not a copy operation; the returned Slice points into |a| and must
diff --git a/components/services/leveldb/public/mojom/leveldb.mojom b/components/services/leveldb/public/mojom/leveldb.mojom
index eac34b5..4bc70cf1 100644
--- a/components/services/leveldb/public/mojom/leveldb.mojom
+++ b/components/services/leveldb/public/mojom/leveldb.mojom
@@ -4,15 +4,6 @@
 
 module leveldb.mojom;
 
-enum DatabaseError {
-  OK,
-  NOT_FOUND,
-  CORRUPTION,
-  NOT_SUPPORTED,
-  INVALID_ARGUMENT,
-  IO_ERROR
-};
-
 enum BatchOperationType {
   PUT_KEY,
   DELETE_KEY,
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java
index 7450dd1d..a85f51b 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java
@@ -173,10 +173,29 @@
      */
     @MainThread
     public void getAccessToken(Account account, String scope, GetAccessTokenCallback callback) {
+        getAccessTokenWithFacade(mAccountManagerFacade, account, scope, callback);
+    }
+
+    /**
+     * Call this method to retrieve an OAuth2 access token for the given account and scope. Please
+     * note that this method expects a scope with 'oauth2:' prefix.
+     *
+     * @deprecated Use getAccessToken instead. crbug.com/1014098: This method is available as a
+     *         workaround for a callsite where native is not initialized yet.
+     *
+     * @param accountManagerFacade AccountManagerFacade to request the access token from.
+     * @param account the account to get the access token for.
+     * @param scope The scope to get an auth token for (with Android-style 'oauth2:' prefix).
+     * @param callback called on successful and unsuccessful fetching of auth token.
+     */
+    @MainThread
+    @Deprecated
+    public static void getAccessTokenWithFacade(AccountManagerFacade accountManagerFacade,
+            Account account, String scope, GetAccessTokenCallback callback) {
         ConnectionRetry.runAuthTask(new AuthTask<String>() {
             @Override
             public String run() throws AuthException {
-                return mAccountManagerFacade.getAccessToken(account, scope);
+                return accountManagerFacade.getAccessToken(account, scope);
             }
             @Override
             public void onSuccess(String token) {
diff --git a/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc b/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
index 00a3111..d68b0a8b 100644
--- a/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
+++ b/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
@@ -163,7 +163,6 @@
 std::unique_ptr<blink::WebDocumentSubresourceFilter>
 WebDocumentSubresourceFilterImpl::BuilderImpl::Build() {
   DCHECK(ruleset_file_.IsValid());
-  DCHECK(!main_task_runner_->BelongsToCurrentThread());
   scoped_refptr<MemoryMappedRuleset> ruleset =
       MemoryMappedRuleset::CreateAndInitialize(std::move(ruleset_file_));
   if (!ruleset)
diff --git a/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h b/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h
index 3a26e1c..d1931625 100644
--- a/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h
+++ b/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h
@@ -24,8 +24,10 @@
     : public blink::WebDocumentSubresourceFilter,
       public base::SupportsWeakPtr<WebDocumentSubresourceFilterImpl> {
  public:
-  // This builder class is created on the main thread and passed to a worker
-  // thread to create the subresource filter for the worker thread.
+  // This builder class is used for creating the subresource filter for workers
+  // and worklets. For workers and threaded worklets, this is created on the
+  // main thread and passed to a worker thread. For main thread worklets, this
+  // is created and used on the main thread.
   class BuilderImpl : public blink::WebDocumentSubresourceFilter::Builder {
    public:
     BuilderImpl(url::Origin document_origin,
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index a85c427..3a3dded 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -331,6 +331,8 @@
     "nigori/nigori_sync_bridge.h",
     "nigori/nigori_sync_bridge_impl.cc",
     "nigori/nigori_sync_bridge_impl.h",
+    "nigori/pending_local_nigori_commit.cc",
+    "nigori/pending_local_nigori_commit.h",
     "syncable/base_node.cc",
     "syncable/base_node.h",
     "syncable/base_transaction.cc",
diff --git a/components/sync/driver/BUILD.gn b/components/sync/driver/BUILD.gn
index 5b27a3f6..58eff10 100644
--- a/components/sync/driver/BUILD.gn
+++ b/components/sync/driver/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/config/features.gni")
 import("//build/config/jumbo.gni")
+import("//tools/grit/grit_rule.gni")
 
 declare_args() {
   # Controls the product part of the user agent calculated in sync_util.cc.
@@ -138,6 +139,23 @@
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 }
 
+grit("resources") {
+  source = "resources.grd"
+
+  # The .grd contains references to generated files.
+  source_is_generated = true
+  outputs = [
+    "grit/sync_driver_resources.h",
+    "sync_driver_resources.pak",
+  ]
+  output_dir = "$root_gen_dir/components"
+  depfile_dir = target_gen_dir
+  grit_flags = [
+    "-E",
+    "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
+  ]
+}
+
 static_library("test_support") {
   testonly = true
   sources = [
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index 0f1a3c1..6fde79dea 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -867,8 +867,7 @@
           initial_types, debug_info_listener, &data_type_controllers_,
           user_settings_.get(), engine_.get(), this);
 
-  crypto_.SetSyncEngine(GetAuthenticatedAccountInfo().account_id,
-                        engine_.get());
+  crypto_.SetSyncEngine(GetAuthenticatedAccountInfo(), engine_.get());
 
   // Auto-start means IsFirstSetupComplete gets set automatically.
   if (start_behavior_ == AUTO_START &&
diff --git a/components/sync/driver/resources.grd b/components/sync/driver/resources.grd
new file mode 100644
index 0000000..5b59e1f09
--- /dev/null
+++ b/components/sync/driver/resources.grd
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0"
+      current_release="1"
+      output_all_resource_defines="false">
+  <outputs>
+    <output filename="grit/sync_driver_resources.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="sync_driver_resources.pak" type="data_package" />
+  </outputs>
+  <release seq="1">
+    <includes>
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_INDEX_HTML" file="resources/index.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_INDEX_JS" file="resources/sync_index.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_CHROME_SYNC_JS" file="resources/chrome_sync.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_TYPES_JS" file="resources/types.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_LOG_JS" file="resources/sync_log.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_NODE_BROWSER_JS" file="resources/sync_node_browser.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_SEARCH_JS" file="resources/sync_search.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_ABOUT_JS" file="resources/about.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_DATA_JS" file="resources/data.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_EVENTS_JS" file="resources/events.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SEARCH_JS" file="resources/search.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_USER_EVENTS_JS" file="resources/user_events.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_TRAFFIC_LOG_JS"  file="resources/traffic_log.js" type="BINDATA" compress="gzip" />
+    </includes>
+  </release>
+</grit>
diff --git a/components/sync/driver/sync_service_crypto.cc b/components/sync/driver/sync_service_crypto.cc
index afbe6c62..96879888 100644
--- a/components/sync/driver/sync_service_crypto.cc
+++ b/components/sync/driver/sync_service_crypto.cc
@@ -33,12 +33,12 @@
 
   // TrustedVaultClient implementatio.
   void FetchKeys(
-      const CoreAccountId& account_id,
+      const std::string& gaia_id,
       base::OnceCallback<void(const std::vector<std::string>&)> cb) override {
     std::move(cb).Run({});
   }
 
-  void StoreKeys(const CoreAccountId& account_id,
+  void StoreKeys(const std::string& gaia_id,
                  const std::vector<std::string>& keys) override {}
 };
 
@@ -317,11 +317,11 @@
 }
 
 void SyncServiceCrypto::AddTrustedVaultDecryptionKeys(
-    const CoreAccountId& account_id,
+    const std::string& gaia_id,
     const std::vector<std::string>& keys) {
-  trusted_vault_client_->StoreKeys(account_id, keys);
+  trusted_vault_client_->StoreKeys(gaia_id, keys);
 
-  if (state_.engine && state_.account_id == account_id) {
+  if (state_.engine && state_.account_info.gaia == gaia_id) {
     state_.engine->AddTrustedVaultDecryptionKeys(keys, base::DoNothing());
   }
 }
@@ -397,10 +397,16 @@
 
   state_.required_user_action = RequiredUserAction::kFetchingTrustedVaultKeys;
 
-  trusted_vault_client_->FetchKeys(
-      state_.account_id,
-      base::BindOnce(&SyncServiceCrypto::TrustedVaultKeysFetched,
-                     weak_factory_.GetWeakPtr()));
+  if (!state_.engine) {
+    // If SetSyncEngine() hasn't been called yet, it means
+    // OnTrustedVaultKeyRequired() was called as part of the engine's
+    // initialization. Fetching the keys is not useful right now because there
+    // is known engine to feed the keys to, so let's defer fetching until
+    // SetSyncEngine() is called.
+    return;
+  }
+
+  FetchTrustedVaultKeys();
 }
 
 void SyncServiceCrypto::OnTrustedVaultKeyAccepted() {
@@ -477,11 +483,18 @@
   notify_observers_.Run();
 }
 
-void SyncServiceCrypto::SetSyncEngine(const CoreAccountId& account_id,
+void SyncServiceCrypto::SetSyncEngine(const CoreAccountInfo& account_info,
                                       SyncEngine* engine) {
   DCHECK(engine);
-  state_.account_id = account_id;
+  state_.account_info = account_info;
   state_.engine = engine;
+
+  // This indicates OnTrustedVaultKeyRequired() was called as part of the
+  // engine's initialization.
+  if (state_.required_user_action ==
+      RequiredUserAction::kFetchingTrustedVaultKeys) {
+    FetchTrustedVaultKeys();
+  }
 }
 
 std::unique_ptr<SyncEncryptionHandler::Observer>
@@ -491,6 +504,17 @@
       weak_factory_.GetWeakPtr(), base::SequencedTaskRunnerHandle::Get());
 }
 
+void SyncServiceCrypto::FetchTrustedVaultKeys() {
+  DCHECK(state_.engine);
+  DCHECK_EQ(state_.required_user_action,
+            RequiredUserAction::kFetchingTrustedVaultKeys);
+
+  trusted_vault_client_->FetchKeys(
+      state_.account_info.gaia,
+      base::BindOnce(&SyncServiceCrypto::TrustedVaultKeysFetched,
+                     weak_factory_.GetWeakPtr()));
+}
+
 void SyncServiceCrypto::TrustedVaultKeysFetched(
     const std::vector<std::string>& keys) {
   // The engine could have been shut down while keys were being fetched.
diff --git a/components/sync/driver/sync_service_crypto.h b/components/sync/driver/sync_service_crypto.h
index 7859d350..4986bb35 100644
--- a/components/sync/driver/sync_service_crypto.h
+++ b/components/sync/driver/sync_service_crypto.h
@@ -12,11 +12,11 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "components/signin/public/identity_manager/account_info.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/engine/configure_reason.h"
 #include "components/sync/engine/sync_encryption_handler.h"
 #include "components/sync/engine/sync_engine.h"
-#include "google_apis/gaia/core_account_id.h"
 
 namespace syncer {
 
@@ -49,7 +49,7 @@
   bool IsEncryptEverythingEnabled() const;
   void SetEncryptionPassphrase(const std::string& passphrase);
   bool SetDecryptionPassphrase(const std::string& passphrase);
-  void AddTrustedVaultDecryptionKeys(const CoreAccountId& account_id,
+  void AddTrustedVaultDecryptionKeys(const std::string& gaia_id,
                                      const std::vector<std::string>& keys);
 
   // Returns the actual passphrase type being used for encryption.
@@ -77,7 +77,7 @@
                                base::Time passphrase_time) override;
 
   // Used to provide the engine when it is initialized.
-  void SetSyncEngine(const CoreAccountId& account_id, SyncEngine* engine);
+  void SetSyncEngine(const CoreAccountInfo& account_info, SyncEngine* engine);
 
   // Creates a proxy observer object that will post calls to this thread.
   std::unique_ptr<SyncEncryptionHandler::Observer> GetEncryptionObserverProxy();
@@ -97,6 +97,9 @@
     kTrustedVaultKeyRequired,
   };
 
+  // Reads trusted vault keys from the client and feeds them to the sync engine.
+  void FetchTrustedVaultKeys();
+
   // Called at various stages of asynchronously fetching and processing trusted
   // vault encryption keys.
   void TrustedVaultKeysFetched(const std::vector<std::string>& keys);
@@ -126,7 +129,7 @@
     SyncEngine* engine = nullptr;
 
     // Populated when the engine is initialized.
-    CoreAccountId account_id;
+    CoreAccountInfo account_info;
 
     RequiredUserAction required_user_action = RequiredUserAction::kNone;
 
diff --git a/components/sync/driver/sync_service_crypto_unittest.cc b/components/sync/driver/sync_service_crypto_unittest.cc
index c37ce666..9c3520c 100644
--- a/components/sync/driver/sync_service_crypto_unittest.cc
+++ b/components/sync/driver/sync_service_crypto_unittest.cc
@@ -39,6 +39,12 @@
   return encrypted;
 }
 
+CoreAccountInfo MakeAccountInfoWithGaia(const std::string& gaia) {
+  CoreAccountInfo result;
+  result.gaia = gaia;
+  return result;
+}
+
 class MockCryptoSyncPrefs : public CryptoSyncPrefs {
  public:
   MockCryptoSyncPrefs() = default;
@@ -57,10 +63,10 @@
 
   MOCK_METHOD2(
       FetchKeys,
-      void(const CoreAccountId& account_id,
+      void(const std::string& gaia_id,
            base::OnceCallback<void(const std::vector<std::string>&)> cb));
   MOCK_METHOD2(StoreKeys,
-               void(const CoreAccountId& account_id,
+               void(const std::string& gaia_id,
                     const std::vector<std::string>& keys));
 };
 
@@ -95,7 +101,7 @@
 TEST_F(SyncServiceCryptoTest, ShouldExposePassphraseRequired) {
   const std::string kTestPassphrase = "somepassphrase";
 
-  crypto_.SetSyncEngine(CoreAccountId(), &engine_);
+  crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
   ASSERT_FALSE(crypto_.IsPassphraseRequired());
 
   // Mimic the engine determining that a passphrase is required.
@@ -126,45 +132,99 @@
 
 TEST_F(SyncServiceCryptoTest,
        ShouldStoreTrustedVaultKeysBeforeEngineInitialization) {
-  const CoreAccountId kAccount = CoreAccountId("account1");
+  const std::string kAccount = "account1";
   const std::vector<std::string> kKeys = {"key1"};
-  EXPECT_CALL(trusted_vault_client_, StoreKeys(kAccount, kKeys));
+  EXPECT_CALL(trusted_vault_client_, StoreKeys("account1", kKeys));
   crypto_.AddTrustedVaultDecryptionKeys(kAccount, kKeys);
 }
 
 TEST_F(SyncServiceCryptoTest,
        ShouldStoreTrustedVaultKeysAfterEngineInitialization) {
-  const CoreAccountId kSyncingAccount = CoreAccountId("syncingaccount");
-  const CoreAccountId kOtherAccount = CoreAccountId("otheraccount");
+  const CoreAccountInfo kSyncingAccount =
+      MakeAccountInfoWithGaia("syncingaccount");
+  const CoreAccountInfo kOtherAccount = MakeAccountInfoWithGaia("otheraccount");
   const std::vector<std::string> kSyncingAccountKeys = {"key1"};
   const std::vector<std::string> kOtherAccountKeys = {"key2"};
 
   crypto_.SetSyncEngine(kSyncingAccount, &engine_);
 
   EXPECT_CALL(trusted_vault_client_,
-              StoreKeys(kOtherAccount, kOtherAccountKeys));
+              StoreKeys(kOtherAccount.gaia, kOtherAccountKeys));
   EXPECT_CALL(trusted_vault_client_,
-              StoreKeys(kSyncingAccount, kSyncingAccountKeys));
+              StoreKeys(kSyncingAccount.gaia, kSyncingAccountKeys));
 
   // Only the sync-ing account should be propagated to the engine.
   EXPECT_CALL(engine_, AddTrustedVaultDecryptionKeys(kOtherAccountKeys, _))
       .Times(0);
   EXPECT_CALL(engine_, AddTrustedVaultDecryptionKeys(kSyncingAccountKeys, _));
-  crypto_.AddTrustedVaultDecryptionKeys(kOtherAccount, kOtherAccountKeys);
-  crypto_.AddTrustedVaultDecryptionKeys(kSyncingAccount, kSyncingAccountKeys);
+  crypto_.AddTrustedVaultDecryptionKeys(kOtherAccount.gaia, kOtherAccountKeys);
+  crypto_.AddTrustedVaultDecryptionKeys(kSyncingAccount.gaia,
+                                        kSyncingAccountKeys);
 }
 
-TEST_F(SyncServiceCryptoTest, ShouldReadValidTrustedVaultKeysFromClient) {
-  const CoreAccountId kSyncingAccount = CoreAccountId("syncingaccount");
+TEST_F(SyncServiceCryptoTest,
+       ShouldReadValidTrustedVaultKeysFromClientBeforeInitialization) {
+  const CoreAccountInfo kSyncingAccount =
+      MakeAccountInfoWithGaia("syncingaccount");
+  const std::vector<std::string> kFetchedKeys = {"key1"};
+
+  EXPECT_CALL(reconfigure_cb_, Run(_)).Times(0);
+  ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
+
+  // OnTrustedVaultKeyRequired() called during initialization of the sync
+  // engine (i.e. before SetSyncEngine()).
+  EXPECT_CALL(trusted_vault_client_, FetchKeys(_, _)).Times(0);
+  crypto_.OnTrustedVaultKeyRequired();
+
+  base::OnceCallback<void(const std::vector<std::string>&)> fetch_keys_cb;
+  EXPECT_CALL(trusted_vault_client_, FetchKeys(kSyncingAccount.gaia, _))
+      .WillOnce(
+          [&](const std::string& gaia_id,
+              base::OnceCallback<void(const std::vector<std::string>&)> cb) {
+            fetch_keys_cb = std::move(cb);
+          });
+
+  // Trusted vault keys should be fetched only after the engine initialization
+  // is completed.
+  crypto_.SetSyncEngine(kSyncingAccount, &engine_);
+  VerifyAndClearExpectations();
+
+  // While there is an ongoing fetch, there should be no user action required.
+  ASSERT_TRUE(fetch_keys_cb);
+  EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
+
+  base::OnceClosure add_keys_cb;
+  EXPECT_CALL(engine_, AddTrustedVaultDecryptionKeys(kFetchedKeys, _))
+      .WillOnce(
+          [&](const std::vector<std::string>& keys, base::OnceClosure done_cb) {
+            add_keys_cb = std::move(done_cb);
+          });
+
+  // Mimic completion of the fetch.
+  std::move(fetch_keys_cb).Run(kFetchedKeys);
+  ASSERT_TRUE(add_keys_cb);
+  EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
+
+  // Mimic completion of the engine.
+  EXPECT_CALL(reconfigure_cb_, Run(CONFIGURE_REASON_CRYPTO));
+  crypto_.OnTrustedVaultKeyAccepted();
+  std::move(add_keys_cb).Run();
+  EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
+}
+
+TEST_F(SyncServiceCryptoTest,
+       ShouldReadValidTrustedVaultKeysFromClientAfterInitialization) {
+  const CoreAccountInfo kSyncingAccount =
+      MakeAccountInfoWithGaia("syncingaccount");
   const std::vector<std::string> kFetchedKeys = {"key1"};
 
   EXPECT_CALL(reconfigure_cb_, Run(_)).Times(0);
   ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
 
   base::OnceCallback<void(const std::vector<std::string>&)> fetch_keys_cb;
-  EXPECT_CALL(trusted_vault_client_, FetchKeys(kSyncingAccount, _))
+  EXPECT_CALL(trusted_vault_client_, FetchKeys(kSyncingAccount.gaia, _))
       .WillOnce(
-          [&](const CoreAccountId& account_id,
+          [&](const std::string& gaia_id,
               base::OnceCallback<void(const std::vector<std::string>&)> cb) {
             fetch_keys_cb = std::move(cb);
           });
@@ -198,15 +258,16 @@
 }
 
 TEST_F(SyncServiceCryptoTest, ShouldReadInvalidTrustedVaultKeysFromClient) {
-  const CoreAccountId kSyncingAccount = CoreAccountId("syncingaccount");
+  const CoreAccountInfo kSyncingAccount =
+      MakeAccountInfoWithGaia("syncingaccount");
   const std::vector<std::string> kFetchedKeys = {"key1"};
 
   ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
 
   base::OnceCallback<void(const std::vector<std::string>&)> fetch_keys_cb;
-  EXPECT_CALL(trusted_vault_client_, FetchKeys(kSyncingAccount, _))
+  EXPECT_CALL(trusted_vault_client_, FetchKeys(kSyncingAccount.gaia, _))
       .WillOnce(
-          [&](const CoreAccountId& account_id,
+          [&](const std::string& gaia_id,
               base::OnceCallback<void(const std::vector<std::string>&)> cb) {
             fetch_keys_cb = std::move(cb);
           });
diff --git a/components/sync/driver/sync_user_settings.h b/components/sync/driver/sync_user_settings.h
index efa0b6c3..7edebb1 100644
--- a/components/sync/driver/sync_user_settings.h
+++ b/components/sync/driver/sync_user_settings.h
@@ -15,8 +15,6 @@
 #include "components/sync/base/user_selectable_type.h"
 #include "components/sync/driver/data_type_encryption_handler.h"
 
-struct CoreAccountId;
-
 namespace syncer {
 
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser
@@ -120,7 +118,7 @@
   // TRUSTED_VAULT_PASSPHRASE: it provides new decryption keys that could
   // allow decrypting pending Nigori keys.
   virtual void AddTrustedVaultDecryptionKeys(
-      const CoreAccountId& account_id,
+      const std::string& gaia_id,
       const std::vector<std::string>& keys) = 0;
 };
 
diff --git a/components/sync/driver/sync_user_settings_impl.cc b/components/sync/driver/sync_user_settings_impl.cc
index 55391aa7..e32001f 100644
--- a/components/sync/driver/sync_user_settings_impl.cc
+++ b/components/sync/driver/sync_user_settings_impl.cc
@@ -171,10 +171,10 @@
 }
 
 void SyncUserSettingsImpl::AddTrustedVaultDecryptionKeys(
-    const CoreAccountId& account_id,
+    const std::string& gaia_id,
     const std::vector<std::string>& keys) {
   DVLOG(1) << "Adding trusted vault decryption keys.";
-  crypto_->AddTrustedVaultDecryptionKeys(account_id, keys);
+  crypto_->AddTrustedVaultDecryptionKeys(gaia_id, keys);
 }
 
 void SyncUserSettingsImpl::SetSyncRequestedIfNotSetExplicitly() {
diff --git a/components/sync/driver/sync_user_settings_impl.h b/components/sync/driver/sync_user_settings_impl.h
index c18c6b4..cbd247a 100644
--- a/components/sync/driver/sync_user_settings_impl.h
+++ b/components/sync/driver/sync_user_settings_impl.h
@@ -14,8 +14,6 @@
 #include "components/sync/driver/sync_type_preference_provider.h"
 #include "components/sync/driver/sync_user_settings.h"
 
-struct CoreAccountId;
-
 namespace syncer {
 
 class SyncPrefs;
@@ -65,7 +63,7 @@
   void SetEncryptionPassphrase(const std::string& passphrase) override;
   bool SetDecryptionPassphrase(const std::string& passphrase) override;
   void AddTrustedVaultDecryptionKeys(
-      const CoreAccountId& account_id,
+      const std::string& gaia_id,
       const std::vector<std::string>& keys) override;
 
   void SetSyncRequestedIfNotSetExplicitly();
diff --git a/components/sync/driver/sync_user_settings_mock.h b/components/sync/driver/sync_user_settings_mock.h
index 7ab5cf6..b4f8d30 100644
--- a/components/sync/driver/sync_user_settings_mock.h
+++ b/components/sync/driver/sync_user_settings_mock.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "components/sync/driver/sync_user_settings.h"
-#include "google_apis/gaia/core_account_id.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace syncer {
@@ -49,7 +48,7 @@
   MOCK_METHOD1(SetEncryptionPassphrase, void(const std::string&));
   MOCK_METHOD1(SetDecryptionPassphrase, bool(const std::string&));
   MOCK_METHOD2(AddTrustedVaultDecryptionKeys,
-               void(const CoreAccountId&, const std::vector<std::string>&));
+               void(const std::string&, const std::vector<std::string>&));
 };
 
 }  // namespace syncer
diff --git a/components/sync/driver/test_sync_user_settings.cc b/components/sync/driver/test_sync_user_settings.cc
index a5e45d69..939ad14 100644
--- a/components/sync/driver/test_sync_user_settings.cc
+++ b/components/sync/driver/test_sync_user_settings.cc
@@ -153,7 +153,7 @@
 }
 
 void TestSyncUserSettings::AddTrustedVaultDecryptionKeys(
-    const CoreAccountId& account_id,
+    const std::string& gaia_id,
     const std::vector<std::string>& keys) {}
 
 void TestSyncUserSettings::SetFirstSetupComplete() {
diff --git a/components/sync/driver/test_sync_user_settings.h b/components/sync/driver/test_sync_user_settings.h
index 3c0fd09..1de10f1 100644
--- a/components/sync/driver/test_sync_user_settings.h
+++ b/components/sync/driver/test_sync_user_settings.h
@@ -10,8 +10,6 @@
 
 #include "components/sync/driver/sync_user_settings.h"
 
-struct CoreAccountId;
-
 namespace syncer {
 
 class TestSyncService;
@@ -54,7 +52,7 @@
   void SetEncryptionPassphrase(const std::string& passphrase) override;
   bool SetDecryptionPassphrase(const std::string& passphrase) override;
   void AddTrustedVaultDecryptionKeys(
-      const CoreAccountId& account_id,
+      const std::string& gaia_id,
       const std::vector<std::string>& keys) override;
 
   void SetFirstSetupComplete();
diff --git a/components/sync/driver/trusted_vault_client.h b/components/sync/driver/trusted_vault_client.h
index 3ac50ad..bde10024 100644
--- a/components/sync/driver/trusted_vault_client.h
+++ b/components/sync/driver/trusted_vault_client.h
@@ -11,8 +11,6 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 
-struct CoreAccountId;
-
 namespace syncer {
 
 // Interface that allows platform-specific logic related to accessing locally
@@ -26,14 +24,14 @@
   // Implementations are expected to NOT prompt the user for actions. |cb| is
   // called on completion with known keys or an empty list if none known.
   virtual void FetchKeys(
-      const CoreAccountId& account_id,
+      const std::string& gaia_id,
       base::OnceCallback<void(const std::vector<std::string>&)> cb) = 0;
 
   // Allows implementations to store encryption keys fetched by other means such
   // as Web interactions. Implementations are free to completely ignore these
   // keys, so callers may not assume that later calls to FetchKeys() would
   // necessarily return the keys passed here.
-  virtual void StoreKeys(const CoreAccountId& account_id,
+  virtual void StoreKeys(const std::string& gaia_id,
                          const std::vector<std::string>& keys) = 0;
 
  private:
diff --git a/components/sync/nigori/cryptographer_impl.cc b/components/sync/nigori/cryptographer_impl.cc
index bf664725..981bdc59 100644
--- a/components/sync/nigori/cryptographer_impl.cc
+++ b/components/sync/nigori/cryptographer_impl.cc
@@ -88,11 +88,15 @@
   return key_bag_.ExportKey(default_encryption_key_name_);
 }
 
-std::unique_ptr<Cryptographer> CryptographerImpl::Clone() const {
+std::unique_ptr<CryptographerImpl> CryptographerImpl::CloneImpl() const {
   return base::WrapUnique(
       new CryptographerImpl(key_bag_.Clone(), default_encryption_key_name_));
 }
 
+std::unique_ptr<Cryptographer> CryptographerImpl::Clone() const {
+  return CloneImpl();
+}
+
 bool CryptographerImpl::CanEncrypt() const {
   return !default_encryption_key_name_.empty();
 }
diff --git a/components/sync/nigori/cryptographer_impl.h b/components/sync/nigori/cryptographer_impl.h
index bdfc620..5002637 100644
--- a/components/sync/nigori/cryptographer_impl.h
+++ b/components/sync/nigori/cryptographer_impl.h
@@ -69,6 +69,9 @@
   // have a default encryption key set, as reflected by CanEncrypt().
   sync_pb::NigoriKey ExportDefaultKey() const;
 
+  // Similar to Clone() but returns CryptographerImpl.
+  std::unique_ptr<CryptographerImpl> CloneImpl() const;
+
   // Cryptographer overrides.
   std::unique_ptr<Cryptographer> Clone() const override;
   bool CanEncrypt() const override;
diff --git a/components/sync/nigori/nigori_local_change_processor.h b/components/sync/nigori/nigori_local_change_processor.h
index b5c8507..fb03e80b 100644
--- a/components/sync/nigori/nigori_local_change_processor.h
+++ b/components/sync/nigori/nigori_local_change_processor.h
@@ -50,6 +50,10 @@
   // Informs the Nigori processor of a new or updated Nigori entity.
   virtual void Put(std::unique_ptr<EntityData> entity_data) = 0;
 
+  // Returns true the Nigori entity as tracked by the processor has local
+  // changes. A commit may or may not be in progress at this time.
+  virtual bool IsEntityUnsynced() = 0;
+
   // Returns both the entity metadata and model type state such that the Nigori
   // model takes care of persisting them.
   virtual NigoriMetadataBatch GetMetadata() = 0;
diff --git a/components/sync/nigori/nigori_model_type_processor.cc b/components/sync/nigori/nigori_model_type_processor.cc
index f9b67c7..77ce8b2 100644
--- a/components/sync/nigori/nigori_model_type_processor.cc
+++ b/components/sync/nigori/nigori_model_type_processor.cc
@@ -335,6 +335,15 @@
   NudgeForCommitIfNeeded();
 }
 
+bool NigoriModelTypeProcessor::IsEntityUnsynced() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (entity_ == nullptr) {
+    return false;
+  }
+
+  return entity_->IsUnsynced();
+}
+
 NigoriMetadataBatch NigoriModelTypeProcessor::GetMetadata() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(IsTrackingMetadata());
diff --git a/components/sync/nigori/nigori_model_type_processor.h b/components/sync/nigori/nigori_model_type_processor.h
index 39d4ccc5..0121473 100644
--- a/components/sync/nigori/nigori_model_type_processor.h
+++ b/components/sync/nigori/nigori_model_type_processor.h
@@ -50,6 +50,7 @@
   void ModelReadyToSync(NigoriSyncBridge* bridge,
                         NigoriMetadataBatch nigori_metadata) override;
   void Put(std::unique_ptr<EntityData> entity_data) override;
+  bool IsEntityUnsynced() override;
   NigoriMetadataBatch GetMetadata() override;
   void ReportError(const ModelError& error) override;
   base::WeakPtr<ModelTypeControllerDelegate> GetControllerDelegate() override;
diff --git a/components/sync/nigori/nigori_state.cc b/components/sync/nigori/nigori_state.cc
index df20b2e..8f9e3e9 100644
--- a/components/sync/nigori/nigori_state.cc
+++ b/components/sync/nigori/nigori_state.cc
@@ -303,4 +303,19 @@
   return specifics;
 }
 
+NigoriState NigoriState::Clone() const {
+  NigoriState result;
+  result.cryptographer = cryptographer->CloneImpl();
+  result.pending_keys = pending_keys;
+  result.passphrase_type = passphrase_type;
+  result.keystore_migration_time = keystore_migration_time;
+  result.custom_passphrase_time = custom_passphrase_time;
+  result.custom_passphrase_key_derivation_params =
+      custom_passphrase_key_derivation_params;
+  result.encrypt_everything = encrypt_everything;
+  result.keystore_keys = keystore_keys;
+  result.pending_keystore_decryptor_token = pending_keystore_decryptor_token;
+  return result;
+}
+
 }  // namespace syncer
diff --git a/components/sync/nigori/nigori_state.h b/components/sync/nigori/nigori_state.h
index 5ac9060..9ade4694 100644
--- a/components/sync/nigori/nigori_state.h
+++ b/components/sync/nigori/nigori_state.h
@@ -52,6 +52,9 @@
   // Serialization to proto as sent to the sync server.
   sync_pb::NigoriSpecifics ToSpecificsProto() const;
 
+  // Makes a deep copy of |this|.
+  NigoriState Clone() const;
+
   std::unique_ptr<CryptographerImpl> cryptographer;
 
   // Pending keys represent a remote update that contained a keybag that cannot
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.cc b/components/sync/nigori/nigori_sync_bridge_impl.cc
index 33c68b2..e9ee597 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl.cc
@@ -7,6 +7,8 @@
 #include <utility>
 
 #include "base/base64.h"
+#include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/location.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/sync/base/encryptor.h"
@@ -17,6 +19,7 @@
 #include "components/sync/model/entity_data.h"
 #include "components/sync/nigori/nigori.h"
 #include "components/sync/nigori/nigori_storage.h"
+#include "components/sync/nigori/pending_local_nigori_commit.h"
 #include "components/sync/protocol/encryption.pb.h"
 #include "components/sync/protocol/nigori_local_data.pb.h"
 #include "components/sync/protocol/nigori_specifics.pb.h"
@@ -52,36 +55,6 @@
   return state.ToSpecificsProto();
 }
 
-// Returns the key derivation method to be used when a user sets a new
-// custom passphrase.
-KeyDerivationMethod GetDefaultKeyDerivationMethodForCustomPassphrase() {
-  if (base::FeatureList::IsEnabled(
-          switches::kSyncUseScryptForNewCustomPassphrases) &&
-      !base::FeatureList::IsEnabled(
-          switches::kSyncForceDisableScryptForCustomPassphrase)) {
-    return KeyDerivationMethod::SCRYPT_8192_8_11;
-  }
-
-  return KeyDerivationMethod::PBKDF2_HMAC_SHA1_1003;
-}
-
-KeyDerivationParams CreateKeyDerivationParamsForCustomPassphrase(
-    const base::RepeatingCallback<std::string()>& random_salt_generator) {
-  KeyDerivationMethod method =
-      GetDefaultKeyDerivationMethodForCustomPassphrase();
-  switch (method) {
-    case KeyDerivationMethod::PBKDF2_HMAC_SHA1_1003:
-      return KeyDerivationParams::CreateForPbkdf2();
-    case KeyDerivationMethod::SCRYPT_8192_8_11:
-      return KeyDerivationParams::CreateForScrypt(random_salt_generator.Run());
-    case KeyDerivationMethod::UNSUPPORTED:
-      break;
-  }
-
-  NOTREACHED();
-  return KeyDerivationParams::CreateWithUnsupportedMethod();
-}
-
 KeyDerivationMethod GetKeyDerivationMethodFromSpecifics(
     const sync_pb::NigoriSpecifics& specifics) {
   KeyDerivationMethod key_derivation_method = ProtoKeyDerivationMethodToEnum(
@@ -330,7 +303,10 @@
 class NigoriSyncBridgeImpl::BroadcastingObserver
     : public SyncEncryptionHandler::Observer {
  public:
-  BroadcastingObserver() = default;
+  explicit BroadcastingObserver(
+      const base::RepeatingClosure& post_passphrase_accepted_cb)
+      : post_passphrase_accepted_cb_(post_passphrase_accepted_cb) {}
+
   ~BroadcastingObserver() override = default;
 
   void AddObserver(SyncEncryptionHandler::Observer* observer) {
@@ -356,6 +332,7 @@
     for (auto& observer : observers_) {
       observer.OnPassphraseAccepted();
     }
+    post_passphrase_accepted_cb_.Run();
   }
 
   void OnTrustedVaultKeyRequired() override {
@@ -410,6 +387,8 @@
   // implementation to use checked ObserverList as well.
   base::ObserverList<SyncEncryptionHandler::Observer>::Unchecked observers_;
 
+  const base::RepeatingClosure post_passphrase_accepted_cb_;
+
   DISALLOW_COPY_AND_ASSIGN(BroadcastingObserver);
 };
 
@@ -426,7 +405,12 @@
       explicit_passphrase_key_(
           UnpackExplicitPassphraseKey(*encryptor,
                                       packed_explicit_passphrase_key)),
-      broadcasting_observer_(std::make_unique<BroadcastingObserver>()) {
+      broadcasting_observer_(std::make_unique<BroadcastingObserver>(
+          // base::Unretained() legit because the observer gets destroyed
+          // together with |this|.
+          base::BindRepeating(
+              &NigoriSyncBridgeImpl::MaybeNotifyBootstrapTokenUpdated,
+              base::Unretained(this)))) {
   DCHECK(encryptor);
 
   // TODO(crbug.com/922900): we currently don't verify |deserialized_data|.
@@ -512,68 +496,9 @@
 void NigoriSyncBridgeImpl::SetEncryptionPassphrase(
     const std::string& passphrase) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  switch (state_.passphrase_type) {
-    case NigoriSpecifics::UNKNOWN:
-      NOTREACHED();
-      return;
-    case NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE:
-    case NigoriSpecifics::CUSTOM_PASSPHRASE:
-      // Attempt to set the explicit passphrase when one was already set. It's
-      // possible if we received new NigoriSpecifics during the passphrase
-      // setup.
-      DVLOG(1) << "Attempt to set explicit passphrase failed, because one was "
-                  "already set.";
-      // TODO(crbug.com/922900): investigate whether we need to call
-      // OnPassphraseRequired() to prompt for decryption passphrase.
-      return;
-    case NigoriSpecifics::IMPLICIT_PASSPHRASE:
-    case NigoriSpecifics::KEYSTORE_PASSPHRASE:
-    case NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE:
-      if (state_.pending_keys.has_value()) {
-        // TODO(crbug.com/922900): investigate whether we need to call
-        // MaybeNotifyOfPendingKeys() to prompt for decryption passphrase.
-        DVLOG(1) << "Attempt to set explicit passphrase failed, because there "
-                 << "are pending keys.";
-        return;
-      }
-      break;
-  }
-  DCHECK(!state_.pending_keys.has_value());
-  DCHECK(state_.cryptographer->CanEncrypt());
 
-  const KeyDerivationParams custom_passphrase_key_derivation_params =
-      CreateKeyDerivationParamsForCustomPassphrase(random_salt_generator_);
-
-  const std::string default_key_name = state_.cryptographer->EmplaceKey(
-      passphrase, custom_passphrase_key_derivation_params);
-  if (default_key_name.empty()) {
-    DLOG(ERROR) << "Failed to set encryption passphrase";
-    return;
-  }
-
-  state_.cryptographer->SelectDefaultEncryptionKey(default_key_name);
-  state_.pending_keystore_decryptor_token.reset();
-  state_.passphrase_type = NigoriSpecifics::CUSTOM_PASSPHRASE;
-  state_.custom_passphrase_key_derivation_params =
-      custom_passphrase_key_derivation_params;
-
-  state_.encrypt_everything = true;
-  state_.custom_passphrase_time = base::Time::Now();
-  processor_->Put(GetData());
-  storage_->StoreData(SerializeAsNigoriLocalData());
-
-  broadcasting_observer_->OnPassphraseAccepted();
-  broadcasting_observer_->OnPassphraseTypeChanged(
-      PassphraseType::kCustomPassphrase, state_.custom_passphrase_time);
-  broadcasting_observer_->OnCryptographerStateChanged(
-      state_.cryptographer.get(), state_.pending_keys.has_value());
-  broadcasting_observer_->OnEncryptedTypesChanged(EncryptableUserTypes(),
-                                                  state_.encrypt_everything);
-
-  MaybeNotifyBootstrapTokenUpdated();
-  UMA_HISTOGRAM_BOOLEAN("Sync.CustomEncryption", true);
-  // OnLocalSetPassphraseEncryption() is intentionally not called here, because
-  // it's needed only for the Directory implementation unit tests.
+  QueuePendingLocalCommit(PendingLocalNigoriCommit::ForSetCustomPassphrase(
+      passphrase, random_salt_generator_));
 }
 
 void NigoriSyncBridgeImpl::SetDecryptionPassphrase(
@@ -611,7 +536,6 @@
       state_.cryptographer.get(), state_.pending_keys.has_value());
   broadcasting_observer_->OnPassphraseAccepted();
 
-  MaybeNotifyBootstrapTokenUpdated();
   // TODO(crbug.com/922900): we may need to rewrite encryption_keybag in Nigori
   // node in case we have some keys in |cryptographer_| which is not stored in
   // encryption_keybag yet.
@@ -764,6 +688,7 @@
   }
   // We received uninitialized Nigori and need to initialize it as default
   // keystore Nigori.
+  // TODO(crbug.com/922900): Adopt QueuePendingLocalCommit().
   NigoriSpecifics initialized_specifics =
       MakeDefaultKeystoreNigori(state_.keystore_keys);
   // In rare cases the crypto operations may fail.
@@ -779,14 +704,28 @@
     base::Optional<EntityData> data) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_NE(state_.passphrase_type, NigoriSpecifics::UNKNOWN);
-  if (!data) {
-    // Receiving empty |data| means metadata-only change, we need to persist
-    // its state.
-    storage_->StoreData(SerializeAsNigoriLocalData());
-    return base::nullopt;
+
+  if (data) {
+    DCHECK(data->specifics.has_nigori());
+    return UpdateLocalState(data->specifics.nigori());
   }
-  DCHECK(data->specifics.has_nigori());
-  return UpdateLocalState(data->specifics.nigori());
+
+  if (!pending_local_commit_queue_.empty() && !processor_->IsEntityUnsynced()) {
+    // Successfully committed first element in queue.
+    bool success = pending_local_commit_queue_.front()->TryApply(&state_);
+    DCHECK(success);
+    pending_local_commit_queue_.front()->OnSuccess(
+        state_, broadcasting_observer_.get());
+    pending_local_commit_queue_.pop_front();
+
+    // Advance until the next applicable local change if any and call Put().
+    PutNextApplicablePendingLocalCommit();
+  }
+
+  // Receiving empty |data| means metadata-only change (e.g. no remote updates,
+  // or local commit completion), so we need to persist its state.
+  storage_->StoreData(SerializeAsNigoriLocalData());
+  return base::nullopt;
 }
 
 base::Optional<ModelError> NigoriSyncBridgeImpl::UpdateLocalState(
@@ -856,7 +795,6 @@
       UpdateCryptographerFromNonKeystoreNigori(encryption_keybag);
   }
 
-  storage_->StoreData(SerializeAsNigoriLocalData());
   if (passphrase_type_changed) {
     broadcasting_observer_->OnPassphraseTypeChanged(
         *ProtoPassphraseInt32ToEnum(state_.passphrase_type),
@@ -873,6 +811,18 @@
       state_.cryptographer.get(), state_.pending_keys.has_value());
 
   MaybeNotifyOfPendingKeys();
+
+  // TODO(crbug.com/): Instead of issuing errors for local commits, call
+  // PutNextApplicablePendingLocalCommit() to verify pending local changes
+  // (relevant for the conflict case). Issue failures if they are no longer
+  // applicable, or call Put() otherwise.
+  for (const auto& pending_local_commit : pending_local_commit_queue_) {
+    pending_local_commit->OnFailure(broadcasting_observer_.get());
+  }
+  pending_local_commit_queue_.clear();
+
+  storage_->StoreData(SerializeAsNigoriLocalData());
+
   return base::nullopt;
 }
 
@@ -983,7 +933,19 @@
 
 std::unique_ptr<EntityData> NigoriSyncBridgeImpl::GetData() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (state_.passphrase_type == NigoriSpecifics::UNKNOWN) {
+
+  NigoriSpecifics specifics;
+  if (!pending_local_commit_queue_.empty()) {
+    NigoriState changed_state = state_.Clone();
+    bool success =
+        pending_local_commit_queue_.front()->TryApply(&changed_state);
+    DCHECK(success);
+    specifics = changed_state.ToSpecificsProto();
+  } else {
+    specifics = state_.ToSpecificsProto();
+  }
+
+  if (specifics.passphrase_type() == NigoriSpecifics::UNKNOWN) {
     // Bridge never received NigoriSpecifics from the server. This line should
     // be reachable only from processor's GetAllNodesForDebugging().
     DCHECK(!state_.cryptographer->CanEncrypt());
@@ -991,7 +953,6 @@
     return nullptr;
   }
 
-  const sync_pb::NigoriSpecifics specifics = state_.ToSpecificsProto();
   DCHECK(IsValidNigoriSpecifics(specifics));
 
   auto entity_data = std::make_unique<EntityData>();
@@ -1160,4 +1121,38 @@
   return output;
 }
 
+void NigoriSyncBridgeImpl::QueuePendingLocalCommit(
+    std::unique_ptr<PendingLocalNigoriCommit> local_commit) {
+  NigoriState tmp_state = state_.Clone();
+  if (state_.passphrase_type == NigoriSpecifics::UNKNOWN) {
+    local_commit->OnFailure(broadcasting_observer_.get());
+    return;
+  }
+
+  pending_local_commit_queue_.push_back(std::move(local_commit));
+
+  if (pending_local_commit_queue_.size() == 1) {
+    // Verify that the newly-introduced commit (if first in the queue) applies
+    // and if so call Put(), or otherwise issue an immediate failure.
+    PutNextApplicablePendingLocalCommit();
+  }
+}
+
+void NigoriSyncBridgeImpl::PutNextApplicablePendingLocalCommit() {
+  while (!pending_local_commit_queue_.empty()) {
+    NigoriState tmp_state = state_.Clone();
+    bool success = pending_local_commit_queue_.front()->TryApply(&tmp_state);
+    if (success) {
+      // This particular commit applies cleanly.
+      processor_->Put(GetData());
+      break;
+    }
+
+    // The local change failed to apply.
+    pending_local_commit_queue_.front()->OnFailure(
+        broadcasting_observer_.get());
+    pending_local_commit_queue_.pop_front();
+  }
+}
+
 }  // namespace syncer
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.h b/components/sync/nigori/nigori_sync_bridge_impl.h
index d5ff4f13..d941cf47 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl.h
+++ b/components/sync/nigori/nigori_sync_bridge_impl.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_SYNC_NIGORI_NIGORI_SYNC_BRIDGE_IMPL_H_
 #define COMPONENTS_SYNC_NIGORI_NIGORI_SYNC_BRIDGE_IMPL_H_
 
+#include <list>
 #include <memory>
 #include <string>
 #include <vector>
@@ -32,6 +33,7 @@
 
 class Encryptor;
 class NigoriStorage;
+class PendingLocalNigoriCommit;
 
 // USS implementation of SyncEncryptionHandler.
 // This class holds the current Nigori state and processes incoming changes and
@@ -130,6 +132,17 @@
   // Serializes state of the bridge and sync metadata into the proto.
   sync_pb::NigoriLocalData SerializeAsNigoriLocalData() const;
 
+  // Appends |local_commit| to |pending_local_commit_queue_| and if appropriate
+  // calls Put() to trigger the commit.
+  void QueuePendingLocalCommit(
+      std::unique_ptr<PendingLocalNigoriCommit> local_commit);
+
+  // Processes |pending_local_commit_queue_| FIFO such that all non-applicable
+  // pending commits issue a failure, until the first one that is applicable is
+  // found (if any). If such applicable commit is found, the corresponding Put()
+  // call is issued.
+  void PutNextApplicablePendingLocalCommit();
+
   const Encryptor* const encryptor_;
 
   const std::unique_ptr<NigoriLocalChangeProcessor> processor_;
@@ -146,6 +159,9 @@
 
   syncer::NigoriState state_;
 
+  std::list<std::unique_ptr<PendingLocalNigoriCommit>>
+      pending_local_commit_queue_;
+
   // Observer that owns the list of actual observers, and broadcasts
   // notifications to all observers in the list.
   class BroadcastingObserver;
diff --git a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
index a25188d73..3d57c05 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
@@ -292,6 +292,7 @@
 
   MOCK_METHOD2(ModelReadyToSync, void(NigoriSyncBridge*, NigoriMetadataBatch));
   MOCK_METHOD1(Put, void(std::unique_ptr<EntityData>));
+  MOCK_METHOD0(IsEntityUnsynced, bool());
   MOCK_METHOD0(GetMetadata, NigoriMetadataBatch());
   MOCK_METHOD1(ReportError, void(const ModelError&));
   MOCK_METHOD0(GetControllerDelegate,
@@ -877,6 +878,8 @@
 // passphrase.
 TEST_F(NigoriSyncBridgeImplTest,
        ShouldPutAndNotifyObserversWhenSetEncryptionPassphrase) {
+  const std::string kCustomPassphrase = "passphrase";
+
   EntityData default_entity_data;
   *default_entity_data.specifics.mutable_nigori() =
       sync_pb::NigoriSpecifics::default_instance();
@@ -885,7 +888,13 @@
               Eq(base::nullopt));
   ASSERT_THAT(bridge()->GetData(), Not(HasCustomPassphraseNigori()));
 
-  const std::string passphrase = "passphrase";
+  // Calling SetEncryptionPassphrase() triggers a commit cycle but doesn't
+  // immediately expose the new state, until the commit completes.
+  EXPECT_CALL(*processor(), Put(HasCustomPassphraseNigori()));
+  bridge()->SetEncryptionPassphrase(kCustomPassphrase);
+  EXPECT_THAT(bridge()->GetData(), HasCustomPassphraseNigori());
+
+  // Mimic commit completion.
   EXPECT_CALL(*observer(), OnPassphraseAccepted());
   EXPECT_CALL(*observer(), OnEncryptedTypesChanged(
                                /*encrypted_types=*/EncryptableUserTypes(),
@@ -897,8 +906,7 @@
                                       /*passphrase_time=*/Not(NullTime())));
   EXPECT_CALL(*observer(), OnBootstrapTokenUpdated(Ne(std::string()),
                                                    PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(*processor(), Put(HasCustomPassphraseNigori()));
-  bridge()->SetEncryptionPassphrase(passphrase);
+  EXPECT_THAT(bridge()->ApplySyncChanges(base::nullopt), Eq(base::nullopt));
   EXPECT_THAT(bridge()->GetData(), HasCustomPassphraseNigori());
 
   // TODO(crbug.com/922900): find a good way to get key derivation method and
@@ -1178,6 +1186,13 @@
               Eq(sync_pb::NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE));
   ASSERT_THAT(bridge()->GetData(), Not(HasCustomPassphraseNigori()));
 
+  // Calling SetEncryptionPassphrase() triggers a commit cycle but doesn't
+  // immediately expose the new state, until the commit completes.
+  EXPECT_CALL(*processor(), Put(HasCustomPassphraseNigori()));
+  bridge()->SetEncryptionPassphrase(kCustomPassphrase);
+  EXPECT_THAT(bridge()->GetData(), HasCustomPassphraseNigori());
+
+  // Mimic commit completion.
   EXPECT_CALL(*observer(), OnPassphraseAccepted());
   EXPECT_CALL(*observer(), OnEncryptedTypesChanged(
                                /*encrypted_types=*/EncryptableUserTypes(),
@@ -1189,8 +1204,7 @@
                                       /*passphrase_time=*/Not(NullTime())));
   EXPECT_CALL(*observer(), OnBootstrapTokenUpdated(Ne(std::string()),
                                                    PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(*processor(), Put(HasCustomPassphraseNigori()));
-  bridge()->SetEncryptionPassphrase(kCustomPassphrase);
+  EXPECT_THAT(bridge()->ApplySyncChanges(base::nullopt), Eq(base::nullopt));
   EXPECT_THAT(bridge()->GetData(), HasCustomPassphraseNigori());
 }
 
diff --git a/components/sync/nigori/pending_local_nigori_commit.cc b/components/sync/nigori/pending_local_nigori_commit.cc
new file mode 100644
index 0000000..8e48e40c
--- /dev/null
+++ b/components/sync/nigori/pending_local_nigori_commit.cc
@@ -0,0 +1,143 @@
+// Copyright 2019 The Chromium 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/sync/nigori/pending_local_nigori_commit.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/time/time.h"
+#include "components/sync/base/sync_base_switches.h"
+#include "components/sync/engine/sync_engine_switches.h"
+#include "components/sync/nigori/cryptographer_impl.h"
+#include "components/sync/nigori/nigori_state.h"
+
+namespace syncer {
+
+namespace {
+
+using sync_pb::NigoriSpecifics;
+
+// Returns the key derivation method to be used when a user sets a new
+// custom passphrase.
+KeyDerivationMethod GetDefaultKeyDerivationMethodForCustomPassphrase() {
+  if (base::FeatureList::IsEnabled(
+          switches::kSyncUseScryptForNewCustomPassphrases) &&
+      !base::FeatureList::IsEnabled(
+          switches::kSyncForceDisableScryptForCustomPassphrase)) {
+    return KeyDerivationMethod::SCRYPT_8192_8_11;
+  }
+
+  return KeyDerivationMethod::PBKDF2_HMAC_SHA1_1003;
+}
+
+KeyDerivationParams CreateKeyDerivationParamsForCustomPassphrase(
+    const base::RepeatingCallback<std::string()>& random_salt_generator) {
+  KeyDerivationMethod method =
+      GetDefaultKeyDerivationMethodForCustomPassphrase();
+  switch (method) {
+    case KeyDerivationMethod::PBKDF2_HMAC_SHA1_1003:
+      return KeyDerivationParams::CreateForPbkdf2();
+    case KeyDerivationMethod::SCRYPT_8192_8_11:
+      return KeyDerivationParams::CreateForScrypt(random_salt_generator.Run());
+    case KeyDerivationMethod::UNSUPPORTED:
+      break;
+  }
+
+  NOTREACHED();
+  return KeyDerivationParams::CreateWithUnsupportedMethod();
+}
+
+class CustomPassphraseSetter : public PendingLocalNigoriCommit {
+ public:
+  CustomPassphraseSetter(
+      const std::string& passphrase,
+      const base::RepeatingCallback<std::string()>& random_salt_generator)
+      : passphrase_(passphrase),
+        key_derivation_params_(CreateKeyDerivationParamsForCustomPassphrase(
+            random_salt_generator)) {}
+
+  ~CustomPassphraseSetter() override = default;
+
+  bool TryApply(NigoriState* state) const override {
+    if (state->pending_keys.has_value()) {
+      return false;
+    }
+
+    switch (state->passphrase_type) {
+      case NigoriSpecifics::UNKNOWN:
+        return false;
+      case NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE:
+      case NigoriSpecifics::CUSTOM_PASSPHRASE:
+        // Attempt to set the explicit passphrase when one was already set. It's
+        // possible if we received new NigoriSpecifics during the passphrase
+        // setup.
+        DVLOG(1)
+            << "Attempt to set explicit passphrase failed, because one was "
+               "already set.";
+        return false;
+      case NigoriSpecifics::IMPLICIT_PASSPHRASE:
+      case NigoriSpecifics::KEYSTORE_PASSPHRASE:
+      case NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE:
+        break;
+    }
+
+    const std::string default_key_name =
+        state->cryptographer->EmplaceKey(passphrase_, key_derivation_params_);
+    if (default_key_name.empty()) {
+      DLOG(ERROR) << "Failed to set encryption passphrase";
+      return false;
+    }
+
+    state->cryptographer->SelectDefaultEncryptionKey(default_key_name);
+    state->pending_keystore_decryptor_token.reset();
+    state->passphrase_type = NigoriSpecifics::CUSTOM_PASSPHRASE;
+    state->custom_passphrase_key_derivation_params = key_derivation_params_;
+    state->encrypt_everything = true;
+    state->custom_passphrase_time = base::Time::Now();
+
+    return true;
+  }
+
+  void OnSuccess(const NigoriState& state,
+                 SyncEncryptionHandler::Observer* observer) override {
+    DCHECK(!state.pending_keys.has_value());
+
+    observer->OnPassphraseAccepted();
+    observer->OnPassphraseTypeChanged(PassphraseType::kCustomPassphrase,
+                                      state.custom_passphrase_time);
+    observer->OnCryptographerStateChanged(state.cryptographer.get(),
+                                          /*has_pending_keys=*/false);
+    observer->OnEncryptedTypesChanged(EncryptableUserTypes(),
+                                      /*encrypt_everything=*/true);
+
+    UMA_HISTOGRAM_BOOLEAN("Sync.CustomEncryption", true);
+
+    // OnLocalSetPassphraseEncryption() is intentionally not called here,
+    // because it's needed only for the Directory implementation unit tests.
+  }
+
+  void OnFailure(SyncEncryptionHandler::Observer* observer) override {
+    // TODO(crbug.com/922900): investigate whether we need to call
+    // OnPassphraseRequired() to prompt for decryption passphrase.
+  }
+
+ private:
+  const std::string passphrase_;
+  const KeyDerivationParams key_derivation_params_;
+
+  DISALLOW_COPY_AND_ASSIGN(CustomPassphraseSetter);
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<PendingLocalNigoriCommit>
+PendingLocalNigoriCommit::ForSetCustomPassphrase(
+    const std::string& passphrase,
+    const base::RepeatingCallback<std::string()>& random_salt_generator) {
+  return std::make_unique<CustomPassphraseSetter>(passphrase,
+                                                  random_salt_generator);
+}
+
+}  // namespace syncer
diff --git a/components/sync/nigori/pending_local_nigori_commit.h b/components/sync/nigori/pending_local_nigori_commit.h
new file mode 100644
index 0000000..e2f3895
--- /dev/null
+++ b/components/sync/nigori/pending_local_nigori_commit.h
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SYNC_NIGORI_PENDING_LOCAL_NIGORI_COMMIT_H_
+#define COMPONENTS_SYNC_NIGORI_PENDING_LOCAL_NIGORI_COMMIT_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/sync/engine/sync_encryption_handler.h"
+
+namespace syncer {
+
+struct NigoriState;
+
+// Interface representing an intended local change to the Nigori state that
+// is pending a commit to the sync server.
+class PendingLocalNigoriCommit {
+ public:
+  static std::unique_ptr<PendingLocalNigoriCommit> ForSetCustomPassphrase(
+      const std::string& passphrase,
+      const base::RepeatingCallback<std::string()>& random_salt_generator);
+
+  PendingLocalNigoriCommit() = default;
+  virtual ~PendingLocalNigoriCommit() = default;
+
+  // Attempts to modify |*state| to reflect the intended commit. Returns true if
+  // the change was successfully applied (which may include the no-op case) or
+  // false if it no longer applies (leading to OnFailure()).
+  //
+  // |state| must not be null.
+  virtual bool TryApply(NigoriState* state) const = 0;
+
+  // Invoked when the commit has been successfully acked by the server.
+  // |observer| must not be null.
+  virtual void OnSuccess(const NigoriState& state,
+                         SyncEncryptionHandler::Observer* observer) = 0;
+
+  // Invoked when the change no longer applies or was aborted for a different
+  // reason (e.g. sync disabled). |observer| must not be null.
+  virtual void OnFailure(SyncEncryptionHandler::Observer* observer) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PendingLocalNigoriCommit);
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_NIGORI_PENDING_LOCAL_NIGORI_COMMIT_H_
diff --git a/components/sync/syncable/directory_backing_store.cc b/components/sync/syncable/directory_backing_store.cc
index bfa5e5e..ead2463 100644
--- a/components/sync/syncable/directory_backing_store.cc
+++ b/components/sync/syncable/directory_backing_store.cc
@@ -15,11 +15,11 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/single_thread_task_runner.h"
+#include "base/sequenced_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -228,10 +228,10 @@
   // An error has been detected. Ignore unless it is catastrophic.
   if (sql::IsErrorCatastrophic(err)) {
     // At this point sql::* and DirectoryBackingStore may be on the callstack so
-    // don't invoke the error handler directly. Instead, PostTask to this thread
-    // to avoid potential reentrancy issues.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                  catastrophic_error_handler);
+    // don't invoke the error handler directly. Instead, PostTask to this
+    // sequence to avoid potential reentrancy issues.
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, catastrophic_error_handler);
   }
 }
 
@@ -283,7 +283,7 @@
       database_page_size_(kCurrentPageSizeKB),
       needs_metas_column_refresh_(false),
       needs_share_info_column_refresh_(false) {
-  DCHECK(base::ThreadTaskRunnerHandle::IsSet());
+  DCHECK(base::SequencedTaskRunnerHandle::IsSet());
   ResetAndCreateConnection();
 }
 
@@ -297,7 +297,7 @@
       db_(db),
       needs_metas_column_refresh_(false),
       needs_share_info_column_refresh_(false) {
-  DCHECK(base::ThreadTaskRunnerHandle::IsSet());
+  DCHECK(base::SequencedTaskRunnerHandle::IsSet());
 }
 
 DirectoryBackingStore::~DirectoryBackingStore() {
diff --git a/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java b/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java
index 6a2b5ee..4c5a844 100644
--- a/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java
+++ b/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java
@@ -30,6 +30,7 @@
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.Locale;
 
 /**
@@ -139,6 +140,8 @@
         public boolean isGzipCompressed;
         public byte[] seedData;
 
+        // TODO(crbug.com/1013390): Delete once Date header to timestamp migration is done (~M81).
+        @Deprecated
         public static long parseDateHeader(String header) throws ParseException {
             // The date field comes from the HTTP "Date" header, which has this format.
             // (See RFC 2616, sections 3.3.1 and 14.18.) SimpleDateFormat is weirdly not
@@ -186,7 +189,7 @@
                         VariationsPlatform.ANDROID, restrictMode, milestone, channel);
                 VariationsSeedBridge.setVariationsFirstRunSeed(info.seedData, info.signature,
                         info.country, info.date, info.isGzipCompressed);
-            } catch (IOException | ParseException e) {
+            } catch (IOException e) {
                 Log.e(TAG, "Exception when fetching variations seed.", e);
                 // Exceptions are handled and logged in the downloadContent method, so we don't
                 // need any exception handling here. The only reason we need a catch-statement here
@@ -225,7 +228,6 @@
      * @param milestone the milestone parameter to pass to the server via a URL param.
      * @param channel the channel parameter to pass to the server via a URL param.
      * @return the object holds the seed data and its related header fields.
-     * @throws ParseException when the seed response has an invalid Date header.
      * @throws SocketTimeoutException when fetching seed connection times out.
      * @throws UnknownHostException when fetching seed connection has an unknown host.
      * @throws IOException when response code is not HTTP_OK or transmission fails on the open
@@ -233,7 +235,7 @@
      */
     public SeedInfo downloadContent(
             @VariationsPlatform int platform, String restrictMode, String milestone, String channel)
-            throws ParseException, SocketTimeoutException, UnknownHostException, IOException {
+            throws SocketTimeoutException, UnknownHostException, IOException {
         HttpURLConnection connection = null;
         try {
             long startTimeMillis = SystemClock.elapsedRealtime();
@@ -257,14 +259,10 @@
             info.seedData = getRawSeed(connection);
             info.signature = getHeaderFieldOrEmpty(connection, "X-Seed-Signature");
             info.country = getHeaderFieldOrEmpty(connection, "X-Country");
-            info.date = SeedInfo.parseDateHeader(getHeaderFieldOrEmpty(connection, "Date"));
+            info.date = new Date().getTime();
             info.isGzipCompressed = getHeaderFieldOrEmpty(connection, "IM").equals("gzip");
             recordSeedFetchTime(SystemClock.elapsedRealtime() - startTimeMillis);
             return info;
-        } catch (ParseException e) {
-            recordFetchResultOrCode(SEED_FETCH_RESULT_INVALID_DATE_HEADER);
-            Log.e(TAG, "ParseException parsing Date header when fetching variations seed.", e);
-            throw e;
         } catch (SocketTimeoutException e) {
             recordFetchResultOrCode(SEED_FETCH_RESULT_TIMEOUT);
             Log.w(TAG, "SocketTimeoutException timeout when fetching variations seed.", e);
diff --git a/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java b/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java
index 87cc5ebd..40090687 100644
--- a/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java
+++ b/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java
@@ -5,6 +5,8 @@
 package org.chromium.components.variations.firstrun;
 
 import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -30,12 +32,11 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.components.variations.firstrun.VariationsSeedFetcher.SeedInfo;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.net.HttpURLConnection;
-import java.text.ParseException;
+import java.util.Date;
 
 /**
  * Tests for VariationsSeedFetcher
@@ -43,8 +44,6 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class VariationsSeedFetcherTest {
-    private static final String RFC_822_DATE = "Sat, 01 Jan 2000 00:00:00 GMT";
-
     private HttpURLConnection mConnection;
     private VariationsSeedFetcher mFetcher;
     private SharedPreferences mPrefs;
@@ -78,26 +77,32 @@
      * @throws IOException
      */
     @Test
-    public void testFetchSeed() throws IOException, ParseException {
+    public void testFetchSeed() throws IOException {
         // Pretend we are on a background thread; set the UI thread looper to something other than
         // the current thread.
 
         when(mConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
         when(mConnection.getHeaderField("X-Seed-Signature")).thenReturn("signature");
         when(mConnection.getHeaderField("X-Country")).thenReturn("Nowhere Land");
-        when(mConnection.getHeaderField("Date")).thenReturn(RFC_822_DATE);
         when(mConnection.getHeaderField("IM")).thenReturn("gzip");
         when(mConnection.getInputStream())
                 .thenReturn(new ByteArrayInputStream(ApiCompatibilityUtils.getBytesUtf8("1234")));
 
+        long startTime = new Date().getTime();
         mFetcher.fetchSeed(sRestrict, sMilestone, sChannel);
+        long endTime = new Date().getTime();
 
         assertThat(mPrefs.getString(VariationsSeedBridge.VARIATIONS_FIRST_RUN_SEED_SIGNATURE, ""),
                 equalTo("signature"));
         assertThat(mPrefs.getString(VariationsSeedBridge.VARIATIONS_FIRST_RUN_SEED_COUNTRY, ""),
                 equalTo("Nowhere Land"));
-        assertThat(mPrefs.getLong(VariationsSeedBridge.VARIATIONS_FIRST_RUN_SEED_DATE, 0),
-                equalTo(SeedInfo.parseDateHeader(RFC_822_DATE)));
+        long seedDate = mPrefs.getLong(VariationsSeedBridge.VARIATIONS_FIRST_RUN_SEED_DATE, 0);
+        // We use *OrEqualTo comparisons here to account for when both points in time fall into the
+        // same tick of the clock.
+        assertThat("Seed date should be after the test start time", seedDate,
+                greaterThanOrEqualTo(startTime));
+        assertThat("Seed date should be before the test end time", seedDate,
+                lessThanOrEqualTo(endTime));
         assertTrue(mPrefs.getBoolean(
                 VariationsSeedBridge.VARIATIONS_FIRST_RUN_SEED_IS_GZIP_COMPRESSED, false));
         assertThat(mPrefs.getString(VariationsSeedBridge.VARIATIONS_FIRST_RUN_SEED_BASE64, ""),
diff --git a/components/variations/net/variations_http_headers.cc b/components/variations/net/variations_http_headers.cc
index c22c206b..b35f572 100644
--- a/components/variations/net/variations_http_headers.cc
+++ b/components/variations/net/variations_http_headers.cc
@@ -16,10 +16,8 @@
 #include "base/strings/string_util.h"
 #include "components/google/core/common/google_util.h"
 #include "components/variations/variations_http_header_provider.h"
-#include "net/http/http_request_headers.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/redirect_info.h"
-#include "net/url_request/url_request.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/cpp/simple_url_loader.h"
@@ -75,26 +73,19 @@
   return true;
 }
 
-constexpr network::ResourceRequest* null_resource_request = nullptr;
-constexpr net::URLRequest* null_url_request = nullptr;
-
 class VariationsHeaderHelper {
  public:
   // Note: It's OK to pass SignedIn::kNo if it's unknown, as it does not affect
   // transmission of experiments coming from the variations server.
   VariationsHeaderHelper(network::ResourceRequest* request,
                          SignedIn signed_in = SignedIn::kNo)
-      : VariationsHeaderHelper(request,
-                               null_url_request,
-                               CreateVariationsHeader(signed_in)) {}
-  VariationsHeaderHelper(net::URLRequest* request,
-                         SignedIn signed_in = SignedIn::kNo)
-      : VariationsHeaderHelper(null_resource_request,
-                               request,
-                               CreateVariationsHeader(signed_in)) {}
-  VariationsHeaderHelper(network::ResourceRequest* request,
-                         const std::string& variations_header)
-      : VariationsHeaderHelper(request, null_url_request, variations_header) {}
+      : VariationsHeaderHelper(request, CreateVariationsHeader(signed_in)) {}
+  VariationsHeaderHelper(network::ResourceRequest* resource_request,
+                         std::string variations_header)
+      : resource_request_(resource_request) {
+    DCHECK(resource_request_);
+    variations_header_ = std::move(variations_header);
+  }
 
   bool AppendHeaderIfNeeded(const GURL& url, InIncognito incognito) {
     // Note the criteria for attaching client experiment headers:
@@ -112,18 +103,10 @@
     if (variations_header_.empty())
       return false;
 
-    if (resource_request_) {
-      // Set the variations header to cors_exempt_headers rather than headers
-      // to be exempted from CORS checks.
-      resource_request_->cors_exempt_headers.SetHeaderIfMissing(
-          kClientDataHeader, variations_header_);
-    } else if (url_request_) {
-      url_request_->SetExtraRequestHeaderByName(kClientDataHeader,
-                                                variations_header_, false);
-    } else {
-      NOTREACHED();
-      return false;
-    }
+    // Set the variations header to cors_exempt_headers rather than headers
+    // to be exempted from CORS checks.
+    resource_request_->cors_exempt_headers.SetHeaderIfMissing(
+        kClientDataHeader, variations_header_);
     return true;
   }
 
@@ -133,16 +116,7 @@
         signed_in == SignedIn::kYes);
   }
 
-  VariationsHeaderHelper(network::ResourceRequest* resource_request,
-                         net::URLRequest* url_request,
-                         std::string variations_header)
-      : resource_request_(resource_request), url_request_(url_request) {
-    DCHECK(resource_request_ || url_request_);
-    variations_header_ = std::move(variations_header);
-  }
-
   network::ResourceRequest* resource_request_;
-  net::URLRequest* url_request_;
   std::string variations_header_;
 
   DISALLOW_COPY_AND_ASSIGN(VariationsHeaderHelper);
diff --git a/components/variations/service/variations_field_trial_creator.cc b/components/variations/service/variations_field_trial_creator.cc
index 01d097f..a6094726 100644
--- a/components/variations/service/variations_field_trial_creator.cc
+++ b/components/variations/service/variations_field_trial_creator.cc
@@ -409,7 +409,7 @@
   if (last_fetch_time.is_null()) {
     // If the last fetch time is missing and we have a seed, then this must be
     // the first run of Chrome. Store the current time as the last fetch time.
-    seed_store_->RecordLastFetchTime();
+    seed_store_->RecordLastFetchTime(base::Time::Now());
     RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_FETCH_TIME_MISSING);
     return true;
   }
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc
index 14e60656..46dd8bbe 100644
--- a/components/variations/service/variations_service.cc
+++ b/components/variations/service/variations_service.cc
@@ -888,7 +888,7 @@
 }
 
 void VariationsService::RecordSuccessfulFetch() {
-  field_trial_creator_.seed_store()->RecordLastFetchTime();
+  field_trial_creator_.seed_store()->RecordLastFetchTime(base::Time::Now());
   safe_seed_manager_.RecordSuccessfulFetch(field_trial_creator_.seed_store());
 }
 
diff --git a/components/variations/variations_seed_store.cc b/components/variations/variations_seed_store.cc
index 9ef4ffa8..b54cca3 100644
--- a/components/variations/variations_seed_store.cc
+++ b/components/variations/variations_seed_store.cc
@@ -311,15 +311,14 @@
   return local_state_->GetTime(prefs::kVariationsLastFetchTime);
 }
 
-void VariationsSeedStore::RecordLastFetchTime() {
-  base::Time now = base::Time::Now();
-  local_state_->SetTime(prefs::kVariationsLastFetchTime, now);
+void VariationsSeedStore::RecordLastFetchTime(base::Time fetch_time) {
+  local_state_->SetTime(prefs::kVariationsLastFetchTime, fetch_time);
 
   // If the latest and safe seeds are identical, update the fetch time for the
   // safe seed as well.
   if (local_state_->GetString(prefs::kVariationsCompressedSeed) ==
       kIdenticalToSafeSeedSentinel) {
-    local_state_->SetTime(prefs::kVariationsSafeSeedFetchTime, now);
+    local_state_->SetTime(prefs::kVariationsSafeSeedFetchTime, fetch_time);
   }
 }
 
diff --git a/components/variations/variations_seed_store.h b/components/variations/variations_seed_store.h
index 4a9de8b..839c5b6b 100644
--- a/components/variations/variations_seed_store.h
+++ b/components/variations/variations_seed_store.h
@@ -94,10 +94,10 @@
   // store.
   base::Time GetLastFetchTime() const;
 
-  // Records the current time as the last time at which a seed was fetched
+  // Records |fetch_time| as the last time at which a seed was fetched
   // successfully. Also updates the safe seed's fetch time if the latest and
   // safe seeds are identical.
-  void RecordLastFetchTime();
+  void RecordLastFetchTime(base::Time fetch_time);
 
   // Updates |kVariationsSeedDate| and logs when previous date was from a
   // different day.
diff --git a/components/variations/variations_seed_store_unittest.cc b/components/variations/variations_seed_store_unittest.cc
index 0bb8e72..91b03c1 100644
--- a/components/variations/variations_seed_store_unittest.cc
+++ b/components/variations/variations_seed_store_unittest.cc
@@ -1085,14 +1085,14 @@
   prefs.SetTime(prefs::kVariationsLastFetchTime, WrapTime(1));
   prefs.SetTime(prefs::kVariationsSafeSeedFetchTime, WrapTime(0));
 
-  base::Time start_time = base::Time::Now();
+  base::Time start_time = WrapTime(10);
   TestVariationsSeedStore seed_store(&prefs);
-  seed_store.RecordLastFetchTime();
+  seed_store.RecordLastFetchTime(WrapTime(11));
 
   // Verify that the last fetch time was updated.
   const base::Time last_fetch_time =
       prefs.GetTime(prefs::kVariationsLastFetchTime);
-  EXPECT_NE(WrapTime(1), last_fetch_time);
+  EXPECT_EQ(WrapTime(11), last_fetch_time);
   EXPECT_GE(last_fetch_time, start_time);
 
   // Verify that the safe seed's fetch time was *not* updated.
@@ -1108,14 +1108,14 @@
   prefs.SetTime(prefs::kVariationsLastFetchTime, WrapTime(1));
   prefs.SetTime(prefs::kVariationsSafeSeedFetchTime, WrapTime(0));
 
-  base::Time start_time = base::Time::Now();
+  base::Time start_time = WrapTime(10);
   TestVariationsSeedStore seed_store(&prefs);
-  seed_store.RecordLastFetchTime();
+  seed_store.RecordLastFetchTime(WrapTime(11));
 
   // Verify that the last fetch time was updated.
   const base::Time last_fetch_time =
       prefs.GetTime(prefs::kVariationsLastFetchTime);
-  EXPECT_NE(WrapTime(1), last_fetch_time);
+  EXPECT_EQ(WrapTime(11), last_fetch_time);
   EXPECT_GE(last_fetch_time, start_time);
 
   // Verify that the safe seed's fetch time *was* also updated.
diff --git a/components/viz/demo/demo_main.cc b/components/viz/demo/demo_main.cc
index e40e9eb..61be4e90 100644
--- a/components/viz/demo/demo_main.cc
+++ b/components/viz/demo/demo_main.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <utility>
+
 #include "base/at_exit.h"
 #include "base/command_line.h"
 #include "base/i18n/icu_util.h"
@@ -16,6 +18,8 @@
 #include "components/viz/demo/service/demo_service.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "ui/events/platform/platform_event_source.h"
 #include "ui/platform_window/platform_window_base.h"
 #include "ui/platform_window/platform_window_delegate.h"
@@ -134,23 +138,25 @@
     // actual process of setting up the viz host and the service.
     // First, set up the mojo message-pipes that the host and the service will
     // use to communicate with each other.
-    viz::mojom::FrameSinkManagerPtr frame_sink_manager;
-    viz::mojom::FrameSinkManagerRequest frame_sink_manager_request =
-        mojo::MakeRequest(&frame_sink_manager);
-    viz::mojom::FrameSinkManagerClientPtr frame_sink_manager_client;
-    viz::mojom::FrameSinkManagerClientRequest
-        frame_sink_manager_client_request =
-            mojo::MakeRequest(&frame_sink_manager_client);
+    mojo::PendingRemote<viz::mojom::FrameSinkManager> frame_sink_manager;
+    mojo::PendingReceiver<viz::mojom::FrameSinkManager>
+        frame_sink_manager_receiver =
+            frame_sink_manager.InitWithNewPipeAndPassReceiver();
+    mojo::PendingRemote<viz::mojom::FrameSinkManagerClient>
+        frame_sink_manager_client;
+    mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient>
+        frame_sink_manager_client_receiver =
+            frame_sink_manager_client.InitWithNewPipeAndPassReceiver();
 
     // Next, create the host and the service, and pass them the right ends of
     // the message-pipes.
     host_ = std::make_unique<demo::DemoHost>(
         widget_, platform_window_->GetBounds().size(),
-        std::move(frame_sink_manager_client_request),
+        std::move(frame_sink_manager_client_receiver),
         std::move(frame_sink_manager));
 
     service_ = std::make_unique<demo::DemoService>(
-        std::move(frame_sink_manager_request),
+        std::move(frame_sink_manager_receiver),
         std::move(frame_sink_manager_client));
   }
 
diff --git a/components/viz/demo/host/demo_host.cc b/components/viz/demo/host/demo_host.cc
index 347ab6e..1e53d24 100644
--- a/components/viz/demo/host/demo_host.cc
+++ b/components/viz/demo/host/demo_host.cc
@@ -10,20 +10,20 @@
 #include "components/viz/demo/client/demo_client.h"
 #include "components/viz/host/renderer_settings_creation.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace demo {
 
-DemoHost::DemoHost(gfx::AcceleratedWidget widget,
-                   const gfx::Size& size,
-                   viz::mojom::FrameSinkManagerClientRequest client_request,
-                   viz::mojom::FrameSinkManagerPtr frame_sink_manager_ptr)
+DemoHost::DemoHost(
+    gfx::AcceleratedWidget widget,
+    const gfx::Size& size,
+    mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient> client_receiver,
+    mojo::PendingRemote<viz::mojom::FrameSinkManager> frame_sink_manager_remote)
     : widget_(widget), size_(size), thread_("DemoHost") {
   CHECK(thread_.Start());
   thread_.task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&DemoHost::Initialize, base::Unretained(this),
-                                std::move(client_request),
-                                frame_sink_manager_ptr.PassInterface()));
+                                std::move(client_receiver),
+                                std::move(frame_sink_manager_remote)));
 }
 
 DemoHost::~DemoHost() = default;
@@ -112,11 +112,11 @@
   embedded_clients_.push_back(std::move(embedded_client));
 }
 
-void DemoHost::Initialize(viz::mojom::FrameSinkManagerClientRequest request,
-                          viz::mojom::FrameSinkManagerPtrInfo ptr_info) {
-  host_frame_sink_manager_.BindAndSetManager(
-      std::move(request), nullptr,
-      viz::mojom::FrameSinkManagerPtr(std::move(ptr_info)));
+void DemoHost::Initialize(
+    mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient> receiver,
+    mojo::PendingRemote<viz::mojom::FrameSinkManager> remote) {
+  host_frame_sink_manager_.BindAndSetManager(std::move(receiver), nullptr,
+                                             std::move(remote));
 
   display_client_ = std::make_unique<viz::HostDisplayClient>(widget_);
 
diff --git a/components/viz/demo/host/demo_host.h b/components/viz/demo/host/demo_host.h
index e83423d0..d9fb2da 100644
--- a/components/viz/demo/host/demo_host.h
+++ b/components/viz/demo/host/demo_host.h
@@ -13,6 +13,8 @@
 #include "components/viz/host/host_display_client.h"
 #include "components/viz/host/host_frame_sink_client.h"
 #include "components/viz/host/host_frame_sink_manager.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/viz/privileged/mojom/compositing/display_private.mojom.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h"
 #include "ui/gfx/native_widget_types.h"
@@ -26,10 +28,12 @@
 // the service.
 class DemoHost : public viz::HostFrameSinkClient {
  public:
-  DemoHost(gfx::AcceleratedWidget widget,
-           const gfx::Size& size,
-           viz::mojom::FrameSinkManagerClientRequest client_request,
-           viz::mojom::FrameSinkManagerPtr frame_sink_manager_ptr);
+  DemoHost(
+      gfx::AcceleratedWidget widget,
+      const gfx::Size& size,
+      mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient> client_receiver,
+      mojo::PendingRemote<viz::mojom::FrameSinkManager>
+          frame_sink_manager_remote);
   ~DemoHost() override;
 
   void Resize(const gfx::Size& size);
@@ -39,8 +43,9 @@
 
   void EmbedClients(DemoClient* embedder_client, const gfx::Rect& child_bounds);
 
-  void Initialize(viz::mojom::FrameSinkManagerClientRequest request,
-                  viz::mojom::FrameSinkManagerPtrInfo ptr_info);
+  void Initialize(
+      mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient> receiver,
+      mojo::PendingRemote<viz::mojom::FrameSinkManager> remote);
 
   // viz::HostFrameSinkClient:
   void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override;
diff --git a/components/viz/demo/service/demo_service.cc b/components/viz/demo/service/demo_service.cc
index a1cc8aaa..2d2d153 100644
--- a/components/viz/demo/service/demo_service.cc
+++ b/components/viz/demo/service/demo_service.cc
@@ -12,14 +12,15 @@
 
 namespace demo {
 
-DemoService::DemoService(viz::mojom::FrameSinkManagerRequest request,
-                         viz::mojom::FrameSinkManagerClientPtr client) {
+DemoService::DemoService(
+    mojo::PendingReceiver<viz::mojom::FrameSinkManager> receiver,
+    mojo::PendingRemote<viz::mojom::FrameSinkManagerClient> client) {
   auto params = viz::mojom::FrameSinkManagerParams::New();
   params->restart_id = viz::BeginFrameSource::kNotRestartableId;
   params->use_activation_deadline = false;
   params->activation_deadline_in_frames = 0u;
-  params->frame_sink_manager = std::move(request);
-  params->frame_sink_manager_client = client.PassInterface();
+  params->frame_sink_manager = std::move(receiver);
+  params->frame_sink_manager_client = std::move(client);
   runner_ = std::make_unique<viz::VizCompositorThreadRunnerImpl>();
   runner_->CreateFrameSinkManager(std::move(params));
 }
diff --git a/components/viz/demo/service/demo_service.h b/components/viz/demo/service/demo_service.h
index 5bd5b0ca..50d3a10 100644
--- a/components/viz/demo/service/demo_service.h
+++ b/components/viz/demo/service/demo_service.h
@@ -8,6 +8,8 @@
 #include <memory>
 
 #include "base/threading/thread.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h"
 
 namespace viz {
@@ -22,8 +24,8 @@
 // the host over the mojom.FrameSinkManagerClient API.
 class DemoService {
  public:
-  DemoService(viz::mojom::FrameSinkManagerRequest request,
-              viz::mojom::FrameSinkManagerClientPtr client);
+  DemoService(mojo::PendingReceiver<viz::mojom::FrameSinkManager> receiver,
+              mojo::PendingRemote<viz::mojom::FrameSinkManagerClient> client);
   ~DemoService();
 
  private:
diff --git a/components/viz/host/gpu_host_impl.cc b/components/viz/host/gpu_host_impl.cc
index 0e024ba..6795c45 100644
--- a/components/viz/host/gpu_host_impl.cc
+++ b/components/viz/host/gpu_host_impl.cc
@@ -176,8 +176,8 @@
 }
 
 void GpuHostImpl::ConnectFrameSinkManager(
-    mojom::FrameSinkManagerRequest request,
-    mojom::FrameSinkManagerClientPtrInfo client) {
+    mojo::PendingReceiver<mojom::FrameSinkManager> receiver,
+    mojo::PendingRemote<mojom::FrameSinkManagerClient> client) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   TRACE_EVENT0("gpu", "GpuHostImpl::ConnectFrameSinkManager");
 
@@ -188,7 +188,7 @@
       params_.deadline_to_synchronize_surfaces.has_value();
   params->activation_deadline_in_frames =
       params_.deadline_to_synchronize_surfaces.value_or(0u);
-  params->frame_sink_manager = std::move(request);
+  params->frame_sink_manager = std::move(receiver);
   params->frame_sink_manager_client = std::move(client);
   viz_main_->CreateFrameSinkManager(std::move(params));
 }
diff --git a/components/viz/host/gpu_host_impl.h b/components/viz/host/gpu_host_impl.h
index 515464e..df4b675 100644
--- a/components/viz/host/gpu_host_impl.h
+++ b/components/viz/host/gpu_host_impl.h
@@ -148,8 +148,9 @@
   void BlockLiveOffscreenContexts();
 
   // Connects to FrameSinkManager running in the Viz service.
-  void ConnectFrameSinkManager(mojom::FrameSinkManagerRequest request,
-                               mojom::FrameSinkManagerClientPtrInfo client);
+  void ConnectFrameSinkManager(
+      mojo::PendingReceiver<mojom::FrameSinkManager> receiver,
+      mojo::PendingRemote<mojom::FrameSinkManagerClient> client);
 
 #if BUILDFLAG(USE_VIZ_DEVTOOLS)
   // Connects to Viz DevTools running in the Viz service.
diff --git a/components/viz/host/host_frame_sink_manager.cc b/components/viz/host/host_frame_sink_manager.cc
index 94a3e9e..2d235bc0 100644
--- a/components/viz/host/host_frame_sink_manager.cc
+++ b/components/viz/host/host_frame_sink_manager.cc
@@ -14,37 +14,35 @@
 #include "components/viz/common/surfaces/surface_info.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/sync_call_restrictions.h"
 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h"
 
 namespace viz {
 
-HostFrameSinkManager::HostFrameSinkManager() : binding_(this) {}
+HostFrameSinkManager::HostFrameSinkManager() = default;
 
 HostFrameSinkManager::~HostFrameSinkManager() = default;
 
 void HostFrameSinkManager::SetLocalManager(
     FrameSinkManagerImpl* frame_sink_manager_impl) {
-  DCHECK(!frame_sink_manager_ptr_);
+  DCHECK(!frame_sink_manager_remote_);
   frame_sink_manager_impl_ = frame_sink_manager_impl;
 
   frame_sink_manager_ = frame_sink_manager_impl;
 }
 
 void HostFrameSinkManager::BindAndSetManager(
-    mojom::FrameSinkManagerClientRequest request,
+    mojo::PendingReceiver<mojom::FrameSinkManagerClient> receiver,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    mojom::FrameSinkManagerPtr ptr) {
+    mojo::PendingRemote<mojom::FrameSinkManager> remote) {
   DCHECK(!frame_sink_manager_impl_);
-  DCHECK(!binding_.is_bound());
+  DCHECK(!receiver_.is_bound());
 
-  binding_.Bind(std::move(request), std::move(task_runner));
-  frame_sink_manager_ptr_ = std::move(ptr);
-  frame_sink_manager_ = frame_sink_manager_ptr_.get();
+  receiver_.Bind(std::move(receiver), std::move(task_runner));
+  frame_sink_manager_remote_.Bind(std::move(remote));
+  frame_sink_manager_ = frame_sink_manager_remote_.get();
 
-  frame_sink_manager_ptr_.set_connection_error_handler(base::BindOnce(
+  frame_sink_manager_remote_.set_disconnect_handler(base::BindOnce(
       &HostFrameSinkManager::OnConnectionLost, base::Unretained(this)));
 
   if (connection_was_lost_) {
@@ -143,7 +141,7 @@
 void HostFrameSinkManager::CreateRootCompositorFrameSink(
     mojom::RootCompositorFrameSinkParamsPtr params) {
   // Should only be used with an out-of-process display compositor.
-  DCHECK(frame_sink_manager_ptr_);
+  DCHECK(frame_sink_manager_remote_);
 
   FrameSinkId frame_sink_id = params->frame_sink_id;
   FrameSinkData& data = frame_sink_data_map_[frame_sink_id];
@@ -346,8 +344,8 @@
 void HostFrameSinkManager::OnConnectionLost() {
   connection_was_lost_ = true;
 
-  binding_.Close();
-  frame_sink_manager_ptr_.reset();
+  receiver_.reset();
+  frame_sink_manager_remote_.reset();
   frame_sink_manager_ = nullptr;
 
   // Any cached back buffers are invalid once the connection to the
@@ -431,15 +429,15 @@
   DCHECK(it != frame_sink_data_map_.end());
   DCHECK(it->second.is_root);
   DCHECK(it->second.IsFrameSinkRegistered());
-  DCHECK(frame_sink_manager_ptr_);
+  DCHECK(frame_sink_manager_remote_);
 
   uint32_t cache_id = next_cache_back_buffer_id_++;
-  frame_sink_manager_ptr_->CacheBackBuffer(cache_id, root_sink_id);
+  frame_sink_manager_remote_->CacheBackBuffer(cache_id, root_sink_id);
   return cache_id;
 }
 
 void HostFrameSinkManager::EvictCachedBackBuffer(uint32_t cache_id) {
-  DCHECK(frame_sink_manager_ptr_);
+  DCHECK(frame_sink_manager_remote_);
 
   if (cache_id < min_valid_cache_back_buffer_id_)
     return;
@@ -448,7 +446,7 @@
   // the platform window (eg. XWindow or HWND) get destroyed before the
   // platform window is destroyed.
   mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
-  frame_sink_manager_ptr_->EvictBackBuffer(cache_id);
+  frame_sink_manager_remote_->EvictBackBuffer(cache_id);
 }
 
 HostFrameSinkManager::FrameSinkData::FrameSinkData() = default;
diff --git a/components/viz/host/host_frame_sink_manager.h b/components/viz/host/host_frame_sink_manager.h
index 926df0b..458da8d4 100644
--- a/components/viz/host/host_frame_sink_manager.h
+++ b/components/viz/host/host_frame_sink_manager.h
@@ -24,9 +24,10 @@
 #include "components/viz/host/host_frame_sink_client.h"
 #include "components/viz/host/viz_host_export.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support_manager.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h"
 
 namespace base {
@@ -61,17 +62,17 @@
   // Sets a local FrameSinkManagerImpl instance and connects directly to it.
   void SetLocalManager(FrameSinkManagerImpl* frame_sink_manager_impl);
 
-  // Binds |this| as a FrameSinkManagerClient for |request| on |task_runner|. On
-  // Mac |task_runner| will be the resize helper task runner. May only be called
-  // once. If |task_runner| is null, it uses the default mojo task runner for
-  // the thread this call is made on.
+  // Binds |this| as a FrameSinkManagerClient for |receiver| on |task_runner|.
+  // On Mac |task_runner| will be the resize helper task runner. May only be
+  // called once. If |task_runner| is null, it uses the default mojo task runner
+  // for the thread this call is made on.
   void BindAndSetManager(
-      mojom::FrameSinkManagerClientRequest request,
+      mojo::PendingReceiver<mojom::FrameSinkManagerClient> receiver,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-      mojom::FrameSinkManagerPtr ptr);
+      mojo::PendingRemote<mojom::FrameSinkManager> remote);
 
   // Sets a callback to be notified when the connection to the FrameSinkManager
-  // on |frame_sink_manager_ptr_| is lost.
+  // on |frame_sink_manager_remote_| is lost.
   void SetConnectionLostCallback(base::RepeatingClosure callback);
 
   // Sets a callback to be notified after Viz sent bad message to Viz host.
@@ -256,7 +257,7 @@
     DISALLOW_COPY_AND_ASSIGN(FrameSinkData);
   };
 
-  // Handles connection loss to |frame_sink_manager_ptr_|. This should only
+  // Handles connection loss to |frame_sink_manager_remote_|. This should only
   // happen when the GPU process crashes.
   void OnConnectionLost();
 
@@ -269,28 +270,28 @@
       const FrameSinkId& frame_sink_id,
       const std::vector<AggregatedHitTestRegion>& hit_test_data) override;
 
-  // This will point to |frame_sink_manager_ptr_| if using Mojo or
+  // This will point to |frame_sink_manager_remote_| if using Mojo or
   // |frame_sink_manager_impl_| if directly connected. Use this to make function
   // calls.
   mojom::FrameSinkManager* frame_sink_manager_ = nullptr;
 
   // Mojo connection to the FrameSinkManager. If this is bound then
   // |frame_sink_manager_impl_| must be null.
-  mojom::FrameSinkManagerPtr frame_sink_manager_ptr_;
+  mojo::Remote<mojom::FrameSinkManager> frame_sink_manager_remote_;
 
   // Mojo connection back from the FrameSinkManager.
-  mojo::Binding<mojom::FrameSinkManagerClient> binding_;
+  mojo::Receiver<mojom::FrameSinkManagerClient> receiver_{this};
 
   // A direct connection to FrameSinkManagerImpl. If this is set then
-  // |frame_sink_manager_ptr_| must be unbound. For use in browser process only,
-  // viz process should not set this.
+  // |frame_sink_manager_remote_| must be unbound. For use in browser process
+  // only, viz process should not set this.
   FrameSinkManagerImpl* frame_sink_manager_impl_ = nullptr;
 
   // Per CompositorFrameSink data.
   std::unordered_map<FrameSinkId, FrameSinkData, FrameSinkIdHash>
       frame_sink_data_map_;
 
-  // If |frame_sink_manager_ptr_| connection was lost.
+  // If |frame_sink_manager_remote_| connection was lost.
   bool connection_was_lost_ = false;
 
   base::RepeatingClosure connection_lost_callback_;
diff --git a/components/viz/host/host_frame_sink_manager_unittest.cc b/components/viz/host/host_frame_sink_manager_unittest.cc
index cb5485c..d30601b 100644
--- a/components/viz/host/host_frame_sink_manager_unittest.cc
+++ b/components/viz/host/host_frame_sink_manager_unittest.cc
@@ -134,8 +134,8 @@
   }
 
   bool IsBoundToFrameSinkManager() {
-    return host_manager_->frame_sink_manager_ptr_.is_bound() ||
-           host_manager_->binding_.is_bound();
+    return host_manager_->frame_sink_manager_remote_.is_bound() ||
+           host_manager_->receiver_.is_bound();
   }
 
   bool DisplayHitTestQueryExists(const FrameSinkId& frame_sink_id) {
@@ -206,17 +206,19 @@
         std::make_unique<testing::NiceMock<MockFrameSinkManagerImpl>>(
             &shared_bitmap_manager_);
 
-    mojom::FrameSinkManagerPtr frame_sink_manager;
-    mojom::FrameSinkManagerRequest frame_sink_manager_request =
-        mojo::MakeRequest(&frame_sink_manager);
-    mojom::FrameSinkManagerClientPtr frame_sink_manager_client;
-    mojom::FrameSinkManagerClientRequest frame_sink_manager_client_request =
-        mojo::MakeRequest(&frame_sink_manager_client);
+    mojo::PendingRemote<mojom::FrameSinkManager> frame_sink_manager;
+    mojo::PendingReceiver<mojom::FrameSinkManager> frame_sink_manager_receiver =
+        frame_sink_manager.InitWithNewPipeAndPassReceiver();
+    mojo::PendingRemote<mojom::FrameSinkManagerClient>
+        frame_sink_manager_client;
+    mojo::PendingReceiver<mojom::FrameSinkManagerClient>
+        frame_sink_manager_client_receiver =
+            frame_sink_manager_client.InitWithNewPipeAndPassReceiver();
 
     host_manager_->BindAndSetManager(
-        std::move(frame_sink_manager_client_request), nullptr,
+        std::move(frame_sink_manager_client_receiver), nullptr,
         std::move(frame_sink_manager));
-    manager_impl_->BindAndSetClient(std::move(frame_sink_manager_request),
+    manager_impl_->BindAndSetClient(std::move(frame_sink_manager_receiver),
                                     nullptr,
                                     std::move(frame_sink_manager_client));
   }
diff --git a/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc b/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
index ae10e53..3738e060 100644
--- a/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
+++ b/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
@@ -136,6 +136,11 @@
   void GetVideoMemoryUsageStats(
       GetVideoMemoryUsageStatsCallback callback) override {}
 
+  void StartPeakMemoryMonitor(uint32_t sequence_num) override {}
+
+  void GetPeakMemoryUsage(uint32_t sequence_num,
+                          GetPeakMemoryUsageCallback callback) override {}
+
 #if defined(OS_WIN)
   void RequestCompleteGpuInfo(
       RequestCompleteGpuInfoCallback callback) override {}
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index ff9f8882..60aec7d 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -22,6 +22,7 @@
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/copy_output_util.h"
 #include "components/viz/common/quads/draw_quad.h"
+#include "components/viz/common/quads/render_pass_draw_quad.h"
 #include "components/viz/service/display/bsp_tree.h"
 #include "components/viz/service/display/bsp_walk_action.h"
 #include "components/viz/service/display/output_surface.h"
@@ -308,6 +309,11 @@
       render_pass_backdrop_filters_[pass->id] = &pass->backdrop_filters;
       render_pass_backdrop_filter_bounds_[pass->id] =
           pass->backdrop_filter_bounds;
+      if (pass->backdrop_filters.HasFilterThatMovesPixels()) {
+        backdrop_filter_output_rects_[pass->id] =
+            cc::MathUtil::MapEnclosingClippedRect(
+                pass->transform_to_root_target, pass->output_rect);
+      }
     }
   }
 
@@ -403,6 +409,7 @@
   render_pass_backdrop_filters_.clear();
   render_pass_backdrop_filter_bounds_.clear();
   render_pass_bypass_quads_.clear();
+  backdrop_filter_output_rects_.clear();
 
   current_frame_valid_ = false;
 }
@@ -581,8 +588,9 @@
   const gfx::Rect surface_rect_in_draw_space = OutputSurfaceRectInDrawSpace();
   gfx::Rect render_pass_scissor_in_draw_space = surface_rect_in_draw_space;
 
-  if (current_frame()->current_render_pass ==
-      current_frame()->root_render_pass) {
+  bool is_root_render_pass =
+      current_frame()->current_render_pass == current_frame()->root_render_pass;
+  if (is_root_render_pass) {
     render_pass_scissor_in_draw_space.Intersect(
         DeviceViewportRectInDrawSpace());
   }
@@ -592,9 +600,6 @@
         ComputeScissorRectForRenderPass(current_frame()->current_render_pass));
   }
 
-  bool is_root_render_pass =
-      current_frame()->current_render_pass == current_frame()->root_render_pass;
-
   bool render_pass_is_clipped =
       !render_pass_scissor_in_draw_space.Contains(surface_rect_in_draw_space);
 
@@ -627,6 +632,9 @@
   PrepareSurfaceForPass(
       mode, MoveFromDrawToWindowSpace(render_pass_scissor_in_draw_space));
 
+  if (is_root_render_pass)
+    last_root_render_pass_scissor_rect_ = render_pass_scissor_in_draw_space;
+
   const QuadList& quad_list = render_pass->quad_list;
   base::circular_deque<std::unique_ptr<DrawPolygon>> poly_list;
 
@@ -743,6 +751,22 @@
 
   if (render_pass == root_render_pass) {
     root_damage_rect.Union(output_surface_->GetCurrentFramebufferDamage());
+    // If the root damage rect intersects any child render pass that has a
+    // pixel-moving backdrop-filter, expand the damage to include the entire
+    // child pass. See crbug.com/986206 for context.
+    if (!backdrop_filter_output_rects_.empty() && !root_damage_rect.IsEmpty()) {
+      for (auto* quad : render_pass->quad_list) {
+        if (quad->material == DrawQuad::Material::kRenderPass) {
+          auto iter = backdrop_filter_output_rects_.find(
+              RenderPassDrawQuad::MaterialCast(quad)->render_pass_id);
+          if (iter != backdrop_filter_output_rects_.end()) {
+            auto this_output_rect = iter->second;
+            if (root_damage_rect.Intersects(this_output_rect))
+              root_damage_rect.Union(this_output_rect);
+          }
+        }
+      }
+    }
     return root_damage_rect;
   }
 
diff --git a/components/viz/service/display/direct_renderer.h b/components/viz/service/display/direct_renderer.h
index d78e4da..c12b9d1d 100644
--- a/components/viz/service/display/direct_renderer.h
+++ b/components/viz/service/display/direct_renderer.h
@@ -116,6 +116,10 @@
   }
   bool OverlayNeedsSurfaceOccludingDamageRect() const;
 
+  gfx::Rect GetLastRootScissorRectForTesting() const {
+    return last_root_render_pass_scissor_rect_;
+  }
+
  protected:
   friend class BspWalkActionDrawPolygon;
 
@@ -258,6 +262,7 @@
       render_pass_backdrop_filters_;
   base::flat_map<RenderPassId, base::Optional<gfx::RRectF>>
       render_pass_backdrop_filter_bounds_;
+  base::flat_map<RenderPassId, gfx::Rect> backdrop_filter_output_rects_;
 
   bool visible_ = false;
   bool disable_color_checks_for_testing_ = false;
@@ -286,6 +291,7 @@
 #if DCHECK_IS_ON()
   bool overdraw_feedback_support_missing_logged_once_ = false;
 #endif
+  gfx::Rect last_root_render_pass_scissor_rect_;
   gfx::Size enlarge_pass_texture_amount_;
 
   // The current drawing frame is valid only during the duration of the
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index a6705f8..c13e6e3 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -218,6 +218,11 @@
       surface_manager_->RemoveObserver(scheduler_.get());
   }
 
+  // Un-register as DisplaySchedulerClient to prevent us from being called in a
+  // partially destructed state.
+  if (scheduler_)
+    scheduler_->SetClient(nullptr);
+
   RunDrawCallbacks();
 }
 
diff --git a/components/viz/service/display/display_scheduler.cc b/components/viz/service/display/display_scheduler.cc
index 2d68d55..d913520 100644
--- a/components/viz/service/display/display_scheduler.cc
+++ b/components/viz/service/display/display_scheduler.cc
@@ -212,7 +212,7 @@
   DCHECK_LT(pending_swaps_, max_pending_swaps_);
   DCHECK(!output_surface_lost_);
 
-  bool success = client_->DrawAndSwap();
+  bool success = client_ && client_->DrawAndSwap();
   if (!success)
     return false;
 
@@ -328,14 +328,15 @@
 
 bool DisplayScheduler::OnSurfaceDamaged(const SurfaceId& surface_id,
                                         const BeginFrameAck& ack) {
-  bool damaged = client_->SurfaceDamaged(surface_id, ack);
+  bool damaged = client_ && client_->SurfaceDamaged(surface_id, ack);
   ProcessSurfaceDamage(surface_id, ack, damaged);
 
   return damaged;
 }
 
 void DisplayScheduler::OnSurfaceDestroyed(const SurfaceId& surface_id) {
-  client_->SurfaceDestroyed(surface_id);
+  if (client_)
+    client_->SurfaceDestroyed(surface_id);
 }
 
 void DisplayScheduler::OnSurfaceDamageExpected(const SurfaceId& surface_id,
@@ -508,7 +509,8 @@
   DCHECK(begin_frame_source_);
   begin_frame_source_->DidFinishFrame(this);
   BeginFrameAck ack(current_begin_frame_args_, did_draw);
-  client_->DidFinishFrame(ack);
+  if (client_)
+    client_->DidFinishFrame(ack);
 }
 
 void DisplayScheduler::DidSwapBuffers() {
diff --git a/components/viz/service/display/display_scheduler_unittest.cc b/components/viz/service/display/display_scheduler_unittest.cc
index 41ed7146..8b2fc5b 100644
--- a/components/viz/service/display/display_scheduler_unittest.cc
+++ b/components/viz/service/display/display_scheduler_unittest.cc
@@ -825,5 +825,22 @@
   EXPECT_FALSE(fake_begin_frame_source_.RequestCallbackOnGpuAvailable());
 }
 
+TEST_F(DisplaySchedulerTest, OnBeginFrameDeadlineNoClient) {
+  SurfaceId root_surface_id(
+      kArbitraryFrameSinkId,
+      LocalSurfaceId(1, base::UnguessableToken::Create()));
+
+  scheduler_.SetVisible(true);
+  scheduler_.SetNewRootSurface(root_surface_id);
+
+  AdvanceTimeAndBeginFrameForTest({root_surface_id});
+  SurfaceDamaged(root_surface_id);
+
+  // During teardown, we may get a BeginFrameDeadline while |client_| is null.
+  // This should not crash.
+  scheduler_.SetClient(nullptr);
+  scheduler_.BeginFrameDeadlineForTest();
+}
+
 }  // namespace
 }  // namespace viz
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index ef48963..55f7120 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -23,6 +23,7 @@
 #include "components/viz/common/quads/surface_draw_quad.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "components/viz/service/display/direct_renderer.h"
 #include "components/viz/service/display/display_client.h"
 #include "components/viz/service/display/display_scheduler.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
@@ -46,6 +47,7 @@
 
 static constexpr FrameSinkId kArbitraryFrameSinkId(3, 3);
 static constexpr FrameSinkId kAnotherFrameSinkId(4, 4);
+static constexpr FrameSinkId kAnotherFrameSinkId2(5, 5);
 
 class TestSoftwareOutputDevice : public SoftwareOutputDevice {
  public:
@@ -668,6 +670,157 @@
   TearDownDisplay();
 }
 
+TEST_F(DisplayTest, BackdropFilterTest) {
+  RendererSettings settings;
+  settings.partial_swap_enabled = true;
+  id_allocator_.GenerateId();
+  const LocalSurfaceId local_surface_id(
+      id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id());
+
+  // Set up first display.
+  SetUpSoftwareDisplay(settings);
+  StubDisplayClient client;
+  display_->Initialize(&client, manager_.surface_manager());
+  display_->SetLocalSurfaceId(local_surface_id, 1.f);
+
+  // Create frame sink for a sub surface.
+  const LocalSurfaceId sub_local_surface_id1(6,
+                                             base::UnguessableToken::Create());
+  const SurfaceId sub_surface_id1(kAnotherFrameSinkId, sub_local_surface_id1);
+  auto sub_support1 = std::make_unique<CompositorFrameSinkSupport>(
+      nullptr, &manager_, kAnotherFrameSinkId, /*is_root=*/false,
+      /*needs_sync_points=*/true);
+
+  // Create frame sink for another sub surface.
+  const LocalSurfaceId sub_local_surface_id2(7,
+                                             base::UnguessableToken::Create());
+  const SurfaceId sub_surface_id2(kAnotherFrameSinkId2, sub_local_surface_id2);
+  auto sub_support2 = std::make_unique<CompositorFrameSinkSupport>(
+      nullptr, &manager_, kAnotherFrameSinkId2, /*is_root=*/false,
+      /*needs_sync_points=*/true);
+
+  // Main surface M, damage D, sub-surface B with backdrop filter.
+  //   +-----------+
+  //   | +----+   M|
+  //   | |B +-|-+  |
+  //   | +--|-+ |  |
+  //   |    |  D|  |
+  //   |    +---+  |
+  //   +-----------+
+  const gfx::Size display_size(100, 100);
+  const gfx::Rect damage_rect(20, 20, 40, 40);
+  display_->Resize(display_size);
+  const gfx::Rect sub_surface_rect(5, 5, 25, 25);
+  const gfx::Rect no_damage;
+
+  uint64_t next_render_pass_id = 1;
+  for (size_t frame_num = 1; frame_num <= 2; ++frame_num) {
+    bool first_frame = frame_num == 1;
+    scheduler_->ResetDamageForTest();
+    {
+      // Sub-surface with backdrop-filter.
+      RenderPassList pass_list;
+      auto bd_pass = RenderPass::Create();
+      cc::FilterOperations backdrop_filters;
+      backdrop_filters.Append(cc::FilterOperation::CreateBlurFilter(5.0));
+      bd_pass->SetAll(
+          next_render_pass_id++, sub_surface_rect, no_damage, gfx::Transform(),
+          cc::FilterOperations(), backdrop_filters,
+          gfx::RRectF(gfx::RectF(sub_surface_rect), 0),
+          gfx::ColorSpace::CreateSRGB(), false, false, false, false);
+      pass_list.push_back(std::move(bd_pass));
+
+      CompositorFrame frame = CompositorFrameBuilder()
+                                  .SetRenderPassList(std::move(pass_list))
+                                  .Build();
+      sub_support1->SubmitCompositorFrame(sub_local_surface_id1,
+                                          std::move(frame));
+    }
+
+    {
+      // Sub-surface with damage.
+      RenderPassList pass_list;
+      auto other_pass = RenderPass::Create();
+      other_pass->output_rect = gfx::Rect(display_size);
+      other_pass->damage_rect = damage_rect;
+      other_pass->id = next_render_pass_id++;
+      pass_list.push_back(std::move(other_pass));
+
+      CompositorFrame frame = CompositorFrameBuilder()
+                                  .SetRenderPassList(std::move(pass_list))
+                                  .Build();
+      sub_support2->SubmitCompositorFrame(sub_local_surface_id2,
+                                          std::move(frame));
+    }
+
+    {
+      RenderPassList pass_list;
+      auto pass = RenderPass::Create();
+      pass->output_rect = gfx::Rect(display_size);
+      pass->damage_rect = damage_rect;
+      pass->id = next_render_pass_id++;
+
+      // Embed sub surface 1, with backdrop filter.
+      auto* shared_quad_state1 = pass->CreateAndAppendSharedQuadState();
+      shared_quad_state1->SetAll(
+          gfx::Transform(), /*quad_layer_rect=*/sub_surface_rect,
+          /*visible_quad_layer_rect=*/sub_surface_rect,
+          /*rounded_corner_bounds=*/gfx::RRectF(),
+          /*clip_rect=*/sub_surface_rect, /*is_clipped=*/false,
+          /*are_contents_opaque=*/true, /*opacity=*/1.0f, SkBlendMode::kSrcOver,
+          /*sorting_context_id=*/0);
+      auto* quad1 = pass->quad_list.AllocateAndConstruct<SurfaceDrawQuad>();
+      quad1->SetNew(shared_quad_state1, /*rect=*/sub_surface_rect,
+                    /*visible_rect=*/sub_surface_rect,
+                    SurfaceRange(base::nullopt, sub_surface_id1), SK_ColorBLACK,
+                    /*stretch_content_to_fill_bounds=*/false,
+                    /*has_pointer_events_none=*/false);
+      quad1->allow_merge = false;
+
+      // Embed sub surface 2, with damage.
+      auto* shared_quad_state2 = pass->CreateAndAppendSharedQuadState();
+      gfx::Rect rect1(display_size);
+      shared_quad_state2->SetAll(gfx::Transform(), /*quad_layer_rect=*/rect1,
+                                 /*visible_quad_layer_rect=*/rect1,
+                                 /*rounded_corner_bounds=*/gfx::RRectF(),
+                                 /*clip_rect=*/rect1, /*is_clipped=*/false,
+                                 /*are_contents_opaque=*/true, /*opacity=*/1.0f,
+                                 SkBlendMode::kSrcOver,
+                                 /*sorting_context_id=*/0);
+      auto* quad2 = pass->quad_list.AllocateAndConstruct<SurfaceDrawQuad>();
+      quad2->SetNew(shared_quad_state2, /*rect=*/rect1,
+                    /*visible_rect=*/rect1,
+                    SurfaceRange(base::nullopt, sub_surface_id2), SK_ColorBLACK,
+                    /*stretch_content_to_fill_bounds=*/false,
+                    /*has_pointer_events_none=*/false);
+      quad2->allow_merge = false;
+
+      pass_list.push_back(std::move(pass));
+      SubmitCompositorFrame(&pass_list, local_surface_id);
+
+      scheduler_->swapped = false;
+      display_->DrawAndSwap();
+      EXPECT_TRUE(scheduler_->swapped);
+      EXPECT_EQ(frame_num, output_surface_->num_sent_frames());
+      EXPECT_EQ(display_size, software_output_device_->viewport_pixel_size());
+      // The damage rect produced by surface_aggregator only includes the
+      // damaged surface rect, and is not expanded for the backdrop-filter
+      // surface.
+      auto expected_damage =
+          first_frame ? gfx::Rect(display_size) : gfx::Rect(20, 20, 40, 40);
+      EXPECT_EQ(expected_damage, software_output_device_->damage_rect());
+      // The scissor rect is expanded by direct_renderer to include the
+      // overlapping pixel-moving backdrop filter surface.
+      auto expected_scissor_rect =
+          first_frame ? gfx::Rect(display_size) : gfx::Rect(5, 5, 55, 55);
+      EXPECT_EQ(
+          expected_scissor_rect,
+          display_->renderer_for_testing()->GetLastRootScissorRectForTesting());
+    }
+  }
+  TearDownDisplay();
+}
+
 class CountLossDisplayClient : public StubDisplayClient {
  public:
   CountLossDisplayClient() = default;
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index 4b4e078..99b18d1 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -98,17 +98,15 @@
   RoundedCornerInfo() = default;
 
   // |target_transform| is the transform that maps |bounds_arg| from its current
-  // space into the desired target space. It must be a scale+translation matrix.
+  // space into the desired target space. It must be an axis aligned transform.
   RoundedCornerInfo(const gfx::RRectF& bounds_arg,
                     bool is_fast_rounded_corner,
                     const gfx::Transform target_transform)
       : bounds(bounds_arg), is_fast_rounded_corner(is_fast_rounded_corner) {
     if (bounds.IsEmpty())
       return;
-    DCHECK(target_transform.Preserves2dAxisAlignment());
-    SkMatrix matrix = target_transform.matrix();
-    bounds.Scale(matrix.getScaleX(), matrix.getScaleY());
-    bounds.Offset(target_transform.To2dTranslation());
+    bool success = target_transform.TransformRRectF(&bounds);
+    DCHECK(success);
   }
 
   gfx::RRectF bounds;
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 4fd2375..0a4b974 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -58,8 +58,7 @@
       hit_test_manager_(surface_manager()),
       restart_id_(params.restart_id),
       run_all_compositor_stages_before_draw_(
-          params.run_all_compositor_stages_before_draw),
-      binding_(this) {
+          params.run_all_compositor_stages_before_draw) {
   surface_manager_.AddObserver(&hit_test_manager_);
   surface_manager_.AddObserver(this);
 }
@@ -86,28 +85,28 @@
 }
 
 void FrameSinkManagerImpl::BindAndSetClient(
-    mojom::FrameSinkManagerRequest request,
+    mojo::PendingReceiver<mojom::FrameSinkManager> receiver,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    mojom::FrameSinkManagerClientPtr client) {
+    mojo::PendingRemote<mojom::FrameSinkManagerClient> client) {
   DCHECK(!client_);
-  DCHECK(!binding_.is_bound());
-  binding_.Bind(std::move(request), std::move(task_runner));
-  client_ptr_ = std::move(client);
-  client_ = client_ptr_.get();
+  DCHECK(!receiver_.is_bound());
+  receiver_.Bind(std::move(receiver), std::move(task_runner));
+  client_remote_.Bind(std::move(client));
+  client_ = client_remote_.get();
 }
 
 void FrameSinkManagerImpl::SetLocalClient(
     mojom::FrameSinkManagerClient* client,
     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
-  DCHECK(!client_ptr_);
+  DCHECK(!client_remote_);
   DCHECK(!ui_task_runner_);
   client_ = client;
   ui_task_runner_ = ui_task_runner;
 }
 
 void FrameSinkManagerImpl::ForceShutdown() {
-  if (binding_.is_bound())
-    binding_.Close();
+  if (receiver_.is_bound())
+    receiver_.reset();
 
   for (auto& it : cached_back_buffers_)
     it.second.RunAndReset();
@@ -544,7 +543,7 @@
 
 void FrameSinkManagerImpl::OnFrameTokenChanged(const FrameSinkId& frame_sink_id,
                                                uint32_t frame_token) {
-  if (client_ptr_ || !ui_task_runner_) {
+  if (client_remote_ || !ui_task_runner_) {
     // This is a Mojo client or a locally-connected client *without* a task
     // runner. In this case, call directly.
     OnFrameTokenChangedDirect(frame_sink_id, frame_token);
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
index ee0b79a..a8af755 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.h
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -34,9 +34,10 @@
 #include "components/viz/service/surfaces/surface_observer.h"
 #include "components/viz/service/viz_service_export.h"
 #include "gpu/ipc/common/surface_handle.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom.h"
 #include "services/viz/public/mojom/compositing/video_detector_observer.mojom.h"
@@ -83,12 +84,13 @@
   // incoming IPCs and destroys all [Root]CompositorFrameSinkImpls.
   void ForceShutdown();
 
-  // Binds |this| as a FrameSinkManagerImpl for |request| on |task_runner|. On
+  // Binds |this| as a FrameSinkManagerImpl for |receiver| on |task_runner|. On
   // Mac |task_runner| will be the resize helper task runner. May only be called
   // once.
-  void BindAndSetClient(mojom::FrameSinkManagerRequest request,
-                        scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-                        mojom::FrameSinkManagerClientPtr client);
+  void BindAndSetClient(
+      mojo::PendingReceiver<mojom::FrameSinkManager> receiver,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      mojo::PendingRemote<mojom::FrameSinkManagerClient> client);
 
   // Sets up a direction connection to |client| without using Mojo.
   void SetLocalClient(
@@ -335,21 +337,22 @@
   std::unique_ptr<VideoDetector> video_detector_;
 
   // There are three states this can be in:
-  //  1. Mojo client: |client_| will point to |client_ptr_|, the Mojo client,
+  //  1. Mojo client: |client_| will point to |client_remote_|, the Mojo client,
   //     and |ui_task_runner_| will not be used. Calls to OnFrameTokenChanged()
   //     will go through Mojo. This is the normal state.
   //  2. Local (directly connected) client, *with* task runner: |client_| will
-  //     point to the client, |client_ptr_| will be nullptr, and calls to
-  //     OnFrameTokenChanged() will be PostTasked using |ui_task_runner_|. Used
-  //     mostly for layout tests.
+  //     point to the client, |client_remote_| will not be bound to any remote
+  //     client, and calls to OnFrameTokenChanged() will be PostTasked using
+  //     |ui_task_runner_|. Used mostly for layout tests.
   //  3. Local (directly connected) client, *without* task runner: |client_|
-  //     will point to the client, |client_ptr_| and |ui_task_runner_| will be
-  //     nullptr, and calls to OnFrameTokenChanged() will be directly called
-  //     (without PostTask) on |client_|. Used for some unit tests.
+  //     will point to the client, |client_remote_| will not be bound to any
+  //     remote client and |ui_task_runner_| will be nullptr, and calls to
+  //     OnFrameTokenChanged() will be directly called (without PostTask) on
+  //     |client_|. Used for some unit tests.
   mojom::FrameSinkManagerClient* client_ = nullptr;
   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_ = nullptr;
-  mojom::FrameSinkManagerClientPtr client_ptr_;
-  mojo::Binding<mojom::FrameSinkManager> binding_;
+  mojo::Remote<mojom::FrameSinkManagerClient> client_remote_;
+  mojo::Receiver<mojom::FrameSinkManager> receiver_{this};
 
   base::ObserverList<FrameSinkObserver>::Unchecked observer_list_;
 
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 4714666c..97a0b25c 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -524,6 +524,22 @@
   std::move(callback).Run(video_memory_usage_stats);
 }
 
+void GpuServiceImpl::StartPeakMemoryMonitor(uint32_t sequence_num) {
+  DCHECK(io_runner_->BelongsToCurrentThread());
+  main_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&GpuServiceImpl::StartPeakMemoryMonitorOnMainThread,
+                     weak_ptr_, sequence_num));
+}
+
+void GpuServiceImpl::GetPeakMemoryUsage(uint32_t sequence_num,
+                                        GetPeakMemoryUsageCallback callback) {
+  DCHECK(io_runner_->BelongsToCurrentThread());
+  main_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&GpuServiceImpl::GetPeakMemoryUsageOnMainThread,
+                                weak_ptr_, sequence_num, std::move(callback)));
+}
+
 #if defined(OS_WIN)
 void GpuServiceImpl::GetGpuSupportedRuntimeVersion(
     GetGpuSupportedRuntimeVersionCallback callback) {
@@ -878,6 +894,18 @@
       std::move(callback));
 }
 
+void GpuServiceImpl::StartPeakMemoryMonitorOnMainThread(uint32_t sequence_num) {
+  gpu_channel_manager_->StartPeakMemoryMonitor(sequence_num);
+}
+
+void GpuServiceImpl::GetPeakMemoryUsageOnMainThread(
+    uint32_t sequence_num,
+    GetPeakMemoryUsageCallback callback) {
+  uint64_t peak_memory = gpu_channel_manager_->GetPeakMemoryUsage(sequence_num);
+  io_runner_->PostTask(FROM_HERE,
+                       base::BindOnce(std::move(callback), peak_memory));
+}
+
 void GpuServiceImpl::MaybeExit(bool for_context_loss) {
   DCHECK(main_runner_->BelongsToCurrentThread());
 
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h
index ab4e1656..466f20ed 100644
--- a/components/viz/service/gl/gpu_service_impl.h
+++ b/components/viz/service/gl/gpu_service_impl.h
@@ -151,6 +151,10 @@
                               const gpu::SyncToken& sync_token) override;
   void GetVideoMemoryUsageStats(
       GetVideoMemoryUsageStatsCallback callback) override;
+  void StartPeakMemoryMonitor(uint32_t sequence_num) override;
+  void GetPeakMemoryUsage(uint32_t sequence_num,
+                          GetPeakMemoryUsageCallback callback) override;
+
 #if defined(OS_WIN)
   void RequestCompleteGpuInfo(RequestCompleteGpuInfoCallback callback) override;
   void GetGpuSupportedRuntimeVersion(
@@ -284,6 +288,13 @@
 
   void OnBackgroundedOnMainThread();
 
+  // Ensure that all peak memory tracking occurs on the main thread as all
+  // MemoryTracker are created on that thread. All requests made before
+  // GpuServiceImpl::InitializeWithHost will be enqueued.
+  void StartPeakMemoryMonitorOnMainThread(uint32_t sequence_num);
+  void GetPeakMemoryUsageOnMainThread(uint32_t sequence_num,
+                                      GetPeakMemoryUsageCallback callback);
+
   // Attempts to cleanly exit the process but only if not running in host
   // process. If |for_context_loss| is true an error message will be logged.
   void MaybeExit(bool for_context_loss);
diff --git a/components/viz/service/main/viz_compositor_thread_runner_impl.cc b/components/viz/service/main/viz_compositor_thread_runner_impl.cc
index 1337730a..1d710a7 100644
--- a/components/viz/service/main/viz_compositor_thread_runner_impl.cc
+++ b/components/viz/service/main/viz_compositor_thread_runner_impl.cc
@@ -195,8 +195,7 @@
   frame_sink_manager_ = std::make_unique<FrameSinkManagerImpl>(init_params);
   frame_sink_manager_->BindAndSetClient(
       std::move(params->frame_sink_manager), nullptr,
-      mojom::FrameSinkManagerClientPtr(
-          std::move(params->frame_sink_manager_client)));
+      std::move(params->frame_sink_manager_client));
 
 #if BUILDFLAG(USE_VIZ_DEVTOOLS)
   if (pending_viz_dev_tools_params_)
diff --git a/components/viz/test/gpu_host_impl_test_api.cc b/components/viz/test/gpu_host_impl_test_api.cc
index f82239eb..a58e568 100644
--- a/components/viz/test/gpu_host_impl_test_api.cc
+++ b/components/viz/test/gpu_host_impl_test_api.cc
@@ -15,6 +15,10 @@
 
 GpuHostImplTestApi::~GpuHostImplTestApi() = default;
 
+void GpuHostImplTestApi::FlushRemoteForTesting() {
+  gpu_host_->gpu_service_remote_.FlushForTesting();
+}
+
 void GpuHostImplTestApi::SetGpuService(
     mojo::Remote<mojom::GpuService> gpu_service) {
   gpu_host_->gpu_service_remote_ = std::move(gpu_service);
diff --git a/components/viz/test/gpu_host_impl_test_api.h b/components/viz/test/gpu_host_impl_test_api.h
index 471d7602..5d2e0f35 100644
--- a/components/viz/test/gpu_host_impl_test_api.h
+++ b/components/viz/test/gpu_host_impl_test_api.h
@@ -16,6 +16,9 @@
   explicit GpuHostImplTestApi(GpuHostImpl* gpu_host);
   ~GpuHostImplTestApi();
 
+  // Waits until all messages to the mojo::Remote<mojom::GpuService> have been
+  // processed.
+  void FlushRemoteForTesting();
   void SetGpuService(mojo::Remote<mojom::GpuService> gpu_service);
 
  private:
diff --git a/components/viz/test/test_frame_sink_manager.cc b/components/viz/test/test_frame_sink_manager.cc
index 1c31c8e5..a9fd534 100644
--- a/components/viz/test/test_frame_sink_manager.cc
+++ b/components/viz/test/test_frame_sink_manager.cc
@@ -4,17 +4,19 @@
 
 #include "components/viz/test/test_frame_sink_manager.h"
 
+#include <utility>
+
 namespace viz {
 
-TestFrameSinkManagerImpl::TestFrameSinkManagerImpl() : binding_(this) {}
+TestFrameSinkManagerImpl::TestFrameSinkManagerImpl() = default;
 
-TestFrameSinkManagerImpl::~TestFrameSinkManagerImpl() {}
+TestFrameSinkManagerImpl::~TestFrameSinkManagerImpl() = default;
 
-void TestFrameSinkManagerImpl::BindRequest(
-    mojom::FrameSinkManagerRequest request,
-    mojom::FrameSinkManagerClientPtr client) {
-  binding_.Bind(std::move(request));
-  client_ = std::move(client);
+void TestFrameSinkManagerImpl::BindReceiver(
+    mojo::PendingReceiver<mojom::FrameSinkManager> receiver,
+    mojo::PendingRemote<mojom::FrameSinkManagerClient> client) {
+  receiver_.Bind(std::move(receiver));
+  client_.Bind(std::move(client));
 }
 
 }  // namespace viz
diff --git a/components/viz/test/test_frame_sink_manager.h b/components/viz/test/test_frame_sink_manager.h
index 5b871a82..f410a7d 100644
--- a/components/viz/test/test_frame_sink_manager.h
+++ b/components/viz/test/test_frame_sink_manager.h
@@ -6,9 +6,10 @@
 #define COMPONENTS_VIZ_TEST_TEST_FRAME_SINK_MANAGER_H_
 
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h"
 
 namespace viz {
@@ -18,8 +19,8 @@
   TestFrameSinkManagerImpl();
   ~TestFrameSinkManagerImpl() override;
 
-  void BindRequest(mojom::FrameSinkManagerRequest request,
-                   mojom::FrameSinkManagerClientPtr client);
+  void BindReceiver(mojo::PendingReceiver<mojom::FrameSinkManager> receiver,
+                    mojo::PendingRemote<mojom::FrameSinkManagerClient> client);
 
  private:
   // mojom::FrameSinkManager:
@@ -63,8 +64,8 @@
   void EvictBackBuffer(uint32_t cache_id,
                        EvictBackBufferCallback callback) override {}
 
-  mojo::Binding<mojom::FrameSinkManager> binding_;
-  mojom::FrameSinkManagerClientPtr client_;
+  mojo::Receiver<mojom::FrameSinkManager> receiver_{this};
+  mojo::Remote<mojom::FrameSinkManagerClient> client_;
 
   DISALLOW_COPY_AND_ASSIGN(TestFrameSinkManagerImpl);
 };
diff --git a/components/zucchini/disassembler_elf.cc b/components/zucchini/disassembler_elf.cc
index 1d75cb3e..ff3b085b 100644
--- a/components/zucchini/disassembler_elf.cc
+++ b/components/zucchini/disassembler_elf.cc
@@ -211,14 +211,6 @@
     if (section->sh_size == 0)
       continue;
 
-    // Be lax with RVAs: Assume they fit in int32_t, even for 64-bit. If
-    // assumption fails, simply skip the section with warning.
-    if (!RangeIsBounded(section->sh_addr, section->sh_size, kRvaBound) ||
-        !RangeIsBounded(section->sh_offset, section->sh_size, kOffsetBound)) {
-      LOG(WARNING) << "Section " << i << " does not fit in int32_t.";
-      continue;
-    }
-
     // Extract dimensions to 32-bit integers to facilitate conversion. Range of
     // values was ensured above when checking that the section is bounded.
     uint32_t sh_size = base::checked_cast<uint32_t>(section->sh_size);
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 588ce4a..3e1ba57 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -943,6 +943,8 @@
     "gpu/gpu_memory_buffer_manager_singleton.h",
     "gpu/gpu_process_host.cc",
     "gpu/gpu_process_host.h",
+    "gpu/peak_gpu_memory_tracker_impl.cc",
+    "gpu/peak_gpu_memory_tracker_impl.h",
     "gpu/shader_cache_factory.cc",
     "gpu/shader_cache_factory.h",
     "hid/hid_service.cc",
@@ -1176,6 +1178,8 @@
     "media/media_devices_util.h",
     "media/media_experiment_manager.cc",
     "media/media_experiment_manager.h",
+    "media/media_interface_factory_holder.cc",
+    "media/media_interface_factory_holder.h",
     "media/media_interface_proxy.cc",
     "media/media_interface_proxy.h",
     "media/media_internals.cc",
@@ -1590,8 +1594,6 @@
     "renderer_host/text_input_manager.h",
     "renderer_host/ui_events_helper.cc",
     "renderer_host/ui_events_helper.h",
-    "renderer_host/visual_properties_manager.cc",
-    "renderer_host/visual_properties_manager.h",
     "renderer_host/web_database_host_impl.cc",
     "renderer_host/web_database_host_impl.h",
     "renderer_host/webmenurunner_mac.h",
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 02612b5..98be862 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -111,7 +111,6 @@
   "+third_party/blink/public/platform/web_screen_info.h",
   "+third_party/blink/public/platform/web_scroll_into_view_params.h",
   "+third_party/blink/public/platform/web_scroll_types.h",
-  "+third_party/blink/public/platform/web_sudden_termination_disabler_type.h",
   "+third_party/blink/public/platform/web_text_autosizer_page_info.h",
   "+third_party/blink/public/platform/web_touch_event.h",
   "+third_party/blink/public/platform/web_text_input_type.h",
@@ -133,7 +132,6 @@
   "+third_party/blink/public/web/WebSharedWorkerCreationErrors.h",
   "+third_party/blink/public/web/web_text_direction.h",
   "+third_party/blink/public/web/web_tree_scope_type.h",
-  "+third_party/blink/public/web/web_triggering_event_info.h",
 
   # DO NOT ADD ANY CHROME OR COMPONENTS INCLUDES HERE!!!
   # See https://sites.google.com/a/chromium.org/dev/developers/content-module
diff --git a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
index 3456046d..6cc514dd 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
@@ -323,7 +323,9 @@
     "math root",
     "subscript",
     "superscript",
-    "footnote",  // ATK_ROLE_FOOTNOTE = 122.
+    "footnote",           // ATK_ROLE_FOOTNOTE = 122.
+    "content deletion",   // ATK_ROLE_CONTENT_DELETION = 123.
+    "content insertion",  // ATK_ROLE_CONTENT_DELETION = 124.
 };
 
 void AccessibilityTreeFormatterAuraLinux::AddTextProperties(
diff --git a/content/browser/accessibility/accessibility_tree_formatter_base.h b/content/browser/accessibility/accessibility_tree_formatter_base.h
index 5e78def..fc779030 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_base.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_base.h
@@ -34,7 +34,7 @@
 class CONTENT_EXPORT AccessibilityTreeFormatterBase
     : public AccessibilityTreeFormatter {
  public:
-  explicit AccessibilityTreeFormatterBase();
+  AccessibilityTreeFormatterBase();
   ~AccessibilityTreeFormatterBase() override;
 
   // AccessibilityTreeFormatter overrides.
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index ac6c53d1..57b818b 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -1998,6 +1998,10 @@
   return node()->GetSetSize();
 }
 
+std::string BrowserAccessibility::ToString() const {
+  return GetData().ToString();
+}
+
 bool BrowserAccessibility::SetHypertextSelection(int start_offset,
                                                  int end_offset) {
   manager()->SetSelection(
@@ -2176,4 +2180,9 @@
                       }) != attributes.end();
 }
 
+std::ostream& operator<<(std::ostream& stream,
+                         const BrowserAccessibility& object) {
+  return stream << object.ToString();
+}
+
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index e74d6f37..90d1a96 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -9,6 +9,7 @@
 
 #include <map>
 #include <memory>
+#include <ostream>
 #include <set>
 #include <string>
 #include <utility>
@@ -545,6 +546,9 @@
   base::Optional<int> GetPosInSet() const override;
   base::Optional<int> GetSetSize() const override;
 
+  // Returns a string representation of this object for debugging purposes.
+  std::string ToString() const;
+
  protected:
   // The UIA tree formatter needs access to GetUniqueId() to identify the
   // starting point for tree dumps.
@@ -642,6 +646,9 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserAccessibility);
 };
 
+CONTENT_EXPORT std::ostream& operator<<(std::ostream& stream,
+                                        const BrowserAccessibility& object);
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_H_
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index 97d4aa5..2851641 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -207,6 +207,18 @@
 NSString* const NSAccessibilityLengthForTextMarkerRangeParameterizedAttribute =
     @"AXLengthForTextMarkerRange";
 
+// Private attributes that can be used for testing text markers, e.g. in dump
+// tree tests.
+NSString* const
+    NSAccessibilityTextMarkerDebugDescriptionParameterizedAttribute =
+        @"AXTextMarkerDebugDescription";
+NSString* const
+    NSAccessibilityTextMarkerRangeDebugDescriptionParameterizedAttribute =
+        @"AXTextMarkerRangeDebugDescription";
+NSString* const
+    NSAccessibilityTextMarkerNodeDebugDescriptionParameterizedAttribute =
+        @"AXTextMarkerNodeDebugDescription";
+
 // Other private attributes.
 NSString* const NSAccessibilitySelectTextWithCriteriaParameterizedAttribute =
     @"AXSelectTextWithCriteria";
@@ -3028,6 +3040,32 @@
 
   if ([attribute
           isEqualToString:
+              NSAccessibilityTextMarkerDebugDescriptionParameterizedAttribute]) {
+    BrowserAccessibilityPositionInstance position =
+        CreatePositionFromTextMarker(parameter);
+    return base::SysUTF8ToNSString(position->ToString());
+  }
+
+  if ([attribute
+          isEqualToString:
+              NSAccessibilityTextMarkerRangeDebugDescriptionParameterizedAttribute]) {
+    AXPlatformRange range = CreateRangeFromTextMarkerRange(parameter);
+    return base::SysUTF8ToNSString(range.ToString());
+  }
+
+  if ([attribute
+          isEqualToString:
+              NSAccessibilityTextMarkerNodeDebugDescriptionParameterizedAttribute]) {
+    BrowserAccessibilityPositionInstance position =
+        CreatePositionFromTextMarker(parameter);
+    if (position->IsNullPosition())
+      return @"nil";
+    DCHECK(position->GetAnchor());
+    return base::SysUTF8ToNSString(position->GetAnchor()->ToString());
+  }
+
+  if ([attribute
+          isEqualToString:
               NSAccessibilityIndexForChildUIElementParameterizedAttribute]) {
     if (![parameter isKindOfClass:[BrowserAccessibilityCocoa class]])
       return nil;
diff --git a/content/browser/accessibility/browser_accessibility_mac.mm b/content/browser/accessibility/browser_accessibility_mac.mm
index d97c51e9..966036f 100644
--- a/content/browser/accessibility/browser_accessibility_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_mac.mm
@@ -62,18 +62,20 @@
 
 BrowserAccessibility* BrowserAccessibilityMac::PlatformGetChild(
     uint32_t child_index) const {
-  uint32_t child_count = BrowserAccessibility::PlatformChildCount();
-  if (child_index < child_count)
+  if (child_index < BrowserAccessibility::PlatformChildCount())
     return BrowserAccessibility::PlatformGetChild(child_index);
 
+  if (child_index >= PlatformChildCount())
+    return nullptr;
+
   // If this is a table, include the extra fake nodes generated by
   // AXTableInfo, for the column nodes and the table header container, all of
   // which are only important on macOS.
   const std::vector<ui::AXNode*>* extra_mac_nodes = node()->GetExtraMacNodes();
-  if (!extra_mac_nodes)
+  if (!extra_mac_nodes || extra_mac_nodes->empty())
     return nullptr;
 
-  child_index -= child_count;
+  child_index -= BrowserAccessibility::PlatformChildCount();
   if (child_index < extra_mac_nodes->size())
     return manager_->GetFromAXNode((*extra_mac_nodes)[child_index]);
 
@@ -86,7 +88,7 @@
 
 BrowserAccessibility* BrowserAccessibilityMac::PlatformGetLastChild() const {
   const std::vector<ui::AXNode*>* extra_mac_nodes = node()->GetExtraMacNodes();
-  if (extra_mac_nodes && extra_mac_nodes->size())
+  if (extra_mac_nodes && !extra_mac_nodes->empty())
     return manager_->GetFromAXNode(extra_mac_nodes->back());
   return BrowserAccessibility::PlatformGetLastChild();
 }
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
index 1a5c754b..6d01281 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.cc
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
@@ -145,6 +145,7 @@
 void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives(
     const std::string& test_html,
     std::vector<std::string>* wait_for,
+    std::vector<std::string>* execute,
     std::vector<std::string>* run_until,
     std::vector<std::string>* default_action_on) {
   for (const std::string& line : base::SplitString(
@@ -154,6 +155,7 @@
     const std::string& deny_str = formatter_->GetDenyString();
     const std::string& deny_node_str = formatter_->GetDenyNodeString();
     const std::string& wait_str = "@WAIT-FOR:";
+    const std::string& execute_str = "@EXECUTE-AND-WAIT-FOR:";
     const std::string& until_str = "@RUN-UNTIL-EVENT:";
     const std::string& default_action_on_str = "@DEFAULT-ACTION-ON:";
     if (base::StartsWith(line, allow_empty_str, base::CompareCase::SENSITIVE)) {
@@ -181,6 +183,9 @@
       }
     } else if (base::StartsWith(line, wait_str, base::CompareCase::SENSITIVE)) {
       wait_for->push_back(line.substr(wait_str.size()));
+    } else if (base::StartsWith(line, execute_str,
+                                base::CompareCase::SENSITIVE)) {
+      execute->push_back(line.substr(execute_str.size()));
     } else if (base::StartsWith(line, until_str,
                                 base::CompareCase::SENSITIVE)) {
       run_until->push_back(line.substr(until_str.size()));
@@ -267,13 +272,14 @@
 
   // Parse filters and other directives in the test file.
   std::vector<std::string> wait_for;
+  std::vector<std::string> execute;
   std::vector<std::string> run_until;
   std::vector<std::string> default_action_on;
   property_filters_.clear();
   node_filters_.clear();
   formatter_->AddDefaultFilters(&property_filters_);
   AddDefaultFilters(&property_filters_);
-  ParseHtmlForExtraDirectives(html_contents, &wait_for, &run_until,
+  ParseHtmlForExtraDirectives(html_contents, &wait_for, &execute, &run_until,
                               &default_action_on);
 
   // Get the test URL.
@@ -386,6 +392,35 @@
   // Call the subclass to dump the output.
   std::vector<std::string> actual_lines = Dump(run_until);
 
+  // Execute and wait for specified string
+  for (const auto& function_name : execute) {
+    DLOG(INFO) << "executing: " << function_name;
+    base::Value result =
+        ExecuteScriptAndGetValue(web_contents->GetMainFrame(), function_name);
+    const std::string& str = result.is_string() ? result.GetString() : "";
+    // If no string is specified, do not wait.
+    bool wait_for_string = str != "";
+    while (wait_for_string) {
+      // Loop until specified string is found.
+      base::string16 tree_dump = DumpUnfilteredAccessibilityTreeAsString();
+      if (base::UTF16ToUTF8(tree_dump).find(str) != std::string::npos) {
+        wait_for_string = false;
+        // Append an additional dump if the specified string was found.
+        std::vector<std::string> additional_dump = Dump(run_until);
+        actual_lines.emplace_back("=== Start Continuation ===");
+        actual_lines.insert(actual_lines.end(), additional_dump.begin(),
+                            additional_dump.end());
+        break;
+      }
+      // Block until the next accessibility notification in any frame.
+      VLOG(1) << "Still waiting on this text to be found: " << str;
+      VLOG(1) << "Waiting until the next accessibility event";
+      AccessibilityNotificationWaiter accessibility_waiter(
+          web_contents, ui::AXMode(), ax::mojom::Event::kNone);
+      accessibility_waiter.WaitForNotification();
+    }
+  }
+
   // Validate against the expectation file.
   bool matches_expectation = test_helper.ValidateAgainstExpectation(
       file_path, expected_file, actual_lines, *expected_lines);
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.h b/content/browser/accessibility/dump_accessibility_browsertest_base.h
index b731dc2..45da92b 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.h
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.h
@@ -90,6 +90,7 @@
   // @WAIT-FOR: directives.
   void ParseHtmlForExtraDirectives(const std::string& test_html,
                                    std::vector<std::string>* wait_for,
+                                   std::vector<std::string>* execute,
                                    std::vector<std::string>* run_until,
                                    std::vector<std::string>* default_action_on);
 
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 2dc6906..7cc2d01d 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1836,6 +1836,10 @@
   RunHtmlTest(FILE_PATH_LITERAL("optgroup.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityOpenModal) {
+  RunHtmlTest(FILE_PATH_LITERAL("open-modal.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
                        AccessibilityOptionindatalist) {
   RunHtmlTest(FILE_PATH_LITERAL("option-in-datalist.html"));
diff --git a/content/browser/accessibility/one_shot_accessibility_tree_search_unittest.cc b/content/browser/accessibility/one_shot_accessibility_tree_search_unittest.cc
index 90293c6f..5ef3ad02 100644
--- a/content/browser/accessibility/one_shot_accessibility_tree_search_unittest.cc
+++ b/content/browser/accessibility/one_shot_accessibility_tree_search_unittest.cc
@@ -46,7 +46,8 @@
 #define MAYBE_OneShotAccessibilityTreeSearchTest \
   OneShotAccessibilityTreeSearchTest
 #endif
-class MAYBE_OneShotAccessibilityTreeSearchTest : public testing::Test {
+class MAYBE_OneShotAccessibilityTreeSearchTest
+    : public testing::TestWithParam<bool> {
  public:
   MAYBE_OneShotAccessibilityTreeSearchTest() {}
   ~MAYBE_OneShotAccessibilityTreeSearchTest() override {}
@@ -69,9 +70,6 @@
   root.role = ax::mojom::Role::kRootWebArea;
   root.relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
   root.AddBoolAttribute(ax::mojom::BoolAttribute::kClipsChildren, true);
-  root.child_ids.push_back(2);
-  root.child_ids.push_back(3);
-  root.child_ids.push_back(6);
 
   ui::AXNodeData heading;
   heading.id = 2;
@@ -79,41 +77,93 @@
   heading.role = ax::mojom::Role::kHeading;
   heading.relative_bounds.bounds = gfx::RectF(0, 0, 800, 50);
 
+  ui::AXNodeData table;
+  table.id = 3;
+  table.role = ax::mojom::Role::kTable;
+  table.AddIntAttribute(ax::mojom::IntAttribute::kTableRowCount, 1);
+  table.AddIntAttribute(ax::mojom::IntAttribute::kTableColumnCount, 2);
+
+  ui::AXNodeData table_row;
+  table_row.id = 4;
+  table_row.role = ax::mojom::Role::kRow;
+  table_row.AddIntAttribute(ax::mojom::IntAttribute::kTableRowIndex, 0);
+
+  ui::AXNodeData table_column_header_1;
+  table_column_header_1.id = 5;
+  table_column_header_1.role = ax::mojom::Role::kColumnHeader;
+  table_column_header_1.SetName("Cell1");
+  table_column_header_1.AddIntAttribute(
+      ax::mojom::IntAttribute::kTableCellRowIndex, 0);
+  table_column_header_1.AddIntAttribute(
+      ax::mojom::IntAttribute::kTableCellColumnIndex, 0);
+
+  ui::AXNodeData table_column_header_2;
+  table_column_header_2.id = 6;
+  table_column_header_2.role = ax::mojom::Role::kColumnHeader;
+  table_column_header_2.SetName("Cell2");
+  table_column_header_2.AddIntAttribute(
+      ax::mojom::IntAttribute::kTableCellRowIndex, 0);
+  table_column_header_2.AddIntAttribute(
+      ax::mojom::IntAttribute::kTableCellColumnIndex, 1);
+
   ui::AXNodeData list;
-  list.id = 3;
+  list.id = 7;
   list.role = ax::mojom::Role::kList;
   list.relative_bounds.bounds = gfx::RectF(0, 50, 500, 500);
-  list.child_ids.push_back(4);
-  list.child_ids.push_back(5);
 
   ui::AXNodeData list_item_1;
-  list_item_1.id = 4;
+  list_item_1.id = 8;
   list_item_1.SetName("Autobots");
   list_item_1.role = ax::mojom::Role::kListItem;
   list_item_1.relative_bounds.bounds = gfx::RectF(10, 10, 200, 30);
 
   ui::AXNodeData list_item_2;
-  list_item_2.id = 5;
+  list_item_2.id = 9;
   list_item_2.SetName("Decepticons");
   list_item_2.role = ax::mojom::Role::kListItem;
   list_item_2.relative_bounds.bounds = gfx::RectF(10, 40, 200, 60);
 
   ui::AXNodeData footer;
-  footer.id = 6;
+  footer.id = 10;
   footer.SetName("Footer");
   footer.role = ax::mojom::Role::kFooter;
   footer.relative_bounds.bounds = gfx::RectF(0, 650, 100, 800);
 
-  tree_.reset(new TestBrowserAccessibilityManager(
-      MakeAXTreeUpdate(root, heading, list, list_item_1, list_item_2, footer)));
+  table_row.child_ids = {table_column_header_1.id, table_column_header_2.id};
+  table.child_ids = {table_row.id};
+  list.child_ids = {list_item_1.id, list_item_2.id};
+  root.child_ids = {heading.id, table.id, list.id, footer.id};
+
+  tree_.reset(new TestBrowserAccessibilityManager(MakeAXTreeUpdate(
+      root, heading, table, table_row, table_column_header_1,
+      table_column_header_2, list, list_item_1, list_item_2, footer)));
 }
 
 TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, GetAll) {
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
-  ASSERT_EQ(6U, search.CountMatches());
+  ASSERT_EQ(10U, search.CountMatches());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, NoCycle) {
+TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, BackwardsWrapFromRoot) {
+  OneShotAccessibilityTreeSearch search(tree_->GetRoot());
+  search.SetDirection(OneShotAccessibilityTreeSearch::BACKWARDS);
+  search.SetResultLimit(100);
+  search.SetCanWrapToLastElement(true);
+  ASSERT_EQ(10U, search.CountMatches());
+  EXPECT_EQ(1, search.GetMatchAtIndex(0)->GetId());
+  EXPECT_EQ(10, search.GetMatchAtIndex(1)->GetId());
+  EXPECT_EQ(9, search.GetMatchAtIndex(2)->GetId());
+  EXPECT_EQ(8, search.GetMatchAtIndex(3)->GetId());
+  EXPECT_EQ(7, search.GetMatchAtIndex(4)->GetId());
+  EXPECT_EQ(6, search.GetMatchAtIndex(5)->GetId());
+  EXPECT_EQ(5, search.GetMatchAtIndex(6)->GetId());
+  EXPECT_EQ(4, search.GetMatchAtIndex(7)->GetId());
+  EXPECT_EQ(3, search.GetMatchAtIndex(8)->GetId());
+  EXPECT_EQ(2, search.GetMatchAtIndex(9)->GetId());
+}
+
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, NoCycle) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   // If you set a result limit of 1, you won't get the root node back as
   // the first match.
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
@@ -122,15 +172,18 @@
   EXPECT_NE(1, search.GetMatchAtIndex(0)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, ForwardsWithStartNode) {
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, ForwardsWithStartNode) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
-  search.SetStartNode(tree_->GetFromID(4));
-  ASSERT_EQ(2U, search.CountMatches());
-  EXPECT_EQ(5, search.GetMatchAtIndex(0)->GetId());
-  EXPECT_EQ(6, search.GetMatchAtIndex(1)->GetId());
+  search.SetStartNode(tree_->GetFromID(7));
+  ASSERT_EQ(3U, search.CountMatches());
+  EXPECT_EQ(8, search.GetMatchAtIndex(0)->GetId());
+  EXPECT_EQ(9, search.GetMatchAtIndex(1)->GetId());
+  EXPECT_EQ(10, search.GetMatchAtIndex(2)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, BackwardsWithStartNode) {
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, BackwardsWithStartNode) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
   search.SetStartNode(tree_->GetFromID(4));
   search.SetDirection(OneShotAccessibilityTreeSearch::BACKWARDS);
@@ -140,8 +193,9 @@
   EXPECT_EQ(1, search.GetMatchAtIndex(2)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest,
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest,
        BackwardsWithStartNodeForAndroid) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
   search.SetStartNode(tree_->GetFromID(4));
   search.SetDirection(OneShotAccessibilityTreeSearch::BACKWARDS);
@@ -153,115 +207,147 @@
   EXPECT_EQ(1, search.GetMatchAtIndex(2)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, BackwardsWrapFromRoot) {
-  OneShotAccessibilityTreeSearch search(tree_->GetRoot());
-  search.SetDirection(OneShotAccessibilityTreeSearch::BACKWARDS);
-  search.SetResultLimit(100);
-  search.SetCanWrapToLastElement(true);
-  ASSERT_EQ(6U, search.CountMatches());
-  EXPECT_EQ(1, search.GetMatchAtIndex(0)->GetId());
-  EXPECT_EQ(6, search.GetMatchAtIndex(1)->GetId());
-  EXPECT_EQ(5, search.GetMatchAtIndex(2)->GetId());
-  EXPECT_EQ(4, search.GetMatchAtIndex(3)->GetId());
-  EXPECT_EQ(3, search.GetMatchAtIndex(4)->GetId());
-  EXPECT_EQ(2, search.GetMatchAtIndex(5)->GetId());
-}
-
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest,
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest,
        ForwardsWithStartNodeAndScope) {
-  OneShotAccessibilityTreeSearch search(tree_->GetFromID(4));
-  search.SetStartNode(tree_->GetFromID(5));
-  ASSERT_EQ(1U, search.CountMatches());
-  EXPECT_EQ(6, search.GetMatchAtIndex(0)->GetId());
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
+  OneShotAccessibilityTreeSearch search(tree_->GetFromID(7));
+  search.SetStartNode(tree_->GetFromID(8));
+  ASSERT_EQ(2U, search.CountMatches());
+  EXPECT_EQ(9, search.GetMatchAtIndex(0)->GetId());
+  EXPECT_EQ(10, search.GetMatchAtIndex(1)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, ResultLimitZero) {
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, ResultLimitZero) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
   search.SetResultLimit(0);
   ASSERT_EQ(0U, search.CountMatches());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, ResultLimitFive) {
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, ResultLimitFive) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
   search.SetResultLimit(5);
   ASSERT_EQ(5U, search.CountMatches());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, DescendantsOnlyOfRoot) {
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, DescendantsOnlyOfRoot) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
   search.SetStartNode(tree_->GetFromID(1));
   search.SetImmediateDescendantsOnly(true);
-  ASSERT_EQ(3U, search.CountMatches());
+  ASSERT_EQ(4U, search.CountMatches());
   EXPECT_EQ(2, search.GetMatchAtIndex(0)->GetId());
   EXPECT_EQ(3, search.GetMatchAtIndex(1)->GetId());
-  EXPECT_EQ(6, search.GetMatchAtIndex(2)->GetId());
+  EXPECT_EQ(7, search.GetMatchAtIndex(2)->GetId());
+  EXPECT_EQ(10, search.GetMatchAtIndex(3)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, DescendantsOnlyOfNode) {
-  OneShotAccessibilityTreeSearch search(tree_->GetFromID(3));
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, DescendantsOnlyOfNode) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
+  OneShotAccessibilityTreeSearch search(tree_->GetFromID(7));
   search.SetImmediateDescendantsOnly(true);
   ASSERT_EQ(2U, search.CountMatches());
-  EXPECT_EQ(4, search.GetMatchAtIndex(0)->GetId());
-  EXPECT_EQ(5, search.GetMatchAtIndex(1)->GetId());
+  EXPECT_EQ(8, search.GetMatchAtIndex(0)->GetId());
+  EXPECT_EQ(9, search.GetMatchAtIndex(1)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest,
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest,
        DescendantsOnlyOfNodeWithStartNode) {
-  OneShotAccessibilityTreeSearch search(tree_->GetFromID(3));
-  search.SetStartNode(tree_->GetFromID(4));
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
+  OneShotAccessibilityTreeSearch search(tree_->GetFromID(7));
+  search.SetStartNode(tree_->GetFromID(8));
   search.SetImmediateDescendantsOnly(true);
   ASSERT_EQ(1U, search.CountMatches());
-  EXPECT_EQ(5, search.GetMatchAtIndex(0)->GetId());
+  EXPECT_EQ(9, search.GetMatchAtIndex(0)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest,
-       DescendantsOnlyOfNodeWithStartNode2) {
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest,
+       DescendantsOnlyOfNodeWithStartNodeBackwardsTableCell) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   OneShotAccessibilityTreeSearch search(tree_->GetFromID(3));
   search.SetStartNode(tree_->GetFromID(5));
+  search.SetDirection(OneShotAccessibilityTreeSearch::BACKWARDS);
   search.SetImmediateDescendantsOnly(true);
   ASSERT_EQ(0U, search.CountMatches());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest,
-       DescendantsOnlyOfNodeWithStartNodeBackwards) {
-  OneShotAccessibilityTreeSearch search(tree_->GetFromID(3));
-  search.SetStartNode(tree_->GetFromID(5));
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest,
+       DescendantsOnlyOfNodeWithStartNodeBackwardsListItem) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
+  OneShotAccessibilityTreeSearch search(tree_->GetFromID(7));
+  search.SetStartNode(tree_->GetFromID(9));
   search.SetImmediateDescendantsOnly(true);
   search.SetDirection(OneShotAccessibilityTreeSearch::BACKWARDS);
   ASSERT_EQ(1U, search.CountMatches());
-  EXPECT_EQ(4, search.GetMatchAtIndex(0)->GetId());
+  EXPECT_EQ(8, search.GetMatchAtIndex(0)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, OnscreenOnly) {
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, OnscreenOnly) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
   search.SetOnscreenOnly(true);
-  ASSERT_EQ(5U, search.CountMatches());
+  ASSERT_EQ(7U, search.CountMatches());
   EXPECT_EQ(1, search.GetMatchAtIndex(0)->GetId());
   EXPECT_EQ(2, search.GetMatchAtIndex(1)->GetId());
   EXPECT_EQ(3, search.GetMatchAtIndex(2)->GetId());
   EXPECT_EQ(4, search.GetMatchAtIndex(3)->GetId());
-  EXPECT_EQ(5, search.GetMatchAtIndex(4)->GetId());
+  EXPECT_EQ(7, search.GetMatchAtIndex(4)->GetId());
+  EXPECT_EQ(8, search.GetMatchAtIndex(5)->GetId());
+  EXPECT_EQ(9, search.GetMatchAtIndex(6)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, CaseInsensitiveStringMatch) {
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, CaseInsensitiveStringMatch) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
   search.SetSearchText("eCEptiCOn");
   ASSERT_EQ(1U, search.CountMatches());
-  EXPECT_EQ(5, search.GetMatchAtIndex(0)->GetId());
+  EXPECT_EQ(9, search.GetMatchAtIndex(0)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, OnePredicate) {
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, OnePredicateTableCell) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
+  OneShotAccessibilityTreeSearch search(tree_->GetRoot());
+  search.AddPredicate(
+      [](BrowserAccessibility* start, BrowserAccessibility* current) {
+        return current->GetRole() == ax::mojom::Role::kColumnHeader;
+      });
+  ASSERT_EQ(2U, search.CountMatches());
+  EXPECT_EQ(5, search.GetMatchAtIndex(0)->GetId());
+  EXPECT_EQ(6, search.GetMatchAtIndex(1)->GetId());
+}
+
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, OnePredicateListItem) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
   search.AddPredicate(
       [](BrowserAccessibility* start, BrowserAccessibility* current) {
         return current->GetRole() == ax::mojom::Role::kListItem;
       });
   ASSERT_EQ(2U, search.CountMatches());
-  EXPECT_EQ(4, search.GetMatchAtIndex(0)->GetId());
-  EXPECT_EQ(5, search.GetMatchAtIndex(1)->GetId());
+  EXPECT_EQ(8, search.GetMatchAtIndex(0)->GetId());
+  EXPECT_EQ(9, search.GetMatchAtIndex(1)->GetId());
 }
 
-TEST_F(MAYBE_OneShotAccessibilityTreeSearchTest, TwoPredicates) {
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, TwoPredicatesTableRowAndCell) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
+  OneShotAccessibilityTreeSearch search(tree_->GetRoot());
+  search.AddPredicate(
+      [](BrowserAccessibility* start, BrowserAccessibility* current) {
+        return (current->GetRole() == ax::mojom::Role::kRow ||
+                current->GetRole() == ax::mojom::Role::kColumnHeader);
+      });
+  search.AddPredicate(
+      [](BrowserAccessibility* start, BrowserAccessibility* current) {
+        return (current->GetId() % 2 == 0);
+      });
+  ASSERT_EQ(2U, search.CountMatches());
+  EXPECT_EQ(4, search.GetMatchAtIndex(0)->GetId());
+  EXPECT_EQ(6, search.GetMatchAtIndex(1)->GetId());
+}
+
+TEST_P(MAYBE_OneShotAccessibilityTreeSearchTest, TwoPredicatesListItem) {
+  tree_->ax_tree()->SetEnableExtraMacNodes(GetParam());
   OneShotAccessibilityTreeSearch search(tree_->GetRoot());
   search.AddPredicate(
       [](BrowserAccessibility* start, BrowserAccessibility* current) {
@@ -273,8 +359,16 @@
         return (current->GetId() % 2 == 1);
       });
   ASSERT_EQ(2U, search.CountMatches());
-  EXPECT_EQ(3, search.GetMatchAtIndex(0)->GetId());
-  EXPECT_EQ(5, search.GetMatchAtIndex(1)->GetId());
+  EXPECT_EQ(7, search.GetMatchAtIndex(0)->GetId());
+  EXPECT_EQ(9, search.GetMatchAtIndex(1)->GetId());
 }
 
+INSTANTIATE_TEST_SUITE_P(EnableExtraMacNodes,
+                         MAYBE_OneShotAccessibilityTreeSearchTest,
+                         testing::Values(true));
+
+INSTANTIATE_TEST_SUITE_P(DisableExtraMacNodes,
+                         MAYBE_OneShotAccessibilityTreeSearchTest,
+                         testing::Values(false));
+
 }  // namespace content
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 4481669a..0c9ae5d 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -491,18 +491,17 @@
 
   // 3) Flush A.
   web_contents()->GetController().GetBackForwardCache().Flush();
-  EXPECT_TRUE(delete_observer_rfh_a.deleted());
+  EXPECT_TRUE(delete_observer_rfh_a.deleted());  // Flush is synchronous.
   EXPECT_FALSE(delete_observer_rfh_b.deleted());
 
   // 4) Go back to a new A.
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  EXPECT_TRUE(delete_observer_rfh_a.deleted());
   EXPECT_FALSE(delete_observer_rfh_b.deleted());
 
   // 5) Flush B.
   web_contents()->GetController().GetBackForwardCache().Flush();
-  EXPECT_TRUE(delete_observer_rfh_b.deleted());
+  EXPECT_TRUE(delete_observer_rfh_b.deleted());  // Flush is synchronous.
 }
 
 // Check the visible URL in the omnibox is properly updated when restoring a
@@ -1887,7 +1886,7 @@
 
   // rfh_a should have been deleted, and page A navigated to normally.
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  EXPECT_TRUE(delete_observer_rfh_a.deleted());
+  delete_observer_rfh_a.WaitUntilDeleted();
   RenderFrameHostImpl* rfh_a2 = current_frame_host();
   EXPECT_NE(rfh_a2, rfh_b);
   EXPECT_EQ(rfh_a2->GetLastCommittedURL(), url_a);
@@ -1927,7 +1926,7 @@
 
   // The cached RenderFrameHost should be destroyed (not kept in the cache).
   crash_observer.Wait();
-  EXPECT_TRUE(delete_observer_rfh_a.deleted());
+  delete_observer_rfh_a.WaitUntilDeleted();
 
   // rfh_b should still be the current frame.
   EXPECT_EQ(current_frame_host(), rfh_b);
@@ -2038,7 +2037,7 @@
   // 3) Go back to A.
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  EXPECT_TRUE(delete_observer_rfh_a.deleted());
+  delete_observer_rfh_a.WaitUntilDeleted();
 }
 
 // Only HTTP/HTTPS main document can enter the BackForwardCache.
@@ -2477,7 +2476,7 @@
 
   // 2) Navigate to B.
   EXPECT_TRUE(NavigateToURL(shell(), url_b));
-  EXPECT_TRUE(delete_observer_rfh_a.deleted());
+  delete_observer_rfh_a.WaitUntilDeleted();
 
   // 3) Go back to A.
   web_contents()->GetController().GoBack();
@@ -2504,7 +2503,7 @@
 
   // 2) Navigate to B.
   EXPECT_TRUE(NavigateToURL(shell(), url_b));
-  EXPECT_TRUE(delete_observer_rfh_a.deleted());
+  delete_observer_rfh_a.WaitUntilDeleted();
 
   // This should not die
   BackForwardCache::DisableForRenderFrameHost(
@@ -2517,15 +2516,8 @@
   ExpectEvictedIsEmpty(FROM_HERE);
 }
 
-#if defined(OS_LINUX) || defined(OS_WIN)
-// Flaky. https://crbug.com/1013802
-#define MAYBE_DisableBackForwardCacheIframe \
-  DISABLED_DisableBackForwardCacheIframe
-#else
-#define MAYBE_DisableBackForwardCacheIframe DisableBackForwardCacheIframe
-#endif
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
-                       MAYBE_DisableBackForwardCacheIframe) {
+                       DisableBackForwardCacheIframe) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_a(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b)"));
@@ -2541,10 +2533,10 @@
   BackForwardCache::DisableForRenderFrameHost(
       rfh_b, "DisabledByBackForwardCacheBrowserTest");
 
-  // 2) Navigate to C. A and B are immediately deleted.
+  // 2) Navigate to C. A and B are deleted.
   EXPECT_TRUE(NavigateToURL(shell(), url_c));
-  EXPECT_TRUE(delete_observer_rfh_a.deleted());
-  EXPECT_TRUE(delete_observer_rfh_b.deleted());
+  delete_observer_rfh_a.WaitUntilDeleted();
+  delete_observer_rfh_b.WaitUntilDeleted();
 
   // 3) Go back to A.
   web_contents()->GetController().GoBack();
@@ -2913,4 +2905,60 @@
   EXPECT_EQ(web_contents()->GetEncoding(), "windows-1250");
 }
 
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, RestoreWhilePendingCommit) {
+  net::test_server::ControllableHttpResponse response(embedded_test_server(),
+                                                      "/main_document");
+  ASSERT_TRUE(embedded_test_server()->Start());
+  const GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  const GURL url2(embedded_test_server()->GetURL("b.com", "/title2.html"));
+  const GURL url3(embedded_test_server()->GetURL("c.com", "/main_document"));
+
+  // Load a page and navigate away from it, so it is stored in the back-forward
+  // cache.
+  EXPECT_TRUE(NavigateToURL(shell(), url1));
+  RenderFrameHost* rfh1 = current_frame_host();
+  EXPECT_TRUE(NavigateToURL(shell(), url2));
+
+  // Try to navigate to a new page, but leave it in a pending state.
+  shell()->LoadURL(url3);
+  response.WaitForRequest();
+
+  // Navigate back and restore page from the cache, cancelling the previous
+  // navigation.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  EXPECT_EQ(rfh1, current_frame_host());
+}
+
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       DoesNotCacheCrossSiteHttpPost) {
+  SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // Note we do a cross-site post because same-site navigations of any kind
+  // aren't cached currently.
+  GURL form_url(embedded_test_server()->GetURL(
+      "a.com", "/form_that_posts_cross_site.html"));
+  GURL redirect_target_url(embedded_test_server()->GetURL("x.com", "/echoall"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  // Navigate to the page with form that posts via 307 redirection to
+  // |redirect_target_url| (cross-site from |form_url|).
+  EXPECT_TRUE(NavigateToURL(shell(), form_url));
+
+  // Submit the form.
+  TestNavigationObserver form_post_observer(shell()->web_contents(), 1);
+  EXPECT_TRUE(ExecJs(shell(), "document.getElementById('text-form').submit()"));
+  form_post_observer.Wait();
+
+  // Verify that we arrived at the expected, redirected location.
+  EXPECT_EQ(redirect_target_url,
+            shell()->web_contents()->GetLastCommittedURL());
+  RenderFrameDeletedObserver delete_observer_rfh(current_frame_host());
+
+  // Navigate away. |redirect_target_url|'s page should not be cached.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b));
+  delete_observer_rfh.WaitUntilDeleted();
+}
+
 }  // namespace content
diff --git a/content/browser/builtin_service_manifests.cc b/content/browser/builtin_service_manifests.cc
index 0d4e0cb..805496d 100644
--- a/content/browser/builtin_service_manifests.cc
+++ b/content/browser/builtin_service_manifests.cc
@@ -62,6 +62,7 @@
           media::GetCdmManifest(),
 #endif
           media::GetMediaManifest(),
+          media::GetMediaRendererManifest(),
           data_decoder::GetManifest(),
           device::GetManifest(),
           media_session::GetManifest(),
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index 1d9e3e3..0dc44d6 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -186,11 +186,8 @@
 }
 
 bool ChildProcessLauncherPriority::is_background() const {
-  if (boost_for_pending_views || has_foreground_service_worker ||
-      has_media_stream) {
-    return false;
-  }
-  return has_only_low_priority_frames || !visible;
+  return !visible && !has_media_stream && !boost_for_pending_views &&
+         !has_foreground_service_worker;
 }
 
 bool ChildProcessLauncherPriority::operator==(
@@ -198,7 +195,6 @@
   return visible == other.visible &&
          has_media_stream == other.has_media_stream &&
          has_foreground_service_worker == other.has_foreground_service_worker &&
-         has_only_low_priority_frames == other.has_only_low_priority_frames &&
          frame_depth == other.frame_depth &&
          intersects_viewport == other.intersects_viewport &&
          boost_for_pending_views == other.boost_for_pending_views
diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h
index 0136b71..0e5f2250 100644
--- a/content/browser/child_process_launcher.h
+++ b/content/browser/child_process_launcher.h
@@ -59,7 +59,6 @@
   ChildProcessLauncherPriority(bool visible,
                                bool has_media_stream,
                                bool has_foreground_service_worker,
-                               bool has_only_low_priority_frames,
                                unsigned int frame_depth,
                                bool intersects_viewport,
                                bool boost_for_pending_views
@@ -71,7 +70,6 @@
       : visible(visible),
         has_media_stream(has_media_stream),
         has_foreground_service_worker(has_foreground_service_worker),
-        has_only_low_priority_frames(has_only_low_priority_frames),
         frame_depth(frame_depth),
         intersects_viewport(intersects_viewport),
         boost_for_pending_views(boost_for_pending_views)
@@ -108,10 +106,6 @@
   // processes.
   bool has_foreground_service_worker;
 
-  // True if this ChildProcessLauncher has a non-zero number of frames attached
-  // to it and they're all low priority.
-  bool has_only_low_priority_frames;
-
   // |frame_depth| is the depth of the shallowest frame this process is
   // responsible for which has |visible| visibility. It only makes sense to
   // compare this property for two ChildProcessLauncherPriority instances with
diff --git a/content/browser/compositor/test/test_image_transport_factory.cc b/content/browser/compositor/test/test_image_transport_factory.cc
index 7ba2ad0..7bd6c7e 100644
--- a/content/browser/compositor/test/test_image_transport_factory.cc
+++ b/content/browser/compositor/test/test_image_transport_factory.cc
@@ -12,6 +12,8 @@
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/test/test_frame_sink_manager.h"
 #include "content/browser/compositor/surface_utils.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "ui/compositor/reflector.h"
 #include "ui/compositor/test/in_process_context_provider.h"
 
@@ -40,24 +42,26 @@
     test_frame_sink_manager_impl_ =
         std::make_unique<viz::TestFrameSinkManagerImpl>();
 
-    viz::mojom::FrameSinkManagerPtr frame_sink_manager;
-    viz::mojom::FrameSinkManagerRequest frame_sink_manager_request =
-        mojo::MakeRequest(&frame_sink_manager);
-    viz::mojom::FrameSinkManagerClientPtr frame_sink_manager_client;
-    viz::mojom::FrameSinkManagerClientRequest
-        frame_sink_manager_client_request =
-            mojo::MakeRequest(&frame_sink_manager_client);
+    mojo::PendingRemote<viz::mojom::FrameSinkManager> frame_sink_manager;
+    mojo::PendingReceiver<viz::mojom::FrameSinkManager>
+        frame_sink_manager_receiver =
+            frame_sink_manager.InitWithNewPipeAndPassReceiver();
+    mojo::PendingRemote<viz::mojom::FrameSinkManagerClient>
+        frame_sink_manager_client;
+    mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient>
+        frame_sink_manager_client_receiver =
+            frame_sink_manager_client.InitWithNewPipeAndPassReceiver();
 
     // Bind endpoints in HostFrameSinkManager.
     host_frame_sink_manager_.BindAndSetManager(
-        std::move(frame_sink_manager_client_request), nullptr,
+        std::move(frame_sink_manager_client_receiver), nullptr,
         std::move(frame_sink_manager));
 
     // Bind endpoints in TestFrameSinkManagerImpl. For non-tests there would be
     // a FrameSinkManagerImpl running in another process and these interface
     // endpoints would be bound there.
-    test_frame_sink_manager_impl_->BindRequest(
-        std::move(frame_sink_manager_request),
+    test_frame_sink_manager_impl_->BindReceiver(
+        std::move(frame_sink_manager_receiver),
         std::move(frame_sink_manager_client));
   } else {
     shared_bitmap_manager_ = std::make_unique<viz::ServerSharedBitmapManager>();
diff --git a/content/browser/compositor/viz_process_transport_factory.cc b/content/browser/compositor/viz_process_transport_factory.cc
index f9dbd30..22635f8 100644
--- a/content/browser/compositor/viz_process_transport_factory.cc
+++ b/content/browser/compositor/viz_process_transport_factory.cc
@@ -34,6 +34,8 @@
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/client/raster_interface.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/compositor/reflector.h"
@@ -127,16 +129,19 @@
 }
 
 void VizProcessTransportFactory::ConnectHostFrameSinkManager() {
-  viz::mojom::FrameSinkManagerPtr frame_sink_manager;
-  viz::mojom::FrameSinkManagerRequest frame_sink_manager_request =
-      mojo::MakeRequest(&frame_sink_manager);
-  viz::mojom::FrameSinkManagerClientPtr frame_sink_manager_client;
-  viz::mojom::FrameSinkManagerClientRequest frame_sink_manager_client_request =
-      mojo::MakeRequest(&frame_sink_manager_client);
+  mojo::PendingRemote<viz::mojom::FrameSinkManager> frame_sink_manager;
+  mojo::PendingReceiver<viz::mojom::FrameSinkManager>
+      frame_sink_manager_receiver =
+          frame_sink_manager.InitWithNewPipeAndPassReceiver();
+  mojo::PendingRemote<viz::mojom::FrameSinkManagerClient>
+      frame_sink_manager_client;
+  mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient>
+      frame_sink_manager_client_receiver =
+          frame_sink_manager_client.InitWithNewPipeAndPassReceiver();
 
   // Setup HostFrameSinkManager with interface endpoints.
   context_factory_private_.GetHostFrameSinkManager()->BindAndSetManager(
-      std::move(frame_sink_manager_client_request),
+      std::move(frame_sink_manager_client_receiver),
       context_factory_private_.resize_task_runner(),
       std::move(frame_sink_manager));
 
@@ -144,8 +149,8 @@
     // Hop to the IO thread, then send the other side of interface to viz
     // process.
     auto connect_on_io_thread =
-        [](viz::mojom::FrameSinkManagerRequest request,
-           viz::mojom::FrameSinkManagerClientPtrInfo client) {
+        [](mojo::PendingReceiver<viz::mojom::FrameSinkManager> receiver,
+           mojo::PendingRemote<viz::mojom::FrameSinkManagerClient> client) {
           // There should always be a GpuProcessHost instance, and GPU process,
           // for running the compositor thread. The exception is during shutdown
           // the GPU process won't be restarted and GpuProcessHost::Get() can
@@ -153,13 +158,13 @@
           auto* gpu_process_host = GpuProcessHost::Get();
           if (gpu_process_host) {
             gpu_process_host->gpu_host()->ConnectFrameSinkManager(
-                std::move(request), std::move(client));
+                std::move(receiver), std::move(client));
           }
         };
     base::PostTask(FROM_HERE, {BrowserThread::IO},
                    base::BindOnce(connect_on_io_thread,
-                                  std::move(frame_sink_manager_request),
-                                  frame_sink_manager_client.PassInterface()));
+                                  std::move(frame_sink_manager_receiver),
+                                  std::move(frame_sink_manager_client)));
   } else {
     DCHECK(!viz_compositor_thread_);
 
@@ -176,9 +181,8 @@
     params->use_activation_deadline = activation_deadline_in_frames.has_value();
     params->activation_deadline_in_frames =
         activation_deadline_in_frames.value_or(0u);
-    params->frame_sink_manager = std::move(frame_sink_manager_request);
-    params->frame_sink_manager_client =
-        frame_sink_manager_client.PassInterface();
+    params->frame_sink_manager = std::move(frame_sink_manager_receiver);
+    params->frame_sink_manager_client = std::move(frame_sink_manager_client);
     viz_compositor_thread_->CreateFrameSinkManager(std::move(params));
   }
 }
diff --git a/content/browser/compositor/viz_process_transport_factory.h b/content/browser/compositor/viz_process_transport_factory.h
index d324bf02e..5550113e 100644
--- a/content/browser/compositor/viz_process_transport_factory.h
+++ b/content/browser/compositor/viz_process_transport_factory.h
@@ -13,7 +13,6 @@
 #include "components/viz/service/main/viz_compositor_thread_runner_impl.h"
 #include "content/browser/compositor/image_transport_factory.h"
 #include "gpu/command_buffer/common/context_result.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h"
 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h"
 #include "ui/compositor/compositor.h"
diff --git a/content/browser/content_service_delegate_impl.cc b/content/browser/content_service_delegate_impl.cc
index 37692e762..10c1cca 100644
--- a/content/browser/content_service_delegate_impl.cc
+++ b/content/browser/content_service_delegate_impl.cc
@@ -51,7 +51,7 @@
     renderer_prefs->can_accept_load_drops = false;
     renderer_prefs->browser_handles_all_top_level_requests =
         params.suppress_navigations;
-    web_contents_->GetRenderViewHost()->SyncRendererPrefs();
+    web_contents_->SyncRendererPrefs();
   }
 
   ~NavigableContentsDelegateImpl() override {
diff --git a/content/browser/devtools/devtools_session.cc b/content/browser/devtools/devtools_session.cc
index ad1d447..ab21cb14 100644
--- a/content/browser/devtools/devtools_session.cc
+++ b/content/browser/devtools/devtools_session.cc
@@ -137,42 +137,44 @@
   session_.set_disconnect_handler(base::BindOnce(
       &DevToolsSession::MojoConnectionDestroyed, base::Unretained(this)));
 
-  // If we auto attached to a new oopif the session is not suspended but the
-  // render frame host may be updated during navigation, so outstanding messages
-  // are resent to the new host.
-  if (!suspended_sending_messages_to_agent_) {
-    for (const auto& pair : waiting_for_response_messages_) {
-      int call_id = pair.first;
-      const WaitingMessage& message = pair.second;
-      DispatchProtocolMessageToAgent(call_id, message.method, message.message);
-    }
-  } else {
-    std::vector<SuspendedMessage> new_suspended_messages;
-    new_suspended_messages.reserve(waiting_for_response_messages_.size());
-    for (const auto& pair : waiting_for_response_messages_) {
-      int call_id = pair.first;
-      const WaitingMessage& message = pair.second;
-      if (TerminateOnCrossProcessNavigation(message.method)) {
-        // We'll not receive any responses from the old agent, so send errors to
-        // the client.
-        auto error = protocol::InternalResponse::createErrorResponse(
-            call_id, protocol::DispatchResponse::kServerError,
-            kTargetClosedMessage);
-        sendProtocolResponse(call_id, std::move(error));
-      } else {
-        new_suspended_messages.push_back(
-            {call_id, message.method, message.message});
-      }
-    }
-    suspended_messages_.insert(suspended_messages_.begin(),
-                               new_suspended_messages.begin(),
-                               new_suspended_messages.end());
-    waiting_for_response_messages_.clear();
-  }
-
   // Set cookie to an empty struct to reattach next time instead of attaching.
   if (!session_state_cookie_)
     session_state_cookie_ = blink::mojom::DevToolsSessionState::New();
+
+  // We're attaching to a new agent while suspended; therefore, messages that
+  // have been sent previously either need to be terminated or re-sent once we
+  // resume, as we will not get any responses from the old agent at this point.
+  if (suspended_sending_messages_to_agent_) {
+    for (auto it = pending_messages_.begin(); it != pending_messages_.end();) {
+      const Message& message = *it;
+      if (waiting_for_response_.count(message.call_id) &&
+          TerminateOnCrossProcessNavigation(message.method)) {
+        // Send error to the client and remove the message from pending.
+        auto error = protocol::InternalResponse::createErrorResponse(
+            message.call_id, protocol::DispatchResponse::kServerError,
+            kTargetClosedMessage);
+        sendProtocolResponse(message.call_id, std::move(error));
+        it = pending_messages_.erase(it);
+      } else {
+        // We'll send or re-send the message in ResumeSendingMessagesToAgent.
+        ++it;
+      }
+    }
+    waiting_for_response_.clear();
+    return;
+  }
+
+  // The session is not suspended but the render frame host may be updated
+  // during navigation because:
+  // - auto attached to a new OOPIF
+  // - cross-process navigation in the main frame
+  // Therefore, we re-send outstanding messages to the new host.
+  for (const Message& message : pending_messages_) {
+    if (waiting_for_response_.count(message.call_id)) {
+      DispatchProtocolMessageToAgent(message.call_id, message.method,
+                                     message.message);
+    }
+  }
 }
 
 void DevToolsSession::MojoConnectionDestroyed() {
@@ -269,13 +271,13 @@
   // In browser-only mode, we should've handled everything in dispatcher.
   DCHECK(!browser_only_);
 
-  if (suspended_sending_messages_to_agent_) {
-    suspended_messages_.push_back({call_id, method, message});
+  auto it = pending_messages_.insert(pending_messages_.end(),
+                                     {call_id, method, message});
+  if (suspended_sending_messages_to_agent_)
     return;
-  }
 
   DispatchProtocolMessageToAgent(call_id, method, message);
-  waiting_for_response_messages_[call_id] = {method, message};
+  waiting_for_response_[call_id] = it;
 }
 
 void DevToolsSession::DispatchProtocolMessageToAgent(
@@ -305,13 +307,15 @@
 void DevToolsSession::ResumeSendingMessagesToAgent() {
   DCHECK(!browser_only_);
   suspended_sending_messages_to_agent_ = false;
-  for (const SuspendedMessage& message : suspended_messages_) {
+  for (auto it = pending_messages_.begin(); it != pending_messages_.end();
+       ++it) {
+    const Message& message = *it;
+    if (waiting_for_response_.count(message.call_id))
+      continue;
     DispatchProtocolMessageToAgent(message.call_id, message.method,
                                    message.message);
-    waiting_for_response_messages_[message.call_id] = {message.method,
-                                                       message.message};
+    waiting_for_response_[message.call_id] = it;
   }
-  suspended_messages_.clear();
 }
 
 // The following methods handle responses or notifications coming from
@@ -365,7 +369,13 @@
     int call_id,
     blink::mojom::DevToolsSessionStatePtr updates) {
   ApplySessionStateUpdates(std::move(updates));
-  waiting_for_response_messages_.erase(call_id);
+  auto it = waiting_for_response_.find(call_id);
+  // TODO(johannes): Consider shutting down renderer instead of just
+  // dropping the message. See shutdownForBadMessage().
+  if (it == waiting_for_response_.end())
+    return;
+  pending_messages_.erase(it->second);
+  waiting_for_response_.erase(it);
   DispatchProtocolResponseOrNotification(client_, agent_host_,
                                          std::move(message));
   // |this| may be deleted at this point.
diff --git a/content/browser/devtools/devtools_session.h b/content/browser/devtools/devtools_session.h
index 4f57d30..c10a926 100644
--- a/content/browser/devtools/devtools_session.h
+++ b/content/browser/devtools/devtools_session.h
@@ -5,10 +5,10 @@
 #ifndef CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_SESSION_H_
 #define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_SESSION_H_
 
+#include <list>
 #include <map>
 #include <memory>
 #include <string>
-#include <vector>
 
 #include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
@@ -115,22 +115,17 @@
   HandlersMap handlers_;
   std::unique_ptr<protocol::UberDispatcher> dispatcher_;
 
-  // These messages were queued after suspending, not sent to the agent,
-  // and will be sent after resuming.
-  struct SuspendedMessage {
+  bool suspended_sending_messages_to_agent_ = false;
+
+  struct Message {
     int call_id;
     std::string method;
     std::string message;
   };
-  std::vector<SuspendedMessage> suspended_messages_;
-  bool suspended_sending_messages_to_agent_ = false;
-
-  // These messages have been sent to agent, but did not get a response yet.
-  struct WaitingMessage {
-    std::string method;
-    std::string message;
-  };
-  std::map<int, WaitingMessage> waiting_for_response_messages_;
+  // Messages that were sent to the agent or queued after suspending.
+  std::list<Message> pending_messages_;
+  // Pending messages that were sent and are thus waiting for a response.
+  base::flat_map<int, std::list<Message>::iterator> waiting_for_response_;
 
   // |session_state_cookie_| always corresponds to a state before
   // any of the waiting for response messages have been handled.
diff --git a/content/browser/dom_storage/local_storage_context_mojo.cc b/content/browser/dom_storage/local_storage_context_mojo.cc
index b42b0a9..2614178 100644
--- a/content/browser/dom_storage/local_storage_context_mojo.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo.cc
@@ -19,6 +19,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
@@ -57,9 +58,7 @@
 
 namespace {
 
-const char kVersionKey[] = "VERSION";
-const char kOriginSeparator = '\x00';
-const char kDataPrefix[] = "_";
+constexpr base::StringPiece kVersionKey = "VERSION";
 const uint8_t kMetaPrefix[] = {'M', 'E', 'T', 'A', ':'};
 const int64_t kMinSchemaVersion = 1;
 const int64_t kCurrentLocalStorageSchemaVersion = 1;
@@ -81,9 +80,9 @@
 static const uint8_t kUTF16Format = 0;
 static const uint8_t kLatin1Format = 1;
 
-std::vector<uint8_t> CreateMetaDataKey(const url::Origin& origin) {
+storage::DomStorageDatabase::Key CreateMetaDataKey(const url::Origin& origin) {
   auto serialized_origin = leveldb::StdStringToUint8Vector(origin.Serialize());
-  std::vector<uint8_t> key;
+  storage::DomStorageDatabase::Key key;
   key.reserve(base::size(kMetaPrefix) + serialized_origin.size());
   key.insert(key.end(), kMetaPrefix, kMetaPrefix + base::size(kMetaPrefix));
   key.insert(key.end(), serialized_origin.begin(), serialized_origin.end());
@@ -94,8 +93,7 @@
   std::move(callback).Run();
 }
 
-void DatabaseErrorResponse(base::OnceClosure callback,
-                           leveldb::mojom::DatabaseError error) {
+void IgnoreStatus(base::OnceClosure callback, leveldb::Status status) {
   std::move(callback).Run();
 }
 
@@ -122,20 +120,36 @@
   std::move(callback).Run(std::move(data));
 }
 
-void AddDeleteOriginOperations(
-    std::vector<leveldb::mojom::BatchedOperationPtr>* operations,
-    const url::Origin& origin) {
-  leveldb::mojom::BatchedOperationPtr item =
-      leveldb::mojom::BatchedOperation::New();
-  item->type = leveldb::mojom::BatchOperationType::DELETE_PREFIXED_KEY;
-  item->key = leveldb::StdStringToUint8Vector(kDataPrefix + origin.Serialize() +
-                                              kOriginSeparator);
-  operations->push_back(std::move(item));
+storage::DomStorageDatabase::Key MakeOriginPrefix(const url::Origin& origin) {
+  const char kDataPrefix = '_';
+  const std::string serialized_origin = origin.Serialize();
+  const char kOriginSeparator = '\x00';
 
-  item = leveldb::mojom::BatchedOperation::New();
-  item->type = leveldb::mojom::BatchOperationType::DELETE_KEY;
-  item->key = CreateMetaDataKey(origin);
-  operations->push_back(std::move(item));
+  storage::DomStorageDatabase::Key prefix;
+  prefix.reserve(serialized_origin.size() + 2);
+  prefix.push_back(kDataPrefix);
+  prefix.insert(prefix.end(), serialized_origin.begin(),
+                serialized_origin.end());
+  prefix.push_back(kOriginSeparator);
+  return prefix;
+}
+
+void DeleteOrigins(leveldb::LevelDBDatabaseImpl* database,
+                   std::vector<url::Origin> origins,
+                   base::OnceCallback<void(leveldb::Status)> callback) {
+  database->RunDatabaseTask(
+      base::BindOnce(
+          [](std::vector<url::Origin> origins,
+             const storage::DomStorageDatabase& db) {
+            leveldb::WriteBatch batch;
+            for (const auto& origin : origins) {
+              db.DeletePrefixed(MakeOriginPrefix(origin), &batch);
+              batch.Delete(leveldb_env::MakeSlice(CreateMetaDataKey(origin)));
+            }
+            return db.Commit(&batch);
+          },
+          std::move(origins)),
+      std::move(callback));
 }
 
 enum class CachePurgeReason {
@@ -208,8 +222,7 @@
     }
 #endif
     area_ = std::make_unique<StorageAreaImpl>(
-        context_->database_.get(),
-        kDataPrefix + origin_.Serialize() + kOriginSeparator, this, options);
+        context_->database_.get(), MakeOriginPrefix(origin_), this, options);
     area_ptr_ = area_.get();
   }
 
@@ -223,49 +236,45 @@
     storage_area()->ScheduleImmediateCommit();
   }
 
-  std::vector<leveldb::mojom::BatchedOperationPtr> PrepareToCommit() override {
-    std::vector<leveldb::mojom::BatchedOperationPtr> operations;
-
+  void PrepareToCommit(std::vector<storage::DomStorageDatabase::KeyValuePair>*
+                           extra_entries_to_add,
+                       std::vector<storage::DomStorageDatabase::Key>*
+                           extra_keys_to_delete) override {
     // Write schema version if not already done so before.
     if (!context_->database_initialized_) {
-      leveldb::mojom::BatchedOperationPtr item =
-          leveldb::mojom::BatchedOperation::New();
-      item->type = leveldb::mojom::BatchOperationType::PUT_KEY;
-      item->key = leveldb::StdStringToUint8Vector(kVersionKey);
-      item->value = leveldb::StdStringToUint8Vector(
-          base::NumberToString(kCurrentLocalStorageSchemaVersion));
-      operations.push_back(std::move(item));
+      const std::string version =
+          base::NumberToString(kCurrentLocalStorageSchemaVersion);
+      extra_entries_to_add->emplace_back(
+          storage::DomStorageDatabase::Key(kVersionKey.begin(),
+                                           kVersionKey.end()),
+          storage::DomStorageDatabase::Value(version.begin(), version.end()));
       context_->database_initialized_ = true;
     }
 
-    leveldb::mojom::BatchedOperationPtr item =
-        leveldb::mojom::BatchedOperation::New();
-    item->type = leveldb::mojom::BatchOperationType::PUT_KEY;
-    item->key = CreateMetaDataKey(origin_);
+    storage::DomStorageDatabase::Key metadata_key = CreateMetaDataKey(origin_);
     if (storage_area()->empty()) {
-      item->type = leveldb::mojom::BatchOperationType::DELETE_KEY;
+      extra_keys_to_delete->push_back(std::move(metadata_key));
     } else {
-      item->type = leveldb::mojom::BatchOperationType::PUT_KEY;
       LocalStorageOriginMetaData data;
       data.set_last_modified(base::Time::Now().ToInternalValue());
       data.set_size_bytes(storage_area()->storage_used());
-      item->value = leveldb::StdStringToUint8Vector(data.SerializeAsString());
+      std::string serialized_data = data.SerializeAsString();
+      extra_entries_to_add->emplace_back(
+          std::move(metadata_key),
+          storage::DomStorageDatabase::Value(serialized_data.begin(),
+                                             serialized_data.end()));
     }
-    operations.push_back(std::move(item));
-
-    return operations;
   }
 
-  void DidCommit(leveldb::mojom::DatabaseError error) override {
+  void DidCommit(leveldb::Status status) override {
     UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.CommitResult",
-                              leveldb::GetLevelDBStatusUMAValue(error),
+                              leveldb_env::GetLevelDBStatusUMAValue(status),
                               leveldb_env::LEVELDB_STATUS_MAX);
 
     // Delete any old database that might still exist if we successfully wrote
     // data to LevelDB, and our LevelDB is actually disk backed.
-    if (error == leveldb::mojom::DatabaseError::OK && !deleted_old_data_ &&
-        !context_->directory_.empty() && context_->task_runner_ &&
-        !context_->old_localstorage_path_.empty()) {
+    if (status.ok() && !deleted_old_data_ && !context_->directory_.empty() &&
+        context_->task_runner_ && !context_->old_localstorage_path_.empty()) {
       deleted_old_data_ = true;
       context_->task_runner_->PostShutdownBlockingTask(
           FROM_HERE, DOMStorageTaskRunner::PRIMARY_SEQUENCE,
@@ -273,7 +282,7 @@
                          sql_db_path()));
     }
 
-    context_->OnCommitResult(error);
+    context_->OnCommitResult(status);
   }
 
   void MigrateData(StorageAreaImpl::ValueMapCallback callback) override {
@@ -341,11 +350,12 @@
     return changes;
   }
 
-  void OnMapLoaded(leveldb::mojom::DatabaseError error) override {
-    if (error != leveldb::mojom::DatabaseError::OK)
+  void OnMapLoaded(leveldb::Status status) override {
+    if (!status.ok()) {
       UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.MapLoadError",
-                                leveldb::GetLevelDBStatusUMAValue(error),
+                                leveldb_env::GetLevelDBStatusUMAValue(status),
                                 leveldb_env::LEVELDB_STATUS_MAX);
+    }
   }
 
   void Bind(mojo::PendingReceiver<blink::mojom::StorageArea> receiver) {
@@ -455,11 +465,11 @@
         "\n", base::BindOnce(&SuccessResponse, std::move(callback)));
     found->second->storage_area()->ScheduleImmediateCommit();
   } else if (database_) {
-    std::vector<leveldb::mojom::BatchedOperationPtr> operations;
-    AddDeleteOriginOperations(&operations, origin);
-    database_->Write(
-        std::move(operations),
-        base::BindOnce(&DatabaseErrorResponse, std::move(callback)));
+    DeleteOrigins(
+        database_.get(), {std::move(origin)},
+        base::BindOnce([](base::OnceClosure callback,
+                          leveldb::Status) { std::move(callback).Run(); },
+                       std::move(callback)));
   } else {
     std::move(callback).Run();
   }
@@ -478,8 +488,7 @@
     // an area is not ready to commit its changes, nothing breaks but the
     // rewrite doesn't remove all traces of old data.
     Flush();
-    database_->RewriteDB(
-        base::BindOnce(&DatabaseErrorResponse, std::move(callback)));
+    database_->RewriteDB(base::BindOnce(&IgnoreStatus, std::move(callback)));
   } else {
     std::move(callback).Run();
   }
@@ -510,7 +519,7 @@
   // Nothing to do if no connection to the database was ever finished.
   if (connection_state_ != CONNECTION_FINISHED) {
     connection_state_ = CONNECTION_SHUTDOWN;
-    OnShutdownComplete(leveldb::mojom::DatabaseError::OK);
+    OnShutdownComplete(leveldb::Status::OK());
     return;
   }
 
@@ -531,7 +540,7 @@
   // Respect the content policy settings about what to
   // keep and what to discard.
   if (force_keep_session_state_) {
-    OnShutdownComplete(leveldb::mojom::DatabaseError::OK);
+    OnShutdownComplete(leveldb::Status::OK());
     return;  // Keep everything.
   }
 
@@ -544,7 +553,7 @@
         base::BindOnce(&LocalStorageContextMojo::OnGotStorageUsageForShutdown,
                        base::Unretained(this)));
   } else {
-    OnShutdownComplete(leveldb::mojom::DatabaseError::OK);
+    OnShutdownComplete(leveldb::Status::OK());
   }
 }
 
@@ -734,19 +743,18 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-void LocalStorageContextMojo::OnDatabaseOpened(
-    leveldb::mojom::DatabaseError status) {
-  if (status != leveldb::mojom::DatabaseError::OK) {
+void LocalStorageContextMojo::OnDatabaseOpened(leveldb::Status status) {
+  if (!status.ok()) {
     UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.DatabaseOpenError",
-                              leveldb::GetLevelDBStatusUMAValue(status),
+                              leveldb_env::GetLevelDBStatusUMAValue(status),
                               leveldb_env::LEVELDB_STATUS_MAX);
     if (in_memory_) {
       UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.DatabaseOpenError.Memory",
-                                leveldb::GetLevelDBStatusUMAValue(status),
+                                leveldb_env::GetLevelDBStatusUMAValue(status),
                                 leveldb_env::LEVELDB_STATUS_MAX);
     } else {
       UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.DatabaseOpenError.Disk",
-                                leveldb::GetLevelDBStatusUMAValue(status),
+                                leveldb_env::GetLevelDBStatusUMAValue(status),
                                 leveldb_env::LEVELDB_STATUS_MAX);
     }
     LogDatabaseOpenResult(OpenResult::DATABASE_OPEN_FAILED);
@@ -759,7 +767,7 @@
   // Verify DB schema version.
   if (database_) {
     database_->Get(
-        leveldb::StdStringToUint8Vector(kVersionKey),
+        std::vector<uint8_t>(kVersionKey.begin(), kVersionKey.end()),
         base::BindOnce(&LocalStorageContextMojo::OnGotDatabaseVersion,
                        weak_ptr_factory_.GetWeakPtr()));
     return;
@@ -769,12 +777,12 @@
 }
 
 void LocalStorageContextMojo::OnGotDatabaseVersion(
-    leveldb::mojom::DatabaseError status,
+    leveldb::Status status,
     const std::vector<uint8_t>& value) {
-  if (status == leveldb::mojom::DatabaseError::NOT_FOUND) {
+  if (status.IsNotFound()) {
     // New database, nothing more to do. Current version will get written
     // when first data is committed.
-  } else if (status == leveldb::mojom::DatabaseError::OK) {
+  } else if (status.ok()) {
     // Existing database, check if version number matches current schema
     // version.
     int64_t db_version;
@@ -792,7 +800,7 @@
   } else {
     // Other read error. Possibly database corruption.
     UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.ReadVersionError",
-                              leveldb::GetLevelDBStatusUMAValue(status),
+                              leveldb_env::GetLevelDBStatusUMAValue(status),
                               leveldb_env::LEVELDB_STATUS_MAX);
     LogDatabaseOpenResult(OpenResult::VERSION_READ_ERROR);
     DeleteAndRecreateDatabase(
@@ -925,7 +933,7 @@
 
 void LocalStorageContextMojo::OnGotMetaData(
     GetStorageUsageCallback callback,
-    leveldb::mojom::DatabaseError status,
+    leveldb::Status status,
     std::vector<leveldb::mojom::KeyValuePtr> data) {
   std::vector<StorageUsageInfo> result;
   std::set<url::Origin> origins;
@@ -967,28 +975,25 @@
 
 void LocalStorageContextMojo::OnGotStorageUsageForShutdown(
     std::vector<StorageUsageInfo> usage) {
-  std::vector<leveldb::mojom::BatchedOperationPtr> operations;
+  std::vector<url::Origin> origins_to_delete;
   for (const auto& info : usage) {
     if (special_storage_policy_->IsStorageProtected(info.origin.GetURL()))
       continue;
     if (!special_storage_policy_->IsStorageSessionOnly(info.origin.GetURL()))
       continue;
-
-    AddDeleteOriginOperations(&operations, info.origin);
+    origins_to_delete.push_back(info.origin);
   }
 
-  if (!operations.empty()) {
-    database_->Write(
-        std::move(operations),
-        base::BindOnce(&LocalStorageContextMojo::OnShutdownComplete,
-                       base::Unretained(this)));
+  if (!origins_to_delete.empty()) {
+    DeleteOrigins(database_.get(), std::move(origins_to_delete),
+                  base::BindOnce(&LocalStorageContextMojo::OnShutdownComplete,
+                                 base::Unretained(this)));
   } else {
-    OnShutdownComplete(leveldb::mojom::DatabaseError::OK);
+    OnShutdownComplete(leveldb::Status::OK());
   }
 }
 
-void LocalStorageContextMojo::OnShutdownComplete(
-    leveldb::mojom::DatabaseError error) {
+void LocalStorageContextMojo::OnShutdownComplete(leveldb::Status status) {
   delete this;
 }
 
@@ -1003,12 +1008,11 @@
   }
 }
 
-void LocalStorageContextMojo::OnCommitResult(
-    leveldb::mojom::DatabaseError error) {
+void LocalStorageContextMojo::OnCommitResult(leveldb::Status status) {
   DCHECK(connection_state_ == CONNECTION_FINISHED ||
          connection_state_ == CONNECTION_SHUTDOWN)
       << connection_state_;
-  if (error == leveldb::mojom::DatabaseError::OK) {
+  if (status.ok()) {
     commit_error_count_ = 0;
     return;
   }
diff --git a/content/browser/dom_storage/local_storage_context_mojo.h b/content/browser/dom_storage/local_storage_context_mojo.h
index 66afada..87aacf4 100644
--- a/content/browser/dom_storage/local_storage_context_mojo.h
+++ b/content/browser/dom_storage/local_storage_context_mojo.h
@@ -134,8 +134,8 @@
 
   // Part of our asynchronous directory opening called from RunWhenConnected().
   void InitiateConnection(bool in_memory_only = false);
-  void OnDatabaseOpened(leveldb::mojom::DatabaseError status);
-  void OnGotDatabaseVersion(leveldb::mojom::DatabaseError status,
+  void OnDatabaseOpened(leveldb::Status status);
+  void OnGotDatabaseVersion(leveldb::Status status,
                             const std::vector<uint8_t>& value);
   void OnConnectionFinished();
   void DeleteAndRecreateDatabase(const char* histogram_name);
@@ -152,14 +152,14 @@
   // directly from that function, or through |on_database_open_callbacks_|.
   void RetrieveStorageUsage(GetStorageUsageCallback callback);
   void OnGotMetaData(GetStorageUsageCallback callback,
-                     leveldb::mojom::DatabaseError status,
+                     leveldb::Status status,
                      std::vector<leveldb::mojom::KeyValuePtr> data);
 
   void OnGotStorageUsageForShutdown(std::vector<StorageUsageInfo> usage);
-  void OnShutdownComplete(leveldb::mojom::DatabaseError error);
+  void OnShutdownComplete(leveldb::Status status);
 
   void GetStatistics(size_t* total_cache_size, size_t* unused_area_count);
-  void OnCommitResult(leveldb::mojom::DatabaseError error);
+  void OnCommitResult(leveldb::Status status);
 
   // These values are written to logs.  New enum values can be added, but
   // existing enums must never be renumbered or deleted and reused.
diff --git a/content/browser/dom_storage/session_storage_area_impl_unittest.cc b/content/browser/dom_storage/session_storage_area_impl_unittest.cc
index 85d423ab..e7058d9 100644
--- a/content/browser/dom_storage/session_storage_area_impl_unittest.cc
+++ b/content/browser/dom_storage/session_storage_area_impl_unittest.cc
@@ -34,10 +34,15 @@
 #include "url/gurl.h"
 
 namespace content {
+
 namespace {
+
 using leveldb::StdStringToUint8Vector;
 using leveldb::Uint8VectorToStdString;
-using leveldb::mojom::DatabaseError;
+
+MATCHER(OKStatus, "Equality matcher for type OK leveldb::Status") {
+  return arg.ok();
+}
 
 class MockListener : public SessionStorageDataMap::Listener {
  public:
@@ -47,7 +52,7 @@
                void(const std::vector<uint8_t>& map_id,
                     SessionStorageDataMap* map));
   MOCK_METHOD1(OnDataMapDestruction, void(const std::vector<uint8_t>& map_id));
-  MOCK_METHOD1(OnCommitResult, void(DatabaseError error));
+  MOCK_METHOD1(OnCommitResult, void(leveldb::Status));
 };
 
 class SessionStorageAreaImplTest : public testing::Test {
@@ -229,7 +234,7 @@
   EXPECT_CALL(listener_,
               OnDataMapCreation(StdStringToUint8Vector("1"), testing::_))
       .Times(1);
-  EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK))
+  EXPECT_CALL(listener_, OnCommitResult(OKStatus()))
       .Times(testing::AnyNumber());
   EXPECT_TRUE(test::PutSync(ss_leveldb2.get(), StdStringToUint8Vector("key2"),
                             StdStringToUint8Vector("data2"), base::nullopt,
@@ -349,7 +354,7 @@
       .Times(1);
   // There should be no commits, as we don't actually have to change any data.
   // |ss_leveldb_impl1| should just switch to a new, empty map.
-  EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)).Times(0);
+  EXPECT_CALL(listener_, OnCommitResult(OKStatus())).Times(0);
   EXPECT_TRUE(test::DeleteAllSync(ss_leveldb1.get(), "source"));
 
   // The maps were forked on the above call.
@@ -379,7 +384,7 @@
       GetRegisterNewAreaMapCallback());
 
   base::RunLoop loop;
-  EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK))
+  EXPECT_CALL(listener_, OnCommitResult(OKStatus()))
       .WillOnce(base::test::RunClosure(loop.QuitClosure()));
   EXPECT_TRUE(test::DeleteAllSync(ss_leveldb_impl1.get(), "source"));
   ss_leveldb_impl1->data_map()->storage_area()->ScheduleImmediateCommit();
@@ -425,7 +430,7 @@
       .Times(1);
   // There should be no commits, as we don't actually have to change any data.
   // |ss_leveldb_impl1| should just switch to a new, empty map.
-  EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)).Times(0);
+  EXPECT_CALL(listener_, OnCommitResult(OKStatus())).Times(0);
   EXPECT_TRUE(test::DeleteAllSync(ss_leveldb_impl1.get(), "source"));
 
   // The maps were forked on the above call.
diff --git a/content/browser/dom_storage/session_storage_context_mojo.cc b/content/browser/dom_storage/session_storage_context_mojo.cc
index 764cee12..e619636 100644
--- a/content/browser/dom_storage/session_storage_context_mojo.cc
+++ b/content/browser/dom_storage/session_storage_context_mojo.cc
@@ -96,7 +96,7 @@
 }
 
 void SessionStorageErrorResponse(base::OnceClosure callback,
-                                 leveldb::mojom::DatabaseError error) {
+                                 leveldb::Status status) {
   std::move(callback).Run();
 }
 }  // namespace
@@ -364,7 +364,7 @@
   // Nothing to do if no connection to the database was ever finished.
   if (connection_state_ != CONNECTION_FINISHED) {
     connection_state_ = CONNECTION_SHUTDOWN;
-    OnShutdownComplete(leveldb::mojom::DatabaseError::OK);
+    OnShutdownComplete(leveldb::Status::OK());
     return;
   }
   connection_state_ = CONNECTION_SHUTDOWN;
@@ -381,7 +381,7 @@
     area->CancelAllPendingRequests();
   }
 
-  OnShutdownComplete(leveldb::mojom::DatabaseError::OK);
+  OnShutdownComplete(leveldb::Status::OK());
 }
 
 void SessionStorageContextMojo::PurgeMemory() {
@@ -522,7 +522,7 @@
 }
 
 void SessionStorageContextMojo::PretendToConnectForTesting() {
-  OnDatabaseOpened(leveldb::mojom::DatabaseError::OK);
+  OnDatabaseOpened(leveldb::Status::OK());
 }
 
 void SessionStorageContextMojo::FlushAreaForTesting(
@@ -571,13 +571,12 @@
   data_maps_.erase(map_prefix);
 }
 
-void SessionStorageContextMojo::OnCommitResult(
-    leveldb::mojom::DatabaseError error) {
+void SessionStorageContextMojo::OnCommitResult(leveldb::Status status) {
   DCHECK_EQ(connection_state_, CONNECTION_FINISHED);
   UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.CommitResult",
-                            leveldb::GetLevelDBStatusUMAValue(error),
+                            leveldb_env::GetLevelDBStatusUMAValue(status),
                             leveldb_env::LEVELDB_STATUS_MAX);
-  if (error == leveldb::mojom::DatabaseError::OK) {
+  if (status.ok()) {
     commit_error_count_ = 0;
     return;
   }
@@ -601,8 +600,8 @@
 
 void SessionStorageContextMojo::OnCommitResultWithCallback(
     base::OnceClosure callback,
-    leveldb::mojom::DatabaseError error) {
-  OnCommitResult(error);
+    leveldb::Status status) {
+  OnCommitResult(status);
   std::move(callback).Run();
 }
 
@@ -738,20 +737,19 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-void SessionStorageContextMojo::OnDatabaseOpened(
-    leveldb::mojom::DatabaseError status) {
-  if (status != leveldb::mojom::DatabaseError::OK) {
+void SessionStorageContextMojo::OnDatabaseOpened(leveldb::Status status) {
+  if (!status.ok()) {
     UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.DatabaseOpenError",
-                              leveldb::GetLevelDBStatusUMAValue(status),
+                              leveldb_env::GetLevelDBStatusUMAValue(status),
                               leveldb_env::LEVELDB_STATUS_MAX);
     if (in_memory_) {
       UMA_HISTOGRAM_ENUMERATION(
           "SessionStorageContext.DatabaseOpenError.Memory",
-          leveldb::GetLevelDBStatusUMAValue(status),
+          leveldb_env::GetLevelDBStatusUMAValue(status),
           leveldb_env::LEVELDB_STATUS_MAX);
     } else {
       UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.DatabaseOpenError.Disk",
-                                leveldb::GetLevelDBStatusUMAValue(status),
+                                leveldb_env::GetLevelDBStatusUMAValue(status),
                                 leveldb_env::LEVELDB_STATUS_MAX);
     }
     LogDatabaseOpenResult(OpenResult::kDatabaseOpenFailed);
@@ -890,11 +888,11 @@
     database_->Write(
         std::move(migration_operations),
         base::BindOnce(
-            [](base::OnceCallback<void(leveldb::mojom::DatabaseError)> callback,
+            [](base::OnceCallback<void(leveldb::Status)> callback,
                scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-               leveldb::mojom::DatabaseError error) {
+               leveldb::Status status) {
               callback_task_runner->PostTask(
-                  FROM_HERE, base::BindOnce(std::move(callback), error));
+                  FROM_HERE, base::BindOnce(std::move(callback), status));
             },
             base::BindOnce(&SessionStorageContextMojo::OnCommitResult,
                            weak_ptr_factory_.GetWeakPtr()),
@@ -1001,8 +999,7 @@
   InitiateConnection(recreate_in_memory);
 }
 
-void SessionStorageContextMojo::OnShutdownComplete(
-    leveldb::mojom::DatabaseError error) {
+void SessionStorageContextMojo::OnShutdownComplete(leveldb::Status status) {
   delete this;
 }
 
diff --git a/content/browser/dom_storage/session_storage_context_mojo.h b/content/browser/dom_storage/session_storage_context_mojo.h
index 2924b7e..af56e1f 100644
--- a/content/browser/dom_storage/session_storage_context_mojo.h
+++ b/content/browser/dom_storage/session_storage_context_mojo.h
@@ -181,9 +181,9 @@
   void OnDataMapCreation(const std::vector<uint8_t>& map_prefix,
                          SessionStorageDataMap* map) override;
   void OnDataMapDestruction(const std::vector<uint8_t>& map_prefix) override;
-  void OnCommitResult(leveldb::mojom::DatabaseError error) override;
+  void OnCommitResult(leveldb::Status status) override;
   void OnCommitResultWithCallback(base::OnceClosure callback,
-                                  leveldb::mojom::DatabaseError error);
+                                  leveldb::Status status);
 
   // SessionStorageNamespaceImplMojo::Delegate implementation:
   scoped_refptr<SessionStorageDataMap> MaybeGetExistingDataMapForId(
@@ -206,7 +206,7 @@
 
   // Part of our asynchronous directory opening called from RunWhenConnected().
   void InitiateConnection(bool in_memory_only = false);
-  void OnDatabaseOpened(leveldb::mojom::DatabaseError status);
+  void OnDatabaseOpened(leveldb::Status status);
 
   struct DatabaseMetadataResult {
     DatabaseMetadataResult();
@@ -242,10 +242,10 @@
   void OnDBDestroyed(bool recreate_in_memory, leveldb::Status status);
 
   void OnGotMetaData(GetStorageUsageCallback callback,
-                     leveldb::mojom::DatabaseError status,
+                     leveldb::Status status,
                      std::vector<leveldb::mojom::KeyValuePtr> data);
 
-  void OnShutdownComplete(leveldb::mojom::DatabaseError error);
+  void OnShutdownComplete(leveldb::Status status);
 
   void GetStatistics(size_t* total_cache_size, size_t* unused_areas_count);
 
diff --git a/content/browser/dom_storage/session_storage_context_mojo_unittest.cc b/content/browser/dom_storage/session_storage_context_mojo_unittest.cc
index 1b7eb97..c877da43 100644
--- a/content/browser/dom_storage/session_storage_context_mojo_unittest.cc
+++ b/content/browser/dom_storage/session_storage_context_mojo_unittest.cc
@@ -42,7 +42,6 @@
 using leveldb::StdStringToUint8Vector;
 using leveldb::String16ToUint8Vector;
 using leveldb::Uint8VectorToStdString;
-using leveldb::mojom::DatabaseError;
 using leveldb::mojom::KeyValuePtr;
 
 static const char kSessionStorageDirectory[] = "Session Storage";
@@ -997,9 +996,9 @@
   base::RunLoop loop;
   context()->DatabaseForTesting()->DeletePrefixed(
       leveldb::StringPieceToUint8Vector("map"),
-      base::BindLambdaForTesting([&](DatabaseError status) {
+      base::BindLambdaForTesting([&](leveldb::Status status) {
         loop.Quit();
-        EXPECT_EQ(DatabaseError::OK, status);
+        EXPECT_TRUE(status.ok());
       }));
   loop.Run();
 
diff --git a/content/browser/dom_storage/session_storage_data_map.cc b/content/browser/dom_storage/session_storage_data_map.cc
index 49e3168..1df59039 100644
--- a/content/browser/dom_storage/session_storage_data_map.cc
+++ b/content/browser/dom_storage/session_storage_data_map.cc
@@ -7,6 +7,7 @@
 #include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/services/leveldb/public/cpp/util.h"
 #include "content/browser/dom_storage/dom_storage_types.h"
 
 namespace content {
@@ -38,13 +39,8 @@
       listener, std::move(map_data), std::move(clone_from)));
 }
 
-std::vector<leveldb::mojom::BatchedOperationPtr>
-SessionStorageDataMap::PrepareToCommit() {
-  return std::vector<leveldb::mojom::BatchedOperationPtr>();
-}
-
-void SessionStorageDataMap::DidCommit(leveldb::mojom::DatabaseError error) {
-  listener_->OnCommitResult(error);
+void SessionStorageDataMap::DidCommit(leveldb::Status status) {
+  listener_->OnCommitResult(status);
 }
 
 SessionStorageDataMap::SessionStorageDataMap(
@@ -99,7 +95,7 @@
   storage_area()->ScheduleImmediateCommit();
 }
 
-void SessionStorageDataMap::OnMapLoaded(leveldb::mojom::DatabaseError) {
+void SessionStorageDataMap::OnMapLoaded(leveldb::Status) {
   clone_from_data_map_.reset();
 }
 
diff --git a/content/browser/dom_storage/session_storage_data_map.h b/content/browser/dom_storage/session_storage_data_map.h
index 478835cb..8649d80 100644
--- a/content/browser/dom_storage/session_storage_data_map.h
+++ b/content/browser/dom_storage/session_storage_data_map.h
@@ -42,7 +42,7 @@
     virtual void OnDataMapCreation(const std::vector<uint8_t>& map_id,
                                    SessionStorageDataMap* map) = 0;
     virtual void OnDataMapDestruction(const std::vector<uint8_t>& map_id) = 0;
-    virtual void OnCommitResult(leveldb::mojom::DatabaseError error) = 0;
+    virtual void OnCommitResult(leveldb::Status status) = 0;
   };
 
   static scoped_refptr<SessionStorageDataMap> CreateFromDisk(
@@ -77,9 +77,7 @@
   // Note: this is irrelevant, as the parent area is handling binding.
   void OnNoBindings() override {}
 
-  std::vector<leveldb::mojom::BatchedOperationPtr> PrepareToCommit() override;
-
-  void DidCommit(leveldb::mojom::DatabaseError error) override;
+  void DidCommit(leveldb::Status status) override;
 
  private:
   friend class base::RefCounted<SessionStorageDataMap>;
@@ -95,7 +93,7 @@
       scoped_refptr<SessionStorageDataMap> forking_from);
   ~SessionStorageDataMap() override;
 
-  void OnMapLoaded(leveldb::mojom::DatabaseError error) override;
+  void OnMapLoaded(leveldb::Status status) override;
 
   static StorageAreaImpl::Options GetOptions();
 
diff --git a/content/browser/dom_storage/session_storage_data_map_unittest.cc b/content/browser/dom_storage/session_storage_data_map_unittest.cc
index b8a7e121..0fef0cb 100644
--- a/content/browser/dom_storage/session_storage_data_map_unittest.cc
+++ b/content/browser/dom_storage/session_storage_data_map_unittest.cc
@@ -26,10 +26,16 @@
 #include "url/gurl.h"
 
 namespace content {
+
 namespace {
+
 using leveldb::StdStringToUint8Vector;
 using leveldb::Uint8VectorToStdString;
 
+MATCHER(OKStatus, "Equality matcher for type OK leveldb::Status") {
+  return arg.ok();
+}
+
 base::span<const uint8_t> MakeBytes(base::StringPiece str) {
   return base::as_bytes(base::make_span(str));
 }
@@ -42,7 +48,7 @@
                void(const std::vector<uint8_t>& map_id,
                     SessionStorageDataMap* map));
   MOCK_METHOD1(OnDataMapDestruction, void(const std::vector<uint8_t>& map_id));
-  MOCK_METHOD1(OnCommitResult, void(leveldb::mojom::DatabaseError error));
+  MOCK_METHOD1(OnCommitResult, void(leveldb::Status status));
 };
 
 void GetAllDataCallback(bool* success_out,
@@ -92,8 +98,8 @@
     database_ = leveldb::LevelDBDatabaseImpl::OpenInMemory(
         base::nullopt, "SessionStorageDataMapTest",
         base::CreateSequencedTaskRunner({base::MayBlock(), base::ThreadPool()}),
-        base::BindLambdaForTesting([&](leveldb::mojom::DatabaseError error) {
-          ASSERT_EQ(leveldb::mojom::DatabaseError::OK, error);
+        base::BindLambdaForTesting([&](leveldb::Status status) {
+          ASSERT_TRUE(status.ok());
           loop.Quit();
         }));
     loop.Run();
@@ -223,8 +229,7 @@
               OnDataMapCreation(StdStringToUint8Vector("2"), testing::_))
       .Times(1);
   // One call on fork.
-  EXPECT_CALL(listener_, OnCommitResult(leveldb::mojom::DatabaseError::OK))
-      .Times(1);
+  EXPECT_CALL(listener_, OnCommitResult(OKStatus())).Times(1);
 
   scoped_refptr<SessionStorageDataMap> map2 =
       SessionStorageDataMap::CreateClone(
diff --git a/content/browser/dom_storage/session_storage_metadata_unittest.cc b/content/browser/dom_storage/session_storage_metadata_unittest.cc
index a5a5ec0..a7d9a0d 100644
--- a/content/browser/dom_storage/session_storage_metadata_unittest.cc
+++ b/content/browser/dom_storage/session_storage_metadata_unittest.cc
@@ -35,10 +35,9 @@
 namespace {
 using leveldb::StdStringToUint8Vector;
 using leveldb::Uint8VectorToStdString;
-using leveldb::mojom::DatabaseError;
 
-void ErrorCallback(DatabaseError* error_out, DatabaseError error) {
-  *error_out = error;
+void ErrorCallback(leveldb::Status* status_out, leveldb::Status status) {
+  *status_out = status;
 }
 
 class SessionStorageMetadataTest : public testing::Test {
@@ -53,8 +52,7 @@
     database_ = leveldb::LevelDBDatabaseImpl::OpenInMemory(
         base::nullopt, "SessionStorageMetadataTest",
         base::CreateSequencedTaskRunner({base::MayBlock(), base::ThreadPool()}),
-        base::BindLambdaForTesting(
-            [&](leveldb::mojom::DatabaseError) { loop.Quit(); }));
+        base::BindLambdaForTesting([&](leveldb::Status) { loop.Quit(); }));
     loop.Run();
 
     next_map_id_key_ = std::vector<uint8_t>(
@@ -164,16 +162,14 @@
     return contents;
   }
 
-  void WriteBatch(
-      std::vector<leveldb::mojom::BatchedOperationPtr> operations,
-      base::OnceCallback<void(leveldb::mojom::DatabaseError)> callback) {
+  void WriteBatch(std::vector<leveldb::mojom::BatchedOperationPtr> operations,
+                  base::OnceCallback<void(leveldb::Status)> callback) {
     base::RunLoop loop;
-    database_->Write(
-        std::move(operations),
-        base::BindLambdaForTesting([&](leveldb::mojom::DatabaseError error) {
-          std::move(callback).Run(error);
-          loop.Quit();
-        }));
+    database_->Write(std::move(operations),
+                     base::BindLambdaForTesting([&](leveldb::Status status) {
+                       std::move(callback).Run(status);
+                       loop.Quit();
+                     }));
     loop.Run();
   }
 
@@ -196,9 +192,9 @@
   std::vector<leveldb::mojom::BatchedOperationPtr> operations =
       metadata.SetupNewDatabase();
 
-  DatabaseError error;
-  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &error));
-  EXPECT_EQ(DatabaseError::OK, error);
+  leveldb::Status status;
+  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &status));
+  EXPECT_TRUE(status.ok());
 
   auto contents = GetDatabaseContents();
   EXPECT_EQ(StdStringToUint8Vector("1"), contents[database_version_key_]);
@@ -256,9 +252,9 @@
                    ->second[test_origin1_]
                    ->ReferenceCount());
 
-  DatabaseError error;
-  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &error));
-  EXPECT_EQ(DatabaseError::OK, error);
+  leveldb::Status status;
+  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &status));
+  EXPECT_TRUE(status.ok());
 
   // Verify metadata was written to disk.
   auto contents = GetDatabaseContents();
@@ -280,9 +276,10 @@
   std::vector<leveldb::mojom::BatchedOperationPtr> operations;
   metadata.RegisterShallowClonedNamespace(ns1_entry, ns3_entry, &operations);
 
-  DatabaseError error;
-  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &error));
-  EXPECT_EQ(DatabaseError::OK, error);
+  leveldb::Status status;
+  ;
+  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &status));
+  EXPECT_TRUE(status.ok());
 
   // Verify in-memory metadata is correct.
   EXPECT_EQ(StdStringToUint8Vector("map-1-"),
@@ -315,9 +312,9 @@
 
   std::vector<leveldb::mojom::BatchedOperationPtr> operations;
   metadata.DeleteNamespace(test_namespace1_id_, &operations);
-  DatabaseError error;
-  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &error));
-  EXPECT_EQ(DatabaseError::OK, error);
+  leveldb::Status status;
+  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &status));
+  EXPECT_TRUE(status.ok());
 
   EXPECT_FALSE(
       base::Contains(metadata.namespace_origin_map(), test_namespace1_id_));
@@ -349,9 +346,9 @@
   // First delete an area with a shared map.
   std::vector<leveldb::mojom::BatchedOperationPtr> operations;
   metadata.DeleteArea(test_namespace1_id_, test_origin1_, &operations);
-  DatabaseError error;
-  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &error));
-  EXPECT_EQ(DatabaseError::OK, error);
+  leveldb::Status status;
+  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &status));
+  EXPECT_TRUE(status.ok());
 
   // Verify in-memory metadata is correct.
   auto ns1_entry = metadata.GetOrCreateNamespaceEntry(test_namespace1_id_);
@@ -377,8 +374,8 @@
   // Now delete an area with a unique map.
   operations.clear();
   metadata.DeleteArea(test_namespace2_id_, test_origin2_, &operations);
-  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &error));
-  EXPECT_EQ(DatabaseError::OK, error);
+  WriteBatch(std::move(operations), base::BindOnce(&ErrorCallback, &status));
+  EXPECT_TRUE(status.ok());
 
   // Verify in-memory metadata is correct.
   EXPECT_FALSE(base::Contains(ns1_entry->second, test_origin1_));
diff --git a/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc b/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
index d9cb280f..48af5b0 100644
--- a/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
+++ b/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
@@ -31,15 +31,20 @@
 #include "url/gurl.h"
 
 namespace content {
+
 namespace {
+
 using leveldb::StdStringToUint8Vector;
-using leveldb::mojom::DatabaseError;
 using NamespaceEntry = SessionStorageMetadata::NamespaceEntry;
 
 constexpr const int kTestProcessIdOrigin1 = 11;
 constexpr const int kTestProcessIdAllOrigins = 12;
 constexpr const int kTestProcessIdOrigin3 = 13;
 
+MATCHER(OKStatus, "Equality matcher for type OK leveldb::Status") {
+  return arg.ok();
+}
+
 class MockListener : public SessionStorageDataMap::Listener {
  public:
   MockListener() {}
@@ -48,7 +53,7 @@
                void(const std::vector<uint8_t>& map_id,
                     SessionStorageDataMap* map));
   MOCK_METHOD1(OnDataMapDestruction, void(const std::vector<uint8_t>& map_id));
-  MOCK_METHOD1(OnCommitResult, void(leveldb::mojom::DatabaseError error));
+  MOCK_METHOD1(OnCommitResult, void(leveldb::Status));
 };
 
 class SessionStorageNamespaceImplMojoTest
@@ -65,9 +70,9 @@
 
   void WriteBatch(std::vector<leveldb::mojom::BatchedOperationPtr> operations) {
     base::RunLoop loop(base::RunLoop::Type::kNestableTasksAllowed);
-    database_->Write(std::move(operations),
-                     base::BindLambdaForTesting(
-                         [&](leveldb::mojom::DatabaseError) { loop.Quit(); }));
+    database_->Write(
+        std::move(operations),
+        base::BindLambdaForTesting([&](leveldb::Status) { loop.Quit(); }));
     loop.Run();
   }
 
@@ -77,8 +82,7 @@
     database_ = leveldb::LevelDBDatabaseImpl::OpenInMemory(
         base::nullopt, "SessionStorageNamespaceImplMojoTest",
         base::CreateSequencedTaskRunner({base::MayBlock(), base::ThreadPool()}),
-        base::BindLambdaForTesting(
-            [&](leveldb::mojom::DatabaseError) { loop.Quit(); }));
+        base::BindLambdaForTesting([&](leveldb::Status) { loop.Quit(); }));
     loop.Run();
 
     metadata_.SetupNewDatabase();
@@ -267,7 +271,7 @@
                          leveldb_1.BindNewEndpointAndPassReceiver());
 
   base::RunLoop commit_loop;
-  EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK))
+  EXPECT_CALL(listener_, OnCommitResult(OKStatus()))
       .Times(1)
       .WillOnce(testing::Invoke([&](auto error) { commit_loop.Quit(); }));
   test::PutSync(leveldb_1.get(), StdStringToUint8Vector("key2"),
@@ -323,7 +327,7 @@
   // Do a put in the cloned namespace.
   base::RunLoop commit_loop;
   auto commit_callback = base::BarrierClosure(2, commit_loop.QuitClosure());
-  EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK))
+  EXPECT_CALL(listener_, OnCommitResult(OKStatus()))
       .Times(2)
       .WillRepeatedly(
           testing::Invoke([&](auto error) { commit_callback.Run(); }));
@@ -397,7 +401,7 @@
 
   // Do a put in the cloned namespace.
   base::RunLoop commit_loop;
-  EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK))
+  EXPECT_CALL(listener_, OnCommitResult(OKStatus()))
       .Times(1)
       .WillOnce(testing::Invoke([&](auto error) { commit_loop.Quit(); }));
   test::PutSync(leveldb_n2_o2.get(), StdStringToUint8Vector("key2"),
@@ -458,7 +462,7 @@
       .WillOnce(base::test::RunClosure(loop.QuitClosure()));
 
   base::RunLoop commit_loop;
-  EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK))
+  EXPECT_CALL(listener_, OnCommitResult(OKStatus()))
       .Times(1)
       .WillOnce(testing::Invoke([&](auto error) { commit_loop.Quit(); }));
   namespace_impl->RemoveOriginData(test_origin1_, base::DoNothing());
@@ -489,7 +493,7 @@
       metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
 
   base::RunLoop loop;
-  EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK))
+  EXPECT_CALL(listener_, OnCommitResult(OKStatus()))
       .WillOnce(base::test::RunClosure(loop.QuitClosure()));
   namespace_impl->RemoveOriginData(test_origin1_, base::DoNothing());
   loop.Run();
diff --git a/content/browser/dom_storage/storage_area_impl.cc b/content/browser/dom_storage/storage_area_impl.cc
index e405b01..dbb47977 100644
--- a/content/browser/dom_storage/storage_area_impl.cc
+++ b/content/browser/dom_storage/storage_area_impl.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/containers/span.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
@@ -14,16 +15,17 @@
 #include "components/services/leveldb/public/cpp/util.h"
 #include "content/public/browser/browser_thread.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
+#include "third_party/leveldatabase/env_chromium.h"
 
 namespace content {
-namespace {
-using leveldb::mojom::BatchedOperation;
-using leveldb::mojom::BatchedOperationPtr;
-using leveldb::mojom::DatabaseError;
-}  // namespace
 
 StorageAreaImpl::Delegate::~Delegate() {}
 
+void StorageAreaImpl::Delegate::PrepareToCommit(
+    std::vector<storage::DomStorageDatabase::KeyValuePair>*
+        extra_entries_to_add,
+    std::vector<storage::DomStorageDatabase::Key>* extra_keys_to_delete) {}
+
 void StorageAreaImpl::Delegate::MigrateData(
     base::OnceCallback<void(std::unique_ptr<ValueMap>)> callback) {
   std::move(callback).Run(nullptr);
@@ -34,7 +36,7 @@
   return std::vector<Change>();
 }
 
-void StorageAreaImpl::Delegate::OnMapLoaded(DatabaseError) {}
+void StorageAreaImpl::Delegate::OnMapLoaded(leveldb::Status) {}
 
 bool StorageAreaImpl::s_aggressive_flushing_enabled_ = false;
 
@@ -98,7 +100,7 @@
 void StorageAreaImpl::InitializeAsEmpty() {
   DCHECK_EQ(map_state_, MapState::UNLOADED);
   map_state_ = MapState::LOADING_FROM_DATABASE;
-  OnMapLoaded(leveldb::mojom::DatabaseError::OK,
+  OnMapLoaded(leveldb::Status::OK(),
               std::vector<leveldb::mojom::KeyValuePtr>());
 }
 
@@ -570,7 +572,7 @@
   map_state_ = MapState::LOADING_FROM_DATABASE;
 
   if (!database_) {
-    OnMapLoaded(DatabaseError::IO_ERROR,
+    OnMapLoaded(leveldb::Status::IOError(""),
                 std::vector<leveldb::mojom::KeyValuePtr>());
     return;
   }
@@ -581,12 +583,12 @@
 }
 
 void StorageAreaImpl::OnMapLoaded(
-    DatabaseError status,
+    leveldb::Status status,
     std::vector<leveldb::mojom::KeyValuePtr> data) {
   DCHECK(keys_values_map_.empty());
   DCHECK_EQ(map_state_, MapState::LOADING_FROM_DATABASE);
 
-  if (data.empty() && status == DatabaseError::OK) {
+  if (data.empty() && status.ok()) {
     delegate_->MigrateData(base::BindOnce(&StorageAreaImpl::OnGotMigrationData,
                                           weak_ptr_factory_.GetWeakPtr()));
     return;
@@ -632,7 +634,7 @@
   // We proceed without using a backing store, nothing will be persisted but the
   // class is functional for the lifetime of the object.
   delegate_->OnMapLoaded(status);
-  if (status != DatabaseError::OK) {
+  if (!status.ok()) {
     database_ = nullptr;
     SetCacheMode(CacheMode::KEYS_AND_VALUES);
   }
@@ -648,7 +650,7 @@
   keys_values_map_ = data ? std::move(*data) : ValueMap();
   map_state_ = MapState::LOADED_KEYS_AND_VALUES;
   CalculateStorageAndMemoryUsed();
-  delegate_->OnMapLoaded(leveldb::mojom::DatabaseError::OK);
+  delegate_->OnMapLoaded(leveldb::Status::OK());
 
   if (database_ && !empty()) {
     CreateCommitBatchIfNeeded();
@@ -756,56 +758,60 @@
   commit_rate_limiter_.add_samples(1);
 
   // Commit all our changes in a single batch.
-  std::vector<BatchedOperationPtr> operations = delegate_->PrepareToCommit();
-  bool has_changes = !operations.empty() ||
-                     !commit_batch_->changed_values.empty() ||
-                     !commit_batch_->changed_keys.empty();
-  if (commit_batch_->clear_all_first) {
-    BatchedOperationPtr item = BatchedOperation::New();
-    item->type = leveldb::mojom::BatchOperationType::DELETE_PREFIXED_KEY;
-    item->key = prefix_;
-    operations.push_back(std::move(item));
-  }
+  struct Commit {
+    storage::DomStorageDatabase::Key prefix;
+    bool clear_all_first;
+    std::vector<storage::DomStorageDatabase::KeyValuePair> entries_to_add;
+    std::vector<storage::DomStorageDatabase::Key> keys_to_delete;
+    base::Optional<storage::DomStorageDatabase::Key> copy_to_prefix;
+  };
+
+  Commit commit;
+  commit.prefix = prefix_;
+  commit.clear_all_first = commit_batch_->clear_all_first;
+  delegate_->PrepareToCommit(&commit.entries_to_add, &commit.keys_to_delete);
+
+  const bool has_changes = !commit.entries_to_add.empty() ||
+                           !commit.keys_to_delete.empty() ||
+                           !commit_batch_->changed_values.empty() ||
+                           !commit_batch_->changed_keys.empty();
   size_t data_size = 0;
   if (map_state_ == MapState::LOADED_KEYS_AND_VALUES) {
     DCHECK(commit_batch_->changed_values.empty())
         << "Map state and commit state out of sync.";
     for (const auto& key : commit_batch_->changed_keys) {
       data_size += key.size();
-      BatchedOperationPtr item = BatchedOperation::New();
-      item->key.reserve(prefix_.size() + key.size());
-      item->key.insert(item->key.end(), prefix_.begin(), prefix_.end());
-      item->key.insert(item->key.end(), key.begin(), key.end());
-      auto kv_it = keys_values_map_.find(key);
-      if (kv_it != keys_values_map_.end()) {
-        item->type = leveldb::mojom::BatchOperationType::PUT_KEY;
-        data_size += kv_it->second.size();
-        item->value = kv_it->second;
+      storage::DomStorageDatabase::Key prefixed_key;
+      prefixed_key.reserve(prefix_.size() + key.size());
+      prefixed_key.insert(prefixed_key.end(), prefix_.begin(), prefix_.end());
+      prefixed_key.insert(prefixed_key.end(), key.begin(), key.end());
+      auto it = keys_values_map_.find(key);
+      if (it != keys_values_map_.end()) {
+        data_size += it->second.size();
+        commit.entries_to_add.emplace_back(std::move(prefixed_key), it->second);
       } else {
-        item->type = leveldb::mojom::BatchOperationType::DELETE_KEY;
+        commit.keys_to_delete.push_back(std::move(prefixed_key));
       }
-      operations.push_back(std::move(item));
     }
   } else {
     DCHECK(commit_batch_->changed_keys.empty())
         << "Map state and commit state out of sync.";
     DCHECK_EQ(map_state_, MapState::LOADED_KEYS_ONLY);
-    for (auto& it : commit_batch_->changed_values) {
-      const auto& key = it.first;
+    for (auto& entry : commit_batch_->changed_values) {
+      const auto& key = entry.first;
       data_size += key.size();
-      BatchedOperationPtr item = BatchedOperation::New();
-      item->key.reserve(prefix_.size() + key.size());
-      item->key.insert(item->key.end(), prefix_.begin(), prefix_.end());
-      item->key.insert(item->key.end(), key.begin(), key.end());
-      auto kv_it = keys_only_map_.find(key);
-      if (kv_it != keys_only_map_.end()) {
-        item->type = leveldb::mojom::BatchOperationType::PUT_KEY;
-        data_size += it.second.size();
-        item->value = std::move(it.second);
+      storage::DomStorageDatabase::Key prefixed_key;
+      prefixed_key.reserve(prefix_.size() + key.size());
+      prefixed_key.insert(prefixed_key.end(), prefix_.begin(), prefix_.end());
+      prefixed_key.insert(prefixed_key.end(), key.begin(), key.end());
+      auto it = keys_only_map_.find(key);
+      if (it != keys_only_map_.end()) {
+        data_size += entry.second.size();
+        commit.entries_to_add.emplace_back(std::move(prefixed_key),
+                                           std::move(entry.second));
       } else {
-        item->type = leveldb::mojom::BatchOperationType::DELETE_KEY;
+        commit.keys_to_delete.push_back(std::move(prefixed_key));
       }
-      operations.push_back(std::move(item));
     }
   }
   // Schedule the copy, and ignore if |clear_all_first| is specified and there
@@ -813,11 +819,7 @@
   if (commit_batch_->copy_to_prefix) {
     DCHECK(!has_changes);
     DCHECK(!commit_batch_->clear_all_first);
-    BatchedOperationPtr item = BatchedOperation::New();
-    item->type = leveldb::mojom::BatchOperationType::COPY_PREFIXED_KEY;
-    item->key = prefix_;
-    item->value = std::move(commit_batch_->copy_to_prefix.value());
-    operations.push_back(std::move(item));
+    commit.copy_to_prefix = std::move(commit_batch_->copy_to_prefix);
   }
   commit_batch_.reset();
 
@@ -825,26 +827,41 @@
 
   ++commit_batches_in_flight_;
 
-  // TODO(michaeln): Currently there is no guarantee LevelDBDatabaseImpl::Write
-  // will run during a clean shutdown. We need that to avoid dataloss.
-  database_->Write(std::move(operations),
-                   base::BindOnce(&StorageAreaImpl::OnCommitComplete,
-                                  weak_ptr_factory_.GetWeakPtr()));
+  database_->RunDatabaseTask(
+      base::BindOnce(
+          [](Commit commit, const storage::DomStorageDatabase& db) {
+            leveldb::WriteBatch batch;
+            if (commit.clear_all_first)
+              db.DeletePrefixed(commit.prefix, &batch);
+            for (const auto& entry : commit.entries_to_add) {
+              batch.Put(leveldb_env::MakeSlice(entry.key),
+                        leveldb_env::MakeSlice(entry.value));
+            }
+            for (const auto& key : commit.keys_to_delete)
+              batch.Delete(leveldb_env::MakeSlice(key));
+            if (commit.copy_to_prefix) {
+              db.CopyPrefixed(commit.prefix, commit.copy_to_prefix.value(),
+                              &batch);
+            }
+            return db.Commit(&batch);
+          },
+          std::move(commit)),
+      base::BindOnce(&StorageAreaImpl::OnCommitComplete,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
-void StorageAreaImpl::OnCommitComplete(DatabaseError error) {
+void StorageAreaImpl::OnCommitComplete(leveldb::Status status) {
   has_committed_data_ = true;
   --commit_batches_in_flight_;
   StartCommitTimer();
 
-  if (error != DatabaseError::OK) {
+  if (!status.ok())
     SetCacheMode(CacheMode::KEYS_AND_VALUES);
-  }
 
   // Call before |DidCommit| as delegate can destroy this object.
   UnloadMapIfPossible();
 
-  delegate_->DidCommit(error);
+  delegate_->DidCommit(status);
 }
 
 void StorageAreaImpl::UnloadMapIfPossible() {
diff --git a/content/browser/dom_storage/storage_area_impl.h b/content/browser/dom_storage/storage_area_impl.h
index 28bc039..0f31929 100644
--- a/content/browser/dom_storage/storage_area_impl.h
+++ b/content/browser/dom_storage/storage_area_impl.h
@@ -16,6 +16,7 @@
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "components/services/leveldb/public/mojom/leveldb.mojom.h"
+#include "components/services/storage/dom_storage/dom_storage_database.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -58,15 +59,17 @@
    public:
     virtual ~Delegate();
     virtual void OnNoBindings() = 0;
-    virtual std::vector<leveldb::mojom::BatchedOperationPtr>
-    PrepareToCommit() = 0;
-    virtual void DidCommit(leveldb::mojom::DatabaseError error) = 0;
+    virtual void PrepareToCommit(
+        std::vector<storage::DomStorageDatabase::KeyValuePair>*
+            extra_entries_to_add,
+        std::vector<storage::DomStorageDatabase::Key>* extra_keys_to_delete);
+    virtual void DidCommit(leveldb::Status error) = 0;
     // Called during loading if no data was found. Needs to call |callback|.
     virtual void MigrateData(ValueMapCallback callback);
     // Called during loading to give delegate a chance to modify the data as
     // stored in the database.
     virtual std::vector<Change> FixUpData(const ValueMap& data);
-    virtual void OnMapLoaded(leveldb::mojom::DatabaseError error);
+    virtual void OnMapLoaded(leveldb::Status status);
   };
 
   enum class CacheMode {
@@ -285,7 +288,7 @@
   // Then if the |cache_mode_| is keys-only, it unloads the map to the
   // |keys_only_map_| and sets the |map_state_| to LOADED_KEYS_ONLY
   void LoadMap(base::OnceClosure completion_callback);
-  void OnMapLoaded(leveldb::mojom::DatabaseError status,
+  void OnMapLoaded(leveldb::Status status,
                    std::vector<leveldb::mojom::KeyValuePtr> data);
   void OnGotMigrationData(std::unique_ptr<ValueMap> data);
   void CalculateStorageAndMemoryUsed();
@@ -296,7 +299,7 @@
   base::TimeDelta ComputeCommitDelay() const;
 
   void CommitChanges();
-  void OnCommitComplete(leveldb::mojom::DatabaseError error);
+  void OnCommitComplete(leveldb::Status status);
 
   void UnloadMapIfPossible();
 
diff --git a/content/browser/dom_storage/storage_area_impl_unittest.cc b/content/browser/dom_storage/storage_area_impl_unittest.cc
index 963db65..1d7cf8c7 100644
--- a/content/browser/dom_storage/storage_area_impl_unittest.cc
+++ b/content/browser/dom_storage/storage_area_impl_unittest.cc
@@ -34,7 +34,6 @@
 using test::MakeGetAllCallback;
 using test::MakeSuccessCallback;
 using CacheMode = StorageAreaImpl::CacheMode;
-using DatabaseError = leveldb::mojom::DatabaseError;
 
 const char* kTestSource = "source";
 const size_t kTestSizeLimit = 512;
@@ -53,16 +52,13 @@
   ~MockDelegate() override {}
 
   void OnNoBindings() override {}
-  std::vector<leveldb::mojom::BatchedOperationPtr> PrepareToCommit() override {
-    return std::vector<leveldb::mojom::BatchedOperationPtr>();
-  }
-  void DidCommit(DatabaseError error) override {
-    if (error != DatabaseError::OK)
+  void DidCommit(leveldb::Status status) override {
+    if (!status.ok())
       LOG(ERROR) << "error committing!";
     if (committed_)
       std::move(committed_).Run();
   }
-  void OnMapLoaded(DatabaseError error) override { map_load_count_++; }
+  void OnMapLoaded(leveldb::Status) override { map_load_count_++; }
   std::vector<StorageAreaImpl::Change> FixUpData(
       const StorageAreaImpl::ValueMap& data) override {
     return std::move(mock_changes_);
@@ -131,7 +127,7 @@
         base::nullopt, "StorageAreaImplTest",
         base::CreateSequencedTaskRunner({base::MayBlock(), base::ThreadPool()}),
         base::BindLambdaForTesting(
-            [&](leveldb::mojom::DatabaseError error) { loop.Quit(); }));
+            [&](leveldb::Status status) { loop.Quit(); }));
     loop.Run();
 
     StorageAreaImpl::Options options =
diff --git a/content/browser/find_request_manager.cc b/content/browser/find_request_manager.cc
index 4b6a61c..0bda4a5 100644
--- a/content/browser/find_request_manager.cc
+++ b/content/browser/find_request_manager.cc
@@ -409,6 +409,11 @@
   if (current_session_id_ == kInvalidId || !CheckFrame(rfh))
     return;
 
+  // Make sure to always clear the highlighted selection. It is useful in case
+  // the user goes back to the same page using the BackForwardCache.
+  static_cast<RenderFrameHostImpl*>(rfh)->GetFindInPage()->StopFinding(
+      blink::mojom::StopFindAction::kStopFindActionClearSelection);
+
   // If matches are counted for the frame that is being removed, decrement the
   // match total before erasing that entry.
   auto it = find_in_page_clients_.find(rfh);
diff --git a/content/browser/find_request_manager_browsertest.cc b/content/browser/find_request_manager_browsertest.cc
index a0190132..b07c002d 100644
--- a/content/browser/find_request_manager_browsertest.cc
+++ b/content/browser/find_request_manager_browsertest.cc
@@ -845,4 +845,63 @@
 }
 #endif  // defined(OS_ANDROID)
 
+// Test basic find-in-page functionality after going back and forth to the same
+// page. In particular, find-in-page should continue to work after going back to
+// a page using the back-forward cache.
+IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, HistoryBackAndForth) {
+  GURL url_a = embedded_test_server()->GetURL("a.com", "/find_in_page.html");
+  GURL url_b = embedded_test_server()->GetURL("b.com", "/find_in_page.html");
+
+  auto test_page = [&] {
+    if (GetParam())
+      MakeChildFrameCrossProcess();
+
+    auto options = blink::mojom::FindOptions::New();
+
+    // The initial find-in-page request.
+    Find("result", options->Clone());
+    delegate()->WaitForFinalReply();
+
+    FindResults results = delegate()->GetFindResults();
+    EXPECT_EQ(last_request_id(), results.request_id);
+    EXPECT_EQ(19, results.number_of_matches);
+
+    // Iterate forward/backward over a few elements.
+    int match_index = results.active_match_ordinal;
+    for (int delta : {-1, -1, +1, +1, +1, +1, -1, +1, +1}) {
+      options->find_next = true;
+      options->forward = delta > 0;
+      // |active_match_ordinal| uses 1-based index. It belongs to [1, 19].
+      match_index += delta;
+      match_index = (match_index + 18) % 19 + 1;
+
+      Find("result", options->Clone());
+      delegate()->WaitForFinalReply();
+      results = delegate()->GetFindResults();
+
+      EXPECT_EQ(last_request_id(), results.request_id);
+      EXPECT_EQ(19, results.number_of_matches);
+      EXPECT_EQ(match_index, results.active_match_ordinal);
+    }
+  };
+
+  // 1) Navigate to A.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  test_page();
+
+  // 2) Navigate to B.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b));
+  test_page();
+
+  // 3) Go back to A.
+  contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  test_page();
+
+  // 4) Go forward to B.
+  contents()->GetController().GoForward();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  test_page();
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/back_forward_cache_impl.cc b/content/browser/frame_host/back_forward_cache_impl.cc
index 23d9b73..48c73cf 100644
--- a/content/browser/frame_host/back_forward_cache_impl.cc
+++ b/content/browser/frame_host/back_forward_cache_impl.cc
@@ -17,6 +17,7 @@
 #include "content/common/page_messages.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/navigation_policy.h"
+#include "net/http/http_request_headers.h"
 #include "net/http/http_status_code.h"
 #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
 
@@ -190,6 +191,8 @@
       return "No: BackForwardCache::DisableForRenderFrameHost() was called";
     case Reason::kDomainNotAllowed:
       return "No: This domain is not allowed to be stored in BackForwardCache";
+    case Reason::kHTTPMethodNotGET:
+      return "No: HTTP method is not GET";
   }
 }
 
@@ -293,6 +296,12 @@
         BackForwardCacheMetrics::CanNotStoreDocumentReason::kHTTPStatusNotOK);
   }
 
+  // Only store documents that were fetched via HTTP GET method.
+  if (rfh->last_http_method() != net::HttpRequestHeaders::kGetMethod) {
+    return CanStoreDocumentResult::No(
+        BackForwardCacheMetrics::CanNotStoreDocumentReason::kHTTPMethodNotGET);
+  }
+
   // Do not store main document with non HTTP/HTTPS URL scheme. In particular,
   // this excludes the new tab page.
   if (!rfh->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
diff --git a/content/browser/frame_host/back_forward_cache_metrics.h b/content/browser/frame_host/back_forward_cache_metrics.h
index d03db36..a66fa537 100644
--- a/content/browser/frame_host/back_forward_cache_metrics.h
+++ b/content/browser/frame_host/back_forward_cache_metrics.h
@@ -42,7 +42,8 @@
     kWasGrantedMediaAccess,
     kBlocklistedFeatures,
     kDisableForRenderFrameHostCalled,
-    kDomainNotAllowed
+    kDomainNotAllowed,
+    kHTTPMethodNotGET
   };
 
   // Please keep in sync with BackForwardCacheHistoryNavigationOutcome in
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index a9648cb..cc5506b 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -6622,7 +6622,7 @@
       ->web_contents()
       ->GetMutableRendererPrefs()
       ->browser_handles_all_top_level_requests = true;
-  shell()->web_contents()->GetRenderViewHost()->SyncRendererPrefs();
+  shell()->web_contents()->SyncRendererPrefs();
 
   // Submit the form.
   TestNavigationObserver form_post_observer(shell()->web_contents(), 1);
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 556c04f..6f34a9896 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -1420,9 +1420,9 @@
   // For renderer-initiated navigations we need to check if the source has
   // access to the URL. Browser-initiated navigations only rely on the
   // |CanRedirectToURL| test above.
-  if (!browser_initiated_ && source_site_instance() &&
+  if (!browser_initiated_ && GetSourceSiteInstance() &&
       !ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
-          source_site_instance()->GetProcess()->GetID(),
+          GetSourceSiteInstance()->GetProcess()->GetID(),
           redirect_info.new_url)) {
     DVLOG(1) << "Denied unauthorized redirect for "
              << redirect_info.new_url.possibly_invalid_spec();
@@ -2058,19 +2058,20 @@
 
   // Initialize the BundledExchangesHandle.
   if (bundled_exchanges_handle_tracker_) {
-    DCHECK(base::FeatureList::IsEnabled(features::kBundledHTTPExchanges) ||
+    DCHECK(base::FeatureList::IsEnabled(features::kWebBundles) ||
            base::CommandLine::ForCurrentProcess()->HasSwitch(
                switches::kTrustableBundledExchangesFileUrl));
     bundled_exchanges_handle_ =
         bundled_exchanges_handle_tracker_->MaybeCreateBundledExchangesHandle(
-            common_params_->url);
+            common_params_->url, frame_tree_node_->frame_tree_node_id());
   }
   if (!bundled_exchanges_handle_ && bundled_exchanges_navigation_info_) {
-    DCHECK(base::FeatureList::IsEnabled(features::kBundledHTTPExchanges) ||
+    DCHECK(base::FeatureList::IsEnabled(features::kWebBundles) ||
            base::CommandLine::ForCurrentProcess()->HasSwitch(
                switches::kTrustableBundledExchangesFileUrl));
     bundled_exchanges_handle_ = BundledExchangesHandle::CreateForNavigationInfo(
-        bundled_exchanges_navigation_info_->Clone());
+        bundled_exchanges_navigation_info_->Clone(),
+        frame_tree_node_->frame_tree_node_id());
   }
   if (!bundled_exchanges_handle_) {
     if (bundled_exchanges_utils::CanLoadAsTrustableBundledExchangesFile(
@@ -2081,11 +2082,13 @@
       // invalid character.
       if (source) {
         bundled_exchanges_handle_ =
-            BundledExchangesHandle::CreateForTrustableFile(std::move(source));
+            BundledExchangesHandle::CreateForTrustableFile(
+                std::move(source), frame_tree_node_->frame_tree_node_id());
       }
     } else if (bundled_exchanges_utils::CanLoadAsBundledExchangesFile(
                    common_params_->url)) {
-      bundled_exchanges_handle_ = BundledExchangesHandle::CreateForFile();
+      bundled_exchanges_handle_ = BundledExchangesHandle::CreateForFile(
+          frame_tree_node_->frame_tree_node_id());
     }
   }
 
@@ -3609,6 +3612,10 @@
   return starting_site_instance_.get();
 }
 
+SiteInstanceImpl* NavigationRequest::GetSourceSiteInstance() {
+  return source_site_instance_.get();
+}
+
 bool NavigationRequest::IsRendererInitiated() {
   return !browser_initiated_;
 }
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 9483ea6..281aeacf 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -193,6 +193,7 @@
   int64_t GetNavigationId() override;
   const GURL& GetURL() override;
   SiteInstanceImpl* GetStartingSiteInstance() override;
+  SiteInstanceImpl* GetSourceSiteInstance() override;
   bool IsInMainFrame() override;
   bool IsParentMainFrame() override;
   bool IsRendererInitiated() override;
@@ -280,10 +281,6 @@
 
   FrameTreeNode* frame_tree_node() const { return frame_tree_node_; }
 
-  SiteInstanceImpl* source_site_instance() const {
-    return source_site_instance_.get();
-  }
-
   SiteInstanceImpl* dest_site_instance() const {
     return dest_site_instance_.get();
   }
diff --git a/content/browser/frame_host/navigator.h b/content/browser/frame_host/navigator.h
index 8ffb3f6..194aa85 100644
--- a/content/browser/frame_host/navigator.h
+++ b/content/browser/frame_host/navigator.h
@@ -15,7 +15,7 @@
 #include "content/public/browser/navigation_controller.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "ui/base/window_open_disposition.h"
 
 class GURL;
@@ -116,7 +116,7 @@
       WindowOpenDisposition disposition,
       bool should_replace_current_entry,
       bool user_gesture,
-      blink::WebTriggeringEventInfo triggering_event_info,
+      blink::TriggeringEventInfo triggering_event_info,
       const std::string& href_translate,
       scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {}
 
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index 66c8a81..0fa600da 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -407,7 +407,7 @@
     WindowOpenDisposition disposition,
     bool should_replace_current_entry,
     bool user_gesture,
-    blink::WebTriggeringEventInfo triggering_event_info,
+    blink::TriggeringEventInfo triggering_event_info,
     const std::string& href_translate,
     scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
   // Note: This can be called for subframes (even when OOPIFs are not possible)
diff --git a/content/browser/frame_host/navigator_impl.h b/content/browser/frame_host/navigator_impl.h
index 4ca0982e..c3a53d4 100644
--- a/content/browser/frame_host/navigator_impl.h
+++ b/content/browser/frame_host/navigator_impl.h
@@ -72,7 +72,7 @@
                       WindowOpenDisposition disposition,
                       bool should_replace_current_entry,
                       bool user_gesture,
-                      blink::WebTriggeringEventInfo triggering_event_info,
+                      blink::TriggeringEventInfo triggering_event_info,
                       const std::string& href_translate,
                       scoped_refptr<network::SharedURLLoaderFactory>
                           blob_url_loader_factory) override;
diff --git a/content/browser/frame_host/render_frame_host_delegate.cc b/content/browser/frame_host/render_frame_host_delegate.cc
index 4dfe0da..e4f2dcf6 100644
--- a/content/browser/frame_host/render_frame_host_delegate.cc
+++ b/content/browser/frame_host/render_frame_host_delegate.cc
@@ -163,9 +163,4 @@
   return nullptr;
 }
 
-bool RenderFrameHostDelegate::IsFrameLowPriority(
-    const RenderFrameHost* render_frame_host) {
-  return false;
-}
-
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_delegate.h b/content/browser/frame_host/render_frame_host_delegate.h
index a08c40d..7af38fe 100644
--- a/content/browser/frame_host/render_frame_host_delegate.h
+++ b/content/browser/frame_host/render_frame_host_delegate.h
@@ -457,9 +457,6 @@
                                    const base::Optional<SkColor>& theme_color) {
   }
 
-  // Determine if the frame is of a low priority.
-  virtual bool IsFrameLowPriority(const RenderFrameHost* render_frame_host);
-
  protected:
   virtual ~RenderFrameHostDelegate() {}
 };
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index b945a5f..c799201 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -87,7 +87,6 @@
 #include "content/browser/renderer_host/input/input_router.h"
 #include "content/browser/renderer_host/input/timeout_monitor.h"
 #include "content/browser/renderer_host/media/audio_input_delegate_impl.h"
-#include "content/browser/renderer_host/media/media_stream_dispatcher_host.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_delegate.h"
 #include "content/browser/renderer_host/render_view_host_delegate_view.h"
@@ -911,7 +910,9 @@
       keep_alive_timeout_(base::TimeDelta::FromSeconds(30)),
       subframe_unload_timeout_(base::TimeDelta::FromMilliseconds(
           RenderViewHostImpl::kUnloadTimeoutMS)),
-      commit_callback_interceptor_(nullptr) {
+      commit_callback_interceptor_(nullptr),
+      media_device_id_salt_base_(
+          BrowserContext::CreateRandomMediaDeviceIDSalt()) {
   GetProcess()->AddRoute(routing_id_, this);
   g_routing_id_frame_map.Get().emplace(
       GlobalFrameRoutingId(GetProcess()->GetID(), routing_id_), this);
@@ -968,8 +969,6 @@
       owned_render_widget_host_ = RenderWidgetHostFactory::Create(
           frame_tree_->render_widget_delegate(), GetProcess(),
           widget_routing_id, std::move(widget), /*hidden=*/true);
-      owned_render_widget_host_->BindVisualPropertiesManager(
-          render_view_host_->GetVisualPropertiesManager());
       owned_render_widget_host_->set_owned_by_render_frame_host(true);
     }
 
@@ -1019,10 +1018,6 @@
   ClearAllWebUI();
 
   SetLastCommittedSiteUrl(GURL());
-  if (last_committed_document_priority_) {
-    GetProcess()->UpdateFrameWithPriority(last_committed_document_priority_,
-                                          base::nullopt);
-  }
 
   if (overlay_routing_token_)
     g_token_frame_map.Get().erase(*overlay_routing_token_);
@@ -2226,6 +2221,10 @@
     ResetFeaturePolicy();
     active_sandbox_flags_ = frame_tree_node()->active_sandbox_flags();
   }
+
+  // Reset the salt so that media device IDs are reset after the new navigation
+  // if necessary.
+  media_device_id_salt_base_ = BrowserContext::CreateRandomMediaDeviceIDSalt();
 }
 
 void RenderFrameHostImpl::SetLastCommittedOrigin(const url::Origin& origin) {
@@ -2387,18 +2386,6 @@
   last_committed_url_ = url;
 }
 
-void RenderFrameHostImpl::UpdateRenderProcessHostFramePriorities() {
-  const auto new_committed_document_priority =
-      (delegate_ && delegate_->IsFrameLowPriority(this))
-          ? RenderProcessHostImpl::FramePriority::kLow
-          : RenderProcessHostImpl::FramePriority::kNormal;
-  if (last_committed_document_priority_ != new_committed_document_priority) {
-    GetProcess()->UpdateFrameWithPriority(last_committed_document_priority_,
-                                          new_committed_document_priority);
-    last_committed_document_priority_ = new_committed_document_priority;
-  }
-}
-
 void RenderFrameHostImpl::OnDetach() {
   if (!parent_) {
     bad_message::ReceivedBadMessage(GetProcess(),
@@ -3865,7 +3852,7 @@
 
 void RenderFrameHostImpl::OnSuddenTerminationDisablerChanged(
     bool present,
-    blink::WebSuddenTerminationDisablerType disabler_type) {
+    blink::SuddenTerminationDisablerType disabler_type) {
   DCHECK_NE(GetSuddenTerminationDisablerState(disabler_type), present);
   if (present) {
     sudden_termination_disabler_types_enabled_ |= disabler_type;
@@ -3875,7 +3862,7 @@
 }
 
 bool RenderFrameHostImpl::GetSuddenTerminationDisablerState(
-    blink::WebSuddenTerminationDisablerType disabler_type) {
+    blink::SuddenTerminationDisablerType disabler_type) {
   return (sudden_termination_disabler_types_enabled_ & disabler_type) != 0;
 }
 
@@ -4695,7 +4682,6 @@
   }
   send_before_unload_start_time_ = base::TimeTicks();
   render_view_host_->is_waiting_for_close_ack_ = false;
-  network_service_connection_error_handler_holder_.reset();
 }
 
 CanCommitStatus RenderFrameHostImpl::CanCommitOriginAndUrl(
@@ -7143,9 +7129,8 @@
                                            NavigationGestureUser);
 
   last_http_status_code_ = validated_params->http_status_code;
+  last_http_method_ = validated_params->method;
   UpdateSiteURL(validated_params->url, validated_params->url_is_unreachable);
-  if (!is_same_document_navigation)
-    UpdateRenderProcessHostFramePriorities();
 
   // Set the state whether this navigation is to an MHTML document, since there
   // are certain security checks that we cannot apply to subframes in MHTML
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index fe45416..6e56e4e 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -79,6 +79,7 @@
 #include "third_party/blink/public/common/frame/blocked_navigation_types.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
 #include "third_party/blink/public/common/frame/user_activation_update_type.h"
+#include "third_party/blink/public/common/sudden_termination_disabler_type.h"
 #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom.h"
 #include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
 #include "third_party/blink/public/mojom/commit_result/commit_result.mojom.h"
@@ -104,7 +105,6 @@
 #include "third_party/blink/public/platform/web_focus_type.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/public/platform/web_scroll_types.h"
-#include "third_party/blink/public/platform/web_sudden_termination_disabler_type.h"
 #include "third_party/blink/public/web/web_text_direction.h"
 #include "third_party/blink/public/web/web_tree_scope_type.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -186,8 +186,6 @@
 struct ResourceTimingInfo;
 struct SubresourceLoaderParams;
 
-class MediaStreamDispatcherHost;
-
 // To be called when a RenderFrameHostImpl receives an event.
 // Provides the host, the event fired, and which node id the event was for.
 typedef base::RepeatingCallback<
@@ -293,7 +291,7 @@
   void DisableBeforeUnloadHangMonitorForTesting() override;
   bool IsBeforeUnloadHangMonitorDisabledForTesting() override;
   bool GetSuddenTerminationDisablerState(
-      blink::WebSuddenTerminationDisablerType disabler_type) override;
+      blink::SuddenTerminationDisablerType disabler_type) override;
   bool IsFeatureEnabled(blink::mojom::FeaturePolicyFeature feature) override;
   bool IsFeatureEnabled(blink::mojom::FeaturePolicyFeature feature,
                         blink::PolicyValue threshold_value) override;
@@ -471,6 +469,9 @@
   // cases, use GetLastCommittedURL instead.
   const GURL& last_successful_url() { return last_successful_url_; }
 
+  // Return the http method of the last committed navigation.
+  const std::string& last_http_method() { return last_http_method_; }
+
   // Return the http status code of the last committed navigation.
   int last_http_status_code() { return last_http_status_code_; }
 
@@ -1187,6 +1188,12 @@
 
   const std::string& GetEncoding() const { return canonical_encoding_; }
 
+  // Returns a base salt used to generate frame-specific IDs for media-device
+  // enumerations.
+  const std::string& GetMediaDeviceIDSaltBase() const {
+    return media_device_id_salt_base_;
+  }
+
   // Return true if this contains at least one NavigationRequest waiting to
   // commit in this RenderFrameHost.
   bool HasPendingCommitNavigationForTesting();
@@ -1332,16 +1339,13 @@
   FRIEND_TEST_ALL_PREFIXES(
       SitePerProcessBrowserTest,
       IsDetachedSubframeObservableDuringUnloadHandlerCrossProcess);
-  FRIEND_TEST_ALL_PREFIXES(SitePerProcessSSLBrowserTest,
+  FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                            UnloadHandlersArePowerful);
-  FRIEND_TEST_ALL_PREFIXES(SitePerProcessSSLBrowserTest,
+  FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                            UnloadHandlersArePowerfulGrandChild);
 
   class DroppedInterfaceRequestLogger;
 
-  // Update the RenderProcessHost priority when a navigation occurs.
-  void UpdateRenderProcessHostFramePriorities();
-
   // IPC Message handlers.
   void OnDetach();
   void OnOpenURL(const FrameHostMsg_OpenURL_Params& params);
@@ -1398,7 +1402,7 @@
                                        const AXContentTreeUpdate& snapshot);
   void OnSuddenTerminationDisablerChanged(
       bool present,
-      blink::WebSuddenTerminationDisablerType disabler_type);
+      blink::SuddenTerminationDisablerType disabler_type);
   void OnDidFinishDocumentLoad();
   void OnDidStopLoading();
   void OnDidChangeLoadProgress(double load_progress);
@@ -1979,11 +1983,6 @@
   // Track this frame's last committed URL.
   GURL last_committed_url_;
 
-  // Track the frame priority of the last committed document, which is nullopt
-  // prior to the first commit.
-  base::Optional<RenderProcessHostImpl::FramePriority>
-      last_committed_document_priority_;
-
   // Track this frame's last committed origin.
   url::Origin last_committed_origin_;
 
@@ -1999,6 +1998,9 @@
   // See https://crbug.com/588314.
   GURL last_successful_url_;
 
+  // The http method of the last committed navigation.
+  std::string last_http_method_;
+
   // The http status code of the last committed navigation.
   int last_http_status_code_ = 0;
 
@@ -2238,7 +2240,7 @@
       non_network_url_loader_factories_;
 
   // Bitfield for renderer-side state that blocks fast shutdown of the frame.
-  blink::WebSuddenTerminationDisablerType
+  blink::SuddenTerminationDisablerType
       sudden_termination_disabler_types_enabled_ = 0;
 
   // We switch between |audio_service_audio_output_stream_factory_| and
@@ -2255,9 +2257,6 @@
       audio_service_audio_input_stream_factory_;
   UniqueAudioInputStreamFactoryPtr in_content_audio_input_stream_factory_;
 
-  std::unique_ptr<MediaStreamDispatcherHost, BrowserThread::DeleteOnIOThread>
-      media_stream_dispatcher_host_;
-
   // Hosts media::mojom::InterfaceFactory for the RenderFrame and forwards
   // media::mojom::InterfaceFactory calls to the remote "media" service.
   std::unique_ptr<MediaInterfaceProxy> media_interface_proxy_;
@@ -2510,6 +2509,9 @@
   // Tainted once MediaStream access was granted.
   bool was_granted_media_access_ = false;
 
+  // Salt for generating frame-specific media device IDs.
+  std::string media_device_id_salt_base_;
+
   // NOTE: This must be the last member.
   base::WeakPtrFactory<RenderFrameHostImpl> weak_ptr_factory_{this};
 
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 00c2766..7c71c3d 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -657,6 +657,10 @@
 void RenderFrameHostManager::DidCreateNavigationRequest(
     NavigationRequest* request) {
   if (request->IsServedFromBackForwardCache()) {
+    // Cleanup existing pending RenderFrameHost. This corresponds to what is
+    // done inside GetFrameHostForNavigation(request), but this isn't called
+    // with the back-forward cache.
+    CleanUpNavigation();
     // Since the frame from the back-forward cache is being committed to the
     // SiteInstance we already have, it is treated as current.
     request->set_associated_site_instance_type(
@@ -2238,7 +2242,7 @@
     // Subframe navigations will use the current renderer, unless specifically
     // allowed to swap processes.
     no_renderer_swap_allowed |= !CanSubframeSwapProcess(
-        request->common_params().url, request->source_site_instance(),
+        request->common_params().url, request->GetSourceSiteInstance(),
         request->dest_site_instance());
   }
 
@@ -2255,7 +2259,7 @@
           : nullptr;
 
   scoped_refptr<SiteInstance> dest_site_instance = GetSiteInstanceForNavigation(
-      request->common_params().url, request->source_site_instance(),
+      request->common_params().url, request->GetSourceSiteInstance(),
       request->dest_site_instance(), candidate_site_instance,
       request->common_params().transition,
       request->state() == NavigationRequest::FAILED,
diff --git a/content/browser/gpu/peak_gpu_memory_tracker_impl.cc b/content/browser/gpu/peak_gpu_memory_tracker_impl.cc
new file mode 100644
index 0000000..8c8e83f
--- /dev/null
+++ b/content/browser/gpu/peak_gpu_memory_tracker_impl.cc
@@ -0,0 +1,83 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gpu/peak_gpu_memory_tracker_impl.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/task/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/browser/gpu/gpu_process_host.h"
+#include "content/public/browser/gpu_data_manager.h"
+#include "services/viz/privileged/mojom/gl/gpu_service.mojom.h"
+
+namespace content {
+
+// static
+std::unique_ptr<PeakGpuMemoryTracker> PeakGpuMemoryTracker::Create(
+    PeakMemoryCallback callback) {
+  return std::make_unique<PeakGpuMemoryTrackerImpl>(std::move(callback));
+}
+
+// static
+uint32_t PeakGpuMemoryTrackerImpl::next_sequence_number_ = 0;
+
+PeakGpuMemoryTrackerImpl::PeakGpuMemoryTrackerImpl(PeakMemoryCallback callback)
+    : callback_(std::move(callback)),
+      callback_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+  // Actually performs request to GPU service to begin memory tracking for
+  // |sequence_number_|. This will normally be created from the UI thread, so
+  // repost to the IO thread.
+  GpuProcessHost::CallOnIO(
+      GPU_PROCESS_KIND_SANDBOXED, /* force_create=*/false,
+      base::BindOnce(
+          [](uint32_t sequence_num, GpuProcessHost* host) {
+            // There may be no host nor service available. This may occur during
+            // shutdown, when the service is fully disabled, and in some tests.
+            // In those cases do nothing.
+            if (!host)
+              return;
+            if (auto* gpu_service = host->gpu_service()) {
+              gpu_service->StartPeakMemoryMonitor(sequence_num);
+            }
+          },
+          sequence_num_));
+}
+
+PeakGpuMemoryTrackerImpl::~PeakGpuMemoryTrackerImpl() {
+  // The reply arrives on the IO Thread, repost to the callback's thread.
+  auto wrap_callback = base::BindOnce(
+      [](base::SingleThreadTaskRunner* task_runner, PeakMemoryCallback callback,
+         const uint64_t peak_memory) {
+        task_runner->PostTask(FROM_HERE,
+                              base::BindOnce(std::move(callback), peak_memory));
+      },
+      base::RetainedRef(std::move(callback_task_runner_)),
+      std::move(callback_));
+
+  GpuProcessHost::CallOnIO(
+      GPU_PROCESS_KIND_SANDBOXED, /* force_create=*/false,
+      base::BindOnce(
+          [](uint32_t sequence_num, PeakMemoryCallback callback,
+             GpuProcessHost* host) {
+            // There may be no host nor service available. This may occur during
+            // shutdown, when the service is fully disabled, and in some tests.
+            // In those cases run the callback, reporting 0 memory usage. This
+            // will signify a failure state, and allow for the callback to
+            // perform any needed cleanup.
+            if (!host) {
+              std::move(callback).Run(0u);
+              return;
+            }
+            if (auto* gpu_service = host->gpu_service()) {
+              gpu_service->GetPeakMemoryUsage(sequence_num,
+                                              std::move(callback));
+            }
+          },
+          sequence_num_, std::move(wrap_callback)));
+}
+
+}  // namespace content
diff --git a/content/browser/gpu/peak_gpu_memory_tracker_impl.h b/content/browser/gpu/peak_gpu_memory_tracker_impl.h
new file mode 100644
index 0000000..07539d8
--- /dev/null
+++ b/content/browser/gpu/peak_gpu_memory_tracker_impl.h
@@ -0,0 +1,46 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_GPU_PEAK_GPU_MEMORY_TRACKER_IMPL_H_
+#define CONTENT_BROWSER_GPU_PEAK_GPU_MEMORY_TRACKER_IMPL_H_
+
+#include "base/single_thread_task_runner.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/peak_gpu_memory_tracker.h"
+
+namespace content {
+
+// Tracks the peak memory of the GPU service for its lifetime. Upon its
+// destruction a report will be requested from the GPU service. The peak will be
+// reported to the provided PeakMemoryCallback. This will occur on the thread
+// that this was created on.
+//
+// If the GPU is lost during this objects lifetime, upon destruction the
+// PeakMemoryCallback will be ran with "0" as the reported peak usage. The same
+// for if there is never a successful GPU connection.
+//
+// This is instaniated via PeakGpuMemoryTracker::Create.
+class CONTENT_EXPORT PeakGpuMemoryTrackerImpl : public PeakGpuMemoryTracker {
+ public:
+  // Requests the GPU service to begin peak memory tracking.
+  PeakGpuMemoryTrackerImpl(PeakMemoryCallback callback);
+  // Requests the GPU service provides the peak memory, the result is passed to
+  // |callback_|.
+  ~PeakGpuMemoryTrackerImpl() override;
+
+  PeakGpuMemoryTrackerImpl(const PeakGpuMemoryTrackerImpl*) = delete;
+  PeakGpuMemoryTrackerImpl& operator=(const PeakGpuMemoryTrackerImpl&) = delete;
+
+ private:
+  // Provides the unique identifier for each PeakGpuMemoryTrackerImpl.
+  static uint32_t next_sequence_number_;
+
+  PeakMemoryCallback callback_;
+  scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner_;
+  uint32_t sequence_num_ = next_sequence_number_++;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_GPU_PEAK_GPU_MEMORY_TRACKER_IMPL_H_
diff --git a/content/browser/gpu/peak_gpu_memory_tracker_impl_browsertest.cc b/content/browser/gpu/peak_gpu_memory_tracker_impl_browsertest.cc
new file mode 100644
index 0000000..3e3448e002
--- /dev/null
+++ b/content/browser/gpu/peak_gpu_memory_tracker_impl_browsertest.cc
@@ -0,0 +1,221 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gpu/peak_gpu_memory_tracker_impl.h"
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/location.h"
+#include "base/run_loop.h"
+#include "base/task/post_task.h"
+#include "build/build_config.h"
+#include "components/viz/test/gpu_host_impl_test_api.h"
+#include "content/browser/gpu/gpu_process_host.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/content_browser_test.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/viz/privileged/mojom/gl/gpu_service.mojom.h"
+
+namespace content {
+
+namespace {
+
+const uint64_t kPeakMemory = 42u;
+
+// Test implementation of viz::mojom::GpuService which only implements the peak
+// memory monitoring aspects.
+class TestGpuService : public viz::mojom::GpuService {
+ public:
+  TestGpuService() = default;
+  TestGpuService(const TestGpuService*) = delete;
+  ~TestGpuService() override = default;
+  TestGpuService& operator=(const TestGpuService&) = delete;
+
+  // mojom::GpuService:
+  void StartPeakMemoryMonitor(uint32_t sequence_num) override {
+    peak_memory_monitor_started_ = true;
+  }
+
+  void GetPeakMemoryUsage(uint32_t sequence_num,
+                          GetPeakMemoryUsageCallback callback) override {
+    std::move(callback).Run(kPeakMemory);
+  }
+
+  bool peak_memory_monitor_started() const {
+    return peak_memory_monitor_started_;
+  }
+
+ private:
+  // mojom::GpuService:
+  void EstablishGpuChannel(int32_t client_id,
+                           uint64_t client_tracing_id,
+                           bool is_gpu_host,
+                           bool cache_shaders_on_disk,
+                           EstablishGpuChannelCallback callback) override {}
+  void CloseChannel(int32_t client_id) override {}
+#if defined(OS_CHROMEOS)
+  void CreateArcVideoDecodeAccelerator(
+      mojo::PendingReceiver<arc::mojom::VideoDecodeAccelerator> vda_receiver)
+      override {}
+  void CreateArcVideoEncodeAccelerator(
+      mojo::PendingReceiver<arc::mojom::VideoEncodeAccelerator> vea_receiver)
+      override {}
+  void CreateArcVideoProtectedBufferAllocator(
+      mojo::PendingReceiver<arc::mojom::VideoProtectedBufferAllocator>
+          pba_receiver) override {}
+  void CreateArcProtectedBufferManager(
+      mojo::PendingReceiver<arc::mojom::ProtectedBufferManager> pbm_receiver)
+      override {}
+  void CreateJpegDecodeAccelerator(
+      mojo::PendingReceiver<chromeos_camera::mojom::MjpegDecodeAccelerator>
+          jda_receiver) override {}
+  void CreateJpegEncodeAccelerator(
+      mojo::PendingReceiver<chromeos_camera::mojom::JpegEncodeAccelerator>
+          jea_receiver) override {}
+#endif  // defined(OS_CHROMEOS)
+  void CreateVideoEncodeAcceleratorProvider(
+      mojo::PendingReceiver<media::mojom::VideoEncodeAcceleratorProvider>
+          receiver) override {}
+  void CreateGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+                             const gfx::Size& size,
+                             gfx::BufferFormat format,
+                             gfx::BufferUsage usage,
+                             int client_id,
+                             gpu::SurfaceHandle surface_handle,
+                             CreateGpuMemoryBufferCallback callback) override {}
+  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+                              int client_id,
+                              const gpu::SyncToken& sync_token) override {}
+  void GetVideoMemoryUsageStats(
+      GetVideoMemoryUsageStatsCallback callback) override {}
+#if defined(OS_WIN)
+  void RequestCompleteGpuInfo(
+      RequestCompleteGpuInfoCallback callback) override {}
+  void GetGpuSupportedRuntimeVersion(
+      GetGpuSupportedRuntimeVersionCallback callback) override {}
+#endif
+  void RequestHDRStatus(RequestHDRStatusCallback callback) override {}
+  void LoadedShader(int32_t client_id,
+                    const std::string& key,
+                    const std::string& data) override {}
+  void WakeUpGpu() override {}
+  void GpuSwitched() override {}
+  void DestroyAllChannels() override {}
+  void OnBackgroundCleanup() override {}
+  void OnBackgrounded() override {}
+  void OnForegrounded() override {}
+#if defined(OS_MACOSX)
+  void BeginCATransaction() override {}
+  void CommitCATransaction(CommitCATransactionCallback callback) override {}
+#endif
+  void Crash() override {}
+  void Hang() override {}
+  void ThrowJavaException() override {}
+  void Stop(StopCallback callback) override {}
+
+  bool peak_memory_monitor_started_ = false;
+};
+
+// Runs |task| on the Browser's IO thread, and blocks the Main thread until that
+// task has ran.
+void PostTaskToIOThreadAndWait(base::OnceClosure task) {
+  base::RunLoop run_loop;
+  base::PostTaskAndReply(FROM_HERE, {content::BrowserThread::IO},
+                         std::move(task), run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+}  // namespace
+
+class PeakGpuMemoryTrackerImplTest : public ContentBrowserTest {
+ public:
+  PeakGpuMemoryTrackerImplTest() = default;
+  ~PeakGpuMemoryTrackerImplTest() override = default;
+
+  // Waits until all messages to the mojo::Remote<viz::mojom::GpuService> have
+  // been processed.
+  void FlushRemoteForTesting() {
+    PostTaskToIOThreadAndWait(
+        base::BindOnce(&viz::GpuHostImplTestApi::FlushRemoteForTesting,
+                       base::Unretained(gpu_host_impl_test_api_.get())));
+  }
+
+  // Initializes the TestGpuService, and installs it as the active service.
+  void InitOnIOThread() {
+    gpu_host_impl_test_api_ = std::make_unique<viz::GpuHostImplTestApi>(
+        GpuProcessHost::Get()->gpu_host());
+    test_gpu_service_ = std::make_unique<TestGpuService>();
+    mojo::Remote<viz::mojom::GpuService> gpu_service_remote;
+    gpu_service_receiver_ =
+        std::make_unique<mojo::Receiver<viz::mojom::GpuService>>(
+            test_gpu_service_.get(),
+            gpu_service_remote.BindNewPipeAndPassReceiver());
+    gpu_host_impl_test_api_->SetGpuService(std::move(gpu_service_remote));
+  }
+
+  // Callback to provide to a PeakGpuMemoryTracker. Tests must provide
+  // |runloop_closure| and run the base::RunLoop. This will then quit the loop
+  // once the response from mojo has been received and reposted to the main
+  // thread.
+  void PeakMemoryCallback(base::OnceClosure runloop_closure,
+                          uint64_t peak_memory) {
+    peak_memory_ = peak_memory;
+    std::move(runloop_closure).Run();
+  }
+
+  uint64_t peak_memory() const { return peak_memory_; }
+
+  // Provides access to the TestGpuService on the Main Thread for test
+  // verifications. All mojo calls should be performed on the IO Thread.
+  TestGpuService* gpu_service() const { return test_gpu_service_.get(); }
+
+  // Setup requires that we have the Browser threads still initialized.
+  // ContentBrowserTest:
+  void PreRunTestOnMainThread() override {
+    ContentBrowserTest::PreRunTestOnMainThread();
+    PostTaskToIOThreadAndWait(base::BindOnce(
+        &PeakGpuMemoryTrackerImplTest::InitOnIOThread, base::Unretained(this)));
+  }
+  void PostRunTestOnMainThread() override {
+    PostTaskToIOThreadAndWait(base::BindOnce(
+        [](std::unique_ptr<mojo::Receiver<viz::mojom::GpuService>>
+               gpu_service_receiver) {},
+        std::move(gpu_service_receiver_)));
+    ContentBrowserTest::PostRunTestOnMainThread();
+  }
+
+ private:
+  uint64_t peak_memory_ = 0;
+  std::unique_ptr<TestGpuService> test_gpu_service_ = nullptr;
+  std::unique_ptr<viz::GpuHostImplTestApi> gpu_host_impl_test_api_ = nullptr;
+  std::unique_ptr<mojo::Receiver<viz::mojom::GpuService>>
+      gpu_service_receiver_ = nullptr;
+};
+
+// Verifies that when a PeakGpuMemoryTracker is destroyed, that the client's
+// callback is appropriately called.
+IN_PROC_BROWSER_TEST_F(PeakGpuMemoryTrackerImplTest, PeakGpuMemoryCallback) {
+  base::RunLoop run_loop;
+  std::unique_ptr<PeakGpuMemoryTracker> tracker = PeakGpuMemoryTracker::Create(
+      base::BindOnce(&PeakGpuMemoryTrackerImplTest::PeakMemoryCallback,
+                     base::Unretained(this), run_loop.QuitClosure()));
+  FlushRemoteForTesting();
+  // No report in response to creation.
+  EXPECT_EQ(0u, peak_memory());
+  // However the serive should have started monitoring.
+  EXPECT_TRUE(gpu_service()->peak_memory_monitor_started());
+
+  // Deleting the tracker should start a request for peak Gpu memory usage, with
+  // the callback being a posted task.
+  tracker.reset();
+  FlushRemoteForTesting();
+  // Wait for PeakMemoryCallback to be ran on this thread.
+  run_loop.Run();
+  EXPECT_EQ(kPeakMemory, peak_memory());
+}
+
+}  // namespace content
diff --git a/content/browser/loader/loader_browsertest.cc b/content/browser/loader/loader_browsertest.cc
index f5fa54d..d04f38f9 100644
--- a/content/browser/loader/loader_browsertest.cc
+++ b/content/browser/loader/loader_browsertest.cc
@@ -19,7 +19,6 @@
 #include "build/build_config.h"
 #include "content/browser/download/download_manager_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
-#include "content/common/view_messages.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -147,15 +146,9 @@
   ASSERT_TRUE(NavigateToURL(
       shell(), embedded_test_server()->GetURL("/stale-while-revalidate.html")));
 
-  // Create new renderer preferences and force-disable the |enable_referrers|
-  // preference.
-  blink::mojom::RendererPreferences renderer_preferences;
-  renderer_preferences.enable_referrers = false;
-
-  // Send updated renderer preferences to the renderer.
-  RenderViewHost* rvh = web_contents->GetRenderViewHost();
-  rvh->Send(
-      new ViewMsg_SetRendererPrefs(rvh->GetRoutingID(), renderer_preferences));
+  // Force-disable the |enable_referrers| preference.
+  web_contents->GetMutableRendererPrefs()->enable_referrers = false;
+  web_contents->SyncRendererPrefs();
 
   // Wait for the stale-while-revalidate tests to pass by observing the page's
   // title. If the renderer crashes, the test immediately fails.
diff --git a/content/browser/media/media_devices_util.cc b/content/browser/media/media_devices_util.cc
index e715618..564a090 100644
--- a/content/browser/media/media_devices_util.cc
+++ b/content/browser/media/media_devices_util.cc
@@ -21,6 +21,7 @@
 #include "content/public/browser/media_device_id.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_client.h"
 #include "media/base/media_switches.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 
@@ -130,22 +131,49 @@
 MediaDeviceSaltAndOrigin GetMediaDeviceSaltAndOrigin(int render_process_id,
                                                      int render_frame_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  RenderFrameHost* frame_host =
-      RenderFrameHost::FromID(render_process_id, render_frame_id);
+  RenderFrameHostImpl* frame_host =
+      RenderFrameHostImpl::FromID(render_process_id, render_frame_id);
   RenderProcessHost* process_host =
       RenderProcessHost::FromID(render_process_id);
-  WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
-      WebContents::FromRenderFrameHost(frame_host));
 
-  std::string device_id_salt =
-      process_host ? process_host->GetBrowserContext()->GetMediaDeviceIDSalt()
-                   : std::string();
-  std::string group_id_salt =
-      device_id_salt + (web_contents
-                            ? web_contents->GetMediaDeviceGroupIDSaltBase()
-                            : std::string());
-  url::Origin origin =
-      frame_host ? frame_host->GetLastCommittedOrigin() : url::Origin();
+  url::Origin origin;
+  GURL url;
+  GURL site_for_cookies;
+  url::Origin top_level_origin;
+  std::string frame_salt;
+
+  if (frame_host) {
+    origin = frame_host->GetLastCommittedOrigin();
+    url = frame_host->GetLastCommittedURL();
+    site_for_cookies = frame_host->ComputeSiteForCookies();
+    top_level_origin = frame_host->frame_tree_node()
+                           ->frame_tree()
+                           ->GetMainFrame()
+                           ->GetLastCommittedOrigin();
+    frame_salt = frame_host->GetMediaDeviceIDSaltBase();
+  }
+
+  bool are_persistent_ids_allowed = false;
+  std::string device_id_salt;
+  std::string group_id_salt;
+  if (process_host) {
+    are_persistent_ids_allowed =
+        GetContentClient()->browser()->ArePersistentMediaDeviceIDsAllowed(
+            process_host->GetBrowserContext(), url, site_for_cookies,
+            top_level_origin);
+    device_id_salt = process_host->GetBrowserContext()->GetMediaDeviceIDSalt();
+    group_id_salt = device_id_salt;
+  }
+
+  // If persistent IDs are not allowed, append |frame_salt| to make it
+  // specific to the current document.
+  if (!are_persistent_ids_allowed)
+    device_id_salt += frame_salt;
+
+  // |group_id_salt| must be unique per document, but it must also change if
+  // cookies are cleared. Also, it must be different from |device_id_salt|,
+  // thus appending a constant.
+  group_id_salt += frame_salt + "groupid";
 
   return {std::move(device_id_salt), std::move(group_id_salt),
           std::move(origin)};
diff --git a/content/browser/media/media_devices_util.h b/content/browser/media/media_devices_util.h
index 7665000..1dbf23c6 100644
--- a/content/browser/media/media_devices_util.h
+++ b/content/browser/media/media_devices_util.h
@@ -39,8 +39,8 @@
 // unique media-device IDs for each origin and renderer process. These values
 // should not be cached since the user can explicitly change them at any time.
 // This function must run on the UI thread.
-MediaDeviceSaltAndOrigin GetMediaDeviceSaltAndOrigin(int render_process_id,
-                                                     int render_frame_id);
+CONTENT_EXPORT MediaDeviceSaltAndOrigin
+GetMediaDeviceSaltAndOrigin(int render_process_id, int render_frame_id);
 
 // Returns a translated version of |device_info| suitable for use in a renderer
 // process.
diff --git a/content/browser/media/media_interface_factory_holder.cc b/content/browser/media/media_interface_factory_holder.cc
new file mode 100644
index 0000000..7eb1246
--- /dev/null
+++ b/content/browser/media/media_interface_factory_holder.cc
@@ -0,0 +1,56 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/media/media_interface_factory_holder.h"
+
+#include "base/bind.h"
+#include "content/public/common/service_manager_connection.h"
+#include "media/mojo/mojom/media_service.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace content {
+
+MediaInterfaceFactoryHolder::MediaInterfaceFactoryHolder(
+    const std::string& service_name,
+    CreateInterfaceProviderCB create_interface_provider_cb)
+    : service_name_(service_name),
+      create_interface_provider_cb_(std::move(create_interface_provider_cb)) {}
+
+MediaInterfaceFactoryHolder::~MediaInterfaceFactoryHolder() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+media::mojom::InterfaceFactory* MediaInterfaceFactoryHolder::Get() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (!interface_factory_ptr_)
+    ConnectToMediaService();
+
+  return interface_factory_ptr_.get();
+}
+
+void MediaInterfaceFactoryHolder::ConnectToMediaService() {
+  media::mojom::MediaServicePtr media_service;
+
+  // TODO(slan): Use the BrowserContext Connector instead. See crbug.com/638950.
+  service_manager::Connector* connector =
+      ServiceManagerConnection::GetForProcess()->GetConnector();
+  connector->BindInterface(service_name_, &media_service);
+
+  media_service->CreateInterfaceFactory(MakeRequest(&interface_factory_ptr_),
+                                        create_interface_provider_cb_.Run());
+
+  interface_factory_ptr_.set_connection_error_handler(base::BindOnce(
+      &MediaInterfaceFactoryHolder::OnMediaServiceConnectionError,
+      base::Unretained(this)));
+}
+
+void MediaInterfaceFactoryHolder::OnMediaServiceConnectionError() {
+  DVLOG(1) << __func__;
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  interface_factory_ptr_.reset();
+}
+
+}  // namespace content
diff --git a/content/browser/media/media_interface_factory_holder.h b/content/browser/media/media_interface_factory_holder.h
new file mode 100644
index 0000000..cfb5e71
--- /dev/null
+++ b/content/browser/media/media_interface_factory_holder.h
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_MEDIA_MEDIA_INTERFACE_FACTORY_HOLDER_H_
+#define CONTENT_BROWSER_MEDIA_MEDIA_INTERFACE_FACTORY_HOLDER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "media/mojo/mojom/interface_factory.mojom.h"
+#include "services/service_manager/public/mojom/interface_provider.mojom.h"
+
+namespace content {
+
+// Helper class to get media::mojom::InterfaceFactoryPtr.
+// Get() lazily connects to the media service specified by |service_name_|.
+class MediaInterfaceFactoryHolder {
+ public:
+  using CreateInterfaceProviderCB =
+      base::RepeatingCallback<service_manager::mojom::InterfaceProviderPtr()>;
+
+  MediaInterfaceFactoryHolder(
+      const std::string& service_name,
+      CreateInterfaceProviderCB create_interface_provider_cb);
+  ~MediaInterfaceFactoryHolder();
+
+  // Gets the MediaService |interface_factory_ptr_|. The returned pointer is
+  // still owned by this class.
+  media::mojom::InterfaceFactory* Get();
+
+ private:
+  void ConnectToMediaService();
+
+  // Callback for connection error from |interface_factory_ptr_|.
+  void OnMediaServiceConnectionError();
+
+  const std::string service_name_;
+  CreateInterfaceProviderCB create_interface_provider_cb_;
+  media::mojom::InterfaceFactoryPtr interface_factory_ptr_;
+
+  THREAD_CHECKER(thread_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(MediaInterfaceFactoryHolder);
+};
+
+}  // namespace content
+#endif  // CONTENT_BROWSER_MEDIA_MEDIA_INTERFACE_FACTORY_HOLDER_H_
diff --git a/content/browser/media/media_interface_proxy.cc b/content/browser/media/media_interface_proxy.cc
index 2f4ea7d..2abb7e49 100644
--- a/content/browser/media/media_interface_proxy.cc
+++ b/content/browser/media/media_interface_proxy.cc
@@ -125,10 +125,22 @@
   DCHECK(render_frame_host_);
   DCHECK(!error_handler.is_null());
 
+  auto create_interface_provider_cb =
+      base::BindRepeating(&MediaInterfaceProxy::GetFrameServices,
+                          base::Unretained(this), base::Token(), std::string());
+  media_interface_factory_ptr_ = std::make_unique<MediaInterfaceFactoryHolder>(
+      media::mojom::kMediaServiceName, create_interface_provider_cb);
+
+#if BUILDFLAG(ENABLE_CAST_RENDERER)
+  media_renderer_interface_factory_ptr_ =
+      std::make_unique<MediaInterfaceFactoryHolder>(
+          media::mojom::kMediaRendererServiceName,
+          std::move(create_interface_provider_cb));
+#endif  // BUILDFLAG(ENABLE_CAST_RENDERER)
+
   binding_.set_connection_error_handler(std::move(error_handler));
 
-  // |interface_factory_ptr_| and |cdm_factory_map_| will be lazily
-  // connected in GetMediaInterfaceFactory() and GetCdmFactory().
+  // |cdm_factory_map_| will be lazily connected in GetCdmFactory().
 }
 
 MediaInterfaceProxy::~MediaInterfaceProxy() {
@@ -137,17 +149,17 @@
 }
 
 void MediaInterfaceProxy::CreateAudioDecoder(
-    media::mojom::AudioDecoderRequest request) {
+    mojo::PendingReceiver<media::mojom::AudioDecoder> receiver) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  InterfaceFactory* factory = GetMediaInterfaceFactory();
+  InterfaceFactory* factory = media_interface_factory_ptr_->Get();
   if (factory)
-    factory->CreateAudioDecoder(std::move(request));
+    factory->CreateAudioDecoder(std::move(receiver));
 }
 
 void MediaInterfaceProxy::CreateVideoDecoder(
     media::mojom::VideoDecoderRequest request) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  InterfaceFactory* factory = GetMediaInterfaceFactory();
+  InterfaceFactory* factory = media_interface_factory_ptr_->Get();
   if (factory)
     factory->CreateVideoDecoder(std::move(request));
 }
@@ -157,7 +169,7 @@
     media::mojom::RendererRequest request) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  InterfaceFactory* factory = GetMediaInterfaceFactory();
+  InterfaceFactory* factory = media_interface_factory_ptr_->Get();
   if (factory)
     factory->CreateDefaultRenderer(audio_device_id, std::move(request));
 }
@@ -168,7 +180,8 @@
     media::mojom::RendererRequest request) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  InterfaceFactory* factory = GetMediaInterfaceFactory();
+  // CastRenderer is always hosted in "media_renderer" service.
+  InterfaceFactory* factory = media_renderer_interface_factory_ptr_->Get();
   if (factory)
     factory->CreateCastRenderer(overlay_plane_id, std::move(request));
 }
@@ -217,21 +230,25 @@
     const std::string& key_system,
     media::mojom::ContentDecryptionModuleRequest request) {
   DCHECK(thread_checker_.CalledOnValidThread());
-#if !BUILDFLAG(ENABLE_LIBRARY_CDMS)
-  auto* factory = GetMediaInterfaceFactory();
-  if (factory)
-    factory->CreateCdm(key_system, std::move(request));
-#else
+#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
   auto* factory = GetCdmFactory(key_system);
+#elif BUILDFLAG(ENABLE_CAST_RENDERER)
+  // CDM service lives together with renderer service if cast renderer is
+  // enabled, because cast renderer creates its own audio/video decoder.
+  auto* factory = media_renderer_interface_factory_ptr_->Get();
+#else
+  // CDM service lives together with audio/video decoder service.
+  auto* factory = media_interface_factory_ptr_->Get();
+#endif
+
   if (factory)
     factory->CreateCdm(key_system, std::move(request));
-#endif
 }
 
 void MediaInterfaceProxy::CreateDecryptor(
     int cdm_id,
     mojo::PendingReceiver<media::mojom::Decryptor> receiver) {
-  InterfaceFactory* factory = GetMediaInterfaceFactory();
+  InterfaceFactory* factory = media_interface_factory_ptr_->Get();
   if (factory)
     factory->CreateDecryptor(cdm_id, std::move(receiver));
 }
@@ -284,42 +301,6 @@
   return interfaces;
 }
 
-media::mojom::InterfaceFactory*
-MediaInterfaceProxy::GetMediaInterfaceFactory() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (!interface_factory_ptr_)
-    ConnectToMediaService();
-
-  return interface_factory_ptr_.get();
-}
-
-void MediaInterfaceProxy::ConnectToMediaService() {
-  DVLOG(1) << __func__;
-  DCHECK(!interface_factory_ptr_);
-
-  media::mojom::MediaServicePtr media_service;
-
-  // TODO(slan): Use the BrowserContext Connector instead. See crbug.com/638950.
-  GetSystemConnector()->BindInterface(media::mojom::kMediaServiceName,
-                                      &media_service);
-
-  media_service->CreateInterfaceFactory(
-      MakeRequest(&interface_factory_ptr_),
-      GetFrameServices(base::Token{}, std::string()));
-
-  interface_factory_ptr_.set_connection_error_handler(
-      base::BindOnce(&MediaInterfaceProxy::OnMediaServiceConnectionError,
-                     base::Unretained(this)));
-}
-
-void MediaInterfaceProxy::OnMediaServiceConnectionError() {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  interface_factory_ptr_.reset();
-}
-
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
 media::mojom::CdmFactory* MediaInterfaceProxy::GetCdmFactory(
@@ -409,7 +390,7 @@
   DVLOG(1) << __func__;
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  InterfaceFactory* factory = GetMediaInterfaceFactory();
+  InterfaceFactory* factory = media_interface_factory_ptr_->Get();
   if (factory)
     factory->CreateCdmProxy(cdm_guid, std::move(receiver));
 }
diff --git a/content/browser/media/media_interface_proxy.h b/content/browser/media/media_interface_proxy.h
index a3c1b4f..0b003eebc 100644
--- a/content/browser/media/media_interface_proxy.h
+++ b/content/browser/media/media_interface_proxy.h
@@ -14,6 +14,7 @@
 #include "base/token.h"
 #include "base/unguessable_token.h"
 #include "build/build_config.h"
+#include "content/browser/media/media_interface_factory_holder.h"
 #include "media/media_buildflags.h"
 #include "media/mojo/buildflags.h"
 #include "media/mojo/mojom/content_decryption_module.mojom.h"
@@ -48,7 +49,8 @@
   ~MediaInterfaceProxy() final;
 
   // media::mojom::InterfaceFactory implementation.
-  void CreateAudioDecoder(media::mojom::AudioDecoderRequest request) final;
+  void CreateAudioDecoder(
+      mojo::PendingReceiver<media::mojom::AudioDecoder> receiver) final;
   void CreateVideoDecoder(media::mojom::VideoDecoderRequest request) final;
   void CreateDefaultRenderer(const std::string& audio_device_id,
                              media::mojom::RendererRequest request) final;
@@ -84,15 +86,6 @@
       const base::Token& cdm_guid,
       const std::string& cdm_file_system_id);
 
-  // Gets the MediaService |interface_factory_ptr_|. Returns null if unexpected
-  // error happened.
-  InterfaceFactory* GetMediaInterfaceFactory();
-
-  void ConnectToMediaService();
-
-  // Callback for connection error from |interface_factory_ptr_|.
-  void OnMediaServiceConnectionError();
-
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
   // Gets a CdmFactory pointer for |key_system|. Returns null if unexpected
   // error happened.
@@ -134,7 +127,16 @@
   // in the service named kMediaServiceName hosted in the process specified by
   // the "mojo_media_host" gn argument. Available options are browser, GPU and
   // utility processes.
-  media::mojom::InterfaceFactoryPtr interface_factory_ptr_;
+  std::unique_ptr<MediaInterfaceFactoryHolder> media_interface_factory_ptr_;
+
+#if BUILDFLAG(ENABLE_CAST_RENDERER)
+  // InterfacePtr to the remote InterfaceFactory implementation
+  // in the service named kMediaRendererServiceName hosted. It provides the
+  // remote implementation of media::Renderer and
+  // media::ContentDecryptionModule.
+  std::unique_ptr<MediaInterfaceFactoryHolder>
+      media_renderer_interface_factory_ptr_;
+#endif  // BUILDFLAG(ENABLE_CAST_RENDERER)
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
   // CDM GUID to CDM InterfaceFactoryPtr mapping, where the InterfaceFactory
diff --git a/content/browser/media/video_decoder_proxy.cc b/content/browser/media/video_decoder_proxy.cc
index 47e01705..8217faf6 100644
--- a/content/browser/media/video_decoder_proxy.cc
+++ b/content/browser/media/video_decoder_proxy.cc
@@ -31,7 +31,7 @@
 }
 
 void VideoDecoderProxy::CreateAudioDecoder(
-    media::mojom::AudioDecoderRequest request) {}
+    mojo::PendingReceiver<media::mojom::AudioDecoder> receiver) {}
 
 void VideoDecoderProxy::CreateVideoDecoder(
     media::mojom::VideoDecoderRequest request) {
diff --git a/content/browser/media/video_decoder_proxy.h b/content/browser/media/video_decoder_proxy.h
index 25d98636..bb939801 100644
--- a/content/browser/media/video_decoder_proxy.h
+++ b/content/browser/media/video_decoder_proxy.h
@@ -30,7 +30,8 @@
   void Add(media::mojom::InterfaceFactoryRequest request);
 
   // media::mojom::InterfaceFactory implementation.
-  void CreateAudioDecoder(media::mojom::AudioDecoderRequest request) final;
+  void CreateAudioDecoder(
+      mojo::PendingReceiver<media::mojom::AudioDecoder> receiver) final;
   void CreateVideoDecoder(media::mojom::VideoDecoderRequest request) final;
   void CreateDefaultRenderer(const std::string& audio_device_id,
                              media::mojom::RendererRequest request) final;
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 6d64bac..f7b92ff 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -16,7 +16,6 @@
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
-#include "components/network_session_configurator/common/network_switches.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -2341,6 +2340,16 @@
 // Regression test for https://crbug.com/998284.
 IN_PROC_BROWSER_TEST_P(NavigationBaseBrowserTest,
                        BackForwardInOldDocumentCancelPendingNavigation) {
+  // This test expects a new request to be made when navigating back, which is
+  // not happening with back-forward cache enabled.
+  // See BackForwardCacheBrowserTest.RestoreWhilePendingCommit which covers the
+  // same scenario for back-forward cache.
+  shell()
+      ->web_contents()
+      ->GetController()
+      .GetBackForwardCache()
+      .DisableForTesting(BackForwardCacheImpl::TEST_ASSUMES_NO_CACHING);
+
   using Response = net::test_server::ControllableHttpResponse;
   Response response_A1(embedded_test_server(), "/A");
   Response response_A2(embedded_test_server(), "/A");
@@ -2568,47 +2577,19 @@
   console_delegate_2->Wait();
 }
 
-// Tests for cookies. Provides an HTTPS server.
-class NavigationCookiesBrowserTest : public NavigationBaseBrowserTest {
- protected:
-  NavigationCookiesBrowserTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    NavigationBaseBrowserTest::SetUpCommandLine(command_line);
-
-    // This is necessary to use https with arbitrary hostnames.
-    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
-  }
-
-  void SetUpOnMainThread() override {
-    https_server()->AddDefaultHandlers(GetTestDataFilePath());
-    NavigationBaseBrowserTest::SetUpOnMainThread();
-  }
-
-  net::EmbeddedTestServer* https_server() { return &https_server_; }
-
- private:
-  net::EmbeddedTestServer https_server_;
-};
-
-INSTANTIATE_TEST_SUITE_P(/* no prefix */,
-                         NavigationCookiesBrowserTest,
-                         ::testing::Bool());
-
 // Test how cookies are inherited in about:srcdoc iframes.
 //
 // Regression test: https://crbug.com/1003167.
-IN_PROC_BROWSER_TEST_P(NavigationCookiesBrowserTest, CookiesInheritedSrcDoc) {
+IN_PROC_BROWSER_TEST_P(NavigationBaseBrowserTest, CookiesInheritedSrcDoc) {
   using Response = net::test_server::ControllableHttpResponse;
-  Response response_1(https_server(), "/response_1");
-  Response response_2(https_server(), "/response_2");
-  Response response_3(https_server(), "/response_3");
+  Response response_1(embedded_test_server(), "/response_1");
+  Response response_2(embedded_test_server(), "/response_2");
+  Response response_3(embedded_test_server(), "/response_3");
 
-  ASSERT_TRUE(https_server()->Start());
+  ASSERT_TRUE(embedded_test_server()->Start());
 
-  GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
-  GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url_a));
 
   EXPECT_TRUE(ExecJs(shell(), R"(
@@ -2665,8 +2646,7 @@
   EXPECT_EQ("", EvalJs(sub_document_2, "document.cookie"));
 
   // 6. Set a cookie in the child. It doesn't affect its parent.
-  EXPECT_TRUE(ExecJs(sub_document_2,
-                     "document.cookie = 'd=0; SameSite=none; Secure';"));
+  EXPECT_TRUE(ExecJs(sub_document_2, "document.cookie = 'd=0';"));
 
   EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
   EXPECT_EQ("d=0", EvalJs(sub_document_2, "document.cookie"));
@@ -2713,21 +2693,20 @@
 }
 
 // Test how cookies are inherited in about:blank iframes.
-IN_PROC_BROWSER_TEST_P(NavigationCookiesBrowserTest,
-                       CookiesInheritedAboutBlank) {
+IN_PROC_BROWSER_TEST_P(NavigationBaseBrowserTest, CookiesInheritedAboutBlank) {
   // This test expects several cross-site navigation to happen.
   if (!AreAllSitesIsolatedForTesting())
     return;
 
   using Response = net::test_server::ControllableHttpResponse;
-  Response response_1(https_server(), "/response_1");
-  Response response_2(https_server(), "/response_2");
-  Response response_3(https_server(), "/response_3");
+  Response response_1(embedded_test_server(), "/response_1");
+  Response response_2(embedded_test_server(), "/response_2");
+  Response response_3(embedded_test_server(), "/response_3");
 
-  ASSERT_TRUE(https_server()->Start());
+  ASSERT_TRUE(embedded_test_server()->Start());
 
-  GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
-  GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url_a));
 
   EXPECT_TRUE(
@@ -2769,7 +2748,7 @@
   EXPECT_EQ("a=0; b=0", EvalJs(sub_document_1, "document.cookie"));
 
   // 3. Checks cookies are sent while requesting resources.
-  GURL url_response_1 = https_server()->GetURL("a.com", "/response_1");
+  GURL url_response_1 = embedded_test_server()->GetURL("a.com", "/response_1");
   EXPECT_TRUE(ExecJs(sub_document_1, JsReplace("fetch($1)", url_response_1)));
   response_1.WaitForRequest();
   EXPECT_EQ("a=0; b=0", response_1.http_request()->headers.at("Cookie"));
@@ -2790,8 +2769,7 @@
   EXPECT_EQ("", EvalJs(sub_document_2, "document.cookie"));
 
   // 6. Set a cookie in the child. It doesn't affect its parent.
-  EXPECT_TRUE(ExecJs(sub_document_2,
-                     "document.cookie = 'd=0; SameSite=none; Secure';"));
+  EXPECT_TRUE(ExecJs(sub_document_2, "document.cookie = 'd=0';"));
 
   EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
   EXPECT_EQ("d=0", EvalJs(sub_document_2, "document.cookie"));
@@ -2838,25 +2816,23 @@
 
 // Test how cookies are inherited in about:blank iframes.
 //
-// This is a variation of
-// NavigationCookiesBrowserTest.CookiesInheritedAboutBlank. Instead of
-// requesting an history navigation, a new navigation is requested from the main
-// frame. The navigation is cross-site instead of being same-site.
-IN_PROC_BROWSER_TEST_P(NavigationCookiesBrowserTest,
-                       CookiesInheritedAboutBlank2) {
+// This is a variation of NavigationBaseBrowserTest.CookiesInheritedAboutBlank.
+// Instead of requesting an history navigation, a new navigation is requested
+// from the main frame. The navigation is cross-site instead of being same-site.
+IN_PROC_BROWSER_TEST_P(NavigationBaseBrowserTest, CookiesInheritedAboutBlank2) {
   // This test expects several cross-site navigation to happen.
   if (!AreAllSitesIsolatedForTesting())
     return;
 
   using Response = net::test_server::ControllableHttpResponse;
-  Response response_1(https_server(), "/response_1");
-  Response response_2(https_server(), "/response_2");
-  Response response_3(https_server(), "/response_3");
+  Response response_1(embedded_test_server(), "/response_1");
+  Response response_2(embedded_test_server(), "/response_2");
+  Response response_3(embedded_test_server(), "/response_3");
 
-  ASSERT_TRUE(https_server()->Start());
+  ASSERT_TRUE(embedded_test_server()->Start());
 
-  GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
-  GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url_a));
 
   EXPECT_TRUE(
@@ -2917,8 +2893,7 @@
   EXPECT_EQ("", EvalJs(sub_document_2, "document.cookie"));
 
   // 6. Set a cookie in the child. It doesn't affect its parent.
-  EXPECT_TRUE(ExecJs(sub_document_2,
-                     "document.cookie = 'd=0; SameSite=none; Secure';"));
+  EXPECT_TRUE(ExecJs(sub_document_2, "document.cookie = 'd=0';"));
 
   EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
   EXPECT_EQ("d=0", EvalJs(sub_document_2, "document.cookie"));
@@ -2966,16 +2941,16 @@
 }
 
 // Test how cookies are inherited in data-URL iframes.
-IN_PROC_BROWSER_TEST_P(NavigationCookiesBrowserTest, CookiesInheritedDataUrl) {
+IN_PROC_BROWSER_TEST_P(NavigationBaseBrowserTest, CookiesInheritedDataUrl) {
   using Response = net::test_server::ControllableHttpResponse;
-  Response response_1(https_server(), "/response_1");
-  Response response_2(https_server(), "/response_2");
-  Response response_3(https_server(), "/response_3");
+  Response response_1(embedded_test_server(), "/response_1");
+  Response response_2(embedded_test_server(), "/response_2");
+  Response response_3(embedded_test_server(), "/response_3");
 
-  ASSERT_TRUE(https_server()->Start());
+  ASSERT_TRUE(embedded_test_server()->Start());
 
-  GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
-  GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url_a));
 
   EXPECT_TRUE(ExecJs(shell(), R"(
@@ -3016,7 +2991,7 @@
   // the data-URL.
   EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'a=0;SameSite=Lax'"));
   EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'b=0;SameSite=Strict'"));
-  GURL url_response_1 = https_server()->GetURL("a.com", "/response_1");
+  GURL url_response_1 = embedded_test_server()->GetURL("a.com", "/response_1");
   EXPECT_TRUE(ExecJs(sub_document_1, JsReplace("fetch($1)", url_response_1)));
   response_1.WaitForRequest();
   EXPECT_EQ(0u, response_1.http_request()->headers.count("Cookie"));
@@ -3053,7 +3028,7 @@
   console_delegate_4->Wait();
 
   // 7. No cookies are sent when requested from the data-URL.
-  GURL url_response_2 = https_server()->GetURL("a.com", "/response_2");
+  GURL url_response_2 = embedded_test_server()->GetURL("a.com", "/response_2");
   EXPECT_TRUE(ExecJs(sub_document_2, JsReplace("fetch($1)", url_response_2)));
   response_2.WaitForRequest();
   EXPECT_EQ(0u, response_2.http_request()->headers.count("Cookie"));
diff --git a/content/browser/network_service_instance_impl.cc b/content/browser/network_service_instance_impl.cc
index dea73ed4..f4440ea5 100644
--- a/content/browser/network_service_instance_impl.cc
+++ b/content/browser/network_service_instance_impl.cc
@@ -42,6 +42,7 @@
 #include "services/network/public/cpp/network_switches.h"
 #include "services/network/public/mojom/net_log.mojom.h"
 #include "services/network/public/mojom/network_change_manager.mojom.h"
+#include "services/network/public/mojom/network_service.mojom.h"
 #include "services/network/public/mojom/network_service_test.mojom.h"
 
 namespace content {
@@ -162,8 +163,12 @@
     return;
   }
 
-  GetLocalNetworkService() =
-      std::make_unique<network::NetworkService>(nullptr, std::move(receiver));
+  GetLocalNetworkService() = std::make_unique<network::NetworkService>(
+      nullptr /* registry */, std::move(receiver),
+      true /* delay_initialization_until_set_client */);
+  GetLocalNetworkService()->Initialize(
+      network::mojom::NetworkServiceParams::New(),
+      true /* mock_network_change_notifier */);
   if (completion_event)
     completion_event->Signal();
 }
diff --git a/content/browser/renderer_host/compositor_dependencies_android.cc b/content/browser/renderer_host/compositor_dependencies_android.cc
index f741e0c..a9bb7e4 100644
--- a/content/browser/renderer_host/compositor_dependencies_android.cc
+++ b/content/browser/renderer_host/compositor_dependencies_android.cc
@@ -4,6 +4,8 @@
 
 #include "content/browser/renderer_host/compositor_dependencies_android.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
@@ -20,6 +22,7 @@
 #include "content/browser/gpu/gpu_process_host.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace content {
 
@@ -95,16 +98,19 @@
 CompositorDependenciesAndroid::~CompositorDependenciesAndroid() = default;
 
 void CompositorDependenciesAndroid::CreateVizFrameSinkManager() {
-  viz::mojom::FrameSinkManagerPtr frame_sink_manager;
-  viz::mojom::FrameSinkManagerRequest frame_sink_manager_request =
-      mojo::MakeRequest(&frame_sink_manager);
-  viz::mojom::FrameSinkManagerClientPtr frame_sink_manager_client;
-  viz::mojom::FrameSinkManagerClientRequest frame_sink_manager_client_request =
-      mojo::MakeRequest(&frame_sink_manager_client);
+  mojo::PendingRemote<viz::mojom::FrameSinkManager> frame_sink_manager;
+  mojo::PendingReceiver<viz::mojom::FrameSinkManager>
+      frame_sink_manager_receiver =
+          frame_sink_manager.InitWithNewPipeAndPassReceiver();
+  mojo::PendingRemote<viz::mojom::FrameSinkManagerClient>
+      frame_sink_manager_client;
+  mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient>
+      frame_sink_manager_client_receiver =
+          frame_sink_manager_client.InitWithNewPipeAndPassReceiver();
 
   // Setup HostFrameSinkManager with interface endpoints.
   host_frame_sink_manager_.BindAndSetManager(
-      std::move(frame_sink_manager_client_request),
+      std::move(frame_sink_manager_client_receiver),
       base::ThreadTaskRunnerHandle::Get(), std::move(frame_sink_manager));
 
   // Set up a callback to automatically re-connect if we lose our
@@ -117,8 +123,8 @@
   // connected to the GPU process.
   pending_connect_viz_on_io_thread_ = base::BindOnce(
       &CompositorDependenciesAndroid::ConnectVizFrameSinkManagerOnIOThread,
-      std::move(frame_sink_manager_request),
-      frame_sink_manager_client.PassInterface());
+      std::move(frame_sink_manager_receiver),
+      std::move(frame_sink_manager_client));
 }
 
 cc::TaskGraphRunner* CompositorDependenciesAndroid::GetTaskGraphRunner() {
@@ -144,12 +150,12 @@
 // re-run when the request is deleted (goes out of scope).
 // static
 void CompositorDependenciesAndroid::ConnectVizFrameSinkManagerOnIOThread(
-    viz::mojom::FrameSinkManagerRequest request,
-    viz::mojom::FrameSinkManagerClientPtrInfo client) {
+    mojo::PendingReceiver<viz::mojom::FrameSinkManager> receiver,
+    mojo::PendingRemote<viz::mojom::FrameSinkManagerClient> client) {
   auto* gpu_process_host = GpuProcessHost::Get();
   if (!gpu_process_host)
     return;
-  gpu_process_host->gpu_host()->ConnectFrameSinkManager(std::move(request),
+  gpu_process_host->gpu_host()->ConnectFrameSinkManager(std::move(receiver),
                                                         std::move(client));
 }
 
diff --git a/content/browser/renderer_host/compositor_dependencies_android.h b/content/browser/renderer_host/compositor_dependencies_android.h
index b647c706..d31f4a7 100644
--- a/content/browser/renderer_host/compositor_dependencies_android.h
+++ b/content/browser/renderer_host/compositor_dependencies_android.h
@@ -13,6 +13,8 @@
 #include "base/no_destructor.h"
 #include "components/viz/common/surfaces/frame_sink_id_allocator.h"
 #include "components/viz/host/host_frame_sink_manager.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h"
 
 namespace cc {
@@ -50,8 +52,8 @@
   friend class base::NoDestructor<CompositorDependenciesAndroid>;
 
   static void ConnectVizFrameSinkManagerOnIOThread(
-      viz::mojom::FrameSinkManagerRequest request,
-      viz::mojom::FrameSinkManagerClientPtrInfo client);
+      mojo::PendingReceiver<viz::mojom::FrameSinkManager> receiver,
+      mojo::PendingRemote<viz::mojom::FrameSinkManagerClient> client);
 
   CompositorDependenciesAndroid();
   ~CompositorDependenciesAndroid();
diff --git a/content/browser/renderer_host/media/OWNERS b/content/browser/renderer_host/media/OWNERS
index 097cb213..0cd4b45 100644
--- a/content/browser/renderer_host/media/OWNERS
+++ b/content/browser/renderer_host/media/OWNERS
@@ -5,9 +5,8 @@
 tommi@chromium.org
 olka@chromium.org
 
+# Original (legacy) owners.
 per-file *video*=chfremer@chromium.org
-
-# Original (legacy) owner.
 per-file *video*=emircan@chromium.org
 per-file *video*=mcasas@chromium.org
 
diff --git a/content/browser/renderer_host/media/audio_output_authorization_handler_unittest.cc b/content/browser/renderer_host/media/audio_output_authorization_handler_unittest.cc
index 480536f..e370b5f 100644
--- a/content/browser/renderer_host/media/audio_output_authorization_handler_unittest.cc
+++ b/content/browser/renderer_host/media/audio_output_authorization_handler_unittest.cc
@@ -42,10 +42,6 @@
 using MockAuthorizationCallback = base::MockCallback<
     AudioOutputAuthorizationHandler::AuthorizationCompletedCallback>;
 
-url::Origin SecurityOrigin() {
-  return url::Origin::Create(GURL(kSecurityOriginString));
-}
-
 // TestBrowserContext has a URLRequestContextGetter which uses a NullTaskRunner.
 // This causes it to be destroyed on the wrong thread. This BrowserContext
 // instead uses the IO thread task runner for the URLRequestContextGetter.
@@ -229,8 +225,10 @@
 TEST_F(AudioOutputAuthorizationHandlerTest,
        AuthorizeNondefaultDeviceIdWithoutPermission_NotAuthorized) {
   std::string raw_nondefault_id = GetRawNondefaultId();
+  MediaDeviceSaltAndOrigin salt_and_origin = GetMediaDeviceSaltAndOrigin(
+      process()->GetID(), main_rfh()->GetRoutingID());
   std::string hashed_id = MediaStreamManager::GetHMACForMediaDeviceID(
-      browser_context()->GetMediaDeviceIDSalt(), SecurityOrigin(),
+      salt_and_origin.device_id_salt, salt_and_origin.origin,
       raw_nondefault_id);
 
   MockAuthorizationCallback listener;
@@ -263,8 +261,10 @@
 TEST_F(AudioOutputAuthorizationHandlerTest,
        AuthorizeNondefaultDeviceIdWithPermission_Ok) {
   std::string raw_nondefault_id = GetRawNondefaultId();
+  MediaDeviceSaltAndOrigin salt_and_origin = GetMediaDeviceSaltAndOrigin(
+      process()->GetID(), main_rfh()->GetRoutingID());
   std::string hashed_id = MediaStreamManager::GetHMACForMediaDeviceID(
-      browser_context()->GetMediaDeviceIDSalt(), SecurityOrigin(),
+      salt_and_origin.device_id_salt, salt_and_origin.origin,
       raw_nondefault_id);
   MockAuthorizationCallback listener;
   std::unique_ptr<AudioOutputAuthorizationHandler> handler =
@@ -376,8 +376,10 @@
 TEST_F(AudioOutputAuthorizationHandlerTest,
        AuthorizeNondefaultDeviceIdAfterSaltChange_NotFound) {
   std::string raw_nondefault_id = GetRawNondefaultId();
+  MediaDeviceSaltAndOrigin salt_and_origin = GetMediaDeviceSaltAndOrigin(
+      process()->GetID(), main_rfh()->GetRoutingID());
   std::string hashed_id = MediaStreamManager::GetHMACForMediaDeviceID(
-      browser_context()->GetMediaDeviceIDSalt(), SecurityOrigin(),
+      salt_and_origin.device_id_salt, salt_and_origin.origin,
       raw_nondefault_id);
   MockAuthorizationCallback listener;
   std::unique_ptr<AudioOutputAuthorizationHandler> handler =
diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc
index 506bff6..4817447d 100644
--- a/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc
+++ b/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc
@@ -89,7 +89,6 @@
  public:
   MediaDevicesDispatcherHostTest()
       : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
-        browser_context_(new TestBrowserContext()),
         origin_(url::Origin::Create(GetParam())) {
     // Make sure we use fake devices to avoid long delays.
     base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
@@ -178,9 +177,11 @@
   void VideoInputCapabilitiesCallback(
       std::vector<blink::mojom::VideoInputDeviceCapabilitiesPtr> capabilities) {
     MockVideoInputCapabilitiesCallback();
+    MediaDeviceSaltAndOrigin salt_and_origin =
+        GetMediaDeviceSaltAndOrigin(-1, -1);
     std::string expected_first_device_id =
-        GetHMACForMediaDeviceID(browser_context_->GetMediaDeviceIDSalt(),
-                                origin_, kDefaultVideoDeviceID);
+        GetHMACForMediaDeviceID(salt_and_origin.device_id_salt,
+                                salt_and_origin.origin, kDefaultVideoDeviceID);
     EXPECT_EQ(kNumFakeVideoDevices, capabilities.size());
     EXPECT_EQ(expected_first_device_id, capabilities[0]->device_id);
     for (const auto& capability : capabilities) {
@@ -206,9 +207,11 @@
     // MediaDevicesManager always returns 3 fake audio input devices.
     const size_t kNumExpectedEntries = 3;
     EXPECT_EQ(kNumExpectedEntries, capabilities.size());
+    MediaDeviceSaltAndOrigin salt_and_origin =
+        GetMediaDeviceSaltAndOrigin(-1, -1);
     std::string expected_first_device_id =
-        GetHMACForMediaDeviceID(browser_context_->GetMediaDeviceIDSalt(),
-                                origin_, kDefaultAudioDeviceID);
+        GetHMACForMediaDeviceID(salt_and_origin.device_id_salt,
+                                salt_and_origin.origin, kDefaultAudioDeviceID);
     EXPECT_EQ(expected_first_device_id, capabilities[0]->device_id);
     for (const auto& capability : capabilities)
       EXPECT_TRUE(capability->parameters.IsValid());
@@ -304,12 +307,14 @@
   bool DoesEveryDeviceMapToRawId(
       const std::vector<std::vector<blink::WebMediaDeviceInfo>>& enumeration,
       const url::Origin& origin) {
+    MediaDeviceSaltAndOrigin salt_and_origin =
+        GetMediaDeviceSaltAndOrigin(-1, -1);
     for (size_t i = 0; i < blink::NUM_MEDIA_DEVICE_TYPES; ++i) {
       for (const auto& device_info : enumeration[i]) {
         bool found_match = false;
         for (const auto& raw_device_info : physical_devices_[i]) {
           if (DoesMediaDeviceIDMatchHMAC(
-                  browser_context_->GetMediaDeviceIDSalt(), origin,
+                  salt_and_origin.device_id_salt, salt_and_origin.origin,
                   device_info.device_id, raw_device_info.device_id)) {
             EXPECT_FALSE(found_match);
             found_match = true;
@@ -392,8 +397,7 @@
 
   MediaDeviceSaltAndOrigin GetSaltAndOrigin(int /* process_id */,
                                             int /* frame_id */) {
-    return MediaDeviceSaltAndOrigin(browser_context_->GetMediaDeviceIDSalt(),
-                                    "fake_group_id_salt", origin_);
+    return GetMediaDeviceSaltAndOrigin(-1, -1);
   }
 
   // The order of these members is important on teardown:
@@ -407,7 +411,6 @@
   std::unique_ptr<media::AudioManager> audio_manager_;
   std::unique_ptr<media::AudioSystem> audio_system_;
   media::FakeVideoCaptureDeviceFactory* video_capture_device_factory_;
-  std::unique_ptr<TestBrowserContext> browser_context_;
   MediaDeviceEnumeration physical_devices_;
   url::Origin origin_;
 
@@ -486,9 +489,11 @@
   base::RunLoop run_loop;
   EXPECT_CALL(*this, MockAllVideoInputDeviceFormatsCallback())
       .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
+  MediaDeviceSaltAndOrigin salt_and_origin =
+      GetMediaDeviceSaltAndOrigin(-1, -1);
   host_->GetAllVideoInputDeviceFormats(
-      GetHMACForMediaDeviceID(browser_context_->GetMediaDeviceIDSalt(), origin_,
-                              kDefaultVideoDeviceID),
+      GetHMACForMediaDeviceID(salt_and_origin.device_id_salt,
+                              salt_and_origin.origin, kDefaultVideoDeviceID),
       base::BindOnce(
           &MediaDevicesDispatcherHostTest::AllVideoInputDeviceFormatsCallback,
           base::Unretained(this)));
@@ -499,45 +504,17 @@
   base::RunLoop run_loop;
   EXPECT_CALL(*this, MockAvailableVideoInputDeviceFormatsCallback())
       .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
+  MediaDeviceSaltAndOrigin salt_and_origin =
+      GetMediaDeviceSaltAndOrigin(-1, -1);
   host_->GetAvailableVideoInputDeviceFormats(
-      GetHMACForMediaDeviceID(browser_context_->GetMediaDeviceIDSalt(), origin_,
-                              kNormalVideoDeviceID),
+      GetHMACForMediaDeviceID(salt_and_origin.device_id_salt,
+                              salt_and_origin.origin, kNormalVideoDeviceID),
       base::BindOnce(&MediaDevicesDispatcherHostTest::
                          AvailableVideoInputDeviceFormatsCallback,
                      base::Unretained(this)));
   run_loop.Run();
 }
 
-TEST_P(MediaDevicesDispatcherHostTest, Salt) {
-  EnumerateDevicesAndWaitForResult(true, true, true);
-  auto devices = enumerated_devices_;
-  EnumerateDevicesAndWaitForResult(true, true, true);
-  // Expect two enumerations with the same salt to produce the same device IDs
-  EXPECT_EQ(devices.size(), enumerated_devices_.size());
-  for (size_t i = 0; i < enumerated_devices_.size(); ++i) {
-    EXPECT_EQ(devices[i].size(), enumerated_devices_[i].size());
-    for (size_t j = 0; j < devices[i].size(); ++j)
-      EXPECT_EQ(devices[i][j].device_id, enumerated_devices_[i][j].device_id);
-  }
-
-  // Reset the salt and expect different device IDs in a new enumeration, except
-  // for default audio devices, which are always hashed to the same constant.
-  browser_context_ = std::make_unique<TestBrowserContext>();
-  EnumerateDevicesAndWaitForResult(true, true, true);
-  EXPECT_EQ(devices.size(), enumerated_devices_.size());
-  for (size_t i = 0; i < enumerated_devices_.size(); ++i) {
-    EXPECT_EQ(devices[i].size(), enumerated_devices_[i].size());
-    for (size_t j = 0; j < devices[i].size(); ++j) {
-      if (media::AudioDeviceDescription::IsDefaultDevice(
-              devices[i][j].device_id)) {
-        EXPECT_EQ(devices[i][j].device_id, enumerated_devices_[i][j].device_id);
-      } else {
-        EXPECT_NE(devices[i][j].device_id, enumerated_devices_[i][j].device_id);
-      }
-    }
-  }
-}
-
 INSTANTIATE_TEST_SUITE_P(,
                          MediaDevicesDispatcherHostTest,
                          testing::Values(GURL(), GURL("https://test.com")));
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher.cc b/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
index 8fadfdfa..084a6e2 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
@@ -141,11 +141,8 @@
   // GpuMemoryBuffer-based VideoCapture buffer works only on the Chrome OS
   // VideoCaptureDevice implementation. It's not supported by
   // FakeVideoCaptureDevice.
-  // TODO: Support GpuMemoryBuffer in FakeVideoCaptureDevice (crbug.com/1006613)
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kVideoCaptureUseGpuMemoryBuffer) &&
-      !base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kUseFakeDeviceForMediaStream)) {
+          switches::kVideoCaptureUseGpuMemoryBuffer)) {
     new_params.buffer_type = media::VideoCaptureBufferType::kGpuMemoryBuffer;
   }
 
diff --git a/content/browser/renderer_host/media/video_capture_unittest.cc b/content/browser/renderer_host/media/video_capture_unittest.cc
index 2302a73..bb9f292 100644
--- a/content/browser/renderer_host/media/video_capture_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_unittest.cc
@@ -145,11 +145,13 @@
       base::RunLoop run_loop;
       MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
       devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+      MediaDeviceSaltAndOrigin salt_and_origin =
+          GetMediaDeviceSaltAndOrigin(render_process_id, render_frame_id);
       media_stream_manager_->media_devices_manager()->EnumerateDevices(
           devices_to_enumerate,
           base::BindOnce(&VideoInputDevicesEnumerated, run_loop.QuitClosure(),
-                         browser_context_.GetMediaDeviceIDSalt(),
-                         security_origin, &video_devices));
+                         salt_and_origin.device_id_salt, salt_and_origin.origin,
+                         &video_devices));
       run_loop.Run();
     }
     ASSERT_FALSE(video_devices.empty());
@@ -161,9 +163,7 @@
           render_process_id, render_frame_id, requester_id, page_request_id,
           video_devices[0].device_id,
           blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
-          MediaDeviceSaltAndOrigin{browser_context_.GetMediaDeviceIDSalt(),
-                                   browser_context_.GetMediaDeviceIDSalt(),
-                                   security_origin},
+          GetMediaDeviceSaltAndOrigin(render_process_id, render_frame_id),
           base::BindOnce(&VideoCaptureTest::OnDeviceOpened,
                          base::Unretained(this), run_loop.QuitClosure()),
           MediaStreamManager::DeviceStoppedCallback());
diff --git a/content/browser/renderer_host/render_process_host_browsertest.cc b/content/browser/renderer_host/render_process_host_browsertest.cc
index be3d057..0b6372da 100644
--- a/content/browser/renderer_host/render_process_host_browsertest.cc
+++ b/content/browser/renderer_host/render_process_host_browsertest.cc
@@ -22,7 +22,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/content_client.h"
-#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
@@ -51,6 +50,7 @@
 #endif
 
 namespace content {
+namespace {
 
 std::unique_ptr<net::test_server::HttpResponse> HandleBeacon(
     const net::test_server::HttpRequest& request) {
@@ -73,19 +73,7 @@
 class RenderProcessHostTest : public ContentBrowserTest,
                               public RenderProcessHostObserver {
  public:
-  RenderProcessHostTest()
-      : process_exits_(0), host_destructions_(0), use_frame_priority_(false) {}
-
-  void SetUp() override {
-    if (use_frame_priority_) {
-      feature_list_.InitAndEnableFeature(
-          features::kUseFramePriorityInRenderProcessHost);
-    } else {
-      feature_list_.InitAndDisableFeature(
-          features::kUseFramePriorityInRenderProcessHost);
-    }
-    ContentBrowserTest::SetUp();
-  }
+  RenderProcessHostTest() : process_exits_(0), host_destructions_(0) {}
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitchASCII(
@@ -98,11 +86,6 @@
     host_resolver()->AddRule("*", "127.0.0.1");
   }
 
-  void SetVisibleClients(RenderProcessHost* process, int32_t visible_clients) {
-    RenderProcessHostImpl* impl = static_cast<RenderProcessHostImpl*>(process);
-    impl->visible_clients_ = visible_clients;
-  }
-
  protected:
   void set_process_exit_callback(const base::Closure& callback) {
     process_exit_callback_ = callback;
@@ -126,8 +109,6 @@
   int process_exits_;
   int host_destructions_;
   base::Closure process_exit_callback_;
-  bool use_frame_priority_;
-  base::test::ScopedFeatureList feature_list_;
 };
 
 // A mock ContentBrowserClient that only considers a spare renderer to be a
@@ -1113,28 +1094,6 @@
     rph->RemoveObserver(this);
 }
 
-IN_PROC_BROWSER_TEST_F(RenderProcessHostTest, LowPriorityFramesDisabled) {
-  // RenderProcessHostImpl::UpdateProcessPriority has an early check of
-  // run_renderer_in_process and exits for RenderProcessHosts without a child
-  // process launcher.  In order to skip initializing that here and the layer of
-  // indirection, we explicitly run in-process, which we must also disable once
-  // the test has finished to prevent crashing on exit.
-  RenderProcessHost::SetRunRendererInProcess(true);
-  RenderProcessHostImpl* process = static_cast<RenderProcessHostImpl*>(
-      RenderProcessHostImpl::CreateRenderProcessHost(
-          ShellContentBrowserClient::Get()->browser_context(), nullptr, nullptr,
-          false /* is_for_guests_only */));
-  // It starts off as normal priority.
-  EXPECT_FALSE(process->IsProcessBackgrounded());
-  // With the feature off it stays low priority when adding low priority frames.
-  process->UpdateFrameWithPriority(base::nullopt,
-                                   RenderProcessHostImpl::FramePriority::kLow);
-  process->UpdateFrameWithPriority(base::nullopt,
-                                   RenderProcessHostImpl::FramePriority::kLow);
-  EXPECT_FALSE(process->IsProcessBackgrounded());
-  RenderProcessHost::SetRunRendererInProcess(false);
-}
-
 // This test verifies properties of RenderProcessHostImpl *before* Init method
 // is called.
 IN_PROC_BROWSER_TEST_F(RenderProcessHostTest, ConstructedButNotInitializedYet) {
@@ -1177,73 +1136,5 @@
   process->Cleanup();
 }
 
-class RenderProcessHostFramePriorityTest : public RenderProcessHostTest {
- public:
-  RenderProcessHostFramePriorityTest() : RenderProcessHostTest() {
-    use_frame_priority_ = true;
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(RenderProcessHostFramePriorityTest,
-                       LowPriorityFramesEnabled) {
-  // RenderProcessHostImpl::UpdateProcessPriority has an early check of
-  // run_renderer_in_process and exits for RenderProcessHosts without a child
-  // process launcher.  In order to skip initializing that here and the layer of
-  // indirection, we explicitly run in-process, which we must also disable once
-  // the test has finished to prevent crashing on exit.
-  RenderProcessHost::SetRunRendererInProcess(true);
-  RenderProcessHostImpl* process = static_cast<RenderProcessHostImpl*>(
-      RenderProcessHostImpl::CreateRenderProcessHost(
-          ShellContentBrowserClient::Get()->browser_context(), nullptr, nullptr,
-          false /* is_for_guests_only */));
-  // For these tests, assume something is always visible.
-  SetVisibleClients(process, 1);
-  // When no frames are attached, it's not low priority.
-  EXPECT_FALSE(process->IsProcessBackgrounded());
-  // When all frames added are low priority, it's low priority.
-  process->UpdateFrameWithPriority(base::nullopt,
-                                   RenderProcessHostImpl::FramePriority::kLow);
-  process->UpdateFrameWithPriority(base::nullopt,
-                                   RenderProcessHostImpl::FramePriority::kLow);
-  EXPECT_TRUE(process->IsProcessBackgrounded());
-  // When all the low priority frames are removed, it's not low priority.
-  process->UpdateFrameWithPriority(RenderProcessHostImpl::FramePriority::kLow,
-                                   base::nullopt);
-  process->UpdateFrameWithPriority(RenderProcessHostImpl::FramePriority::kLow,
-                                   base::nullopt);
-  EXPECT_FALSE(process->IsProcessBackgrounded());
-  // When a low priority frame is added back in, it's low priority.
-  process->UpdateFrameWithPriority(base::nullopt,
-                                   RenderProcessHostImpl::FramePriority::kLow);
-  EXPECT_TRUE(process->IsProcessBackgrounded());
-  // As soon as a non-low priority frame is added, it's not low priority.
-  process->UpdateFrameWithPriority(
-      base::nullopt, RenderProcessHostImpl::FramePriority::kNormal);
-  EXPECT_FALSE(process->IsProcessBackgrounded());
-  // It remains not low priority even if we add more low priority frames.
-  process->UpdateFrameWithPriority(base::nullopt,
-                                   RenderProcessHostImpl::FramePriority::kLow);
-  EXPECT_FALSE(process->IsProcessBackgrounded());
-  // As soon as the non-low priority frame is removed, it becomes low priority.
-  process->UpdateFrameWithPriority(
-      RenderProcessHostImpl::FramePriority::kNormal, base::nullopt);
-  EXPECT_TRUE(process->IsProcessBackgrounded());
-  // Add a non-low priority frame, but then transition it to low, the process
-  // should go from unbackgrounded to backgrounded.
-  process->UpdateFrameWithPriority(
-      base::nullopt, RenderProcessHostImpl::FramePriority::kNormal);
-  EXPECT_FALSE(process->IsProcessBackgrounded());
-  process->UpdateFrameWithPriority(
-      RenderProcessHostImpl::FramePriority::kNormal,
-      RenderProcessHostImpl::FramePriority::kLow);
-  EXPECT_TRUE(process->IsProcessBackgrounded());
-  // Transition the frame back to normal priority, it becomes normal priority.
-  process->UpdateFrameWithPriority(
-      RenderProcessHostImpl::FramePriority::kLow,
-      RenderProcessHostImpl::FramePriority::kNormal);
-  EXPECT_FALSE(process->IsProcessBackgrounded());
-
-  RenderProcessHost::SetRunRendererInProcess(false);
-}
-
+}  // namespace
 }  // namespace content
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 311756e..cdaf4b1 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1437,7 +1437,6 @@
       priority_(!blink::kLaunchingProcessIsBackgrounded,
                 false /* has_media_stream */,
                 false /* has_foreground_service_worker */,
-                false /* all_low_priority_frames */,
                 frame_depth_,
                 false /* intersects_viewport */,
                 true /* boost_for_pending_views */
@@ -1717,10 +1716,6 @@
     InitializeChannelProxy();
 }
 
-bool RenderProcessHostImpl::HasOnlyLowPriorityFrames() {
-  return (low_priority_frames_ > 0) && (total_frames_ == low_priority_frames_);
-}
-
 void RenderProcessHostImpl::InitializeChannelProxy() {
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner =
       base::CreateSingleThreadTaskRunner({BrowserThread::IO});
@@ -2643,25 +2638,6 @@
   UpdateProcessPriorityInputs();
 }
 
-void RenderProcessHostImpl::UpdateFrameWithPriority(
-    base::Optional<FramePriority> previous_priority,
-    base::Optional<FramePriority> new_priority) {
-  if (!base::FeatureList::IsEnabled(
-          features::kUseFramePriorityInRenderProcessHost)) {
-    return;
-  }
-
-  const bool previous_all_low_priority_frames = HasOnlyLowPriorityFrames();
-  total_frames_ =
-      total_frames_ - (previous_priority ? 1 : 0) + (new_priority ? 1 : 0);
-  low_priority_frames_ =
-      low_priority_frames_ -
-      (previous_priority && previous_priority == FramePriority::kLow ? 1 : 0) +
-      (new_priority && new_priority == FramePriority::kLow ? 1 : 0);
-  if (previous_all_low_priority_frames != HasOnlyLowPriorityFrames())
-    UpdateProcessPriority();
-}
-
 int RenderProcessHostImpl::VisibleClientCount() {
   return visible_clients_;
 }
@@ -4404,7 +4380,7 @@
       visible_clients_ > 0 || base::CommandLine::ForCurrentProcess()->HasSwitch(
                                   switches::kDisableRendererBackgrounding),
       media_stream_count_ > 0, foreground_service_worker_count_ > 0,
-      HasOnlyLowPriorityFrames(), frame_depth_, intersects_viewport_,
+      frame_depth_, intersects_viewport_,
       !!pending_views_ /* boost_for_pending_views */
 #if defined(OS_ANDROID)
       ,
@@ -4412,11 +4388,11 @@
 #endif
   );
 
-  if (priority_ == priority)
-    return;
   const bool background_state_changed =
       priority_.is_background() != priority.is_background();
   const bool visibility_state_changed = priority_.visible != priority.visible;
+  if (priority_ == priority)
+    return;
 
   TRACE_EVENT2("renderer_host", "RenderProcessHostImpl::UpdateProcessPriority",
                "should_background", priority.is_background(),
@@ -4461,14 +4437,14 @@
 }
 
 void RenderProcessHostImpl::SendProcessStateToRenderer() {
-  mojom::RenderProcessBackgroundState background_state =
-      priority_.is_background()
-          ? mojom::RenderProcessBackgroundState::kBackgrounded
-          : mojom::RenderProcessBackgroundState::kForegrounded;
-  mojom::RenderProcessVisibleState visible_state =
-      priority_.visible ? mojom::RenderProcessVisibleState::kVisible
-                        : mojom::RenderProcessVisibleState::kHidden;
-  GetRendererInterface()->SetProcessState(background_state, visible_state);
+  mojom::RenderProcessState process_state;
+  if (priority_.is_background())
+    process_state = mojom::RenderProcessState::kBackgrounded;
+  else if (priority_.visible)
+    process_state = mojom::RenderProcessState::kVisible;
+  else
+    process_state = mojom::RenderProcessState::kHidden;
+  GetRendererInterface()->SetProcessState(process_state);
 }
 
 void RenderProcessHostImpl::OnProcessLaunched() {
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index b76dc4a1..3418a7d6 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -108,7 +108,6 @@
 class RenderFrameMessageFilter;
 class RenderProcessHostCreationObserver;
 class RenderProcessHostFactory;
-class RenderProcessHostTest;
 class RenderWidgetHelper;
 class SiteInstance;
 class SiteInstanceImpl;
@@ -284,14 +283,6 @@
           header_client,
       mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver);
 
-  // Update the total and low priority count as indicated by the previous and
-  // new priorities of the underlying document.  The nullopt option is used when
-  // there is no previous/subsequent navigation (when the frame is added/removed
-  // from the RenderProcessHostImpl).
-  void UpdateFrameWithPriority(
-      base::Optional<FramePriority> previous_priority,
-      base::Optional<FramePriority> new_priority) override;
-
   // Call this function when it is evident that the child process is actively
   // performing some operation, for example if we just received an IPC message.
   void mark_child_process_activity_time() {
@@ -474,7 +465,6 @@
   get_render_process_host_factory_for_testing();
 
   // Tracks which sites frames are hosted in which RenderProcessHosts.
-  // TODO(ericrobinson): These don't need to be static.
   static void AddFrameWithSite(BrowserContext* browser_context,
                                RenderProcessHost* render_process_host,
                                const GURL& site_url);
@@ -591,7 +581,6 @@
   friend class ChildProcessLauncherBrowserTest_ChildSpawnFail_Test;
   friend class VisitRelayingRenderProcessHost;
   friend class StoragePartitonInterceptor;
-  friend class RenderProcessHostTest;
 
   // Use CreateRenderProcessHost() instead of calling this constructor
   // directly.
@@ -599,13 +588,8 @@
                         StoragePartitionImpl* storage_partition_impl,
                         bool is_for_guests_only);
 
-  // True if this ChildProcessLauncher has a non-zero number of frames attached
-  // to it and they're all low priority.  Note: This will always return false
-  // unless features::kUseFramePriorityInProcessHost is enabled.
-  bool HasOnlyLowPriorityFrames();
-
-  // Initializes a new IPC::ChannelProxy in |channel_|, which will be
-  // connected to the next child process launched for this host, if any.
+  // Initializes a new IPC::ChannelProxy in |channel_|, which will be connected
+  // to the next child process launched for this host, if any.
   void InitializeChannelProxy();
 
   // Resets |channel_|, removing it from the attachment broker if necessary.
@@ -852,12 +836,6 @@
   // processes of same visibility. It indicates process has frames that
   // intersect with the viewport.
   bool intersects_viewport_ = false;
-  // Tracks the number of low priority frames currently hosted in this process.
-  // Always 0 unless features::kUseFramePriorityInProcessHost is enabled.
-  unsigned int low_priority_frames_ = 0;
-  // Tracks the total number of frames currently hosted in this process.
-  // Always 0 unless features::kUseFramePriorityInProcessHost is enabled.
-  unsigned int total_frames_ = 0;
 #if defined(OS_ANDROID)
   // Highest importance of all clients that contribute priority.
   ChildProcessImportance effective_importance_ = ChildProcessImportance::NORMAL;
diff --git a/content/browser/renderer_host/render_view_host_factory.cc b/content/browser/renderer_host/render_view_host_factory.cc
index 11d568ac..b92cc3c4 100644
--- a/content/browser/renderer_host/render_view_host_factory.cc
+++ b/content/browser/renderer_host/render_view_host_factory.cc
@@ -59,8 +59,6 @@
                                       /*hidden=*/true),
       delegate, routing_id, main_frame_routing_id, swapped_out,
       true /* has_initialized_audio_host */);
-  view_host->GetWidget()->BindVisualPropertiesManager(
-      view_host->GetVisualPropertiesManager());
   return view_host;
 }
 
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 0d66081..542c9ee4 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -139,36 +139,6 @@
 }
 #endif  // OS_WIN
 
-void GetPlatformSpecificPrefs(blink::mojom::RendererPreferences* prefs) {
-#if defined(OS_WIN)
-  // Note that what is called "height" in this struct is actually the font size;
-  // font "height" typically includes ascender, descender, and padding and is
-  // often a third or so larger than the given font size.
-  GetFontInfo(gfx::win::SystemFont::kCaption, &prefs->caption_font_family_name,
-              &prefs->caption_font_height);
-  GetFontInfo(gfx::win::SystemFont::kSmallCaption,
-              &prefs->small_caption_font_family_name,
-              &prefs->small_caption_font_height);
-  GetFontInfo(gfx::win::SystemFont::kMenu, &prefs->menu_font_family_name,
-              &prefs->menu_font_height);
-  GetFontInfo(gfx::win::SystemFont::kMessage, &prefs->message_font_family_name,
-              &prefs->message_font_height);
-  GetFontInfo(gfx::win::SystemFont::kStatus, &prefs->status_font_family_name,
-              &prefs->status_font_height);
-
-  prefs->vertical_scroll_bar_width_in_dips =
-      display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXVSCROLL);
-  prefs->horizontal_scroll_bar_height_in_dips =
-      display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYHSCROLL);
-  prefs->arrow_bitmap_height_vertical_scroll_bar_in_dips =
-      display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYVSCROLL);
-  prefs->arrow_bitmap_width_horizontal_scroll_bar_in_dips =
-      display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXHSCROLL);
-#elif defined(OS_LINUX)
-  prefs->system_font_family_name = gfx::Font().GetFontName();
-#endif
-}
-
 }  // namespace
 
 // static
@@ -211,6 +181,38 @@
   return rvh;
 }
 
+// static
+void RenderViewHostImpl::GetPlatformSpecificPrefs(
+    blink::mojom::RendererPreferences* prefs) {
+#if defined(OS_WIN)
+  // Note that what is called "height" in this struct is actually the font size;
+  // font "height" typically includes ascender, descender, and padding and is
+  // often a third or so larger than the given font size.
+  GetFontInfo(gfx::win::SystemFont::kCaption, &prefs->caption_font_family_name,
+              &prefs->caption_font_height);
+  GetFontInfo(gfx::win::SystemFont::kSmallCaption,
+              &prefs->small_caption_font_family_name,
+              &prefs->small_caption_font_height);
+  GetFontInfo(gfx::win::SystemFont::kMenu, &prefs->menu_font_family_name,
+              &prefs->menu_font_height);
+  GetFontInfo(gfx::win::SystemFont::kMessage, &prefs->message_font_family_name,
+              &prefs->message_font_height);
+  GetFontInfo(gfx::win::SystemFont::kStatus, &prefs->status_font_family_name,
+              &prefs->status_font_height);
+
+  prefs->vertical_scroll_bar_width_in_dips =
+      display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXVSCROLL);
+  prefs->horizontal_scroll_bar_height_in_dips =
+      display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYHSCROLL);
+  prefs->arrow_bitmap_height_vertical_scroll_bar_in_dips =
+      display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYVSCROLL);
+  prefs->arrow_bitmap_width_horizontal_scroll_bar_in_dips =
+      display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXHSCROLL);
+#elif defined(OS_LINUX)
+  prefs->system_font_family_name = gfx::Font().GetFontName();
+#endif
+}
+
 RenderViewHostImpl::RenderViewHostImpl(
     SiteInstance* instance,
     std::unique_ptr<RenderWidgetHostImpl> widget,
@@ -224,8 +226,7 @@
       instance_(static_cast<SiteInstanceImpl*>(instance)),
       is_swapped_out_(swapped_out),
       routing_id_(routing_id),
-      main_frame_routing_id_(main_frame_routing_id),
-      visual_properties_manager_(this) {
+      main_frame_routing_id_(main_frame_routing_id) {
   DCHECK(instance_.get());
   CHECK(delegate_);  // http://crbug.com/82827
   DCHECK_NE(GetRoutingID(), render_widget_host_->GetRoutingID());
@@ -337,7 +338,8 @@
   mojom::CreateViewParamsPtr params = mojom::CreateViewParams::New();
   params->renderer_preferences =
       delegate_->GetRendererPrefs(GetProcess()->GetBrowserContext()).Clone();
-  GetPlatformSpecificPrefs(params->renderer_preferences.get());
+  RenderViewHostImpl::GetPlatformSpecificPrefs(
+      params->renderer_preferences.get());
   params->web_preferences = GetWebkitPreferences();
   params->view_id = GetRoutingID();
   params->main_frame_routing_id = main_frame_routing_id_;
@@ -425,13 +427,6 @@
          GetWidget()->renderer_initialized();
 }
 
-void RenderViewHostImpl::SyncRendererPrefs() {
-  blink::mojom::RendererPreferences renderer_preferences =
-      delegate_->GetRendererPrefs(GetProcess()->GetBrowserContext());
-  GetPlatformSpecificPrefs(&renderer_preferences);
-  Send(new ViewMsg_SetRendererPrefs(GetRoutingID(), renderer_preferences));
-}
-
 void RenderViewHostImpl::SetBackgroundOpaque(bool opaque) {
   Send(new ViewMsg_SetBackgroundOpaque(GetRoutingID(), opaque));
 }
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index 8053f72..170e94a 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -24,7 +24,6 @@
 #include "content/browser/renderer_host/input/input_device_change_observer.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_owner_delegate.h"
-#include "content/browser/renderer_host/visual_properties_manager.h"
 #include "content/browser/site_instance_impl.h"
 #include "content/common/render_message_filter.mojom.h"
 #include "content/public/browser/notification_observer.h"
@@ -78,6 +77,9 @@
   // Convenience function, just like RenderViewHost::From.
   static RenderViewHostImpl* From(RenderWidgetHost* rwh);
 
+  static void GetPlatformSpecificPrefs(
+      blink::mojom::RendererPreferences* prefs);
+
   RenderViewHostImpl(SiteInstance* instance,
                      std::unique_ptr<RenderWidgetHostImpl> widget,
                      RenderViewHostDelegate* delegate,
@@ -102,7 +104,6 @@
   void NotifyMoveOrResizeStarted() override;
   void SetWebUIProperty(const std::string& name,
                         const std::string& value) override;
-  void SyncRendererPrefs() override;
   WebPreferences GetWebkitPreferences() override;
   void UpdateWebkitPreferences(const WebPreferences& prefs) override;
   void OnWebkitPreferencesChanged() override;
@@ -133,10 +134,6 @@
       const FrameReplicationState& replicated_frame_state,
       bool window_was_created_with_opener);
 
-  base::WeakPtr<VisualPropertiesManager> GetVisualPropertiesManager() {
-    return visual_properties_manager_.GetWeakPtr();
-  }
-
   // Tracks whether this RenderViewHost is in an active state (rather than
   // pending swap out or swapped out), according to its main frame
   // RenderFrameHost.
@@ -369,9 +366,6 @@
   // BackForwardCache:
   bool is_in_back_forward_cache_ = false;
 
-  // Used to send Page and Widget visual properties to Renderers.
-  VisualPropertiesManager visual_properties_manager_;
-
   base::WeakPtrFactory<RenderViewHostImpl> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(RenderViewHostImpl);
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 406c7d8..f9635e9 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -66,7 +66,6 @@
 #include "content/browser/renderer_host/render_widget_host_owner_delegate.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
 #include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
-#include "content/browser/renderer_host/visual_properties_manager.h"
 #include "content/common/content_constants_internal.h"
 #include "content/common/cursors/webcursor.h"
 #include "content/common/drag_messages.h"
@@ -431,13 +430,6 @@
     Destroy(false);
 }
 
-void RenderWidgetHostImpl::BindVisualPropertiesManager(
-    base::WeakPtr<VisualPropertiesManager> visual_properties_manager) {
-  DCHECK(visual_properties_manager);
-  DCHECK(!visual_properties_manager_);
-  visual_properties_manager_ = std::move(visual_properties_manager);
-}
-
 // static
 RenderWidgetHost* RenderWidgetHost::FromID(
     int32_t process_id,
@@ -933,16 +925,12 @@
       old_visual_properties_->visible_viewport_size !=
           visual_properties->visible_viewport_size;
 
-  bool sent_visual_properties = false;
-  if (visual_properties_manager_) {
-    visual_properties_manager_->SendVisualProperties(*visual_properties,
-                                                     GetRoutingID());
-    sent_visual_properties = true;
-  }
+  Send(new WidgetMsg_UpdateVisualProperties(routing_id_, *visual_properties));
 
-  // Ideally, page visual properties and widget visual properties would be sent
-  // synchronously. As they become decoupled, members should be moved from the
-  // VisualPropertiesManager::SendVisualProperties into this one.
+  // TODO(danakj): All visual properties should go through
+  // WidgetMsg_UpdateVisualProperties in order to be synchronized by the
+  // LocalSurfaceIdAllocation which synchronizes in the display compositor for
+  // a global atomic screen update across all frame widgets. This should move.
   if (delegate() && visible_viewport_size_changed) {
     delegate()->NotifyVisibleViewportSizeChanged(
         visual_properties->visible_viewport_size);
@@ -951,20 +939,18 @@
   bool width_changed =
       !old_visual_properties_ || old_visual_properties_->new_size.width() !=
                                      visual_properties->new_size.width();
-  if (sent_visual_properties) {
-    TRACE_EVENT_WITH_FLOW2(
-        TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
-        "RenderWidgetHostImpl::SynchronizeVisualProperties send message",
-        visual_properties->local_surface_id_allocation->local_surface_id()
-            .submission_trace_id(),
-        TRACE_EVENT_FLAG_FLOW_OUT, "message",
-        "WidgetMsg_SynchronizeVisualProperties", "local_surface_id",
-        visual_properties->local_surface_id_allocation->local_surface_id()
-            .ToString());
-    visual_properties_ack_pending_ =
-        DoesVisualPropertiesNeedAck(old_visual_properties_, *visual_properties);
-    old_visual_properties_ = std::move(visual_properties);
-  }
+  TRACE_EVENT_WITH_FLOW2(
+      TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+      "RenderWidgetHostImpl::SynchronizeVisualProperties send message",
+      visual_properties->local_surface_id_allocation->local_surface_id()
+          .submission_trace_id(),
+      TRACE_EVENT_FLAG_FLOW_OUT, "message",
+      "WidgetMsg_SynchronizeVisualProperties", "local_surface_id",
+      visual_properties->local_surface_id_allocation->local_surface_id()
+          .ToString());
+  visual_properties_ack_pending_ =
+      DoesVisualPropertiesNeedAck(old_visual_properties_, *visual_properties);
+  old_visual_properties_ = std::move(visual_properties);
 
   // Warning: |visual_properties| invalid after this point.
 
@@ -972,7 +958,7 @@
     delegate_->RenderWidgetWasResized(this, width_changed);
   }
 
-  return sent_visual_properties;
+  return true;
 }
 
 void RenderWidgetHostImpl::GotFocus() {
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 4df850f5..bb4e82d 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -115,7 +115,6 @@
 class SyntheticGestureController;
 class TimeoutMonitor;
 class TouchEmulator;
-class VisualPropertiesManager;
 class WebCursor;
 struct EditCommand;
 struct VisualProperties;
@@ -175,20 +174,6 @@
 
   ~RenderWidgetHostImpl() override;
 
-  // The visual properties manager is used to send visual property updates to
-  // Renderers. Visual properties include both page and widget-specific
-  // properties. Both must be sent in lock-step, and the widget may not exist,
-  // whereas the page always will. Therefore, this class cannot directly send
-  // visual property updates, it must use the manager, a member of
-  // RenderViewHostImpl. Eventually, page state should be stored on the visual
-  // properties manager as well.
-  // This method must be called immediately after construction of the
-  // RenderWidgetHostImpl. The only reason it isn't a member of the constructor
-  // is due to ordering constraints of construction of RenderViewHostImpl [which
-  // requires an instance of RenderWidgetHostImpl].
-  void BindVisualPropertiesManager(
-      base::WeakPtr<VisualPropertiesManager> visual_properties_manager);
-
   // Similar to RenderWidgetHost::FromID, but returning the Impl object.
   static RenderWidgetHostImpl* FromID(int32_t process_id, int32_t routing_id);
 
@@ -1309,12 +1294,6 @@
   // See comments on Add/ClearPendingUserActivation().
   base::OneShotTimer pending_user_activation_timer_;
 
-  // VisualPropertiesManager is owned by the RenderViewHost. For popup or pepper
-  // fullscreen widgets, the widget might outlive the RenderViewHost.
-  // TODO(https://crbug.com/1004009): Assert that this never happens and change
-  // this to be a raw pointer.
-  base::WeakPtr<VisualPropertiesManager> visual_properties_manager_;
-
   base::WeakPtrFactory<RenderWidgetHostImpl> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostImpl);
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 353a3a5..c46e127 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -35,7 +35,6 @@
 #include "content/browser/renderer_host/render_view_host_delegate_view.h"
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
-#include "content/browser/renderer_host/visual_properties_manager.h"
 #include "content/common/edit_command.h"
 #include "content/common/input/synthetic_web_input_event_builders.h"
 #include "content/common/input_messages.h"
@@ -446,16 +445,6 @@
                void(const VisualProperties& visual_properties));
 };
 
-class MockRenderWidgetHostVisualPropertiesManager
-    : public VisualPropertiesManager {
- public:
-  MockRenderWidgetHostVisualPropertiesManager()
-      : VisualPropertiesManager(nullptr) {}
-  MOCK_METHOD2(SendVisualProperties,
-               void(const VisualProperties& visual_properties,
-                    int widget_routing_id));
-};
-
 // RenderWidgetHostTest --------------------------------------------------------
 
 class RenderWidgetHostTest : public testing::Test {
@@ -506,8 +495,6 @@
                                              process_->GetNextRoutingID()));
     // Set up the RenderWidgetHost as being for a main frame.
     host_->set_owner_delegate(&mock_owner_delegate_);
-    host_->BindVisualPropertiesManager(
-        mock_visual_properties_manager_.GetWeakPtr());
     view_.reset(new TestView(host_.get()));
     ConfigureView(view_.get());
     host_->SetView(view_.get());
@@ -724,8 +711,6 @@
   RenderWidgetHostProcess* process_;  // Deleted automatically by the widget.
   std::unique_ptr<MockRenderWidgetHostDelegate> delegate_;
   testing::NiceMock<MockRenderWidgetHostOwnerDelegate> mock_owner_delegate_;
-  testing::NiceMock<MockRenderWidgetHostVisualPropertiesManager>
-      mock_visual_properties_manager_;
   std::unique_ptr<MockRenderWidgetHost> host_;
   std::unique_ptr<TestView> view_;
   std::unique_ptr<display::Screen> screen_;
@@ -761,55 +746,58 @@
 // -----------------------------------------------------------------------------
 
 TEST_F(RenderWidgetHostTest, SynchronizeVisualProperties) {
+  sink_->ClearMessages();
+
   // The initial zoom is 0 so host should not send a sync message
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(0);
   delegate_->SetZoomLevel(0);
   EXPECT_FALSE(host_->SynchronizeVisualProperties());
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(0u, sink_->message_count());
 
-  // The zoom has changed so host should send out a sync message
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
+  sink_->ClearMessages();
+
+  // The zoom has changed so host should send out a sync message.
   double new_zoom_level = blink::PageZoomFactorToZoomLevel(0.25);
   delegate_->SetZoomLevel(new_zoom_level);
   EXPECT_TRUE(host_->SynchronizeVisualProperties());
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
   EXPECT_NEAR(new_zoom_level, host_->old_visual_properties_->zoom_level, 0.01);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(1u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   // The initial bounds is the empty rect, so setting it to the same thing
   // shouldn't send the resize message.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(0);
   view_->SetBounds(gfx::Rect());
   host_->SynchronizeVisualProperties();
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(0u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   // No visual properties ACK if the physical backing gets set, but the view
   // bounds are zero.
   view_->SetMockCompositorViewportPixelSize(gfx::Size(200, 200));
   host_->SynchronizeVisualProperties();
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
+  EXPECT_EQ(1u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   // Setting the view bounds to nonzero should send out the notification.
   // but should not expect ack for empty physical backing size.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
   gfx::Rect original_size(0, 0, 100, 100);
   view_->SetBounds(original_size);
   view_->SetMockCompositorViewportPixelSize(gfx::Size());
   host_->SynchronizeVisualProperties();
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
   EXPECT_EQ(original_size.size(), host_->old_visual_properties_->new_size);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(1u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   // Setting the bounds and physical backing size to nonzero should send out
   // the notification and expect an ack.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
   view_->ClearMockCompositorViewportPixelSize();
   host_->SynchronizeVisualProperties();
   EXPECT_TRUE(host_->visual_properties_ack_pending_);
@@ -819,7 +807,9 @@
   metadata.local_surface_id_allocation = base::nullopt;
   host_->DidUpdateVisualProperties(metadata);
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(1u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   gfx::Rect second_size(0, 0, 110, 110);
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
@@ -827,87 +817,87 @@
   EXPECT_TRUE(host_->SynchronizeVisualProperties());
   EXPECT_TRUE(host_->visual_properties_ack_pending_);
 
+  sink_->ClearMessages();
+
   // Sending out a new notification should NOT send out a new IPC message since
   // a visual properties ACK is pending.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(0);
   gfx::Rect third_size(0, 0, 120, 120);
   process_->sink().ClearMessages();
   view_->SetBounds(third_size);
   EXPECT_FALSE(host_->SynchronizeVisualProperties());
   EXPECT_TRUE(host_->visual_properties_ack_pending_);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(0u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   // Send a update that's a visual properties ACK, but for the original_size we
   // sent. Since this isn't the second_size, the message handler should
   // immediately send a new resize message for the new size to the renderer.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
   metadata.viewport_size_in_pixels = original_size.size();
   metadata.local_surface_id_allocation = base::nullopt;
   host_->DidUpdateVisualProperties(metadata);
   EXPECT_TRUE(host_->visual_properties_ack_pending_);
   EXPECT_EQ(third_size.size(), host_->old_visual_properties_->new_size);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(1u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   // Send the visual properties ACK for the latest size.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(0);
   metadata.viewport_size_in_pixels = third_size.size();
   metadata.local_surface_id_allocation = base::nullopt;
   host_->DidUpdateVisualProperties(metadata);
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
   EXPECT_EQ(third_size.size(), host_->old_visual_properties_->new_size);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(0u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   // Now clearing the bounds should send out a notification but we shouldn't
   // expect a visual properties ACK (since the renderer won't ack empty sizes).
   // The message should contain the new size (0x0) and not the previous one that
   // we skipped.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
   view_->SetBounds(gfx::Rect());
   host_->SynchronizeVisualProperties();
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
   EXPECT_EQ(gfx::Size(), host_->old_visual_properties_->new_size);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(1u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   // Send a rect that has no area but has either width or height set.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
   view_->SetBounds(gfx::Rect(0, 0, 0, 30));
   host_->SynchronizeVisualProperties();
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
   EXPECT_EQ(gfx::Size(0, 30), host_->old_visual_properties_->new_size);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(1u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   // Set the same size again. It should not be sent again.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(0);
   host_->SynchronizeVisualProperties();
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
   EXPECT_EQ(gfx::Size(0, 30), host_->old_visual_properties_->new_size);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(0u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   // A different size should be sent again, however.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
   view_->SetBounds(gfx::Rect(0, 0, 0, 31));
   host_->SynchronizeVisualProperties();
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
   EXPECT_EQ(gfx::Size(0, 31), host_->old_visual_properties_->new_size);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(1u, sink_->message_count());
+
+  sink_->ClearMessages();
 
   // An invalid LocalSurfaceId should result in no change to the
   // |visual_properties_ack_pending_| bit.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
   view_->SetBounds(gfx::Rect(25, 25));
   view_->InvalidateLocalSurfaceId();
   host_->SynchronizeVisualProperties();
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
   EXPECT_EQ(gfx::Size(25, 25), host_->old_visual_properties_->new_size);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
+  EXPECT_EQ(1u, sink_->message_count());
 }
 
 // Test that a resize event is sent if SynchronizeVisualProperties() is called
@@ -920,58 +910,73 @@
   screen_info.orientation_angle = 0;
   screen_info.orientation_type = SCREEN_ORIENTATION_VALUES_PORTRAIT_PRIMARY;
 
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
+  sink_->ClearMessages();
   view_->SetScreenInfo(screen_info);
-  host_->SynchronizeVisualProperties();
+  EXPECT_EQ(0u, sink_->message_count());
+  EXPECT_TRUE(host_->SynchronizeVisualProperties());
+  // WidgetMsg_UpdateVisualProperties sent to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
+  EXPECT_EQ(WidgetMsg_UpdateVisualProperties::ID,
+            sink_->GetMessageAt(0)->type());
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
 
   screen_info.orientation_angle = 180;
   screen_info.orientation_type = SCREEN_ORIENTATION_VALUES_LANDSCAPE_PRIMARY;
 
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
+  sink_->ClearMessages();
   view_->SetScreenInfo(screen_info);
-  host_->SynchronizeVisualProperties();
+  EXPECT_EQ(0u, sink_->message_count());
+  EXPECT_TRUE(host_->SynchronizeVisualProperties());
+  // WidgetMsg_UpdateVisualProperties sent to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
+  EXPECT_EQ(WidgetMsg_UpdateVisualProperties::ID,
+            sink_->GetMessageAt(0)->type());
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
 
   screen_info.device_scale_factor = 2.f;
 
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
+  sink_->ClearMessages();
   view_->SetScreenInfo(screen_info);
-  host_->SynchronizeVisualProperties();
+  EXPECT_EQ(0u, sink_->message_count());
+  EXPECT_TRUE(host_->SynchronizeVisualProperties());
+  // WidgetMsg_UpdateVisualProperties sent to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
+  EXPECT_EQ(WidgetMsg_UpdateVisualProperties::ID,
+            sink_->GetMessageAt(0)->type());
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
 
   // No screen change.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(0);
+  sink_->ClearMessages();
   view_->SetScreenInfo(screen_info);
-  host_->SynchronizeVisualProperties();
+  EXPECT_FALSE(host_->SynchronizeVisualProperties());
+  EXPECT_EQ(0u, sink_->message_count());
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
 }
 
 // Test for crbug.com/25097. If a renderer crashes between a resize and the
 // corresponding update message, we must be sure to clear the visual properties
 // ACK logic.
 TEST_F(RenderWidgetHostTest, ResizeThenCrash) {
+  sink_->ClearMessages();
   // Setting the bounds to a "real" rect should send out the notification.
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
-  gfx::Rect original_size(0, 0, 100, 100);
-  view_->SetBounds(original_size);
+  view_->SetBounds(gfx::Rect(100, 100));
   host_->SynchronizeVisualProperties();
+  // WidgetMsg_UpdateVisualProperties is sent to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
+  {
+    const IPC::Message* msg = sink_->GetMessageAt(0);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    // Size sent to the renderer.
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size);
+  }
   EXPECT_TRUE(host_->visual_properties_ack_pending_);
-  EXPECT_EQ(original_size.size(), host_->old_visual_properties_->new_size);
-  ::testing::Mock::VerifyAndClearExpectations(&mock_visual_properties_manager_);
 
-  // Simulate a renderer crash before the update message. Ensure all the visual
-  // properties ACK logic is cleared. Must clear the view first so it doesn't
-  // get deleted.
+  // Simulate a renderer crash before an ACK for the UpdateVisualProperties
+  // message arrives. Ensure all the visual properties ACK logic is cleared.
+  // Must clear the view first so it doesn't get deleted.
   host_->SetView(nullptr);
   host_->RendererExited();
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
@@ -1663,12 +1668,21 @@
 }
 
 TEST_F(RenderWidgetHostTest, HideUnthrottlesResize) {
-  gfx::Size original_size(100, 100);
-  view_->SetBounds(gfx::Rect(original_size));
-  EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
-      .Times(1);
+  sink_->ClearMessages();
+  view_->SetBounds(gfx::Rect(100, 100));
   EXPECT_TRUE(host_->SynchronizeVisualProperties());
-  EXPECT_EQ(original_size, host_->old_visual_properties_->new_size);
+  // WidgetMsg_UpdateVisualProperties is sent to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
+  {
+    const IPC::Message* msg = sink_->GetMessageAt(0);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    // Size sent to the renderer.
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size);
+  }
+  // An ack is pending, throttling further updates.
   EXPECT_TRUE(host_->visual_properties_ack_pending_);
 
   // Hiding the widget should unthrottle resize.
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index aaf056a..cfaf408 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -59,7 +59,6 @@
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_event_handler.h"
 #include "content/browser/renderer_host/text_input_manager.h"
-#include "content/browser/renderer_host/visual_properties_manager.h"
 #include "content/browser/web_contents/web_contents_view_aura.h"
 #include "content/common/input/synthetic_web_input_event_builders.h"
 #include "content/common/input_messages.h"
@@ -158,23 +157,6 @@
 
 namespace content {
 
-namespace {
-class MockRenderWidgetHostVisualPropertiesManager
-    : public VisualPropertiesManager {
- public:
-  MockRenderWidgetHostVisualPropertiesManager()
-      : VisualPropertiesManager(nullptr) {}
-  void SendVisualProperties(const VisualProperties& visual_properties,
-                            int widget_routing_id) override {
-    ++sent_visual_properties_count_;
-    sent_visual_properties_ = visual_properties;
-  }
-
-  int sent_visual_properties_count_ = 0;
-  VisualProperties sent_visual_properties_;
-};
-}  // namespace
-
 void InstallDelegatedFrameHostClient(
     RenderWidgetHostViewAura* render_widget_host_view,
     std::unique_ptr<DelegatedFrameHostClient> delegated_frame_host_client);
@@ -579,8 +561,6 @@
                                           gfx::Rect());
     view_ = CreateView(is_guest_view_hack_);
     widget_host_ = static_cast<MockRenderWidgetHostImpl*>(view_->host());
-    widget_host_->BindVisualPropertiesManager(
-        visual_properties_manager_.GetWeakPtr());
     // Set the mouse_wheel_phase_handler_ timer timeout to 100ms.
     view_->event_handler()->set_mouse_wheel_wheel_phase_handler_timeout(
         base::TimeDelta::FromMilliseconds(100));
@@ -706,12 +686,11 @@
   MockRenderWidgetHostImpl* widget_host_;
   FakeRenderWidgetHostViewAura* view_;
 
-  IPC::TestSink* sink_;
+  IPC::TestSink* sink_ = nullptr;
   base::test::ScopedFeatureList mojo_feature_list_;
   base::test::ScopedFeatureList feature_list_;
 
   viz::ParentLocalSurfaceIdAllocator parent_local_surface_id_allocator_;
-  MockRenderWidgetHostVisualPropertiesManager visual_properties_manager_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraTest);
@@ -2617,164 +2596,229 @@
       view_->GetNativeView(),
       parent_view_->GetNativeView()->GetRootWindow(),
       gfx::Rect());
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 1);
+
+  sink_->ClearMessages();
+
   view_->SetSize(gfx::Size(100, 100));
-  EXPECT_EQ("100x100", view_->GetCompositorViewportPixelSize().ToString());
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 2);
+
+  // Physical pixel size.
+  EXPECT_EQ(gfx::Size(100, 100), view_->GetCompositorViewportPixelSize());
+  // Update to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
   {
-    VisualProperties visual_properties =
-        visual_properties_manager_.sent_visual_properties_;
-    EXPECT_EQ("100x100", visual_properties.new_size.ToString());  // dip size
-    EXPECT_EQ("100x100",
-              visual_properties.compositor_viewport_pixel_rect.size()
-                  .ToString());  // backing size
+    const IPC::Message* msg = sink_->GetMessageAt(0);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    // DIP size.
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size);
+    // Physical pixel size.
+    EXPECT_EQ(gfx::Size(100, 100),
+              visual_properties.compositor_viewport_pixel_rect.size());
   }
 
   widget_host_->ResetSentVisualProperties();
+  sink_->ClearMessages();
+
+  // Device scale factor changes to 2, so the physical pixel sizes should
+  // change, while the DIP sizes do not.
 
   aura_test_helper_->test_screen()->SetDeviceScaleFactor(2.0f);
-  EXPECT_EQ("200x200", view_->GetCompositorViewportPixelSize().ToString());
-  // Extra ScreenInfoChanged message for |parent_view_|.
-  // Changing the device scale factor triggers the
-  // RenderWidgetHostViewAura::OnDisplayMetricsChanged() observer callback,
-  // which sends a ViewMsg_UpdateVisualProperties::ID message to the
-  // renderer.
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 3);
+  // Physical pixel size.
+  EXPECT_EQ(gfx::Size(200, 200), view_->GetCompositorViewportPixelSize());
+  // Update to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
+  {
+    const IPC::Message* msg = sink_->GetMessageAt(0);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    // DIP size.
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size);
+    // Physical pixel size.
+    EXPECT_EQ(gfx::Size(200, 200),
+              visual_properties.compositor_viewport_pixel_rect.size());
+  }
 
   widget_host_->ResetSentVisualProperties();
+  sink_->ClearMessages();
 
   aura_test_helper_->test_screen()->SetDeviceScaleFactor(1.0f);
-  // Extra ScreenInfoChanged message for |parent_view_|.
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 4);
-  EXPECT_EQ("100x100", view_->GetCompositorViewportPixelSize().ToString());
+
+  // Physical pixel size.
+  EXPECT_EQ(gfx::Size(100, 100), view_->GetCompositorViewportPixelSize());
+  // Update to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
+  {
+    const IPC::Message* msg = sink_->GetMessageAt(0);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    // DIP size.
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size);
+    // Physical pixel size.
+    EXPECT_EQ(gfx::Size(100, 100),
+              visual_properties.compositor_viewport_pixel_rect.size());
+  }
 }
 
 // This test verifies that in AutoResize mode a new
-// ViewMsg_UpdateVisualProperties message is sent when ScreenInfo
+// WidgetMsg_UpdateVisualProperties message is sent when ScreenInfo
 // changes and that message contains the latest ScreenInfo.
 TEST_F(RenderWidgetHostViewAuraTest, AutoResizeWithScale) {
   view_->InitAsChild(nullptr);
   aura::client::ParentWindowWithContext(
       view_->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
       gfx::Rect());
-  viz::LocalSurfaceIdAllocation local_surface_id_allocation1(
-      view_->GetLocalSurfaceIdAllocation());
-  EXPECT_TRUE(local_surface_id_allocation1.IsValid());
 
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 1);
+  viz::LocalSurfaceIdAllocation host_local_surface_id_allocation =
+      view_->GetLocalSurfaceIdAllocation();
+  EXPECT_TRUE(host_local_surface_id_allocation.IsValid());
+
+  sink_->ClearMessages();
+
   view_->EnableAutoResize(gfx::Size(50, 50), gfx::Size(100, 100));
 
-  viz::LocalSurfaceIdAllocation local_surface_id_allocation2;
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 2);
+  // Update to the renderer. It includes the current LocalSurfaceIdAllocation.
+  ASSERT_EQ(1u, sink_->message_count());
   {
-    VisualProperties visual_properties =
-        visual_properties_manager_.sent_visual_properties_;
-    EXPECT_EQ("50x50", visual_properties.min_size_for_auto_resize.ToString());
-    EXPECT_EQ("100x100", visual_properties.max_size_for_auto_resize.ToString());
+    const IPC::Message* msg = sink_->GetMessageAt(0);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    // Auto resize parameters that we set above.
+    EXPECT_EQ(gfx::Size(50, 50), visual_properties.min_size_for_auto_resize);
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.max_size_for_auto_resize);
+    // Default DSF is 1.
     EXPECT_EQ(1, visual_properties.screen_info.device_scale_factor);
-    local_surface_id_allocation2 =
-        visual_properties.local_surface_id_allocation.value_or(
-            viz::LocalSurfaceIdAllocation());
-    EXPECT_EQ(local_surface_id_allocation1, local_surface_id_allocation2);
-    EXPECT_TRUE(local_surface_id_allocation2.IsValid());
+    // Passed the original LocalSurfaceIdAllocation.
+    EXPECT_TRUE(visual_properties.local_surface_id_allocation.has_value());
+    EXPECT_EQ(host_local_surface_id_allocation,
+              visual_properties.local_surface_id_allocation.value());
   }
 
-  viz::LocalSurfaceIdAllocation local_surface_id_allocation(
+  // Receive a changed LocalSurfaceIdAllocation from the renderer with a size.
+  viz::LocalSurfaceIdAllocation renderer_local_surface_id_allocation(
       viz::LocalSurfaceId(
-          local_surface_id_allocation1.local_surface_id()
+          host_local_surface_id_allocation.local_surface_id()
               .parent_sequence_number(),
-          local_surface_id_allocation1.local_surface_id()
+          host_local_surface_id_allocation.local_surface_id()
                   .child_sequence_number() +
               1,
-          local_surface_id_allocation1.local_surface_id().embed_token()),
+          host_local_surface_id_allocation.local_surface_id().embed_token()),
       base::TimeTicks::Now());
   {
     cc::RenderFrameMetadata metadata;
     metadata.viewport_size_in_pixels = gfx::Size(75, 75);
-    metadata.local_surface_id_allocation = local_surface_id_allocation;
+    metadata.local_surface_id_allocation = renderer_local_surface_id_allocation;
     widget_host_->DidUpdateVisualProperties(metadata);
   }
 
+  // Changing the device scale factor updates the renderer.
+  sink_->ClearMessages();
   aura_test_helper_->test_screen()->SetDeviceScaleFactor(2.0f);
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 3);
 
+  // Update to the renderer.
+  // TODO(samans): There should be only one message in the sink, but some
+  // testers are seeing two (crrev.com/c/839580). Investigate why.
+  ASSERT_LE(1u, sink_->message_count());
   {
-    // TODO(samans): There should be only one message in the sink, but some
-    // testers are seeing two (crrev.com/c/839580). Investigate why.
-    VisualProperties visual_properties =
-        visual_properties_manager_.sent_visual_properties_;
-    EXPECT_EQ("50x50", visual_properties.min_size_for_auto_resize.ToString());
-    EXPECT_EQ("100x100", visual_properties.max_size_for_auto_resize.ToString());
+    const IPC::Message* msg =
+        sink_->GetFirstMessageMatching(WidgetMsg_UpdateVisualProperties::ID);
+    ASSERT_TRUE(msg);
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    // Auto resize parameters did not change as they DIP values.
+    EXPECT_EQ(gfx::Size(50, 50), visual_properties.min_size_for_auto_resize);
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.max_size_for_auto_resize);
+    // Updated DSF for the renderer.
     EXPECT_EQ(2, visual_properties.screen_info.device_scale_factor);
-    EXPECT_NE(local_surface_id_allocation1,
-              visual_properties.local_surface_id_allocation.value_or(
-                  viz::LocalSurfaceIdAllocation()));
-    EXPECT_NE(local_surface_id_allocation2,
-              visual_properties.local_surface_id_allocation.value_or(
-                  viz::LocalSurfaceIdAllocation()));
+    // The LocalSurfaceIdAllocation has changed to the one from the renderer.
+    EXPECT_TRUE(visual_properties.local_surface_id_allocation.has_value());
+    EXPECT_NE(host_local_surface_id_allocation,
+              visual_properties.local_surface_id_allocation.value());
+    EXPECT_NE(renderer_local_surface_id_allocation,
+              visual_properties.local_surface_id_allocation.value());
   }
 }
 
 // This test verifies that in AutoResize mode a new
-// ViewMsg_UpdateVisualProperties message is sent when size changes.
+// WidgetMsg_UpdateVisualProperties message is sent when size changes.
 TEST_F(RenderWidgetHostViewAuraTest, AutoResizeWithBrowserInitiatedResize) {
   view_->InitAsChild(nullptr);
   aura::client::ParentWindowWithContext(
       view_->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
       gfx::Rect());
-  viz::LocalSurfaceIdAllocation local_surface_id_allocation1(
+  viz::LocalSurfaceIdAllocation host_local_surface_id_allocation(
       view_->GetLocalSurfaceIdAllocation());
-  EXPECT_TRUE(local_surface_id_allocation1.IsValid());
+  EXPECT_TRUE(host_local_surface_id_allocation.IsValid());
 
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 1);
+  sink_->ClearMessages();
   view_->EnableAutoResize(gfx::Size(50, 50), gfx::Size(100, 100));
 
-  viz::LocalSurfaceIdAllocation local_surface_id_allocation2;
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 2);
+  // WidgetMsg_UpdateVisualProperties is sent to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
   {
-    VisualProperties visual_properties =
-        visual_properties_manager_.sent_visual_properties_;
-    EXPECT_EQ("50x50", visual_properties.min_size_for_auto_resize.ToString());
-    EXPECT_EQ("100x100", visual_properties.max_size_for_auto_resize.ToString());
-    EXPECT_EQ(1, visual_properties.screen_info.device_scale_factor);
-    local_surface_id_allocation2 =
-        visual_properties.local_surface_id_allocation.value_or(
-            viz::LocalSurfaceIdAllocation());
-    EXPECT_TRUE(local_surface_id_allocation2.IsValid());
-    EXPECT_EQ(local_surface_id_allocation1, local_surface_id_allocation2);
+    const IPC::Message* msg = sink_->GetMessageAt(0);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    // Auto-resizve limits sent to the renderer.
+    EXPECT_EQ(gfx::Size(50, 50), visual_properties.min_size_for_auto_resize);
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.max_size_for_auto_resize);
+    // The original LocalSurfaceIdAllocation is sent.
+    EXPECT_TRUE(visual_properties.local_surface_id_allocation.has_value());
+    EXPECT_EQ(host_local_surface_id_allocation,
+              visual_properties.local_surface_id_allocation.value());
   }
 
-  viz::LocalSurfaceIdAllocation local_surface_id_allocation(
+  // A size arrives from the renderer with a changed LocalSurfaceIdAllocation.
+  viz::LocalSurfaceIdAllocation renderer_local_surface_id_allocation(
       viz::LocalSurfaceId(
-          local_surface_id_allocation1.local_surface_id()
+          host_local_surface_id_allocation.local_surface_id()
               .parent_sequence_number(),
-          local_surface_id_allocation1.local_surface_id()
+          host_local_surface_id_allocation.local_surface_id()
                   .child_sequence_number() +
               1,
-          local_surface_id_allocation1.local_surface_id().embed_token()),
+          host_local_surface_id_allocation.local_surface_id().embed_token()),
       base::TimeTicks::Now());
   {
     cc::RenderFrameMetadata metadata;
     metadata.viewport_size_in_pixels = gfx::Size(75, 75);
-    metadata.local_surface_id_allocation = local_surface_id_allocation;
+    metadata.local_surface_id_allocation = renderer_local_surface_id_allocation;
     widget_host_->DidUpdateVisualProperties(metadata);
   }
 
+  // Do a resize in the browser. It does not apply, but VisualProperties are
+  // sent. (Why?)
+  sink_->ClearMessages();
   view_->SetSize(gfx::Size(120, 120));
-  viz::LocalSurfaceIdAllocation local_surface_id_allocation3;
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 3);
+
+  // WidgetMsg_UpdateVisualProperties is sent to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
   {
-    VisualProperties visual_properties =
-        visual_properties_manager_.sent_visual_properties_;
-    EXPECT_EQ("50x50", visual_properties.min_size_for_auto_resize.ToString());
-    EXPECT_EQ("100x100", visual_properties.max_size_for_auto_resize.ToString());
+    const IPC::Message* msg = sink_->GetMessageAt(0);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    // Auto-resizve limits sent to the renderer.
+    EXPECT_EQ(gfx::Size(50, 50), visual_properties.min_size_for_auto_resize);
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.max_size_for_auto_resize);
+    EXPECT_EQ(gfx::Size(120, 120), visual_properties.new_size);
     EXPECT_EQ(1, visual_properties.screen_info.device_scale_factor);
-    local_surface_id_allocation3 =
-        visual_properties.local_surface_id_allocation.value_or(
-            viz::LocalSurfaceIdAllocation());
-    EXPECT_TRUE(local_surface_id_allocation3.IsValid());
-    EXPECT_NE(local_surface_id_allocation1, local_surface_id_allocation3);
-    EXPECT_NE(local_surface_id_allocation2, local_surface_id_allocation3);
+    // A newly generated LocalSurfaceIdAllocation is sent.
+    EXPECT_TRUE(visual_properties.local_surface_id_allocation.has_value());
+    EXPECT_NE(host_local_surface_id_allocation,
+              visual_properties.local_surface_id_allocation.value());
+    EXPECT_NE(renderer_local_surface_id_allocation,
+              visual_properties.local_surface_id_allocation.value());
   }
 }
 
@@ -2812,7 +2856,7 @@
 
 // This test verifies that if the parent is hidden when the child sends a
 // child-allocated viz::LocalSurfaceId, the parent will store it and it will
-// not send a ViewMsg_UpdateVisualProperties back to the child.
+// not send a WidgetMsg_UpdateVisualProperties back to the child.
 TEST_F(RenderWidgetHostViewAuraTest,
        ChildAllocationAcceptedInParentWhileHidden) {
   view_->InitAsChild(nullptr);
@@ -2847,7 +2891,7 @@
   EXPECT_EQ(local_surface_id_allocation2, local_surface_id_allocation3);
 
   EXPECT_FALSE(
-      sink_->GetUniqueMessageMatching(ViewMsg_UpdateVisualProperties::ID));
+      sink_->GetUniqueMessageMatching(WidgetMsg_UpdateVisualProperties::ID));
 }
 
 // This test verifies that when the child and parent both allocate their own
@@ -3130,14 +3174,13 @@
   {
     // 0 is CreatingNew message.
     const IPC::Message* msg = sink_->GetMessageAt(0);
-    EXPECT_EQ(static_cast<uint32_t>(ViewMsg_UpdateVisualProperties::ID),
+    EXPECT_EQ(static_cast<uint32_t>(WidgetMsg_UpdateVisualProperties::ID),
               msg->type());
-    ViewMsg_UpdateVisualProperties::Param params;
-    ViewMsg_UpdateVisualProperties::Read(msg, &params);
-    EXPECT_EQ(
-        "0,0 800x600",
-        std::get<0>(params).screen_info.available_rect.ToString());
-    EXPECT_EQ("800x600", std::get<0>(params).new_size.ToString());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    EXPECT_EQ(gfx::Rect(800, 600),
+              std::get<0>(params).screen_info.available_rect);
+    EXPECT_EQ(gfx::Size(800, 600), std::get<0>(params).new_size);
     // Resizes are blocked until we swapped a frame of the correct size, and
     // we've committed it.
     view_->SubmitCompositorFrame(
@@ -3155,17 +3198,16 @@
   // Make sure the corrent screen size is set along in the resize
   // request when the screen size has changed.
   aura_test_helper_->test_screen()->SetUIScale(0.5);
-  EXPECT_EQ(1u, sink_->message_count());
+  ASSERT_EQ(1u, sink_->message_count());
   {
     const IPC::Message* msg = sink_->GetMessageAt(0);
-    EXPECT_EQ(static_cast<uint32_t>(ViewMsg_UpdateVisualProperties::ID),
+    EXPECT_EQ(static_cast<uint32_t>(WidgetMsg_UpdateVisualProperties::ID),
               msg->type());
-    ViewMsg_UpdateVisualProperties::Param params;
-    ViewMsg_UpdateVisualProperties::Read(msg, &params);
-    EXPECT_EQ(
-        "0,0 1600x1200",
-        std::get<0>(params).screen_info.available_rect.ToString());
-    EXPECT_EQ("1600x1200", std::get<0>(params).new_size.ToString());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    EXPECT_EQ(gfx::Rect(1600, 1200),
+              std::get<0>(params).screen_info.available_rect);
+    EXPECT_EQ(gfx::Size(1600, 1200), std::get<0>(params).new_size);
     view_->SubmitCompositorFrame(
         kArbitraryLocalSurfaceId,
         MakeDelegatedFrame(1.f, std::get<0>(params).new_size,
@@ -3177,26 +3219,33 @@
 }
 
 TEST_F(RenderWidgetHostViewAuraTest, ZeroSizeStillGetsLocalSurfaceId) {
-  gfx::Size frame_size;
   parent_local_surface_id_allocator_.GenerateId();
   viz::LocalSurfaceId local_surface_id =
       parent_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()
           .local_surface_id();
 
   view_->InitAsChild(nullptr);
+
+  // Set an empty size.
   aura::client::ParentWindowWithContext(
       view_->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
       gfx::Rect());
-  view_->SetSize(frame_size);
-  view_->Show();
 
+  // It's set on the layer.
   ui::Layer* parent_layer = view_->GetNativeView()->layer();
   EXPECT_EQ(gfx::Rect(), parent_layer->bounds());
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 1);
+
+  // Update to the renderer.
+  ASSERT_EQ(2u, sink_->message_count());
   {
-    VisualProperties visual_properties =
-        visual_properties_manager_.sent_visual_properties_;
-    EXPECT_EQ(frame_size.ToString(), visual_properties.new_size.ToString());
+    const IPC::Message* msg = sink_->GetMessageAt(1);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    // Empty size is sent.
+    EXPECT_EQ(gfx::Size(), visual_properties.new_size);
+    // A LocalSurfaceIdAllocation is sent too.
     ASSERT_TRUE(visual_properties.local_surface_id_allocation.has_value());
     EXPECT_TRUE(visual_properties.local_surface_id_allocation->IsValid());
   }
@@ -3290,14 +3339,14 @@
   // Resize renderer, should produce a Resize message
   view_->SetSize(size2);
   EXPECT_EQ(size2.ToString(), view_->GetRequestedRendererSize().ToString());
-  EXPECT_EQ(1u, sink_->message_count());
+  ASSERT_EQ(1u, sink_->message_count());
   {
     const IPC::Message* msg = sink_->GetMessageAt(0);
-    EXPECT_EQ(static_cast<uint32_t>(ViewMsg_UpdateVisualProperties::ID),
+    EXPECT_EQ(static_cast<uint32_t>(WidgetMsg_UpdateVisualProperties::ID),
               msg->type());
-    ViewMsg_UpdateVisualProperties::Param params;
-    ViewMsg_UpdateVisualProperties::Read(msg, &params);
-    EXPECT_EQ(size2.ToString(), std::get<0>(params).new_size.ToString());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    EXPECT_EQ(size2, std::get<0>(params).new_size);
   }
   // Send resize ack to observe new Resize messages.
   {
@@ -3349,11 +3398,11 @@
   for (uint32_t i = 0; i < sink_->message_count(); ++i) {
     const IPC::Message* msg = sink_->GetMessageAt(i);
     switch (msg->type()) {
-      case ViewMsg_UpdateVisualProperties::ID: {
+      case WidgetMsg_UpdateVisualProperties::ID: {
         EXPECT_FALSE(has_resize);
-        ViewMsg_UpdateVisualProperties::Param params;
-        ViewMsg_UpdateVisualProperties::Read(msg, &params);
-        EXPECT_EQ(size3.ToString(), std::get<0>(params).new_size.ToString());
+        WidgetMsg_UpdateVisualProperties::Param params;
+        WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+        EXPECT_EQ(size3, std::get<0>(params).new_size);
         has_resize = true;
         break;
       }
@@ -3705,22 +3754,43 @@
       view_->GetNativeView(),
       parent_view_->GetNativeView()->GetRootWindow(),
       gfx::Rect());
+
+  sink_->ClearMessages();
   view_->SetSize(view_rect.size());
   view_->Show();
 
   // Defaults to full height of the view.
   EXPECT_EQ(100, view_->GetVisibleViewportSize().height());
 
-  widget_host_->ResetSentVisualProperties();
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 2);
-  view_->SetInsets(gfx::Insets(0, 0, 40, 0));
+  // Update to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
+  {
+    const IPC::Message* msg = sink_->GetMessageAt(0);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size);
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.visible_viewport_size);
+  }
 
+  widget_host_->ResetSentVisualProperties();
+
+  sink_->ClearMessages();
+  view_->SetInsets(gfx::Insets(0, 0, 40, 0));
   EXPECT_EQ(60, view_->GetVisibleViewportSize().height());
 
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 3);
-  VisualProperties visual_properties =
-      visual_properties_manager_.sent_visual_properties_;
-  EXPECT_EQ(60, visual_properties.visible_viewport_size.height());
+  // Update to the renderer has the inset size.
+  ASSERT_EQ(1u, sink_->message_count());
+  {
+    const IPC::Message* msg = sink_->GetMessageAt(0);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties visual_properties = std::get<0>(params);
+    EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size);
+    EXPECT_EQ(gfx::Size(100, 60), visual_properties.visible_viewport_size);
+  }
 }
 
 // Ensures that touch event positions are never truncated to integers.
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
index 930d301..8e38b301 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
@@ -28,7 +28,6 @@
 #include "content/browser/renderer_host/frame_connector_delegate.h"
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/browser/renderer_host/visual_properties_manager.h"
 #include "content/common/frame_visual_properties.h"
 #include "content/common/view_messages.h"
 #include "content/common/widget_messages.h"
@@ -51,21 +50,6 @@
 namespace content {
 namespace {
 
-class MockRenderWidgetHostVisualPropertiesManager
-    : public VisualPropertiesManager {
- public:
-  MockRenderWidgetHostVisualPropertiesManager()
-      : VisualPropertiesManager(nullptr) {}
-  void SendVisualProperties(const VisualProperties& visual_properties,
-                            int widget_routing_id) override {
-    ++sent_visual_properties_count_;
-    sent_visual_properties_ = visual_properties;
-  }
-
-  int sent_visual_properties_count_ = 0;
-  VisualProperties sent_visual_properties_;
-};
-
 const viz::LocalSurfaceId kArbitraryLocalSurfaceId(
     1,
     base::UnguessableToken::Deserialize(2, 3));
@@ -132,14 +116,15 @@
 
     MockRenderProcessHost* process_host =
         new MockRenderProcessHost(browser_context_.get());
+
     int32_t routing_id = process_host->GetNextRoutingID();
+    sink_ = &process_host->sink();
+
     mojo::PendingRemote<mojom::Widget> widget;
     widget_impl_ = std::make_unique<MockWidgetImpl>(
         widget.InitWithNewPipeAndPassReceiver());
     widget_host_ = new RenderWidgetHostImpl(
         &delegate_, process_host, routing_id, std::move(widget), false);
-    widget_host_->BindVisualPropertiesManager(
-        visual_properties_manager_.GetWeakPtr());
     view_ = RenderWidgetHostViewChildFrame::Create(widget_host_);
 
     test_frame_connector_ =
@@ -159,6 +144,7 @@
   }
 
   void TearDown() override {
+    sink_ = nullptr;
     if (view_)
       view_->Destroy();
     delete widget_host_;
@@ -186,6 +172,7 @@
   BrowserTaskEnvironment task_environment_;
 
   std::unique_ptr<BrowserContext> browser_context_;
+  IPC::TestSink* sink_ = nullptr;
   MockRenderWidgetHostDelegate delegate_;
 
   // Tests should set these to NULL if they've already triggered their
@@ -196,7 +183,6 @@
   MockFrameConnectorDelegate* test_frame_connector_;
   std::unique_ptr<FakeRendererCompositorFrameSink>
       renderer_compositor_frame_sink_;
-  MockRenderWidgetHostVisualPropertiesManager visual_properties_manager_;
 
  private:
   mojo::Remote<viz::mojom::CompositorFrameSinkClient>
@@ -307,26 +293,33 @@
       allocator.GetCurrentLocalSurfaceIdAllocation();
   constexpr viz::FrameSinkId frame_sink_id(1, 1);
 
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 0);
   FrameVisualProperties visual_properties;
   visual_properties.screen_space_rect = screen_space_rect;
   visual_properties.compositor_viewport = compositor_viewport_pixel_rect;
   visual_properties.local_frame_size = compositor_viewport_pixel_rect.size();
   visual_properties.capture_sequence_number = 123u;
   visual_properties.local_surface_id_allocation = local_surface_id_allocation;
+
+  sink_->ClearMessages();
   test_frame_connector_->SynchronizeVisualProperties(frame_sink_id,
                                                      visual_properties);
 
-  EXPECT_EQ(visual_properties_manager_.sent_visual_properties_count_, 1);
+  // Update to the renderer.
+  ASSERT_EQ(1u, sink_->message_count());
+  {
+    const IPC::Message* msg = sink_->GetMessageAt(0);
+    ASSERT_EQ(WidgetMsg_UpdateVisualProperties::ID, msg->type());
+    WidgetMsg_UpdateVisualProperties::Param params;
+    WidgetMsg_UpdateVisualProperties::Read(msg, &params);
+    VisualProperties sent_visual_properties = std::get<0>(params);
 
-  VisualProperties sent_visual_properties =
-      visual_properties_manager_.sent_visual_properties_;
-  EXPECT_EQ(compositor_viewport_pixel_rect,
-            sent_visual_properties.compositor_viewport_pixel_rect);
-  EXPECT_EQ(screen_space_rect.size(), sent_visual_properties.new_size);
-  EXPECT_EQ(local_surface_id_allocation,
-            sent_visual_properties.local_surface_id_allocation);
-  EXPECT_EQ(123u, sent_visual_properties.capture_sequence_number);
+    EXPECT_EQ(compositor_viewport_pixel_rect,
+              sent_visual_properties.compositor_viewport_pixel_rect);
+    EXPECT_EQ(screen_space_rect.size(), sent_visual_properties.new_size);
+    EXPECT_EQ(local_surface_id_allocation,
+              sent_visual_properties.local_surface_id_allocation);
+    EXPECT_EQ(123u, sent_visual_properties.capture_sequence_number);
+  }
 }
 
 // Test that when we have a gesture scroll sequence that is not consumed by the
diff --git a/content/browser/renderer_host/visual_properties_manager.cc b/content/browser/renderer_host/visual_properties_manager.cc
deleted file mode 100644
index 2df6a12a..0000000
--- a/content/browser/renderer_host/visual_properties_manager.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/renderer_host/visual_properties_manager.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/common/view_messages.h"
-#include "content/common/visual_properties.h"
-
-namespace content {
-
-VisualPropertiesManager::VisualPropertiesManager(
-    RenderViewHostImpl* render_view_host)
-    : render_view_host_(render_view_host) {}
-VisualPropertiesManager::~VisualPropertiesManager() = default;
-
-void VisualPropertiesManager::SendVisualProperties(
-    const VisualProperties& visual_properties,
-    int widget_routing_id) {
-  render_view_host_->Send(new ViewMsg_UpdateVisualProperties(
-      render_view_host_->GetRoutingID(), visual_properties, widget_routing_id));
-}
-
-}  // namespace content
diff --git a/content/browser/renderer_host/visual_properties_manager.h b/content/browser/renderer_host/visual_properties_manager.h
deleted file mode 100644
index f302359..0000000
--- a/content/browser/renderer_host/visual_properties_manager.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_RENDERER_HOST_VISUAL_PROPERTIES_MANAGER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_VISUAL_PROPERTIES_MANAGER_H_
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "content/common/content_export.h"
-
-namespace content {
-
-class RenderViewHostImpl;
-struct VisualProperties;
-
-// This class manages page visual properties in the browser. Visual properties
-// can be page-specific or widget-specific. Right now, this state is stored in
-// RenderWidgetHost. Eventually, we want page-specific state to be stored in
-// this class instead.
-class CONTENT_EXPORT VisualPropertiesManager {
- public:
-  VisualPropertiesManager(RenderViewHostImpl* render_view_host);
-  ~VisualPropertiesManager();
-
-  // Sends both Page and Widget visual properties to the renderer hosting the
-  // RenderView. The widget_routing_id is used by the renderer to forward Widget
-  // visual properties to the Widget.
-  // virtual for testing.
-  virtual void SendVisualProperties(const VisualProperties& visual_properties,
-                                    int widget_routing_id);
-
-  base::WeakPtr<VisualPropertiesManager> GetWeakPtr() {
-    return weak_factory_.GetWeakPtr();
-  }
-
- private:
-  RenderViewHostImpl* render_view_host_ = nullptr;
-
-  // TODO(https://crbug.com/1004009): We should not need a weak factory because
-  // RenderWidgetHostImpl should not outlive RenderViewHostImpl.
-  // Instances of this class are owned by instances of RenderViewHostImpl. Due
-  // to unclear ownership semantics, it's not clear if a RenderWidgetHostImpl
-  // can outlive its RenderViewHostImpl. We assume that it can and thus pass a
-  // weak ptr.
-  base::WeakPtrFactory<VisualPropertiesManager> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(VisualPropertiesManager);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_RENDERER_HOST_VISUAL_PROPERTIES_MANAGER_H_
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index f87a421..dcb687e4 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -79,10 +79,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/blob/blob_utils.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom-test-utils.h"
 #include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
 
 using IPC::IpcSecurityTestUtil;
 
@@ -139,7 +139,7 @@
       wc->GetFrameTree()->root()->current_frame_host(), extension_url,
       url::Origin::Create(foo), false, nullptr, std::string(), Referrer(),
       WindowOpenDisposition::CURRENT_TAB, false, true,
-      blink::WebTriggeringEventInfo::kFromTrustedEvent, std::string(),
+      blink::TriggeringEventInfo::kFromTrustedEvent, std::string(),
       nullptr /* blob_url_loader_factory */);
 
   // Since the navigation above requires a cross-process swap, there will be a
diff --git a/content/browser/service_worker/service_worker_client_utils.cc b/content/browser/service_worker/service_worker_client_utils.cc
index 4b790a4e..a9be97cf 100644
--- a/content/browser/service_worker/service_worker_client_utils.cc
+++ b/content/browser/service_worker/service_worker_client_utils.cc
@@ -309,8 +309,7 @@
           url, Referrer(script_url, network::mojom::ReferrerPolicy::kDefault)),
       WindowOpenDisposition::CURRENT_TAB,
       false /* should_replace_current_entry */, false /* user_gesture */,
-      blink::WebTriggeringEventInfo::kUnknown,
-      std::string() /* href_translate */,
+      blink::TriggeringEventInfo::kUnknown, std::string() /* href_translate */,
       nullptr /* blob_url_loader_factory */);
   new OpenURLObserver(web_contents, frame_tree_node_id, std::move(callback));
 }
diff --git a/content/browser/site_per_process_unload_browsertest.cc b/content/browser/site_per_process_unload_browsertest.cc
index 974b88c..566c4a5 100644
--- a/content/browser/site_per_process_unload_browsertest.cc
+++ b/content/browser/site_per_process_unload_browsertest.cc
@@ -22,7 +22,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
-#include "components/network_session_configurator/common/network_switches.h"
 #include "content/browser/frame_host/cross_process_frame_connector.h"
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/navigation_controller_impl.h"
@@ -1293,32 +1292,6 @@
   delete_B1.WaitUntilDeleted();
 }
 
-// Some tests need an https server because third-party cookies are used, and
-// SameSite=None cookies must be Secure. This is a separate fixture due to
-// kIgnoreCertificateErrors flag.
-class SitePerProcessSSLBrowserTest : public SitePerProcessBrowserTest {
- protected:
-  SitePerProcessSSLBrowserTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    SitePerProcessBrowserTest::SetUpCommandLine(command_line);
-    // This is necessary to use https with arbitrary hostnames.
-    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
-  }
-
-  void SetUpOnMainThread() override {
-    https_server()->AddDefaultHandlers(GetTestDataFilePath());
-    ASSERT_TRUE(https_server()->Start());
-    SitePerProcessBrowserTest::SetUpOnMainThread();
-  }
-
-  net::EmbeddedTestServer* https_server() { return &https_server_; }
-
- private:
-  net::EmbeddedTestServer https_server_;
-};
-
 // Unload handlers should be able to do things that might require for instance
 // the RenderFrameHostImpl to stay alive.
 // - use console.log (handled via RFHI::DidAddMessageToConsole).
@@ -1335,11 +1308,10 @@
 //
 // This test is similar to UnloadHandlersArePowerfulGrandChild, but with a
 // different frame hierarchy.
-IN_PROC_BROWSER_TEST_F(SitePerProcessSSLBrowserTest,
-                       UnloadHandlersArePowerful) {
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, UnloadHandlersArePowerful) {
   // Navigate to a page hosting a cross-origin frame.
-  GURL url =
-      https_server()->GetURL("a.com", "/cross_site_iframe_factory.html?a(b)");
+  GURL url = embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)");
   EXPECT_TRUE(NavigateToURL(shell(), url));
 
   RenderFrameHostImpl* A1 = web_contents()->GetMainFrame();
@@ -1368,8 +1340,7 @@
 
       // As a sanity check, test that RFHI-independent things also work fine.
       localStorage.localstorage_test_key = 'localstorage_test_value';
-      document.cookie = 'cookie_test_key=' +
-                        'cookie_test_value; SameSite=none; Secure';
+      document.cookie = 'cookie_test_key=' + 'cookie_test_value';
     });
   )"));
 
@@ -1381,7 +1352,7 @@
     RenderFrameDeletedObserver B2_deleted(B2);
 
     // Navigate
-    GURL away_url(https_server()->GetURL("a.com", "/title1.html"));
+    GURL away_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
     ASSERT_TRUE(ExecJs(A1, JsReplace("location = $1", away_url)));
 
     // Observers must be reached.
@@ -1426,11 +1397,11 @@
 //
 // This test is similar to UnloadHandlersArePowerful, but with a different frame
 // hierarchy.
-IN_PROC_BROWSER_TEST_F(SitePerProcessSSLBrowserTest,
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
                        UnloadHandlersArePowerfulGrandChild) {
   // Navigate to a page hosting a cross-origin frame.
-  GURL url = https_server()->GetURL("a.com",
-                                    "/cross_site_iframe_factory.html?a(b(c))");
+  GURL url = embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(c))");
   EXPECT_TRUE(NavigateToURL(shell(), url));
 
   RenderFrameHostImpl* A1 = web_contents()->GetMainFrame();
@@ -1461,8 +1432,7 @@
 
       // As a sanity check, test that RFHI-independent things also work fine.
       localStorage.localstorage_test_key = 'localstorage_test_value';
-      document.cookie = 'cookie_test_key=' +
-                        'cookie_test_value; SameSite=none; Secure';
+      document.cookie = 'cookie_test_key=' + 'cookie_test_value';
     });
   )"));
 
@@ -1475,7 +1445,7 @@
     RenderFrameDeletedObserver C3_deleted(C3);
 
     // Navigate
-    GURL away_url(https_server()->GetURL("a.com", "/title1.html"));
+    GURL away_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
     ASSERT_TRUE(ExecJs(A1, JsReplace("location = $1", away_url)));
 
     // Observers must be reached.
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index b92e423..812cfe34 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -595,8 +595,6 @@
       audio_stream_monitor_(this),
       media_web_contents_observer_(
           std::make_unique<MediaWebContentsObserver>(this)),
-      media_device_group_id_salt_base_(
-          BrowserContext::CreateRandomMediaDeviceIDSalt()),
 #if !defined(OS_ANDROID)
       page_scale_factor_is_one_(true),
 #endif  // !defined(OS_ANDROID)
@@ -1286,10 +1284,8 @@
 
   renderer_preferences_.user_agent_override = override;
 
-  // Send the new override string to the renderer.
-  RenderViewHost* host = GetRenderViewHost();
-  if (host)
-    host->SyncRendererPrefs();
+  // Send the new override string to all renderers in the current page.
+  SyncRendererPrefs();
 
   // Reload the page if a load is currently in progress to avoid having
   // different parts of the page loaded using different user agents.
@@ -1339,10 +1335,6 @@
   return page_importance_signals_;
 }
 
-const std::string& WebContentsImpl::GetMediaDeviceGroupIDSaltBase() const {
-  return media_device_group_id_salt_base_;
-}
-
 #if defined(OS_ANDROID)
 
 void WebContentsImpl::SetDisplayCutoutSafeArea(gfx::Insets insets) {
@@ -2022,6 +2014,14 @@
     render_view_host->OnWebkitPreferencesChanged();
 }
 
+void WebContentsImpl::SyncRendererPrefs() {
+  blink::mojom::RendererPreferences renderer_preferences =
+      GetRendererPrefs(GetBrowserContext());
+  RenderViewHostImpl::GetPlatformSpecificPrefs(&renderer_preferences);
+  SendPageMessage(
+      new PageMsg_SetRendererPrefs(MSG_ROUTING_NONE, renderer_preferences));
+}
+
 void WebContentsImpl::OnCookiesRead(const GURL& url,
                                     const GURL& first_party_url,
                                     const net::CookieList& cookie_list,
@@ -3030,9 +3030,6 @@
 
   RenderWidgetHostImpl* widget_host = new RenderWidgetHostImpl(
       this, process, route_id, std::move(widget), IsHidden());
-  widget_host->BindVisualPropertiesManager(
-      render_view_host->GetVisualPropertiesManager());
-
   RenderWidgetHostViewBase* widget_view =
       static_cast<RenderWidgetHostViewBase*>(
           view_->CreateViewForChildWidget(widget_host));
@@ -7240,13 +7237,6 @@
   return nullptr;
 }
 
-bool WebContentsImpl::IsFrameLowPriority(
-    const RenderFrameHost* render_frame_host) {
-  if (!delegate_)
-    return false;
-  return delegate_->IsFrameLowPriority(this, render_frame_host);
-}
-
 void WebContentsImpl::UpdateWebContentsVisibility(Visibility visibility) {
   // Occlusion is disabled when |features::kWebContentsOcclusion| is disabled
   // (for power and speed impact assessment) or when
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 912e2ed..71299edb 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -383,6 +383,7 @@
   std::vector<WebContents*> GetInnerWebContents() override;
   void DidChangeVisibleSecurityState() override;
   void NotifyPreferencesChanged() override;
+  void SyncRendererPrefs() override;
   void OnCookiesRead(const GURL& url,
                      const GURL& first_party_url,
                      const net::CookieList& cookie_list,
@@ -639,7 +640,6 @@
       FrameTreeNode* frame_tree_node) override;
   void OnThemeColorChanged(RenderFrameHostImpl* source,
                            const base::Optional<SkColor>& theme_color) override;
-  bool IsFrameLowPriority(const RenderFrameHost* render_frame_host) override;
 
   // RenderViewHostDelegate ----------------------------------------------------
   RenderViewHostDelegateView* GetDelegateView() override;
@@ -1050,10 +1050,6 @@
                                  const gfx::RectF& active_rect);
 #endif
 
-  // Returns a base salt used to generate group IDs for media-device
-  // enumerations.
-  const std::string& GetMediaDeviceGroupIDSaltBase() const;
-
 #if defined(OS_ANDROID)
   // Called by WebContentsAndroid to send the Display Cutout safe area to
   // DisplayCutoutHostImpl.
@@ -1840,8 +1836,6 @@
 
   PageImportanceSignals page_importance_signals_;
 
-  std::string media_device_group_id_salt_base_;
-
 #if !defined(OS_ANDROID)
   bool page_scale_factor_is_one_;
 #endif  // !defined(OS_ANDROID)
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index d55a9a15..1c1d68c 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <algorithm>
 #include <array>
 #include <utility>
 #include <vector>
@@ -30,7 +31,9 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/web_contents/web_contents_view.h"
 #include "content/common/frame_messages.h"
+#include "content/common/page_messages.h"
 #include "content/common/unfreezable_frame_messages.h"
+#include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/file_select_listener.h"
 #include "content/public/browser/invalidate_type.h"
@@ -1052,11 +1055,13 @@
                               const GURL& worker) {
     DCHECK(url.is_valid());
 
-    // Do a cross-process navigation to clear the in-memory cache.
-    // We assume that we don't start this call from "chrome://gpu", as
-    // otherwise it won't be a cross-process navigation. We are relying
-    // on this navigation to discard the old process.
-    EXPECT_TRUE(NavigateToURL(shell(), GetWebUIURL("gpu")));
+    // Clear the in-memory cache held by the current process:
+    // 1) Prevent the old page from entering the back-forward cache. Otherwise
+    //    the old process will be kept alive, because it is still being used.
+    // 2) Navigate to a WebUI URL, which uses a new process.
+    BackForwardCache::DisableForRenderFrameHost(
+        shell()->web_contents()->GetMainFrame(), "test");
+    EXPECT_TRUE(NavigateToURL(shell(), GetWebUIURL(kChromeUIGpuHost)));
 
     // Observe network requests.
     ResourceLoadObserver observer(shell());
@@ -3353,6 +3358,115 @@
   SetBrowserClientForTesting(old_client);
 }
 
+namespace {
+
+class OutgoingSetRendererPrefsIPCWatcher {
+ public:
+  OutgoingSetRendererPrefsIPCWatcher(RenderProcessHostImpl* rph)
+      : rph_(rph), outgoing_message_seen_(false) {
+    rph_->SetIpcSendWatcherForTesting(
+        base::BindRepeating(&OutgoingSetRendererPrefsIPCWatcher::OnMessage,
+                            base::Unretained(this)));
+  }
+  ~OutgoingSetRendererPrefsIPCWatcher() {
+    rph_->SetIpcSendWatcherForTesting(
+        base::RepeatingCallback<void(const IPC::Message& msg)>());
+  }
+
+  void WaitForIPC() {
+    if (outgoing_message_seen_)
+      return;
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
+    run_loop_.reset();
+  }
+
+  const blink::mojom::RendererPreferences& renderer_preferences() const {
+    return renderer_preferences_;
+  }
+
+ private:
+  void OnMessage(const IPC::Message& message) {
+    IPC_BEGIN_MESSAGE_MAP(OutgoingSetRendererPrefsIPCWatcher, message)
+      IPC_MESSAGE_HANDLER(PageMsg_SetRendererPrefs, OnSetRendererPrefs)
+    IPC_END_MESSAGE_MAP()
+  }
+
+  void OnSetRendererPrefs(
+      const blink::mojom::RendererPreferences& renderer_prefs) {
+    outgoing_message_seen_ = true;
+    renderer_preferences_ = renderer_prefs;
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
+  RenderProcessHostImpl* rph_;
+  bool outgoing_message_seen_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+  blink::mojom::RendererPreferences renderer_preferences_;
+};
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, SyncRendererPrefs) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+
+  // Navigate to a site with two iframes in different origins.
+  GURL url = embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b,c)");
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+
+  // Retrieve an arbitrary renderer preference.
+  blink::mojom::RendererPreferences* renderer_preferences =
+      web_contents->GetMutableRendererPrefs();
+  const bool use_custom_colors_old = renderer_preferences->use_custom_colors;
+
+  // Retrieve all unique render process hosts.
+  std::vector<RenderProcessHostImpl*> render_process_hosts;
+  for (FrameTreeNode* frame_tree_node : web_contents->GetFrameTree()->Nodes()) {
+    RenderProcessHostImpl* render_process_host =
+        static_cast<RenderProcessHostImpl*>(
+            frame_tree_node->current_frame_host()->GetProcess());
+    ASSERT_NE(nullptr, render_process_host);
+    DLOG(INFO) << "render_process_host=" << render_process_host;
+
+    // It's possible (Android e.g.) for frame hosts to share a
+    // RenderProcessHost.
+    if (std::find(render_process_hosts.begin(), render_process_hosts.end(),
+                  render_process_host) == render_process_hosts.end()) {
+      render_process_hosts.push_back(render_process_host);
+    }
+  }
+
+  // Set up watchers for PageMsg_SetRendererPrefs message being sent from unique
+  // render process hosts.
+  std::vector<std::unique_ptr<OutgoingSetRendererPrefsIPCWatcher>> ipc_watchers;
+  for (auto* render_process_host : render_process_hosts) {
+    ipc_watchers.push_back(std::make_unique<OutgoingSetRendererPrefsIPCWatcher>(
+        render_process_host));
+
+    // Make sure the IPC watchers have the same default value for the arbitrary
+    // preference.
+    EXPECT_EQ(use_custom_colors_old,
+              ipc_watchers.back()->renderer_preferences().use_custom_colors);
+  }
+
+  // Change the arbitrary renderer preference.
+  const bool use_custom_colors_new = !use_custom_colors_old;
+  renderer_preferences->use_custom_colors = use_custom_colors_new;
+  web_contents->SyncRendererPrefs();
+
+  // Ensure IPC is sent to each frame.
+  for (auto& ipc_watcher : ipc_watchers) {
+    ipc_watcher->WaitForIPC();
+    EXPECT_EQ(use_custom_colors_new,
+              ipc_watcher->renderer_preferences().use_custom_colors);
+  }
+
+  renderer_preferences->use_custom_colors = use_custom_colors_old;
+}
+
 IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, SetPageFrozen) {
   EXPECT_TRUE(embedded_test_server()->Start());
 
diff --git a/content/browser/web_package/bundled_exchanges_browsertest.cc b/content/browser/web_package/bundled_exchanges_browsertest.cc
index c21cd8f..c123472 100644
--- a/content/browser/web_package/bundled_exchanges_browsertest.cc
+++ b/content/browser/web_package/bundled_exchanges_browsertest.cc
@@ -363,14 +363,23 @@
   if (!original_client_)
     return;
 
+  WebContents* web_contents = shell()->web_contents();
+  ConsoleObserverDelegate console_delegate(web_contents, "*");
+  web_contents->SetDelegate(&console_delegate);
   base::RunLoop run_loop;
-  FinishNavigationObserver finish_navigation_observer(shell()->web_contents(),
+  FinishNavigationObserver finish_navigation_observer(web_contents,
                                                       run_loop.QuitClosure());
-  EXPECT_FALSE(NavigateToURL(shell()->web_contents(), test_data_url()));
+  EXPECT_FALSE(NavigateToURL(web_contents, test_data_url()));
   run_loop.Run();
   ASSERT_TRUE(finish_navigation_observer.error_code());
   EXPECT_EQ(net::ERR_INVALID_BUNDLED_EXCHANGES,
             *finish_navigation_observer.error_code());
+  if (console_delegate.messages().empty())
+    console_delegate.Wait();
+
+  EXPECT_FALSE(console_delegate.messages().empty());
+  EXPECT_EQ("Failed to read metadata of Web Bundle file: FILE_ERROR_FAILED",
+            console_delegate.message());
 }
 
 class BundledExchangesFileBrowserTest
@@ -381,7 +390,7 @@
   ~BundledExchangesFileBrowserTest() override = default;
 
   void SetUp() override {
-    feature_list_.InitWithFeatures({features::kBundledHTTPExchanges}, {});
+    feature_list_.InitWithFeatures({features::kWebBundles}, {});
     BundledExchangesBrowserTestBase::SetUp();
   }
 
@@ -478,14 +487,83 @@
   const GURL test_data_url =
       GetTestUrlForFile(GetTestDataPath("invalid_bundled_exchanges.wbn"));
 
+  WebContents* web_contents = shell()->web_contents();
+  ConsoleObserverDelegate console_delegate(web_contents, "*");
+  web_contents->SetDelegate(&console_delegate);
+
   base::RunLoop run_loop;
-  FinishNavigationObserver finish_navigation_observer(shell()->web_contents(),
+  FinishNavigationObserver finish_navigation_observer(web_contents,
                                                       run_loop.QuitClosure());
-  EXPECT_FALSE(NavigateToURL(shell()->web_contents(), test_data_url));
+  EXPECT_FALSE(NavigateToURL(web_contents, test_data_url));
   run_loop.Run();
   ASSERT_TRUE(finish_navigation_observer.error_code());
   EXPECT_EQ(net::ERR_INVALID_BUNDLED_EXCHANGES,
             *finish_navigation_observer.error_code());
+
+  if (console_delegate.messages().empty())
+    console_delegate.Wait();
+
+  EXPECT_FALSE(console_delegate.messages().empty());
+  EXPECT_EQ("Failed to read metadata of Web Bundle file: Wrong magic bytes.",
+            console_delegate.message());
+}
+
+IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest,
+                       ResponseParseErrorInMainResource) {
+  const GURL test_data_url = GetTestUrlForFile(
+      GetTestDataPath("broken_bundle_broken_first_entry.wbn"));
+
+  WebContents* web_contents = shell()->web_contents();
+  ConsoleObserverDelegate console_delegate(web_contents, "*");
+  web_contents->SetDelegate(&console_delegate);
+
+  base::RunLoop run_loop;
+  FinishNavigationObserver finish_navigation_observer(web_contents,
+                                                      run_loop.QuitClosure());
+  EXPECT_FALSE(NavigateToURL(web_contents, test_data_url));
+  run_loop.Run();
+  ASSERT_TRUE(finish_navigation_observer.error_code());
+  EXPECT_EQ(net::ERR_INVALID_BUNDLED_EXCHANGES,
+            *finish_navigation_observer.error_code());
+
+  if (console_delegate.messages().empty())
+    console_delegate.Wait();
+
+  EXPECT_FALSE(console_delegate.messages().empty());
+  EXPECT_EQ(
+      "Failed to read response header of Web Bundle file: Response headers map "
+      "must have exactly one pseudo-header, :status.",
+      console_delegate.message());
+}
+
+IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest,
+                       ResponseParseErrorInSubresource) {
+  const GURL test_data_url = GetTestUrlForFile(
+      GetTestDataPath("broken_bundle_broken_script_entry.wbn"));
+  NavigateToBundleAndWaitForReady(
+      test_data_url,
+      bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
+          test_data_url, GURL(kTestPageUrl)));
+
+  WebContents* web_contents = shell()->web_contents();
+  ConsoleObserverDelegate console_delegate(web_contents, "*");
+  web_contents->SetDelegate(&console_delegate);
+
+  ExecuteScriptAndWaitForTitle(R"(
+    const script = document.createElement("script");
+    script.onerror = () => { document.title = "load failed";};
+    script.src = "script.js";
+    document.body.appendChild(script);)",
+                               "load failed");
+
+  if (console_delegate.messages().empty())
+    console_delegate.Wait();
+
+  EXPECT_FALSE(console_delegate.messages().empty());
+  EXPECT_EQ(
+      "Failed to read response header of Web Bundle file: Response headers map "
+      "must have exactly one pseudo-header, :status.",
+      console_delegate.message());
 }
 
 IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest, NoLocalFileScheme) {
diff --git a/content/browser/web_package/bundled_exchanges_handle.cc b/content/browser/web_package/bundled_exchanges_handle.cc
index 42fb878..b810d8e7 100644
--- a/content/browser/web_package/bundled_exchanges_handle.cc
+++ b/content/browser/web_package/bundled_exchanges_handle.cc
@@ -18,6 +18,8 @@
 #include "content/browser/web_package/bundled_exchanges_utils.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -64,6 +66,19 @@
     "by Blink, but based on a user initiated navigation."
   )");
 
+void AddMetadataParseErrorMessageToConsole(
+    int frame_tree_node_id,
+    const data_decoder::mojom::BundleMetadataParseErrorPtr& metadata_error) {
+  WebContents* web_contents =
+      WebContents::FromFrameTreeNodeId(frame_tree_node_id);
+  if (!web_contents)
+    return;
+  web_contents->GetMainFrame()->AddMessageToConsole(
+      blink::mojom::ConsoleMessageLevel::kError,
+      std::string("Failed to read metadata of Web Bundle file: ") +
+          metadata_error->message);
+}
+
 // A class to provide a network::mojom::URLLoader interface to redirect a
 // request to the BundledExchanges to the main resource url.
 class PrimaryURLRedirectLoader final : public network::mojom::URLLoader {
@@ -129,8 +144,10 @@
 //     StartResponse() to create the loader for the main resource.
 class InterceptorForFile final : public NavigationLoaderInterceptor {
  public:
-  explicit InterceptorForFile(DoneCallback done_callback)
-      : done_callback_(std::move(done_callback)) {}
+  explicit InterceptorForFile(DoneCallback done_callback,
+                              int frame_tree_node_id)
+      : done_callback_(std::move(done_callback)),
+        frame_tree_node_id_(frame_tree_node_id) {}
   ~InterceptorForFile() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   }
@@ -188,6 +205,8 @@
       data_decoder::mojom::BundleMetadataParseErrorPtr metadata_error) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     if (metadata_error) {
+      AddMetadataParseErrorMessageToConsole(frame_tree_node_id_,
+                                            metadata_error);
       forwarding_client_->OnComplete(network::URLLoaderCompletionStatus(
           net::ERR_INVALID_BUNDLED_EXCHANGES));
       forwarding_client_.reset();
@@ -195,8 +214,8 @@
     }
     DCHECK(reader_);
     primary_url_ = reader_->GetPrimaryURL();
-    url_loader_factory_ =
-        std::make_unique<BundledExchangesURLLoaderFactory>(std::move(reader_));
+    url_loader_factory_ = std::make_unique<BundledExchangesURLLoaderFactory>(
+        std::move(reader_), frame_tree_node_id_);
 
     const GURL new_url =
         bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
@@ -220,6 +239,7 @@
   }
 
   DoneCallback done_callback_;
+  const int frame_tree_node_id_;
   scoped_refptr<BundledExchangesReader> reader_;
   GURL primary_url_;
   std::unique_ptr<BundledExchangesURLLoaderFactory> url_loader_factory_;
@@ -252,10 +272,12 @@
 class InterceptorForTrustableFile final : public NavigationLoaderInterceptor {
  public:
   InterceptorForTrustableFile(std::unique_ptr<BundledExchangesSource> source,
-                              DoneCallback done_callback)
+                              DoneCallback done_callback,
+                              int frame_tree_node_id)
       : source_(std::move(source)),
         reader_(base::MakeRefCounted<BundledExchangesReader>(source_->Clone())),
-        done_callback_(std::move(done_callback)) {
+        done_callback_(std::move(done_callback)),
+        frame_tree_node_id_(frame_tree_node_id) {
     reader_->ReadMetadata(
         base::BindOnce(&InterceptorForTrustableFile::OnMetadataReady,
                        weak_factory_.GetWeakPtr()));
@@ -322,11 +344,12 @@
     DCHECK(!url_loader_factory_);
 
     if (error) {
+      AddMetadataParseErrorMessageToConsole(frame_tree_node_id_, error);
       metadata_error_ = std::move(error);
     } else {
       primary_url_ = reader_->GetPrimaryURL();
       url_loader_factory_ = std::make_unique<BundledExchangesURLLoaderFactory>(
-          std::move(reader_));
+          std::move(reader_), frame_tree_node_id_);
     }
 
     if (pending_request_) {
@@ -339,6 +362,7 @@
   std::unique_ptr<BundledExchangesSource> source_;
   scoped_refptr<BundledExchangesReader> reader_;
   DoneCallback done_callback_;
+  const int frame_tree_node_id_;
 
   network::ResourceRequest pending_resource_request_;
   network::mojom::URLLoaderRequest pending_request_;
@@ -371,9 +395,11 @@
  public:
   InterceptorForTrackedNavigationFromTrustableFile(
       scoped_refptr<BundledExchangesReader> reader,
-      DoneCallback done_callback)
+      DoneCallback done_callback,
+      int frame_tree_node_id)
       : url_loader_factory_(std::make_unique<BundledExchangesURLLoaderFactory>(
-            std::move(reader))),
+            std::move(reader),
+            frame_tree_node_id)),
         done_callback_(std::move(done_callback)) {}
   ~InterceptorForTrackedNavigationFromTrustableFile() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -432,9 +458,11 @@
  public:
   InterceptorForTrackedNavigationFromFile(
       scoped_refptr<BundledExchangesReader> reader,
-      DoneCallback done_callback)
+      DoneCallback done_callback,
+      int frame_tree_node_id)
       : url_loader_factory_(std::make_unique<BundledExchangesURLLoaderFactory>(
-            std::move(reader))),
+            std::move(reader),
+            frame_tree_node_id)),
         done_callback_(std::move(done_callback)) {}
   ~InterceptorForTrackedNavigationFromFile() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -514,11 +542,13 @@
  public:
   InterceptorForNavigationInfo(
       std::unique_ptr<BundledExchangesNavigationInfo> navigation_info,
-      DoneCallback done_callback)
+      DoneCallback done_callback,
+      int frame_tree_node_id)
       : reader_(base::MakeRefCounted<BundledExchangesReader>(
             navigation_info->source().Clone())),
         target_inner_url_(navigation_info->target_inner_url()),
-        done_callback_(std::move(done_callback)) {
+        done_callback_(std::move(done_callback)),
+        frame_tree_node_id_(frame_tree_node_id) {
     reader_->ReadMetadata(
         base::BindOnce(&InterceptorForNavigationInfo::OnMetadataReady,
                        weak_factory_.GetWeakPtr()));
@@ -571,10 +601,11 @@
     DCHECK(!url_loader_factory_);
 
     if (error) {
+      AddMetadataParseErrorMessageToConsole(frame_tree_node_id_, error);
       metadata_error_ = std::move(error);
     } else {
       url_loader_factory_ = std::make_unique<BundledExchangesURLLoaderFactory>(
-          std::move(reader_));
+          std::move(reader_), frame_tree_node_id_);
     }
 
     if (pending_request_) {
@@ -587,6 +618,7 @@
   scoped_refptr<BundledExchangesReader> reader_;
   const GURL target_inner_url_;
   DoneCallback done_callback_;
+  const int frame_tree_node_id_;
 
   network::ResourceRequest pending_resource_request_;
   network::mojom::URLLoaderRequest pending_request_;
@@ -605,32 +637,36 @@
 }  // namespace
 
 // static
-std::unique_ptr<BundledExchangesHandle>
-BundledExchangesHandle::CreateForFile() {
+std::unique_ptr<BundledExchangesHandle> BundledExchangesHandle::CreateForFile(
+    int frame_tree_node_id) {
   auto handle = base::WrapUnique(new BundledExchangesHandle());
   handle->SetInterceptor(std::make_unique<InterceptorForFile>(
       base::BindOnce(&BundledExchangesHandle::OnBundledExchangesFileLoaded,
-                     handle->weak_factory_.GetWeakPtr())));
+                     handle->weak_factory_.GetWeakPtr()),
+      frame_tree_node_id));
   return handle;
 }
 
 // static
 std::unique_ptr<BundledExchangesHandle>
 BundledExchangesHandle::CreateForTrustableFile(
-    std::unique_ptr<BundledExchangesSource> source) {
+    std::unique_ptr<BundledExchangesSource> source,
+    int frame_tree_node_id) {
   DCHECK(source->is_trusted());
   auto handle = base::WrapUnique(new BundledExchangesHandle());
   handle->SetInterceptor(std::make_unique<InterceptorForTrustableFile>(
       std::move(source),
       base::BindOnce(&BundledExchangesHandle::OnBundledExchangesFileLoaded,
-                     handle->weak_factory_.GetWeakPtr())));
+                     handle->weak_factory_.GetWeakPtr()),
+      frame_tree_node_id));
   return handle;
 }
 
 // static
 std::unique_ptr<BundledExchangesHandle>
 BundledExchangesHandle::CreateForTrackedNavigation(
-    scoped_refptr<BundledExchangesReader> reader) {
+    scoped_refptr<BundledExchangesReader> reader,
+    int frame_tree_node_id) {
   auto handle = base::WrapUnique(new BundledExchangesHandle());
   if (reader->source().is_trusted()) {
     handle->SetInterceptor(
@@ -638,14 +674,16 @@
             std::move(reader),
             base::BindOnce(
                 &BundledExchangesHandle::OnBundledExchangesFileLoaded,
-                handle->weak_factory_.GetWeakPtr())));
+                handle->weak_factory_.GetWeakPtr()),
+            frame_tree_node_id));
   } else {
     handle->SetInterceptor(
         std::make_unique<InterceptorForTrackedNavigationFromFile>(
             std::move(reader),
             base::BindOnce(
                 &BundledExchangesHandle::OnBundledExchangesFileLoaded,
-                handle->weak_factory_.GetWeakPtr())));
+                handle->weak_factory_.GetWeakPtr()),
+            frame_tree_node_id));
   }
   return handle;
 }
@@ -653,12 +691,14 @@
 // static
 std::unique_ptr<BundledExchangesHandle>
 BundledExchangesHandle::CreateForNavigationInfo(
-    std::unique_ptr<BundledExchangesNavigationInfo> navigation_info) {
+    std::unique_ptr<BundledExchangesNavigationInfo> navigation_info,
+    int frame_tree_node_id) {
   auto handle = base::WrapUnique(new BundledExchangesHandle());
   handle->SetInterceptor(std::make_unique<InterceptorForNavigationInfo>(
       std::move(navigation_info),
       base::BindOnce(&BundledExchangesHandle::OnBundledExchangesFileLoaded,
-                     handle->weak_factory_.GetWeakPtr())));
+                     handle->weak_factory_.GetWeakPtr()),
+      frame_tree_node_id));
   return handle;
 }
 
diff --git a/content/browser/web_package/bundled_exchanges_handle.h b/content/browser/web_package/bundled_exchanges_handle.h
index 51eae599..632ed25 100644
--- a/content/browser/web_package/bundled_exchanges_handle.h
+++ b/content/browser/web_package/bundled_exchanges_handle.h
@@ -27,13 +27,17 @@
 // loading. Running on the UI thread.
 class BundledExchangesHandle {
  public:
-  static std::unique_ptr<BundledExchangesHandle> CreateForFile();
+  static std::unique_ptr<BundledExchangesHandle> CreateForFile(
+      int frame_tree_node_id);
   static std::unique_ptr<BundledExchangesHandle> CreateForTrustableFile(
-      std::unique_ptr<BundledExchangesSource> source);
+      std::unique_ptr<BundledExchangesSource> source,
+      int frame_tree_node_id);
   static std::unique_ptr<BundledExchangesHandle> CreateForTrackedNavigation(
-      scoped_refptr<BundledExchangesReader> reader);
+      scoped_refptr<BundledExchangesReader> reader,
+      int frame_tree_node_id);
   static std::unique_ptr<BundledExchangesHandle> CreateForNavigationInfo(
-      std::unique_ptr<BundledExchangesNavigationInfo> navigation_info);
+      std::unique_ptr<BundledExchangesNavigationInfo> navigation_info,
+      int frame_tree_node_id);
 
   ~BundledExchangesHandle();
 
diff --git a/content/browser/web_package/bundled_exchanges_handle_tracker.cc b/content/browser/web_package/bundled_exchanges_handle_tracker.cc
index 1aa2ca42..bf54b97 100644
--- a/content/browser/web_package/bundled_exchanges_handle_tracker.cc
+++ b/content/browser/web_package/bundled_exchanges_handle_tracker.cc
@@ -23,9 +23,12 @@
 
 std::unique_ptr<BundledExchangesHandle>
 BundledExchangesHandleTracker::MaybeCreateBundledExchangesHandle(
-    const GURL& url) {
-  if (reader_->HasEntry(url))
-    return BundledExchangesHandle::CreateForTrackedNavigation(reader_);
+    const GURL& url,
+    int frame_tree_node_id) {
+  if (reader_->HasEntry(url)) {
+    return BundledExchangesHandle::CreateForTrackedNavigation(
+        reader_, frame_tree_node_id);
+  }
   if (!reader_->source().is_trusted() &&
       url == bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
                  reader_->source().url(), target_inner_url_)) {
@@ -33,7 +36,8 @@
     // reloaded.
     return BundledExchangesHandle::CreateForNavigationInfo(
         std::make_unique<BundledExchangesNavigationInfo>(
-            reader_->source().Clone(), target_inner_url_));
+            reader_->source().Clone(), target_inner_url_),
+        frame_tree_node_id);
   }
   return nullptr;
 }
diff --git a/content/browser/web_package/bundled_exchanges_handle_tracker.h b/content/browser/web_package/bundled_exchanges_handle_tracker.h
index 5ddbece..353af53 100644
--- a/content/browser/web_package/bundled_exchanges_handle_tracker.h
+++ b/content/browser/web_package/bundled_exchanges_handle_tracker.h
@@ -25,7 +25,8 @@
   // if the bundled exchanges file contains the matching response. Otherwise
   // returns null.
   std::unique_ptr<BundledExchangesHandle> MaybeCreateBundledExchangesHandle(
-      const GURL& url);
+      const GURL& url,
+      int frame_tree_node_id);
 
  private:
   scoped_refptr<BundledExchangesReader> reader_;
diff --git a/content/browser/web_package/bundled_exchanges_reader.cc b/content/browser/web_package/bundled_exchanges_reader.cc
index d7dddf6..babc558 100644
--- a/content/browser/web_package/bundled_exchanges_reader.cc
+++ b/content/browser/web_package/bundled_exchanges_reader.cc
@@ -163,7 +163,13 @@
 
   auto it = entries_.find(net::SimplifyUrlForRequest(url));
   if (it == entries_.end() || it->second->response_locations.empty()) {
-    PostTask(FROM_HERE, base::BindOnce(std::move(callback), nullptr));
+    PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            std::move(callback), nullptr,
+            data_decoder::mojom::BundleResponseParseError::New(
+                data_decoder::mojom::BundleParseErrorType::kParserInternalError,
+                "Not found in Web Bundle file.")));
     return;
   }
 
@@ -264,8 +270,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(metadata_ready_);
 
-  // TODO(crbug.com/966753): Handle |error|.
-  std::move(callback).Run(std::move(response));
+  std::move(callback).Run(std::move(response), std::move(error));
 }
 
 }  // namespace content
diff --git a/content/browser/web_package/bundled_exchanges_reader.h b/content/browser/web_package/bundled_exchanges_reader.h
index b684b8ea..3a75529 100644
--- a/content/browser/web_package/bundled_exchanges_reader.h
+++ b/content/browser/web_package/bundled_exchanges_reader.h
@@ -49,8 +49,9 @@
   // Gets data_decoder::mojom::BundleResponsePtr for the given |url| that
   // contains response headers and range information for its body.
   // Should be called after ReadMetadata finishes.
-  using ResponseCallback =
-      base::OnceCallback<void(data_decoder::mojom::BundleResponsePtr)>;
+  using ResponseCallback = base::OnceCallback<void(
+      data_decoder::mojom::BundleResponsePtr,
+      data_decoder::mojom::BundleResponseParseErrorPtr)>;
   void ReadResponse(const GURL& url, ResponseCallback callback);
 
   // Starts loading response body. |response| should be obtained by
diff --git a/content/browser/web_package/bundled_exchanges_reader_unittest.cc b/content/browser/web_package/bundled_exchanges_reader_unittest.cc
index 3990246..70bc0ab 100644
--- a/content/browser/web_package/bundled_exchanges_reader_unittest.cc
+++ b/content/browser/web_package/bundled_exchanges_reader_unittest.cc
@@ -88,14 +88,17 @@
 
   GetMockFactory()->ReadAndFullfillResponse(
       GetReader(), GetPrimaryURL(), std::move(response),
-      base::BindOnce([](data_decoder::mojom::BundleResponsePtr response) {
-        EXPECT_TRUE(response);
-        if (response) {
-          EXPECT_EQ(200, response->response_code);
-          EXPECT_EQ(0xdeadu, response->payload_offset);
-          EXPECT_EQ(0xbeafu, response->payload_length);
-        }
-      }));
+      base::BindOnce(
+          [](data_decoder::mojom::BundleResponsePtr response,
+             data_decoder::mojom::BundleResponseParseErrorPtr error) {
+            EXPECT_TRUE(response);
+            EXPECT_FALSE(error);
+            if (response) {
+              EXPECT_EQ(200, response->response_code);
+              EXPECT_EQ(0xdeadu, response->payload_offset);
+              EXPECT_EQ(0xbeafu, response->payload_length);
+            }
+          }));
 }
 
 TEST_F(BundledExchangesReaderTest, ReadResponseForURLContainingUserAndPass) {
@@ -112,14 +115,17 @@
 
   GetMockFactory()->ReadAndFullfillResponse(
       GetReader(), url, std::move(response),
-      base::BindOnce([](data_decoder::mojom::BundleResponsePtr response) {
-        EXPECT_TRUE(response);
-        if (response) {
-          EXPECT_EQ(200, response->response_code);
-          EXPECT_EQ(0xdeadu, response->payload_offset);
-          EXPECT_EQ(0xbeafu, response->payload_length);
-        }
-      }));
+      base::BindOnce(
+          [](data_decoder::mojom::BundleResponsePtr response,
+             data_decoder::mojom::BundleResponseParseErrorPtr error) {
+            EXPECT_TRUE(response);
+            EXPECT_FALSE(error);
+            if (response) {
+              EXPECT_EQ(200, response->response_code);
+              EXPECT_EQ(0xdeadu, response->payload_offset);
+              EXPECT_EQ(0xbeafu, response->payload_length);
+            }
+          }));
 }
 
 TEST_F(BundledExchangesReaderTest, ReadResponseForURLContainingFragment) {
@@ -136,14 +142,17 @@
 
   GetMockFactory()->ReadAndFullfillResponse(
       GetReader(), url, std::move(response),
-      base::BindOnce([](data_decoder::mojom::BundleResponsePtr response) {
-        EXPECT_TRUE(response);
-        if (response) {
-          EXPECT_EQ(200, response->response_code);
-          EXPECT_EQ(0xdeadu, response->payload_offset);
-          EXPECT_EQ(0xbeafu, response->payload_length);
-        }
-      }));
+      base::BindOnce(
+          [](data_decoder::mojom::BundleResponsePtr response,
+             data_decoder::mojom::BundleResponseParseErrorPtr error) {
+            EXPECT_TRUE(response);
+            EXPECT_FALSE(error);
+            if (response) {
+              EXPECT_EQ(200, response->response_code);
+              EXPECT_EQ(0xdeadu, response->payload_offset);
+              EXPECT_EQ(0xbeafu, response->payload_length);
+            }
+          }));
 }
 
 TEST_F(BundledExchangesReaderTest, ReadResponseBody) {
diff --git a/content/browser/web_package/bundled_exchanges_url_loader_factory.cc b/content/browser/web_package/bundled_exchanges_url_loader_factory.cc
index d5409bf7..c77f567b 100644
--- a/content/browser/web_package/bundled_exchanges_url_loader_factory.cc
+++ b/content/browser/web_package/bundled_exchanges_url_loader_factory.cc
@@ -12,6 +12,8 @@
 #include "base/strings/string_util.h"
 #include "base/task/post_task.h"
 #include "content/browser/web_package/bundled_exchanges_reader.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -58,6 +60,19 @@
   return response_head;
 }
 
+void AddResponseParseErrorMessageToConsole(
+    int frame_tree_node_id,
+    const data_decoder::mojom::BundleResponseParseErrorPtr& error) {
+  WebContents* web_contents =
+      WebContents::FromFrameTreeNodeId(frame_tree_node_id);
+  if (!web_contents)
+    return;
+  web_contents->GetMainFrame()->AddMessageToConsole(
+      blink::mojom::ConsoleMessageLevel::kError,
+      std::string("Failed to read response header of Web Bundle file: ") +
+          error->message);
+}
+
 }  // namespace
 
 // TODO(crbug.com/966753): Consider security models, i.e. plausible CORS
@@ -67,8 +82,11 @@
  public:
   EntryLoader(base::WeakPtr<BundledExchangesURLLoaderFactory> factory,
               mojo::PendingRemote<network::mojom::URLLoaderClient> client,
-              const network::ResourceRequest& resource_request)
-      : factory_(std::move(factory)), loader_client_(std::move(client)) {
+              const network::ResourceRequest& resource_request,
+              int frame_tree_node_id)
+      : factory_(std::move(factory)),
+        loader_client_(std::move(client)),
+        frame_tree_node_id_(frame_tree_node_id) {
     DCHECK(factory_);
 
     // Parse the Range header if any.
@@ -100,13 +118,16 @@
   void PauseReadingBodyFromNet() override {}
   void ResumeReadingBodyFromNet() override {}
 
-  void OnResponseReady(data_decoder::mojom::BundleResponsePtr response) {
+  void OnResponseReady(data_decoder::mojom::BundleResponsePtr response,
+                       data_decoder::mojom::BundleResponseParseErrorPtr error) {
     if (!factory_ || !loader_client_.is_connected())
       return;
 
     // TODO(crbug.com/990733): For the initial implementation, we allow only
     // net::HTTP_OK, but we should clarify acceptable status code in the spec.
     if (!response || response->response_code != net::HTTP_OK) {
+      if (error)
+        AddResponseParseErrorMessageToConsole(frame_tree_node_id_, error);
       loader_client_->OnComplete(network::URLLoaderCompletionStatus(
           net::ERR_INVALID_BUNDLED_EXCHANGES));
       return;
@@ -170,6 +191,7 @@
 
   base::WeakPtr<BundledExchangesURLLoaderFactory> factory_;
   mojo::Remote<network::mojom::URLLoaderClient> loader_client_;
+  const int frame_tree_node_id_;
   base::Optional<net::HttpByteRange> byte_range_;
 
   base::WeakPtrFactory<EntryLoader> weak_factory_{this};
@@ -178,8 +200,9 @@
 };
 
 BundledExchangesURLLoaderFactory::BundledExchangesURLLoaderFactory(
-    scoped_refptr<BundledExchangesReader> reader)
-    : reader_(std::move(reader)) {}
+    scoped_refptr<BundledExchangesReader> reader,
+    int frame_tree_node_id)
+    : reader_(std::move(reader)), frame_tree_node_id_(frame_tree_node_id) {}
 
 BundledExchangesURLLoaderFactory::~BundledExchangesURLLoaderFactory() = default;
 
@@ -199,9 +222,9 @@
   if (base::EqualsCaseInsensitiveASCII(resource_request.method,
                                        net::HttpRequestHeaders::kGetMethod) &&
       reader_->HasEntry(resource_request.url)) {
-    auto loader = std::make_unique<EntryLoader>(weak_factory_.GetWeakPtr(),
-                                                loader_client.PassInterface(),
-                                                resource_request);
+    auto loader = std::make_unique<EntryLoader>(
+        weak_factory_.GetWeakPtr(), loader_client.PassInterface(),
+        resource_request, frame_tree_node_id_);
     std::unique_ptr<network::mojom::URLLoader> url_loader = std::move(loader);
     mojo::MakeSelfOwnedReceiver(
         std::move(url_loader), mojo::PendingReceiver<network::mojom::URLLoader>(
diff --git a/content/browser/web_package/bundled_exchanges_url_loader_factory.h b/content/browser/web_package/bundled_exchanges_url_loader_factory.h
index dac1746..5d148fb 100644
--- a/content/browser/web_package/bundled_exchanges_url_loader_factory.h
+++ b/content/browser/web_package/bundled_exchanges_url_loader_factory.h
@@ -24,7 +24,8 @@
     : public network::mojom::URLLoaderFactory {
  public:
   explicit BundledExchangesURLLoaderFactory(
-      scoped_refptr<BundledExchangesReader> reader);
+      scoped_refptr<BundledExchangesReader> reader,
+      int frame_tree_node_id);
   ~BundledExchangesURLLoaderFactory() override;
 
   // Set a |network::mojom::URLLoaderFactory| remote interface used for requests
@@ -55,6 +56,7 @@
 
   mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_;
   scoped_refptr<BundledExchangesReader> reader_;
+  const int frame_tree_node_id_;
   mojo::Remote<network::mojom::URLLoaderFactory> fallback_factory_;
 
   base::WeakPtrFactory<BundledExchangesURLLoaderFactory> weak_factory_{this};
diff --git a/content/browser/web_package/bundled_exchanges_url_loader_factory_unittest.cc b/content/browser/web_package/bundled_exchanges_url_loader_factory_unittest.cc
index 0c7bebb..c73b6d1 100644
--- a/content/browser/web_package/bundled_exchanges_url_loader_factory_unittest.cc
+++ b/content/browser/web_package/bundled_exchanges_url_loader_factory_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
+#include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/web_package/bundled_exchanges_reader.h"
 #include "content/browser/web_package/mock_bundled_exchanges_reader_factory.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -34,8 +35,8 @@
     mock_factory_ = MockBundledExchangesReaderFactory::Create();
     auto reader = mock_factory_->CreateReader(body_);
     reader_ = reader.get();
-    loader_factory_ =
-        std::make_unique<BundledExchangesURLLoaderFactory>(std::move(reader));
+    loader_factory_ = std::make_unique<BundledExchangesURLLoaderFactory>(
+        std::move(reader), FrameTreeNode::kFrameTreeNodeInvalidId);
 
     base::flat_map<GURL, data_decoder::mojom::BundleIndexValuePtr> items;
     data_decoder::mojom::BundleIndexValuePtr item =
diff --git a/content/browser/web_package/bundled_exchanges_utils.cc b/content/browser/web_package/bundled_exchanges_utils.cc
index af0fcd56..b494791 100644
--- a/content/browser/web_package/bundled_exchanges_utils.cc
+++ b/content/browser/web_package/bundled_exchanges_utils.cc
@@ -50,13 +50,13 @@
 }
 
 bool CanLoadAsBundledExchangesFile(const GURL& url) {
-  if (!base::FeatureList::IsEnabled(features::kBundledHTTPExchanges))
+  if (!base::FeatureList::IsEnabled(features::kWebBundles))
     return false;
   return IsSupprtedFileScheme(url);
 }
 
 bool CanLoadAsBundledExchanges(const GURL& url, const std::string& mime_type) {
-  if (!base::FeatureList::IsEnabled(features::kBundledHTTPExchanges))
+  if (!base::FeatureList::IsEnabled(features::kWebBundles))
     return false;
   // Currently loading bundled exchanges file from server response is not
   // implemented yet.
@@ -68,7 +68,7 @@
 bool GetBundledExchangesFileMimeTypeFromFile(const base::FilePath& path,
                                              std::string* mime_type) {
   DCHECK(mime_type);
-  if (!base::FeatureList::IsEnabled(features::kBundledHTTPExchanges))
+  if (!base::FeatureList::IsEnabled(features::kWebBundles))
     return false;
   if (path.Extension() != kBundledExchangesFileExtension)
     return false;
diff --git a/content/browser/web_package/mock_bundled_exchanges_reader_factory.cc b/content/browser/web_package/mock_bundled_exchanges_reader_factory.cc
index 346d37e..a1b0527 100644
--- a/content/browser/web_package/mock_bundled_exchanges_reader_factory.cc
+++ b/content/browser/web_package/mock_bundled_exchanges_reader_factory.cc
@@ -207,8 +207,10 @@
         url, base::BindOnce(
                  [](base::Closure quit_closure,
                     BundledExchangesReader::ResponseCallback callback,
-                    data_decoder::mojom::BundleResponsePtr response) {
-                   std::move(callback).Run(std::move(response));
+                    data_decoder::mojom::BundleResponsePtr response,
+                    data_decoder::mojom::BundleResponseParseErrorPtr error) {
+                   std::move(callback).Run(std::move(response),
+                                           std::move(error));
                    std::move(quit_closure).Run();
                  },
                  run_loop.QuitClosure(), std::move(callback)));
diff --git a/content/browser/webrtc/OWNERS b/content/browser/webrtc/OWNERS
index 99868f9..0bec65a03 100644
--- a/content/browser/webrtc/OWNERS
+++ b/content/browser/webrtc/OWNERS
@@ -1,10 +1,10 @@
-chfremer@chromium.org
 guidou@chromium.org
 tommi@chromium.org
 
 per-file *test*=phoglund@chromium.org
 
-# Original (legacy) owner.
+# Original (legacy) owners.
+chfremer@chromium.org
 emircan@chromium.org
 mcasas@chromium.org
 
diff --git a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
index 909183a..ad5116a6 100644
--- a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
+++ b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
@@ -304,13 +304,13 @@
                        MAYBE_GetUserMediaWithMandatorySourceID) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
+  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+
   std::vector<std::string> audio_ids;
   std::vector<std::string> video_ids;
   GetInputDevices(&audio_ids, &video_ids);
 
-  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), url));
-
   // Test all combinations of mandatory sourceID;
   for (std::vector<std::string>::const_iterator video_it = video_ids.begin();
        video_it != video_ids.end(); ++video_it) {
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 95e3d31a..f9865ab 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -132,311 +132,247 @@
 #endif
 }
 
+enum RuntimeFeatureEnableOptions {
+  // Always set the Blink feature to the enabled state of the base::Feature.
+  // Example: A run time feature that is completely controlled
+  // by base::Feature.
+  kUseFeatureState,
+  // Enables the Blink feature when the base::Feature is enabled,
+  // otherwise no change.
+  kEnableOnly,
+  // Disables the Blink feature when the base::Feature is *disabled*,
+  // otherwise no change.
+  kDisableOnly,
+};
+
+template <typename T>
+// Helper class that describes the desired actions for the runtime feature
+// depending on a check for chromium base::Feature.
+struct RuntimeFeatureToChromiumFeatureMap {
+  // This can be either an enabler function defined in web_runtime_features.cc
+  // or the string name of the feature in runtime_enabled_features.json5.
+  T feature_enabler;
+  // The chromium base::Feature to check.
+  const base::Feature& chromium_feature;
+  const RuntimeFeatureEnableOptions option;
+};
+
 // Sets blink runtime features that are either directly
 // controlled by Chromium base::Feature or are overridden
 // by base::Feature states.
 void SetRuntimeFeaturesFromChromiumFeatures() {
-  if (!base::FeatureList::IsEnabled(features::kWebUsb))
-    WebRuntimeFeatures::EnableWebUsb(false);
-
-  if (base::FeatureList::IsEnabled(
-          blink::features::kBlockingFocusWithoutUserActivation)) {
-    WebRuntimeFeatures::EnableBlockingFocusWithoutUserActivation(true);
+  using wf = WebRuntimeFeatures;
+  // To add a runtime feature control, add a new
+  // RuntimeFeatureToChromiumFeatureMap entry here if there is a custom
+  // enabler function defined. Otherwise add the entry with string name
+  // in the next list.
+  const RuntimeFeatureToChromiumFeatureMap<void (*)(bool)>
+      blinkFeatureToBaseFeatureMapping[] = {
+          // TODO(rodneyding): Sort features in alphabetical order
+          {wf::EnableWebUsb, features::kWebUsb, kDisableOnly},
+          {wf::EnableBlockingFocusWithoutUserActivation,
+           blink::features::kBlockingFocusWithoutUserActivation, kEnableOnly},
+          {wf::EnableNotificationContentImage,
+           features::kNotificationContentImage, kDisableOnly},
+          {wf::EnableReducedReferrerGranularity,
+           features::kReducedReferrerGranularity, kUseFeatureState},
+          {wf::EnablePeriodicBackgroundSync, features::kPeriodicBackgroundSync,
+           kEnableOnly},
+          {wf::EnableWebXR, features::kWebXr, kUseFeatureState},
+          {wf::EnableWebXRARDOMOverlay, features::kWebXrArDOMOverlay,
+           kEnableOnly},
+          {wf::EnableWebXRARModule, features::kWebXrArModule, kEnableOnly},
+          {wf::EnableWebXRHitTest, features::kWebXrHitTest, kEnableOnly},
+          {wf::EnableWebXRAnchors, features::kWebXrAnchors, kEnableOnly},
+          {wf::EnableWebXRPlaneDetection, features::kWebXrPlaneDetection,
+           kEnableOnly},
+          {wf::EnableWebXrGamepadModule, features::kWebXrGamepadModule,
+           kUseFeatureState},
+          {wf::EnableFetchMetadata, network::features::kFetchMetadata,
+           kUseFeatureState},
+          {wf::EnableFetchMetadataDestination,
+           network::features::kFetchMetadataDestination, kUseFeatureState},
+          {wf::EnableUserActivationPostMessageTransfer,
+           features::kUserActivationPostMessageTransfer, kUseFeatureState},
+          {wf::EnableUserActivationSameOriginVisibility,
+           features::kUserActivationSameOriginVisibility, kUseFeatureState},
+          {wf::EnableUserActivationV2, features::kUserActivationV2,
+           kUseFeatureState},
+          {wf::EnablePassiveDocumentEventListeners,
+           features::kPassiveDocumentEventListeners, kUseFeatureState},
+          {wf::EnablePassiveDocumentWheelEventListeners,
+           features::kPassiveDocumentWheelEventListeners, kUseFeatureState},
+          {wf::EnableExpensiveBackgroundTimerThrottling,
+           features::kExpensiveBackgroundTimerThrottling, kUseFeatureState},
+          {wf::EnableTimerThrottlingForHiddenFrames,
+           features::kTimerThrottlingForHiddenFrames, kUseFeatureState},
+          {wf::EnableSendBeaconThrowForBlobWithNonSimpleType,
+           features::kSendBeaconThrowForBlobWithNonSimpleType, kEnableOnly},
+          {wf::EnablePaymentRequest, features::kWebPayments, kUseFeatureState},
+          {wf::EnablePaymentApp, features::kServiceWorkerPaymentApps,
+           kEnableOnly},
+          {wf::EnableCompositorTouchAction, features::kCompositorTouchAction,
+           kEnableOnly},
+          {wf::EnableGenericSensorExtraClasses,
+           features::kGenericSensorExtraClasses, kEnableOnly},
+          {wf::EnableMediaCastOverlayButton, media::kMediaCastOverlayButton,
+           kUseFeatureState},
+          {wf::EnableBuiltInModuleAll, features::kBuiltInModuleAll,
+           kEnableOnly},
+          {wf::EnableBuiltInModuleInfra, features::kBuiltInModuleInfra,
+           kEnableOnly},
+          {wf::EnableBuiltInModuleKvStorage, features::kBuiltInModuleKvStorage,
+           kEnableOnly},
+          {wf::EnableLazyInitializeMediaControls,
+           features::kLazyInitializeMediaControls, kUseFeatureState},
+          {wf::EnableMediaEngagementBypassAutoplayPolicies,
+           media::kMediaEngagementBypassAutoplayPolicies, kUseFeatureState},
+          {wf::EnableOverflowIconsForMediaControls,
+           media::kOverflowIconsForMediaControls, kUseFeatureState},
+          {wf::EnableAllowActivationDelegationAttr,
+           features::kAllowActivationDelegationAttr, kUseFeatureState},
+          {wf::EnableScriptStreamingOnPreload,
+           features::kScriptStreamingOnPreload, kUseFeatureState},
+          {wf::EnableMergeBlockingNonBlockingPools,
+           base::kMergeBlockingNonBlockingPools, kUseFeatureState},
+          {wf::EnableLazyFrameLoading, features::kLazyFrameLoading,
+           kUseFeatureState},
+          {wf::EnableLazyFrameVisibleLoadTimeMetrics,
+           features::kLazyFrameVisibleLoadTimeMetrics, kUseFeatureState},
+          {wf::EnableLazyImageLoading, features::kLazyImageLoading,
+           kUseFeatureState},
+          {wf::EnableLazyImageVisibleLoadTimeMetrics,
+           features::kLazyImageVisibleLoadTimeMetrics, kUseFeatureState},
+          {wf::EnablePictureInPicture, media::kPictureInPicture,
+           kUseFeatureState},
+          {wf::EnableCacheInlineScriptCode, features::kCacheInlineScriptCode,
+           kUseFeatureState},
+          {wf::EnableWasmCodeCache, blink::features::kWasmCodeCache,
+           kUseFeatureState},
+          {wf::EnableExperimentalProductivityFeatures,
+           features::kExperimentalProductivityFeatures, kEnableOnly},
+          {wf::EnableFeaturePolicyForSandbox,
+           features::kFeaturePolicyForSandbox, kEnableOnly},
+          {wf::EnableAccessibilityExposeARIAAnnotations,
+           features::kEnableAccessibilityExposeARIAAnnotations,
+           kUseFeatureState},
+          {wf::EnableAccessibilityExposeDisplayNone,
+           features::kEnableAccessibilityExposeDisplayNone, kUseFeatureState},
+          {wf::EnableAllowSyncXHRInPageDismissal,
+           blink::features::kAllowSyncXHRInPageDismissal, kEnableOnly},
+          {wf::EnableAutoplayIgnoresWebAudio, media::kAutoplayIgnoreWebAudio,
+           kUseFeatureState},
+          {wf::EnablePortals, blink::features::kPortals, kUseFeatureState},
+          {wf::EnableImplicitRootScroller,
+           blink::features::kImplicitRootScroller, kUseFeatureState},
+          {wf::EnableCSSOMViewScrollCoordinates,
+           blink::features::kCSSOMViewScrollCoordinates, kEnableOnly},
+          {wf::EnableTextFragmentAnchor, blink::features::kTextFragmentAnchor,
+           kUseFeatureState},
+          {wf::EnableBackgroundFetch, features::kBackgroundFetch, kDisableOnly},
+          {wf::EnableUpdateHoverAtBeginFrame,
+           features::kUpdateHoverAtBeginFrame, kUseFeatureState},
+          {wf::EnableForcedColors, features::kForcedColors, kUseFeatureState},
+          {wf::EnableFractionalScrollOffsets,
+           features::kFractionalScrollOffsets, kUseFeatureState},
+          {wf::EnableGetDisplayMedia, blink::features::kRTCGetDisplayMedia,
+           kUseFeatureState},
+          {wf::EnableMimeHandlerViewInCrossProcessFrame,
+           features::kMimeHandlerViewInCrossProcessFrame, kUseFeatureState},
+          {wf::EnableFallbackCursorMode, features::kFallbackCursorMode,
+           kUseFeatureState},
+          {wf::EnableSignedExchangePrefetchCacheForNavigations,
+           features::kSignedExchangePrefetchCacheForNavigations,
+           kUseFeatureState},
+          {wf::EnableSignedExchangeSubresourcePrefetch,
+           features::kSignedExchangeSubresourcePrefetch, kUseFeatureState},
+          {wf::EnableIdleDetection, features::kIdleDetection, kDisableOnly},
+          {wf::EnableSkipTouchEventFilter, features::kSkipTouchEventFilter,
+           kUseFeatureState},
+          {wf::EnableSmsReceiver, features::kSmsReceiver, kDisableOnly},
+          {wf::EnableDisplayLocking, blink::features::kDisplayLocking,
+           kUseFeatureState},
+          {wf::EnableConsolidatedMovementXY, features::kConsolidatedMovementXY,
+           kUseFeatureState},
+          {wf::EnableCooperativeScheduling, features::kCooperativeScheduling,
+           kUseFeatureState},
+          {wf::EnableMouseSubframeNoImplicitCapture,
+           features::kMouseSubframeNoImplicitCapture, kUseFeatureState},
+          {wf::EnableBackForwardCache, features::kBackForwardCache,
+           kUseFeatureState},
+          {wf::EnableCookieDeprecationMessages,
+           features::kCookieDeprecationMessages, kEnableOnly},
+          {wf::EnableSameSiteByDefaultCookies,
+           net::features::kSameSiteByDefaultCookies, kEnableOnly},
+          {wf::EnableCookiesWithoutSameSiteMustBeSecure,
+           net::features::kCookiesWithoutSameSiteMustBeSecure, kEnableOnly},
+          {wf::EnablePointerLockOptions, features::kPointerLockOptions,
+           kEnableOnly},
+          {wf::EnableDocumentPolicy, features::kDocumentPolicy,
+           kUseFeatureState},
+      };
+  for (const auto& mapping : blinkFeatureToBaseFeatureMapping) {
+    const bool featureEnabled =
+        base::FeatureList::IsEnabled(mapping.chromium_feature);
+    switch (mapping.option) {
+      case kEnableOnly:
+        if (featureEnabled)
+          mapping.feature_enabler(true);
+        break;
+      case kDisableOnly:
+        if (!featureEnabled)
+          mapping.feature_enabler(false);
+        break;
+      case kUseFeatureState:
+        mapping.feature_enabler(featureEnabled);
+    }
   }
 
-  if (!base::FeatureList::IsEnabled(features::kNotificationContentImage))
-    WebRuntimeFeatures::EnableNotificationContentImage(false);
-
-  WebRuntimeFeatures::EnableSharedArrayBuffer(
-      base::FeatureList::IsEnabled(features::kSharedArrayBuffer) ||
-      base::FeatureList::IsEnabled(features::kWebAssemblyThreads));
-
-  WebRuntimeFeatures::EnableReducedReferrerGranularity(
-      base::FeatureList::IsEnabled(features::kReducedReferrerGranularity));
-
-  if (base::FeatureList::IsEnabled(features::kPeriodicBackgroundSync))
-    WebRuntimeFeatures::EnablePeriodicBackgroundSync(true);
-
-  WebRuntimeFeatures::EnableWebXR(
-      base::FeatureList::IsEnabled(features::kWebXr));
-
-  if (base::FeatureList::IsEnabled(features::kWebXrArDOMOverlay))
-    WebRuntimeFeatures::EnableWebXRARDOMOverlay(true);
-
-  if (base::FeatureList::IsEnabled(features::kWebXrArModule))
-    WebRuntimeFeatures::EnableWebXRARModule(true);
-
-  if (base::FeatureList::IsEnabled(features::kWebXrHitTest))
-    WebRuntimeFeatures::EnableWebXRHitTest(true);
-
-  if (base::FeatureList::IsEnabled(features::kWebXrAnchors))
-    WebRuntimeFeatures::EnableWebXRAnchors(true);
-
-  if (base::FeatureList::IsEnabled(features::kWebXrPlaneDetection))
-    WebRuntimeFeatures::EnableWebXRPlaneDetection(true);
-
-  if (base::FeatureList::IsEnabled(features::kWebXrGamepadModule))
-    WebRuntimeFeatures::EnableWebXrGamepadModule(true);
-
-  WebRuntimeFeatures::EnableFetchMetadata(
-      base::FeatureList::IsEnabled(network::features::kFetchMetadata));
-  WebRuntimeFeatures::EnableFetchMetadataDestination(
-      base::FeatureList::IsEnabled(
-          network::features::kFetchMetadataDestination));
-
-  WebRuntimeFeatures::EnableUserActivationPostMessageTransfer(
-      base::FeatureList::IsEnabled(
-          features::kUserActivationPostMessageTransfer));
-
-  WebRuntimeFeatures::EnableUserActivationSameOriginVisibility(
-      base::FeatureList::IsEnabled(
-          features::kUserActivationSameOriginVisibility));
-
-  WebRuntimeFeatures::EnableUserActivationV2(
-      base::FeatureList::IsEnabled(features::kUserActivationV2));
-
-  WebRuntimeFeatures::EnableFeatureFromString(
-      "CSSBackdropFilter",
-      base::FeatureList::IsEnabled(blink::features::kCSSBackdropFilter));
-
-  WebRuntimeFeatures::EnableFeatureFromString(
-      "FastBorderRadius",
-      base::FeatureList::IsEnabled(blink::features::kFastBorderRadius));
-
-  WebRuntimeFeatures::EnablePassiveDocumentEventListeners(
-      base::FeatureList::IsEnabled(features::kPassiveDocumentEventListeners));
-
-  WebRuntimeFeatures::EnablePassiveDocumentWheelEventListeners(
-      base::FeatureList::IsEnabled(
-          features::kPassiveDocumentWheelEventListeners));
-
-  WebRuntimeFeatures::EnableFeatureFromString(
-      "FontSrcLocalMatching",
-      base::FeatureList::IsEnabled(features::kFontSrcLocalMatching));
-
-  WebRuntimeFeatures::EnableFeatureFromString(
-      "LegacyWindowsDWriteFontFallback",
-      base::FeatureList::IsEnabled(features::kLegacyWindowsDWriteFontFallback));
-
-  WebRuntimeFeatures::EnableExpensiveBackgroundTimerThrottling(
-      base::FeatureList::IsEnabled(
-          features::kExpensiveBackgroundTimerThrottling));
-
-  WebRuntimeFeatures::EnableTimerThrottlingForHiddenFrames(
-      base::FeatureList::IsEnabled(features::kTimerThrottlingForHiddenFrames));
-
-  if (base::FeatureList::IsEnabled(
-          features::kSendBeaconThrowForBlobWithNonSimpleType))
-    WebRuntimeFeatures::EnableSendBeaconThrowForBlobWithNonSimpleType(true);
-
-  WebRuntimeFeatures::EnablePaymentRequest(
-      base::FeatureList::IsEnabled(features::kWebPayments));
-
-  if (base::FeatureList::IsEnabled(features::kServiceWorkerPaymentApps))
-    WebRuntimeFeatures::EnablePaymentApp(true);
-
-  if (base::FeatureList::IsEnabled(features::kCompositorTouchAction))
-    WebRuntimeFeatures::EnableCompositorTouchAction(true);
-
-  if (base::FeatureList::IsEnabled(features::kGenericSensorExtraClasses))
-    WebRuntimeFeatures::EnableGenericSensorExtraClasses(true);
-
-  if (base::FeatureList::IsEnabled(
-          network::features::kBlockNonSecureExternalRequests)) {
-    WebRuntimeFeatures::EnableFeatureFromString("AddressSpace", true);
+  // TODO(crbug/832393): Cleanup the inconsistency between custom WRF enabler
+  // function and using feature string name with EnableFeatureFromString.
+  const RuntimeFeatureToChromiumFeatureMap<const char*>
+      runtimeFeatureNameToChromiumFeatureMapping[] = {
+          {"CSSBackdropFilter", blink::features::kCSSBackdropFilter,
+           kUseFeatureState},
+          {"FastBorderRadius", blink::features::kFastBorderRadius,
+           kUseFeatureState},
+          {"FontSrcLocalMatching", features::kFontSrcLocalMatching,
+           kUseFeatureState},
+          {"LegacyWindowsDWriteFontFallback",
+           features::kLegacyWindowsDWriteFontFallback, kUseFeatureState},
+          {"AddressSpace", network::features::kBlockNonSecureExternalRequests,
+           kEnableOnly},
+          {"BlockCredentialedSubresources",
+           features::kBlockCredentialedSubresources, kDisableOnly},
+          {"AllowContentInitiatedDataUrlNavigations",
+           features::kAllowContentInitiatedDataUrlNavigations,
+           kUseFeatureState},
+          {"LayoutNG", blink::features::kLayoutNG, kUseFeatureState},
+          {"UserAgentClientHint", features::kUserAgentClientHint, kEnableOnly},
+          {"AudioWorkletRealtimeThread",
+           blink::features::kAudioWorkletRealtimeThread, kEnableOnly},
+          {"TrustedDOMTypes", features::kTrustedDOMTypes, kEnableOnly},
+          {"IgnoreCrossOriginWindowWhenNamedAccessOnWindow",
+           blink::features::kIgnoreCrossOriginWindowWhenNamedAccessOnWindow,
+           kEnableOnly},
+          {"StorageAccessAPI", blink::features::kStorageAccessAPI, kEnableOnly},
+      };
+  for (const auto& mapping : runtimeFeatureNameToChromiumFeatureMapping) {
+    const bool featureEnabled =
+        base::FeatureList::IsEnabled(mapping.chromium_feature);
+    switch (mapping.option) {
+      case kEnableOnly:
+        if (featureEnabled)
+          wf::EnableFeatureFromString(mapping.feature_enabler, true);
+        break;
+      case kDisableOnly:
+        if (!featureEnabled)
+          wf::EnableFeatureFromString(mapping.feature_enabler, false);
+        break;
+      case kUseFeatureState:
+        wf::EnableFeatureFromString(mapping.feature_enabler, featureEnabled);
+    }
   }
-
-  WebRuntimeFeatures::EnableMediaCastOverlayButton(
-      base::FeatureList::IsEnabled(media::kMediaCastOverlayButton));
-
-  if (!base::FeatureList::IsEnabled(features::kBlockCredentialedSubresources)) {
-    WebRuntimeFeatures::EnableFeatureFromString("BlockCredentialedSubresources",
-                                                false);
-  }
-
-  WebRuntimeFeatures::EnableFeatureFromString(
-      "AllowContentInitiatedDataUrlNavigations",
-      base::FeatureList::IsEnabled(
-          features::kAllowContentInitiatedDataUrlNavigations));
-
-  if (base::FeatureList::IsEnabled(features::kBuiltInModuleAll))
-    WebRuntimeFeatures::EnableBuiltInModuleAll(true);
-
-  if (base::FeatureList::IsEnabled(features::kBuiltInModuleInfra))
-    WebRuntimeFeatures::EnableBuiltInModuleInfra(true);
-
-  if (base::FeatureList::IsEnabled(features::kBuiltInModuleKvStorage))
-    WebRuntimeFeatures::EnableBuiltInModuleKvStorage(true);
-
-  WebRuntimeFeatures::EnableFeatureFromString(
-      "LayoutNG", base::FeatureList::IsEnabled(blink::features::kLayoutNG));
-
-  WebRuntimeFeatures::EnableLazyInitializeMediaControls(
-      base::FeatureList::IsEnabled(features::kLazyInitializeMediaControls));
-
-  WebRuntimeFeatures::EnableMediaEngagementBypassAutoplayPolicies(
-      base::FeatureList::IsEnabled(
-          media::kMediaEngagementBypassAutoplayPolicies));
-
-  WebRuntimeFeatures::EnableOverflowIconsForMediaControls(
-      base::FeatureList::IsEnabled(media::kOverflowIconsForMediaControls));
-
-  WebRuntimeFeatures::EnableAllowActivationDelegationAttr(
-      base::FeatureList::IsEnabled(features::kAllowActivationDelegationAttr));
-
-  WebRuntimeFeatures::EnableScriptStreamingOnPreload(
-      base::FeatureList::IsEnabled(features::kScriptStreamingOnPreload));
-
-  WebRuntimeFeatures::EnableMergeBlockingNonBlockingPools(
-      base::FeatureList::IsEnabled(base::kMergeBlockingNonBlockingPools));
-
-  WebRuntimeFeatures::EnableLazyFrameLoading(
-      base::FeatureList::IsEnabled(features::kLazyFrameLoading));
-  WebRuntimeFeatures::EnableLazyFrameVisibleLoadTimeMetrics(
-      base::FeatureList::IsEnabled(features::kLazyFrameVisibleLoadTimeMetrics));
-  WebRuntimeFeatures::EnableLazyImageLoading(
-      base::FeatureList::IsEnabled(features::kLazyImageLoading));
-  WebRuntimeFeatures::EnableLazyImageVisibleLoadTimeMetrics(
-      base::FeatureList::IsEnabled(features::kLazyImageVisibleLoadTimeMetrics));
-
-  WebRuntimeFeatures::EnablePictureInPicture(
-      base::FeatureList::IsEnabled(media::kPictureInPicture));
-
-  WebRuntimeFeatures::EnableCacheInlineScriptCode(
-      base::FeatureList::IsEnabled(features::kCacheInlineScriptCode));
-
-  WebRuntimeFeatures::EnableWasmCodeCache(
-      base::FeatureList::IsEnabled(blink::features::kWasmCodeCache));
-
-  if (base::FeatureList::IsEnabled(
-          features::kExperimentalProductivityFeatures)) {
-    WebRuntimeFeatures::EnableExperimentalProductivityFeatures(true);
-  }
-
-  if (base::FeatureList::IsEnabled(features::kFeaturePolicyForSandbox))
-    WebRuntimeFeatures::EnableFeaturePolicyForSandbox(true);
-
-  WebRuntimeFeatures::EnableAccessibilityExposeARIAAnnotations(
-      base::FeatureList::IsEnabled(
-          features::kEnableAccessibilityExposeARIAAnnotations));
-
-  WebRuntimeFeatures::EnableAccessibilityExposeDisplayNone(
-      base::FeatureList::IsEnabled(
-          features::kEnableAccessibilityExposeDisplayNone));
-
-  if (base::FeatureList::IsEnabled(
-          blink::features::kAllowSyncXHRInPageDismissal)) {
-    WebRuntimeFeatures::EnableAllowSyncXHRInPageDismissal(true);
-  }
-
-  WebRuntimeFeatures::EnableAutoplayIgnoresWebAudio(
-      base::FeatureList::IsEnabled(media::kAutoplayIgnoreWebAudio));
-
-  WebRuntimeFeatures::EnablePortals(
-      base::FeatureList::IsEnabled(blink::features::kPortals));
-
-  WebRuntimeFeatures::EnableImplicitRootScroller(
-      base::FeatureList::IsEnabled(blink::features::kImplicitRootScroller));
-
-  if (base::FeatureList::IsEnabled(
-          blink::features::kCSSOMViewScrollCoordinates))
-    WebRuntimeFeatures::EnableCSSOMViewScrollCoordinates(true);
-
-  WebRuntimeFeatures::EnableTextFragmentAnchor(
-      base::FeatureList::IsEnabled(blink::features::kTextFragmentAnchor));
-
-  if (!base::FeatureList::IsEnabled(features::kBackgroundFetch))
-    WebRuntimeFeatures::EnableBackgroundFetch(false);
-
-  WebRuntimeFeatures::EnableUpdateHoverAtBeginFrame(
-      base::FeatureList::IsEnabled(features::kUpdateHoverAtBeginFrame));
-
-  WebRuntimeFeatures::EnableForcedColors(
-      base::FeatureList::IsEnabled(features::kForcedColors));
-
-  WebRuntimeFeatures::EnableFractionalScrollOffsets(
-      base::FeatureList::IsEnabled(features::kFractionalScrollOffsets));
-
-  WebRuntimeFeatures::EnableGetDisplayMedia(
-      base::FeatureList::IsEnabled(blink::features::kRTCGetDisplayMedia));
-
-  WebRuntimeFeatures::EnableMimeHandlerViewInCrossProcessFrame(
-      base::FeatureList::IsEnabled(
-          features::kMimeHandlerViewInCrossProcessFrame));
-
-  WebRuntimeFeatures::EnableFallbackCursorMode(
-      base::FeatureList::IsEnabled(features::kFallbackCursorMode));
-
-  if (base::FeatureList::IsEnabled(features::kUserAgentClientHint))
-    WebRuntimeFeatures::EnableFeatureFromString("UserAgentClientHint", true);
-
-  WebRuntimeFeatures::EnableSignedExchangePrefetchCacheForNavigations(
-      base::FeatureList::IsEnabled(
-          features::kSignedExchangePrefetchCacheForNavigations));
-  WebRuntimeFeatures::EnableSignedExchangeSubresourcePrefetch(
-      base::FeatureList::IsEnabled(
-          features::kSignedExchangeSubresourcePrefetch));
-
-  if (!base::FeatureList::IsEnabled(features::kIdleDetection))
-    WebRuntimeFeatures::EnableIdleDetection(false);
-
-  WebRuntimeFeatures::EnableSkipTouchEventFilter(
-      base::FeatureList::IsEnabled(features::kSkipTouchEventFilter));
-
-  if (!base::FeatureList::IsEnabled(features::kSmsReceiver))
-    WebRuntimeFeatures::EnableSmsReceiver(false);
-
-  WebRuntimeFeatures::EnableDisplayLocking(
-      base::FeatureList::IsEnabled(blink::features::kDisplayLocking));
-
-  if (base::FeatureList::IsEnabled(
-          blink::features::kAudioWorkletRealtimeThread)) {
-    WebRuntimeFeatures::EnableFeatureFromString("AudioWorkletRealtimeThread",
-                                                true);
-  }
-
-  WebRuntimeFeatures::EnableConsolidatedMovementXY(
-      base::FeatureList::IsEnabled(features::kConsolidatedMovementXY));
-
-  WebRuntimeFeatures::EnableCooperativeScheduling(
-      base::FeatureList::IsEnabled(features::kCooperativeScheduling));
-
-  WebRuntimeFeatures::EnableMouseSubframeNoImplicitCapture(
-      base::FeatureList::IsEnabled(features::kMouseSubframeNoImplicitCapture));
-
-  if (base::FeatureList::IsEnabled(features::kTrustedDOMTypes))
-    WebRuntimeFeatures::EnableFeatureFromString("TrustedDOMTypes", true);
-
-  WebRuntimeFeatures::EnableBackForwardCache(
-      base::FeatureList::IsEnabled(features::kBackForwardCache));
-
-  if (base::FeatureList::IsEnabled(features::kCookieDeprecationMessages))
-    WebRuntimeFeatures::EnableCookieDeprecationMessages(true);
-
-  if (base::FeatureList::IsEnabled(net::features::kSameSiteByDefaultCookies))
-    WebRuntimeFeatures::EnableSameSiteByDefaultCookies(true);
-
-  if (base::FeatureList::IsEnabled(
-          net::features::kCookiesWithoutSameSiteMustBeSecure)) {
-    WebRuntimeFeatures::EnableCookiesWithoutSameSiteMustBeSecure(true);
-  }
-
-  if (base::FeatureList::IsEnabled(
-          blink::features::kIgnoreCrossOriginWindowWhenNamedAccessOnWindow)) {
-    WebRuntimeFeatures::EnableFeatureFromString(
-        "IgnoreCrossOriginWindowWhenNamedAccessOnWindow", true);
-  }
-
-  if (base::FeatureList::IsEnabled(blink::features::kStorageAccessAPI)) {
-    WebRuntimeFeatures::EnableFeatureFromString("StorageAccessAPI", true);
-  }
-
-  if (base::FeatureList::IsEnabled(features::kPointerLockOptions)) {
-    WebRuntimeFeatures::EnablePointerLockOptions(true);
-  }
-
-  WebRuntimeFeatures::EnableDocumentPolicy(
-      base::FeatureList::IsEnabled(features::kDocumentPolicy));
 }
 
 // Helper class that describes the desired enable/disable action
@@ -503,6 +439,8 @@
        switches::kEnableAccessibilityObjectModel, true},
       {wrf::EnableAllowSyncXHRInPageDismissal,
        switches::kAllowSyncXHRInPageDismissal, true},
+      {wrf::EnableOutOfBlinkCors, network::switches::kEnableOutOfBlinkCors,
+       true},
   };
   for (const auto& mapping : switchToFeatureMapping) {
     if (command_line.HasSwitch(mapping.switch_name))
@@ -568,12 +506,15 @@
     WebRuntimeFeatures::EnableDecodeLossyWebPImagesToYUV(true);
   }
 
+  WebRuntimeFeatures::EnableSharedArrayBuffer(
+      base::FeatureList::IsEnabled(features::kSharedArrayBuffer) ||
+      base::FeatureList::IsEnabled(features::kWebAssemblyThreads));
+
+  // These checks are custom wrappers around base::FeatureList::IsEnabled
+  // They're moved here to distinguish them from actual base checks
   if (ui::IsOverlayScrollbarEnabled())
     WebRuntimeFeatures::EnableOverlayScrollbars(true);
 
-  if (command_line.HasSwitch(network::switches::kEnableOutOfBlinkCors))
-    WebRuntimeFeatures::EnableOutOfBlinkCors(true);
-
   WebRuntimeFeatures::EnableFormControlsRefresh(
       features::IsFormControlsRefreshEnabled());
 
@@ -587,6 +528,8 @@
     WebRuntimeFeatures::EnableFeatureFromString("FileHandling", true);
   }
 
+  // TODO(rodneyding): This is a rare case for a stable feature
+  // Need to investigate more to determine whether to refactor it.
   if (command_line.HasSwitch(switches::kDisableV8IdleTasks))
     WebRuntimeFeatures::EnableV8IdleTasks(false);
   else
diff --git a/content/common/DEPS b/content/common/DEPS
index f7ddce6..571d236 100644
--- a/content/common/DEPS
+++ b/content/common/DEPS
@@ -47,7 +47,6 @@
   "+third_party/blink/public/platform/web_scroll_into_view_params.h",
   "+third_party/blink/public/platform/web_scroll_types.h",
   "+third_party/blink/public/platform/web_storage_area.h",
-  "+third_party/blink/public/platform/web_sudden_termination_disabler_type.h",
   "+third_party/blink/public/platform/web_text_autosizer_page_info.h",
   "+third_party/blink/public/platform/web_touch_event.h",
   "+third_party/blink/public/platform/linux/web_fallback_font.h",
@@ -69,7 +68,6 @@
   "+third_party/blink/public/web/WebSharedWorkerCreationErrors.h",
   "+third_party/blink/public/web/web_text_direction.h",
   "+third_party/blink/public/web/web_tree_scope_type.h",
-  "+third_party/blink/public/web/web_triggering_event_info.h",
   "+third_party/blink/public/web/win/web_font_rendering.h",
 ]
 specific_include_rules = {
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 7a1d564..24decdb 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -59,6 +59,8 @@
 #include "third_party/blink/public/common/frame/user_activation_update_type.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
 #include "third_party/blink/public/common/messaging/transferable_message.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
+#include "third_party/blink/public/common/sudden_termination_disabler_type.h"
 #include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom.h"
@@ -70,11 +72,9 @@
 #include "third_party/blink/public/platform/web_intrinsic_sizing_info.h"
 #include "third_party/blink/public/platform/web_scroll_into_view_params.h"
 #include "third_party/blink/public/platform/web_scroll_types.h"
-#include "third_party/blink/public/platform/web_sudden_termination_disabler_type.h"
 #include "third_party/blink/public/web/web_frame_owner_properties.h"
 #include "third_party/blink/public/web/web_media_player_action.h"
 #include "third_party/blink/public/web/web_tree_scope_type.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
 #include "ui/events/types/scroll_types.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -138,8 +138,8 @@
                           blink::mojom::FeaturePolicyFeature::kMaxValue)
 IPC_ENUM_TRAITS_MAX_VALUE(content::CSPDisposition,
                           content::CSPDisposition::LAST)
-IPC_ENUM_TRAITS_MAX_VALUE(blink::WebTriggeringEventInfo,
-                          blink::WebTriggeringEventInfo::kMaxValue)
+IPC_ENUM_TRAITS_MAX_VALUE(blink::TriggeringEventInfo,
+                          blink::TriggeringEventInfo::kMaxValue)
 IPC_ENUM_TRAITS_MAX_VALUE(blink::UserActivationUpdateType,
                           blink::UserActivationUpdateType::kMaxValue)
 IPC_ENUM_TRAITS_MAX_VALUE(blink::WebMediaPlayerAction::Type,
@@ -502,7 +502,7 @@
   IPC_STRUCT_MEMBER(WindowOpenDisposition, disposition)
   IPC_STRUCT_MEMBER(bool, should_replace_current_entry)
   IPC_STRUCT_MEMBER(bool, user_gesture)
-  IPC_STRUCT_MEMBER(blink::WebTriggeringEventInfo, triggering_event_info)
+  IPC_STRUCT_MEMBER(blink::TriggeringEventInfo, triggering_event_info)
   IPC_STRUCT_MEMBER(mojo::MessagePipeHandle, blob_url_token)
   IPC_STRUCT_MEMBER(std::string, href_translate)
   IPC_STRUCT_MEMBER(content::NavigationDownloadPolicy, download_policy)
@@ -1365,7 +1365,7 @@
 // removed.
 IPC_MESSAGE_ROUTED2(FrameHostMsg_SuddenTerminationDisablerChanged,
                     bool /* present */,
-                    blink::WebSuddenTerminationDisablerType /* disabler_type */)
+                    blink::SuddenTerminationDisablerType /* disabler_type */)
 
 // Requests that the resource timing info be added to the performance entries of
 // a remote parent frame.
diff --git a/content/common/page_messages.h b/content/common/page_messages.h
index 3400716..21fd5d1e 100644
--- a/content/common/page_messages.h
+++ b/content/common/page_messages.h
@@ -58,6 +58,9 @@
 IPC_MESSAGE_ROUTED1(PageMsg_UpdateTextAutosizerPageInfoForRemoteMainFrames,
                     blink::WebTextAutosizerPageInfo /* page_info */)
 
+// Sends updated preferences to the renderer.
+IPC_MESSAGE_ROUTED1(PageMsg_SetRendererPrefs, blink::mojom::RendererPreferences)
+
 // -----------------------------------------------------------------------------
 // Messages sent from the renderer to the browser.
 
diff --git a/content/common/renderer.mojom b/content/common/renderer.mojom
index 8179ef9f..4e4c161 100644
--- a/content/common/renderer.mojom
+++ b/content/common/renderer.mojom
@@ -174,24 +174,14 @@
   map<SystemThemeColor, uint32> colors;
 };
 
-// The background state for the render process.  When backgrounded the process's
-// priority will be lower (via base::Process::SetProcessBackgrounded()) if
-// allowed on the current platform (as determined by
-// base::Process::CanBackgroundProcesses()).
-enum RenderProcessBackgroundState {
-  // The renderer process has not been backgrounded, a hidden renderer may still
-  // be foregrounded, e.g. when it is playing audio.
-  kForegrounded,
-  // The renderer process has been backgrounded, a visible renderer may still
-  // be backgrounded, e.g. when it is hosting only low priority frames.
-  kBackgrounded,
-};
-
-// The visibility state for the renderer process, indicating whether or not any
-// of the frames associated with the renderer process are visible.
-enum RenderProcessVisibleState {
+enum RenderProcessState {
   kVisible,
+  // Hidden render processes can still be foregrounded. For example, a hidden
+  // renderer playing audio would be foregrounded.
   kHidden,
+  // Refers to a renderer that is hidden and running at background process
+  // priority.
+  kBackgrounded,
 };
 
 // The primordial Channel-associated interface implemented by a render process.
@@ -273,8 +263,7 @@
 
 
   // Tells the renderer process of a change in visibility or background state.
-  SetProcessState(RenderProcessBackgroundState background_state,
-                  RenderProcessVisibleState visible_state);
+  SetProcessState(RenderProcessState process_state);
 
   // Tells the scheduler about "keep-alive" state which can be due to:
   // service workers, shared workers, or fetch keep-alive.
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index 73bbbf9..ac32a53c 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -115,9 +115,6 @@
 // Make the RenderWidget background transparent or opaque.
 IPC_MESSAGE_ROUTED1(ViewMsg_SetBackgroundOpaque, bool /* opaque */)
 
-// Sends updated preferences to the renderer.
-IPC_MESSAGE_ROUTED1(ViewMsg_SetRendererPrefs, blink::mojom::RendererPreferences)
-
 // This passes a set of webkit preferences down to the renderer.
 IPC_MESSAGE_ROUTED1(ViewMsg_UpdateWebPreferences,
                     content::WebPreferences)
@@ -200,14 +197,6 @@
                     bool /* result */)
 #endif
 
-// Updates a Renderer's visual properties, including both page properties and
-// widget properties. Eventually, we'd like to be able to send separate IPCs,
-// but it's not clear if that's possible. The |widget_routing_id| is used to
-// forward widget visual properties to the relevant widget.
-IPC_MESSAGE_ROUTED2(ViewMsg_UpdateVisualProperties,
-                    content::VisualProperties /* visual_properties */,
-                    int /* widget_routing_id */)
-
 // Sent to the main-frame's view to request performing a page scale animation
 // based on the point/rect provided.
 IPC_MESSAGE_ROUTED2(ViewMsg_AnimateDoubleTapZoom,
diff --git a/content/common/widget_messages.h b/content/common/widget_messages.h
index a780b43..1fbd8f1f 100644
--- a/content/common/widget_messages.h
+++ b/content/common/widget_messages.h
@@ -147,8 +147,14 @@
 // are in progress.
 IPC_MESSAGE_ROUTED0(WidgetMsg_SetBounds_ACK)
 
+// Updates a RenderWidget's visual properties. This should include all
+// geometries and compositing inputs so that they are updated atomically.
+IPC_MESSAGE_ROUTED1(WidgetMsg_UpdateVisualProperties,
+                    content::VisualProperties /* visual_properties */)
+
 // Informs the RenderWidget of its position on the user's screen, as well as
 // the position of the native window holding the RenderWidget.
+// TODO(danakj): These should be part of UpdateVisualProperties.
 IPC_MESSAGE_ROUTED2(WidgetMsg_UpdateScreenRects,
                     gfx::Rect /* widget_screen_rect */,
                     gfx::Rect /* window_screen_rect */)
diff --git a/content/gpu/gpu_child_thread.cc b/content/gpu/gpu_child_thread.cc
index 04fbf79..9ee49d360 100644
--- a/content/gpu/gpu_child_thread.cc
+++ b/content/gpu/gpu_child_thread.cc
@@ -32,6 +32,8 @@
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
 #include "ipc/ipc_sync_message_filter.h"
 #include "media/gpu/ipc/service/media_gpu_channel_manager.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/metrics/public/cpp/mojo_ukm_recorder.h"
 #include "services/metrics/public/mojom/ukm_interface.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
@@ -383,17 +385,19 @@
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     const base::UnguessableToken& routing_token,
     media::AndroidOverlayConfig config) {
-  media::mojom::AndroidOverlayProviderPtr overlay_provider;
+  mojo::PendingRemote<media::mojom::AndroidOverlayProvider> overlay_provider;
   if (main_task_runner->RunsTasksInCurrentSequence()) {
-    ChildThread::Get()->BindHostReceiver(mojo::MakeRequest(&overlay_provider));
+    ChildThread::Get()->BindHostReceiver(
+        overlay_provider.InitWithNewPipeAndPassReceiver());
   } else {
     main_task_runner->PostTask(
         FROM_HERE,
         base::BindOnce(
-            [](media::mojom::AndroidOverlayProviderRequest request) {
-              ChildThread::Get()->BindHostReceiver(std::move(request));
+            [](mojo::PendingReceiver<media::mojom::AndroidOverlayProvider>
+                   receiver) {
+              ChildThread::Get()->BindHostReceiver(std::move(receiver));
             },
-            mojo::MakeRequest(&overlay_provider)));
+            overlay_provider.InitWithNewPipeAndPassReceiver()));
   }
 
   return std::make_unique<media::MojoAndroidOverlay>(
diff --git a/content/public/app/content_browser_manifest.cc b/content/public/app/content_browser_manifest.cc
index 08dc2e5..d13e5f9e 100644
--- a/content/public/app/content_browser_manifest.cc
+++ b/content/public/app/content_browser_manifest.cc
@@ -123,6 +123,7 @@
           .RequireCapability("network", "test")
           .RequireCapability(mojom::kRendererServiceName, "browser")
           .RequireCapability("media", "media:media")
+          .RequireCapability("media_renderer", "media:media")
           .RequireCapability("*", "app")
           .RequireCapability("content", "navigation")
           .RequireCapability("resource_coordinator", "service_callbacks")
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index d7cf72e..0cca034 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -251,6 +251,7 @@
     "page_navigator.h",
     "page_visibility_state.h",
     "payment_app_provider.h",
+    "peak_gpu_memory_tracker.h",
     "pepper_flash_settings_helper.h",
     "pepper_vpn_provider_resource_host_proxy.h",
     "permission_controller.h",
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 75ca397..f2b2365 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -1016,4 +1016,12 @@
   return false;
 }
 
+bool ContentBrowserClient::ArePersistentMediaDeviceIDsAllowed(
+    content::BrowserContext* browser_context,
+    const GURL& scope,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& top_frame_origin) {
+  return false;
+}
+
 }  // namespace content
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index cbcb02c7..7a02c26 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1696,6 +1696,13 @@
   // Returns true if the extra ICU data file is available and should be used to
   // initialize ICU.
   virtual bool ShouldLoadExtraIcuDataFile();
+
+  // Returns true if the site is allowed to use persistent media device IDs.
+  virtual bool ArePersistentMediaDeviceIDsAllowed(
+      content::BrowserContext* browser_context,
+      const GURL& scope,
+      const GURL& site_for_cookies,
+      const base::Optional<url::Origin>& top_frame_origin);
 };
 
 }  // namespace content
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h
index d671551..e2bbb05 100644
--- a/content/public/browser/navigation_handle.h
+++ b/content/public/browser/navigation_handle.h
@@ -66,11 +66,16 @@
   // e.g. URLs might be rewritten by the renderer before being committed.
   virtual const GURL& GetURL() = 0;
 
-  // Returns the SiteInstance that started the request.
-  // If a frame in SiteInstance A navigates a frame in SiteInstance B to a URL
-  // in SiteInstance C, then this returns B.
+  // Returns the SiteInstance where the frame being navigated was at the start
+  // of the navigation.  If a frame in SiteInstance A navigates a frame in
+  // SiteInstance B to a URL in SiteInstance C, then this returns B.
   virtual SiteInstance* GetStartingSiteInstance() = 0;
 
+  // Returns the SiteInstance of the initiator of the navigation.  If a frame in
+  // SiteInstance A navigates a frame in SiteInstance B to a URL in SiteInstance
+  // C, then this returns A.
+  virtual SiteInstance* GetSourceSiteInstance() = 0;
+
   // Whether the navigation is taking place in the main frame or in a subframe.
   // This remains constant over the navigation lifetime.
   virtual bool IsInMainFrame() = 0;
diff --git a/content/public/browser/page_navigator.cc b/content/public/browser/page_navigator.cc
index ad8ddc6..2b39154 100644
--- a/content/public/browser/page_navigator.cc
+++ b/content/public/browser/page_navigator.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "content/public/browser/page_navigator.h"
+#include "content/browser/frame_host/navigation_request.h"
 
 namespace content {
 
@@ -66,4 +67,26 @@
 
 OpenURLParams::~OpenURLParams() = default;
 
+// static
+OpenURLParams OpenURLParams::FromNavigationHandle(NavigationHandle* handle) {
+  OpenURLParams params(
+      handle->GetURL(), Referrer(handle->GetReferrer()),
+      handle->GetFrameTreeNodeId(), WindowOpenDisposition::CURRENT_TAB,
+      handle->GetPageTransition(), handle->IsRendererInitiated());
+
+  params.initiator_origin = handle->GetInitiatorOrigin();
+  params.source_site_instance = handle->GetSourceSiteInstance();
+  params.redirect_chain = handle->GetRedirectChain();
+  params.user_gesture = handle->HasUserGesture();
+  params.started_from_context_menu = handle->WasStartedFromContextMenu();
+  params.href_translate = handle->GetHrefTranslate();
+  params.reload_type = handle->GetReloadType();
+
+  // TODO(lukasza): Consider also covering |post_data| (and |uses_post|) and
+  // |extra_headers| (this is difficult, because we can't cast |handle| to
+  // NavigationRequest*, because it may be MockNavigationHandle in unit tests).
+
+  return params;
+}
+
 }  // namespace content
diff --git a/content/public/browser/page_navigator.h b/content/public/browser/page_navigator.h
index da36d45..0694536 100644
--- a/content/public/browser/page_navigator.h
+++ b/content/public/browser/page_navigator.h
@@ -23,13 +23,14 @@
 #include "ipc/ipc_message.h"
 #include "services/network/public/cpp/resource_request_body.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/window_open_disposition.h"
 #include "url/gurl.h"
 
 namespace content {
 
+class NavigationHandle;
 class WebContents;
 
 struct CONTENT_EXPORT OpenURLParams {
@@ -53,6 +54,11 @@
   OpenURLParams(const OpenURLParams& other);
   ~OpenURLParams();
 
+  // Creates OpenURLParams that 1) preserve all applicable |handle| properties
+  // (URL, referrer, initiator, etc.) with OpenURLParams equivalents and 2) fill
+  // in reasonable defaults for other properties (like WindowOpenDisposition).
+  static OpenURLParams FromNavigationHandle(NavigationHandle* handle);
+
   // The URL/referrer to be opened.
   GURL url;
   Referrer referrer;
@@ -107,8 +113,8 @@
 
   // Whether the call to OpenURL was triggered by an Event, and what the
   // isTrusted flag of the event was.
-  blink::WebTriggeringEventInfo triggering_event_info =
-      blink::WebTriggeringEventInfo::kUnknown;
+  blink::TriggeringEventInfo triggering_event_info =
+      blink::TriggeringEventInfo::kUnknown;
 
   // Indicates whether this navigation was started via context menu.
   bool started_from_context_menu;
diff --git a/content/public/browser/peak_gpu_memory_tracker.h b/content/public/browser/peak_gpu_memory_tracker.h
new file mode 100644
index 0000000..8d9e78e
--- /dev/null
+++ b/content/public/browser/peak_gpu_memory_tracker.h
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_PUBLIC_BROWSER_PEAK_GPU_MEMORY_TRACKER_H_
+#define CONTENT_PUBLIC_BROWSER_PEAK_GPU_MEMORY_TRACKER_H_
+
+#include <stdint.h>
+#include <memory>
+
+#include "base/callback.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// Tracks the peak memory of the GPU service for its lifetime. Upon its
+// destruction a report will be requested from the GPU service. The peak will be
+// reported to the provided PeakMemoryCallback. This will occur on the thread
+// that this was created on.
+//
+// If the GPU is lost during this objects lifetime, upon destruction the
+// PeakMemoryCallback will be ran with "0" as the reported peak usage. The same
+// for if there is never a successful GPU connection.
+//
+// See PeakGpuMemoryTracker::Create.
+class CONTENT_EXPORT PeakGpuMemoryTracker {
+ public:
+  // The callback will be provided with the peak memory usage in Bytes. Or 0 if
+  // getting a report from the GPU service was not possible.
+  using PeakMemoryCallback = base::OnceCallback<void(uint64_t)>;
+
+  // Creates the PeakGpuMemoryTracker, which performs the registration with the
+  // GPU service. Destroy the PeakGpuMemoryTracker to request a report from the
+  // GPU service. The report will be delivered to |callback| on the thread which
+  // calls Create.
+  static std::unique_ptr<PeakGpuMemoryTracker> Create(
+      PeakMemoryCallback callback);
+
+  virtual ~PeakGpuMemoryTracker() = default;
+
+  PeakGpuMemoryTracker(const PeakGpuMemoryTracker*) = delete;
+  PeakGpuMemoryTracker& operator=(const PeakGpuMemoryTracker&) = delete;
+
+ protected:
+  PeakGpuMemoryTracker() = default;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_PEAK_GPU_MEMORY_TRACKER_H_
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index e0f9cda..2e76b75d 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -26,10 +26,10 @@
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
 #include "third_party/blink/public/common/frame/sandbox_flags.h"
 #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
+#include "third_party/blink/public/common/sudden_termination_disabler_type.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "third_party/blink/public/mojom/frame/frame.mojom.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom-forward.h"
-#include "third_party/blink/public/platform/web_sudden_termination_disabler_type.h"
 #include "ui/accessibility/ax_tree_id.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/native_widget_types.h"
@@ -330,7 +330,7 @@
   // Check whether the specific Blink feature is currently preventing fast
   // shutdown of the frame.
   virtual bool GetSuddenTerminationDisablerState(
-      blink::WebSuddenTerminationDisablerType disabler_type) = 0;
+      blink::SuddenTerminationDisablerType disabler_type) = 0;
 
   // Returns true if the given |threshold_value| is below the threshold value
   // specified in the policy for |feature| for this RenderFrameHost. See
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h
index a82248cd..812102b6 100644
--- a/content/public/browser/render_process_host.h
+++ b/content/public/browser/render_process_host.h
@@ -85,10 +85,6 @@
  public:
   using iterator = base::IDMap<RenderProcessHost*>::iterator;
 
-  // The priority of a frame added to the RenderProcessHost.
-  // TODO(ericrobinson): Move to RenderProcessHostImpl after Mock refactor.
-  enum class FramePriority { kLow, kNormal };
-
   // Priority (or on Android, the importance) that a client contributes to this
   // RenderProcessHost. Eg a RenderProcessHost with a visible client has higher
   // priority / importance than a RenderProcessHost with hidden clients only.
@@ -163,15 +159,6 @@
   // individual priority changes.
   virtual void UpdateClientPriority(PriorityClient* client) = 0;
 
-  // Update the total and low priority count as indicated by the previous and
-  // new priorities of the underlying document.  The nullopt option is used when
-  // there is no previous/subsequent navigation (when the frame is added/removed
-  // from the RenderProcessHost).
-  // TODO(ericrobinson): Move to RenderProcessHostImpl after Mock refactor.
-  virtual void UpdateFrameWithPriority(
-      base::Optional<FramePriority> previous_priority,
-      base::Optional<FramePriority> new_priority) = 0;
-
   // Number of visible (ie |!is_hidden|) PriorityClients.
   virtual int VisibleClientCount() = 0;
 
diff --git a/content/public/browser/render_view_host.h b/content/public/browser/render_view_host.h
index eb86c23..a0c210b 100644
--- a/content/public/browser/render_view_host.h
+++ b/content/public/browser/render_view_host.h
@@ -104,10 +104,6 @@
   virtual void SetWebUIProperty(const std::string& name,
                                 const std::string& value) = 0;
 
-  // Sends the renderer process the current preferences supplied by the
-  // RenderViewHostDelegate.
-  virtual void SyncRendererPrefs() = 0;
-
   // TODO(mustaq): Replace "Webkit" from the following three method names.
   //
   // Returns the current WebKit preferences. Note: WebPreferences is cached, so
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 6708e25..075dd254 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -601,6 +601,10 @@
   // an IPC to all the renderer processes associated with this WebContents.
   virtual void NotifyPreferencesChanged() = 0;
 
+  // Sends the current preferences to all renderer processes for the current
+  // page.
+  virtual void SyncRendererPrefs() = 0;
+
   // Notifies WebContents that an attempt has been made to read the cookies in
   // |cookie_list|.
   virtual void OnCookiesRead(const GURL& url,
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 4fffb0c..9dcdf2d 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -325,11 +325,4 @@
     WebContents* source) {
   return false;
 }
-
-bool WebContentsDelegate::IsFrameLowPriority(
-    const WebContents* web_contents,
-    const RenderFrameHost* render_frame_host) {
-  return false;
-}
-
 }  // namespace content
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 6c9e6b0..dedaf25a 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -670,10 +670,6 @@
   // solid color is displayed instead.
   virtual bool ShouldShowStaleContentOnEviction(WebContents* source);
 
-  // Determine if the frame is of a low priority.
-  virtual bool IsFrameLowPriority(const WebContents* web_contents,
-                                  const RenderFrameHost* render_frame_host);
-
  protected:
   virtual ~WebContentsDelegate();
 
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 42ddd152..8892630 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -88,11 +88,6 @@
 const base::Feature kBrowserVerifiedUserActivation{
     "BrowserVerifiedUserActivation", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Controls whether Bundled HTTP Exchanges is enabled.
-// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html
-const base::Feature kBundledHTTPExchanges{"BundledHTTPExchanges",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Enables code caching for inline scripts.
 const base::Feature kCacheInlineScriptCode{"CacheInlineScriptCode",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
@@ -554,13 +549,6 @@
 const base::Feature kTouchpadAsyncPinchEvents{"TouchpadAsyncPinchEvents",
                                               base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Controls whether the RenderProcessHost uses its frames' priorities for
-// determining if it should be backgrounded. When all frames associated with a
-// RenderProcessHost are low priority, that process may be backgrounded even if
-// those frames are visible.
-const base::Feature kUseFramePriorityInRenderProcessHost{
-    "UseFramePriorityInRenderProcessHost", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Allows developers transfer user activation state to any target window in the
 // frame tree.
 const base::Feature kUserActivationPostMessageTransfer{
@@ -670,6 +658,11 @@
 #endif
 };
 
+// Controls whether Web Bundles (Bundled HTTP Exchanges) is enabled.
+// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html
+const base::Feature kWebBundles{"WebBundles",
+                                base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If WebGL Image Chromium is allowed, this feature controls whether it is
 // enabled.
 const base::Feature kWebGLImageChromium{"WebGLImageChromium",
@@ -710,7 +703,7 @@
 
 // Enables access to the WebXR Device API gamepad module.
 const base::Feature kWebXrGamepadModule{"WebXrGamepadModule",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables access to raycasting against estimated XR scene geometry.
 const base::Feature kWebXrHitTest{"WebXRHitTest",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index fbbe644..06f72499 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -29,7 +29,6 @@
 CONTENT_EXPORT extern const base::Feature kBackForwardCache;
 CONTENT_EXPORT extern const base::Feature kBlockCredentialedSubresources;
 CONTENT_EXPORT extern const base::Feature kBrowserVerifiedUserActivation;
-CONTENT_EXPORT extern const base::Feature kBundledHTTPExchanges;
 CONTENT_EXPORT extern const base::Feature kCacheInlineScriptCode;
 CONTENT_EXPORT extern const base::Feature kCacheStorageParallelOps;
 CONTENT_EXPORT extern const base::Feature kCacheStorageEagerReading;
@@ -122,7 +121,6 @@
 CONTENT_EXPORT extern const base::Feature kTimerThrottlingForHiddenFrames;
 CONTENT_EXPORT extern const base::Feature kTouchpadAsyncPinchEvents;
 CONTENT_EXPORT extern const base::Feature kTouchpadOverscrollHistoryNavigation;
-CONTENT_EXPORT extern const base::Feature kUseFramePriorityInRenderProcessHost;
 CONTENT_EXPORT extern const base::Feature kUserActivationPostMessageTransfer;
 CONTENT_EXPORT extern const base::Feature kUserActivationSameOriginVisibility;
 CONTENT_EXPORT extern const base::Feature kUserActivationV2;
@@ -138,6 +136,7 @@
 CONTENT_EXPORT extern const base::Feature kWebAuth;
 CONTENT_EXPORT extern const base::Feature kWebAuthBle;
 CONTENT_EXPORT extern const base::Feature kWebAuthCable;
+CONTENT_EXPORT extern const base::Feature kWebBundles;
 CONTENT_EXPORT extern const base::Feature kWebContentsOcclusion;
 CONTENT_EXPORT extern const base::Feature kWebGLImageChromium;
 CONTENT_EXPORT extern const base::Feature kWebPayments;
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index 989b912..ed0cb9c6 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -20,11 +20,11 @@
 #include "ppapi/buildflags/buildflags.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/web/web_navigation_policy.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
 #include "ui/accessibility/ax_mode.h"
 
 namespace blink {
diff --git a/content/public/renderer/render_frame_observer.h b/content/public/renderer/render_frame_observer.h
index a5fe51a..f764efa2 100644
--- a/content/public/renderer/render_frame_observer.h
+++ b/content/public/renderer/render_frame_observer.h
@@ -76,6 +76,7 @@
   // Each navigation starts with a DidStartNavigation call. Then it may be
   // followed by a ReadyToCommitNavigation (if the navigation has succeeded),
   // and should always end with a DidFinishNavigation.
+  // TODO(dgozman): ReadyToCommitNavigation will be removed soon.
   //
   // Unfortunately, this is currently a mess. For example, some started
   // navigations which did not commit won't receive any further notifications.
@@ -94,8 +95,11 @@
       const GURL& url,
       base::Optional<blink::WebNavigationType> navigation_type) {}
 
-  // Called when a navigation is about to be committed and |document_loader|
+  // Called when a navigation has just committed and |document_loader|
   // will start loading a new document in the RenderFrame.
+  // TODO(dgozman): the name does not match functionality anymore, we should
+  // merge this with DidCommitProvisionalLoad, which will become
+  // DidFinishNavigation.
   virtual void ReadyToCommitNavigation(
       blink::WebDocumentLoader* document_loader) {}
 
diff --git a/content/public/test/mock_navigation_handle.h b/content/public/test/mock_navigation_handle.h
index de5fac7c..31274f6 100644
--- a/content/public/test/mock_navigation_handle.h
+++ b/content/public/test/mock_navigation_handle.h
@@ -30,6 +30,9 @@
   SiteInstance* GetStartingSiteInstance() override {
     return starting_site_instance_;
   }
+  SiteInstance* GetSourceSiteInstance() override {
+    return nullptr;  // Good enough for unit tests...
+  }
   bool IsInMainFrame() override {
     return render_frame_host_ ? !render_frame_host_->GetParent() : true;
   }
@@ -94,7 +97,7 @@
   MOCK_METHOD0(HasPrefetchedAlternativeSubresourceSignedExchange, bool());
   bool WasResponseCached() override { return was_response_cached_; }
   const net::ProxyServer& GetProxyServer() override { return proxy_server_; }
-  MOCK_METHOD0(GetHrefTranslate, const std::string&());
+  const std::string& GetHrefTranslate() override { return href_translate_; }
   const base::Optional<url::Origin>& GetInitiatorOrigin() override {
     return initiator_origin_;
   }
@@ -173,6 +176,7 @@
   net::ProxyServer proxy_server_;
   base::Optional<url::Origin> initiator_origin_;
   ReloadType reload_type_ = content::ReloadType::NONE;
+  std::string href_translate_;
 };
 
 }  // namespace content
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index 8445d70..1830e32b 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -159,10 +159,6 @@
 
 void MockRenderProcessHost::UpdateClientPriority(PriorityClient* client) {}
 
-void MockRenderProcessHost::UpdateFrameWithPriority(
-    base::Optional<FramePriority> previous_priority,
-    base::Optional<FramePriority> new_priority) {}
-
 int MockRenderProcessHost::VisibleClientCount() {
   int count = 0;
   for (auto* client : priority_clients_) {
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h
index d28777f..fd4afea 100644
--- a/content/public/test/mock_render_process_host.h
+++ b/content/public/test/mock_render_process_host.h
@@ -87,9 +87,6 @@
   void RemoveObserver(RenderProcessHostObserver* observer) override;
   void ShutdownForBadMessage(CrashReportMode crash_report_mode) override;
   void UpdateClientPriority(PriorityClient* client) override;
-  void UpdateFrameWithPriority(
-      base::Optional<FramePriority> previous_priority,
-      base::Optional<FramePriority> new_priority) override;
   int VisibleClientCount() override;
   unsigned int GetFrameDepth() override;
   bool GetIntersectsViewport() override;
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 3b27daa..7635ba48 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -688,6 +688,9 @@
 
 void RenderViewTest::Resize(gfx::Size new_size,
                             bool is_fullscreen_granted) {
+  RenderViewImpl* view = static_cast<RenderViewImpl*>(view_);
+  RenderWidget* render_widget = view->GetWidget();
+
   VisualProperties visual_properties;
   visual_properties.screen_info = ScreenInfo();
   visual_properties.new_size = new_size;
@@ -696,13 +699,10 @@
   visual_properties.browser_controls_shrink_blink_size = false;
   visual_properties.is_fullscreen_granted = is_fullscreen_granted;
   visual_properties.display_mode = blink::mojom::DisplayMode::kBrowser;
-  RenderViewImpl* view = static_cast<RenderViewImpl*>(view_);
-  RenderWidget* render_widget = view->GetWidget();
-  std::unique_ptr<IPC::Message> resize_message(
-      new ViewMsg_UpdateVisualProperties(view->GetRoutingID(),
-                                         visual_properties,
-                                         render_widget->routing_id()));
-  view->OnMessageReceived(*resize_message);
+
+  WidgetMsg_UpdateVisualProperties resize_msg(render_widget->routing_id(),
+                                              visual_properties);
+  render_widget->OnMessageReceived(resize_msg);
 }
 
 void RenderViewTest::SimulateUserTypingASCIICharacter(char ascii_character,
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 267600e..4a3583e 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -6,7 +6,6 @@
 import("//build/config/features.gni")
 import("//build/config/jumbo.gni")
 import("//build/config/ui.gni")
-import("//build/split_static_library.gni")
 import("//content/common/features.gni")
 import("//media/media_options.gni")
 import("//ppapi/buildflags/buildflags.gni")
@@ -17,7 +16,7 @@
 if (is_component_build) {
   link_target_type = "jumbo_source_set"
 } else {
-  link_target_type = "jumbo_split_static_library"
+  link_target_type = "jumbo_static_library"
 }
 
 target(link_target_type, "renderer") {
@@ -323,15 +322,6 @@
     ]
   }
 
-  if (!is_component_build) {
-    if (is_win && is_official_build) {
-      split_count = 2  # In certain configurations a full renderer.lib can
-                       # be 2+ GB which breaks some Windows tools.
-    } else {
-      split_count = 1
-    }
-  }
-
   configs += [
     "//content:content_implementation",
     "//build/config/compiler:no_size_t_to_int_warning",
diff --git a/content/renderer/media/android/media_player_renderer_client.cc b/content/renderer/media/android/media_player_renderer_client.cc
index 20c61db..9a12c93 100644
--- a/content/renderer/media/android/media_player_renderer_client.cc
+++ b/content/renderer/media/android/media_player_renderer_client.cc
@@ -115,6 +115,13 @@
     renderer_extension_ptr_->InitiateScopedSurfaceRequest(
         base::Bind(&MediaPlayerRendererClient::OnScopedSurfaceRequested,
                    weak_factory_.GetWeakPtr()));
+
+    // Signal that we're using MediaPlayer so that we can properly differentiate
+    // within our metrics.
+    media::PipelineStatistics stats;
+    stats.video_decoder_info =
+        stats.audio_decoder_info = {true, false, "MediaPlayer"};
+    client_->OnStatisticsUpdate(stats);
   }
   std::move(init_cb_).Run(status);
 }
diff --git a/content/renderer/media/media_interface_factory.cc b/content/renderer/media/media_interface_factory.cc
index 0197bd4..db4ab44 100644
--- a/content/renderer/media/media_interface_factory.cc
+++ b/content/renderer/media/media_interface_factory.cc
@@ -27,16 +27,16 @@
 }
 
 void MediaInterfaceFactory::CreateAudioDecoder(
-    media::mojom::AudioDecoderRequest request) {
+    mojo::PendingReceiver<media::mojom::AudioDecoder> receiver) {
   if (!task_runner_->BelongsToCurrentThread()) {
     task_runner_->PostTask(
         FROM_HERE, base::BindOnce(&MediaInterfaceFactory::CreateAudioDecoder,
-                                  weak_this_, std::move(request)));
+                                  weak_this_, std::move(receiver)));
     return;
   }
 
   DVLOG(1) << __func__;
-  GetMediaInterfaceFactory()->CreateAudioDecoder(std::move(request));
+  GetMediaInterfaceFactory()->CreateAudioDecoder(std::move(receiver));
 }
 
 void MediaInterfaceFactory::CreateVideoDecoder(
diff --git a/content/renderer/media/media_interface_factory.h b/content/renderer/media/media_interface_factory.h
index e58efa5..45a5460 100644
--- a/content/renderer/media/media_interface_factory.h
+++ b/content/renderer/media/media_interface_factory.h
@@ -34,7 +34,8 @@
   ~MediaInterfaceFactory() final;
 
   // media::mojom::InterfaceFactory implementation.
-  void CreateAudioDecoder(media::mojom::AudioDecoderRequest request) final;
+  void CreateAudioDecoder(
+      mojo::PendingReceiver<media::mojom::AudioDecoder> receiver) final;
   void CreateVideoDecoder(media::mojom::VideoDecoderRequest request) final;
   void CreateDefaultRenderer(const std::string& audio_device_id,
                              media::mojom::RendererRequest request) final;
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 5259079..799dcc81 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -2488,7 +2488,7 @@
 scoped_refptr<base::SingleThreadTaskRunner>
 RTCPeerConnectionHandler::signaling_thread() const {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  return dependency_factory_->GetWebRtcSignalingThread();
+  return dependency_factory_->GetWebRtcSignalingTaskRunner();
 }
 
 blink::WebRTCSessionDescription
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
index 7ac4048..a31bfb19 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
@@ -558,7 +558,7 @@
 
   template <typename T>
   void InvokeOnSignalingThread(T callback) {
-    mock_dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+    mock_dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
         FROM_HERE, std::move(callback));
     RunMessageLoopsUntilIdle();
   }
@@ -569,7 +569,7 @@
     base::WaitableEvent waitable_event(
         base::WaitableEvent::ResetPolicy::MANUAL,
         base::WaitableEvent::InitialState::NOT_SIGNALED);
-    mock_dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+    mock_dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
         FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal,
                                   base::Unretained(&waitable_event)));
     waitable_event.Wait();
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
index 3f5695a..de38efa 100644
--- a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
@@ -231,7 +231,7 @@
     observer_ = new WebRtcSetDescriptionObserverForTest();
     observer_handler_ = std::make_unique<ObserverHandlerWrapper>(
         handler_type_, main_thread_,
-        dependency_factory_->GetWebRtcSignalingThread(), pc_,
+        dependency_factory_->GetWebRtcSignalingTaskRunner(), pc_,
         track_adapter_map_, observer_,
         surfacer_type_ == StateSurfacerType::kReceiversOnly);
   }
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 09870291..8163ba87 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1530,8 +1530,7 @@
                                   &params->visual_properties.screen_info);
   // AttachWebFrameWidget() is not needed here since InitForMainFrame() received
   // the WebFrameWidget.
-  render_widget->SynchronizeVisualPropertiesFromRenderView(
-      params->visual_properties);
+  render_widget->OnUpdateVisualProperties(params->visual_properties);
 
   // The WebFrame created here was already attached to the Page as its
   // main frame, and the WebFrameWidget has been initialized, so we can call
@@ -1749,7 +1748,7 @@
     // thus would not get VisualProperty updates while the frame is provisional,
     // we need at least one update to them in order to meet expectations in the
     // renderer, and that update comes as part of the CreateFrame message.
-    render_frame->render_widget_->SynchronizeVisualPropertiesFromRenderView(
+    render_frame->render_widget_->OnUpdateVisualProperties(
         widget_params->visual_properties);
   }
 
@@ -5691,7 +5690,7 @@
 
 void RenderFrameImpl::SuddenTerminationDisablerChanged(
     bool present,
-    blink::WebSuddenTerminationDisablerType disabler_type) {
+    blink::SuddenTerminationDisablerType disabler_type) {
   Send(new FrameHostMsg_SuddenTerminationDisablerChanged(routing_id_, present,
                                                          disabler_type));
 }
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 611d2d2..c971160f 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -83,6 +83,7 @@
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
 #include "third_party/blink/public/common/loader/loading_behavior_flag.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "third_party/blink/public/mojom/autoplay/autoplay.mojom.h"
 #include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
 #include "third_party/blink/public/mojom/commit_result/commit_result.mojom.h"
@@ -102,7 +103,6 @@
 #include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/public/web/web_meaningful_layout.h"
 #include "third_party/blink/public/web/web_script_execution_callback.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
 #include "ui/accessibility/ax_mode.h"
 #include "ui/gfx/range/range.h"
 #include "url/gurl.h"
@@ -835,7 +835,7 @@
   void HandleAccessibilityFindInPageTermination() override;
   void SuddenTerminationDisablerChanged(
       bool present,
-      blink::WebSuddenTerminationDisablerType disabler_type) override;
+      blink::SuddenTerminationDisablerType disabler_type) override;
   void RegisterProtocolHandler(const blink::WebString& scheme,
                                const blink::WebURL& url,
                                const blink::WebString& title) override;
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index 9e174e43..cf7c893 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -22,7 +22,6 @@
 #include "content/common/navigation_params_mojom_traits.h"
 #include "content/common/renderer.mojom.h"
 #include "content/common/unfreezable_frame_messages.h"
-#include "content/common/view_messages.h"
 #include "content/common/widget_messages.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/previews_state.h"
@@ -224,20 +223,18 @@
   // subframe's widget, and it will set the size for the WebView.
   RenderWidget* main_frame_widget =
       GetMainRenderFrame()->GetLocalRootRenderWidget();
-  ViewMsg_UpdateVisualProperties resize_message(
-      view_->GetRoutingID(), visual_properties,
-      main_frame_widget->routing_id());
+  WidgetMsg_UpdateVisualProperties resize_message(
+      main_frame_widget->routing_id(), visual_properties);
 
-  static_cast<RenderViewImpl*>(view_)->OnMessageReceived(resize_message);
+  main_frame_widget->OnMessageReceived(resize_message);
   EXPECT_EQ(view_->GetWebView()->MainFrameWidget()->Size(),
             blink::WebSize(size));
   EXPECT_NE(frame_widget()->GetWebWidget()->Size(), blink::WebSize(size));
 
   // The subframe sets the size only for itself.
-  ViewMsg_UpdateVisualProperties resize_message_subframe(
-      view_->GetRoutingID(), visual_properties, frame_widget()->routing_id());
-  static_cast<RenderViewImpl*>(view_)->OnMessageReceived(
-      resize_message_subframe);
+  WidgetMsg_UpdateVisualProperties resize_message_subframe(
+      frame_widget()->routing_id(), visual_properties);
+  frame_widget()->OnMessageReceived(resize_message_subframe);
   EXPECT_EQ(frame_widget()->GetWebWidget()->Size(), blink::WebSize(size));
 }
 
@@ -1113,7 +1110,7 @@
       {{GURL(kNoDocumentMarkerURL), kFrameEventDidCreateNewFrame},
        {initial_empty_url, kFrameEventDidCreateNewDocument},
        {initial_empty_url, kFrameEventDidCreateDocumentElement},
-       {initial_empty_url, kFrameEventReadyToCommitNavigation},
+       {child_frame_url, kFrameEventReadyToCommitNavigation},
        // TODO(https://crbug.com/555773): It seems strange that the new
        // document is created and DidCreateNewDocument is invoked *before* the
        // provisional load would have even committed.
@@ -1167,7 +1164,7 @@
       main_frame_exerciser
           .browser_interface_broker_receiver_for_initial_empty_document(),
       {{initial_empty_url, kFrameEventDidCreateNewFrame},
-       {initial_empty_url, kFrameEventReadyToCommitNavigation},
+       {new_window_url, kFrameEventReadyToCommitNavigation},
        {new_window_url, kFrameEventDidCreateNewDocument}});
   ExpectPendingInterfaceRequestsFromSources(
       main_frame_exerciser.interface_request_for_first_document(),
@@ -1235,7 +1232,7 @@
         {{GURL(kNoDocumentMarkerURL), kFrameEventDidCreateNewFrame},
          {initial_empty_url, kFrameEventDidCreateNewDocument},
          {initial_empty_url, kFrameEventDidCreateDocumentElement},
-         {initial_empty_url, kFrameEventReadyToCommitNavigation},
+         {child_frame_url, kFrameEventReadyToCommitNavigation},
          {child_frame_url, kFrameEventDidCreateNewDocument},
          {child_frame_url, kFrameEventDidCommitProvisionalLoad},
          {child_frame_url, kFrameEventDidCreateDocumentElement}});
@@ -1293,7 +1290,7 @@
       std::move(document_interface_broker_receiver_for_first_document),
       std::move(browser_interface_broker_receiver_for_first_document),
       {{GURL(kTestFirstURL), kFrameEventAfterCommit},
-       {GURL(kTestFirstURL), kFrameEventReadyToCommitNavigation},
+       {GURL(kTestSecondURL), kFrameEventReadyToCommitNavigation},
        {GURL(kTestSecondURL), kFrameEventDidCreateNewDocument}});
 
   ASSERT_TRUE(interface_provider_request_for_second_document.is_pending());
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 3368d7d..112c703 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -39,12 +39,12 @@
 #include "printing/buildflags/buildflags.h"
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/common/frame/frame_policy.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "third_party/blink/public/platform/url_conversion.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_resource_timing_info.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/web_local_frame.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
 #include "third_party/blink/public/web/web_user_gesture_indicator.h"
 #include "third_party/blink/public/web/web_view.h"
 #include "ui/gfx/geometry/size_conversions.h"
@@ -797,7 +797,7 @@
   params.disposition = WindowOpenDisposition::CURRENT_TAB;
   params.should_replace_current_entry = should_replace_current_entry;
   params.user_gesture = request.HasUserGesture();
-  params.triggering_event_info = blink::WebTriggeringEventInfo::kUnknown;
+  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
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index aaf5a4487..e2cb076 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1632,27 +1632,26 @@
 }
 
 void RenderThreadImpl::SetProcessState(
-    mojom::RenderProcessBackgroundState background_state,
-    mojom::RenderProcessVisibleState visible_state) {
-  DCHECK(background_state_ != background_state ||
-         visible_state_ != visible_state);
+    mojom::RenderProcessState process_state) {
+  DCHECK(process_state_ != process_state);
 
-  if (background_state != background_state_) {
-    if (background_state == mojom::RenderProcessBackgroundState::kForegrounded)
-      OnRendererForegrounded();
-    else
-      OnRendererBackgrounded();
+  if (process_state_ == mojom::RenderProcessState::kBackgrounded ||
+      (!process_state_.has_value() &&
+       process_state != mojom::RenderProcessState::kBackgrounded)) {
+    OnRendererForegrounded();
   }
 
-  if (visible_state != visible_state_) {
-    if (visible_state == mojom::RenderProcessVisibleState::kVisible)
-      OnRendererVisible();
-    else
-      OnRendererHidden();
+  if (process_state == mojom::RenderProcessState::kVisible) {
+    OnRendererVisible();
+  } else if (process_state_ == mojom::RenderProcessState::kVisible ||
+             !process_state_.has_value()) {
+    OnRendererHidden();
   }
 
-  background_state_ = background_state;
-  visible_state_ = visible_state;
+  if (process_state == mojom::RenderProcessState::kBackgrounded)
+    OnRendererBackgrounded();
+
+  process_state_ = process_state;
 }
 
 void RenderThreadImpl::SetIsLockedToSite() {
@@ -2307,7 +2306,8 @@
 }
 
 bool RenderThreadImpl::RendererIsHidden() const {
-  return visible_state_ == mojom::RenderProcessVisibleState::kHidden;
+  return process_state_ == mojom::RenderProcessState::kHidden ||
+         process_state_ == mojom::RenderProcessState::kBackgrounded;
 }
 
 void RenderThreadImpl::OnRendererHidden() {
@@ -2327,8 +2327,7 @@
 }
 
 bool RenderThreadImpl::RendererIsBackgrounded() const {
-  return background_state_ ==
-         mojom::RenderProcessBackgroundState::kBackgrounded;
+  return process_state_ == mojom::RenderProcessState::kBackgrounded;
 }
 
 void RenderThreadImpl::OnRendererBackgrounded() {
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 16def76..7dfca92 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -508,8 +508,7 @@
   void UpdateSystemColorInfo(
       mojom::UpdateSystemColorInfoParamsPtr params) override;
   void PurgePluginListCache(bool reload_pages) override;
-  void SetProcessState(mojom::RenderProcessBackgroundState background_state,
-                       mojom::RenderProcessVisibleState visible_state) override;
+  void SetProcessState(mojom::RenderProcessState process_state) override;
   void SetSchedulerKeepActive(bool keep_active) override;
   void SetIsLockedToSite() override;
   void EnableV8LowMemoryMode() override;
@@ -573,8 +572,7 @@
   // Used to keep track of the renderer's backgrounded and visibility state.
   // Updated via an IPC from the browser process. If nullopt, the browser
   // process has yet to send an update and the state is unknown.
-  base::Optional<mojom::RenderProcessBackgroundState> background_state_;
-  base::Optional<mojom::RenderProcessVisibleState> visible_state_;
+  base::Optional<mojom::RenderProcessState> process_state_;
 
   blink::WebString user_agent_;
   blink::UserAgentMetadata user_agent_metadata_;
diff --git a/content/renderer/render_thread_impl_browsertest.cc b/content/renderer/render_thread_impl_browsertest.cc
index a2c265a..91dab8b 100644
--- a/content/renderer/render_thread_impl_browsertest.cc
+++ b/content/renderer/render_thread_impl_browsertest.cc
@@ -240,22 +240,9 @@
  protected:
   IPC::Sender* sender() { return channel_.get(); }
 
-  void SetBackgroundState(
-      mojom::RenderProcessBackgroundState background_state) {
+  void SetProcessState(mojom::RenderProcessState process_state) {
     mojom::Renderer* renderer_interface = thread_;
-    const mojom::RenderProcessVisibleState visible_state =
-        RendererIsHidden() ? mojom::RenderProcessVisibleState::kHidden
-                           : mojom::RenderProcessVisibleState::kVisible;
-    renderer_interface->SetProcessState(background_state, visible_state);
-  }
-
-  void SetVisibleState(mojom::RenderProcessVisibleState visible_state) {
-    mojom::Renderer* renderer_interface = thread_;
-    const mojom::RenderProcessBackgroundState background_state =
-        RendererIsBackgrounded()
-            ? mojom::RenderProcessBackgroundState::kBackgrounded
-            : mojom::RenderProcessBackgroundState::kForegrounded;
-    renderer_interface->SetProcessState(background_state, visible_state);
+    renderer_interface->SetProcessState(process_state);
   }
 
   bool RendererIsBackgrounded() { return thread_->RendererIsBackgrounded(); }
@@ -317,18 +304,24 @@
 }
 
 TEST_F(RenderThreadImplBrowserTest, RendererIsBackgrounded) {
-  SetBackgroundState(mojom::RenderProcessBackgroundState::kBackgrounded);
+  SetProcessState(mojom::RenderProcessState::kBackgrounded);
   EXPECT_TRUE(RendererIsBackgrounded());
 
-  SetBackgroundState(mojom::RenderProcessBackgroundState::kForegrounded);
+  SetProcessState(mojom::RenderProcessState::kHidden);
+  EXPECT_FALSE(RendererIsBackgrounded());
+
+  SetProcessState(mojom::RenderProcessState::kVisible);
   EXPECT_FALSE(RendererIsBackgrounded());
 }
 
 TEST_F(RenderThreadImplBrowserTest, RendererIsHidden) {
-  SetVisibleState(mojom::RenderProcessVisibleState::kHidden);
+  SetProcessState(mojom::RenderProcessState::kBackgrounded);
   EXPECT_TRUE(RendererIsHidden());
 
-  SetVisibleState(mojom::RenderProcessVisibleState::kVisible);
+  SetProcessState(mojom::RenderProcessState::kHidden);
+  EXPECT_TRUE(RendererIsHidden());
+
+  SetProcessState(mojom::RenderProcessState::kVisible);
   EXPECT_FALSE(RendererIsHidden());
 }
 
@@ -339,24 +332,27 @@
   EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false));
   EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
   EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
-  SetVisibleState(mojom::RenderProcessVisibleState::kVisible);
+  SetProcessState(mojom::RenderProcessState::kVisible);
   testing::Mock::VerifyAndClear(main_thread_scheduler_);
 
   // Going from a hidden to a visible state should mark the renderer as visible.
-  SetVisibleState(mojom::RenderProcessVisibleState::kHidden);
+  // A hidden renderer is already foregrounded.
+  SetProcessState(mojom::RenderProcessState::kHidden);
   EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false));
   EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
   EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
   EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
-  SetVisibleState(mojom::RenderProcessVisibleState::kVisible);
+  SetProcessState(mojom::RenderProcessState::kVisible);
   testing::Mock::VerifyAndClear(main_thread_scheduler_);
 
-  // Going from a visible to a hidden state should mark the renderer as hidden.
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
+  // Going from a backgrounded to a visible state should mark the renderer as
+  // foregrounded and visible.
+  SetProcessState(mojom::RenderProcessState::kBackgrounded);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false));
   EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true));
-  SetVisibleState(mojom::RenderProcessVisibleState::kHidden);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
+  SetProcessState(mojom::RenderProcessState::kVisible);
   testing::Mock::VerifyAndClear(main_thread_scheduler_);
 
   testing::Mock::AllowLeak(main_thread_scheduler_);
@@ -369,7 +365,27 @@
   EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true));
   EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
   EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
-  SetVisibleState(mojom::RenderProcessVisibleState::kHidden);
+  SetProcessState(mojom::RenderProcessState::kHidden);
+  testing::Mock::VerifyAndClear(main_thread_scheduler_);
+
+  // Going from a visible to a hidden state should mark the renderer as hidden.
+  // A visible renderer is already foregrounded.
+  SetProcessState(mojom::RenderProcessState::kVisible);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true));
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
+  SetProcessState(mojom::RenderProcessState::kHidden);
+  testing::Mock::VerifyAndClear(main_thread_scheduler_);
+
+  // Going from a backgrounded to a hidden state should mark the renderer as
+  // foregrounded and hidden.
+  SetProcessState(mojom::RenderProcessState::kBackgrounded);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
+  SetProcessState(mojom::RenderProcessState::kHidden);
   testing::Mock::VerifyAndClear(main_thread_scheduler_);
 
   testing::Mock::AllowLeak(main_thread_scheduler_);
@@ -377,43 +393,32 @@
 
 TEST_F(RenderThreadImplBrowserTest, RendererStateTransitionBackgrounded) {
   // Going from an unknown to a backgrounded state should mark the renderer as
-  // backgrounded but not hidden.
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true));
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false));
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
-  SetBackgroundState(mojom::RenderProcessBackgroundState::kBackgrounded);
-  testing::Mock::VerifyAndClear(main_thread_scheduler_);
-
-  // Going from a backgrounded to a foregrounded state should mark the renderer
-  // as foregrounded.
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
-  SetBackgroundState(mojom::RenderProcessBackgroundState::kForegrounded);
-  testing::Mock::VerifyAndClear(main_thread_scheduler_);
-
-  // Going from a foregrounded to a backgrounded state should mark the renderer
-  // as backgrounded.
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
+  // hidden and backgrounded.
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true));
   EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true));
   EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
   EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
-  SetBackgroundState(mojom::RenderProcessBackgroundState::kBackgrounded);
+  SetProcessState(mojom::RenderProcessState::kBackgrounded);
   testing::Mock::VerifyAndClear(main_thread_scheduler_);
 
-  testing::Mock::AllowLeak(main_thread_scheduler_);
-}
+  // Going from a visible to a backgrounded state should mark the renderer as
+  // hidden and backgrounded.
+  SetProcessState(mojom::RenderProcessState::kVisible);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true));
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true));
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
+  SetProcessState(mojom::RenderProcessState::kBackgrounded);
+  testing::Mock::VerifyAndClear(main_thread_scheduler_);
 
-TEST_F(RenderThreadImplBrowserTest, RendererStateTransitionForegrounded) {
-  // Going from an unknown to a foregrounded state should mark the renderer as
-  // foregrounded and visible.
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
+  // Going from a hidden state to a backgrounded state should mark the renderer
+  // backgrounded. A hidden renderer is already marked as hidden.
+  SetProcessState(mojom::RenderProcessState::kHidden);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true));
   EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
-  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false));
-  SetBackgroundState(mojom::RenderProcessBackgroundState::kForegrounded);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
+  EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
+  SetProcessState(mojom::RenderProcessState::kBackgrounded);
   testing::Mock::VerifyAndClear(main_thread_scheduler_);
 
   testing::Mock::AllowLeak(main_thread_scheduler_);
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index e005197b..d7a2407 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -428,7 +428,7 @@
   int GetScrollbarWidth() {
     blink::WebView* webview = view()->webview();
     return webview->MainFrameWidget()->Size().width -
-           webview->MainFrame()->VisibleContentRect().width;
+           webview->MainFrame()->ToWebLocalFrame()->VisibleContentRect().width;
   }
 
  private:
@@ -464,11 +464,13 @@
   }
 
   void SetDeviceScaleFactor(float dsf) {
-    view()->GetWidget()->SynchronizeVisualPropertiesFromRenderView(
-        MakeVisualPropertiesWithDeviceScaleFactor(dsf));
+    RenderWidget* widget = view()->GetWidget();
+    WidgetMsg_UpdateVisualProperties msg(
+        widget->routing_id(), MakeVisualPropertiesWithDeviceScaleFactor(dsf));
+    widget->OnMessageReceived(msg);
+
     ASSERT_EQ(dsf, view()->GetMainRenderFrame()->GetDeviceScaleFactor());
-    ASSERT_EQ(dsf,
-              view()->GetWidget()->GetOriginalScreenInfo().device_scale_factor);
+    ASSERT_EQ(dsf, widget->GetOriginalScreenInfo().device_scale_factor);
   }
 
   VisualProperties MakeVisualPropertiesWithDeviceScaleFactor(float dsf) {
@@ -621,10 +623,26 @@
       FrameHostMsg_UpdateState::ID));
 }
 
+class RenderViewImplEmulatingPopupTest : public RenderViewImplTest {
+ protected:
+  VisualProperties InitialVisualProperties() override {
+    VisualProperties visual_properties =
+        RenderViewImplTest::InitialVisualProperties();
+    visual_properties.screen_info.rect = gfx::Rect(800, 600);
+    return visual_properties;
+  }
+};
+
 // Popup RenderWidgets should inherit emulation params from the parent.
-TEST_F(RenderViewImplTest, EmulatingPopupRect) {
-  gfx::Rect window_screen_rect(1, 2, 137, 137);
-  gfx::Rect widget_screen_rect(5, 7, 57, 57);
+TEST_F(RenderViewImplEmulatingPopupTest, EmulatingPopupRect) {
+  // Real screen rect set to 800x600.
+  gfx::Rect screen_rect(800, 600);
+  // Real widget and window screen rects.
+  gfx::Rect window_screen_rect(1, 2, 137, 139);
+  gfx::Rect widget_screen_rect(5, 7, 57, 59);
+
+  // Verify screen rect will be set.
+  EXPECT_EQ(gfx::Rect(view()->GetWidget()->GetScreenInfo().rect), screen_rect);
 
   {
     // Make a popup widget.
@@ -634,25 +652,38 @@
     ASSERT_TRUE(popup_widget);
 
     // Set its size.
-    popup_widget->OnUpdateScreenRects(widget_screen_rect, window_screen_rect);
+    {
+      WidgetMsg_UpdateScreenRects msg(popup_widget->routing_id(),
+                                      widget_screen_rect, window_screen_rect);
+      popup_widget->OnMessageReceived(msg);
+    }
 
-    // Check that the size was set.
-    EXPECT_EQ(window_screen_rect.x(), popup_widget->WindowRect().x);
-    EXPECT_EQ(window_screen_rect.y(), popup_widget->WindowRect().y);
+    // The WindowScreenRect, WidgetScreenRect, and ScreenRect are all available
+    // to the popup.
+    EXPECT_EQ(window_screen_rect, gfx::Rect(popup_widget->WindowRect()));
+    EXPECT_EQ(widget_screen_rect, gfx::Rect(popup_widget->ViewRect()));
+    EXPECT_EQ(screen_rect, gfx::Rect(popup_widget->GetScreenInfo().rect));
 
-    EXPECT_EQ(widget_screen_rect.x(), popup_widget->ViewRect().x);
-    EXPECT_EQ(widget_screen_rect.y(), popup_widget->ViewRect().y);
-
-    popup_widget->OnClose();
+    // Close and destroy the widget.
+    {
+      WidgetMsg_Close msg(popup_widget->routing_id());
+      popup_widget->OnMessageReceived(msg);
+    }
   }
 
   // Enable device emulation on the parent widget.
   blink::WebDeviceEmulationParams emulation_params;
-  gfx::Rect emulated_window_rect(0, 0, 980, 1200);
+  gfx::Rect emulated_widget_rect(150, 160, 980, 1200);
+  // In mobile emulation the WindowScreenRect and ScreenRect are both set to
+  // match the WidgetScreenRect, which we set here.
   emulation_params.screen_position = blink::WebDeviceEmulationParams::kMobile;
-  emulation_params.view_size = emulated_window_rect.size();
-  emulation_params.view_position = blink::WebPoint(150, 160);
-  view()->GetWidget()->OnEnableDeviceEmulation(emulation_params);
+  emulation_params.view_size = emulated_widget_rect.size();
+  emulation_params.view_position = emulated_widget_rect.origin();
+  {
+    WidgetMsg_EnableDeviceEmulation msg(view()->GetWidget()->routing_id(),
+                                        emulation_params);
+    view()->GetWidget()->OnMessageReceived(msg);
+  }
 
   {
     // Make a popup again. It should inherit device emulation params.
@@ -662,16 +693,51 @@
     ASSERT_TRUE(popup_widget);
 
     // Set its size again.
-    popup_widget->OnUpdateScreenRects(widget_screen_rect, window_screen_rect);
+    {
+      WidgetMsg_UpdateScreenRects msg(popup_widget->routing_id(),
+                                      widget_screen_rect, window_screen_rect);
+      popup_widget->OnMessageReceived(msg);
+    }
 
-    // This time, the size should be affected by emulation params.
-    EXPECT_EQ(150 + window_screen_rect.x(), popup_widget->WindowRect().x);
-    EXPECT_EQ(160 + window_screen_rect.y(), popup_widget->WindowRect().y);
+    // This time, the position of the WidgetScreenRect and WindowScreenRect
+    // should be affected by emulation params.
+    // TODO(danakj): This means the popup sees the top level widget at the
+    // emulated position *plus* the real position. Whereas the top level
+    // widget will see itself at the emulation position. Why this inconsistency?
+    int window_x = emulated_widget_rect.x() + window_screen_rect.x();
+    int window_y = emulated_widget_rect.y() + window_screen_rect.y();
+    EXPECT_EQ(window_x, popup_widget->WindowRect().x);
+    EXPECT_EQ(window_y, popup_widget->WindowRect().y);
 
-    EXPECT_EQ(150 + widget_screen_rect.x(), popup_widget->ViewRect().x);
-    EXPECT_EQ(160 + widget_screen_rect.y(), popup_widget->ViewRect().y);
+    int widget_x = emulated_widget_rect.x() + widget_screen_rect.x();
+    int widget_y = emulated_widget_rect.y() + widget_screen_rect.y();
+    EXPECT_EQ(widget_x, popup_widget->ViewRect().x);
+    EXPECT_EQ(widget_y, popup_widget->ViewRect().y);
 
-    popup_widget->OnClose();
+    // TODO(danakj): Why don't the sizes get changed by emulation? The comments
+    // that used to be in this test suggest that the sizes used to change, and
+    // we were testing for that. But now we only test for positions changing?
+    EXPECT_EQ(window_screen_rect.width(), popup_widget->WindowRect().width);
+    EXPECT_EQ(window_screen_rect.height(), popup_widget->WindowRect().height);
+    EXPECT_EQ(widget_screen_rect.width(), popup_widget->ViewRect().width);
+    EXPECT_EQ(widget_screen_rect.height(), popup_widget->ViewRect().height);
+    EXPECT_EQ(emulated_widget_rect, gfx::Rect(view()->GetWidget()->ViewRect()));
+    EXPECT_EQ(emulated_widget_rect,
+              gfx::Rect(view()->GetWidget()->WindowRect()));
+
+    // TODO(danakj): Why isn't the ScreenRect visible to the popup an emulated
+    // value? The ScreenRect has been changed by emulation as demonstrated
+    // below.
+    EXPECT_EQ(gfx::Rect(800, 600),
+              gfx::Rect(popup_widget->GetScreenInfo().rect));
+    EXPECT_EQ(emulated_widget_rect,
+              gfx::Rect(view()->GetWidget()->GetScreenInfo().rect));
+
+    // Close and destroy the widget.
+    {
+      WidgetMsg_Close msg(popup_widget->routing_id());
+      popup_widget->OnMessageReceived(msg);
+    }
   }
 }
 
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index bb2a345..16b6279b 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1266,9 +1266,6 @@
     IPC_MESSAGE_HANDLER(ViewMsg_MoveOrResizeStarted, OnMoveOrResizeStarted)
     IPC_MESSAGE_HANDLER(ViewMsg_EnablePreferredSizeChangedMode,
                         OnEnablePreferredSizeChangedMode)
-    IPC_MESSAGE_HANDLER(ViewMsg_UpdateVisualProperties,
-                        OnUpdateVisualProperties)
-    IPC_MESSAGE_HANDLER(ViewMsg_SetRendererPrefs, OnSetRendererPrefs)
     IPC_MESSAGE_HANDLER(ViewMsg_PluginActionAt, OnPluginActionAt)
     IPC_MESSAGE_HANDLER(ViewMsg_AnimateDoubleTapZoom,
                         OnAnimateDoubleTapZoomInMainFrame)
@@ -1290,6 +1287,7 @@
                         RestorePageFromBackForwardCache)
     IPC_MESSAGE_HANDLER(PageMsg_UpdateTextAutosizerPageInfoForRemoteMainFrames,
                         OnTextAutosizerPageInfoChanged)
+    IPC_MESSAGE_HANDLER(PageMsg_SetRendererPrefs, OnSetRendererPrefs)
 
     // Adding a new message? Add platform independent ones first, then put the
     // platform specific ones at the end.
@@ -2007,15 +2005,6 @@
   ApplyPageHidden(/*hidden=*/false, /*initial_setting=*/false);
 }
 
-void RenderViewImpl::OnUpdateVisualProperties(
-    const VisualProperties& visual_properties,
-    int widget_routing_id) {
-  // The widget may have been destroyed while the IPC was in flight.
-  RenderWidget* widget = RenderWidget::FromRoutingID(widget_routing_id);
-  if (widget && !widget->IsUndeadOrProvisional())
-    widget->SynchronizeVisualPropertiesFromRenderView(visual_properties);
-}
-
 void RenderViewImpl::OnUpdatePageVisualProperties(
     const gfx::Size& viewport_size_for_blink) {
   // TODO(https://crbug.com/998273): Handle visual_properties appropriately.
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 7c85b8c4..61f38f2 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -461,12 +461,6 @@
   void OnAudioStateChanged(bool is_audio_playing);
   void OnSetBackgroundOpaque(bool opaque);
 
-  // |visual_properties| contains both page and widget scoped properties. This
-  // method consumes the page visual properties and forwards widget properties
-  // using |widget_routing_id|.
-  void OnUpdateVisualProperties(const VisualProperties& visual_properties,
-                                int widget_routing_id);
-
   // Page message handlers -----------------------------------------------------
   void OnPageWasHidden();
   void OnPageWasShown();
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 65b65ac..1e5d628 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -636,6 +636,8 @@
                         OnDisableDeviceEmulation)
     IPC_MESSAGE_HANDLER(WidgetMsg_ShowContextMenu, OnShowContextMenu)
     IPC_MESSAGE_HANDLER(WidgetMsg_Close, OnClose)
+    IPC_MESSAGE_HANDLER(WidgetMsg_UpdateVisualProperties,
+                        OnUpdateVisualProperties)
     IPC_MESSAGE_HANDLER(WidgetMsg_WasHidden, OnWasHidden)
     IPC_MESSAGE_HANDLER(WidgetMsg_WasShown, OnWasShown)
     IPC_MESSAGE_HANDLER(WidgetMsg_SetActive, OnSetActive)
@@ -701,10 +703,9 @@
   Close(base::WrapUnique(this));
 }
 
-void RenderWidget::SynchronizeVisualPropertiesFromRenderView(
+void RenderWidget::OnUpdateVisualProperties(
     const VisualProperties& visual_properties_from_browser) {
-  TRACE_EVENT0("renderer",
-               "RenderWidget::SynchronizeVisualPropertiesFromRenderView");
+  TRACE_EVENT0("renderer", "RenderWidget::OnUpdateVisualProperties");
 
   // TODO(crbug.com/995981): We shouldn't be sending VisualProperties to undead
   // RenderWidgets already, but if we do we could crash if the RenderWidget
@@ -936,6 +937,8 @@
   // 939118 tracks fixing webviews to not use scroll_focused_node_into_view.
   if (delegate() && visual_properties.scroll_focused_node_into_view)
     delegate()->ScrollFocusedNodeIntoViewForWidget();
+
+  AfterUpdateVisualProperties();
 }
 
 void RenderWidget::OnEnableDeviceEmulation(
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 670f655..48724e5c 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -682,17 +682,15 @@
       base::OnceCallback<void(const gfx::PresentationFeedback&)>;
   virtual void RequestPresentation(PresentationTimeCallback callback);
 
-  // Handles widget VisualProperties updates that are coming from the RenderView
-  // [routing IPC from browser].
-  virtual void SynchronizeVisualPropertiesFromRenderView(
-      const VisualProperties& visual_properties);
-
   base::WeakPtr<RenderWidget> AsWeakPtr();
 
  protected:
   // Notify subclasses that we initiated the paint operation.
   virtual void DidInitiatePaint() {}
 
+  // Notify subclasses that we handled OnUpdateVisualProperties.
+  virtual void AfterUpdateVisualProperties() {}
+
   // Destroy the RenderWidget. The |widget| is the owning pointer of |this|.
   virtual void Close(std::unique_ptr<RenderWidget> widget);
 
@@ -711,8 +709,6 @@
   friend class QueueMessageSwapPromiseTest;
   friend class RenderWidgetTest;
   friend class RenderViewImplTest;
-  FRIEND_TEST_ALL_PREFIXES(RenderWidgetPopupUnittest, EmulatingPopupRect);
-  FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, EmulatingPopupRect);
 
   // Called by InitFor*() methods on a new RenderWidget. This contains
   // initialization that occurs whether the RenderWidget is created as undead or
@@ -740,14 +736,14 @@
   void ResizeWebWidget();
 
   // Enable or disable auto-resize. This is part of
-  // OnSynchronizeVisualProperties though tests may call to it more directly.
+  // OnUpdateVisualProperties though tests may call to it more directly.
   void SetAutoResizeMode(bool auto_resize,
                          const gfx::Size& min_size_before_dsf,
                          const gfx::Size& max_size_before_dsf,
                          float device_scale_factor);
 
   // Sets the zoom level on the RenderView. This is part of
-  // OnSynchronizeVisualProperties though tests may call to it more directly.
+  // OnUpdateVisualProperties though tests may call to it more directly.
   void SetZoomLevel(double zoom_level);
 
   // Helper method to get the device_viewport_rect() from the compositor, which
@@ -761,6 +757,7 @@
       const ui::LatencyInfo& latency_info,
       InputEventDispatchType dispatch_type);
   void OnClose();
+  void OnUpdateVisualProperties(const VisualProperties& properties);
   void OnCreatingNewAck();
   void OnEnableDeviceEmulation(const blink::WebDeviceEmulationParams& params);
   void OnDisableDeviceEmulation();
diff --git a/content/renderer/render_widget_browsertest.cc b/content/renderer/render_widget_browsertest.cc
index 45d4972f..54eea00e 100644
--- a/content/renderer/render_widget_browsertest.cc
+++ b/content/renderer/render_widget_browsertest.cc
@@ -6,6 +6,7 @@
 #include "components/viz/common/surfaces/local_surface_id.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "content/common/visual_properties.h"
+#include "content/common/widget_messages.h"
 #include "content/public/renderer/render_frame_visitor.h"
 #include "content/public/test/render_view_test.h"
 #include "content/renderer/compositor/layer_tree_view.h"
@@ -27,8 +28,11 @@
     return static_cast<RenderViewImpl*>(view_)->GetWidget();
   }
 
-  void OnSynchronizeVisualProperties(const VisualProperties& params) {
-    widget()->SynchronizeVisualPropertiesFromRenderView(params);
+  void OnSynchronizeVisualProperties(
+      const VisualProperties& visual_properties) {
+    WidgetMsg_UpdateVisualProperties msg(widget()->routing_id(),
+                                         visual_properties);
+    widget()->OnMessageReceived(msg);
   }
 
   void GetCompositionRange(gfx::Range* range) {
@@ -234,7 +238,11 @@
 
   // Sync visual properties on a mainframe RenderWidget.
   visual_properties.is_pinch_gesture_active = true;
-  widget()->SynchronizeVisualPropertiesFromRenderView(visual_properties);
+  {
+    WidgetMsg_UpdateVisualProperties msg(widget()->routing_id(),
+                                         visual_properties);
+    widget()->OnMessageReceived(msg);
+  }
   // We do not expect the |is_pinch_gesture_active| value to propagate to the
   // LayerTreeHost for the main-frame. Since GesturePinch events are handled
   // directly by the layer tree for the main frame, it already knows whether or
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index d7839cc..c6c2121 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -380,9 +380,7 @@
   RenderWidget::Close(std::move(widget));
 }
 
-void RenderWidgetFullscreenPepper::SynchronizeVisualPropertiesFromRenderView(
-    const VisualProperties& visual_properties) {
-  RenderWidget::SynchronizeVisualPropertiesFromRenderView(visual_properties);
+void RenderWidgetFullscreenPepper::AfterUpdateVisualProperties() {
   UpdateLayerBounds();
 }
 
diff --git a/content/renderer/render_widget_fullscreen_pepper.h b/content/renderer/render_widget_fullscreen_pepper.h
index 04dae968..8053b7e4 100644
--- a/content/renderer/render_widget_fullscreen_pepper.h
+++ b/content/renderer/render_widget_fullscreen_pepper.h
@@ -69,8 +69,7 @@
   // RenderWidget API.
   void DidInitiatePaint() override;
   void Close(std::unique_ptr<RenderWidget> widget) override;
-  void SynchronizeVisualPropertiesFromRenderView(
-      const VisualProperties& visual_properties) override;
+  void AfterUpdateVisualProperties() override;
 
  private:
   void UpdateLayerBounds();
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index 123a258..166d978 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -419,7 +419,11 @@
   allocator.GenerateId();
   visual_properties.local_surface_id_allocation =
       allocator.GetCurrentLocalSurfaceIdAllocation();
-  widget()->SynchronizeVisualPropertiesFromRenderView(visual_properties);
+  {
+    WidgetMsg_UpdateVisualProperties msg(widget()->routing_id(),
+                                         visual_properties);
+    widget()->OnMessageReceived(msg);
+  }
   EXPECT_EQ(allocator.GetCurrentLocalSurfaceIdAllocation(),
             widget()->local_surface_id_allocation_from_parent());
   EXPECT_FALSE(widget()
@@ -470,17 +474,25 @@
 
   // Sync visual properties on a child RenderWidget.
   visual_properties.is_pinch_gesture_active = true;
-  widget()->SynchronizeVisualPropertiesFromRenderView(visual_properties);
+  {
+    WidgetMsg_UpdateVisualProperties msg(widget()->routing_id(),
+                                         visual_properties);
+    widget()->OnMessageReceived(msg);
+  }
   // We expect the |is_pinch_gesture_active| value to propagate to the
   // LayerTreeHost for sub-frames. Since GesturePinch events are handled
   // directly in the main-frame's layer tree (and only there), information about
   // whether or not we're in a pinch gesture must be communicated separately to
-  // sub-frame layer trees, via SynchronizeVisualProperties. This information
+  // sub-frame layer trees, via OnUpdateVisualProperties. This information
   // is required to allow sub-frame compositors to throttle rastering while
   // pinch gestures are active.
   EXPECT_TRUE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
   visual_properties.is_pinch_gesture_active = false;
-  widget()->SynchronizeVisualPropertiesFromRenderView(visual_properties);
+  {
+    WidgetMsg_UpdateVisualProperties msg(widget()->routing_id(),
+                                         visual_properties);
+    widget()->OnMessageReceived(msg);
+  }
   EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
 }
 
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index b2fb238e..541268de 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -558,14 +558,6 @@
   return rtc_dependency_factory->GetWebRtcWorkerThread();
 }
 
-scoped_refptr<base::SingleThreadTaskRunner>
-RendererBlinkPlatformImpl::GetWebRtcSignalingTaskRunner() {
-  auto* rtc_dependency_factory =
-      blink::PeerConnectionDependencyFactory::GetInstance();
-  rtc_dependency_factory->EnsureInitialized();
-  return rtc_dependency_factory->GetWebRtcSignalingThread();
-}
-
 std::unique_ptr<cricket::PortAllocator>
 RendererBlinkPlatformImpl::CreateWebRtcPortAllocator(
     blink::WebLocalFrame* frame) {
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 78b6a0d..68be1b7 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -131,8 +131,6 @@
       blink::WebRTCPeerConnectionHandlerClient* client,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
   scoped_refptr<base::SingleThreadTaskRunner> GetWebRtcWorkerThread() override;
-  scoped_refptr<base::SingleThreadTaskRunner> GetWebRtcSignalingTaskRunner()
-      override;
   std::unique_ptr<cricket::PortAllocator> CreateWebRtcPortAllocator(
       blink::WebLocalFrame* frame) override;
   std::unique_ptr<webrtc::AsyncResolverFactory>
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
index 31127d1..5e17910 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
@@ -36,6 +36,7 @@
 
     private final Handler.Callback mHandlerCallback = new Handler.Callback() {
         @Override
+        @SuppressWarnings("checkstyle:SystemExitCheck") // Allowed due to ChildProcessService.
         public boolean handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_BIND_SERVICE:
diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc
index 1bec54d..a34a127 100644
--- a/content/shell/browser/shell.cc
+++ b/content/shell/browser/shell.cc
@@ -157,7 +157,7 @@
   // RenderFrameCreated or RenderViewCreated instead.
   if (switches::IsRunWebTestsSwitchPresent()) {
     raw_web_contents->GetMutableRendererPrefs()->use_custom_colors = false;
-    raw_web_contents->GetRenderViewHost()->SyncRendererPrefs();
+    raw_web_contents->SyncRendererPrefs();
   }
 
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 9f2b79b..9ab3197 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -83,7 +83,8 @@
 #include "services/service_manager/sandbox/win/sandbox_win.h"
 #endif
 
-#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
+#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) || \
+    BUILDFLAG(ENABLE_CAST_RENDERER)
 #include "media/mojo/mojom/constants.mojom.h"      // nogncheck
 #include "media/mojo/services/media_service_factory.h"  // nogncheck
 #endif
@@ -289,12 +290,24 @@
 void ShellContentBrowserClient::RunServiceInstance(
     const service_manager::Identity& identity,
     mojo::PendingReceiver<service_manager::mojom::Service>* receiver) {
+#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) || \
+    BUILDFLAG(ENABLE_CAST_RENDERER)
+  bool is_media_service = false;
 #if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
-  if (identity.name() == media::mojom::kMediaServiceName) {
+  if (identity.name() == media::mojom::kMediaServiceName)
+    is_media_service = true;
+#endif  // BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
+#if BUILDFLAG(ENABLE_CAST_RENDERER)
+  if (identity.name() == media::mojom::kMediaRendererServiceName)
+    is_media_service = true;
+#endif  // BUILDFLAG(ENABLE_CAST_RENDERER)
+
+  if (is_media_service) {
     service_manager::Service::RunAsyncUntilTermination(
         media::CreateMediaServiceForTesting(std::move(*receiver)));
   }
-#endif
+#endif  // BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) ||
+        // BUILDFLAG(ENABLE_CAST_RENDERER)
 }
 
 bool ShellContentBrowserClient::ShouldTerminateOnServiceQuit(
diff --git a/content/shell/renderer/web_test/web_test_content_renderer_client.cc b/content/shell/renderer/web_test/web_test_content_renderer_client.cc
index f93b63c..8a6002ee 100644
--- a/content/shell/renderer/web_test/web_test_content_renderer_client.cc
+++ b/content/shell/renderer/web_test/web_test_content_renderer_client.cc
@@ -77,12 +77,6 @@
   test_runner->Reset(false /* for_new_test */);
 }
 
-WebThemeEngine* WebTestContentRendererClient::OverrideThemeEngine() {
-  return WebTestRenderThreadObserver::GetInstance()
-      ->test_interfaces()
-      ->ThemeEngine();
-}
-
 std::unique_ptr<blink::WebMediaStreamRendererFactory>
 WebTestContentRendererClient::CreateMediaStreamRendererFactory() {
   return std::unique_ptr<blink::WebMediaStreamRendererFactory>(
diff --git a/content/shell/renderer/web_test/web_test_content_renderer_client.h b/content/shell/renderer/web_test/web_test_content_renderer_client.h
index 0bcc671e..f37de26 100644
--- a/content/shell/renderer/web_test/web_test_content_renderer_client.h
+++ b/content/shell/renderer/web_test/web_test_content_renderer_client.h
@@ -22,7 +22,6 @@
   void RenderThreadStarted() override;
   void RenderFrameCreated(RenderFrame* render_frame) override;
   void RenderViewCreated(RenderView* render_view) override;
-  blink::WebThemeEngine* OverrideThemeEngine() override;
   std::unique_ptr<blink::WebMediaStreamRendererFactory>
   CreateMediaStreamRendererFactory() override;
   std::unique_ptr<content::WebSocketHandshakeThrottleProvider>
diff --git a/content/shell/test_runner/BUILD.gn b/content/shell/test_runner/BUILD.gn
index 42853812..7f0f821a 100644
--- a/content/shell/test_runner/BUILD.gn
+++ b/content/shell/test_runner/BUILD.gn
@@ -43,8 +43,6 @@
     "mock_spell_check.h",
     "mock_web_document_subresource_filter.cc",
     "mock_web_document_subresource_filter.h",
-    "mock_web_theme_engine.cc",
-    "mock_web_theme_engine.h",
     "pixel_dump.cc",
     "pixel_dump.h",
     "spell_check_client.cc",
diff --git a/content/shell/test_runner/mock_web_theme_engine.cc b/content/shell/test_runner/mock_web_theme_engine.cc
deleted file mode 100644
index 422a5171..0000000
--- a/content/shell/test_runner/mock_web_theme_engine.cc
+++ /dev/null
@@ -1,623 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/shell/test_runner/mock_web_theme_engine.h"
-
-#if !defined(OS_MACOSX)
-
-#include "base/logging.h"
-#include "build/build_config.h"
-#include "cc/paint/paint_canvas.h"
-#include "cc/paint/paint_flags.h"
-#include "third_party/blink/public/platform/web_rect.h"
-#include "third_party/blink/public/platform/web_size.h"
-#include "third_party/skia/include/core/SkPaint.h"
-#include "third_party/skia/include/core/SkPath.h"
-#include "third_party/skia/include/core/SkRect.h"
-
-using blink::WebRect;
-using blink::WebThemeEngine;
-
-#endif  // !defined(OS_MACOSX)
-
-namespace test_runner {
-
-#if !defined(OS_MACOSX)
-
-namespace {
-
-const SkColor edgeColor = SK_ColorBLACK;
-const SkColor readOnlyColor = SkColorSetRGB(0xe9, 0xc2, 0xa6);
-
-}  // namespace
-
-SkColor bgColors(WebThemeEngine::State state) {
-  switch (state) {
-    case WebThemeEngine::kStateDisabled:
-      return SkColorSetRGB(0xc9, 0xc9, 0xc9);
-    case WebThemeEngine::kStateHover:
-      return SkColorSetRGB(0x43, 0xf9, 0xff);
-    case WebThemeEngine::kStateNormal:
-      return SkColorSetRGB(0x89, 0xc4, 0xff);
-    case WebThemeEngine::kStatePressed:
-      return SkColorSetRGB(0xa9, 0xff, 0x12);
-    case WebThemeEngine::kStateFocused:
-      return SkColorSetRGB(0x00, 0xf3, 0xac);
-    case WebThemeEngine::kStateReadonly:
-      return SkColorSetRGB(0xf3, 0xe0, 0xd0);
-    default:
-      NOTREACHED();
-  }
-  return SkColorSetRGB(0x00, 0x00, 0xff);
-}
-
-blink::WebSize MockWebThemeEngine::GetSize(WebThemeEngine::Part part) {
-  // FIXME: We use this constant to indicate we are being asked for the size of
-  // a part that we don't expect to be asked about. We return a garbage value
-  // rather than just asserting because this code doesn't have access to either
-  // WTF or base to raise an assertion or do any logging :(.
-  const blink::WebSize invalidPartSize = blink::WebSize(100, 100);
-
-  switch (part) {
-    case WebThemeEngine::kPartScrollbarLeftArrow:
-      return blink::WebSize(17, 15);
-    case WebThemeEngine::kPartScrollbarRightArrow:
-      return invalidPartSize;
-    case WebThemeEngine::kPartScrollbarUpArrow:
-      return blink::WebSize(15, 17);
-    case WebThemeEngine::kPartScrollbarDownArrow:
-      return invalidPartSize;
-    case WebThemeEngine::kPartScrollbarHorizontalThumb:
-      return blink::WebSize(15, 15);
-    case WebThemeEngine::kPartScrollbarVerticalThumb:
-      return blink::WebSize(15, 15);
-    case WebThemeEngine::kPartScrollbarHorizontalTrack:
-      return blink::WebSize(0, 15);
-    case WebThemeEngine::kPartScrollbarVerticalTrack:
-      return blink::WebSize(15, 0);
-    case WebThemeEngine::kPartCheckbox:
-    case WebThemeEngine::kPartRadio:
-      return blink::WebSize(13, 13);
-    case WebThemeEngine::kPartSliderThumb:
-      return blink::WebSize(11, 21);
-    case WebThemeEngine::kPartInnerSpinButton:
-      return blink::WebSize(15, 8);
-    default:
-      return invalidPartSize;
-  }
-}
-
-static SkIRect webRectToSkIRect(const WebRect& webRect) {
-  SkIRect irect;
-  irect.setXYWH(webRect.x, webRect.y, webRect.width - 1, webRect.height - 1);
-  return irect;
-}
-
-static SkIRect validate(const SkIRect& rect, WebThemeEngine::Part part) {
-  switch (part) {
-    case WebThemeEngine::kPartCheckbox:
-    case WebThemeEngine::kPartRadio: {
-      SkIRect retval = rect;
-
-      // The maximum width and height is 13.
-      // Center the square in the passed rectangle.
-      const int maxControlSize = 13;
-      int controlSize = std::min(rect.width(), rect.height());
-      controlSize = std::min(controlSize, maxControlSize);
-
-      retval.fLeft = rect.fLeft + (rect.width() / 2) - (controlSize / 2);
-      retval.fRight = retval.fLeft + controlSize - 1;
-      retval.fTop = rect.fTop + (rect.height() / 2) - (controlSize / 2);
-      retval.fBottom = retval.fTop + controlSize - 1;
-
-      return retval;
-    }
-    default:
-      return rect;
-  }
-}
-
-void box(cc::PaintCanvas* canvas, const SkIRect& rect, SkColor fillColor) {
-  cc::PaintFlags flags;
-
-  flags.setStyle(cc::PaintFlags::kFill_Style);
-  flags.setColor(fillColor);
-  canvas->drawIRect(rect, flags);
-
-  flags.setColor(edgeColor);
-  flags.setStyle(cc::PaintFlags::kStroke_Style);
-  canvas->drawIRect(rect, flags);
-}
-
-void line(cc::PaintCanvas* canvas,
-          int x0,
-          int y0,
-          int x1,
-          int y1,
-          SkColor color) {
-  cc::PaintFlags flags;
-  flags.setColor(color);
-  canvas->drawLine(SkIntToScalar(x0), SkIntToScalar(y0), SkIntToScalar(x1),
-                   SkIntToScalar(y1), flags);
-}
-
-void triangle(cc::PaintCanvas* canvas,
-              int x0,
-              int y0,
-              int x1,
-              int y1,
-              int x2,
-              int y2,
-              SkColor color) {
-  SkPath path;
-  cc::PaintFlags flags;
-
-  flags.setColor(color);
-  flags.setStyle(cc::PaintFlags::kFill_Style);
-  path.incReserve(4);
-  path.moveTo(SkIntToScalar(x0), SkIntToScalar(y0));
-  path.lineTo(SkIntToScalar(x1), SkIntToScalar(y1));
-  path.lineTo(SkIntToScalar(x2), SkIntToScalar(y2));
-  path.close();
-  canvas->drawPath(path, flags);
-
-  flags.setColor(edgeColor);
-  flags.setStyle(cc::PaintFlags::kStroke_Style);
-  canvas->drawPath(path, flags);
-}
-
-void roundRect(cc::PaintCanvas* canvas, SkIRect irect, SkColor color) {
-  SkRect rect;
-  SkScalar radius = SkIntToScalar(5);
-  cc::PaintFlags flags;
-
-  rect.set(irect);
-  flags.setColor(color);
-  flags.setStyle(cc::PaintFlags::kFill_Style);
-  canvas->drawRoundRect(rect, radius, radius, flags);
-
-  flags.setColor(edgeColor);
-  flags.setStyle(cc::PaintFlags::kStroke_Style);
-  canvas->drawRoundRect(rect, radius, radius, flags);
-}
-
-void oval(cc::PaintCanvas* canvas, SkIRect irect, SkColor color) {
-  SkRect rect;
-  cc::PaintFlags flags;
-
-  rect.set(irect);
-  flags.setColor(color);
-  flags.setStyle(cc::PaintFlags::kFill_Style);
-  canvas->drawOval(rect, flags);
-
-  flags.setColor(edgeColor);
-  flags.setStyle(cc::PaintFlags::kStroke_Style);
-  canvas->drawOval(rect, flags);
-}
-
-void circle(cc::PaintCanvas* canvas,
-            SkIRect irect,
-            SkScalar radius,
-            SkColor color) {
-  int left = irect.fLeft;
-  int width = irect.width();
-  int height = irect.height();
-  int top = irect.fTop;
-
-  SkScalar cy = SkIntToScalar(top + height / 2);
-  SkScalar cx = SkIntToScalar(left + width / 2);
-  cc::PaintFlags flags;
-
-  flags.setColor(color);
-  flags.setStyle(cc::PaintFlags::kFill_Style);
-  canvas->drawOval(
-      SkRect::MakeLTRB(cx - radius, cy - radius, cx + radius, cy + radius),
-      flags);
-
-  flags.setColor(edgeColor);
-  flags.setStyle(cc::PaintFlags::kStroke_Style);
-  canvas->drawOval(
-      SkRect::MakeLTRB(cx - radius, cy - radius, cx + radius, cy + radius),
-      flags);
-}
-
-void nestedBoxes(cc::PaintCanvas* canvas,
-                 SkIRect irect,
-                 int indentLeft,
-                 int indentTop,
-                 int indentRight,
-                 int indentBottom,
-                 SkColor outerColor,
-                 SkColor innerColor) {
-  SkIRect lirect;
-  box(canvas, irect, outerColor);
-  lirect.setLTRB(irect.fLeft + indentLeft, irect.fTop + indentTop,
-                 irect.fRight - indentRight, irect.fBottom - indentBottom);
-  box(canvas, lirect, innerColor);
-}
-
-void insetBox(cc::PaintCanvas* canvas,
-              SkIRect irect,
-              int indentLeft,
-              int indentTop,
-              int indentRight,
-              int indentBottom,
-              SkColor color) {
-  SkIRect lirect;
-  lirect.setLTRB(irect.fLeft + indentLeft, irect.fTop + indentTop,
-                 irect.fRight - indentRight, irect.fBottom - indentBottom);
-  box(canvas, lirect, color);
-}
-
-void markState(cc::PaintCanvas* canvas,
-               SkIRect irect,
-               WebThemeEngine::State state) {
-  int left = irect.fLeft;
-  int right = irect.fRight;
-  int top = irect.fTop;
-  int bottom = irect.fBottom;
-
-  // The length of a triangle side for the corner marks.
-  const int triangleSize = 5;
-
-  switch (state) {
-    case WebThemeEngine::kStateDisabled:
-    case WebThemeEngine::kStateNormal:
-      // Don't visually mark these states (color is enough).
-      break;
-
-    case WebThemeEngine::kStateReadonly: {
-      // The horizontal lines in a read only control are spaced by this amount.
-      const int readOnlyLineOffset = 5;
-
-      // Drawing lines across the control.
-      for (int i = top + readOnlyLineOffset; i < bottom;
-           i += readOnlyLineOffset)
-        line(canvas, left + 1, i, right - 1, i, readOnlyColor);
-      break;
-    }
-    case WebThemeEngine::kStateHover:
-      // Draw a triangle in the upper left corner of the control. (Win's "hot")
-      triangle(canvas, left, top, left + triangleSize, top, left,
-               top + triangleSize, edgeColor);
-      break;
-
-    case WebThemeEngine::kStateFocused:
-      // Draw a triangle in the bottom right corner of the control.
-      triangle(canvas, right, bottom, right - triangleSize, bottom, right,
-               bottom - triangleSize, edgeColor);
-      break;
-
-    case WebThemeEngine::kStatePressed:
-      // Draw a triangle in the bottom left corner of the control.
-      triangle(canvas, left, bottom, left, bottom - triangleSize,
-               left + triangleSize, bottom, edgeColor);
-      break;
-
-    default:
-      // FIXME: Should we do something here to indicate that we got an invalid
-      // state?
-      // Unfortunately, we can't assert because we don't have access to WTF or
-      // base.
-      break;
-  }
-}
-
-void MockWebThemeEngine::Paint(cc::PaintCanvas* canvas,
-                               WebThemeEngine::Part part,
-                               WebThemeEngine::State state,
-                               const blink::WebRect& rect,
-                               const WebThemeEngine::ExtraParams* extraParams,
-                               blink::WebColorScheme color_scheme) {
-  SkIRect irect = webRectToSkIRect(rect);
-  cc::PaintFlags flags;
-
-  // Indent amounts for the check in a checkbox or radio button.
-  const int checkIndent = 3;
-
-  // Indent amounts for short and long sides of the scrollbar notches.
-  const int notchLongOffset = 1;
-  const int notchShortOffset = 4;
-  const int noOffset = 0;
-
-  // Indent amounts for the short and long sides of a scroll thumb box.
-  const int thumbLongIndent = 0;
-  const int thumbShortIndent = 2;
-
-  // Indents for the crosshatch on a scroll grip.
-  const int gripLongIndent = 3;
-  const int gripShortIndent = 5;
-
-  // Indents for the the slider track.
-  const int sliderIndent = 2;
-
-  int halfHeight = irect.height() / 2;
-  int halfWidth = irect.width() / 2;
-  int quarterHeight = irect.height() / 4;
-  int quarterWidth = irect.width() / 4;
-  int left = irect.fLeft;
-  int right = irect.fRight;
-  int top = irect.fTop;
-  int bottom = irect.fBottom;
-
-  switch (part) {
-    case WebThemeEngine::kPartScrollbarDownArrow:
-      box(canvas, irect, bgColors(state));
-      triangle(canvas, left + quarterWidth, top + quarterHeight,
-               right - quarterWidth, top + quarterHeight, left + halfWidth,
-               bottom - quarterHeight, edgeColor);
-      markState(canvas, irect, state);
-      break;
-
-    case WebThemeEngine::kPartScrollbarLeftArrow:
-      box(canvas, irect, bgColors(state));
-      triangle(canvas, right - quarterWidth, top + quarterHeight,
-               right - quarterWidth, bottom - quarterHeight,
-               left + quarterWidth, top + halfHeight, edgeColor);
-      break;
-
-    case WebThemeEngine::kPartScrollbarRightArrow:
-      box(canvas, irect, bgColors(state));
-      triangle(canvas, left + quarterWidth, top + quarterHeight,
-               right - quarterWidth, top + halfHeight, left + quarterWidth,
-               bottom - quarterHeight, edgeColor);
-      break;
-
-    case WebThemeEngine::kPartScrollbarUpArrow:
-      box(canvas, irect, bgColors(state));
-      triangle(canvas, left + quarterWidth, bottom - quarterHeight,
-               left + halfWidth, top + quarterHeight, right - quarterWidth,
-               bottom - quarterHeight, edgeColor);
-      markState(canvas, irect, state);
-      break;
-
-    case WebThemeEngine::kPartScrollbarHorizontalThumb: {
-      // Draw a narrower box on top of the outside box.
-      nestedBoxes(canvas, irect, thumbLongIndent, thumbShortIndent,
-                  thumbLongIndent, thumbShortIndent, bgColors(state),
-                  bgColors(state));
-      // Draw a horizontal crosshatch for the grip.
-      int longOffset = halfWidth - gripLongIndent;
-      line(canvas, left + gripLongIndent, top + halfHeight,
-           right - gripLongIndent, top + halfHeight, edgeColor);
-      line(canvas, left + longOffset, top + gripShortIndent, left + longOffset,
-           bottom - gripShortIndent, edgeColor);
-      line(canvas, right - longOffset, top + gripShortIndent,
-           right - longOffset, bottom - gripShortIndent, edgeColor);
-      markState(canvas, irect, state);
-      break;
-    }
-
-    case WebThemeEngine::kPartScrollbarVerticalThumb: {
-      // Draw a shorter box on top of the outside box.
-      nestedBoxes(canvas, irect, thumbShortIndent, thumbLongIndent,
-                  thumbShortIndent, thumbLongIndent, bgColors(state),
-                  bgColors(state));
-      // Draw a vertical crosshatch for the grip.
-      int longOffset = halfHeight - gripLongIndent;
-      line(canvas, left + halfWidth, top + gripLongIndent, left + halfWidth,
-           bottom - gripLongIndent, edgeColor);
-      line(canvas, left + gripShortIndent, top + longOffset,
-           right - gripShortIndent, top + longOffset, edgeColor);
-      line(canvas, left + gripShortIndent, bottom - longOffset,
-           right - gripShortIndent, bottom - longOffset, edgeColor);
-      markState(canvas, irect, state);
-      break;
-    }
-
-    case WebThemeEngine::kPartScrollbarHorizontalTrack: {
-      int longOffset = halfHeight - notchLongOffset;
-      int shortOffset = irect.width() - notchShortOffset;
-      box(canvas, irect, bgColors(state));
-      // back, notch on right
-      insetBox(canvas, irect, noOffset, longOffset, shortOffset, longOffset,
-               edgeColor);
-      // forward, notch on right
-      insetBox(canvas, irect, shortOffset, longOffset, noOffset, longOffset,
-               edgeColor);
-      markState(canvas, irect, state);
-      break;
-    }
-
-    case WebThemeEngine::kPartScrollbarVerticalTrack: {
-      int longOffset = halfWidth - notchLongOffset;
-      int shortOffset = irect.height() - notchShortOffset;
-      box(canvas, irect, bgColors(state));
-      // back, notch at top
-      insetBox(canvas, irect, longOffset, noOffset, longOffset, shortOffset,
-               edgeColor);
-      // forward, notch at bottom
-      insetBox(canvas, irect, longOffset, shortOffset, longOffset, noOffset,
-               edgeColor);
-      markState(canvas, irect, state);
-      break;
-    }
-
-    case WebThemeEngine::kPartScrollbarCorner: {
-      SkIRect cornerRect = {rect.x, rect.y, rect.x + rect.width,
-                            rect.y + rect.height};
-      flags.setColor(SK_ColorWHITE);
-      flags.setStyle(cc::PaintFlags::kFill_Style);
-      flags.setBlendMode(SkBlendMode::kSrc);
-      flags.setAntiAlias(true);
-      canvas->drawIRect(cornerRect, flags);
-      break;
-    }
-
-    case WebThemeEngine::kPartCheckbox:
-      if (extraParams->button.indeterminate) {
-        nestedBoxes(canvas, irect, checkIndent, halfHeight, checkIndent,
-                    halfHeight, bgColors(state), edgeColor);
-      } else if (extraParams->button.checked) {
-        irect = validate(irect, part);
-        nestedBoxes(canvas, irect, checkIndent, checkIndent, checkIndent,
-                    checkIndent, bgColors(state), edgeColor);
-      } else {
-        irect = validate(irect, part);
-        box(canvas, irect, bgColors(state));
-      }
-      break;
-
-    case WebThemeEngine::kPartRadio:
-      irect = validate(irect, part);
-      halfHeight = irect.height() / 2;
-      if (extraParams->button.checked) {
-        circle(canvas, irect, SkIntToScalar(halfHeight), bgColors(state));
-        circle(canvas, irect, SkIntToScalar(halfHeight - checkIndent),
-               edgeColor);
-      } else {
-        circle(canvas, irect, SkIntToScalar(halfHeight), bgColors(state));
-      }
-      break;
-
-    case WebThemeEngine::kPartButton:
-      roundRect(canvas, irect, bgColors(state));
-      markState(canvas, irect, state);
-      break;
-
-    case WebThemeEngine::kPartTextField:
-      flags.setColor(extraParams->text_field.background_color);
-      flags.setStyle(cc::PaintFlags::kFill_Style);
-      canvas->drawIRect(irect, flags);
-
-      flags.setColor(edgeColor);
-      flags.setStyle(cc::PaintFlags::kStroke_Style);
-      canvas->drawIRect(irect, flags);
-
-      markState(canvas, irect, state);
-      break;
-
-    case WebThemeEngine::kPartMenuList:
-      if (extraParams->menu_list.fill_content_area) {
-        box(canvas, irect, extraParams->menu_list.background_color);
-      } else {
-        cc::PaintFlags flags;
-        flags.setColor(edgeColor);
-        flags.setStyle(cc::PaintFlags::kStroke_Style);
-        canvas->drawIRect(irect, flags);
-      }
-
-      // clip the drop-down arrow to be inside the select box
-      irect.fLeft = std::max(irect.fLeft,
-                             extraParams->menu_list.arrow_x -
-                                 (extraParams->menu_list.arrow_size + 1) / 2);
-      irect.fRight = std::min(irect.fLeft + extraParams->menu_list.arrow_size,
-                              irect.fRight);
-
-      irect.fTop = extraParams->menu_list.arrow_y -
-                   (extraParams->menu_list.arrow_size) / 2;
-      irect.fBottom = irect.fTop + (extraParams->menu_list.arrow_size);
-
-      halfWidth = irect.width() / 2;
-      quarterWidth = irect.width() / 4;
-
-      if (state == WebThemeEngine::kStateFocused)  // FIXME: draw differenty?
-        state = WebThemeEngine::kStateNormal;
-      box(canvas, irect, bgColors(state));
-      triangle(canvas, irect.fLeft + quarterWidth, irect.fTop + quarterWidth,
-               irect.fRight - quarterWidth, irect.fTop + quarterWidth,
-               irect.fLeft + halfWidth, irect.fBottom - quarterWidth,
-               edgeColor);
-
-      break;
-
-    case WebThemeEngine::kPartSliderTrack: {
-      SkIRect lirect = irect;
-
-      // Draw a narrow rect for the track plus box hatches on the ends.
-      if (state == WebThemeEngine::kStateFocused)  // FIXME: draw differently?
-        state = WebThemeEngine::kStateNormal;
-      if (extraParams->slider.vertical) {
-        lirect.inset(halfWidth - sliderIndent, noOffset);
-        box(canvas, lirect, bgColors(state));
-        line(canvas, left, top, right, top, edgeColor);
-        line(canvas, left, bottom, right, bottom, edgeColor);
-      } else {
-        lirect.inset(noOffset, halfHeight - sliderIndent);
-        box(canvas, lirect, bgColors(state));
-        line(canvas, left, top, left, bottom, edgeColor);
-        line(canvas, right, top, right, bottom, edgeColor);
-      }
-      break;
-    }
-
-    case WebThemeEngine::kPartSliderThumb:
-      if (state == WebThemeEngine::kStateFocused)  // FIXME: draw differently?
-        state = WebThemeEngine::kStateNormal;
-      oval(canvas, irect, bgColors(state));
-      break;
-
-    case WebThemeEngine::kPartInnerSpinButton: {
-      // stack half-height up and down arrows on top of each other
-      SkIRect lirect;
-      int halfHeight = rect.height / 2;
-      if (extraParams->inner_spin.read_only)
-        state = blink::WebThemeEngine::kStateDisabled;
-
-      lirect.setLTRB(rect.x, rect.y, rect.x + rect.width - 1,
-                     rect.y + halfHeight - 1);
-      box(canvas, lirect, bgColors(state));
-      bottom = lirect.fBottom;
-      quarterHeight = lirect.height() / 4;
-      triangle(canvas, left + quarterWidth, bottom - quarterHeight,
-               right - quarterWidth, bottom - quarterHeight, left + halfWidth,
-               top + quarterHeight, edgeColor);
-
-      lirect.setLTRB(rect.x, rect.y + halfHeight, rect.x + rect.width - 1,
-                     rect.y + 2 * halfHeight - 1);
-      top = lirect.fTop;
-      bottom = lirect.fBottom;
-      quarterHeight = lirect.height() / 4;
-      box(canvas, lirect, bgColors(state));
-      triangle(canvas, left + quarterWidth, top + quarterHeight,
-               right - quarterWidth, top + quarterHeight, left + halfWidth,
-               bottom - quarterHeight, edgeColor);
-      markState(canvas, irect, state);
-      break;
-    }
-    case WebThemeEngine::kPartProgressBar: {
-      flags.setColor(bgColors(state));
-      flags.setStyle(cc::PaintFlags::kFill_Style);
-      canvas->drawIRect(irect, flags);
-
-      // Emulate clipping
-      SkIRect tofill = irect;
-      if (extraParams->progress_bar.determinate) {
-        tofill.setXYWH(extraParams->progress_bar.value_rect_x,
-                       extraParams->progress_bar.value_rect_y,
-                       extraParams->progress_bar.value_rect_width - 1,
-                       extraParams->progress_bar.value_rect_height);
-      }
-
-      if (!tofill.intersect(irect))
-        tofill.setEmpty();
-
-      flags.setColor(edgeColor);
-      flags.setStyle(cc::PaintFlags::kFill_Style);
-      canvas->drawIRect(tofill, flags);
-
-      markState(canvas, irect, state);
-      break;
-    }
-    default:
-      // FIXME: Should we do something here to indicate that we got an invalid
-      // part?
-      // Unfortunately, we can't assert because we don't have access to WTF or
-      // base.
-      break;
-  }
-}
-
-#endif  // !defined(OS_MACOSX)
-
-blink::ForcedColors MockWebThemeEngine::ForcedColors() const {
-  return forced_colors_;
-}
-
-void MockWebThemeEngine::SetForcedColors(
-    const blink::ForcedColors forced_colors) {
-  forced_colors_ = forced_colors;
-}
-
-}  // namespace test_runner
diff --git a/content/shell/test_runner/mock_web_theme_engine.h b/content/shell/test_runner/mock_web_theme_engine.h
deleted file mode 100644
index 85f06da..0000000
--- a/content/shell/test_runner/mock_web_theme_engine.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_SHELL_TEST_RUNNER_MOCK_WEB_THEME_ENGINE_H_
-#define CONTENT_SHELL_TEST_RUNNER_MOCK_WEB_THEME_ENGINE_H_
-
-#include "build/build_config.h"
-#include "third_party/blink/public/platform/web_theme_engine.h"
-
-namespace test_runner {
-
-class MockWebThemeEngine : public blink::WebThemeEngine {
- public:
-  ~MockWebThemeEngine() override {}
-
-#if !defined(OS_MACOSX)
-  // blink::WebThemeEngine:
-  blink::WebSize GetSize(blink::WebThemeEngine::Part) override;
-  void Paint(cc::PaintCanvas*,
-             blink::WebThemeEngine::Part,
-             blink::WebThemeEngine::State,
-             const blink::WebRect&,
-             const blink::WebThemeEngine::ExtraParams*,
-             blink::WebColorScheme) override;
-#endif  // !defined(OS_MACOSX)
-
-  blink::ForcedColors ForcedColors() const override;
-  void SetForcedColors(const blink::ForcedColors forced_colors) override;
-
- private:
-  blink::ForcedColors forced_colors_ = blink::ForcedColors::kNone;
-};
-
-}  // namespace test_runner
-
-#endif  // CONTENT_SHELL_TEST_RUNNER_MOCK_WEB_THEME_ENGINE_H_
diff --git a/content/shell/test_runner/test_interfaces.cc b/content/shell/test_runner/test_interfaces.cc
index efe9746..7f0ac1b 100644
--- a/content/shell/test_runner/test_interfaces.cc
+++ b/content/shell/test_runner/test_interfaces.cc
@@ -123,9 +123,6 @@
       spec.find("://web-platform.test") != std::string::npos ||
       spec.find("/harness-tests/wpt/") != std::string::npos)
     test_runner_->set_is_web_platform_tests_mode();
-
-  use_mock_theme_ =
-      spec.find("/fast/scrolling/scrollbars/") != std::string::npos;
 }
 
 void TestInterfaces::WindowOpened(WebViewTestProxy* proxy) {
@@ -157,12 +154,4 @@
   return window_list_;
 }
 
-blink::WebThemeEngine* TestInterfaces::GetThemeEngine() {
-  if (!use_mock_theme_)
-    return nullptr;
-  if (!theme_engine_.get())
-    theme_engine_.reset(new MockWebThemeEngine());
-  return theme_engine_.get();
-}
-
 }  // namespace test_runner
diff --git a/content/shell/test_runner/test_interfaces.h b/content/shell/test_runner/test_interfaces.h
index 23baf0b..343c062 100644
--- a/content/shell/test_runner/test_interfaces.h
+++ b/content/shell/test_runner/test_interfaces.h
@@ -10,11 +10,9 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "content/shell/test_runner/mock_web_theme_engine.h"
 
 namespace blink {
 class WebLocalFrame;
-class WebThemeEngine;
 class WebURL;
 class WebView;
 }
@@ -47,7 +45,6 @@
   TestRunner* GetTestRunner();
   WebTestDelegate* GetDelegate();
   const std::vector<WebViewTestProxy*>& GetWindowList();
-  blink::WebThemeEngine* GetThemeEngine();
 
  private:
   std::unique_ptr<GamepadController> gamepad_controller_;
@@ -57,9 +54,6 @@
   std::vector<WebViewTestProxy*> window_list_;
   blink::WebView* main_view_;
 
-  bool use_mock_theme_ = false;
-  std::unique_ptr<MockWebThemeEngine> theme_engine_;
-
   DISALLOW_COPY_AND_ASSIGN(TestInterfaces);
 };
 
diff --git a/content/shell/test_runner/web_test_interfaces.cc b/content/shell/test_runner/web_test_interfaces.cc
index db6c5bd..60514094 100644
--- a/content/shell/test_runner/web_test_interfaces.cc
+++ b/content/shell/test_runner/web_test_interfaces.cc
@@ -47,10 +47,6 @@
   return interfaces_->GetTestRunner();
 }
 
-blink::WebThemeEngine* WebTestInterfaces::ThemeEngine() {
-  return interfaces_->GetThemeEngine();
-}
-
 TestInterfaces* WebTestInterfaces::GetTestInterfaces() {
   return interfaces_.get();
 }
diff --git a/content/shell/test_runner/web_test_interfaces.h b/content/shell/test_runner/web_test_interfaces.h
index 2da2e3f..b1b77f7 100644
--- a/content/shell/test_runner/web_test_interfaces.h
+++ b/content/shell/test_runner/web_test_interfaces.h
@@ -15,7 +15,6 @@
 class WebLocalFrameClient;
 class WebRTCPeerConnectionHandler;
 class WebRTCPeerConnectionHandlerClient;
-class WebThemeEngine;
 class WebURL;
 class WebView;
 }
@@ -46,7 +45,6 @@
                                bool protocol_mode);
 
   WebTestRunner* TestRunner();
-  blink::WebThemeEngine* ThemeEngine();
 
   std::unique_ptr<blink::WebRTCPeerConnectionHandler>
   CreateWebRTCPeerConnectionHandler(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index ebb36ffd..78f27ed4 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -936,6 +936,7 @@
     "../browser/generic_sensor/generic_sensor_browsertest.cc",
     "../browser/gpu/gpu_ipc_browsertests.cc",
     "../browser/gpu/in_process_gpu_thread_browsertests.cc",
+    "../browser/gpu/peak_gpu_memory_tracker_impl_browsertest.cc",
     "../browser/idle/idle_browsertest.cc",
     "../browser/indexed_db/indexed_db_browsertest.cc",
     "../browser/indexed_db/indexed_db_execution_context_connection_tracker_browsertest.cc",
diff --git a/content/test/data/accessibility/aria/aria-insertion-deletion-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-insertion-deletion-expected-auralinux.txt
index 51661fa..ef81f6b 100644
--- a/content/test/data/accessibility/aria/aria-insertion-deletion-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-insertion-deletion-expected-auralinux.txt
@@ -1,9 +1,9 @@
 [document web]
 ++[paragraph]
 ++++[text] name='My favorite browser is '
-++++[section]
+++++[content deletion]
 ++++++[text] name='ABC'
 ++++[text] name=' '
-++++[section]
+++++[content insertion]
 ++++++[text] name='Chrome'
 ++++[text] name='!'
diff --git a/content/test/data/accessibility/display-locking/activatable-activated.html b/content/test/data/accessibility/display-locking/activatable-activated.html
index 6be1c7e..e233996d 100644
--- a/content/test/data/accessibility/display-locking/activatable-activated.html
+++ b/content/test/data/accessibility/display-locking/activatable-activated.html
@@ -2,9 +2,9 @@
 @BLINK-ALLOW:offscreen
 -->
 <div>
-  <div id="locked" rendersubtree="invisible">
+  <div id="locked" rendersubtree="invisible skip-viewport-activation">
     <div>child</div>
-    <div id="nested" rendersubtree="invisible">nested locked element!</div>
+    <div id="nested" rendersubtree="invisible skip-viewport-activation">nested locked element!</div>
     <div id="nonActivatable" rendersubtree="invisible skip-activation">nested non activatable locked element</div>
   </div>
 </div>
diff --git a/content/test/data/accessibility/display-locking/activatable.html b/content/test/data/accessibility/display-locking/activatable.html
index 49a5d52f..a868edf 100644
--- a/content/test/data/accessibility/display-locking/activatable.html
+++ b/content/test/data/accessibility/display-locking/activatable.html
@@ -2,9 +2,9 @@
 @BLINK-ALLOW:offscreen
 -->
 <div>
-  <div id="locked" rendersubtree="invisible">
+  <div id="locked" rendersubtree="invisible skip-viewport-activation">
     <div>child</div>
-    <div id="nested" rendersubtree="invisible">nested locked element!</div>
+    <div id="nested" rendersubtree="invisible skip-viewport-activation">nested locked element!</div>
     <div id="nonActivatable" rendersubtree="invisible skip-activation">nested non activatable locked element</div>
   </div>
 </div>
diff --git a/content/test/data/accessibility/display-locking/all-committed.html b/content/test/data/accessibility/display-locking/all-committed.html
index 2266d984..5ffc4cf 100644
--- a/content/test/data/accessibility/display-locking/all-committed.html
+++ b/content/test/data/accessibility/display-locking/all-committed.html
@@ -2,9 +2,9 @@
 @BLINK-ALLOW:offscreen
 -->
 <div>
-  <div id="locked" rendersubtree="invisible skip-activation">
+  <div id="locked" rendersubtree="invisible skip-viewport-activation">
     <div>child</div>
-    <div id="nested" rendersubtree="invisible skip-activation">nested locked element!</div>
+    <div id="nested" rendersubtree="invisible skip-viewport-activation">nested locked element!</div>
     <div id="nonActivatable" rendersubtree="invisible skip-activation">nested non activatable locked element</div>
     <!--
       TODO(rakina): Make display:none, visibility:hidden, aria-hidden nodes
diff --git a/content/test/data/accessibility/display-locking/all.html b/content/test/data/accessibility/display-locking/all.html
index 32bee4b..fbdfd07 100644
--- a/content/test/data/accessibility/display-locking/all.html
+++ b/content/test/data/accessibility/display-locking/all.html
@@ -2,9 +2,9 @@
 @BLINK-ALLOW:offscreen
 -->
 <div>
-  <div id="locked" rendersubtree="invisible">
+  <div id="locked" rendersubtree="invisible skip-viewport-activation">
     <div>child</div>
-    <div id="nested" rendersubtree="invisible">nested locked element!</div>
+    <div id="nested" rendersubtree="invisible skip-viewport-activation">nested locked element!</div>
     <div id="nonActivatable" rendersubtree="invisible skip-activation">nested non activatable locked element</div>
     <!--
       TODO(rakina): Make display:none, visibility:hidden, aria-hidden nodes
diff --git a/content/test/data/accessibility/display-locking/non-activatable.html b/content/test/data/accessibility/display-locking/non-activatable.html
index 15f436e..8597805a 100644
--- a/content/test/data/accessibility/display-locking/non-activatable.html
+++ b/content/test/data/accessibility/display-locking/non-activatable.html
@@ -5,6 +5,6 @@
   <div id="locked" rendersubtree="invisible skip-activation">
     <div>child</div>
     <div id="nested" rendersubtree="invisible skip-activation">nested locked element!</div>
-    <div id="activatable" rendersubtree="invisible skip-activation">non activatable locked element</div>
+    <div id="activatable" rendersubtree="invisible skip-viewport-activation">activatable locked element</div>
   </div>
 </div>
diff --git a/content/test/data/accessibility/html/del-expected-auralinux.txt b/content/test/data/accessibility/html/del-expected-auralinux.txt
index 83ed3cd..c274f8f7 100644
--- a/content/test/data/accessibility/html/del-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/del-expected-auralinux.txt
@@ -1,5 +1,5 @@
 [document web]
 ++[paragraph]
 ++++[text] name='I am '
-++++[section]
+++++[content deletion]
 ++++++[text] name='vegetarian'
diff --git a/content/test/data/accessibility/html/frameset-expected-auralinux.txt b/content/test/data/accessibility/html/frameset-expected-auralinux.txt
index f4e80c4b..b4de8b2 100644
--- a/content/test/data/accessibility/html/frameset-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/frameset-expected-auralinux.txt
@@ -3,10 +3,10 @@
 ++++[document web]
 ++++++[paragraph]
 ++++++++[text] name='My favorite browser is '
-++++++++[section]
+++++++++[content deletion]
 ++++++++++[text] name='ABC'
 ++++++++[text] name=' '
-++++++++[section]
+++++++++[content insertion]
 ++++++++++[text] name='Chrome'
 ++++++++[text] name='!'
 ++[internal frame]
diff --git a/content/test/data/accessibility/html/ins-expected-auralinux.txt b/content/test/data/accessibility/html/ins-expected-auralinux.txt
index 51661fa..ef81f6b 100644
--- a/content/test/data/accessibility/html/ins-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/ins-expected-auralinux.txt
@@ -1,9 +1,9 @@
 [document web]
 ++[paragraph]
 ++++[text] name='My favorite browser is '
-++++[section]
+++++[content deletion]
 ++++++[text] name='ABC'
 ++++[text] name=' '
-++++[section]
+++++[content insertion]
 ++++++[text] name='Chrome'
 ++++[text] name='!'
diff --git a/content/test/data/accessibility/html/modal-dialog-opened-expected-android.txt b/content/test/data/accessibility/html/modal-dialog-opened-expected-android.txt
index e3c3127..badc901b 100644
--- a/content/test/data/accessibility/html/modal-dialog-opened-expected-android.txt
+++ b/content/test/data/accessibility/html/modal-dialog-opened-expected-android.txt
@@ -1,5 +1,5 @@
 android.webkit.WebView focusable scrollable
+++android.view.View
 ++android.app.Dialog role_description='dialog'
 ++++android.view.View name='The dialog subtree should be the only text content in the accessibility tree. '
 ++++android.view.View role_description='link' clickable focusable focused link name='Link inside the dialog.'
-++android.view.View
diff --git a/content/test/data/accessibility/html/modal-dialog-opened-expected-auralinux.txt b/content/test/data/accessibility/html/modal-dialog-opened-expected-auralinux.txt
index 210eb46..6a184f3 100644
--- a/content/test/data/accessibility/html/modal-dialog-opened-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/modal-dialog-opened-expected-auralinux.txt
@@ -1,6 +1,6 @@
 [document web]
+++[section]
 ++[dialog] modal
 ++++[text] name='The dialog subtree should be the only text content in the accessibility tree. '
 ++++[link] name='Link inside the dialog.'
 ++++++[text] name='Link inside the dialog.'
-++[section]
diff --git a/content/test/data/accessibility/html/modal-dialog-opened-expected-blink.txt b/content/test/data/accessibility/html/modal-dialog-opened-expected-blink.txt
index 1cf7805..b31d596 100644
--- a/content/test/data/accessibility/html/modal-dialog-opened-expected-blink.txt
+++ b/content/test/data/accessibility/html/modal-dialog-opened-expected-blink.txt
@@ -1,8 +1,14 @@
 rootWebArea
+++genericContainer ignored invisible
+++++section ignored invisible
+++++++popUpButton collapsed ignored invisible value='This should be pruned out of the tree.'
+++++button ignored invisible name='Choose File' value='No file chosen'
+++genericContainer ignored invisible
+++dialog ignored invisible
+++genericContainer
 ++dialog
 ++++staticText name='The dialog subtree should be the only text content in the accessibility tree. '
 ++++++inlineTextBox name='The dialog subtree should be the only text content in the accessibility tree. '
 ++++link name='Link inside the dialog.'
 ++++++staticText name='Link inside the dialog.'
 ++++++++inlineTextBox name='Link inside the dialog.'
-++genericContainer
diff --git a/content/test/data/accessibility/html/modal-dialog-opened-expected-mac.txt b/content/test/data/accessibility/html/modal-dialog-opened-expected-mac.txt
index b2f19e8..e3e9609b 100644
--- a/content/test/data/accessibility/html/modal-dialog-opened-expected-mac.txt
+++ b/content/test/data/accessibility/html/modal-dialog-opened-expected-mac.txt
@@ -1,6 +1,6 @@
 AXWebArea
+++AXGroup
 ++AXGroup AXSubrole=AXApplicationDialog
 ++++AXStaticText AXValue='The dialog subtree should be the only text content in the accessibility tree. '
 ++++AXLink AXTitle='Link inside the dialog.'
 ++++++AXStaticText AXValue='Link inside the dialog.'
-++AXGroup
diff --git a/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt b/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt
index bcfd56f..380e5b2 100644
--- a/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt
@@ -1,5 +1,5 @@
 document
+++group
 ++dialog Window.IsModal=true
 ++++description Name='The dialog subtree should be the only text content in the accessibility tree. '
 ++++link Name='Link inside the dialog.'
-++group
diff --git a/content/test/data/accessibility/html/modal-dialog-opened-expected-win.txt b/content/test/data/accessibility/html/modal-dialog-opened-expected-win.txt
index 8fd398a..09ab3cb 100644
--- a/content/test/data/accessibility/html/modal-dialog-opened-expected-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-opened-expected-win.txt
@@ -1,6 +1,6 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+++IA2_ROLE_SECTION
 ++ROLE_SYSTEM_DIALOG IA2_STATE_MODAL
 ++++ROLE_SYSTEM_STATICTEXT name='The dialog subtree should be the only text content in the accessibility tree. '
 ++++ROLE_SYSTEM_LINK name='Link inside the dialog.' FOCUSABLE
 ++++++ROLE_SYSTEM_STATICTEXT name='Link inside the dialog.'
-++IA2_ROLE_SECTION
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-android.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-android.txt
index aaa0943..75cb37a 100644
--- a/content/test/data/accessibility/html/modal-dialog-stack-expected-android.txt
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-android.txt
@@ -1,5 +1,5 @@
 android.webkit.WebView focusable focused scrollable
+++android.view.View
 ++android.app.Dialog role_description='dialog'
 ++++android.view.View name='This is the now active dialog. Of course it should be in the tree. '
 ++++android.widget.Button role_description='button' clickable focusable name='This is in the active dialog and should be in the tree.'
-++android.view.View
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-auralinux.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-auralinux.txt
index c072723..7a0a189 100644
--- a/content/test/data/accessibility/html/modal-dialog-stack-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-auralinux.txt
@@ -1,5 +1,5 @@
 [document web]
+++[section]
 ++[dialog] modal
 ++++[text] name='This is the now active dialog. Of course it should be in the tree. '
 ++++[push button] name='This is in the active dialog and should be in the tree.'
-++[section]
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-blink.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-blink.txt
index 98dcc461..c5e010eb 100644
--- a/content/test/data/accessibility/html/modal-dialog-stack-expected-blink.txt
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-blink.txt
@@ -1,6 +1,14 @@
 rootWebArea
+++genericContainer ignored invisible
+++++section ignored invisible
+++++++dialog ignored invisible name=' This was the top dialog and should not be in the tree. '
+++++++popUpButton collapsed ignored invisible value='This should be pruned out of the tree.'
+++++button ignored invisible name='This button should not be in the tree.'
+++++dialog ignored invisible name=' This was the middle dialog and should not be in the tree. '
+++genericContainer ignored invisible
+++dialog ignored invisible
+++genericContainer
 ++dialog
 ++++staticText name='This is the now active dialog. Of course it should be in the tree. '
 ++++++inlineTextBox name='This is the now active dialog. Of course it should be in the tree. '
 ++++button name='This is in the active dialog and should be in the tree.'
-++genericContainer
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-mac.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-mac.txt
index 1561a8b7..fd373b1 100644
--- a/content/test/data/accessibility/html/modal-dialog-stack-expected-mac.txt
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-mac.txt
@@ -1,5 +1,5 @@
 AXWebArea
+++AXGroup
 ++AXGroup AXSubrole=AXApplicationDialog
 ++++AXStaticText AXValue='This is the now active dialog. Of course it should be in the tree. '
 ++++AXButton AXTitle='This is in the active dialog and should be in the tree.'
-++AXGroup
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt
index 35d2709..7275201 100644
--- a/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt
@@ -1,5 +1,5 @@
 document
+++group
 ++dialog Window.IsModal=true
 ++++description Name='This is the now active dialog. Of course it should be in the tree. '
 ++++button Name='This is in the active dialog and should be in the tree.'
-++group
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-win.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-win.txt
index 0160c77e..df25937bd 100644
--- a/content/test/data/accessibility/html/modal-dialog-stack-expected-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+++IA2_ROLE_SECTION
 ++ROLE_SYSTEM_DIALOG IA2_STATE_MODAL
 ++++ROLE_SYSTEM_STATICTEXT name='This is the now active dialog. Of course it should be in the tree. '
 ++++ROLE_SYSTEM_PUSHBUTTON name='This is in the active dialog and should be in the tree.' FOCUSABLE
-++IA2_ROLE_SECTION
diff --git a/content/test/data/accessibility/html/open-modal-expected-blink.txt b/content/test/data/accessibility/html/open-modal-expected-blink.txt
new file mode 100644
index 0000000..d28714eb
--- /dev/null
+++ b/content/test/data/accessibility/html/open-modal-expected-blink.txt
@@ -0,0 +1,23 @@
+rootWebArea
+++genericContainer ignored
+++++staticText name='some text'
+++++++inlineTextBox name='some text'
+++++genericContainer ignored
+=== Start Continuation ===
+rootWebArea
+++genericContainer ignored invisible
+++++genericContainer ignored invisible
+++genericContainer
+++dialog
+++++genericContainer name='Dialog Text'
+++++++staticText name='Dialog Text'
+++++++++inlineTextBox name='Dialog Text'
+=== Start Continuation ===
+rootWebArea
+++genericContainer ignored invisible
+++++genericContainer ignored invisible
+++genericContainer
+++dialog
+++++genericContainer name='Done'
+++++++staticText name='Done'
+++++++++inlineTextBox name='Done'
diff --git a/content/test/data/accessibility/html/open-modal.html b/content/test/data/accessibility/html/open-modal.html
new file mode 100644
index 0000000..7a6ec5d7
--- /dev/null
+++ b/content/test/data/accessibility/html/open-modal.html
@@ -0,0 +1,34 @@
+<!--
+@EXECUTE-AND-WAIT-FOR:open_modal()
+@EXECUTE-AND-WAIT-FOR:remove_span()
+This regression test ensures that the dialog is included in the tree.
+There was a bug where ClearChildren would disconnect indirect children from
+their parent. crbug: 996992
+-->
+<!DOCTYPE html>
+<html>
+
+<span>some text</span>
+<div>
+  <dialog>
+    <div id="dialog-text" tabindex="0">Dialog Text</div>
+  </dialog>
+</div>
+
+
+<script>
+  function open_modal() {
+    document.querySelector("dialog").showModal();
+    return "Dialog Text";
+  }
+  function remove_span() {
+    // Cause the root to be updated.
+    let span = document.querySelector("span");
+    span.parentElement.removeChild(span);
+
+    document.getElementById("dialog-text").innerText = "Done";
+    return "Done";
+  }
+</script>
+
+</html>
\ No newline at end of file
diff --git a/content/test/data/accessibility/readme.md b/content/test/data/accessibility/readme.md
index a521df7..36a00201 100644
--- a/content/test/data/accessibility/readme.md
+++ b/content/test/data/accessibility/readme.md
@@ -118,6 +118,14 @@
 You can add as many `@WAIT-FOR:` directives as you want, the test won't finish
 until all strings appear.
 
+You may also want to execute script and then capture a dump. Rather than use
+`setTimeout` and `@WAIT-FOR:`, consider using the `@EXECUTE-AND-WAIT-FOR:`
+directive. This directive expects a javascript function that returns a string to
+wait for. If a string is not returned, the tree dumper will not wait.
+`@EXECUTE-AND-WAIT-FOR:` directives are executed in order, after the document is
+ready and all `@WAIT-FOR:` strings have been found.
+Example: `@EXECUTE-AND-WAIT-FOR: foo()`
+
 Or, you may need to write an event test that keeps dumping events until a
 specific event line. In this case, use `@RUN-UNTIL-EVENT` with a substring that
 should occur in the event log, e.g.,
diff --git a/content/test/data/bundled_exchanges/broken_bundle/index.html b/content/test/data/bundled_exchanges/broken_bundle/index.html
new file mode 100644
index 0000000..541c2ba
--- /dev/null
+++ b/content/test/data/bundled_exchanges/broken_bundle/index.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<title>Ready</title>>
+<body>
+</body>
diff --git a/content/test/data/bundled_exchanges/broken_bundle/script.js b/content/test/data/bundled_exchanges/broken_bundle/script.js
new file mode 100644
index 0000000..5d099d6e
--- /dev/null
+++ b/content/test/data/bundled_exchanges/broken_bundle/script.js
@@ -0,0 +1,5 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+document.title = "Done";
diff --git a/content/test/data/bundled_exchanges/broken_bundle_base.wbn b/content/test/data/bundled_exchanges/broken_bundle_base.wbn
new file mode 100644
index 0000000..20bdc52
--- /dev/null
+++ b/content/test/data/bundled_exchanges/broken_bundle_base.wbn
Binary files differ
diff --git a/content/test/data/bundled_exchanges/broken_bundle_broken_first_entry.wbn b/content/test/data/bundled_exchanges/broken_bundle_broken_first_entry.wbn
new file mode 100644
index 0000000..dbbf355
--- /dev/null
+++ b/content/test/data/bundled_exchanges/broken_bundle_broken_first_entry.wbn
Binary files differ
diff --git a/content/test/data/bundled_exchanges/broken_bundle_broken_script_entry.wbn b/content/test/data/bundled_exchanges/broken_bundle_broken_script_entry.wbn
new file mode 100644
index 0000000..f09af60
--- /dev/null
+++ b/content/test/data/bundled_exchanges/broken_bundle_broken_script_entry.wbn
Binary files differ
diff --git a/content/test/data/bundled_exchanges/generate-test-wbns.sh b/content/test/data/bundled_exchanges/generate-test-wbns.sh
index 2707c176..9fad460 100755
--- a/content/test/data/bundled_exchanges/generate-test-wbns.sh
+++ b/content/test/data/bundled_exchanges/generate-test-wbns.sh
@@ -19,3 +19,29 @@
   -dir bundled_exchanges_browsertest/ \
   -manifestURL https://test.example.org/manifest.webmanifest \
   -o bundled_exchanges_browsertest.wbn
+
+# Generate a base WBN which will used to generate broken WBN.
+# This WBN must contains 3 entries:
+#   [1]: https://test.example.org/
+#   [2]: https://test.example.org/index.html
+#   [3]: https://test.example.org/script.html
+gen-bundle \
+  -version b1 \
+  -baseURL https://test.example.org/ \
+  -primaryURL https://test.example.org/ \
+  -dir broken_bundle/ \
+  -o broken_bundle_base.wbn
+
+# Rewrite ":status" (3a737461747573) header of the first entry to ":xxxxxx"
+# (3a787878787878).
+xxd -p broken_bundle_base.wbn |
+  tr -d '\n' |
+  sed 's/3a737461747573/3a787878787878/' |
+  xxd -r -p > broken_bundle_broken_first_entry.wbn
+
+# Rewrite ":status" (3a737461747573) header of the third entry (script.js) to
+# ":xxxxxx" (3a787878787878).
+xxd -p broken_bundle_base.wbn |
+  tr -d '\n' |
+  sed 's/3a737461747573/3a787878787878/3' |
+  xxd -r -p > broken_bundle_broken_script_entry.wbn
diff --git a/content/test/gpu/gpu_tests/gpu_helper.py b/content/test/gpu/gpu_tests/gpu_helper.py
index 56f453c1..531f853 100644
--- a/content/test/gpu/gpu_tests/gpu_helper.py
+++ b/content/test/gpu/gpu_tests/gpu_helper.py
@@ -10,8 +10,6 @@
 EXPECTATIONS_DRIVER_TAGS = frozenset([
     'angle_lt_25.20.100.6444',
     'angle_lt_25.20.100.6577',
-    'mesa_lt_17.1.6',
-    'mesa_lt_17.3.9',
     'mesa_lt_19.1.2'
 ])
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 6a4b7c5..e1e7104 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -6,7 +6,7 @@
 #         nvidia nvidia-0xf02 nvidia-0xfe9 nvidia-0x1cb3
 #         qualcomm qualcomm-adreno-(tm)-540 ]
 # tags: [ debug ]
-# tags: [ angle_lt_25.20.100.6444 angle_lt_25.20.100.6577 mesa_lt_17.1.6 mesa_lt_17.3.9 mesa_lt_19.1.2 ]
+# tags: [ angle_lt_25.20.100.6444 angle_lt_25.20.100.6577 mesa_lt_19.1.2 ]
 # results: [ Failure RetryOnFailure Skip ]
 
 
@@ -256,7 +256,6 @@
 crbug.com/989050 [ d3d11 win7 amd ] conformance2/textures/misc/tex-unpack-params-imagedata.html [ RetryOnFailure ]
 
 # Passthrough command decoder
-crbug.com/844349 [ passthrough ] conformance/misc/webgl-specific-stencil-settings.html [ Failure ]
 crbug.com/951628 [ passthrough ] conformance/rendering/blending.html [ Failure ]
 crbug.com/953120 [ passthrough ] conformance/programs/program-test.html [ Failure ]
 
@@ -586,13 +585,8 @@
 
 # Intel failed issues
 crbug.com/950552 [ linux intel ] conformance2/textures/misc/tex-3d-size-limit.html [ Failure ]
-crbug.com/905011 [ linux intel opengl passthrough ] conformance2/textures/misc/tex-subimage3d-pixel-buffer-bug.html [ Failure ]
 
 # Intel driver issues
-crbug.com/680675 [ linux intel mesa_lt_17.1.6 ] conformance/extensions/webgl-compressed-texture-astc.html [ Failure ]
-crbug.com/905011 [ linux intel no-angle mesa_lt_17.3.9 ] conformance2/textures/misc/tex-subimage3d-pixel-buffer-bug.html [ Failure ]
-crbug.com/666384 [ linux intel no-angle mesa_lt_17.3.9 ] deqp/functional/gles3/shadertexturefunction/texturesize.html [ Failure ]
-crbug.com/666384 [ linux intel no-angle mesa_lt_17.3.9 ] conformance2/textures/misc/tex-3d-mipmap-levels-intel-bug.html [ Failure ]
 crbug.com/950552 [ linux intel mesa_lt_19.1.2 ] conformance2/textures/misc/tex-base-level-bug.html [ Failure ]
 
 # Linux only.
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 9ba7f41..d7457bf 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -8,7 +8,7 @@
 # tags: [ d3d11 d3d9 no-angle opengl opengles vulkan ]
 # tags: [ no-passthrough passthrough ]
 # tags: [ webgl-version-1 ]
-# tags: [ angle_lt_25.20.100.6444 mesa_lt_17.1.6 ]
+# tags: [ angle_lt_25.20.100.6444 ]
 # results: [ Failure RetryOnFailure Skip ]
 
 
@@ -321,7 +321,6 @@
 crbug.com/angleproject/3769 [ mac passthrough ] conformance/glsl/misc/shader-struct-scope.html [ Failure ]
 crbug.com/angleproject/3769 [ mac passthrough ] conformance/glsl/misc/shader-with-short-circuiting-operators.html [ Failure ]
 crbug.com/angleproject/3771 [ mac passthrough ] conformance/glsl/samplers/glsl-function-texture2dprojlod.html [ Failure ]
-crbug.com/angleproject/3770 [ mac passthrough ] conformance/ogles/GL/build/build_177_to_178.html [ Failure ]
 crbug.com/angleproject/3766 [ mac passthrough ] conformance/textures/misc/gl-teximage.html [ Failure ]
 
 # Mac / Passthrough command decoder / AMD
@@ -334,9 +333,6 @@
 # Linux failures   #
 ####################
 
-# Intel driver issues
-crbug.com/680675 [ linux intel mesa_lt_17.1.6 ] conformance/extensions/webgl-compressed-texture-astc.html [ Failure ]
-
 # The following two tests only fail on Linux/Intel with Mesa 18.0.5,
 # not on Mesa 17.1.4 with the same Intel HD 630 GPU.
 crbug.com/928530 [ linux intel no-passthrough ] conformance/programs/program-test.html [ Failure ]
diff --git a/content/utility/services.cc b/content/utility/services.cc
index 4213a1bd..bf64f5f 100644
--- a/content/utility/services.cc
+++ b/content/utility/services.cc
@@ -24,8 +24,9 @@
     mojo::PendingReceiver<network::mojom::NetworkService> receiver) {
   auto binders = std::make_unique<service_manager::BinderRegistry>();
   GetContentClient()->utility()->RegisterNetworkBinders(binders.get());
-  return std::make_unique<network::NetworkService>(std::move(binders),
-                                                   std::move(receiver));
+  return std::make_unique<network::NetworkService>(
+      std::move(binders), std::move(receiver),
+      /*delay_initialization_until_set_client=*/true);
 }
 
 auto RunVideoCapture(
diff --git a/device/vr/orientation/orientation_session.cc b/device/vr/orientation/orientation_session.cc
index 994f4df..35207e4 100644
--- a/device/vr/orientation/orientation_session.cc
+++ b/device/vr/orientation/orientation_session.cc
@@ -44,8 +44,8 @@
 }
 
 void VROrientationSession::GetEnvironmentIntegrationProvider(
-    mojom::XREnvironmentIntegrationProviderAssociatedRequest
-        environment_request) {
+    mojo::PendingAssociatedReceiver<mojom::XREnvironmentIntegrationProvider>
+        environment_provider) {
   // Environment integration is not supported. This call should not
   // be made on this device.
   mojo::ReportBadMessage("Environment integration is not supported.");
diff --git a/device/vr/orientation/orientation_session.h b/device/vr/orientation/orientation_session.h
index d978a82..ab8a7af 100644
--- a/device/vr/orientation/orientation_session.h
+++ b/device/vr/orientation/orientation_session.h
@@ -13,6 +13,7 @@
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "device/vr/vr_device.h"
 #include "device/vr/vr_export.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -36,7 +37,7 @@
   ~VROrientationSession() override;
 
   void GetEnvironmentIntegrationProvider(
-      mojom::XREnvironmentIntegrationProviderAssociatedRequest
+      mojo::PendingAssociatedReceiver<mojom::XREnvironmentIntegrationProvider>
           environment_provider) override;
   void SetInputSourceButtonListener(
       mojo::PendingAssociatedRemote<device::mojom::XRInputSourceButtonListener>)
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom
index ba7ef6fd..5e53c01 100644
--- a/device/vr/public/mojom/vr_service.mojom
+++ b/device/vr/public/mojom/vr_service.mojom
@@ -602,7 +602,8 @@
   // for some reason, such as device disconnection.
   GetFrameData(XRFrameDataRequestOptions? options) => (XRFrameData? frame_data);
   GetEnvironmentIntegrationProvider(
-      associated XREnvironmentIntegrationProvider& environment_provider);
+      pending_associated_receiver<XREnvironmentIntegrationProvider>
+          environment_provider);
   SetInputSourceButtonListener(
       pending_associated_remote<XRInputSourceButtonListener>? event_listener);
 };
diff --git a/device/vr/windows/compositor_base.cc b/device/vr/windows/compositor_base.cc
index 4e0c66e8..9ec0f335 100644
--- a/device/vr/windows/compositor_base.cc
+++ b/device/vr/windows/compositor_base.cc
@@ -412,8 +412,8 @@
 }
 
 void XRCompositorCommon::GetEnvironmentIntegrationProvider(
-    device::mojom::XREnvironmentIntegrationProviderAssociatedRequest
-        environment_provider) {
+    mojo::PendingAssociatedReceiver<
+        device::mojom::XREnvironmentIntegrationProvider> environment_provider) {
   // Environment integration is not supported. This call should not
   // be made on this device.
   mojo::ReportBadMessage("Environment integration is not supported.");
diff --git a/device/vr/windows/compositor_base.h b/device/vr/windows/compositor_base.h
index 9e5ebcb..a800719 100644
--- a/device/vr/windows/compositor_base.h
+++ b/device/vr/windows/compositor_base.h
@@ -14,6 +14,7 @@
 #include "device/vr/util/sliding_average.h"
 #include "device/vr/vr_device.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -75,8 +76,9 @@
       mojom::XRFrameDataPtr frame_data);
 
   void GetEnvironmentIntegrationProvider(
-      device::mojom::XREnvironmentIntegrationProviderAssociatedRequest
-          environment_provider) final;
+      mojo::PendingAssociatedReceiver<
+          device::mojom::XREnvironmentIntegrationProvider> environment_provider)
+      final;
 
   void RequestGamepadProvider(
       mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> receiver);
diff --git a/docs/android_accessing_cpp_switches_in_java.md b/docs/android_accessing_cpp_switches_in_java.md
index b9f12f2..79ff81d 100644
--- a/docs/android_accessing_cpp_switches_in_java.md
+++ b/docs/android_accessing_cpp_switches_in_java.md
@@ -11,76 +11,82 @@
 
 ## Usage
 
-1. Add directives to your C++ switch file
-
-    ```cpp
-    // GENERATED_JAVA_PACKAGE: org.chromium.chrome
-
-    // ...snip...
-
-    // Documentation for the following switch.
-    const char kSomeSwitch[] = "some-switch";
-
-    // ...snip...
-    ```
-
-2. Create a template file
+1. Create a template file (ex. `FooSwitches.java.tmpl`)
    ```java
-    // Copyright {YEAR} The Chromium Authors. All rights reserved.
+    // Copyright $YEAR The Chromium Authors. All rights reserved.
     // Use of this source code is governed by a BSD-style license that can be
     // found in the LICENSE file.
 
-    // This file is autogenerated by
-    //     {SCRIPT_NAME}
-    // From
-    //     {SOURCE_PATH}, and
-    //     {TEMPLATE_PATH}
-
-    package my.java.package
+    package org.chromium.foo;
 
     // Be sure to escape any curly braces in your template by doubling as
     // follows.
-    public abstract class MySwitches {{
+    /**
+     * Contains command line switches that are specific to the foo project.
+     */
+    public final class FooSwitches {{
 
-    {NATIVE_SWITCHES}
+    {NATIVE_STRINGS}
 
+        // Prevents instantiation.
+        private FooSwitches() {{}}
     }}
    ```
 
-3. Add a new build target
+2. Add a new build target
 
     ```gn
     import("//build/config/android/rules.gni")
 
-    java_cpp_strings("foo_generated_switch") {
+    java_cpp_strings("java_switches") {
       sources = [
-        "//base/android/native_foo_switches.cc",
+        "//base/android/foo_switches.cc",
       ]
-      template = "//base/android/java_templates/MySwitches.java.tmpl"
+      template = "//base/android/java_templates/FooSwitches.java.tmpl"
     }
     ```
 
-5. Add the new target to the desired android_library targets srcjar_deps:
+3. Add the new target to the desired `android_library` targets `srcjar_deps`:
 
     ```gn
     android_library("base_java") {
       srcjar_deps = [
-        ":foo_generated_switches",
+        ":java_switches",
       ]
     }
     ```
 
-5. The generated file `org/chromium/chrome/NativeFooSwitches.java` would contain:
+4. The generated file `out/Default/gen/.../org/chromium/foo/FooSwitches.java`
+   would contain:
 
     ```java
-    package org.chromium.chrome;
+    // Copyright $YEAR The Chromium Authors. All rights reserved.
+    // Use of this source code is governed by a BSD-style license that can be
+    // found in the LICENSE file.
 
-    public final class NativeFooSwitches {
+    package org.chromium.foo;
+
+    /**
+     * Contains command line switches that are specific to the foo project.
+     */
+    public final class FooSwitches {
+
         // ...snip...
 
+        // This following string constants were inserted by
+        //     java_cpp_strings.py
+        // From
+        //     ../../base/android/foo_switches.cc
+        // Into
+        //     ../../base/android/java_templates/FooSwitches.java.tmpl
+
+        // Documentation for the C++ switch is copied here.
         public static final String SOME_SWITCH = "some-switch";
 
         // ...snip...
+
+        // Prevents instantiation.
+        private FooSwitches() {}
     }
     ```
 
diff --git a/docs/infra/cq.md b/docs/infra/cq.md
index f7c04d89..4249dd8 100644
--- a/docs/infra/cq.md
+++ b/docs/infra/cq.md
@@ -63,8 +63,9 @@
 ### What exactly does the CQ run?
 
 CQ runs the jobs specified in [commit-queue.cfg][2]. See
-[`cq_builders.md`](cq_builders.md) for an auto generated file with links to
-information about the builders on the CQ.
+[`cq-builders.md`](https://chromium.googlesource.com/chromium/src/+/master/src/infra/config/generated/cq-builders.md)
+for an auto generated file with links to information about the builders on the
+CQ.
 
 Some of these jobs are experimental. This means they are executed on a
 percentage of CQ builds, and the outcome of the build doesn't affect if the CL
diff --git a/docs/infra/cq_builders.md b/docs/infra/cq_builders.md
deleted file mode 100644
index 0ebf866..0000000
--- a/docs/infra/cq_builders.md
+++ /dev/null
@@ -1,350 +0,0 @@
-# List of CQ builders
-
-This page is auto generated using the script
-//infra/config/cq_cfg_presubmit.py. Do not manually edit.
-
-[TOC]
-
-Each builder name links to that builder on Milo. The "Backing builders" links
-point to the file used to determine which configurations a builder should copy
-when running. These links might 404 or error; they are hard-coded right now,
-using common assumptions about how builders are configured.
-
-## Required builders
-
-These builders must pass before a CL may land.
-
-* [android-binary-size](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-binary-size) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android-binary-size)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-binary-size))
-
-* [android-kitkat-arm-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-kitkat-arm-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android-kitkat-arm-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-kitkat-arm-rel))
-
-* [android-marshmallow-arm64-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-marshmallow-arm64-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android-marshmallow-arm64-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-marshmallow-arm64-rel))
-
-* [android_arm64_dbg_recipe](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_arm64_dbg_recipe) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android_arm64_dbg_recipe)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_arm64_dbg_recipe))
-
-* [android_clang_dbg_recipe](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_clang_dbg_recipe) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android_clang_dbg_recipe)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_clang_dbg_recipe))
-
-* [android_compile_dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_compile_dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android_compile_dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_compile_dbg))
-
-* [android_cronet](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_cronet) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android_cronet)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_cronet))
-
-* [cast_shell_android](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/cast_shell_android) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/cast_shell_android)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+cast_shell_android))
-
-* [cast_shell_linux](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/cast_shell_linux) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/cast_shell_linux)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+cast_shell_linux))
-
-* [chromeos-amd64-generic-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/chromeos-amd64-generic-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/chromeos-amd64-generic-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+chromeos-amd64-generic-rel))
-
-* [chromeos-arm-generic-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/chromeos-arm-generic-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/chromeos-arm-generic-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+chromeos-arm-generic-rel))
-
-* [chromium_presubmit](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/chromium_presubmit) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/chromium_presubmit)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+chromium_presubmit))
-
-* [fuchsia_arm64](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/fuchsia_arm64) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/fuchsia_arm64)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+fuchsia_arm64))
-
-* [fuchsia_x64](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/fuchsia_x64) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/fuchsia_x64)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+fuchsia_x64))
-
-* [ios-simulator](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-simulator) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/ios-simulator)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-simulator))
-
-* [linux-chromeos-compile-dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-chromeos-compile-dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux-chromeos-compile-dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-chromeos-compile-dbg))
-
-* [linux-chromeos-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-chromeos-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux-chromeos-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-chromeos-rel))
-
-* [linux-libfuzzer-asan-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-libfuzzer-asan-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux-libfuzzer-asan-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-libfuzzer-asan-rel))
-
-* [linux-ozone-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-ozone-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux-ozone-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-ozone-rel))
-
-* [linux-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-rel))
-
-* [linux_chromium_asan_rel_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_chromium_asan_rel_ng) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux_chromium_asan_rel_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_chromium_asan_rel_ng))
-
-* [linux_chromium_compile_dbg_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_chromium_compile_dbg_ng) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux_chromium_compile_dbg_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_chromium_compile_dbg_ng))
-
-* [linux_chromium_tsan_rel_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_chromium_tsan_rel_ng) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux_chromium_tsan_rel_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_chromium_tsan_rel_ng))
-
-* [mac-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/mac-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/mac-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+mac-rel))
-
-* [mac_chromium_compile_dbg_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/mac_chromium_compile_dbg_ng) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/mac_chromium_compile_dbg_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+mac_chromium_compile_dbg_ng))
-
-* [win-libfuzzer-asan-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/win-libfuzzer-asan-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/win-libfuzzer-asan-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+win-libfuzzer-asan-rel))
-
-* [win10_chromium_x64_rel_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/win10_chromium_x64_rel_ng) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/win10_chromium_x64_rel_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+win10_chromium_x64_rel_ng))
-
-* [win_chromium_compile_dbg_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/win_chromium_compile_dbg_ng) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/win_chromium_compile_dbg_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+win_chromium_compile_dbg_ng))
-
-
-## Optional builders
-
-These builders optionally run, depending on the files in a
-CL. For example, a CL which touches `//gpu/BUILD.gn` would trigger the builder
-`android_optional_gpu_tests_rel`, due to the `location_regexp` values for that
-builder.
-
-* [android-cronet-arm-dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-cronet-arm-dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android-cronet-arm-dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-cronet-arm-dbg))
-
-  Path regular expressions:
-    * [`//components/cronet/.+`](https://cs.chromium.org/chromium/src/components/cronet/)
-    * [`//components/grpc_support/.+`](https://cs.chromium.org/chromium/src/components/grpc_support/)
-    * [`//build/android/.+`](https://cs.chromium.org/chromium/src/build/android/)
-    * [`//build/config/android/.+`](https://cs.chromium.org/chromium/src/build/config/android/)
-
-* [android_compile_x64_dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_compile_x64_dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android_compile_x64_dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_compile_x64_dbg))
-
-  Path regular expressions:
-    * [`//chrome/android/java/src/org/chromium/chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser/vr/)
-    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
-    * [`//sandbox/linux/seccomp-bpf/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf/)
-    * [`//sandbox/linux/seccomp-bpf-helpers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf-helpers/)
-    * [`//sandbox/linux/system_headers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/system_headers/)
-    * [`//sandbox/linux/tests/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/tests/)
-    * [`//third_party/gvr-android-sdk/.+`](https://cs.chromium.org/chromium/src/third_party/gvr-android-sdk/)
-
-* [android_compile_x86_dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_compile_x86_dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android_compile_x86_dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_compile_x86_dbg))
-
-  Path regular expressions:
-    * [`//chrome/android/java/src/org/chromium/chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser/vr/)
-    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
-    * [`//sandbox/linux/seccomp-bpf/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf/)
-    * [`//sandbox/linux/seccomp-bpf-helpers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf-helpers/)
-    * [`//sandbox/linux/system_headers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/system_headers/)
-    * [`//sandbox/linux/tests/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/tests/)
-    * [`//third_party/gvr-android-sdk/.+`](https://cs.chromium.org/chromium/src/third_party/gvr-android-sdk/)
-
-* [android_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_optional_gpu_tests_rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_optional_gpu_tests_rel))
-
-  Path regular expressions:
-    * [`//cc/.+`](https://cs.chromium.org/chromium/src/cc/)
-    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
-    * [`//components/viz/.+`](https://cs.chromium.org/chromium/src/components/viz/)
-    * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
-    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
-    * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
-    * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
-    * [`//services/viz/.+`](https://cs.chromium.org/chromium/src/services/viz/)
-    * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
-    * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
-    * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/)
-    * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
-
-* [chromeos-amd64-generic-dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/chromeos-amd64-generic-dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/chromeos-amd64-generic-dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+chromeos-amd64-generic-dbg))
-
-  Path regular expressions:
-    * [`//content/gpu/.+`](https://cs.chromium.org/chromium/src/content/gpu/)
-    * [`//media/.+`](https://cs.chromium.org/chromium/src/media/)
-
-* [chromeos-kevin-compile-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/chromeos-kevin-compile-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/chromeos-kevin-compile-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+chromeos-kevin-compile-rel))
-
-  Path regular expressions:
-    * [`//chromeos/CHROMEOS_LKGM`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:chromeos/CHROMEOS_LKGM)
-
-* [chromeos-kevin-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/chromeos-kevin-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/chromeos-kevin-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+chromeos-kevin-rel))
-
-  Path regular expressions:
-    * [`//build/chromeos/.+`](https://cs.chromium.org/chromium/src/build/chromeos/)
-    * [`//build/config/chromeos/.*`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:build/config/chromeos/.*)
-
-* [closure_compilation](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/closure_compilation) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/closure_compilation)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+closure_compilation))
-
-  Path regular expressions:
-    * [`//third_party/closure_compiler/.+`](https://cs.chromium.org/chromium/src/third_party/closure_compiler/)
-
-* [dawn-linux-x64-deps-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/dawn-linux-x64-deps-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/dawn-linux-x64-deps-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+dawn-linux-x64-deps-rel))
-
-  Path regular expressions:
-    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//testing/buildbot/chromium.dawn.json`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:testing/buildbot/chromium.dawn.json)
-    * [`//third_party/blink/renderer/modules/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgpu/)
-    * [`//third_party/blink/web_tests/external/wpt/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/external/wpt/webgpu/)
-    * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/WebGPUExpectations)
-    * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
-
-* [dawn-mac-x64-deps-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/dawn-mac-x64-deps-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/dawn-mac-x64-deps-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+dawn-mac-x64-deps-rel))
-
-  Path regular expressions:
-    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//testing/buildbot/chromium.dawn.json`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:testing/buildbot/chromium.dawn.json)
-    * [`//third_party/blink/renderer/modules/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgpu/)
-    * [`//third_party/blink/web_tests/external/wpt/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/external/wpt/webgpu/)
-    * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/WebGPUExpectations)
-    * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
-
-* [dawn-win10-x64-deps-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/dawn-win10-x64-deps-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/dawn-win10-x64-deps-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+dawn-win10-x64-deps-rel))
-
-  Path regular expressions:
-    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//testing/buildbot/chromium.dawn.json`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:testing/buildbot/chromium.dawn.json)
-    * [`//third_party/blink/renderer/modules/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgpu/)
-    * [`//third_party/blink/web_tests/external/wpt/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/external/wpt/webgpu/)
-    * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/WebGPUExpectations)
-    * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
-
-* [dawn-win10-x86-deps-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/dawn-win10-x86-deps-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/dawn-win10-x86-deps-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+dawn-win10-x86-deps-rel))
-
-  Path regular expressions:
-    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//testing/buildbot/chromium.dawn.json`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:testing/buildbot/chromium.dawn.json)
-    * [`//third_party/blink/renderer/modules/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgpu/)
-    * [`//third_party/blink/web_tests/external/wpt/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/external/wpt/webgpu/)
-    * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/WebGPUExpectations)
-    * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
-
-* [fuchsia-arm64-cast](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/fuchsia-arm64-cast) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/fuchsia-arm64-cast)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+fuchsia-arm64-cast))
-
-  Path regular expressions:
-    * [`//chromecast/.+`](https://cs.chromium.org/chromium/src/chromecast/)
-
-* [fuchsia-x64-cast](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/fuchsia-x64-cast) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/fuchsia-x64-cast)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+fuchsia-x64-cast))
-
-  Path regular expressions:
-    * [`//chromecast/.+`](https://cs.chromium.org/chromium/src/chromecast/)
-
-* [gpu-fyi-try-android-p-pixel-2-skv-32](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-skv-32) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/gpu-fyi-try-android-p-pixel-2-skv-32)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+gpu-fyi-try-android-p-pixel-2-skv-32))
-
-  Path regular expressions:
-    * [`//components/viz/.+`](https://cs.chromium.org/chromium/src/components/viz/)
-    * [`//content/test/gpu/gpu_tests/.+py`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:content/test/gpu/gpu_tests/.+py)
-    * [`//content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt)
-    * [`//gpu/vulkan/.+`](https://cs.chromium.org/chromium/src/gpu/vulkan/)
-    * [`//services/viz/.+`](https://cs.chromium.org/chromium/src/services/viz/)
-
-* [ios-simulator-cronet](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-simulator-cronet) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/ios-simulator-cronet)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-simulator-cronet))
-
-  Path regular expressions:
-    * [`//components/cronet/.+`](https://cs.chromium.org/chromium/src/components/cronet/)
-    * [`//components/grpc_support/.+`](https://cs.chromium.org/chromium/src/components/grpc_support/)
-    * [`//ios/.+`](https://cs.chromium.org/chromium/src/ios/)
-
-* [ios-simulator-full-configs](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-simulator-full-configs) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/ios-simulator-full-configs)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-simulator-full-configs))
-
-  Path regular expressions:
-    * [`//ios/.+`](https://cs.chromium.org/chromium/src/ios/)
-
-* [linux-blink-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-blink-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux-blink-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-blink-rel))
-
-  Path regular expressions:
-    * [`//cc/.+`](https://cs.chromium.org/chromium/src/cc/)
-    * [`//third_party/blink/renderer/core/paint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/paint/)
-    * [`//third_party/blink/renderer/core/svg/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/svg/)
-    * [`//third_party/blink/renderer/platform/graphics/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/)
-    * [`//third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint)
-    * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/)
-
-* [linux_chromium_dbg_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_chromium_dbg_ng) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux_chromium_dbg_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_chromium_dbg_ng))
-
-  Path regular expressions:
-    * [`//build/.*check_gn_headers.*`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:build/.*check_gn_headers.*)
-
-* [linux_layout_tests_composite_after_paint](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_layout_tests_composite_after_paint) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux_layout_tests_composite_after_paint)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_layout_tests_composite_after_paint))
-
-  Path regular expressions:
-    * [`//third_party/blink/renderer/core/paint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/paint/)
-    * [`//third_party/blink/renderer/core/svg/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/svg/)
-    * [`//third_party/blink/renderer/platform/graphics/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/)
-    * [`//third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint)
-    * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/)
-
-* [linux_layout_tests_layout_ng_disabled](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_layout_tests_layout_ng_disabled) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux_layout_tests_layout_ng_disabled)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_layout_tests_layout_ng_disabled))
-
-  Path regular expressions:
-    * [`//third_party/blink/renderer/core/editing/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/)
-    * [`//third_party/blink/renderer/core/layout/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/layout/)
-    * [`//third_party/blink/renderer/core/paint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/paint/)
-    * [`//third_party/blink/renderer/core/svg/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/svg/)
-    * [`//third_party/blink/renderer/platform/fonts/shaping/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/)
-    * [`//third_party/blink/renderer/platform/graphics/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/)
-    * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/)
-
-* [linux_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_optional_gpu_tests_rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_optional_gpu_tests_rel))
-
-  Path regular expressions:
-    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
-    * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
-    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
-    * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
-    * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
-    * [`//testing/buildbot/chromium.gpu.fyi.json`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:testing/buildbot/chromium.gpu.fyi.json)
-    * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
-    * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
-    * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/)
-    * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
-
-* [linux_vr](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_vr) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux_vr)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_vr))
-
-  Path regular expressions:
-    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
-
-* [mac_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/mac_optional_gpu_tests_rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/mac_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+mac_optional_gpu_tests_rel))
-
-  Path regular expressions:
-    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
-    * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
-    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
-    * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
-    * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
-    * [`//services/shape_detection/.+`](https://cs.chromium.org/chromium/src/services/shape_detection/)
-    * [`//testing/buildbot/chromium.gpu.fyi.json`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:testing/buildbot/chromium.gpu.fyi.json)
-    * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
-    * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
-    * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/)
-    * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
-
-* [win_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/win_optional_gpu_tests_rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/win_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+win_optional_gpu_tests_rel))
-
-  Path regular expressions:
-    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
-    * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
-    * [`//device/vr/.+`](https://cs.chromium.org/chromium/src/device/vr/)
-    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
-    * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
-    * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
-    * [`//testing/buildbot/chromium.gpu.fyi.json`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:testing/buildbot/chromium.gpu.fyi.json)
-    * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
-    * [`//third_party/blink/renderer/modules/vr/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/vr/)
-    * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
-    * [`//third_party/blink/renderer/modules/xr/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/xr/)
-    * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/)
-    * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
-
-
-## Experimental builders
-
-These builders are run on some percentage of builds. Their results are ignored
-by CQ. These are often used to test new configurations before they are added
-as required builders.
-
-* [android-marshmallow-arm64-coverage-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-marshmallow-arm64-coverage-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android-marshmallow-arm64-coverage-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-marshmallow-arm64-coverage-rel))
-
-  * Experimental percentage: 20
-
-* [android-pie-arm64-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-pie-arm64-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android-pie-arm64-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-pie-arm64-rel))
-
-  * Experimental percentage: 50
-
-* [chromeos-kevin-experimental-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/chromeos-kevin-experimental-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/chromeos-kevin-experimental-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+chromeos-kevin-experimental-rel))
-
-  * Experimental percentage: 5
-
-* [fuchsia-compile-x64-dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/fuchsia-compile-x64-dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/fuchsia-compile-x64-dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+fuchsia-compile-x64-dbg))
-
-  * Experimental percentage: 5
-
-* [ios-device](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-device) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/ios-device)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-device))
-
-  https://crbug.com/739556; make this non-experimental ASAP.
-
-  * Experimental percentage: 10
-
-* [ios-device-xcode-clang](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-device-xcode-clang) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/ios-device-xcode-clang)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-device-xcode-clang))
-
-  https://crbug.com/739556
-
-  * Experimental percentage: 10
-
-* [ios-simulator-xcode-clang](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-simulator-xcode-clang) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/ios-simulator-xcode-clang)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-simulator-xcode-clang))
-
-  https://crbug.com/739556
-
-  * Experimental percentage: 10
-
diff --git a/docs/jumbo.md b/docs/jumbo.md
index 0daa6d50..c2426e3 100644
--- a/docs/jumbo.md
+++ b/docs/jumbo.md
@@ -69,7 +69,7 @@
 1. Add `import("//build/config/jumbo.gni")` to `BUILD.gn`.
 2. Change your target, for instance `static_library`, to
    `jumbo_static_library`. So far `source_set`, `component`,
-   `static_library` and `split_static_library` are supported.
+   `static_library` are supported.
 3. Recompile and test.
 
 ### Example
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 9ad769b7..7a44fac8 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -86,6 +86,8 @@
     "content_verifier/content_hash.cc",
     "content_verifier/content_hash.h",
     "content_verifier/content_verifier_key.h",
+    "content_verifier/content_verifier_utils.cc",
+    "content_verifier/content_verifier_utils.h",
     "content_verifier/scoped_uma_recorder.h",
     "content_verifier_delegate.h",
     "content_verifier_io_data.cc",
@@ -613,6 +615,7 @@
     "computed_hashes_unittest.cc",
     "content_hash_fetcher_unittest.cc",
     "content_hash_tree_unittest.cc",
+    "content_verifier/content_hash_unittest.cc",
     "content_verifier_unittest.cc",
     "content_verify_job_unittest.cc",
     "error_map_unittest.cc",
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
index d92d065..b4cf75f 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -98,6 +98,21 @@
       network::URLLoaderCompletionStatus(net::ERR_ABORTED)));
 }
 
+WebRequestProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
+    WebRequestProxyingURLLoaderFactory* factory,
+    uint64_t request_id,
+    const network::ResourceRequest& request)
+    : factory_(factory),
+      request_(request),
+      original_initiator_(request.request_initiator),
+      request_id_(request_id),
+      proxied_loader_binding_(this),
+      proxied_client_binding_(this),
+      for_cors_preflight_(true),
+      has_any_extra_headers_listeners_(
+          ExtensionWebRequestEventRouter::GetInstance()
+              ->HasAnyExtraHeadersListener(factory_->browser_context_)) {}
+
 WebRequestProxyingURLLoaderFactory::InProgressRequest::~InProgressRequest() {
   // This is important to ensure that no outstanding blocking requests continue
   // to reference state owned by this object.
@@ -139,7 +154,8 @@
 
   current_request_uses_header_client_ =
       factory_->url_loader_header_client_receiver_.is_bound() &&
-      request_.url.SchemeIsHTTPOrHTTPS() && network_service_request_id_ != 0 &&
+      request_.url.SchemeIsHTTPOrHTTPS() &&
+      (for_cors_preflight_ || network_service_request_id_ != 0) &&
       ExtensionWebRequestEventRouter::GetInstance()
           ->HasExtraHeadersListenerForRequest(factory_->browser_context_,
                                               &info_.value());
@@ -157,6 +173,9 @@
   if (current_request_uses_header_client_) {
     continuation = base::BindRepeating(
         &InProgressRequest::ContinueToStartRequest, weak_factory_.GetWeakPtr());
+  } else if (for_cors_preflight_) {
+    // In this case we do nothing because extensions should see nothing.
+    return;
   } else {
     continuation =
         base::BindRepeating(&InProgressRequest::ContinueToBeforeSendHeaders,
@@ -374,6 +393,15 @@
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnLoaderCreated(
     mojo::PendingReceiver<network::mojom::TrustedHeaderClient> receiver) {
   header_client_receiver_.Bind(std::move(receiver));
+  if (for_cors_preflight_) {
+    // In this case we don't have |target_loader_| and
+    // |proxied_client_binding_|, and |receiver| is the only connection to the
+    // network service, so we observe mojo connection errors.
+    header_client_receiver_.set_disconnect_handler(base::BindOnce(
+        &WebRequestProxyingURLLoaderFactory::InProgressRequest::OnRequestError,
+        weak_factory_.GetWeakPtr(),
+        network::URLLoaderCompletionStatus(net::ERR_FAILED)));
+  }
 }
 
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnBeforeSendHeaders(
@@ -395,12 +423,19 @@
     OnHeadersReceivedCallback callback) {
   if (!current_request_uses_header_client_) {
     std::move(callback).Run(net::OK, base::nullopt, GURL());
+
+    if (for_cors_preflight_) {
+      // CORS preflight is supported only when "extraHeaders" is specified.
+      // Deletes |this|.
+      factory_->RemoveRequest(network_service_request_id_, request_id_);
+    }
     return;
   }
 
   on_headers_received_callback_ = std::move(callback);
   current_response_.headers =
       base::MakeRefCounted<net::HttpResponseHeaders>(headers);
+  current_response_.remote_endpoint = remote_endpoint;
   HandleResponseOrRedirectHeaders(
       base::BindOnce(&InProgressRequest::ContinueToHandleOverrideHeaders,
                      weak_factory_.GetWeakPtr()));
@@ -534,6 +569,11 @@
   }
 
   if (current_request_uses_header_client_ && !redirect_url_.is_empty()) {
+    if (for_cors_preflight_) {
+      // CORS preflight doesn't support redirect.
+      OnRequestError(network::URLLoaderCompletionStatus(net::ERR_FAILED));
+      return;
+    }
     HandleBeforeRequestRedirect();
     return;
   }
@@ -544,6 +584,13 @@
   if (header_client_receiver_.is_bound())
     header_client_receiver_.Resume();
 
+  if (for_cors_preflight_) {
+    // For CORS preflight requests, we have already started the request in
+    // the network service. We did block the request by blocking
+    // |header_client_receiver_|, which we unblocked right above.
+    return;
+  }
+
   if (!target_loader_.is_bound() && factory_->target_factory_.is_bound()) {
     // No extensions have cancelled us up to this point, so it's now OK to
     // initiate the real network request.
@@ -710,15 +757,36 @@
       current_response_.headers = override_headers_;
     }
   }
+
+  if (for_cors_preflight_ && !redirect_url_.is_empty()) {
+    OnRequestError(network::URLLoaderCompletionStatus(net::ERR_FAILED));
+    return;
+  }
+
   std::move(on_headers_received_callback_).Run(net::OK, headers, redirect_url_);
   override_headers_ = nullptr;
 
+  if (for_cors_preflight_) {
+    // If this is for CORS preflight, there is no associated client. We notify
+    // the completion here, and deletes |this|.
+    info_->AddResponseInfoFromResourceResponse(current_response_);
+    ExtensionWebRequestEventRouter::GetInstance()->OnResponseStarted(
+        factory_->browser_context_, &info_.value(), net::OK);
+    ExtensionWebRequestEventRouter::GetInstance()->OnCompleted(
+        factory_->browser_context_, &info_.value(), net::OK);
+
+    // Deletes |this|.
+    factory_->RemoveRequest(network_service_request_id_, request_id_);
+    return;
+  }
+
   if (proxied_client_binding_)
     proxied_client_binding_.ResumeIncomingMethodCallProcessing();
 }
 
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::
     ContinueToResponseStarted(int error_code) {
+  DCHECK(!for_cors_preflight_);
   if (error_code != net::OK) {
     OnRequestError(network::URLLoaderCompletionStatus(error_code));
     return;
@@ -815,12 +883,15 @@
     }
 
     if (result == net::ERR_IO_PENDING) {
-      // One or more listeners is blocking, so the request must be paused until
-      // they respond. |continuation| above will be invoked asynchronously to
-      // continue or cancel the request.
-      //
-      // We pause the binding here to prevent further client message processing.
-      proxied_client_binding_.PauseIncomingMethodCallProcessing();
+      if (proxied_client_binding_.is_bound()) {
+        // One or more listeners is blocking, so the request must be paused
+        // until they respond. |continuation| above will be invoked
+        // asynchronously to continue or cancel the request.
+        //
+        // We pause the binding here to prevent further client message
+        // processing.
+        proxied_client_binding_.PauseIncomingMethodCallProcessing();
+      }
       return;
     }
 
@@ -832,7 +903,8 @@
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnRequestError(
     const network::URLLoaderCompletionStatus& status) {
   if (!request_completed_) {
-    target_client_->OnComplete(status);
+    if (target_client_)
+      target_client_->OnComplete(status);
     ExtensionWebRequestEventRouter::GetInstance()->OnErrorOccurred(
         factory_->browser_context_, &info_.value(), true /* started */,
         status.error_code);
@@ -983,7 +1055,22 @@
 void WebRequestProxyingURLLoaderFactory::OnLoaderForCorsPreflightCreated(
     const network::ResourceRequest& request,
     mojo::PendingReceiver<network::mojom::TrustedHeaderClient> receiver) {
-  // TODO(yhirano): Implement this.
+  // Please note that the URLLoader is now starting, without waiting for
+  // additional signals from here. The URLLoader will be blocked before
+  // sending HTTP request headers (TrustedHeaderClient.OnBeforeSendHeaders),
+  // but the connection set up will be done before that. This is acceptable from
+  // Web Request API because the extension has already allowed to set up
+  // a connection to the same URL (i.e., the actual request), and distinguishing
+  // two connections for the actual request and the preflight request before
+  // sending request headers is very difficult.
+  const uint64_t web_request_id = request_id_generator_->Generate();
+
+  auto result = requests_.insert(std::make_pair(
+      web_request_id,
+      std::make_unique<InProgressRequest>(this, web_request_id, request)));
+
+  result.first->second->OnLoaderCreated(std::move(receiver));
+  result.first->second->Restart();
 }
 
 void WebRequestProxyingURLLoaderFactory::HandleAuthRequest(
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
index 79c6b0e..704dce7 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
@@ -46,6 +46,7 @@
                             public network::mojom::URLLoaderClient,
                             public network::mojom::TrustedHeaderClient {
    public:
+    // For usual requests
     InProgressRequest(
         WebRequestProxyingURLLoaderFactory* factory,
         uint64_t request_id,
@@ -56,6 +57,10 @@
         const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
         network::mojom::URLLoaderRequest loader_request,
         network::mojom::URLLoaderClientPtr client);
+    // For CORS preflights
+    InProgressRequest(WebRequestProxyingURLLoaderFactory* factory,
+                      uint64_t request_id,
+                      const network::ResourceRequest& request);
     ~InProgressRequest() override;
 
     void Restart();
@@ -128,10 +133,10 @@
     WebRequestProxyingURLLoaderFactory* const factory_;
     network::ResourceRequest request_;
     const base::Optional<url::Origin> original_initiator_;
-    const uint64_t request_id_;
-    const int32_t network_service_request_id_;
-    const int32_t routing_id_;
-    const uint32_t options_;
+    const uint64_t request_id_ = 0;
+    const int32_t network_service_request_id_ = 0;
+    const int32_t routing_id_ = 0;
+    const uint32_t options_ = 0;
     const net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
     mojo::Binding<network::mojom::URLLoader> proxied_loader_binding_;
     network::mojom::URLLoaderClientPtr target_client_;
@@ -154,6 +159,7 @@
     // lifetime.
     base::Optional<net::AuthCredentials> auth_credentials_;
 
+    const bool for_cors_preflight_ = false;
     bool request_completed_ = false;
 
     // If |has_any_extra_headers_listeners_| is set to true, the request will be
diff --git a/extensions/browser/api/webcam_private/webcam_private_api.h b/extensions/browser/api/webcam_private/webcam_private_api.h
index e7e5b48..01087874 100644
--- a/extensions/browser/api/webcam_private/webcam_private_api.h
+++ b/extensions/browser/api/webcam_private/webcam_private_api.h
@@ -176,10 +176,10 @@
   int min_focus_;
   int max_focus_;
   int focus_;
-  bool get_pan_;
-  bool get_tilt_;
-  bool get_zoom_;
-  bool get_focus_;
+  bool got_pan_;
+  bool got_tilt_;
+  bool got_zoom_;
+  bool got_focus_;
   bool success_;
 
   DISALLOW_COPY_AND_ASSIGN(WebcamPrivateGetFunction);
diff --git a/extensions/browser/api/webcam_private/webcam_private_api_chromeos.cc b/extensions/browser/api/webcam_private/webcam_private_api_chromeos.cc
index 80895b9..06c9643 100644
--- a/extensions/browser/api/webcam_private/webcam_private_api_chromeos.cc
+++ b/extensions/browser/api/webcam_private/webcam_private_api_chromeos.cc
@@ -373,11 +373,11 @@
       min_focus_(0),
       max_focus_(0),
       focus_(0),
-      get_pan_(false),
-      get_tilt_(false),
-      get_zoom_(false),
-      get_focus_(false),
-      success_(true) {}
+      got_pan_(false),
+      got_tilt_(false),
+      got_zoom_(false),
+      got_focus_(false),
+      success_(false) {}
 
 WebcamPrivateGetFunction::~WebcamPrivateGetFunction() {
 }
@@ -405,73 +405,82 @@
   return did_respond() ? AlreadyResponded() : RespondLater();
 }
 
+// Retrieve webcam parameters. Will respond a config holding the requested
+// values if any of the requests succeeds. Otherwise will respond an error.
 void WebcamPrivateGetFunction::OnGetWebcamParameters(InquiryType type,
                                                      bool success,
                                                      int value,
                                                      int min_value,
                                                      int max_value) {
-  if (!success_)
-    return;
-  success_ = success_ && success;
+  success_ = success_ || success;
 
-  if (!success_) {
-    Respond(Error(kGetWebcamPTZError));
-  } else {
-    switch (type) {
-      case INQUIRY_PAN:
+  switch (type) {
+    case INQUIRY_PAN:
+      if (success) {
         min_pan_ = min_value;
         max_pan_ = max_value;
         pan_ = value;
-        get_pan_ = true;
-        break;
-      case INQUIRY_TILT:
+      }
+      got_pan_ = true;
+      break;
+    case INQUIRY_TILT:
+      if (success) {
         min_tilt_ = min_value;
         max_tilt_ = max_value;
         tilt_ = value;
-        get_tilt_ = true;
-        break;
-      case INQUIRY_ZOOM:
+      }
+      got_tilt_ = true;
+      break;
+    case INQUIRY_ZOOM:
+      if (success) {
         min_zoom_ = min_value;
         max_zoom_ = max_value;
         zoom_ = value;
-        get_zoom_ = true;
-        break;
-      case INQUIRY_FOCUS:
+      }
+      got_zoom_ = true;
+      break;
+    case INQUIRY_FOCUS:
+      if (success) {
         min_focus_ = min_value;
         max_focus_ = max_value;
         focus_ = value;
-        get_focus_ = true;
-        break;
+      }
+      got_focus_ = true;
+      break;
+  }
+  if (got_pan_ && got_tilt_ && got_zoom_ && got_focus_) {
+    if (!success_) {
+      Respond(Error(kGetWebcamPTZError));
+      return;
     }
-    if (get_pan_ && get_tilt_ && get_zoom_ && get_focus_) {
-      webcam_private::WebcamCurrentConfiguration result;
-      if (min_pan_ != max_pan_) {
-        result.pan_range = std::make_unique<webcam_private::Range>();
-        result.pan_range->min = min_pan_;
-        result.pan_range->max = max_pan_;
-      }
-      if (min_tilt_ != max_tilt_) {
-        result.tilt_range = std::make_unique<webcam_private::Range>();
-        result.tilt_range->min = min_tilt_;
-        result.tilt_range->max = max_tilt_;
-      }
-      if (min_zoom_ != max_zoom_) {
-        result.zoom_range = std::make_unique<webcam_private::Range>();
-        result.zoom_range->min = min_zoom_;
-        result.zoom_range->max = max_zoom_;
-      }
-      if (min_focus_ != max_focus_) {
-        result.focus_range = std::make_unique<webcam_private::Range>();
-        result.focus_range->min = min_focus_;
-        result.focus_range->max = max_focus_;
-      }
 
-      result.pan = pan_;
-      result.tilt = tilt_;
-      result.zoom = zoom_;
-      result.focus = focus_;
-      Respond(OneArgument(result.ToValue()));
+    webcam_private::WebcamCurrentConfiguration result;
+    if (min_pan_ != max_pan_) {
+      result.pan_range = std::make_unique<webcam_private::Range>();
+      result.pan_range->min = min_pan_;
+      result.pan_range->max = max_pan_;
     }
+    if (min_tilt_ != max_tilt_) {
+      result.tilt_range = std::make_unique<webcam_private::Range>();
+      result.tilt_range->min = min_tilt_;
+      result.tilt_range->max = max_tilt_;
+    }
+    if (min_zoom_ != max_zoom_) {
+      result.zoom_range = std::make_unique<webcam_private::Range>();
+      result.zoom_range->min = min_zoom_;
+      result.zoom_range->max = max_zoom_;
+    }
+    if (min_focus_ != max_focus_) {
+      result.focus_range = std::make_unique<webcam_private::Range>();
+      result.focus_range->min = min_focus_;
+      result.focus_range->max = max_focus_;
+    }
+
+    result.pan = pan_;
+    result.tilt = tilt_;
+    result.zoom = zoom_;
+    result.focus = focus_;
+    Respond(OneArgument(result.ToValue()));
   }
 }
 
diff --git a/extensions/browser/app_window/app_window_contents.cc b/extensions/browser/app_window/app_window_contents.cc
index 85525d5..3928051c 100644
--- a/extensions/browser/app_window/app_window_contents.cc
+++ b/extensions/browser/app_window/app_window_contents.cc
@@ -39,7 +39,7 @@
   Observe(web_contents_.get());
   web_contents_->GetMutableRendererPrefs()->
       browser_handles_all_top_level_requests = true;
-  web_contents_->GetRenderViewHost()->SyncRendererPrefs();
+  web_contents_->SyncRendererPrefs();
 }
 
 void AppWindowContentsImpl::LoadContents(int32_t creator_process_id) {
diff --git a/extensions/browser/computed_hashes.cc b/extensions/browser/computed_hashes.cc
index 1533306b..4b0b222 100644
--- a/extensions/browser/computed_hashes.cc
+++ b/extensions/browser/computed_hashes.cc
@@ -15,8 +15,10 @@
 #include "base/stl_util.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
+#include "extensions/browser/content_verifier/content_verifier_utils.h"
 #include "extensions/browser/content_verifier/scoped_uma_recorder.h"
 
 namespace extensions {
@@ -121,24 +123,43 @@
                                        int* block_size,
                                        std::vector<std::string>* hashes) const {
   base::FilePath path = relative_path.NormalizePathSeparatorsTo('/');
-  auto i = data_.find(path);
-  if (i == data_.end()) {
-    // If we didn't find the entry using exact match, it's possible the
-    // developer is using a path with some letters in the incorrect case, which
-    // happens to work on windows/osx. So try doing a linear scan to look for a
-    // case-insensitive match. In practice most extensions don't have that big
-    // a list of files so the performance penalty is probably not too big
-    // here. Also for crbug.com/29941 we plan to start warning developers when
-    // they are making this mistake, since their extension will be broken on
-    // linux/chromeos.
-    for (i = data_.begin(); i != data_.end(); ++i) {
-      const base::FilePath& entry = i->first;
-      if (base::FilePath::CompareEqualIgnoreCase(entry.value(), path.value()))
-        break;
+  auto find_data = [&](const base::FilePath& normalized_path) {
+    auto i = data_.find(normalized_path);
+    if (i == data_.end()) {
+      // If we didn't find the entry using exact match, it's possible the
+      // developer is using a path with some letters in the incorrect case,
+      // which happens to work on windows/osx. So try doing a linear scan to
+      // look for a case-insensitive match. In practice most extensions don't
+      // have that big a list of files so the performance penalty is probably
+      // not too big here. Also for crbug.com/29941 we plan to start warning
+      // developers when they are making this mistake, since their extension
+      // will be broken on linux/chromeos.
+      for (i = data_.begin(); i != data_.end(); ++i) {
+        const base::FilePath& entry = i->first;
+        if (base::FilePath::CompareEqualIgnoreCase(entry.value(),
+                                                   normalized_path.value())) {
+          break;
+        }
+      }
     }
-    if (i == data_.end())
-      return false;
+    return i;
+  };
+  auto i = find_data(path);
+#if defined(OS_WIN)
+  if (i == data_.end()) {
+    base::FilePath::StringType trimmed_path_value;
+    // Also search for path with (.| )+ suffix trimmed as they are ignored in
+    // windows. This matches the canonicalization behavior of
+    // VerifiedContents::Create.
+    if (content_verifier_utils::TrimDotSpaceSuffix(path.value(),
+                                                   &trimmed_path_value)) {
+      i = find_data(base::FilePath(trimmed_path_value));
+    }
   }
+#endif  // defined(OS_WIN)
+  if (i == data_.end())
+    return false;
+
   const HashInfo& info = i->second;
   *block_size = info.first;
   *hashes = info.second;
diff --git a/extensions/browser/computed_hashes_unittest.cc b/extensions/browser/computed_hashes_unittest.cc
index 60e1506..c9e704a 100644
--- a/extensions/browser/computed_hashes_unittest.cc
+++ b/extensions/browser/computed_hashes_unittest.cc
@@ -2,15 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "extensions/browser/computed_hashes.h"
 #include "base/base64.h"
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
 #include "crypto/sha2.h"
-#include "extensions/browser/computed_hashes.h"
+#include "extensions/common/constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
+// Whether or not dot and space suffixes of filename are ignored in the
+// current OS.
+const bool kDotSpaceSuffixIgnored =
+#if defined(OS_WIN)
+    true;
+#else
+    false;
+#endif  // defined(OS_WIN)
+
 // Helper to return base64 encode result by value.
 std::string Base64Encode(const std::string& data) {
   std::string result;
@@ -18,39 +30,59 @@
   return result;
 }
 
+struct HashInfo {
+  base::FilePath path;
+  int block_size;
+  std::vector<std::string> hashes;
+};
+
+testing::AssertionResult WriteThenReadComputedHashes(
+    const std::vector<HashInfo>& hash_infos,
+    extensions::ComputedHashes::Reader* reader) {
+  base::ScopedTempDir scoped_dir;
+  if (!scoped_dir.CreateUniqueTempDir())
+    return testing::AssertionFailure() << "Failed to create temp dir.";
+
+  base::FilePath computed_hashes_path =
+      scoped_dir.GetPath().AppendASCII("computed_hashes.json");
+  extensions::ComputedHashes::Writer writer;
+  for (const auto& info : hash_infos)
+    writer.AddHashes(info.path, info.block_size, info.hashes);
+
+  if (!writer.WriteToFile(computed_hashes_path)) {
+    return testing::AssertionFailure()
+           << "Failed to write computed_hashes.json";
+  }
+  if (!reader->InitFromFile(computed_hashes_path))
+    return testing::AssertionFailure() << "Failed to read computed_hashes.json";
+
+  return testing::AssertionSuccess();
+}
+
 }  // namespace
 
 namespace extensions {
 
-TEST(ComputedHashes, ComputedHashes) {
-  base::ScopedTempDir scoped_dir;
-  ASSERT_TRUE(scoped_dir.CreateUniqueTempDir());
-  base::FilePath computed_hashes =
-      scoped_dir.GetPath().AppendASCII("computed_hashes.json");
-
+TEST(ComputedHashesTest, ComputedHashes) {
   // We'll add hashes for 2 files, one of which uses a subdirectory
   // path. The first file will have a list of 1 block hash, and the
   // second file will have 2 block hashes.
   base::FilePath path1(FILE_PATH_LITERAL("foo.txt"));
   base::FilePath path2 =
       base::FilePath(FILE_PATH_LITERAL("foo")).AppendASCII("bar.txt");
-  std::vector<std::string> hashes1;
-  std::vector<std::string> hashes2;
-  hashes1.push_back(crypto::SHA256HashString("first"));
-  hashes2.push_back(crypto::SHA256HashString("second"));
-  hashes2.push_back(crypto::SHA256HashString("third"));
+  std::vector<std::string> hashes1 = {crypto::SHA256HashString("first")};
+  std::vector<std::string> hashes2 = {crypto::SHA256HashString("second"),
+                                      crypto::SHA256HashString("third")};
+  const int kBlockSize1 = 4096;
+  const int kBlockSize2 = 2048;
 
-  // Write them into the file.
-  ComputedHashes::Writer writer;
-  writer.AddHashes(path1, 4096, hashes1);
-  writer.AddHashes(path2, 2048, hashes2);
-  EXPECT_TRUE(writer.WriteToFile(computed_hashes));
-
-  // Now read them back again and assert that we got what we wrote.
   ComputedHashes::Reader reader;
+  ASSERT_TRUE(WriteThenReadComputedHashes(
+      {{path1, kBlockSize1, hashes1}, {path2, kBlockSize2, hashes2}}, &reader));
+
+  // After reading hashes back assert that we got what we wrote.
   std::vector<std::string> read_hashes1;
   std::vector<std::string> read_hashes2;
-  EXPECT_TRUE(reader.InitFromFile(computed_hashes));
 
   int block_size = 0;
   EXPECT_TRUE(reader.GetHashes(path1, &block_size, &read_hashes1));
@@ -88,7 +120,7 @@
 // $ dd if=hello.txt bs=4096 count=1 | openssl dgst -sha256 -binary | base64
 // $ dd if=hello.txt skip=1 bs=4096 count=1 |
 //   openssl dgst -sha256 -binary | base64
-TEST(ComputedHashes, ComputeHashesForContent) {
+TEST(ComputedHashesTest, ComputeHashesForContent) {
   const int block_size = 4096;
 
   // Simple short input.
@@ -122,4 +154,65 @@
             Base64Encode(hashes3[0]));
 }
 
+// Tests that dot/space path suffixes are treated correctly in
+// ComputedHashes::Reader.
+//
+// Regression test for https://crbug.com/696208.
+TEST(ComputedHashesTest, DotSpaceSuffix) {
+  const std::string hash_value = crypto::SHA256HashString("test");
+  ComputedHashes::Reader reader;
+  // Add hashes for "foo.html" to computed_hashes.json.
+  ASSERT_TRUE(WriteThenReadComputedHashes(
+      {
+          {base::FilePath(FILE_PATH_LITERAL("foo.html")),
+           extension_misc::kContentVerificationDefaultBlockSize,
+           {hash_value}},
+      },
+      &reader));
+  std::vector<std::string> read_hashes;
+
+  struct TestCase {
+    const char* path;
+    bool expect_hash;
+
+    std::string ToString() const {
+      return base::StringPrintf("path = %s, expect_hash = %d", path,
+                                expect_hash);
+    }
+  } test_cases[] = {
+      // Sanity check: existing file.
+      {"foo.html", true},
+      // Sanity check: non existent file.
+      {"notfound.html", false},
+      // Path with "." suffix, along with incorrect case for the same.
+      {"foo.html.", kDotSpaceSuffixIgnored},
+      {"fOo.html.", kDotSpaceSuffixIgnored},
+      // Path with " " suffix, along with incorrect case for the same.
+      {"foo.html ", kDotSpaceSuffixIgnored},
+      {"fOo.html ", kDotSpaceSuffixIgnored},
+      // Path with ". " suffix, along with incorrect case for the same.
+      {"foo.html. ", kDotSpaceSuffixIgnored},
+      {"fOo.html. ", kDotSpaceSuffixIgnored},
+      // Path with " ." suffix, along with incorrect case for the same.
+      {"foo.html .", kDotSpaceSuffixIgnored},
+      {"fOo.html .", kDotSpaceSuffixIgnored},
+  };
+
+  for (const auto& test_case : test_cases) {
+    SCOPED_TRACE(test_case.ToString());
+    int block_size = 0;
+    std::vector<std::string> read_hashes;
+    EXPECT_EQ(test_case.expect_hash,
+              reader.GetHashes(base::FilePath().AppendASCII(test_case.path),
+                               &block_size, &read_hashes));
+
+    if (test_case.expect_hash) {
+      EXPECT_EQ(block_size,
+                extension_misc::kContentVerificationDefaultBlockSize);
+      ASSERT_EQ(1u, read_hashes.size());
+      EXPECT_EQ(hash_value, read_hashes[0]);
+    }
+  }
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/content_hash_fetcher_unittest.cc b/extensions/browser/content_hash_fetcher_unittest.cc
index d5373e39..997341a 100644
--- a/extensions/browser/content_hash_fetcher_unittest.cc
+++ b/extensions/browser/content_hash_fetcher_unittest.cc
@@ -79,10 +79,12 @@
         url_loader_factory_ptr.PassInterface();
 
     std::unique_ptr<ContentHashResult> result =
-        ContentHashWaiter().CreateAndWaitForCallback(ContentHash::FetchKey(
-            extension_->id(), extension_->path(), extension_->version(),
-            std::move(url_loader_factory_ptr_info), fetch_url_,
-            delegate_->GetPublicKey()));
+        ContentHashWaiter().CreateAndWaitForCallback(
+            ContentHash::FetchKey(extension_->id(), extension_->path(),
+                                  extension_->version(),
+                                  std::move(url_loader_factory_ptr_info),
+                                  fetch_url_, delegate_->GetPublicKey()),
+            ContentVerifierDelegate::VerifierSourceType::SIGNED_HASHES);
 
     delegate_.reset();
 
diff --git a/extensions/browser/content_verifier.cc b/extensions/browser/content_verifier.cc
index 536a623..48af209 100644
--- a/extensions/browser/content_verifier.cc
+++ b/extensions/browser/content_verifier.cc
@@ -288,7 +288,9 @@
       const IsCancelledCallback& is_cancelled,
       ContentHash::CreatedCallback created_callback) {
     ContentHash::Create(
-        std::move(fetch_key), is_cancelled,
+        std::move(fetch_key),
+        ContentVerifierDelegate::VerifierSourceType::SIGNED_HASHES,
+        is_cancelled,
         base::BindOnce(&HashHelper::ForwardToIO, std::move(created_callback)));
   }
 
diff --git a/extensions/browser/content_verifier/content_hash.cc b/extensions/browser/content_verifier/content_hash.cc
index 1531b9a..bed503e 100644
--- a/extensions/browser/content_verifier/content_hash.cc
+++ b/extensions/browser/content_verifier/content_hash.cc
@@ -83,9 +83,14 @@
     ContentHash::FetchKey&& other) = default;
 
 // static
-void ContentHash::Create(FetchKey key,
-                         const IsCancelledCallback& is_cancelled,
-                         CreatedCallback created_callback) {
+void ContentHash::Create(
+    FetchKey key,
+    ContentVerifierDelegate::VerifierSourceType source_type,
+    const IsCancelledCallback& is_cancelled,
+    CreatedCallback created_callback) {
+  // TODO(https://crbug.com/958794): Add support for unsigned hashes.
+  DCHECK_EQ(ContentVerifierDelegate::VerifierSourceType::SIGNED_HASHES,
+            source_type);
   // Step 1/2: verified_contents.json:
   std::unique_ptr<VerifiedContents> verified_contents = GetVerifiedContents(
       key,
@@ -136,6 +141,14 @@
   return *computed_hashes_;
 }
 
+// static
+std::string ContentHash::ComputeTreeHashForContent(const std::string& contents,
+                                                   int block_size) {
+  std::vector<std::string> hashes;
+  ComputedHashes::ComputeHashesForContent(contents, block_size, &hashes);
+  return ComputeTreeHashRoot(hashes, block_size / crypto::kSHA256Length);
+}
+
 ContentHash::ContentHash(
     const ExtensionId& id,
     const base::FilePath& root,
diff --git a/extensions/browser/content_verifier/content_hash.h b/extensions/browser/content_verifier/content_hash.h
index 93818a2..111e671 100644
--- a/extensions/browser/content_verifier/content_hash.h
+++ b/extensions/browser/content_verifier/content_hash.h
@@ -12,6 +12,7 @@
 #include "base/version.h"
 #include "extensions/browser/computed_hashes.h"
 #include "extensions/browser/content_verifier/content_verifier_key.h"
+#include "extensions/browser/content_verifier_delegate.h"
 #include "extensions/browser/verified_contents.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_id.h"
@@ -103,6 +104,7 @@
       base::OnceCallback<void(scoped_refptr<ContentHash> hash,
                               bool was_cancelled)>;
   static void Create(FetchKey key,
+                     ContentVerifierDelegate::VerifierSourceType source_type,
                      const IsCancelledCallback& is_cancelled,
                      CreatedCallback created_callback);
 
@@ -140,6 +142,9 @@
            !did_attempt_creating_computed_hashes_;
   }
 
+  static std::string ComputeTreeHashForContent(const std::string& contents,
+                                               int block_size);
+
  private:
   friend class base::RefCountedThreadSafe<ContentHash>;
 
diff --git a/extensions/browser/content_verifier/content_hash_unittest.cc b/extensions/browser/content_verifier/content_hash_unittest.cc
new file mode 100644
index 0000000..be29152
--- /dev/null
+++ b/extensions/browser/content_verifier/content_hash_unittest.cc
@@ -0,0 +1,235 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/content_verifier/content_hash.h"
+
+#include "base/base64url.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/json/json_writer.h"
+#include "crypto/rsa_private_key.h"
+#include "crypto/sha2.h"
+#include "crypto/signature_creator.h"
+#include "extensions/browser/computed_hashes.h"
+#include "extensions/browser/content_hash_tree.h"
+#include "extensions/browser/content_verifier/test_utils.h"
+#include "extensions/browser/content_verifier_delegate.h"
+#include "extensions/browser/extension_file_task_runner.h"
+#include "extensions/browser/extensions_test.h"
+#include "extensions/browser/verified_contents.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/file_util.h"
+#include "extensions/common/value_builder.h"
+#include "extensions/test/test_extension_dir.h"
+
+namespace extensions {
+
+// Helper class to create directory with extension files, including signed
+// hashes for content verification.
+class TestExtensionBuilder {
+ public:
+  TestExtensionBuilder()
+      : test_content_verifier_key_(crypto::RSAPrivateKey::Create(2048)),
+        // We have to provide explicit extension id in verified_contents.json.
+        extension_id_(32, 'a') {
+    base::CreateDirectory(
+        extension_dir_.UnpackedPath().Append(kMetadataFolder));
+  }
+
+  void WriteManifest() {
+    extension_dir_.WriteManifest(DictionaryBuilder()
+                                     .Set("manifest_version", 2)
+                                     .Set("name", "Test extension")
+                                     .Set("version", "1.0")
+                                     .ToJSON());
+  }
+
+  void WriteResource(base::FilePath::StringType relative_path,
+                     std::string contents) {
+    extension_dir_.WriteFile(relative_path, contents);
+    extension_resources_.emplace_back(base::FilePath(std::move(relative_path)),
+                                      std::move(contents));
+  }
+
+  void WriteComputedHashes() {
+    int block_size = extension_misc::kContentVerificationDefaultBlockSize;
+    ComputedHashes::Writer computed_hashes_writer;
+
+    for (const auto& resource : extension_resources_) {
+      std::vector<std::string> hashes;
+      ComputedHashes::ComputeHashesForContent(resource.contents, block_size,
+                                              &hashes);
+      computed_hashes_writer.AddHashes(resource.relative_path, block_size,
+                                       hashes);
+    }
+
+    ASSERT_TRUE(computed_hashes_writer.WriteToFile(
+        file_util::GetComputedHashesPath(extension_dir_.UnpackedPath())));
+  }
+
+  void WriteVerifiedContents() {
+    std::unique_ptr<base::Value> payload = CreateVerifiedContents();
+    std::string payload_value;
+    ASSERT_TRUE(base::JSONWriter::Write(*payload, &payload_value));
+
+    std::string payload_b64;
+    base::Base64UrlEncode(
+        payload_value, base::Base64UrlEncodePolicy::OMIT_PADDING, &payload_b64);
+
+    std::string signature_sha256 = crypto::SHA256HashString("." + payload_b64);
+    std::vector<uint8_t> signature_source(signature_sha256.begin(),
+                                          signature_sha256.end());
+    std::vector<uint8_t> signature_value;
+    ASSERT_TRUE(crypto::SignatureCreator::Sign(
+        test_content_verifier_key_.get(), crypto::SignatureCreator::SHA256,
+        signature_source.data(), signature_source.size(), &signature_value));
+
+    std::string signature_b64;
+    base::Base64UrlEncode(
+        std::string(signature_value.begin(), signature_value.end()),
+        base::Base64UrlEncodePolicy::OMIT_PADDING, &signature_b64);
+
+    std::unique_ptr<base::Value> signatures =
+        ListBuilder()
+            .Append(DictionaryBuilder()
+                        .Set("header",
+                             DictionaryBuilder().Set("kid", "webstore").Build())
+                        .Set("protected", "")
+                        .Set("signature", signature_b64)
+                        .Build())
+            .Build();
+    std::unique_ptr<base::Value> verified_contents =
+        ListBuilder()
+            .Append(DictionaryBuilder()
+                        .Set("description", "treehash per file")
+                        .Set("signed_content",
+                             DictionaryBuilder()
+                                 .Set("payload", payload_b64)
+                                 .Set("signatures", std::move(signatures))
+                                 .Build())
+                        .Build())
+            .Build();
+
+    std::string json;
+    ASSERT_TRUE(base::JSONWriter::Write(*verified_contents, &json));
+
+    base::FilePath verified_contents_path =
+        file_util::GetVerifiedContentsPath(extension_dir_.UnpackedPath());
+    ASSERT_EQ(
+        static_cast<int>(json.size()),
+        base::WriteFile(verified_contents_path, json.data(), json.size()));
+  }
+
+  std::vector<uint8_t> GetTestContentVerifierPublicKey() {
+    std::vector<uint8_t> public_key;
+    test_content_verifier_key_->ExportPublicKey(&public_key);
+    return public_key;
+  }
+
+  base::FilePath extension_path() const {
+    return extension_dir_.UnpackedPath();
+  }
+  const ExtensionId& extension_id() const { return extension_id_; }
+
+ private:
+  struct ExtensionResource {
+    ExtensionResource(base::FilePath relative_path, std::string contents)
+        : relative_path(std::move(relative_path)),
+          contents(std::move(contents)) {}
+
+    base::FilePath relative_path;
+    std::string contents;
+  };
+
+  std::unique_ptr<base::Value> CreateVerifiedContents() {
+    int block_size = extension_misc::kContentVerificationDefaultBlockSize;
+
+    ListBuilder files;
+    for (const auto& resource : extension_resources_) {
+      base::FilePath::StringType path =
+          VerifiedContents::NormalizeResourcePath(resource.relative_path);
+      std::string tree_hash =
+          ContentHash::ComputeTreeHashForContent(resource.contents, block_size);
+
+      std::string tree_hash_b64;
+      base::Base64UrlEncode(
+          tree_hash, base::Base64UrlEncodePolicy::OMIT_PADDING, &tree_hash_b64);
+
+      files.Append(DictionaryBuilder()
+                       .Set("path", path)
+                       .Set("root_hash", tree_hash_b64)
+                       .Build());
+    }
+
+    return DictionaryBuilder()
+        .Set("item_id", extension_id_)
+        .Set("item_version", "1.0")
+        .Set("content_hashes",
+             ListBuilder()
+                 .Append(DictionaryBuilder()
+                             .Set("format", "treehash")
+                             .Set("block_size", block_size)
+                             .Set("hash_block_size", block_size)
+                             .Set("files", files.Build())
+                             .Build())
+                 .Build())
+        .Build();
+  }
+
+  std::unique_ptr<crypto::RSAPrivateKey> test_content_verifier_key_;
+  ExtensionId extension_id_;
+  std::vector<ExtensionResource> extension_resources_;
+
+  TestExtensionDir extension_dir_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestExtensionBuilder);
+};
+
+class ContentHashUnittest : public ExtensionsTest {
+ protected:
+  ContentHashUnittest() = default;
+
+  std::unique_ptr<ContentHashResult> CreateContentHash(
+      Extension* extension,
+      ContentVerifierDelegate::VerifierSourceType source_type,
+      const std::vector<uint8_t>& content_verifier_public_key) {
+    ContentHash::FetchKey key(
+        extension->id(), extension->path(), extension->version(),
+        nullptr /* url_loader_factory_ptr_info */, GURL() /* fetch_url */,
+        content_verifier_public_key);
+    return ContentHashWaiter().CreateAndWaitForCallback(std::move(key),
+                                                        source_type);
+  }
+
+  scoped_refptr<Extension> LoadExtension(const TestExtensionBuilder& builder) {
+    std::string error;
+    scoped_refptr<Extension> extension = file_util::LoadExtension(
+        builder.extension_path(), builder.extension_id(), Manifest::INTERNAL,
+        0 /* flags */, &error);
+    if (!extension)
+      ADD_FAILURE() << " error:'" << error << "'";
+    return extension;
+  }
+};
+
+TEST_F(ContentHashUnittest, ExtensionWithSignedHashes) {
+  TestExtensionBuilder builder;
+  builder.WriteManifest();
+  builder.WriteResource(FILE_PATH_LITERAL("background.js"),
+                        "console.log('Nothing special');");
+  builder.WriteVerifiedContents();
+
+  scoped_refptr<Extension> extension = LoadExtension(builder);
+  ASSERT_NE(nullptr, extension);
+
+  std::unique_ptr<ContentHashResult> result = CreateContentHash(
+      extension.get(),
+      ContentVerifierDelegate::VerifierSourceType::SIGNED_HASHES,
+      builder.GetTestContentVerifierPublicKey());
+  DCHECK(result);
+
+  EXPECT_TRUE(result->success);
+}
+
+}  // namespace extensions
diff --git a/extensions/browser/content_verifier/content_verifier_utils.cc b/extensions/browser/content_verifier/content_verifier_utils.cc
new file mode 100644
index 0000000..a9c3797
--- /dev/null
+++ b/extensions/browser/content_verifier/content_verifier_utils.cc
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/content_verifier/content_verifier_utils.h"
+
+namespace extensions {
+namespace content_verifier_utils {
+
+#if defined(OS_WIN)
+bool TrimDotSpaceSuffix(const base::FilePath::StringType& path,
+                        base::FilePath::StringType* out_path) {
+  base::FilePath::StringType::size_type trim_pos =
+      path.find_last_not_of(FILE_PATH_LITERAL(". "));
+  if (trim_pos == base::FilePath::StringType::npos)
+    return false;
+
+  *out_path = path.substr(0, trim_pos + 1);
+  return true;
+}
+#endif  // defined(OS_WIN)
+
+}  // namespace content_verifier_utils
+}  // namespace extensions
diff --git a/extensions/browser/content_verifier/content_verifier_utils.h b/extensions/browser/content_verifier/content_verifier_utils.h
new file mode 100644
index 0000000..bcfaebf
--- /dev/null
+++ b/extensions/browser/content_verifier/content_verifier_utils.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef EXTENSIONS_BROWSER_CONTENT_VERIFIER_CONTENT_VERIFIER_UTILS_H_
+#define EXTENSIONS_BROWSER_CONTENT_VERIFIER_CONTENT_VERIFIER_UTILS_H_
+
+#include "base/files/file_path.h"
+#include "build/build_config.h"
+
+namespace extensions {
+namespace content_verifier_utils {
+
+#if defined(OS_WIN)
+// Returns true if |path| ends with (.| )+.
+// |out_path| will contain "." and/or " " suffix removed from |path|.
+bool TrimDotSpaceSuffix(const base::FilePath::StringType& path,
+                        base::FilePath::StringType* out_path);
+#endif  // defined(OS_WIN)
+
+}  // namespace content_verifier_utils
+}  // namespace extensions
+
+#endif  // EXTENSIONS_BROWSER_CONTENT_VERIFIER_CONTENT_VERIFIER_UTILS_H_
diff --git a/extensions/browser/content_verifier/test_utils.cc b/extensions/browser/content_verifier/test_utils.cc
index 7ee75a4..da2c492 100644
--- a/extensions/browser/content_verifier/test_utils.cc
+++ b/extensions/browser/content_verifier/test_utils.cc
@@ -221,10 +221,12 @@
 ContentHashWaiter::~ContentHashWaiter() = default;
 
 std::unique_ptr<ContentHashResult> ContentHashWaiter::CreateAndWaitForCallback(
-    ContentHash::FetchKey key) {
+    ContentHash::FetchKey key,
+    ContentVerifierDelegate::VerifierSourceType source_type) {
   GetExtensionFileTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&ContentHashWaiter::CreateContentHash,
-                                base::Unretained(this), std::move(key)));
+      FROM_HERE,
+      base::BindOnce(&ContentHashWaiter::CreateContentHash,
+                     base::Unretained(this), std::move(key), source_type));
   run_loop_.Run();
   DCHECK(result_);
   return std::move(result_);
@@ -248,8 +250,11 @@
   run_loop_.QuitWhenIdle();
 }
 
-void ContentHashWaiter::CreateContentHash(ContentHash::FetchKey key) {
-  ContentHash::Create(std::move(key), ContentHash::IsCancelledCallback(),
+void ContentHashWaiter::CreateContentHash(
+    ContentHash::FetchKey key,
+    ContentVerifierDelegate::VerifierSourceType source_type) {
+  ContentHash::Create(std::move(key), source_type,
+                      ContentHash::IsCancelledCallback(),
                       base::BindOnce(&ContentHashWaiter::CreatedCallback,
                                      base::Unretained(this)));
 }
diff --git a/extensions/browser/content_verifier/test_utils.h b/extensions/browser/content_verifier/test_utils.h
index 179aeb75..1b2bda0 100644
--- a/extensions/browser/content_verifier/test_utils.h
+++ b/extensions/browser/content_verifier/test_utils.h
@@ -176,13 +176,16 @@
   ~ContentHashWaiter();
 
   std::unique_ptr<ContentHashResult> CreateAndWaitForCallback(
-      ContentHash::FetchKey key);
+      ContentHash::FetchKey key,
+      ContentVerifierDelegate::VerifierSourceType source_type);
 
  private:
   void CreatedCallback(scoped_refptr<ContentHash> content_hash,
                        bool was_cancelled);
 
-  void CreateContentHash(ContentHash::FetchKey key);
+  void CreateContentHash(
+      ContentHash::FetchKey key,
+      ContentVerifierDelegate::VerifierSourceType source_type);
 
   scoped_refptr<base::SequencedTaskRunner> reply_task_runner_;
   base::RunLoop run_loop_;
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 214ed12..f091dca 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -1935,6 +1935,8 @@
 #if !defined(OS_MACOSX)
   registry->RegisterBooleanPref(pref_names::kAppFullscreenAllowed, true);
 #endif
+
+  registry->RegisterBooleanPref(pref_names::kBlockExternalExtensions, false);
 }
 
 template <class ExtensionIdContainer>
diff --git a/extensions/browser/pref_names.cc b/extensions/browser/pref_names.cc
index 4f139e55..fca6c08 100644
--- a/extensions/browser/pref_names.cc
+++ b/extensions/browser/pref_names.cc
@@ -31,6 +31,7 @@
 const char kAllowedInstallSites[] = "extensions.allowed_install_sites";
 const char kAllowedTypes[] = "extensions.allowed_types";
 const char kAppFullscreenAllowed[] = "apps.fullscreen.allowed";
+const char kBlockExternalExtensions[] = "extensions.block_external_extensions";
 const char kExtensions[] = "extensions.settings";
 const char kExtensionManagement[] = "extensions.management";
 const char kInstallAllowList[] = "extensions.install.allowlist";
diff --git a/extensions/browser/pref_names.h b/extensions/browser/pref_names.h
index 9aeeed5..fdf0436b 100644
--- a/extensions/browser/pref_names.h
+++ b/extensions/browser/pref_names.h
@@ -39,6 +39,9 @@
 // A boolean that tracks whether apps are allowed to enter fullscreen mode.
 extern const char kAppFullscreenAllowed[];
 
+// A boolean indicating if external extensions are blocked from installing.
+extern const char kBlockExternalExtensions[];
+
 // Dictionary pref that keeps track of per-extension settings. The keys are
 // extension ids.
 extern const char kExtensions[];
diff --git a/extensions/browser/verified_contents.cc b/extensions/browser/verified_contents.cc
index 942275f..662a6cb 100644
--- a/extensions/browser/verified_contents.cc
+++ b/extensions/browser/verified_contents.cc
@@ -17,6 +17,7 @@
 #include "build/build_config.h"
 #include "components/crx_file/id_util.h"
 #include "crypto/signature_verifier.h"
+#include "extensions/browser/content_verifier/content_verifier_utils.h"
 #include "extensions/browser/content_verifier/scoped_uma_recorder.h"
 #include "extensions/common/extension.h"
 
@@ -64,21 +65,6 @@
 const char kUMAVerifiedContentsInitTime[] =
     "Extensions.ContentVerification.VerifiedContentsInitTime";
 
-#if defined(OS_WIN)
-// Returns true if |path| ends with (.| )+.
-// |out_path| will contain "." and/or " " suffix removed from |path|.
-bool TrimDotSpaceSuffix(const base::FilePath::StringType& path,
-                        base::FilePath::StringType* out_path) {
-  base::FilePath::StringType::size_type trim_pos =
-      path.find_last_not_of(FILE_PATH_LITERAL(". "));
-  if (trim_pos == base::FilePath::StringType::npos)
-    return false;
-
-  *out_path = path.substr(0, trim_pos + 1);
-  return true;
-}
-#endif  // defined(OS_WIN)
-
 }  // namespace
 
 namespace extensions {
@@ -197,9 +183,11 @@
       // that any filename with (.| )+ suffix can be matched later, see
       // HasTreeHashRoot() and TreeHashRootEquals().
       base::FilePath::StringType trimmed_path;
-      if (TrimDotSpaceSuffix(lowercase_file_path, &trimmed_path))
+      if (content_verifier_utils::TrimDotSpaceSuffix(lowercase_file_path,
+                                                     &trimmed_path)) {
         verified_contents->root_hashes_.insert(
             std::make_pair(trimmed_path, i->second));
+      }
 #endif  // defined(OS_WIN)
     }
 
@@ -211,14 +199,13 @@
 
 bool VerifiedContents::HasTreeHashRoot(
     const base::FilePath& relative_path) const {
-  base::FilePath::StringType path = base::ToLowerASCII(
-      relative_path.NormalizePathSeparatorsTo('/').value());
+  base::FilePath::StringType path = NormalizeResourcePath(relative_path);
   if (base::Contains(root_hashes_, path))
     return true;
 
 #if defined(OS_WIN)
   base::FilePath::StringType trimmed_path;
-  if (TrimDotSpaceSuffix(path, &trimmed_path))
+  if (content_verifier_utils::TrimDotSpaceSuffix(path, &trimmed_path))
     return base::Contains(root_hashes_, trimmed_path);
 #endif  // defined(OS_WIN)
   return false;
@@ -227,18 +214,27 @@
 bool VerifiedContents::TreeHashRootEquals(const base::FilePath& relative_path,
                                           const std::string& expected) const {
   base::FilePath::StringType normalized_relative_path =
-      base::ToLowerASCII(relative_path.NormalizePathSeparatorsTo('/').value());
+      NormalizeResourcePath(relative_path);
   if (TreeHashRootEqualsImpl(normalized_relative_path, expected))
     return true;
 
 #if defined(OS_WIN)
   base::FilePath::StringType trimmed_relative_path;
-  if (TrimDotSpaceSuffix(normalized_relative_path, &trimmed_relative_path))
+  if (content_verifier_utils::TrimDotSpaceSuffix(normalized_relative_path,
+                                                 &trimmed_relative_path)) {
     return TreeHashRootEqualsImpl(trimmed_relative_path, expected);
+  }
 #endif  // defined(OS_WIN)
   return false;
 }
 
+// static
+base::FilePath::StringType VerifiedContents::NormalizeResourcePath(
+    const base::FilePath& relative_path) {
+  return base::ToLowerASCII(
+      relative_path.NormalizePathSeparatorsTo('/').value());
+}
+
 // We're loosely following the "JSON Web Signature" draft spec for signing
 // a JSON payload:
 //
diff --git a/extensions/browser/verified_contents.h b/extensions/browser/verified_contents.h
index 6af1e5d..a2d5652 100644
--- a/extensions/browser/verified_contents.h
+++ b/extensions/browser/verified_contents.h
@@ -49,6 +49,9 @@
   // signature" mode, this can return false.
   bool valid_signature() { return valid_signature_; }
 
+  static base::FilePath::StringType NormalizeResourcePath(
+      const base::FilePath& relative_path);
+
  private:
   // Note: the public_key must remain valid for the lifetime of this object.
   explicit VerifiedContents(base::span<const uint8_t> public_key);
diff --git a/extensions/common/api/automation.idl b/extensions/common/api/automation.idl
index 3d78900..32bea8f 100644
--- a/extensions/common/api/automation.idl
+++ b/extensions/common/api/automation.idl
@@ -579,6 +579,9 @@
     // Accessible Name Calculation</a> process.
     DOMString? name;
 
+    // The tooltip of the node, if any.
+    DOMString? tooltip;
+
     // The source of the name.
     NameFromType? nameFrom;
 
diff --git a/extensions/common/api/webcam_private.idl b/extensions/common/api/webcam_private.idl
index a1839ce..bbd40d260 100644
--- a/extensions/common/api/webcam_private.idl
+++ b/extensions/common/api/webcam_private.idl
@@ -52,13 +52,22 @@
     // Close a serial port connection to a webcam.
     static void closeWebcam(DOMString webcamId);
 
+    // Retrieve webcam parameters. Will respond with a config holding the
+    // requested values that are available, or default values for those that
+    // aren't. If none of the requests succeed, will respond with an error.
     static void get(DOMString webcamId, WebcamConfigurationCallback callback);
-    static void set(DOMString webcamId, WebcamConfiguration config);
+
+    // A callback is included here which is invoked when the function responds.
+    // No configuration is returned through it.
+    static void set(DOMString webcamId, WebcamConfiguration config,
+                    WebcamConfigurationCallback callback);
 
     // Reset a webcam. Note: the value of the parameter have no effect, it's the
     // presence of the parameter that matters. E.g.: reset(webcamId, {pan: 0,
     // tilt: 1}); will reset pan & tilt, but not zoom.
-    static void reset(DOMString webcamId, WebcamConfiguration config);
+    // A callback is included here which is invoked when the function responds.
+    // No configuration is returned through it.
+    static void reset(DOMString webcamId, WebcamConfiguration config,
+                      WebcamConfigurationCallback callback);
   };
 };
-
diff --git a/extensions/renderer/extension_frame_helper.cc b/extensions/renderer/extension_frame_helper.cc
index 901b784..1d204dc3 100644
--- a/extensions/renderer/extension_frame_helper.cc
+++ b/extensions/renderer/extension_frame_helper.cc
@@ -342,17 +342,26 @@
 void ExtensionFrameHelper::DidCreateScriptContext(
     v8::Local<v8::Context> context,
     int32_t world_id) {
-  if (world_id == kMainWorldId &&
-      render_frame()->IsBrowserSideNavigationPending()) {
-    DCHECK(!delayed_main_world_script_initialization_);
-    // Defer initializing the extensions script context now because it depends
-    // on having the URL of the provisional load which isn't available at this
-    // point.
-    delayed_main_world_script_initialization_ = true;
-  } else {
-    extension_dispatcher_->DidCreateScriptContext(render_frame()->GetWebFrame(),
-                                                  context, world_id);
+  if (world_id == kMainWorldId) {
+    if (render_frame()->IsBrowserSideNavigationPending()) {
+      // Defer initializing the extensions script context now because it depends
+      // on having the URL of the provisional load which isn't available at this
+      // point.
+      // We can come here twice in the case of window.open(url): first for
+      // about:blank empty document, then possibly for the actual url load
+      // (depends on whoever triggers window proxy init), before getting
+      // ReadyToCommitNavigation.
+      delayed_main_world_script_initialization_ = true;
+      return;
+    }
+    // Sometimes DidCreateScriptContext comes before ReadyToCommitNavigation.
+    // In this case we don't have to wait until ReadyToCommitNavigation.
+    // TODO(dgozman): ensure consistent call order between
+    // DidCreateScriptContext and ReadyToCommitNavigation.
+    delayed_main_world_script_initialization_ = false;
   }
+  extension_dispatcher_->DidCreateScriptContext(render_frame()->GetWebFrame(),
+                                                context, world_id);
 }
 
 void ExtensionFrameHelper::WillReleaseScriptContext(
diff --git a/extensions/renderer/resources/automation/automation_node.js b/extensions/renderer/resources/automation/automation_node.js
index a2310bd7..d4caace3 100644
--- a/extensions/renderer/resources/automation/automation_node.js
+++ b/extensions/renderer/resources/automation/automation_node.js
@@ -1164,6 +1164,7 @@
     'placeholder',
     'roleDescription',
     'textInputType',
+    'tooltip',
     'url',
     'value'];
 
diff --git a/extensions/test/test_extension_dir.cc b/extensions/test/test_extension_dir.cc
index cb4edb8..8e1e94f0 100644
--- a/extensions/test/test_extension_dir.cc
+++ b/extensions/test/test_extension_dir.cc
@@ -5,10 +5,7 @@
 #include "extensions/test/test_extension_dir.h"
 
 #include "base/files/file_util.h"
-#include "base/json/json_writer.h"
-#include "base/numerics/safe_conversions.h"
 #include "base/strings/string_util.h"
-#include "base/test/values_test_util.h"
 #include "base/threading/thread_restrictions.h"
 #include "extensions/browser/extension_creator.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -71,7 +68,7 @@
   return crx_path;
 }
 
-base::FilePath TestExtensionDir::UnpackedPath() {
+base::FilePath TestExtensionDir::UnpackedPath() const {
   base::ScopedAllowBlockingForTesting allow_blocking;
   // We make this absolute because it's possible that dir_ contains a symlink as
   // part of it's path. When UnpackedInstaller::GetAbsolutePath() runs as part
diff --git a/extensions/test/test_extension_dir.h b/extensions/test/test_extension_dir.h
index a65311a..d9d30d2 100644
--- a/extensions/test/test_extension_dir.h
+++ b/extensions/test/test_extension_dir.h
@@ -39,7 +39,7 @@
   base::FilePath Pack();
 
   // Returns the path to the unpacked directory.
-  base::FilePath UnpackedPath();
+  base::FilePath UnpackedPath() const;
 
  private:
   // Stores files that make up the extension.
diff --git a/google_apis/gcm/base/socket_stream_unittest.cc b/google_apis/gcm/base/socket_stream_unittest.cc
index a7806bc..a8d8b5c 100644
--- a/google_apis/gcm/base/socket_stream_unittest.cc
+++ b/google_apis/gcm/base/socket_stream_unittest.cc
@@ -70,7 +70,7 @@
   SocketInputStream* input_stream() { return socket_input_stream_.get(); }
   SocketOutputStream* output_stream() { return socket_output_stream_.get(); }
 
-  network::mojom::ProxyResolvingSocketPtr mojo_socket_ptr_;
+  mojo::Remote<network::mojom::ProxyResolvingSocket> mojo_socket_remote_;
 
   void set_socket_output_stream(std::unique_ptr<SocketOutputStream> stream) {
     socket_output_stream_ = std::move(stream);
@@ -233,7 +233,8 @@
   mojo_socket_factory_ptr_->CreateProxyResolvingSocket(
       kDestination, std::move(options),
       net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
-      mojo::MakeRequest(&mojo_socket_ptr_), mojo::NullRemote() /* observer */,
+      mojo_socket_remote_.BindNewPipeAndPassReceiver(),
+      mojo::NullRemote() /* observer */,
       base::BindLambdaForTesting(
           [&](int result, const base::Optional<net::IPEndPoint>& local_addr,
               const base::Optional<net::IPEndPoint>& peer_addr,
@@ -250,13 +251,13 @@
 }
 
 void GCMSocketStreamTest::ResetInputStream() {
-  DCHECK(mojo_socket_ptr_);
+  DCHECK(mojo_socket_remote_);
   socket_input_stream_ =
       std::make_unique<SocketInputStream>(std::move(receive_pipe_handle_));
 }
 
 void GCMSocketStreamTest::ResetOutputStream() {
-  DCHECK(mojo_socket_ptr_);
+  DCHECK(mojo_socket_remote_);
   socket_output_stream_ =
       std::make_unique<SocketOutputStream>(std::move(send_pipe_handle_));
 }
@@ -373,7 +374,7 @@
 TEST_F(GCMSocketStreamTest, ReadDisconnected) {
   BuildSocket(ReadList(1, net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING)),
               WriteList());
-  mojo_socket_ptr_.reset();
+  mojo_socket_remote_.reset();
   WaitForData(kReadDataSize);
   ASSERT_EQ(SocketInputStream::CLOSED, input_stream()->GetState());
   ASSERT_EQ(net::ERR_FAILED, input_stream()->last_error());
@@ -555,7 +556,7 @@
 TEST_F(GCMSocketStreamTest, WriteDisconnected) {
   BuildSocket(ReadList(1, net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING)),
               WriteList());
-  mojo_socket_ptr_.reset();
+  mojo_socket_remote_.reset();
   DoOutputStreamWrite(base::StringPiece(kWriteData, kWriteDataSize));
   ASSERT_EQ(SocketOutputStream::CLOSED, output_stream()->GetState());
   ASSERT_EQ(net::ERR_FAILED, output_stream()->last_error());
diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc
index b1d7289..577d7a86 100644
--- a/google_apis/gcm/engine/connection_factory_impl.cc
+++ b/google_apis/gcm/engine/connection_factory_impl.cc
@@ -360,7 +360,7 @@
   socket_factory_->CreateProxyResolvingSocket(
       current_endpoint, std::move(options),
       net::MutableNetworkTrafficAnnotationTag(traffic_annotation),
-      mojo::MakeRequest(&socket_), mojo::NullRemote() /* observer */,
+      socket_.BindNewPipeAndPassReceiver(), mojo::NullRemote() /* observer */,
       base::BindOnce(&ConnectionFactoryImpl::OnConnectDone,
                      base::Unretained(this)));
 }
diff --git a/google_apis/gcm/engine/connection_factory_impl.h b/google_apis/gcm/engine/connection_factory_impl.h
index 2dbcbc1..ef803fc 100644
--- a/google_apis/gcm/engine/connection_factory_impl.h
+++ b/google_apis/gcm/engine/connection_factory_impl.h
@@ -16,6 +16,7 @@
 #include "google_apis/gcm/engine/connection_event_tracker.h"
 #include "google_apis/gcm/engine/connection_handler.h"
 #include "google_apis/gcm/protocol/mcs.pb.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/base/backoff_entry.h"
 #include "net/log/net_log_with_source.h"
@@ -138,7 +139,7 @@
   GetProxyResolvingFactoryCallback get_socket_factory_callback_;
   network::mojom::ProxyResolvingSocketFactoryPtr socket_factory_;
   // The handle to the socket for the current connection, if one exists.
-  network::mojom::ProxyResolvingSocketPtr socket_;
+  mojo::Remote<network::mojom::ProxyResolvingSocket> socket_;
   // Peer address of |socket_|.
   net::IPEndPoint peer_addr_;
 
diff --git a/google_apis/gcm/engine/connection_factory_impl_unittest.cc b/google_apis/gcm/engine/connection_factory_impl_unittest.cc
index 839f96b4..dfd9032 100644
--- a/google_apis/gcm/engine/connection_factory_impl_unittest.cc
+++ b/google_apis/gcm/engine/connection_factory_impl_unittest.cc
@@ -18,7 +18,6 @@
 #include "google_apis/gcm/base/mcs_util.h"
 #include "google_apis/gcm/engine/fake_connection_handler.h"
 #include "google_apis/gcm/monitoring/fake_gcm_stats_recorder.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/backoff_entry.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request_test_util.h"
diff --git a/google_apis/gcm/engine/connection_handler_impl_unittest.cc b/google_apis/gcm/engine/connection_handler_impl_unittest.cc
index ccba2c7..24bd193 100644
--- a/google_apis/gcm/engine/connection_handler_impl_unittest.cc
+++ b/google_apis/gcm/engine/connection_handler_impl_unittest.cc
@@ -174,7 +174,7 @@
   // Runs the message loop until a message is received.
   void WaitForMessage();
 
-  network::mojom::ProxyResolvingSocketPtr mojo_socket_ptr_;
+  mojo::Remote<network::mojom::ProxyResolvingSocket> mojo_socket_remote_;
 
  private:
   void ReadContinuation(ScopedMessage* dst_proto, ScopedMessage new_proto);
@@ -246,10 +246,12 @@
   network::mojom::ProxyResolvingSocketOptionsPtr options =
       network::mojom::ProxyResolvingSocketOptions::New();
   options->use_tls = true;
+  mojo_socket_remote_.reset();
   mojo_socket_factory_ptr_->CreateProxyResolvingSocket(
       kDestination, std::move(options),
       net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
-      mojo::MakeRequest(&mojo_socket_ptr_), mojo::NullRemote() /* observer */,
+      mojo_socket_remote_.BindNewPipeAndPassReceiver(),
+      mojo::NullRemote() /* observer */,
       base::BindLambdaForTesting(
           [&](int result, const base::Optional<net::IPEndPoint>& local_addr,
               const base::Optional<net::IPEndPoint>& peer_addr,
@@ -755,7 +757,7 @@
   WaitForMessage();  // The login send.
   WaitForMessage();  // The login response.
   EXPECT_TRUE(connection_handler()->CanSendMessage());
-  mojo_socket_ptr_.reset();
+  mojo_socket_remote_.reset();
   mcs_proto::DataMessageStanza data_message;
   data_message.set_from(kDataMsgFrom);
   data_message.set_category(kDataMsgCategory);
diff --git a/gpu/command_buffer/client/webgpu_interface.h b/gpu/command_buffer/client/webgpu_interface.h
index 70b9da65..6255fa7 100644
--- a/gpu/command_buffer/client/webgpu_interface.h
+++ b/gpu/command_buffer/client/webgpu_interface.h
@@ -6,6 +6,7 @@
 #define GPU_COMMAND_BUFFER_CLIENT_WEBGPU_INTERFACE_H_
 
 #include <dawn/dawn.h>
+#include <dawn/dawn_proc_table.h>
 
 #include "gpu/command_buffer/client/interface_base.h"
 #include "gpu/command_buffer/common/webgpu_cmd_enums.h"
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
index b2f6929..82920a6c 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -26,7 +26,7 @@
     },
     {
         0x00000001,
-        "GL_SYNC_FLUSH_COMMANDS_BIT_APPLE",
+        "GL_SUBGROUP_FEATURE_BASIC_BIT_KHR",
     },
     {
         0x00000002,
@@ -34,7 +34,7 @@
     },
     {
         0x00000004,
-        "GL_GEOMETRY_SHADER_BIT_OES",
+        "GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR",
     },
     {
         0x00000008,
@@ -42,19 +42,19 @@
     },
     {
         0x00000010,
-        "GL_TESS_EVALUATION_SHADER_BIT_OES",
+        "GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR",
     },
     {
         0x00000020,
-        "GL_COLOR_BUFFER_BIT5_QCOM",
+        "GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR",
     },
     {
         0x00000040,
-        "GL_COLOR_BUFFER_BIT6_QCOM",
+        "GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR",
     },
     {
         0x00000080,
-        "GL_COLOR_BUFFER_BIT7_QCOM",
+        "GL_SUBGROUP_FEATURE_QUAD_BIT_KHR",
     },
     {
         0x00000100,
@@ -2097,6 +2097,10 @@
         "GL_VERTEX_ATTRIB_ARRAY_POINTER",
     },
     {
+        0x864F,
+        "GL_DEPTH_CLAMP_EXT",
+    },
+    {
         0x86A1,
         "GL_TEXTURE_COMPRESSED",
     },
@@ -3977,6 +3981,70 @@
         "GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET",
     },
     {
+        0x8E60,
+        "GL_MAX_MESH_UNIFORM_BLOCKS_NV",
+    },
+    {
+        0x8E61,
+        "GL_MAX_MESH_TEXTURE_IMAGE_UNITS_NV",
+    },
+    {
+        0x8E62,
+        "GL_MAX_MESH_IMAGE_UNIFORMS_NV",
+    },
+    {
+        0x8E63,
+        "GL_MAX_MESH_UNIFORM_COMPONENTS_NV",
+    },
+    {
+        0x8E64,
+        "GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_NV",
+    },
+    {
+        0x8E65,
+        "GL_MAX_MESH_ATOMIC_COUNTERS_NV",
+    },
+    {
+        0x8E66,
+        "GL_MAX_MESH_SHADER_STORAGE_BLOCKS_NV",
+    },
+    {
+        0x8E67,
+        "GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_NV",
+    },
+    {
+        0x8E68,
+        "GL_MAX_TASK_UNIFORM_BLOCKS_NV",
+    },
+    {
+        0x8E69,
+        "GL_MAX_TASK_TEXTURE_IMAGE_UNITS_NV",
+    },
+    {
+        0x8E6A,
+        "GL_MAX_TASK_IMAGE_UNIFORMS_NV",
+    },
+    {
+        0x8E6B,
+        "GL_MAX_TASK_UNIFORM_COMPONENTS_NV",
+    },
+    {
+        0x8E6C,
+        "GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_NV",
+    },
+    {
+        0x8E6D,
+        "GL_MAX_TASK_ATOMIC_COUNTERS_NV",
+    },
+    {
+        0x8E6E,
+        "GL_MAX_TASK_SHADER_STORAGE_BLOCKS_NV",
+    },
+    {
+        0x8E6F,
+        "GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_NV",
+    },
+    {
         0x8E72,
         "GL_PATCH_VERTICES_OES",
     },
@@ -4261,6 +4329,10 @@
         "GL_PERFMON_GLOBAL_MODE_QCOM",
     },
     {
+        0x8FA1,
+        "GL_MAX_SHADER_SUBSAMPLED_IMAGE_UNITS_QCOM",
+    },
+    {
         0x8FB0,
         "GL_BINNING_CONTROL_HINT_QCOM",
     },
@@ -5637,6 +5709,10 @@
         "GL_FRAGMENT_COVERAGE_COLOR_NV",
     },
     {
+        0x92DF,
+        "GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_NV",
+    },
+    {
         0x92E0,
         "GL_DEBUG_OUTPUT_KHR",
     },
@@ -6037,6 +6113,10 @@
         "GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV",
     },
     {
+        0x937F,
+        "GL_REPRESENTATIVE_FRAGMENT_TEST_NV",
+    },
+    {
         0x9380,
         "GL_NUM_SAMPLE_COUNTS",
     },
@@ -6333,6 +6413,66 @@
         "GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT",
     },
     {
+        0x9532,
+        "GL_SUBGROUP_SIZE_KHR",
+    },
+    {
+        0x9533,
+        "GL_SUBGROUP_SUPPORTED_STAGES_KHR",
+    },
+    {
+        0x9534,
+        "GL_SUBGROUP_SUPPORTED_FEATURES_KHR",
+    },
+    {
+        0x9535,
+        "GL_SUBGROUP_QUAD_ALL_STAGES_KHR",
+    },
+    {
+        0x9536,
+        "GL_MAX_MESH_TOTAL_MEMORY_SIZE_NV",
+    },
+    {
+        0x9537,
+        "GL_MAX_TASK_TOTAL_MEMORY_SIZE_NV",
+    },
+    {
+        0x9538,
+        "GL_MAX_MESH_OUTPUT_VERTICES_NV",
+    },
+    {
+        0x9539,
+        "GL_MAX_MESH_OUTPUT_PRIMITIVES_NV",
+    },
+    {
+        0x953A,
+        "GL_MAX_TASK_OUTPUT_COUNT_NV",
+    },
+    {
+        0x953B,
+        "GL_MAX_MESH_WORK_GROUP_SIZE_NV",
+    },
+    {
+        0x953C,
+        "GL_MAX_TASK_WORK_GROUP_SIZE_NV",
+    },
+    {
+        0x953D,
+        "GL_MAX_DRAW_MESH_TASKS_COUNT_NV",
+    },
+    {
+        0x953E,
+        "GL_MESH_WORK_GROUP_SIZE_NV",
+    },
+    {
+        0x953F,
+        "GL_TASK_WORK_GROUP_SIZE_NV",
+    },
+    {
+        0x9543,
+        "GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_NV",
+    },
+    {
         0x954D,
         "GL_CONSERVATIVE_RASTER_MODE_NV",
     },
@@ -6349,6 +6489,126 @@
         "GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV",
     },
     {
+        0x9555,
+        "GL_SCISSOR_TEST_EXCLUSIVE_NV",
+    },
+    {
+        0x9556,
+        "GL_SCISSOR_BOX_EXCLUSIVE_NV",
+    },
+    {
+        0x9557,
+        "GL_MAX_MESH_VIEWS_NV",
+    },
+    {
+        0x9559,
+        "GL_MESH_SHADER_NV",
+    },
+    {
+        0x955A,
+        "GL_TASK_SHADER_NV",
+    },
+    {
+        0x955B,
+        "GL_SHADING_RATE_IMAGE_BINDING_NV",
+    },
+    {
+        0x955C,
+        "GL_SHADING_RATE_IMAGE_TEXEL_WIDTH_NV",
+    },
+    {
+        0x955D,
+        "GL_SHADING_RATE_IMAGE_TEXEL_HEIGHT_NV",
+    },
+    {
+        0x955E,
+        "GL_SHADING_RATE_IMAGE_PALETTE_SIZE_NV",
+    },
+    {
+        0x955F,
+        "GL_MAX_COARSE_FRAGMENT_SAMPLES_NV",
+    },
+    {
+        0x9563,
+        "GL_SHADING_RATE_IMAGE_NV",
+    },
+    {
+        0x9564,
+        "GL_SHADING_RATE_NO_INVOCATIONS_NV",
+    },
+    {
+        0x9565,
+        "GL_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV",
+    },
+    {
+        0x9566,
+        "GL_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV",
+    },
+    {
+        0x9567,
+        "GL_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV",
+    },
+    {
+        0x9568,
+        "GL_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV",
+    },
+    {
+        0x9569,
+        "GL_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV",
+    },
+    {
+        0x956A,
+        "GL_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV",
+    },
+    {
+        0x956B,
+        "GL_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV",
+    },
+    {
+        0x956C,
+        "GL_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV",
+    },
+    {
+        0x956D,
+        "GL_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV",
+    },
+    {
+        0x956E,
+        "GL_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV",
+    },
+    {
+        0x956F,
+        "GL_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV",
+    },
+    {
+        0x9579,
+        "GL_MESH_VERTICES_OUT_NV",
+    },
+    {
+        0x957A,
+        "GL_MESH_PRIMITIVES_OUT_NV",
+    },
+    {
+        0x957B,
+        "GL_MESH_OUTPUT_TYPE_NV",
+    },
+    {
+        0x957C,
+        "GL_MESH_SUBROUTINE_NV",
+    },
+    {
+        0x957D,
+        "GL_TASK_SUBROUTINE_NV",
+    },
+    {
+        0x957E,
+        "GL_MESH_SUBROUTINE_UNIFORM_NV",
+    },
+    {
+        0x957F,
+        "GL_TASK_SUBROUTINE_UNIFORM_NV",
+    },
+    {
         0x9580,
         "GL_TEXTURE_TILING_EXT",
     },
@@ -6461,6 +6721,90 @@
         "GL_PROTECTED_MEMORY_OBJECT_EXT",
     },
     {
+        0x959C,
+        "GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_NV",
+    },
+    {
+        0x959D,
+        "GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_NV",
+    },
+    {
+        0x959E,
+        "GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_NV",
+    },
+    {
+        0x959F,
+        "GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_NV",
+    },
+    {
+        0x95A0,
+        "GL_REFERENCED_BY_MESH_SHADER_NV",
+    },
+    {
+        0x95A1,
+        "GL_REFERENCED_BY_TASK_SHADER_NV",
+    },
+    {
+        0x95A2,
+        "GL_MAX_MESH_WORK_GROUP_INVOCATIONS_NV",
+    },
+    {
+        0x95A3,
+        "GL_MAX_TASK_WORK_GROUP_INVOCATIONS_NV",
+    },
+    {
+        0x95A4,
+        "GL_ATTACHED_MEMORY_OBJECT_NV",
+    },
+    {
+        0x95A5,
+        "GL_ATTACHED_MEMORY_OFFSET_NV",
+    },
+    {
+        0x95A6,
+        "GL_MEMORY_ATTACHABLE_ALIGNMENT_NV",
+    },
+    {
+        0x95A7,
+        "GL_MEMORY_ATTACHABLE_SIZE_NV",
+    },
+    {
+        0x95A8,
+        "GL_MEMORY_ATTACHABLE_NV",
+    },
+    {
+        0x95A9,
+        "GL_DETACHED_MEMORY_INCARNATION_NV",
+    },
+    {
+        0x95AA,
+        "GL_DETACHED_TEXTURES_NV",
+    },
+    {
+        0x95AB,
+        "GL_DETACHED_BUFFERS_NV",
+    },
+    {
+        0x95AC,
+        "GL_MAX_DETACHED_TEXTURES_NV",
+    },
+    {
+        0x95AD,
+        "GL_MAX_DETACHED_BUFFERS_NV",
+    },
+    {
+        0x95AE,
+        "GL_SHADING_RATE_SAMPLE_ORDER_DEFAULT_NV",
+    },
+    {
+        0x95AF,
+        "GL_SHADING_RATE_SAMPLE_ORDER_PIXEL_MAJOR_NV",
+    },
+    {
+        0x95B0,
+        "GL_SHADING_RATE_SAMPLE_ORDER_SAMPLE_MAJOR_NV",
+    },
+    {
         0x9630,
         "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR",
     },
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index ac90db3..c62afb23 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -309,8 +309,7 @@
     "//gpu/command_buffer/client",
     "//gpu/command_buffer/common:gles2_utils",
     "//gpu/config",
-    "//gpu/ipc/common:surface_handle_type",
-    "//gpu/ipc/common:vulkan_ycbcr_info",
+    "//gpu/ipc/common",
     "//gpu/vulkan:buildflags",
     "//third_party/angle:angle_image_util",
     "//third_party/angle:commit_id",
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 7e40cce9..e76e674 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -8,6 +8,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/ranges.h"
 #include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
 #include "gpu/command_buffer/common/discardable_handle.h"
 #include "gpu/command_buffer/service/decoder_client.h"
 #include "gpu/command_buffer/service/gl_stream_texture_image.h"
@@ -708,7 +709,18 @@
 }
 
 error::Error GLES2DecoderPassthroughImpl::DoCompileShader(GLuint shader) {
+#if defined(OS_MACOSX)
+  // On mac we need this extension to support IOSurface backbuffers, but we
+  // don't want it exposed to WebGL user shaders. Temporarily disable it during
+  // shader compilation.
+  if (feature_info_->IsWebGLContext())
+    api()->glDisableExtensionANGLEFn("GL_ANGLE_texture_rectangle");
+#endif
   api()->glCompileShaderFn(GetShaderServiceID(shader, resources_));
+#if defined(OS_MACOSX)
+  if (feature_info_->IsWebGLContext())
+    api()->glRequestExtensionANGLEFn("GL_ANGLE_texture_rectangle");
+#endif
   return error::kNoError;
 }
 
diff --git a/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.cc b/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.cc
index a0c75c4..fb9aa52d 100644
--- a/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.cc
+++ b/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
+#include "gpu/ipc/common/command_buffer_id.h"
 
 // Macro to reduce code duplication when logging memory in
 // GpuCommandBufferMemoryTracker. This is needed as the UMA_HISTOGRAM_* macros
@@ -36,18 +37,18 @@
 namespace gpu {
 
 GpuCommandBufferMemoryTracker::GpuCommandBufferMemoryTracker(
-    int client_id,
+    CommandBufferId command_buffer_id,
     uint64_t client_tracing_id,
-    uint64_t context_group_tracing_id,
     ContextType context_type,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : client_id_(client_id),
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    Observer* observer)
+    : command_buffer_id_(command_buffer_id),
       client_tracing_id_(client_tracing_id),
-      context_group_tracing_id_(context_group_tracing_id),
       context_type_(context_type),
       memory_pressure_listener_(base::BindRepeating(
           &GpuCommandBufferMemoryTracker::LogMemoryStatsPressure,
-          base::Unretained(this))) {
+          base::Unretained(this))),
+      observer_(observer) {
   // Set up |memory_stats_timer_| to call LogMemoryPeriodic periodically
   // via the provided |task_runner|.
   memory_stats_timer_.SetTaskRunner(std::move(task_runner));
@@ -61,7 +62,10 @@
 }
 
 void GpuCommandBufferMemoryTracker::TrackMemoryAllocatedChange(uint64_t delta) {
+  uint64_t old_size = size_;
   size_ += delta;
+  if (observer_)
+    observer_->OnMemoryAllocatedChange(command_buffer_id_, old_size, size_);
 }
 
 uint64_t GpuCommandBufferMemoryTracker::GetSize() const {
@@ -73,11 +77,11 @@
 }
 
 int GpuCommandBufferMemoryTracker::ClientId() const {
-  return client_id_;
+  return ChannelIdFromCommandBufferId(command_buffer_id_);
 }
 
 uint64_t GpuCommandBufferMemoryTracker::ContextGroupTracingId() const {
-  return context_group_tracing_id_;
+  return command_buffer_id_.GetUnsafeValue();
 }
 
 void GpuCommandBufferMemoryTracker::LogMemoryStatsPeriodic() {
diff --git a/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h b/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h
index aa85412..bf0d0a6 100644
--- a/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h
+++ b/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h
@@ -13,6 +13,7 @@
 #include "gpu/command_buffer/common/context_creation_attribs.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/gpu_gles2_export.h"
+#include "gpu/ipc/common/command_buffer_id.h"
 
 namespace gpu {
 
@@ -21,11 +22,11 @@
 class GPU_GLES2_EXPORT GpuCommandBufferMemoryTracker : public MemoryTracker {
  public:
   GpuCommandBufferMemoryTracker(
-      int client_id,
+      CommandBufferId command_buffer_id,
       uint64_t client_tracing_id,
-      uint64_t context_group_tracing_id,
       ContextType context_type,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      Observer* observer);
   ~GpuCommandBufferMemoryTracker() override;
 
   // MemoryTracker implementation.
@@ -42,15 +43,16 @@
       base::MemoryPressureListener::MemoryPressureLevel pressure_level);
 
   uint64_t size_ = 0;
-  const int client_id_;
+  const CommandBufferId command_buffer_id_;
   const uint64_t client_tracing_id_;
-  const uint64_t context_group_tracing_id_;
 
   // Variables used in memory stat histogram logging.
   const ContextType context_type_;
   base::RepeatingTimer memory_stats_timer_;
   base::MemoryPressureListener memory_pressure_listener_;
 
+  MemoryTracker::Observer* const observer_;
+
   DISALLOW_COPY_AND_ASSIGN(GpuCommandBufferMemoryTracker);
 };
 
diff --git a/gpu/command_buffer/service/memory_tracking.h b/gpu/command_buffer/service/memory_tracking.h
index 05dcc9e..3ce58ed 100644
--- a/gpu/command_buffer/service/memory_tracking.h
+++ b/gpu/command_buffer/service/memory_tracking.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/trace_event/trace_event.h"
+#include "gpu/command_buffer/common/command_buffer_id.h"
 
 namespace gpu {
 
@@ -20,6 +21,20 @@
 // statistics to the global GpuMemoryManager.
 class MemoryTracker {
  public:
+  // Observe all changes in memory notified to this MemoryTracker.
+  class Observer {
+   public:
+    Observer() = default;
+    virtual ~Observer() = default;
+
+    virtual void OnMemoryAllocatedChange(CommandBufferId id,
+                                         uint64_t old_size,
+                                         uint64_t new_size) = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Observer);
+  };
+
   virtual ~MemoryTracker() = default;
   virtual void TrackMemoryAllocatedChange(uint64_t delta) = 0;
   virtual uint64_t GetSize() const = 0;
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc
index 7eb5a59..0d03b52 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc
@@ -29,6 +29,7 @@
 #include "ui/gl/init/gl_factory.h"
 
 #if BUILDFLAG(USE_DAWN)
+#include <dawn/dawn_proc.h>
 #include <dawn/dawncpp.h>
 #include <dawn_native/DawnNative.h>
 #endif  // BUILDFLAG(USE_DAWN)
@@ -267,7 +268,7 @@
 
   dawn::Device device = dawn::Device::Acquire(adapter_it->CreateDevice());
   DawnProcTable procs = dawn_native::GetProcs();
-  dawnSetProcs(&procs);
+  dawnProcSetProcs(&procs);
 
   // Create a backing using mailbox.
   auto mailbox = Mailbox::GenerateForSharedImage();
@@ -355,7 +356,7 @@
 
   // Shut down Dawn
   device = dawn::Device();
-  dawnSetProcs(nullptr);
+  dawnProcSetProcs(nullptr);
 
   skia_representation.reset();
   factory_ref.reset();
diff --git a/gpu/command_buffer/service/shared_image_representation.h b/gpu/command_buffer/service/shared_image_representation.h
index a0e1bfc..ab27e23 100644
--- a/gpu/command_buffer/service/shared_image_representation.h
+++ b/gpu/command_buffer/service/shared_image_representation.h
@@ -6,6 +6,7 @@
 #define GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_REPRESENTATION_H_
 
 #include <dawn/dawn.h>
+#include <dawn/dawn_proc_table.h>
 
 #include "base/callback_helpers.h"
 #include "build/build_config.h"
diff --git a/gpu/command_buffer/tests/gl_ext_window_rectangles_unittest.cc b/gpu/command_buffer/tests/gl_ext_window_rectangles_unittest.cc
index ea69662..5d4a350 100644
--- a/gpu/command_buffer/tests/gl_ext_window_rectangles_unittest.cc
+++ b/gpu/command_buffer/tests/gl_ext_window_rectangles_unittest.cc
@@ -13,7 +13,7 @@
 
 namespace gpu {
 
-// A collection of tests that exercise the GL_EXT_srgb extension.
+// A collection of tests that exercise the GL_EXT_window_rectangles extension.
 class GLEXTWindowRectanglesTest : public testing::Test {
  protected:
   void SetUp() override {
diff --git a/gpu/command_buffer/tests/webgpu_test.cc b/gpu/command_buffer/tests/webgpu_test.cc
index 22207fa..99dfb66 100644
--- a/gpu/command_buffer/tests/webgpu_test.cc
+++ b/gpu/command_buffer/tests/webgpu_test.cc
@@ -5,6 +5,7 @@
 #include "gpu/command_buffer/tests/webgpu_test.h"
 
 #include <dawn/dawn.h>
+#include <dawn/dawn_proc.h>
 
 #include "base/test/test_simple_task_runner.h"
 #include "build/build_config.h"
@@ -81,7 +82,7 @@
   webgpu()->RequestAdapter(webgpu::PowerPreference::kHighPerformance);
 
   DawnProcTable procs = webgpu()->GetProcs();
-  dawnSetProcs(&procs);
+  dawnProcSetProcs(&procs);
 }
 
 webgpu::WebGPUInterface* WebGPUTest::webgpu() const {
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index f2c43ccb..0cd6c03 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3365,6 +3365,15 @@
       "disabled_extensions": [
         "GL_EXT_shader_framebuffer_fetch"
       ]
-   }
+   },
+   {
+      "id": 315,
+      "cr_bugs": [964010],
+      "description": "Disable GL_MESA_framebuffer_flip_y for desktop GL",
+      "gl_type": "gl",
+      "disabled_extensions": [
+        "GL_MESA_framebuffer_flip_y"
+      ]
+    }
   ]
 }
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index c5038c0..dfe512e 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -489,9 +489,8 @@
         base::trace_event::MemoryDumpManager::GetInstance()
             ->GetTracingProcessId();
     memory_tracker = std::make_unique<GpuCommandBufferMemoryTracker>(
-        kInProcessCommandBufferClientId, client_tracing_id,
-        command_buffer_id_.GetUnsafeValue(), params.attribs.context_type,
-        base::ThreadTaskRunnerHandle::Get());
+        command_buffer_id_, client_tracing_id, params.attribs.context_type,
+        base::ThreadTaskRunnerHandle::Get(), /* obserer=*/nullptr);
   }
 
   auto feature_info = base::MakeRefCounted<gles2::FeatureInfo>(
diff --git a/gpu/ipc/service/command_buffer_stub.cc b/gpu/ipc/service/command_buffer_stub.cc
index 9096671a..e430c664 100644
--- a/gpu/ipc/service/command_buffer_stub.cc
+++ b/gpu/ipc/service/command_buffer_stub.cc
@@ -98,7 +98,6 @@
 
 }  // namespace
 
-
 CommandBufferStub::CommandBufferStub(
     GpuChannel* channel,
     const GPUCreateCommandBufferConfig& init_params,
@@ -657,9 +656,9 @@
     return current_factory.Run(init_params);
 
   return std::make_unique<GpuCommandBufferMemoryTracker>(
-      channel_->client_id(), channel_->client_tracing_id(),
-      command_buffer_id_.GetUnsafeValue(), init_params.attribs.context_type,
-      channel_->task_runner());
+      command_buffer_id_, channel_->client_tracing_id(),
+      init_params.attribs.context_type, channel_->task_runner(),
+      channel_->gpu_channel_manager()->peak_memory_monitor());
 }
 
 // static
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index 6293897..cda3158 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -52,6 +52,49 @@
 #endif
 }
 
+GpuChannelManager::GpuPeakMemoryMonitor::GpuPeakMemoryMonitor()
+    : weak_factory_(this) {}
+
+GpuChannelManager::GpuPeakMemoryMonitor::~GpuPeakMemoryMonitor() {}
+
+uint64_t GpuChannelManager::GpuPeakMemoryMonitor::GetPeakMemoryUsage(
+    uint32_t sequence_num) {
+  auto sequence = sequence_trackers_.find(sequence_num);
+  if (sequence != sequence_trackers_.end())
+    return sequence->second;
+  return 0u;
+}
+
+void GpuChannelManager::GpuPeakMemoryMonitor::StartGpuMemoryTracking(
+    uint32_t sequence_num) {
+  sequence_trackers_.emplace(sequence_num, current_memory_);
+}
+
+void GpuChannelManager::GpuPeakMemoryMonitor::StopGpuMemoryTracking(
+    uint32_t sequence_num) {
+  sequence_trackers_.erase(sequence_num);
+}
+
+void GpuChannelManager::GpuPeakMemoryMonitor::OnMemoryAllocatedChange(
+    CommandBufferId id,
+    uint64_t old_size,
+    uint64_t new_size) {
+  current_memory_ += new_size - old_size;
+  if (old_size < new_size) {
+    // When memory has increased, iterate over the sequences to update their
+    // peak.
+    // TODO(jonross): This should be fine if we typically have 1-2 sequences.
+    // However if that grows we may end up iterating many times are memory
+    // approaches peak. If that is the case we should track a
+    // |peak_since_last_sequence_update_| on the the memory changes. Then only
+    // update the sequences with a new one is added, or the peak is requested.
+    for (auto& sequence : sequence_trackers_) {
+      if (current_memory_ > sequence.second)
+        sequence.second = current_memory_;
+    }
+  }
+}
+
 GpuChannelManager::GpuChannelManager(
     const GpuPreferences& gpu_preferences,
     GpuChannelManagerDelegate* delegate,
@@ -250,6 +293,16 @@
   video_memory_usage_stats->bytes_allocated = total_size;
 }
 
+void GpuChannelManager::StartPeakMemoryMonitor(uint32_t sequence_num) {
+  peak_memory_monitor_.StartGpuMemoryTracking(sequence_num);
+}
+
+uint64_t GpuChannelManager::GetPeakMemoryUsage(uint32_t sequence_num) {
+  uint64_t total_memory = peak_memory_monitor_.GetPeakMemoryUsage(sequence_num);
+  peak_memory_monitor_.StopGpuMemoryTracking(sequence_num);
+  return total_memory;
+}
+
 #if defined(OS_ANDROID)
 void GpuChannelManager::DidAccessGpu() {
   last_gpu_access_time_ = base::TimeTicks::Now();
diff --git a/gpu/ipc/service/gpu_channel_manager.h b/gpu/ipc/service/gpu_channel_manager.h
index 5ea38fb..a8abfe5 100644
--- a/gpu/ipc/service/gpu_channel_manager.h
+++ b/gpu/ipc/service/gpu_channel_manager.h
@@ -12,6 +12,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/memory/memory_pressure_listener.h"
 #include "base/memory/ref_counted.h"
@@ -23,6 +24,7 @@
 #include "gpu/command_buffer/common/constants.h"
 #include "gpu/command_buffer/service/gr_cache_controller.h"
 #include "gpu/command_buffer/service/gr_shader_cache.h"
+#include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/passthrough_discardable_manager.h"
 #include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shader_translator_cache.h"
@@ -138,6 +140,10 @@
     return gpu_memory_buffer_factory_;
   }
 
+  MemoryTracker::Observer* peak_memory_monitor() {
+    return &peak_memory_monitor_;
+  }
+
 #if defined(OS_ANDROID)
   void DidAccessGpu();
   void OnBackgroundCleanup();
@@ -157,6 +163,13 @@
   void GetVideoMemoryUsageStats(
       VideoMemoryUsageStats* video_memory_usage_stats) const;
 
+  // Starts tracking the peak memory across all MemoryTrackers for
+  // |sequence_num|. Repeated calls with the same value are ignored.
+  void StartPeakMemoryMonitor(uint32_t sequence_num);
+
+  // Ends the tracking for |sequence_num| and returns the peak memory usage.
+  uint64_t GetPeakMemoryUsage(uint32_t sequence_num);
+
   scoped_refptr<SharedContextState> GetSharedContextState(
       ContextResult* result);
   void ScheduleGrContextCleanup();
@@ -173,6 +186,37 @@
   void LoseAllContexts();
 
  private:
+  friend class GpuChannelManagerTest;
+
+  // Observes changes in GPU memory, and tracks the peak usage for clients. The
+  // client is responsible for providing a unique |sequence_num| for each time
+  // period in which it wishes to track memory usage.
+  class GPU_IPC_SERVICE_EXPORT GpuPeakMemoryMonitor
+      : public MemoryTracker::Observer {
+   public:
+    GpuPeakMemoryMonitor();
+    ~GpuPeakMemoryMonitor() override;
+
+    uint64_t GetPeakMemoryUsage(uint32_t sequence_num);
+    void StartGpuMemoryTracking(uint32_t sequence_num);
+    void StopGpuMemoryTracking(uint32_t sequence_num);
+
+   private:
+    // MemoryTracker::Observer:
+    void OnMemoryAllocatedChange(CommandBufferId id,
+                                 uint64_t old_size,
+                                 uint64_t new_size) override;
+
+    // Tracks all currently requested sequences mapped to the peak memory seen.
+    base::flat_map<uint32_t, uint64_t> sequence_trackers_;
+
+    // Tracks the total current memory across all MemoryTrackers.
+    uint64_t current_memory_ = 0u;
+
+    base::WeakPtrFactory<GpuPeakMemoryMonitor> weak_factory_;
+    DISALLOW_COPY_AND_ASSIGN(GpuPeakMemoryMonitor);
+  };
+
   void InternalDestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id, int client_id);
 
 #if defined(OS_ANDROID)
@@ -251,6 +295,8 @@
   // viz::GpuServiceImpl. The raster decoders will use it for rasterization.
   viz::MetalContextProvider* metal_context_provider_ = nullptr;
 
+  GpuPeakMemoryMonitor peak_memory_monitor_;
+
   // Member variables should appear before the WeakPtrFactory, to ensure
   // that any WeakPtrs to Controller are invalidated before its members
   // variable's destructors are executed, rendering them invalid.
diff --git a/gpu/ipc/service/gpu_channel_manager_unittest.cc b/gpu/ipc/service/gpu_channel_manager_unittest.cc
index 5d170b3..8e1d1a70 100644
--- a/gpu/ipc/service/gpu_channel_manager_unittest.cc
+++ b/gpu/ipc/service/gpu_channel_manager_unittest.cc
@@ -5,6 +5,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/ipc/common/command_buffer_id.h"
 #include "gpu/ipc/common/gpu_messages.h"
 #include "gpu/ipc/service/gpu_channel.h"
@@ -19,6 +20,24 @@
       : GpuChannelTestCommon(true /* use_stub_bindings */) {}
   ~GpuChannelManagerTest() override = default;
 
+  GpuChannelManager::GpuPeakMemoryMonitor* gpu_peak_memory_monitor() {
+    return &channel_manager()->peak_memory_monitor_;
+  }
+
+  uint64_t GetMonitorsPeakMemoryUsage(uint32_t sequence_num) {
+    return channel_manager()->peak_memory_monitor_.GetPeakMemoryUsage(
+        sequence_num);
+  }
+
+  // Helpers to call MemoryTracker::Observer methods of
+  // GpuChannelManager::GpuPeakMemoryMonitor.
+  void OnMemoryAllocatedChange(CommandBufferId id,
+                               uint64_t old_size,
+                               uint64_t new_size) {
+    static_cast<MemoryTracker::Observer*>(gpu_peak_memory_monitor())
+        ->OnMemoryAllocatedChange(id, old_size, new_size);
+  }
+
 #if defined(OS_ANDROID)
   void TestApplicationBackgrounded(ContextType type,
                                    bool should_destroy_channel) {
@@ -93,4 +112,81 @@
 
 #endif
 
+// Tests that peak memory usage is only reported for valid sequence numbers,
+// and that polling shuts down the monitoring.
+TEST_F(GpuChannelManagerTest, GpuPeakMemoryOnlyReportedForValidSequence) {
+  GpuChannelManager* manager = channel_manager();
+  const CommandBufferId buffer_id =
+      CommandBufferIdFromChannelAndRoute(42, 1337);
+  const uint64_t current_memory = 42;
+  OnMemoryAllocatedChange(buffer_id, 0u, current_memory);
+
+  const uint32_t sequence_num = 1;
+  manager->StartPeakMemoryMonitor(sequence_num);
+  EXPECT_EQ(current_memory, GetMonitorsPeakMemoryUsage(sequence_num));
+
+  // With no request to listen to memory it should report 0.
+  const uint32_t invalid_sequence_num = 1337;
+  EXPECT_EQ(0u, GetMonitorsPeakMemoryUsage(invalid_sequence_num));
+  EXPECT_EQ(0u, manager->GetPeakMemoryUsage(invalid_sequence_num));
+
+  // The valid sequence should receive a report.
+  EXPECT_EQ(current_memory, manager->GetPeakMemoryUsage(sequence_num));
+  // However it should be shut-down and no longer report anything.
+  EXPECT_EQ(0u, GetMonitorsPeakMemoryUsage(sequence_num));
+  EXPECT_EQ(0u, manager->GetPeakMemoryUsage(sequence_num));
+}
+
+// Tests that while a channel may exist for longer than a request to monitor,
+// that only peaks seen are reported.
+TEST_F(GpuChannelManagerTest,
+       GpuPeakMemoryOnlyReportsPeaksFromObservationTime) {
+  GpuChannelManager* manager = channel_manager();
+
+  const CommandBufferId buffer_id =
+      CommandBufferIdFromChannelAndRoute(42, 1337);
+  const uint64_t initial_memory = 42;
+  OnMemoryAllocatedChange(buffer_id, 0u, initial_memory);
+  const uint64_t reduced_memory = 2;
+  OnMemoryAllocatedChange(buffer_id, initial_memory, reduced_memory);
+
+  const uint32_t sequence_num = 1;
+  manager->StartPeakMemoryMonitor(sequence_num);
+  EXPECT_EQ(reduced_memory, GetMonitorsPeakMemoryUsage(sequence_num));
+
+  // While not the peak memory for the lifetime of |buffer_id| this should be
+  // the peak seen during the observation of |sequence_num|.
+  const uint64_t localized_peak_memory = 24;
+  OnMemoryAllocatedChange(buffer_id, reduced_memory, localized_peak_memory);
+  EXPECT_EQ(localized_peak_memory, manager->GetPeakMemoryUsage(sequence_num));
+}
+
+// Checks that when there are more than one sequence, that each has a separately
+// calulcated peak.
+TEST_F(GpuChannelManagerTest, GetPeakMemoryUsageCalculatedPerSequence) {
+  GpuChannelManager* manager = channel_manager();
+
+  const CommandBufferId buffer_id =
+      CommandBufferIdFromChannelAndRoute(42, 1337);
+  const uint64_t initial_memory = 42;
+  OnMemoryAllocatedChange(buffer_id, 0u, initial_memory);
+
+  // Start the first sequence so it is the only one to see the peak of
+  // |initial_memory|.
+  const uint32_t sequence_num_1 = 1;
+  manager->StartPeakMemoryMonitor(sequence_num_1);
+
+  // Reduce the memory before the second sequence starts.
+  const uint64_t reduced_memory = 2;
+  OnMemoryAllocatedChange(buffer_id, initial_memory, reduced_memory);
+
+  const uint32_t sequence_num_2 = 2;
+  manager->StartPeakMemoryMonitor(sequence_num_2);
+  const uint64_t localized_peak_memory = 24;
+  OnMemoryAllocatedChange(buffer_id, reduced_memory, localized_peak_memory);
+
+  EXPECT_EQ(initial_memory, manager->GetPeakMemoryUsage(sequence_num_1));
+  EXPECT_EQ(localized_peak_memory, manager->GetPeakMemoryUsage(sequence_num_2));
+}
+
 }  // namespace gpu
diff --git a/gpu/ipc/service/shared_image_stub.cc b/gpu/ipc/service/shared_image_stub.cc
index 09d8f4f69..3b9158c 100644
--- a/gpu/ipc/service/shared_image_stub.cc
+++ b/gpu/ipc/service/shared_image_stub.cc
@@ -22,12 +22,13 @@
 
 SharedImageStub::SharedImageStub(GpuChannel* channel, int32_t route_id)
     : channel_(channel),
+      command_buffer_id_(
+          CommandBufferIdFromChannelAndRoute(channel->client_id(), route_id)),
       sequence_(channel->scheduler()->CreateSequence(SchedulingPriority::kLow)),
       sync_point_client_state_(
           channel->sync_point_manager()->CreateSyncPointClientState(
               CommandBufferNamespace::GPU_IO,
-              CommandBufferIdFromChannelAndRoute(channel->client_id(),
-                                                 route_id),
+              command_buffer_id_,
               sequence_)) {
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       this, "gpu::SharedImageStub", channel_->task_runner());
@@ -440,7 +441,11 @@
 }
 
 void SharedImageStub::TrackMemoryAllocatedChange(uint64_t delta) {
+  uint64_t old_size = size_;
   size_ += delta;
+  channel_->gpu_channel_manager()
+      ->peak_memory_monitor()
+      ->OnMemoryAllocatedChange(command_buffer_id_, old_size, size_);
 }
 
 uint64_t SharedImageStub::GetSize() const {
diff --git a/gpu/ipc/service/shared_image_stub.h b/gpu/ipc/service/shared_image_stub.h
index e7587b8..607f9ca 100644
--- a/gpu/ipc/service/shared_image_stub.h
+++ b/gpu/ipc/service/shared_image_stub.h
@@ -12,6 +12,7 @@
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/sequence_id.h"
 #include "gpu/command_buffer/service/sync_point_manager.h"
+#include "gpu/ipc/common/command_buffer_id.h"
 #include "gpu/ipc/common/gpu_messages.h"
 #include "gpu/ipc/service/gpu_ipc_service_export.h"
 #include "ipc/ipc_listener.h"
@@ -98,6 +99,12 @@
   void DestroySharedImage(const Mailbox& mailbox, const SyncToken& sync_token);
 
   GpuChannel* channel_;
+
+  // While this is not a CommandBuffer, this provides a unique identifier for
+  // a SharedImageStub, comprised of identifiers which it was already using.
+  // TODO(jonross): Look into a rename of CommandBufferId to reflect that it can
+  // be a unique identifier for numerous gpu constructs.
+  CommandBufferId command_buffer_id_;
   SequenceId sequence_;
   scoped_refptr<gpu::SyncPointClientState> sync_point_client_state_;
   scoped_refptr<SharedContextState> context_state_;
diff --git a/headless/test/data/protocol/emulation/virtual-time-history-navigation-same-doc-expected.txt b/headless/test/data/protocol/emulation/virtual-time-history-navigation-same-doc-expected.txt
new file mode 100644
index 0000000..0301eff
--- /dev/null
+++ b/headless/test/data/protocol/emulation/virtual-time-history-navigation-same-doc-expected.txt
@@ -0,0 +1,3 @@
+Tests virtual time with same document history navigation.
+Request to http://test.com/, type: Document
+PAGE: pass
\ No newline at end of file
diff --git a/headless/test/data/protocol/emulation/virtual-time-history-navigation-same-doc.js b/headless/test/data/protocol/emulation/virtual-time-history-navigation-same-doc.js
new file mode 100644
index 0000000..b316efb
--- /dev/null
+++ b/headless/test/data/protocol/emulation/virtual-time-history-navigation-same-doc.js
@@ -0,0 +1,56 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startBlank(
+      `Tests virtual time with same document history navigation.`);
+
+  const FetchHelper = await testRunner.loadScriptAbsolute(
+      '../fetch/resources/fetch-test.js');
+  const helper = new FetchHelper(testRunner, dp);
+  await helper.enable();
+
+  helper.onRequest('http://test.com/').fulfill(
+      FetchHelper.makeContentResponse(`
+        <body onload="step1()">
+        <script>
+          function step1() {
+            // Step 1 - create some history by navigating forward.  Note
+            // that this doesn't cause a load.
+            history.pushState({}, '', '/foo');
+            setTimeout(step2, 100);
+          }
+
+          function step2() {
+            if (location.href !== 'http://test.com/foo')
+              throw 'pushState failed.';
+            history.back();
+            setTimeout(step3, 100);
+          }
+
+          function step3() {
+            if (location.href !== 'http://test.com/')
+              throw 'Backward navigation failed.';
+            history.forward();
+            setTimeout(step4, 100);
+          }
+
+          function step4() {
+            if (location.href !== 'http://test.com/foo')
+              throw 'Forward navigation failed.';
+            console.log('pass');
+          }
+        </script>
+        </body>`)
+  );
+
+  await dp.Runtime.enable();
+  await dp.Emulation.setVirtualTimePolicy({policy: 'pause'});
+  await dp.Emulation.setVirtualTimePolicy({
+      policy: 'pauseIfNetworkFetchesPending', budget: 5000,
+      waitForNavigation: true});
+  dp.Page.navigate({url: 'http://test.com/'});
+  const {params} = await dp.Runtime.onceConsoleAPICalled();
+  testRunner.log(`PAGE: ${params.args[0].value}`);
+  testRunner.completeTest();
+})
diff --git a/headless/test/headless_protocol_browsertest.cc b/headless/test/headless_protocol_browsertest.cc
index 420a617..52082d7 100644
--- a/headless/test/headless_protocol_browsertest.cc
+++ b/headless/test/headless_protocol_browsertest.cc
@@ -246,6 +246,8 @@
                        "emulation/virtual-time-dialog-while-loading.js")
 HEADLESS_PROTOCOL_TEST(VirtualTimeHistoryNavigation,
                        "emulation/virtual-time-history-navigation.js")
+HEADLESS_PROTOCOL_TEST(VirtualTimeHistoryNavigationSameDoc,
+                       "emulation/virtual-time-history-navigation-same-doc.js")
 
 // http://crbug.com/633321
 #if defined(OS_ANDROID)
diff --git a/infra/config/PRESUBMIT.py b/infra/config/PRESUBMIT.py
index 902f91b2..7801680 100644
--- a/infra/config/PRESUBMIT.py
+++ b/infra/config/PRESUBMIT.py
@@ -31,26 +31,11 @@
             '--check'],
         kwargs={}, message=output_api.PresubmitError))
 
-  if 'infra/config/commit-queue.cfg' in input_api.LocalPaths():
-    commands.append(
-      input_api.Command(
-        name='commit-queue.cfg presubmit', cmd=[
-            input_api.python_executable, input_api.os_path.join(
-                'cq_cfg_presubmit.py'),
-            '--check'],
-        kwargs={}, message=output_api.PresubmitError),
-    )
-
   commands.extend(input_api.canned_checks.CheckLucicfgGenOutput(
       input_api, output_api, 'main.star'))
   commands.extend(input_api.canned_checks.CheckLucicfgGenOutput(
       input_api, output_api, 'dev.star'))
 
-  commands.extend(input_api.canned_checks.GetUnitTestsRecursively(
-      input_api, output_api,
-      input_api.os_path.join(input_api.PresubmitLocalPath()),
-      whitelist=[r'.+_unittest\.py$'], blacklist=[]))
-
   results = []
 
   results.extend(input_api.RunTests(commands))
diff --git a/infra/config/buckets/ci.star b/infra/config/buckets/ci.star
index 7320e44..96e5456 100644
--- a/infra/config/buckets/ci.star
+++ b/infra/config/buckets/ci.star
@@ -1184,6 +1184,7 @@
 # OS shouldn't matter.
 fyi_builder(
     name = 'mac-osxbeta-rel',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 fyi_builder(
@@ -1247,6 +1248,7 @@
 fyi_coverage_builder(
     name = 'mac-code-coverage-generation',
     cores = 24,
+    goma_backend = goma.backend.RBE_PROD,
     os = None,
     use_clang_coverage = True,
 )
@@ -1345,10 +1347,12 @@
 fyi_mac_builder(
     name = 'mac-hermetic-upgrade-rel',
     cores = 8,
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 fyi_mac_builder(
     name = 'mac-mojo-rel',
+    goma_backend = goma.backend.RBE_PROD,
     os = os.MAC_ANY,
 )
 
@@ -1577,18 +1581,6 @@
 )
 
 gpu_fyi_linux_ci_tester(
-    name = 'Mac FYI 10.14 Release (AMD)',
-)
-
-gpu_fyi_linux_ci_tester(
-    name = 'Mac FYI 10.14 Release (Intel)',
-)
-
-gpu_fyi_linux_ci_tester(
-    name = 'Mac FYI 10.14 Release (NVIDIA)',
-)
-
-gpu_fyi_linux_ci_tester(
     name = 'Mac FYI Debug (Intel)',
 )
 
@@ -1813,6 +1805,7 @@
 gpu_builder(
     name = 'GPU Mac Builder',
     cores = None,
+    goma_backend = goma.backend.RBE_PROD,
     os = os.MAC_ANY,
 )
 
@@ -2012,10 +2005,12 @@
 
 mac_builder(
     name = 'Mac Builder',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 mac_builder(
     name = 'Mac Builder (dbg)',
+    goma_backend = goma.backend.RBE_PROD,
     os = os.MAC_ANY,
 )
 
diff --git a/infra/config/buckets/try.star b/infra/config/buckets/try.star
index 7549502..7af269a28f 100644
--- a/infra/config/buckets/try.star
+++ b/infra/config/buckets/try.star
@@ -24,6 +24,8 @@
     ],
 )
 
+exec('./try/cq.star')
+
 luci.recipe.defaults.cipd_package.set('infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build')
 
 defaults.bucket.set('try')
@@ -121,6 +123,10 @@
 )
 
 android_builder(
+    name = 'android-opus-kitkat-arm-rel',
+)
+
+android_builder(
     name = 'android-oreo-arm64-cts-networkservice-dbg',
 )
 
@@ -1108,6 +1114,7 @@
 
 mac_builder(
     name = 'mac-rel',
+    goma_backend = goma.backend.RBE_PROD,
     goma_jobs = goma.jobs.J150,
 )
 
@@ -1116,10 +1123,12 @@
 # The 10.xx version translates to which bots will run isolated tests.
 mac_builder(
     name = 'mac_chromium_10.10',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 mac_builder(
     name = 'mac_chromium_10.12_rel_ng',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 mac_builder(
@@ -1140,6 +1149,7 @@
 mac_builder(
     name = 'mac_chromium_compile_dbg_ng',
     builderless = True,
+    goma_backend = goma.backend.RBE_PROD,
     goma_jobs = goma.jobs.J150,
     os = os.MAC_10_13,
     ssd = True,
@@ -1147,10 +1157,12 @@
 
 mac_builder(
     name = 'mac_chromium_compile_rel_ng',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 mac_builder(
     name = 'mac_chromium_dbg_ng',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 mac_builder(
diff --git a/infra/config/buckets/try/cq.star b/infra/config/buckets/try/cq.star
new file mode 100644
index 0000000..80b0e54
--- /dev/null
+++ b/infra/config/buckets/try/cq.star
@@ -0,0 +1,399 @@
+luci.cq_group(
+    name = 'cq',
+    # TODO(crbug/959436): enable it.
+    cancel_stale_tryjobs = False,
+    retry_config = cq.RETRY_ALL_FAILURES,
+    tree_status_host = 'chromium-status.appspot.com/',
+    watch = cq.refset(
+        repo = 'https://chromium.googlesource.com/chromium/src',
+        refs = ['refs/heads/.+'],
+    ),
+    acls = [
+        acl.entry(
+            acl.CQ_COMMITTER,
+            groups = 'project-chromium-committers',
+        ),
+        acl.entry(
+            acl.CQ_DRY_RUNNER,
+            groups = 'project-chromium-tryjob-access',
+        ),
+    ],
+    verifiers = [
+
+        #############################
+        # Always required builders. #
+        #############################
+
+        luci.cq_tryjob_verifier(builder = 'try/android-binary-size'),
+        luci.cq_tryjob_verifier(builder = 'try/android-kitkat-arm-rel'),
+        luci.cq_tryjob_verifier(builder = 'try/android-marshmallow-arm64-rel'),
+        luci.cq_tryjob_verifier(builder = 'try/android_arm64_dbg_recipe'),
+        luci.cq_tryjob_verifier(builder = 'try/android_clang_dbg_recipe'),
+        luci.cq_tryjob_verifier(builder = 'try/android_compile_dbg'),
+        luci.cq_tryjob_verifier(builder = 'try/android_cronet'),
+        luci.cq_tryjob_verifier(builder = 'try/cast_shell_android'),
+        luci.cq_tryjob_verifier(builder = 'try/cast_shell_linux'),
+        luci.cq_tryjob_verifier(builder = 'try/chromeos-amd64-generic-rel'),
+        luci.cq_tryjob_verifier(builder = 'try/chromeos-arm-generic-rel'),
+        luci.cq_tryjob_verifier(
+            builder = 'try/chromium_presubmit',
+            disable_reuse = True,
+        ),
+        luci.cq_tryjob_verifier(builder = 'try/fuchsia_arm64'),
+        luci.cq_tryjob_verifier(builder = 'try/fuchsia_x64'),
+        luci.cq_tryjob_verifier(builder = 'try/ios-simulator'),
+        luci.cq_tryjob_verifier(builder = 'try/linux-chromeos-compile-dbg'),
+        luci.cq_tryjob_verifier(builder = 'try/linux-chromeos-rel'),
+        luci.cq_tryjob_verifier(builder = 'try/linux-libfuzzer-asan-rel'),
+        luci.cq_tryjob_verifier(builder = 'try/linux-ozone-rel'),
+        luci.cq_tryjob_verifier(builder = 'try/linux-rel'),
+        luci.cq_tryjob_verifier(builder = 'try/linux_chromium_asan_rel_ng'),
+        luci.cq_tryjob_verifier(builder = 'try/linux_chromium_compile_dbg_ng'),
+        luci.cq_tryjob_verifier(builder = 'try/linux_chromium_tsan_rel_ng'),
+        luci.cq_tryjob_verifier(builder = 'try/mac-rel'),
+        luci.cq_tryjob_verifier(builder = 'try/mac_chromium_compile_dbg_ng'),
+        luci.cq_tryjob_verifier(builder = 'try/win-libfuzzer-asan-rel'),
+        luci.cq_tryjob_verifier(builder = 'try/win10_chromium_x64_rel_ng'),
+        luci.cq_tryjob_verifier(builder = 'try/win_chromium_compile_dbg_ng'),
+
+        ######################
+        # Optional builders. #
+        ######################
+
+        luci.cq_tryjob_verifier(
+            builder = 'try/android-cronet-arm-dbg',
+            location_regexp = [
+                '.+/[+]/components/cronet/.+',
+                '.+/[+]/components/grpc_support/.+',
+                '.+/[+]/build/android/.+',
+                '.+/[+]/build/config/android/.+',
+            ],
+            location_regexp_exclude = [
+                '.+/[+]/components/cronet/ios/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/android_compile_x64_dbg',
+            location_regexp = [
+                '.+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+',
+                '.+/[+]/chrome/browser/vr/.+',
+                '.+/[+]/sandbox/linux/seccomp-bpf/.+',
+                '.+/[+]/sandbox/linux/seccomp-bpf-helpers/.+',
+                '.+/[+]/sandbox/linux/system_headers/.+',
+                '.+/[+]/sandbox/linux/tests/.+',
+                '.+/[+]/third_party/gvr-android-sdk/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/android_compile_x86_dbg',
+            location_regexp = [
+                '.+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+',
+                '.+/[+]/chrome/browser/vr/.+',
+                '.+/[+]/sandbox/linux/seccomp-bpf/.+',
+                '.+/[+]/sandbox/linux/seccomp-bpf-helpers/.+',
+                '.+/[+]/sandbox/linux/system_headers/.+',
+                '.+/[+]/sandbox/linux/tests/.+',
+                '.+/[+]/third_party/gvr-android-sdk/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/android_optional_gpu_tests_rel',
+            location_regexp = [
+                '.+/[+]/cc/.+',
+                '.+/[+]/chrome/browser/vr/.+',
+                '.+/[+]/components/viz/.+',
+                '.+/[+]/content/test/gpu/.+',
+                '.+/[+]/gpu/.+',
+                '.+/[+]/media/audio/.+',
+                '.+/[+]/media/filters/.+',
+                '.+/[+]/media/gpu/.+',
+                '.+/[+]/services/viz/.+',
+                '.+/[+]/testing/trigger_scripts/.+',
+                '.+/[+]/third_party/blink/renderer/modules/webgl/.+',
+                '.+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+',
+                '.+/[+]/ui/gl/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/chromeos-amd64-generic-dbg',
+            location_regexp = [
+                '.+/[+]/content/gpu/.+',
+                '.+/[+]/media/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/chromeos-kevin-compile-rel',
+            location_regexp = [
+                '.+/[+]/chromeos/CHROMEOS_LKGM',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/chromeos-kevin-rel',
+            location_regexp = [
+                '.+/[+]/build/chromeos/.+',
+                '.+/[+]/build/config/chromeos/.*',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/closure_compilation',
+            location_regexp = [
+                '.+/[+]/third_party/closure_compiler/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/dawn-linux-x64-deps-rel',
+            location_regexp = [
+                '.+/[+]/gpu/.+',
+                '.+/[+]/testing/buildbot/chromium.dawn.json',
+                '.+/[+]/third_party/blink/renderer/modules/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/WebGPUExpectations',
+                '.+/[+]/third_party/dawn/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/dawn-mac-x64-deps-rel',
+            location_regexp = [
+                '.+/[+]/gpu/.+',
+                '.+/[+]/testing/buildbot/chromium.dawn.json',
+                '.+/[+]/third_party/blink/renderer/modules/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/WebGPUExpectations',
+                '.+/[+]/third_party/dawn/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/dawn-win10-x64-deps-rel',
+            location_regexp = [
+                '.+/[+]/gpu/.+',
+                '.+/[+]/testing/buildbot/chromium.dawn.json',
+                '.+/[+]/third_party/blink/renderer/modules/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/WebGPUExpectations',
+                '.+/[+]/third_party/dawn/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/dawn-win10-x86-deps-rel',
+            location_regexp = [
+                '.+/[+]/gpu/.+',
+                '.+/[+]/testing/buildbot/chromium.dawn.json',
+                '.+/[+]/third_party/blink/renderer/modules/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+',
+                '.+/[+]/third_party/blink/web_tests/WebGPUExpectations',
+                '.+/[+]/third_party/dawn/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/fuchsia-arm64-cast',
+            location_regexp = [
+                '.+/[+]/chromecast/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/fuchsia-x64-cast',
+            location_regexp = [
+                '.+/[+]/chromecast/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/gpu-fyi-try-android-p-pixel-2-skv-32',
+            # Some locations disabled due to limited capacity.
+            location_regexp = [
+                #'.+/[+]/cc/.+',
+                '.+/[+]/components/viz/.+',
+                '.+/[+]/content/test/gpu/gpu_tests/.+py',
+                '.+/[+]/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt',
+                '.+/[+]/gpu/vulkan/.+',
+                #'.+/[+]/media/gpu/.+',
+                '.+/[+]/services/viz/.+',
+                #'.+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+',
+                #'.+/[+]/third_party/skia/src/gpu/.+',
+                #'.+/[+]/third_party/skia/include/gpu/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/ios-simulator-cronet',
+            location_regexp = [
+                '.+/[+]/components/cronet/.+',
+                '.+/[+]/components/grpc_support/.+',
+                '.+/[+]/ios/.+',
+            ],
+            location_regexp_exclude = [
+                '.+/[+]/components/cronet/android/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/ios-simulator-full-configs',
+            location_regexp = [
+                '.+/[+]/ios/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/linux-blink-rel',
+            location_regexp = [
+                '.+/[+]/cc/.+',
+                '.+/[+]/third_party/blink/renderer/core/paint/.+',
+                '.+/[+]/third_party/blink/renderer/core/svg/.+',
+                '.+/[+]/third_party/blink/renderer/platform/graphics/.+',
+                '.+/[+]/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint',
+                '.+/[+]/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/linux_chromium_dbg_ng',
+            location_regexp = [
+                '.+/[+]/build/.*check_gn_headers.*',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/linux_layout_tests_composite_after_paint',
+            location_regexp = [
+                '.+/[+]/third_party/blink/renderer/core/paint/.+',
+                '.+/[+]/third_party/blink/renderer/core/svg/.+',
+                '.+/[+]/third_party/blink/renderer/platform/graphics/.+',
+                '.+/[+]/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint',
+                '.+/[+]/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/linux_layout_tests_layout_ng_disabled',
+            location_regexp = [
+                '.+/[+]/third_party/blink/renderer/core/editing/.+',
+                '.+/[+]/third_party/blink/renderer/core/layout/.+',
+                '.+/[+]/third_party/blink/renderer/core/paint/.+',
+                '.+/[+]/third_party/blink/renderer/core/svg/.+',
+                '.+/[+]/third_party/blink/renderer/platform/fonts/shaping/.+',
+                '.+/[+]/third_party/blink/renderer/platform/graphics/.+',
+                '.+/[+]/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/linux_optional_gpu_tests_rel',
+            location_regexp = [
+                '.+/[+]/chrome/browser/vr/.+',
+                '.+/[+]/content/test/gpu/.+',
+                '.+/[+]/gpu/.+',
+                '.+/[+]/media/audio/.+',
+                '.+/[+]/media/filters/.+',
+                '.+/[+]/media/gpu/.+',
+                '.+/[+]/testing/buildbot/chromium.gpu.fyi.json',
+                '.+/[+]/testing/trigger_scripts/.+',
+                '.+/[+]/third_party/blink/renderer/modules/webgl/.+',
+                '.+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+',
+                '.+/[+]/ui/gl/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/linux_vr',
+            location_regexp = [
+                '.+/[+]/chrome/browser/vr/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/mac_optional_gpu_tests_rel',
+            location_regexp = [
+                '.+/[+]/chrome/browser/vr/.+',
+                '.+/[+]/content/test/gpu/.+',
+                '.+/[+]/gpu/.+',
+                '.+/[+]/media/audio/.+',
+                '.+/[+]/media/filters/.+',
+                '.+/[+]/media/gpu/.+',
+                '.+/[+]/services/shape_detection/.+',
+                '.+/[+]/testing/buildbot/chromium.gpu.fyi.json',
+                '.+/[+]/testing/trigger_scripts/.+',
+                '.+/[+]/third_party/blink/renderer/modules/webgl/.+',
+                '.+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+',
+                '.+/[+]/ui/gl/.+',
+            ],
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/win_optional_gpu_tests_rel',
+            location_regexp = [
+                '.+/[+]/chrome/browser/vr/.+',
+                '.+/[+]/content/test/gpu/.+',
+                '.+/[+]/device/vr/.+',
+                '.+/[+]/gpu/.+',
+                '.+/[+]/media/audio/.+',
+                '.+/[+]/media/filters/.+',
+                '.+/[+]/media/gpu/.+',
+                '.+/[+]/testing/buildbot/chromium.gpu.fyi.json',
+                '.+/[+]/testing/trigger_scripts/.+',
+                '.+/[+]/third_party/blink/renderer/modules/vr/.+',
+                '.+/[+]/third_party/blink/renderer/modules/webgl/.+',
+                '.+/[+]/third_party/blink/renderer/modules/xr/.+',
+                '.+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+',
+                '.+/[+]/ui/gl/.+',
+            ],
+        ),
+
+        ##########################
+        # Experimental builders. #
+        ##########################
+
+        luci.cq_tryjob_verifier(
+            builder = 'try/android-marshmallow-arm64-coverage-rel',
+            experiment_percentage = 20,
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/android-pie-arm64-rel',
+            experiment_percentage = 50,
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/chromeos-kevin-experimental-rel',
+            experiment_percentage = 5,
+        ),
+        luci.cq_tryjob_verifier(
+            builder = 'try/fuchsia-compile-x64-dbg',
+            experiment_percentage = 5,
+        ),
+        # https://crbug.com/739556; make this non-experimental ASAP.
+        luci.cq_tryjob_verifier(
+            builder = 'try/ios-device',
+            experiment_percentage = 10,
+        ),
+        # https://crbug.com/739556
+        luci.cq_tryjob_verifier(
+            builder = 'try/ios-device-xcode-clang',
+            experiment_percentage = 10,
+        ),
+        # https://crbug.com/739556
+        luci.cq_tryjob_verifier(
+            builder = 'try/ios-simulator-xcode-clang',
+            experiment_percentage = 10,
+        ),
+    ],
+)
+
+luci.cq_group(
+    name = 'cq-branches',
+    cancel_stale_tryjobs = False,
+    retry_config = cq.RETRY_ALL_FAILURES,
+    tree_status_host = 'chromium-status.appspot.com/',
+    watch = cq.refset(
+        repo = 'https://chromium.googlesource.com/chromium/src',
+        refs = ['refs/branch-heads/.+'],
+    ),
+    acls = [
+        acl.entry(
+            acl.CQ_COMMITTER,
+            groups = 'project-chromium-committers',
+        ),
+        acl.entry(
+            acl.CQ_DRY_RUNNER,
+            groups = 'project-chromium-tryjob-access',
+        ),
+    ],
+    verifiers = [
+        luci.cq_tryjob_verifier(
+            builder = builder,
+            experiment_percentage = 100,
+        ) for builder in [
+            'linux-rel',
+        ]
+    ],
+)
diff --git a/infra/config/commit-queue.cfg b/infra/config/commit-queue.cfg
deleted file mode 100644
index 4a9b36a..0000000
--- a/infra/config/commit-queue.cfg
+++ /dev/null
@@ -1,452 +0,0 @@
-# See http://luci-config.appspot.com/schemas/projects:commit-queue.cfg for the
-# documentation of this file format.
-#
-# This file is also used to auto generate //docs/infra/cq_builders.md. If you
-# change this file, run //infra/config/cq_cfg_presubmit.py, which will generate
-# that file. That script also requires that the builders in this file remain
-# sorted. The script is invoked via presubmit, and will complain if this file is
-# changed but the documentation isn't.
-#
-# The auto generated file copies comments made to builders in this file. If you
-# comment on the line directly above a builder, that comment will get copied to
-# the documentation.
-#
-# The following comment will get copied.
-#
-# # This is a great builder!
-# builders { name: "chromium_presubmit" }
-#
-# The following comment will not get copied.
-#
-# # This is a ok builder!
-#
-# builders { name: "chromium_presubmit" }
-
-cq_status_host: "chromium-cq-status.appspot.com"
-submit_options {
-  max_burst: 2
-  burst_delay {
-    seconds: 60
-  }
-}
-
-# NOTE: To add a new builder to the commit queue, see
-# https://chromium.googlesource.com/chromium/src/+/HEAD/docs/infra/cq.md#how-do-i-add-a-new-builder-to-the-cq
-
-config_groups {
-  gerrit {
-    url: "https://chromium-review.googlesource.com"
-    projects {
-      name: "chromium/src"
-      ref_regexp: "refs/heads/.+"
-    }
-  }
-  verifiers {
-    gerrit_cq_ability {
-      committer_list: "project-chromium-committers"
-      dry_run_access_list: "project-chromium-tryjob-access"
-    }
-    tree_status {
-      url: "https://chromium-status.appspot.com/"
-    }
-    tryjob {
-      # TODO(crbug/959436): enable it.
-      cancel_stale_tryjobs: NO
-      #############################
-      # Always required builders. #
-      #############################
-
-      builders {
-        name: "chromium/try/android-binary-size"
-      }
-      builders {
-        name: "chromium/try/android-kitkat-arm-rel"
-      }
-      builders {
-        name: "chromium/try/android-marshmallow-arm64-rel"
-      }
-      builders {
-        name: "chromium/try/android_arm64_dbg_recipe"
-      }
-      builders {
-        name: "chromium/try/android_clang_dbg_recipe"
-      }
-      builders {
-        name: "chromium/try/android_compile_dbg"
-      }
-      builders {
-        name: "chromium/try/android_cronet"
-      }
-      builders {
-        name: "chromium/try/cast_shell_android"
-      }
-      builders {
-        name: "chromium/try/cast_shell_linux"
-      }
-      builders {
-        name: "chromium/try/chromeos-amd64-generic-rel"
-      }
-      builders {
-        name: "chromium/try/chromeos-arm-generic-rel"
-      }
-      builders {
-        name: "chromium/try/chromium_presubmit"
-        disable_reuse: true
-      }
-      builders {
-        name: "chromium/try/fuchsia_arm64"
-      }
-      builders {
-        name: "chromium/try/fuchsia_x64"
-      }
-      builders {
-        name: "chromium/try/ios-simulator"
-      }
-      builders {
-        name: "chromium/try/linux-chromeos-compile-dbg"
-      }
-      builders {
-        name: "chromium/try/linux-chromeos-rel"
-      }
-      builders {
-        name: "chromium/try/linux-libfuzzer-asan-rel"
-      }
-      builders {
-        name: "chromium/try/linux-ozone-rel"
-      }
-      builders {
-        name: "chromium/try/linux-rel"
-      }
-      builders {
-        name: "chromium/try/linux_chromium_asan_rel_ng"
-      }
-      builders {
-        name: "chromium/try/linux_chromium_compile_dbg_ng"
-      }
-      builders {
-        name: "chromium/try/linux_chromium_tsan_rel_ng"
-      }
-      builders {
-        name: "chromium/try/mac-rel"
-      }
-      builders {
-        name: "chromium/try/mac_chromium_compile_dbg_ng"
-      }
-      builders {
-        name: "chromium/try/win-libfuzzer-asan-rel"
-      }
-      builders {
-        name: "chromium/try/win10_chromium_x64_rel_ng"
-      }
-      builders {
-        name: "chromium/try/win_chromium_compile_dbg_ng"
-      }
-
-      ######################
-      # Optional builders. #
-      ######################
-
-      builders {
-        name: "chromium/try/android-cronet-arm-dbg"
-        location_regexp: ".+/[+]/components/cronet/.+"
-        location_regexp: ".+/[+]/components/grpc_support/.+"
-        location_regexp: ".+/[+]/build/android/.+"
-        location_regexp: ".+/[+]/build/config/android/.+"
-        location_regexp_exclude: ".+/[+]/components/cronet/ios/.+"
-      }
-      builders {
-        name: "chromium/try/android_compile_x64_dbg"
-        location_regexp: ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+"
-        location_regexp: ".+/[+]/chrome/browser/vr/.+"
-        location_regexp: ".+/[+]/sandbox/linux/seccomp-bpf/.+"
-        location_regexp: ".+/[+]/sandbox/linux/seccomp-bpf-helpers/.+"
-        location_regexp: ".+/[+]/sandbox/linux/system_headers/.+"
-        location_regexp: ".+/[+]/sandbox/linux/tests/.+"
-        location_regexp: ".+/[+]/third_party/gvr-android-sdk/.+"
-      }
-      builders {
-        name: "chromium/try/android_compile_x86_dbg"
-        location_regexp: ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+"
-        location_regexp: ".+/[+]/chrome/browser/vr/.+"
-        location_regexp: ".+/[+]/sandbox/linux/seccomp-bpf/.+"
-        location_regexp: ".+/[+]/sandbox/linux/seccomp-bpf-helpers/.+"
-        location_regexp: ".+/[+]/sandbox/linux/system_headers/.+"
-        location_regexp: ".+/[+]/sandbox/linux/tests/.+"
-        location_regexp: ".+/[+]/third_party/gvr-android-sdk/.+"
-      }
-      builders {
-        name: "chromium/try/android_optional_gpu_tests_rel"
-        location_regexp: ".+/[+]/cc/.+"
-        location_regexp: ".+/[+]/chrome/browser/vr/.+"
-        location_regexp: ".+/[+]/components/viz/.+"
-        location_regexp: ".+/[+]/content/test/gpu/.+"
-        location_regexp: ".+/[+]/gpu/.+"
-        location_regexp: ".+/[+]/media/audio/.+"
-        location_regexp: ".+/[+]/media/filters/.+"
-        location_regexp: ".+/[+]/media/gpu/.+"
-        location_regexp: ".+/[+]/services/viz/.+"
-        location_regexp: ".+/[+]/testing/trigger_scripts/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgl/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+"
-        location_regexp: ".+/[+]/ui/gl/.+"
-      }
-      builders {
-        name: "chromium/try/chromeos-amd64-generic-dbg"
-        location_regexp: ".+/[+]/content/gpu/.+"
-        location_regexp: ".+/[+]/media/.+"
-
-      }
-      builders {
-        name: "chromium/try/chromeos-kevin-compile-rel"
-        location_regexp: ".+/[+]/chromeos/CHROMEOS_LKGM"
-      }
-      builders {
-        name: "chromium/try/chromeos-kevin-rel"
-        location_regexp: ".+/[+]/build/chromeos/.+"
-        location_regexp: ".+/[+]/build/config/chromeos/.*"
-      }
-      builders {
-        name: "chromium/try/closure_compilation"
-        location_regexp: ".+/[+]/third_party/closure_compiler/.+"
-      }
-      builders {
-        name: "chromium/try/dawn-linux-x64-deps-rel"
-        location_regexp: ".+/[+]/gpu/.+"
-        location_regexp: ".+/[+]/testing/buildbot/chromium.dawn.json"
-        location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgpu/.+"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
-        location_regexp: ".+/[+]/third_party/dawn/.+"
-      }
-      builders {
-        name: "chromium/try/dawn-mac-x64-deps-rel"
-        location_regexp: ".+/[+]/gpu/.+"
-        location_regexp: ".+/[+]/testing/buildbot/chromium.dawn.json"
-        location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgpu/.+"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
-        location_regexp: ".+/[+]/third_party/dawn/.+"
-      }
-      builders {
-        name: "chromium/try/dawn-win10-x64-deps-rel"
-        location_regexp: ".+/[+]/gpu/.+"
-        location_regexp: ".+/[+]/testing/buildbot/chromium.dawn.json"
-        location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgpu/.+"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
-        location_regexp: ".+/[+]/third_party/dawn/.+"
-      }
-      builders {
-        name: "chromium/try/dawn-win10-x86-deps-rel"
-        location_regexp: ".+/[+]/gpu/.+"
-        location_regexp: ".+/[+]/testing/buildbot/chromium.dawn.json"
-        location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgpu/.+"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
-        location_regexp: ".+/[+]/third_party/dawn/.+"
-      }
-      builders {
-        name: "chromium/try/fuchsia-arm64-cast"
-        location_regexp: ".+/[+]/chromecast/.+"
-      }
-      builders {
-        name: "chromium/try/fuchsia-x64-cast"
-        location_regexp: ".+/[+]/chromecast/.+"
-      }
-      builders {
-        name: "chromium/try/gpu-fyi-try-android-p-pixel-2-skv-32"
-	# Some locations disabled due to limited capacity.
-        #location_regexp: ".+/[+]/cc/.+"
-        location_regexp: ".+/[+]/components/viz/.+"
-        location_regexp: ".+/[+]/content/test/gpu/gpu_tests/.+py"
-        location_regexp: ".+/[+]/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt"
-        location_regexp: ".+/[+]/gpu/vulkan/.+"
-        #location_regexp: ".+/[+]/media/gpu/.+"
-        location_regexp: ".+/[+]/services/viz/.+"
-        #location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+"
-        #location_regexp: ".+/[+]/third_party/skia/src/gpu/.+"
-        #location_regexp: ".+/[+]/third_party/skia/include/gpu/.+"
-      }
-      builders {
-        name: "chromium/try/ios-simulator-cronet"
-        location_regexp: ".+/[+]/components/cronet/.+"
-        location_regexp: ".+/[+]/components/grpc_support/.+"
-        location_regexp: ".+/[+]/ios/.+"
-        location_regexp_exclude: ".+/[+]/components/cronet/android/.+"
-      }
-      builders {
-        name: "chromium/try/ios-simulator-full-configs"
-        location_regexp: ".+/[+]/ios/.+"
-      }
-      builders {
-        name: "chromium/try/linux-blink-rel"
-        location_regexp: ".+/[+]/cc/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/core/paint/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/core/svg/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/.+"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+"
-      }
-      builders {
-        name: "chromium/try/linux_chromium_dbg_ng"
-        location_regexp: ".+/[+]/build/.*check_gn_headers.*"
-      }
-      builders {
-        name: "chromium/try/linux_layout_tests_composite_after_paint"
-        location_regexp: ".+/[+]/third_party/blink/renderer/core/paint/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/core/svg/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/.+"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+"
-      }
-      builders {
-        name: "chromium/try/linux_layout_tests_layout_ng_disabled"
-        location_regexp: ".+/[+]/third_party/blink/renderer/core/editing/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/core/layout/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/core/paint/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/core/svg/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/platform/fonts/shaping/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/.+"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/.+"
-      }
-      builders {
-        name: "chromium/try/linux_optional_gpu_tests_rel"
-        location_regexp: ".+/[+]/chrome/browser/vr/.+"
-        location_regexp: ".+/[+]/content/test/gpu/.+"
-        location_regexp: ".+/[+]/gpu/.+"
-        location_regexp: ".+/[+]/media/audio/.+"
-        location_regexp: ".+/[+]/media/filters/.+"
-        location_regexp: ".+/[+]/media/gpu/.+"
-        location_regexp: ".+/[+]/testing/buildbot/chromium.gpu.fyi.json"
-        location_regexp: ".+/[+]/testing/trigger_scripts/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgl/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+"
-        location_regexp: ".+/[+]/ui/gl/.+"
-      }
-      builders {
-        name: "chromium/try/linux_vr"
-        location_regexp: ".+/[+]/chrome/browser/vr/.+"
-      }
-      builders {
-        name: "chromium/try/mac_optional_gpu_tests_rel"
-        location_regexp: ".+/[+]/chrome/browser/vr/.+"
-        location_regexp: ".+/[+]/content/test/gpu/.+"
-        location_regexp: ".+/[+]/gpu/.+"
-        location_regexp: ".+/[+]/media/audio/.+"
-        location_regexp: ".+/[+]/media/filters/.+"
-        location_regexp: ".+/[+]/media/gpu/.+"
-        location_regexp: ".+/[+]/services/shape_detection/.+"
-        location_regexp: ".+/[+]/testing/buildbot/chromium.gpu.fyi.json"
-        location_regexp: ".+/[+]/testing/trigger_scripts/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgl/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+"
-        location_regexp: ".+/[+]/ui/gl/.+"
-      }
-      builders {
-        name: "chromium/try/win_optional_gpu_tests_rel"
-        location_regexp: ".+/[+]/chrome/browser/vr/.+"
-        location_regexp: ".+/[+]/content/test/gpu/.+"
-        location_regexp: ".+/[+]/device/vr/.+"
-        location_regexp: ".+/[+]/gpu/.+"
-        location_regexp: ".+/[+]/media/audio/.+"
-        location_regexp: ".+/[+]/media/filters/.+"
-        location_regexp: ".+/[+]/media/gpu/.+"
-        location_regexp: ".+/[+]/testing/buildbot/chromium.gpu.fyi.json"
-        location_regexp: ".+/[+]/testing/trigger_scripts/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/modules/vr/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgl/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/modules/xr/.+"
-        location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+"
-        location_regexp: ".+/[+]/ui/gl/.+"
-      }
-
-      ##########################
-      # Experimental builders. #
-      ##########################
-
-      builders {
-        name: "chromium/try/android-marshmallow-arm64-coverage-rel"
-        experiment_percentage: 20
-      }
-      builders {
-        name: "chromium/try/android-pie-arm64-rel"
-        experiment_percentage: 50
-      }
-      builders {
-        name: "chromium/try/chromeos-kevin-experimental-rel"
-        experiment_percentage: 5
-      }
-      builders {
-        name: "chromium/try/fuchsia-compile-x64-dbg"
-        experiment_percentage: 5
-      }
-      # https://crbug.com/739556; make this non-experimental ASAP.
-      builders {
-        name: "chromium/try/ios-device"
-        experiment_percentage: 10
-      }
-      # https://crbug.com/739556
-      builders {
-        name: "chromium/try/ios-device-xcode-clang"
-        experiment_percentage: 10
-      }
-      # https://crbug.com/739556
-      builders {
-        name: "chromium/try/ios-simulator-xcode-clang"
-        experiment_percentage: 10
-      }
-
-      retry_config {
-        single_quota: 1
-        global_quota: 2
-        failure_weight: 1
-        transient_failure_weight: 1
-        timeout_weight: 2
-      }
-    }
-  }
-}
-
-# Config group for tryjobs for branch-heads.
-# Will be removed when it has converged with
-# the refs/heads group.
-config_groups {
-  gerrit {
-    url: "https://chromium-review.googlesource.com"
-    projects {
-      name: "chromium/src"
-      ref_regexp: "refs/branch-heads/.+"
-    }
-  }
-  verifiers {
-    gerrit_cq_ability {
-      committer_list: "project-chromium-committers"
-      dry_run_access_list: "project-chromium-tryjob-access"
-    }
-    tree_status {
-      url: "https://chromium-status.appspot.com/"
-    }
-    tryjob {
-      # TODO(crbug/959436): enable it.
-      cancel_stale_tryjobs: NO
-      ###########################################
-      # Experimental builders for branch-heads. #
-      ###########################################
-      builders {
-        name: "chromium/try/linux-rel"
-        experiment_percentage: 100
-      }
-
-      retry_config {
-        single_quota: 1
-        global_quota: 2
-        failure_weight: 1
-        transient_failure_weight: 1
-        timeout_weight: 2
-      }
-    }
-  }
-}
-
-# NOTE: To add a new builder to the commit queue, see
-# https://chromium.googlesource.com/chromium/src/+/HEAD/docs/infra/cq.md#how-do-i-add-a-new-builder-to-the-cq
diff --git a/infra/config/cq-builders-md.star b/infra/config/cq-builders-md.star
new file mode 100644
index 0000000..769b98c7
--- /dev/null
+++ b/infra/config/cq-builders-md.star
@@ -0,0 +1,174 @@
+_MD_HEADER = """\
+<!-- Auto-generated by lucicfg (via cq-builders-md.star). -->
+<!-- Do not modify manually. -->
+
+# List of CQ builders
+
+[TOC]
+
+Each builder name links to that builder on Milo. The "matching builders" links
+point to the file used to determine which configurations a builder should copy
+when running. These links might 404 or error; they are hard-coded right now,
+using common assumptions about how builders are configured.
+"""
+
+_REQUIRED_HEADER = """\
+These builders must pass before a CL may land.
+"""
+
+_OPTIONAL_HEADER = """\
+These builders optionally run, depending on the files in a CL. For example, a CL
+which touches `//gpu/BUILD.gn` would trigger the builder
+`android_optional_gpu_tests_rel`, due to the `location_regexp` values for that
+builder.
+"""
+
+_EXPERIMENTAL_HEADER = """\
+These builders are run on some percentage of builds. Their results are ignored
+by CQ. These are often used to test new configurations before they are added
+as required builders.
+"""
+
+_TRY_BUILDER_VIEW_URL = 'https://ci.chromium.org/p/chromium/builders/try'
+
+_REGEX_PREFIX = '.+/[+]/'
+
+
+def _get_main_config_group_builders(ctx):
+  cq_cfg = ctx.output['commit-queue.cfg']
+
+  for c in cq_cfg.config_groups:
+    if len(c.gerrit) != 1:
+      continue
+
+    gerrit = c.gerrit[0]
+    if len(gerrit.projects) != 1:
+      continue
+
+    project = gerrit.projects[0]
+    if len(project.ref_regexp) != 1:
+      continue
+
+    if (project.name == 'chromium/src'
+        # Repeated proto fields have an internal type that won't compare equal
+        # to a list, so convert it
+        and list(project.ref_regexp) == ['refs/heads/.+']):
+      return c.verifiers.tryjob.builders
+
+  fail('Could not find the main CQ group')
+
+
+def _group_builders_by_section(builders):
+  required = []
+  experimental = []
+  optional = []
+
+  for builder in builders:
+    if builder.experiment_percentage:
+      experimental.append(builder)
+    elif builder.location_regexp or builder.location_regexp_exclude:
+      optional.append(builder)
+    else:
+      required.append(builder)
+
+  return struct(
+      required = required,
+      experimental = experimental,
+      optional = optional,
+  )
+
+
+def _codesearch_query(*atoms, package=None):
+  query = ['https://cs.chromium.org/search?q=']
+  if package != None:
+    query.append('package:%5E')  # %5E -> encoded ^
+    query.append(package)
+    query.append('$')
+  for atom in atoms:
+    query.append('+')
+    query.append(atom)
+  return ''.join(query)
+
+
+def _get_regex_line_details(regex):
+  if regex.startswith(_REGEX_PREFIX):
+    regex = regex[len(_REGEX_PREFIX):]
+  title = '//' + regex.lstrip('/')
+  if regex.endswith('.+'):
+    regex = regex[:-len('.+')]
+
+  url = _codesearch_query('file:' + regex, package='chromium')
+  # If the regex doesn't have any interesting characters that might be part of a
+  # regex, assume the regex is targeting a single path and direct link to it
+  # Equals sign and dashes used by layout tests
+  if all([c.isalnum() or c in '/-_=' for c in regex.codepoints()]):
+    url = 'https://cs.chromium.org/chromium/src/' + regex
+
+  return struct(
+      title=title,
+      url=url,
+  )
+
+
+def _generate_cq_builders_md(ctx):
+  builders = _get_main_config_group_builders(ctx)
+
+  builders_by_section = _group_builders_by_section(builders)
+
+  lines = [_MD_HEADER]
+
+  for title, header, section in (
+      ('Required builders', _REQUIRED_HEADER, 'required'),
+      ('Optional builders', _OPTIONAL_HEADER, 'optional'),
+      ('Experimental builders', _EXPERIMENTAL_HEADER, 'experimental'),
+  ):
+    builders = getattr(builders_by_section, section)
+    if not builders:
+      continue
+
+    lines.append('## %s' % title)
+    lines.append(header)
+
+    for b in builders:
+      name = b.name.rsplit('/', 1)[-1]
+      lines.append((
+          '* [{name}]({try_builder_view}/{name}) '
+          + '([definition]({definition_query}+{name})) '
+          + '([matching builders]({trybot_query}+{name}))'
+      ).format(
+          name=name,
+          try_builder_view=_TRY_BUILDER_VIEW_URL,
+          definition_query=_codesearch_query(
+              'file:/cq.star$', '-file:/beta/', '-file:/stable/',
+              package='chromium'),
+          trybot_query=_codesearch_query('file:trybots.py'),
+      ))
+
+      if b.experiment_percentage:
+        lines.append('  * Experiment percentage: {percentage}'.format(
+            percentage=b.experiment_percentage,
+        ))
+
+      for attr, regexp_header in (
+          ('location_regexp', 'Path regular expressions:'),
+          ('location_regexp_exclude', 'Path exclude regular expressions:'),
+      ):
+        regexes = getattr(b, attr)
+        if not regexes:
+          continue
+        lines.append('')
+        lines.append('  ' + regexp_header)
+        for regex in regexes:
+          regex_line_details = _get_regex_line_details(regex)
+          lines.append('  * [`{title}`]({url})'.format(
+              title=regex_line_details.title,
+              url=regex_line_details.url,
+          ))
+
+      lines.append('')
+
+    lines.append('')
+
+  ctx.output['cq-builders.md'] = '\n'.join(lines)
+
+lucicfg.generator(_generate_cq_builders_md)
diff --git a/infra/config/cq_cfg_presubmit.py b/infra/config/cq_cfg_presubmit.py
deleted file mode 100755
index 99e4c2a4..0000000
--- a/infra/config/cq_cfg_presubmit.py
+++ /dev/null
@@ -1,386 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import argparse
-import difflib
-import re
-import os
-import string
-import sys
-
-
-# Path to the root of the current chromium checkout.
-CHROMIUM_DIR = os.path.abspath(os.path.join(
-    os.path.dirname(__file__), '..', '..',))
-
-
-MD_HEADER = """# List of CQ builders
-
-This page is auto generated using the script
-//infra/config/cq_cfg_presubmit.py. Do not manually edit.
-
-[TOC]
-
-Each builder name links to that builder on Milo. The "Backing builders" links
-point to the file used to determine which configurations a builder should copy
-when running. These links might 404 or error; they are hard-coded right now,
-using common assumptions about how builders are configured.
-"""
-
-
-REQUIRED_HEADER = """
-These builders must pass before a CL may land."""
-
-
-OPTIONAL_HEADER = """These builders optionally run, depending on the files in a
-CL. For example, a CL which touches `//gpu/BUILD.gn` would trigger the builder
-`android_optional_gpu_tests_rel`, due to the `location_regexp` values for that
-builder."""
-
-
-EXPERIMENTAL_HEADER = """
-These builders are run on some percentage of builds. Their results are ignored
-by CQ. These are often used to test new configurations before they are added
-as required builders."""
-
-
-BUILDER_VIEW_URL = (
-    'https://ci.chromium.org/p/chromium/builders/luci.chromium.try/')
-
-
-CODE_SEARCH_BASE = 'https://cs.chromium.org/'
-
-
-TRYBOT_SOURCE_URL = CODE_SEARCH_BASE + 'search/?q=file:trybots.py+'
-
-
-CQ_CONFIG_LOCATION_URL = (
-    CODE_SEARCH_BASE + 'search/?q=package:%5Echromium$+file:commit-queue.cfg+')
-
-
-REGEX_SEARCH_URL = CODE_SEARCH_BASE + 'search/?q=package:%5Echromium$+'
-
-
-# Location regexps in commit-queue.cfg are expected to have this prefix.
-REGEX_PREFIX = r'.+/[+]/'
-
-
-def parse_text_proto_message(lines):
-  """Parses a text proto. LOW QUALITY, MAY EASILY BREAK.
-
-  If you really need to parse text protos, use the actual python library for
-  protobufs. This exists because the .proto file for commit-queue.cfg lives in
-  another repository.
-  """
-  data = {}
-
-  linenum = 0
-  # Tracks the current comment. Gets cleared if there's a blank line. Is added
-  # to submessages, to allow for builders to contain comments.
-  current_comment = None
-  while linenum < len(lines):
-    line = lines[linenum].strip()
-    if not line:
-      current_comment = None
-      linenum += 1
-    elif line.startswith('#'):
-      if current_comment:
-        current_comment += '\n' + line[1:]
-      else:
-        current_comment = line[1:]
-      linenum += 1
-    elif '{' in line:
-      # Sub message. Put before the ':' clause so that it correctly handles one
-      # line messages.
-      end = linenum
-      count = 0
-      newlines = []
-      while end < len(lines):
-        inner_line = lines[end]
-        if '{' in inner_line:
-          count += 1
-        if '}' in inner_line:
-          count -= 1
-
-        if end == linenum:
-          newline = inner_line.split('{', 1)[1]
-          if count == 0:
-            newline = newline.split('}')[0]
-          newlines.append(newline)
-        elif count == 0:
-          newlines.append(inner_line.split('}')[0])
-        else:
-          newlines.append(inner_line)
-        end += 1
-        if count == 0:
-          break
-      name = line.split('{')[0].strip()
-      value = parse_text_proto_message(newlines)
-      if current_comment:
-        value['comment'] = current_comment
-        current_comment = None
-
-      if name in data:
-        data[name].append(value)
-      else:
-        data[name] = [value]
-      linenum = end
-    elif ':' in line:
-      # It's a field
-      name, value = line.split(':', 1)
-      value = value.strip()
-      if value.startswith('"'):
-        value = value.strip('"')
-
-      if name in data:
-        data[name].append(value)
-      else:
-        data[name] = [value]
-      linenum += 1
-    else:
-      raise ValueError('Invalid line (number %d):\n%s' % (linenum, line))
-
-  return data
-
-
-class BuilderList(object):
-  def __init__(self, builders):
-    self.builders = builders
-
-  def sort(self):
-    """Sorts the builder list.
-
-    Sorts the builders in place. Orders them into three groups: experimental,
-    required, and optional."""
-    self.builders.sort(key=lambda b: '%s|%s|%s' % (
-        'z' if b.get('experiment_percentage') else 'a',
-        'z' if b.get('location_regexp') else 'a',
-        b['name']))
-
-  def by_section(self):
-    required = []
-    experimental = []
-    optional = []
-    for b in self.builders:
-      # Don't handle if something is both optional and experimental
-      if b.get('location_regexp'):
-        optional.append(b)
-      elif b.get('experiment_percentage'):
-        experimental.append(b)
-      else:
-        required.append(b)
-
-    return required, optional, experimental
-
-
-class CQConfig(object):
-  def __init__(self, lines):
-    parsed_value = parse_text_proto_message(lines)
-
-    # Sanity check.
-    assert len(parsed_value['config_groups']) == 2, (
-        'Expected only two config group, found %d' % len(
-            parsed_value['config_groups']))
-    grp = parsed_value['config_groups'][0]
-    gerrit = grp['gerrit'][0]
-    name = gerrit['projects'][0]['name'][0]
-    assert name == 'chromium/src', (
-        'Expected first config group to be chromium src, got %s' % name)
-    # The config group for chromium source refs/heads.
-    self._config_group = grp
-
-  @staticmethod
-  def from_file(path):
-    with open(path) as f:
-      lines = f.readlines()
-
-    return CQConfig(lines)
-
-  def get_location_regexps(self):
-    _, opt, _ = self.builder_list().by_section()
-    for b in opt:
-      if 'location_regexp' in b:
-        for reg in b['location_regexp']:
-          yield reg
-      if 'location_regexp_exclude' in b:
-        for reg in b['location_regexp_exclude']:
-          yield reg
-
-  def builder_list(self):
-    """Returns a list of builders."""
-    items = []
-    for b in self._config_group['verifiers'][0]['tryjob'][0]['builders']:
-      if not b['name'][0].startswith('chromium'):
-        # Buildbot builders, just ignore.
-        continue
-      items.append(b)
-    return BuilderList(items)
-
-  def get_markdown_doc(self):
-    lines = []
-    for l in MD_HEADER.split('\n'):
-      lines.append(l)
-
-    bl = self.builder_list()
-    req, opt, exp = bl.by_section()
-    for title, header, builders in (
-        ('Required builders', REQUIRED_HEADER, req),
-        ('Optional builders', OPTIONAL_HEADER, opt),
-        ('Experimental builders', EXPERIMENTAL_HEADER, exp),
-    ):
-      lines.append('## %s' % title)
-      lines.append('')
-      for l in header.strip().split('\n'):
-        lines.append(l)
-      lines.append('')
-      for b in builders:
-        buildername = b['name'][0].split('/')[-1]
-        lines.append(
-            '* [%s](%s) ([`commit-queue.cfg` entry](%s)) '
-            '([matching builders](%s))' % (
-            buildername, BUILDER_VIEW_URL + buildername,
-            CQ_CONFIG_LOCATION_URL + b['name'][0],
-            TRYBOT_SOURCE_URL + buildername))
-        lines.append('')
-        if 'comment' in b:
-          for l in b['comment'].split('\n'):
-            lines.append('  ' + l.strip())
-          lines.append('')
-        if 'location_regexp' in b:
-          lines.append('  Path regular expressions:')
-          for regex in b['location_regexp']:
-            url = None
-            if regex.startswith(REGEX_PREFIX):
-              regex = regex[len(REGEX_PREFIX):]
-            regex_title = '//' + regex.lstrip('/')
-            if regex.endswith('.+'):
-              regex = regex[:-len('.+')]
-              if all(
-                  # Equals sign and dashes used by layout tests.
-                  c in string.ascii_letters + string.digits + '/-_='
-                  for c in regex):
-                # Assume the regex is targeting a single path, direct link to
-                # it. Check to make sure we don't have weird characters, like
-                # ()|, which could mean it's a regex.
-                url = CODE_SEARCH_BASE + 'chromium/src/' + regex
-            lines.append('    * [`%s`](%s)' % (
-                regex_title, url or REGEX_SEARCH_URL + 'file:' + regex))
-          lines.append('')
-        if 'experiment_percentage' in b:
-          lines.append('  * Experimental percentage: %s' % (
-              b['experiment_percentage'][0]))
-          lines.append('')
-      lines.append('')
-
-    return '\n'.join(lines)
-
-def verify_location_regexps(regexps, verbose=True):
-  # Verify that all the regexps listed in the file have files which they could
-  # be triggered by. Failing this usually means they're old, and the code was
-  # moved somewhere, like the webkit->blink rename.
-  invalid_regexp = False
-  for regexp in regexps:
-    regexp = regexp.replace('\\\\', '')
-    assert regexp.startswith(REGEX_PREFIX), (
-        'location_regexp "%s" must start with "%s"' % (regexp, REGEX_PREFIX))
-    regexp = regexp[len(REGEX_PREFIX):]
-    # Split by path name, so that we don't have to run os.walk on the entire
-    # source tree. commit-queue.cfg always uses '/' as the path separator.
-    parts = regexp.split('/')
-    # Dash and equal sign are used by layout tests.
-    simple_name_re = re.compile(r'^[a-zA-Z0-9_\-=]*$')
-    last_normal_path = 0
-    while last_normal_path < len(parts):
-      itm = parts[last_normal_path]
-      if not simple_name_re.match(itm):
-        break
-      last_normal_path += 1
-    path_to_search = (
-        os.path.join(*parts[:last_normal_path]) if last_normal_path else '')
-    # Simple case. Regexp is just referencing a single file. Just check if the
-    # file exists.
-    if path_to_search == os.path.join(*parts) and os.path.exists(
-        os.path.join(CHROMIUM_DIR, path_to_search)):
-      continue
-
-    if os.path.sep != '/':
-      # Regular expressions require backslashes to be escaped. Need to double
-      # escape it, since the path itself has a double backslash.
-      regexp = regexp.replace('/', '\\\\')
-    compiled_regexp = re.compile(regexp)
-    found = False
-    for root, _, files in os.walk(os.path.join(CHROMIUM_DIR, path_to_search)):
-      for fname in files:
-        fullname = os.path.relpath(os.path.join(root, fname), CHROMIUM_DIR)
-        if compiled_regexp.match(fullname):
-          found = True
-          break
-      if found:
-        break
-    if not found:
-      if verbose:
-        print (
-            'Regexp %s appears to have no valid files which could match it.' % (
-                regexp))
-      invalid_regexp = True
-
-  return not invalid_regexp
-
-
-def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument(
-    '-c', '--check', action='store_true', help=
-    'Do consistency checks of commit-queue.cfg and generated files. Used '
-    'during presubmit. Causes the tool to not generate any files.')
-  args = parser.parse_args()
-
-  exit_code = 0
-
-  cfg = CQConfig.from_file(os.path.join(
-      CHROMIUM_DIR, 'infra', 'config', 'commit-queue.cfg'))
-
-  # Only force sorting on luci.chromium.try builders. Others should go away soon
-  # anyways...
-  bl = cfg.builder_list()
-  assert len(bl.builders) > 0, (
-      'Builders in \'luci.chromium.try\' bucket are missing somehow...')
-  names = [b['name'][0] for b in bl.builders]
-  bl.sort() # Changes the bl, so the next line is sorted.
-  sorted_names = [b['name'][0] for b in bl.builders]
-  if sorted_names != names:
-    print 'ERROR: commit-queue.cfg is unsorted.',
-    if args.check:
-      print
-    else:
-      print ' Please sort as follows:'
-      for line in difflib.unified_diff(
-          names,
-          sorted_names, fromfile='current', tofile='sorted'):
-        print line
-    exit_code = 1
-
-  if args.check:
-    if not verify_location_regexps(cfg.get_location_regexps()):
-      exit_code = 1
-
-    with open(os.path.join(
-        CHROMIUM_DIR, 'docs', 'infra', 'cq_builders.md')) as f:
-      if cfg.get_markdown_doc() != f.read():
-        print (
-            'Markdown file is out of date. Please run '
-            '`//infra/config/cq_cfg_presubmit.py` to regenerate the '
-            'docs.')
-        exit_code = 1
-  else:
-    with open(os.path.join(
-        CHROMIUM_DIR, 'docs', 'infra', 'cq_builders.md'), 'w') as f:
-      f.write(cfg.get_markdown_doc())
-
-  return exit_code
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/infra/config/cq_cfg_presubmit_unittest.py b/infra/config/cq_cfg_presubmit_unittest.py
deleted file mode 100755
index 032d5ae..0000000
--- a/infra/config/cq_cfg_presubmit_unittest.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env vpython
-# Copyright (c) 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""Tests for cq_cfg_presubmit."""
-
-import mock
-import os
-import unittest
-
-import cq_cfg_presubmit
-
-
-class CqCfgPresubmitTest(unittest.TestCase):
-  def test_verify_location_regexp_exists(self):
-    with mock.patch('cq_cfg_presubmit.os.path.exists') as exists:
-      exists.side_effect = [True]
-      self.assertTrue(cq_cfg_presubmit.verify_location_regexps([
-          cq_cfg_presubmit.REGEX_PREFIX + 'simple/file',
-      ]))
-
-  def test_verify_location_regexp_os_walk_found(self):
-    with mock.patch('cq_cfg_presubmit.os.walk') as walk:
-      walk.side_effect = [(
-          (os.path.join(cq_cfg_presubmit.CHROMIUM_DIR, 'random'),
-            None, ['test.txt'],),
-          (os.path.join(cq_cfg_presubmit.CHROMIUM_DIR, 'simple', 'file'),
-           None, ['test.txt'],),
-      )]
-      with mock.patch('cq_cfg_presubmit.os.path.exists') as exists:
-        exists.side_effect = [False]
-        self.assertTrue(cq_cfg_presubmit.verify_location_regexps([
-            cq_cfg_presubmit.REGEX_PREFIX + 'simple/file/.+',
-        ], False))
-
-  def test_verify_location_regexp_os_walk_not_found(self):
-    with mock.patch('cq_cfg_presubmit.os.walk') as walk:
-      walk.side_effect = [(
-          (os.path.join(cq_cfg_presubmit.CHROMIUM_DIR, 'random'),
-            None, ['test.txt'],),
-      )]
-      with mock.patch('cq_cfg_presubmit.os.path.exists') as exists:
-        exists.side_effect = [False]
-        self.assertFalse(cq_cfg_presubmit.verify_location_regexps([
-            cq_cfg_presubmit.REGEX_PREFIX + 'simple/file/.+',
-        ], False))
-
-
-if __name__ == '__main__':
-  unittest.main()
diff --git a/infra/config/cr-buildbucket-dev.cfg b/infra/config/cr-buildbucket-dev.cfg
deleted file mode 100644
index 192a573..0000000
--- a/infra/config/cr-buildbucket-dev.cfg
+++ /dev/null
@@ -1,207 +0,0 @@
-# Defines buckets on cr-buildbucket-dev.appspot.com, used by to schedule builds
-# on buildbot. In particular, CQ uses some of these buckets to schedule tryjobs.
-#
-# See http://luci-config.appspot.com/schemas/projects:buildbucket.cfg for
-# schema of this file and documentation. Also see README.md in this dir.
-#
-# Please keep this list sorted by bucket name.
-
-acl_sets {
-  # This is pure-LUCI w/o buildbot.
-  name: "default"
-  acls {
-    role: READER
-    group: "all"
-  }
-  acls {
-    role: SCHEDULER
-    identity: "luci-scheduler-dev@appspot.gserviceaccount.com"
-  }
-}
-
-acl_sets {
-  name: "tryserver"
-  acls {
-    role: READER
-    group: "all"
-  }
-  acls {
-    role: SCHEDULER
-    group: "project-chromium-tryjob-access"
-  }
-  acls {
-    role: SCHEDULER
-    group: "service-account-cq"
-  }
-  acls {
-    role: SCHEDULER
-    identity: "findit-for-me@appspot.gserviceaccount.com"
-  }
-  acls {
-    role: WRITER
-    group: "service-account-chromium-tryserver"
-  }
-}
-
-builder_mixins {
-  name: "swarm-ci"
-  dimensions: "cpu:x86-64"
-  recipe {
-    name: "swarming/staging"
-    properties: "mastername:chromium.swarm"
-  }
-}
-
-builder_mixins {
-  name: "xcode-mac-9a235"
-  caches: {
-    name: "xcode_mac_9a235"
-    path: "xcode_mac_9a235.app"
-  }
-  recipe {
-    properties_j: <<END
-    $depot_tools/osx_sdk: {
-      "sdk_version": "9a235"
-    }
-    END
-  }
-}
-
-buckets {
-  name: "luci.chromium.ci"
-
-  acl_sets: "default"
-  acls {
-    role: SCHEDULER
-    # Support builder triggering other builders in the same bucket.
-    identity: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
-  }
-  acls {
-    # Allow task force to bump next build number.
-    role: WRITER
-    group: "google/luci-task-force@google.com"
-  }
-
-  swarming {
-    hostname: "chromium-swarm-dev.appspot.com"
-
-    builder_defaults {
-      category: "Chromium"
-      execution_timeout_secs: 10800  # 3h
-      service_account: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      build_numbers: YES
-      recipe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        name: "chromium"
-      }
-    }
-    builders {
-      name: "Android N5 Swarm"
-      dimensions: "os:Ubuntu-16.04"
-      mixins: "swarm-ci"
-    }
-    builders {
-      name: "Android N5X Swarm"
-      dimensions: "os:Ubuntu-16.04"
-      mixins: "swarm-ci"
-    }
-    builders {
-      name: "ChromeOS Swarm"
-      dimensions: "os:Ubuntu-16.04"
-      mixins: "swarm-ci"
-    }
-    builders {
-      name: "Linux Swarm"
-      dimensions: "os:Ubuntu-16.04"
-      mixins: "swarm-ci"
-    }
-    builders {
-      name: "Mac Swarm"
-      dimensions: "os:Mac-10.13"
-      mixins: "swarm-ci"
-    }
-    builders {
-      name: "Windows Swarm"
-      dimensions: "os:Windows-10"
-      mixins: "swarm-ci"
-    }
-  }
-}
-
-buckets {
-    name: "luci.chromium.cron"
-    acl_sets: "default"
-    acls {
-      role: SCHEDULER
-      identity: "snapshot-builder@chops-service-accounts.iam.gserviceaccount.com"
-   }
-
-    swarming {
-      hostname: "chromium-swarm-dev.appspot.com"
-
-      builders {
-        name: "Snapshot Builder"
-        dimensions: "os:Ubuntu-16.04"
-        execution_timeout_secs: 3600
-        service_account: "snapshot-builder@chops-service-accounts.iam.gserviceaccount.com"
-        recipe {
-          cipd_package: "infra/recipe_bundles/chromium.googlesource.com/infra/infra"
-          cipd_version: "refs/heads/master"
-          name: "snapshots/builder"
-        }
-      }
-
-      builders {
-        name: "Snapshots"
-        execution_timeout_secs: 3600
-        recipe {
-          cipd_package: "infra/recipe_bundles/chromium.googlesource.com/infra/infra"
-          cipd_version: "refs/heads/master"
-          name: "snapshots/snapshot"
-        }
-      }
-    }
-}
-
-buckets {
-  name: "try"
-
-  acl_sets: "tryserver"
-
-  swarming {
-    hostname: "chromium-swarm-dev.appspot.com"
-
-    builder_defaults {
-      category: "Chromium CQ"
-      service_account: "chromium-try-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
-      # Max. pending time for builds. CQ considers builds pending >2h as timed
-      # out: http://shortn/_8PaHsdYmlq. Keep this in sync.
-      expiration_secs: 7200 # 2h
-      execution_timeout_secs: 14400  # 4h
-      swarming_tags: "vpython:native-python-wrapper"
-      build_numbers: YES
-      # Adds dimension: "builder:<builder name>" to ensure builder affinity.
-      # To "assign" a bot to a builder, bot config of swarming service should
-      # add the same dimension to the bot.
-      auto_builder_dimension: YES
-      recipe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        name: "chromium_trybot"
-        properties_j: "$kitchen:{\"git_auth\": true, \"devshell\": true}"
-      }
-    }
-
-    builders {
-      name: "mac_upload_clang"
-      dimensions: "os:Mac"
-      mixins: "xcode-mac-9a235"
-      recipe {
-        name: "chromium_upload_clang"
-        properties: "mastername:tryserver.chromium.mac"
-      }
-    }
-  }
-}
diff --git a/infra/config/dev.star b/infra/config/dev.star
index 136dc5d7..cf8cd2b 100755
--- a/infra/config/dev.star
+++ b/infra/config/dev.star
@@ -17,14 +17,15 @@
 # Copy the not-yet migrated files to the generated outputs
 # TODO(https://crbug.com/1011908) Migrate the configuration in these files to starlark
 [lucicfg.emit(dest = f, data = io.read_file(f)) for f in (
-    'cr-buildbucket-dev.cfg',
     'luci-milo-dev.cfg',
     'luci-scheduler-dev.cfg',
 )]
 
 luci.project(
     name = 'chromium',
+    buildbucket = 'cr-buildbucket-dev.appspot.com',
     logdog = 'luci-logdog-dev.appspot.com',
+    swarming = 'chromium-swarm-dev.appspot.com',
     acls = [
         acl.entry(
             roles = [
@@ -43,3 +44,7 @@
 luci.logdog(
     gs_bucket = 'chromium-luci-logdog',
 )
+
+exec('//dev/buckets/ci.star')
+exec('//dev/buckets/cron.star')
+exec('//dev/buckets/try.star')
diff --git a/infra/config/dev/buckets/ci.star b/infra/config/dev/buckets/ci.star
new file mode 100644
index 0000000..45241fb
--- /dev/null
+++ b/infra/config/dev/buckets/ci.star
@@ -0,0 +1,65 @@
+load('//lib/builders.star', 'builder', 'cpu', 'defaults', 'os')
+
+luci.bucket(
+    name = 'ci',
+    acls = [
+        acl.entry(
+            roles = acl.BUILDBUCKET_READER,
+            groups = 'all',
+        ),
+        acl.entry(
+            roles = acl.BUILDBUCKET_TRIGGERER,
+            users = [
+                'luci-scheduler-dev@appspot.gserviceaccount.com',
+                'chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com',
+            ],
+        ),
+        acl.entry(
+            roles = acl.BUILDBUCKET_OWNER,
+            groups = 'google/luci-task-force@google.com',
+        ),
+    ],
+)
+
+luci.recipe.defaults.cipd_package.set(
+    'infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build')
+
+
+defaults.bucket.set('ci')
+defaults.build_numbers.set(True)
+defaults.builderless.set(None)
+defaults.cpu.set(cpu.X86_64)
+defaults.executable.set(luci.recipe(name = 'swarming/staging'))
+defaults.execution_timeout.set(3 * time.hour)
+defaults.mastername.set('chromium.swarm')
+defaults.os.set(os.LINUX_DEFAULT)
+defaults.service_account.set(
+    'chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com')
+defaults.swarming_tags.set(['vpython:native-python-wrapper'])
+
+
+builder(
+    name = 'Android N5 Swarm',
+)
+
+builder(
+    name = 'Android N5X Swarm',
+)
+
+builder(
+    name = 'ChromeOS Swarm',
+)
+
+builder(
+    name = 'Linux Swarm',
+)
+
+builder(
+    name = 'Mac Swarm',
+    os = os.MAC_DEFAULT,
+)
+
+builder(
+    name = 'Windows Swarm',
+    os = os.WINDOWS_DEFAULT,
+)
diff --git a/infra/config/dev/buckets/cron.star b/infra/config/dev/buckets/cron.star
new file mode 100644
index 0000000..2cdd719b
--- /dev/null
+++ b/infra/config/dev/buckets/cron.star
@@ -0,0 +1,39 @@
+load('//lib/builders.star', 'builder', 'defaults', 'os')
+
+luci.bucket(
+    name = 'cron',
+    acls = [
+        acl.entry(
+            roles = acl.BUILDBUCKET_READER,
+            groups = 'all',
+        ),
+        acl.entry(
+            roles = acl.BUILDBUCKET_TRIGGERER,
+            users = [
+                'luci-scheduler-dev@appspot.gserviceaccount.com',
+                'snapshot-builder@chops-service-accounts.iam.gserviceaccount.com',
+            ],
+        ),
+    ],
+)
+
+
+luci.recipe.defaults.cipd_package.set(
+    'infra/recipe_bundles/chromium.googlesource.com/infra/infra')
+
+defaults.bucket.set('cron')
+defaults.builderless.set(None)
+defaults.execution_timeout.set(time.hour)
+
+
+builder(
+    name = 'Snapshot Builder',
+    executable = luci.recipe(name = 'snapshots/builder'),
+    os = os.LINUX_DEFAULT,
+    service_account = 'snapshot-builder@chops-service-accounts.iam.gserviceaccount.com',
+)
+
+builder(
+    name = 'Snapshots',
+    executable = luci.recipe(name = 'snapshots/snapshot'),
+)
diff --git a/infra/config/dev/buckets/try.star b/infra/config/dev/buckets/try.star
new file mode 100644
index 0000000..0466bff
--- /dev/null
+++ b/infra/config/dev/buckets/try.star
@@ -0,0 +1,57 @@
+load('//lib/builders.star', 'builder', 'defaults', 'os')
+
+luci.bucket(
+    name = 'try',
+    acls = [
+        acl.entry(
+            roles = acl.BUILDBUCKET_READER,
+            groups = 'all',
+        ),
+        acl.entry(
+            roles = acl.BUILDBUCKET_TRIGGERER,
+            groups = [
+                'project-chromium-tryjob-access',
+                'service-account-cq',
+            ],
+            users = 'findit-for-me@appspot.gserviceaccount.com',
+        ),
+        acl.entry(
+            roles = acl.BUILDBUCKET_OWNER,
+            groups = 'service-account-chromium-tryserver',
+        ),
+    ],
+)
+
+
+luci.recipe.defaults.cipd_package.set(
+    'infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build')
+
+defaults.bucket.set('try')
+defaults.build_numbers.set(True)
+defaults.configure_kitchen.set(True)
+defaults.execution_timeout.set(4 * time.hour)
+# Max. pending time for builds. CQ considers builds pending >2h as timed
+# out: http://shortn/_8PaHsdYmlq. Keep this in sync.
+defaults.expiration_timeout.set(2 * time.hour)
+defaults.service_account.set(
+    'chromium-try-builder-dev@chops-service-accounts.iam.gserviceaccount.com')
+defaults.swarming_tags.set(['vpython:native-python-wrapper'])
+
+
+builder(
+    name = 'mac_upload_clang',
+    caches = [
+        swarming.cache(
+            name = 'xcode_mac_9a235',
+            path = 'xcode_mac_9a235.app',
+        ),
+    ],
+    executable = luci.recipe(name = 'chromium_upload_clang'),
+    mastername = 'tryserver.chromium.mac',
+    os = os.MAC_ANY,
+    properties = {
+        '$depot_tools/osx_sdk': {
+            'sdk_version': '9a235',
+        },
+    },
+)
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index 4a9b36a..78ad8242 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -1,160 +1,68 @@
-# See http://luci-config.appspot.com/schemas/projects:commit-queue.cfg for the
-# documentation of this file format.
+# Auto-generated by lucicfg.
+# Do not modify manually.
 #
-# This file is also used to auto generate //docs/infra/cq_builders.md. If you
-# change this file, run //infra/config/cq_cfg_presubmit.py, which will generate
-# that file. That script also requires that the builders in this file remain
-# sorted. The script is invoked via presubmit, and will complain if this file is
-# changed but the documentation isn't.
-#
-# The auto generated file copies comments made to builders in this file. If you
-# comment on the line directly above a builder, that comment will get copied to
-# the documentation.
-#
-# The following comment will get copied.
-#
-# # This is a great builder!
-# builders { name: "chromium_presubmit" }
-#
-# The following comment will not get copied.
-#
-# # This is a ok builder!
-#
-# builders { name: "chromium_presubmit" }
+# For the schema of this file, see Config message:
+#   https://luci-config.appspot.com/schemas/projects:commit-queue.cfg
 
 cq_status_host: "chromium-cq-status.appspot.com"
-submit_options {
+submit_options: <
   max_burst: 2
-  burst_delay {
+  burst_delay: <
     seconds: 60
-  }
-}
-
-# NOTE: To add a new builder to the commit queue, see
-# https://chromium.googlesource.com/chromium/src/+/HEAD/docs/infra/cq.md#how-do-i-add-a-new-builder-to-the-cq
-
-config_groups {
-  gerrit {
+  >
+>
+config_groups: <
+  gerrit: <
     url: "https://chromium-review.googlesource.com"
-    projects {
+    projects: <
       name: "chromium/src"
       ref_regexp: "refs/heads/.+"
-    }
-  }
-  verifiers {
-    gerrit_cq_ability {
+    >
+  >
+  verifiers: <
+    gerrit_cq_ability: <
       committer_list: "project-chromium-committers"
       dry_run_access_list: "project-chromium-tryjob-access"
-    }
-    tree_status {
+    >
+    tree_status: <
       url: "https://chromium-status.appspot.com/"
-    }
-    tryjob {
-      # TODO(crbug/959436): enable it.
-      cancel_stale_tryjobs: NO
-      #############################
-      # Always required builders. #
-      #############################
-
-      builders {
+    >
+    tryjob: <
+      builders: <
         name: "chromium/try/android-binary-size"
-      }
-      builders {
-        name: "chromium/try/android-kitkat-arm-rel"
-      }
-      builders {
-        name: "chromium/try/android-marshmallow-arm64-rel"
-      }
-      builders {
-        name: "chromium/try/android_arm64_dbg_recipe"
-      }
-      builders {
-        name: "chromium/try/android_clang_dbg_recipe"
-      }
-      builders {
-        name: "chromium/try/android_compile_dbg"
-      }
-      builders {
-        name: "chromium/try/android_cronet"
-      }
-      builders {
-        name: "chromium/try/cast_shell_android"
-      }
-      builders {
-        name: "chromium/try/cast_shell_linux"
-      }
-      builders {
-        name: "chromium/try/chromeos-amd64-generic-rel"
-      }
-      builders {
-        name: "chromium/try/chromeos-arm-generic-rel"
-      }
-      builders {
-        name: "chromium/try/chromium_presubmit"
-        disable_reuse: true
-      }
-      builders {
-        name: "chromium/try/fuchsia_arm64"
-      }
-      builders {
-        name: "chromium/try/fuchsia_x64"
-      }
-      builders {
-        name: "chromium/try/ios-simulator"
-      }
-      builders {
-        name: "chromium/try/linux-chromeos-compile-dbg"
-      }
-      builders {
-        name: "chromium/try/linux-chromeos-rel"
-      }
-      builders {
-        name: "chromium/try/linux-libfuzzer-asan-rel"
-      }
-      builders {
-        name: "chromium/try/linux-ozone-rel"
-      }
-      builders {
-        name: "chromium/try/linux-rel"
-      }
-      builders {
-        name: "chromium/try/linux_chromium_asan_rel_ng"
-      }
-      builders {
-        name: "chromium/try/linux_chromium_compile_dbg_ng"
-      }
-      builders {
-        name: "chromium/try/linux_chromium_tsan_rel_ng"
-      }
-      builders {
-        name: "chromium/try/mac-rel"
-      }
-      builders {
-        name: "chromium/try/mac_chromium_compile_dbg_ng"
-      }
-      builders {
-        name: "chromium/try/win-libfuzzer-asan-rel"
-      }
-      builders {
-        name: "chromium/try/win10_chromium_x64_rel_ng"
-      }
-      builders {
-        name: "chromium/try/win_chromium_compile_dbg_ng"
-      }
-
-      ######################
-      # Optional builders. #
-      ######################
-
-      builders {
+      >
+      builders: <
         name: "chromium/try/android-cronet-arm-dbg"
         location_regexp: ".+/[+]/components/cronet/.+"
         location_regexp: ".+/[+]/components/grpc_support/.+"
         location_regexp: ".+/[+]/build/android/.+"
         location_regexp: ".+/[+]/build/config/android/.+"
         location_regexp_exclude: ".+/[+]/components/cronet/ios/.+"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/android-kitkat-arm-rel"
+      >
+      builders: <
+        name: "chromium/try/android-marshmallow-arm64-coverage-rel"
+        experiment_percentage: 20
+      >
+      builders: <
+        name: "chromium/try/android-marshmallow-arm64-rel"
+      >
+      builders: <
+        name: "chromium/try/android-pie-arm64-rel"
+        experiment_percentage: 50
+      >
+      builders: <
+        name: "chromium/try/android_arm64_dbg_recipe"
+      >
+      builders: <
+        name: "chromium/try/android_clang_dbg_recipe"
+      >
+      builders: <
+        name: "chromium/try/android_compile_dbg"
+      >
+      builders: <
         name: "chromium/try/android_compile_x64_dbg"
         location_regexp: ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+"
         location_regexp: ".+/[+]/chrome/browser/vr/.+"
@@ -163,8 +71,8 @@
         location_regexp: ".+/[+]/sandbox/linux/system_headers/.+"
         location_regexp: ".+/[+]/sandbox/linux/tests/.+"
         location_regexp: ".+/[+]/third_party/gvr-android-sdk/.+"
-      }
-      builders {
+      >
+      builders: <
         name: "chromium/try/android_compile_x86_dbg"
         location_regexp: ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+"
         location_regexp: ".+/[+]/chrome/browser/vr/.+"
@@ -173,8 +81,11 @@
         location_regexp: ".+/[+]/sandbox/linux/system_headers/.+"
         location_regexp: ".+/[+]/sandbox/linux/tests/.+"
         location_regexp: ".+/[+]/third_party/gvr-android-sdk/.+"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/android_cronet"
+      >
+      builders: <
         name: "chromium/try/android_optional_gpu_tests_rel"
         location_regexp: ".+/[+]/cc/.+"
         location_regexp: ".+/[+]/chrome/browser/vr/.+"
@@ -189,96 +100,138 @@
         location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgl/.+"
         location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+"
         location_regexp: ".+/[+]/ui/gl/.+"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/cast_shell_android"
+      >
+      builders: <
+        name: "chromium/try/cast_shell_linux"
+      >
+      builders: <
         name: "chromium/try/chromeos-amd64-generic-dbg"
         location_regexp: ".+/[+]/content/gpu/.+"
         location_regexp: ".+/[+]/media/.+"
-
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/chromeos-amd64-generic-rel"
+      >
+      builders: <
+        name: "chromium/try/chromeos-arm-generic-rel"
+      >
+      builders: <
         name: "chromium/try/chromeos-kevin-compile-rel"
         location_regexp: ".+/[+]/chromeos/CHROMEOS_LKGM"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/chromeos-kevin-experimental-rel"
+        experiment_percentage: 5
+      >
+      builders: <
         name: "chromium/try/chromeos-kevin-rel"
         location_regexp: ".+/[+]/build/chromeos/.+"
         location_regexp: ".+/[+]/build/config/chromeos/.*"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/chromium_presubmit"
+        disable_reuse: true
+      >
+      builders: <
         name: "chromium/try/closure_compilation"
         location_regexp: ".+/[+]/third_party/closure_compiler/.+"
-      }
-      builders {
+      >
+      builders: <
         name: "chromium/try/dawn-linux-x64-deps-rel"
         location_regexp: ".+/[+]/gpu/.+"
         location_regexp: ".+/[+]/testing/buildbot/chromium.dawn.json"
         location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+"
+        location_regexp: ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
         location_regexp: ".+/[+]/third_party/dawn/.+"
-      }
-      builders {
+      >
+      builders: <
         name: "chromium/try/dawn-mac-x64-deps-rel"
         location_regexp: ".+/[+]/gpu/.+"
         location_regexp: ".+/[+]/testing/buildbot/chromium.dawn.json"
         location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+"
+        location_regexp: ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
         location_regexp: ".+/[+]/third_party/dawn/.+"
-      }
-      builders {
+      >
+      builders: <
         name: "chromium/try/dawn-win10-x64-deps-rel"
         location_regexp: ".+/[+]/gpu/.+"
         location_regexp: ".+/[+]/testing/buildbot/chromium.dawn.json"
         location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+"
+        location_regexp: ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
         location_regexp: ".+/[+]/third_party/dawn/.+"
-      }
-      builders {
+      >
+      builders: <
         name: "chromium/try/dawn-win10-x86-deps-rel"
         location_regexp: ".+/[+]/gpu/.+"
         location_regexp: ".+/[+]/testing/buildbot/chromium.dawn.json"
         location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+"
+        location_regexp: ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
         location_regexp: ".+/[+]/third_party/dawn/.+"
-      }
-      builders {
+      >
+      builders: <
         name: "chromium/try/fuchsia-arm64-cast"
         location_regexp: ".+/[+]/chromecast/.+"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/fuchsia-compile-x64-dbg"
+        experiment_percentage: 5
+      >
+      builders: <
         name: "chromium/try/fuchsia-x64-cast"
         location_regexp: ".+/[+]/chromecast/.+"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/fuchsia_arm64"
+      >
+      builders: <
+        name: "chromium/try/fuchsia_x64"
+      >
+      builders: <
         name: "chromium/try/gpu-fyi-try-android-p-pixel-2-skv-32"
-	# Some locations disabled due to limited capacity.
-        #location_regexp: ".+/[+]/cc/.+"
         location_regexp: ".+/[+]/components/viz/.+"
         location_regexp: ".+/[+]/content/test/gpu/gpu_tests/.+py"
         location_regexp: ".+/[+]/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt"
         location_regexp: ".+/[+]/gpu/vulkan/.+"
-        #location_regexp: ".+/[+]/media/gpu/.+"
         location_regexp: ".+/[+]/services/viz/.+"
-        #location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+"
-        #location_regexp: ".+/[+]/third_party/skia/src/gpu/.+"
-        #location_regexp: ".+/[+]/third_party/skia/include/gpu/.+"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/ios-device"
+        experiment_percentage: 10
+      >
+      builders: <
+        name: "chromium/try/ios-device-xcode-clang"
+        experiment_percentage: 10
+      >
+      builders: <
+        name: "chromium/try/ios-simulator"
+      >
+      builders: <
         name: "chromium/try/ios-simulator-cronet"
         location_regexp: ".+/[+]/components/cronet/.+"
         location_regexp: ".+/[+]/components/grpc_support/.+"
         location_regexp: ".+/[+]/ios/.+"
         location_regexp_exclude: ".+/[+]/components/cronet/android/.+"
-      }
-      builders {
+      >
+      builders: <
         name: "chromium/try/ios-simulator-full-configs"
         location_regexp: ".+/[+]/ios/.+"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/ios-simulator-xcode-clang"
+        experiment_percentage: 10
+      >
+      builders: <
         name: "chromium/try/linux-blink-rel"
         location_regexp: ".+/[+]/cc/.+"
         location_regexp: ".+/[+]/third_party/blink/renderer/core/paint/.+"
@@ -286,20 +239,44 @@
         location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint"
         location_regexp: ".+/[+]/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/linux-chromeos-compile-dbg"
+      >
+      builders: <
+        name: "chromium/try/linux-chromeos-rel"
+      >
+      builders: <
+        name: "chromium/try/linux-libfuzzer-asan-rel"
+      >
+      builders: <
+        name: "chromium/try/linux-ozone-rel"
+      >
+      builders: <
+        name: "chromium/try/linux-rel"
+      >
+      builders: <
+        name: "chromium/try/linux_chromium_asan_rel_ng"
+      >
+      builders: <
+        name: "chromium/try/linux_chromium_compile_dbg_ng"
+      >
+      builders: <
         name: "chromium/try/linux_chromium_dbg_ng"
         location_regexp: ".+/[+]/build/.*check_gn_headers.*"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/linux_chromium_tsan_rel_ng"
+      >
+      builders: <
         name: "chromium/try/linux_layout_tests_composite_after_paint"
         location_regexp: ".+/[+]/third_party/blink/renderer/core/paint/.+"
         location_regexp: ".+/[+]/third_party/blink/renderer/core/svg/.+"
         location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint"
         location_regexp: ".+/[+]/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+"
-      }
-      builders {
+      >
+      builders: <
         name: "chromium/try/linux_layout_tests_layout_ng_disabled"
         location_regexp: ".+/[+]/third_party/blink/renderer/core/editing/.+"
         location_regexp: ".+/[+]/third_party/blink/renderer/core/layout/.+"
@@ -308,8 +285,8 @@
         location_regexp: ".+/[+]/third_party/blink/renderer/platform/fonts/shaping/.+"
         location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/.+"
-      }
-      builders {
+      >
+      builders: <
         name: "chromium/try/linux_optional_gpu_tests_rel"
         location_regexp: ".+/[+]/chrome/browser/vr/.+"
         location_regexp: ".+/[+]/content/test/gpu/.+"
@@ -322,12 +299,18 @@
         location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgl/.+"
         location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+"
         location_regexp: ".+/[+]/ui/gl/.+"
-      }
-      builders {
+      >
+      builders: <
         name: "chromium/try/linux_vr"
         location_regexp: ".+/[+]/chrome/browser/vr/.+"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/mac-rel"
+      >
+      builders: <
+        name: "chromium/try/mac_chromium_compile_dbg_ng"
+      >
+      builders: <
         name: "chromium/try/mac_optional_gpu_tests_rel"
         location_regexp: ".+/[+]/chrome/browser/vr/.+"
         location_regexp: ".+/[+]/content/test/gpu/.+"
@@ -341,8 +324,17 @@
         location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgl/.+"
         location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+"
         location_regexp: ".+/[+]/ui/gl/.+"
-      }
-      builders {
+      >
+      builders: <
+        name: "chromium/try/win-libfuzzer-asan-rel"
+      >
+      builders: <
+        name: "chromium/try/win10_chromium_x64_rel_ng"
+      >
+      builders: <
+        name: "chromium/try/win_chromium_compile_dbg_ng"
+      >
+      builders: <
         name: "chromium/try/win_optional_gpu_tests_rel"
         location_regexp: ".+/[+]/chrome/browser/vr/.+"
         location_regexp: ".+/[+]/content/test/gpu/.+"
@@ -358,95 +350,47 @@
         location_regexp: ".+/[+]/third_party/blink/renderer/modules/xr/.+"
         location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+"
         location_regexp: ".+/[+]/ui/gl/.+"
-      }
-
-      ##########################
-      # Experimental builders. #
-      ##########################
-
-      builders {
-        name: "chromium/try/android-marshmallow-arm64-coverage-rel"
-        experiment_percentage: 20
-      }
-      builders {
-        name: "chromium/try/android-pie-arm64-rel"
-        experiment_percentage: 50
-      }
-      builders {
-        name: "chromium/try/chromeos-kevin-experimental-rel"
-        experiment_percentage: 5
-      }
-      builders {
-        name: "chromium/try/fuchsia-compile-x64-dbg"
-        experiment_percentage: 5
-      }
-      # https://crbug.com/739556; make this non-experimental ASAP.
-      builders {
-        name: "chromium/try/ios-device"
-        experiment_percentage: 10
-      }
-      # https://crbug.com/739556
-      builders {
-        name: "chromium/try/ios-device-xcode-clang"
-        experiment_percentage: 10
-      }
-      # https://crbug.com/739556
-      builders {
-        name: "chromium/try/ios-simulator-xcode-clang"
-        experiment_percentage: 10
-      }
-
-      retry_config {
+      >
+      retry_config: <
         single_quota: 1
         global_quota: 2
         failure_weight: 1
         transient_failure_weight: 1
         timeout_weight: 2
-      }
-    }
-  }
-}
-
-# Config group for tryjobs for branch-heads.
-# Will be removed when it has converged with
-# the refs/heads group.
-config_groups {
-  gerrit {
+      >
+      cancel_stale_tryjobs: NO
+    >
+  >
+>
+config_groups: <
+  gerrit: <
     url: "https://chromium-review.googlesource.com"
-    projects {
+    projects: <
       name: "chromium/src"
       ref_regexp: "refs/branch-heads/.+"
-    }
-  }
-  verifiers {
-    gerrit_cq_ability {
+    >
+  >
+  verifiers: <
+    gerrit_cq_ability: <
       committer_list: "project-chromium-committers"
       dry_run_access_list: "project-chromium-tryjob-access"
-    }
-    tree_status {
+    >
+    tree_status: <
       url: "https://chromium-status.appspot.com/"
-    }
-    tryjob {
-      # TODO(crbug/959436): enable it.
-      cancel_stale_tryjobs: NO
-      ###########################################
-      # Experimental builders for branch-heads. #
-      ###########################################
-      builders {
+    >
+    tryjob: <
+      builders: <
         name: "chromium/try/linux-rel"
         experiment_percentage: 100
-      }
-
-      retry_config {
+      >
+      retry_config: <
         single_quota: 1
         global_quota: 2
         failure_weight: 1
         transient_failure_weight: 1
         timeout_weight: 2
-      }
-    }
-  }
-}
-
-# NOTE: To add a new builder to the commit queue, see
-# https://chromium.googlesource.com/chromium/src/+/HEAD/docs/infra/cq.md#how-do-i-add-a-new-builder-to-the-cq
+      >
+      cancel_stale_tryjobs: NO
+    >
+  >
+>
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
new file mode 100644
index 0000000..73af2b54
--- /dev/null
+++ b/infra/config/generated/cq-builders.md
@@ -0,0 +1,344 @@
+<!-- Auto-generated by lucicfg (via cq-builders-md.star). -->
+<!-- Do not modify manually. -->
+
+# List of CQ builders
+
+[TOC]
+
+Each builder name links to that builder on Milo. The "matching builders" links
+point to the file used to determine which configurations a builder should copy
+when running. These links might 404 or error; they are hard-coded right now,
+using common assumptions about how builders are configured.
+
+## Required builders
+These builders must pass before a CL may land.
+
+* [android-binary-size](https://ci.chromium.org/p/chromium/builders/try/android-binary-size) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android-binary-size)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android-binary-size))
+
+* [android-kitkat-arm-rel](https://ci.chromium.org/p/chromium/builders/try/android-kitkat-arm-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android-kitkat-arm-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android-kitkat-arm-rel))
+
+* [android-marshmallow-arm64-rel](https://ci.chromium.org/p/chromium/builders/try/android-marshmallow-arm64-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android-marshmallow-arm64-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android-marshmallow-arm64-rel))
+
+* [android_arm64_dbg_recipe](https://ci.chromium.org/p/chromium/builders/try/android_arm64_dbg_recipe) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android_arm64_dbg_recipe)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android_arm64_dbg_recipe))
+
+* [android_clang_dbg_recipe](https://ci.chromium.org/p/chromium/builders/try/android_clang_dbg_recipe) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android_clang_dbg_recipe)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android_clang_dbg_recipe))
+
+* [android_compile_dbg](https://ci.chromium.org/p/chromium/builders/try/android_compile_dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android_compile_dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android_compile_dbg))
+
+* [android_cronet](https://ci.chromium.org/p/chromium/builders/try/android_cronet) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android_cronet)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android_cronet))
+
+* [cast_shell_android](https://ci.chromium.org/p/chromium/builders/try/cast_shell_android) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+cast_shell_android)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+cast_shell_android))
+
+* [cast_shell_linux](https://ci.chromium.org/p/chromium/builders/try/cast_shell_linux) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+cast_shell_linux)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+cast_shell_linux))
+
+* [chromeos-amd64-generic-rel](https://ci.chromium.org/p/chromium/builders/try/chromeos-amd64-generic-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+chromeos-amd64-generic-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+chromeos-amd64-generic-rel))
+
+* [chromeos-arm-generic-rel](https://ci.chromium.org/p/chromium/builders/try/chromeos-arm-generic-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+chromeos-arm-generic-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+chromeos-arm-generic-rel))
+
+* [chromium_presubmit](https://ci.chromium.org/p/chromium/builders/try/chromium_presubmit) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+chromium_presubmit)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+chromium_presubmit))
+
+* [fuchsia_arm64](https://ci.chromium.org/p/chromium/builders/try/fuchsia_arm64) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+fuchsia_arm64)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+fuchsia_arm64))
+
+* [fuchsia_x64](https://ci.chromium.org/p/chromium/builders/try/fuchsia_x64) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+fuchsia_x64)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+fuchsia_x64))
+
+* [ios-simulator](https://ci.chromium.org/p/chromium/builders/try/ios-simulator) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+ios-simulator)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+ios-simulator))
+
+* [linux-chromeos-compile-dbg](https://ci.chromium.org/p/chromium/builders/try/linux-chromeos-compile-dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux-chromeos-compile-dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux-chromeos-compile-dbg))
+
+* [linux-chromeos-rel](https://ci.chromium.org/p/chromium/builders/try/linux-chromeos-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux-chromeos-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux-chromeos-rel))
+
+* [linux-libfuzzer-asan-rel](https://ci.chromium.org/p/chromium/builders/try/linux-libfuzzer-asan-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux-libfuzzer-asan-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux-libfuzzer-asan-rel))
+
+* [linux-ozone-rel](https://ci.chromium.org/p/chromium/builders/try/linux-ozone-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux-ozone-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux-ozone-rel))
+
+* [linux-rel](https://ci.chromium.org/p/chromium/builders/try/linux-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux-rel))
+
+* [linux_chromium_asan_rel_ng](https://ci.chromium.org/p/chromium/builders/try/linux_chromium_asan_rel_ng) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux_chromium_asan_rel_ng)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux_chromium_asan_rel_ng))
+
+* [linux_chromium_compile_dbg_ng](https://ci.chromium.org/p/chromium/builders/try/linux_chromium_compile_dbg_ng) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux_chromium_compile_dbg_ng)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux_chromium_compile_dbg_ng))
+
+* [linux_chromium_tsan_rel_ng](https://ci.chromium.org/p/chromium/builders/try/linux_chromium_tsan_rel_ng) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux_chromium_tsan_rel_ng)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux_chromium_tsan_rel_ng))
+
+* [mac-rel](https://ci.chromium.org/p/chromium/builders/try/mac-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+mac-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+mac-rel))
+
+* [mac_chromium_compile_dbg_ng](https://ci.chromium.org/p/chromium/builders/try/mac_chromium_compile_dbg_ng) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+mac_chromium_compile_dbg_ng)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+mac_chromium_compile_dbg_ng))
+
+* [win-libfuzzer-asan-rel](https://ci.chromium.org/p/chromium/builders/try/win-libfuzzer-asan-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+win-libfuzzer-asan-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+win-libfuzzer-asan-rel))
+
+* [win10_chromium_x64_rel_ng](https://ci.chromium.org/p/chromium/builders/try/win10_chromium_x64_rel_ng) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+win10_chromium_x64_rel_ng)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+win10_chromium_x64_rel_ng))
+
+* [win_chromium_compile_dbg_ng](https://ci.chromium.org/p/chromium/builders/try/win_chromium_compile_dbg_ng) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+win_chromium_compile_dbg_ng)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+win_chromium_compile_dbg_ng))
+
+
+## Optional builders
+These builders optionally run, depending on the files in a CL. For example, a CL
+which touches `//gpu/BUILD.gn` would trigger the builder
+`android_optional_gpu_tests_rel`, due to the `location_regexp` values for that
+builder.
+
+* [android-cronet-arm-dbg](https://ci.chromium.org/p/chromium/builders/try/android-cronet-arm-dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android-cronet-arm-dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android-cronet-arm-dbg))
+
+  Path regular expressions:
+  * [`//components/cronet/.+`](https://cs.chromium.org/chromium/src/components/cronet/)
+  * [`//components/grpc_support/.+`](https://cs.chromium.org/chromium/src/components/grpc_support/)
+  * [`//build/android/.+`](https://cs.chromium.org/chromium/src/build/android/)
+  * [`//build/config/android/.+`](https://cs.chromium.org/chromium/src/build/config/android/)
+
+  Path exclude regular expressions:
+  * [`//components/cronet/ios/.+`](https://cs.chromium.org/chromium/src/components/cronet/ios/)
+
+* [android_compile_x64_dbg](https://ci.chromium.org/p/chromium/builders/try/android_compile_x64_dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android_compile_x64_dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android_compile_x64_dbg))
+
+  Path regular expressions:
+  * [`//chrome/android/java/src/org/chromium/chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser/vr/)
+  * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+  * [`//sandbox/linux/seccomp-bpf/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf/)
+  * [`//sandbox/linux/seccomp-bpf-helpers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf-helpers/)
+  * [`//sandbox/linux/system_headers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/system_headers/)
+  * [`//sandbox/linux/tests/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/tests/)
+  * [`//third_party/gvr-android-sdk/.+`](https://cs.chromium.org/chromium/src/third_party/gvr-android-sdk/)
+
+* [android_compile_x86_dbg](https://ci.chromium.org/p/chromium/builders/try/android_compile_x86_dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android_compile_x86_dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android_compile_x86_dbg))
+
+  Path regular expressions:
+  * [`//chrome/android/java/src/org/chromium/chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser/vr/)
+  * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+  * [`//sandbox/linux/seccomp-bpf/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf/)
+  * [`//sandbox/linux/seccomp-bpf-helpers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf-helpers/)
+  * [`//sandbox/linux/system_headers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/system_headers/)
+  * [`//sandbox/linux/tests/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/tests/)
+  * [`//third_party/gvr-android-sdk/.+`](https://cs.chromium.org/chromium/src/third_party/gvr-android-sdk/)
+
+* [android_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/try/android_optional_gpu_tests_rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android_optional_gpu_tests_rel))
+
+  Path regular expressions:
+  * [`//cc/.+`](https://cs.chromium.org/chromium/src/cc/)
+  * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+  * [`//components/viz/.+`](https://cs.chromium.org/chromium/src/components/viz/)
+  * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
+  * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+  * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
+  * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
+  * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
+  * [`//services/viz/.+`](https://cs.chromium.org/chromium/src/services/viz/)
+  * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
+  * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
+  * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/)
+  * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
+
+* [chromeos-amd64-generic-dbg](https://ci.chromium.org/p/chromium/builders/try/chromeos-amd64-generic-dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+chromeos-amd64-generic-dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+chromeos-amd64-generic-dbg))
+
+  Path regular expressions:
+  * [`//content/gpu/.+`](https://cs.chromium.org/chromium/src/content/gpu/)
+  * [`//media/.+`](https://cs.chromium.org/chromium/src/media/)
+
+* [chromeos-kevin-compile-rel](https://ci.chromium.org/p/chromium/builders/try/chromeos-kevin-compile-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+chromeos-kevin-compile-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+chromeos-kevin-compile-rel))
+
+  Path regular expressions:
+  * [`//chromeos/CHROMEOS_LKGM`](https://cs.chromium.org/chromium/src/chromeos/CHROMEOS_LKGM)
+
+* [chromeos-kevin-rel](https://ci.chromium.org/p/chromium/builders/try/chromeos-kevin-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+chromeos-kevin-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+chromeos-kevin-rel))
+
+  Path regular expressions:
+  * [`//build/chromeos/.+`](https://cs.chromium.org/chromium/src/build/chromeos/)
+  * [`//build/config/chromeos/.*`](https://cs.chromium.org/search?q=package:%5Echromium$+file:build/config/chromeos/.*)
+
+* [closure_compilation](https://ci.chromium.org/p/chromium/builders/try/closure_compilation) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+closure_compilation)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+closure_compilation))
+
+  Path regular expressions:
+  * [`//third_party/closure_compiler/.+`](https://cs.chromium.org/chromium/src/third_party/closure_compiler/)
+
+* [dawn-linux-x64-deps-rel](https://ci.chromium.org/p/chromium/builders/try/dawn-linux-x64-deps-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+dawn-linux-x64-deps-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+dawn-linux-x64-deps-rel))
+
+  Path regular expressions:
+  * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+  * [`//testing/buildbot/chromium.dawn.json`](https://cs.chromium.org/search?q=package:%5Echromium$+file:testing/buildbot/chromium.dawn.json)
+  * [`//third_party/blink/renderer/modules/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgpu/)
+  * [`//third_party/blink/web_tests/external/wpt/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/external/wpt/webgpu/)
+  * [`//third_party/blink/web_tests/wpt_internal/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/wpt_internal/webgpu/)
+  * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/WebGPUExpectations)
+  * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
+
+* [dawn-mac-x64-deps-rel](https://ci.chromium.org/p/chromium/builders/try/dawn-mac-x64-deps-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+dawn-mac-x64-deps-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+dawn-mac-x64-deps-rel))
+
+  Path regular expressions:
+  * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+  * [`//testing/buildbot/chromium.dawn.json`](https://cs.chromium.org/search?q=package:%5Echromium$+file:testing/buildbot/chromium.dawn.json)
+  * [`//third_party/blink/renderer/modules/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgpu/)
+  * [`//third_party/blink/web_tests/external/wpt/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/external/wpt/webgpu/)
+  * [`//third_party/blink/web_tests/wpt_internal/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/wpt_internal/webgpu/)
+  * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/WebGPUExpectations)
+  * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
+
+* [dawn-win10-x64-deps-rel](https://ci.chromium.org/p/chromium/builders/try/dawn-win10-x64-deps-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+dawn-win10-x64-deps-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+dawn-win10-x64-deps-rel))
+
+  Path regular expressions:
+  * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+  * [`//testing/buildbot/chromium.dawn.json`](https://cs.chromium.org/search?q=package:%5Echromium$+file:testing/buildbot/chromium.dawn.json)
+  * [`//third_party/blink/renderer/modules/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgpu/)
+  * [`//third_party/blink/web_tests/external/wpt/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/external/wpt/webgpu/)
+  * [`//third_party/blink/web_tests/wpt_internal/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/wpt_internal/webgpu/)
+  * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/WebGPUExpectations)
+  * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
+
+* [dawn-win10-x86-deps-rel](https://ci.chromium.org/p/chromium/builders/try/dawn-win10-x86-deps-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+dawn-win10-x86-deps-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+dawn-win10-x86-deps-rel))
+
+  Path regular expressions:
+  * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+  * [`//testing/buildbot/chromium.dawn.json`](https://cs.chromium.org/search?q=package:%5Echromium$+file:testing/buildbot/chromium.dawn.json)
+  * [`//third_party/blink/renderer/modules/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgpu/)
+  * [`//third_party/blink/web_tests/external/wpt/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/external/wpt/webgpu/)
+  * [`//third_party/blink/web_tests/wpt_internal/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/wpt_internal/webgpu/)
+  * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/WebGPUExpectations)
+  * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
+
+* [fuchsia-arm64-cast](https://ci.chromium.org/p/chromium/builders/try/fuchsia-arm64-cast) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+fuchsia-arm64-cast)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+fuchsia-arm64-cast))
+
+  Path regular expressions:
+  * [`//chromecast/.+`](https://cs.chromium.org/chromium/src/chromecast/)
+
+* [fuchsia-x64-cast](https://ci.chromium.org/p/chromium/builders/try/fuchsia-x64-cast) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+fuchsia-x64-cast)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+fuchsia-x64-cast))
+
+  Path regular expressions:
+  * [`//chromecast/.+`](https://cs.chromium.org/chromium/src/chromecast/)
+
+* [gpu-fyi-try-android-p-pixel-2-skv-32](https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-android-p-pixel-2-skv-32) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+gpu-fyi-try-android-p-pixel-2-skv-32)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+gpu-fyi-try-android-p-pixel-2-skv-32))
+
+  Path regular expressions:
+  * [`//components/viz/.+`](https://cs.chromium.org/chromium/src/components/viz/)
+  * [`//content/test/gpu/gpu_tests/.+py`](https://cs.chromium.org/search?q=package:%5Echromium$+file:content/test/gpu/gpu_tests/.+py)
+  * [`//content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt`](https://cs.chromium.org/search?q=package:%5Echromium$+file:content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt)
+  * [`//gpu/vulkan/.+`](https://cs.chromium.org/chromium/src/gpu/vulkan/)
+  * [`//services/viz/.+`](https://cs.chromium.org/chromium/src/services/viz/)
+
+* [ios-simulator-cronet](https://ci.chromium.org/p/chromium/builders/try/ios-simulator-cronet) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+ios-simulator-cronet)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+ios-simulator-cronet))
+
+  Path regular expressions:
+  * [`//components/cronet/.+`](https://cs.chromium.org/chromium/src/components/cronet/)
+  * [`//components/grpc_support/.+`](https://cs.chromium.org/chromium/src/components/grpc_support/)
+  * [`//ios/.+`](https://cs.chromium.org/chromium/src/ios/)
+
+  Path exclude regular expressions:
+  * [`//components/cronet/android/.+`](https://cs.chromium.org/chromium/src/components/cronet/android/)
+
+* [ios-simulator-full-configs](https://ci.chromium.org/p/chromium/builders/try/ios-simulator-full-configs) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+ios-simulator-full-configs)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+ios-simulator-full-configs))
+
+  Path regular expressions:
+  * [`//ios/.+`](https://cs.chromium.org/chromium/src/ios/)
+
+* [linux-blink-rel](https://ci.chromium.org/p/chromium/builders/try/linux-blink-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux-blink-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux-blink-rel))
+
+  Path regular expressions:
+  * [`//cc/.+`](https://cs.chromium.org/chromium/src/cc/)
+  * [`//third_party/blink/renderer/core/paint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/paint/)
+  * [`//third_party/blink/renderer/core/svg/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/svg/)
+  * [`//third_party/blink/renderer/platform/graphics/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/)
+  * [`//third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint)
+  * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/)
+
+* [linux_chromium_dbg_ng](https://ci.chromium.org/p/chromium/builders/try/linux_chromium_dbg_ng) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux_chromium_dbg_ng)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux_chromium_dbg_ng))
+
+  Path regular expressions:
+  * [`//build/.*check_gn_headers.*`](https://cs.chromium.org/search?q=package:%5Echromium$+file:build/.*check_gn_headers.*)
+
+* [linux_layout_tests_composite_after_paint](https://ci.chromium.org/p/chromium/builders/try/linux_layout_tests_composite_after_paint) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux_layout_tests_composite_after_paint)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux_layout_tests_composite_after_paint))
+
+  Path regular expressions:
+  * [`//third_party/blink/renderer/core/paint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/paint/)
+  * [`//third_party/blink/renderer/core/svg/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/svg/)
+  * [`//third_party/blink/renderer/platform/graphics/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/)
+  * [`//third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint)
+  * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/)
+
+* [linux_layout_tests_layout_ng_disabled](https://ci.chromium.org/p/chromium/builders/try/linux_layout_tests_layout_ng_disabled) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux_layout_tests_layout_ng_disabled)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux_layout_tests_layout_ng_disabled))
+
+  Path regular expressions:
+  * [`//third_party/blink/renderer/core/editing/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/)
+  * [`//third_party/blink/renderer/core/layout/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/layout/)
+  * [`//third_party/blink/renderer/core/paint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/paint/)
+  * [`//third_party/blink/renderer/core/svg/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/svg/)
+  * [`//third_party/blink/renderer/platform/fonts/shaping/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/)
+  * [`//third_party/blink/renderer/platform/graphics/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/)
+  * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/)
+
+* [linux_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/try/linux_optional_gpu_tests_rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux_optional_gpu_tests_rel))
+
+  Path regular expressions:
+  * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+  * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
+  * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+  * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
+  * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
+  * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
+  * [`//testing/buildbot/chromium.gpu.fyi.json`](https://cs.chromium.org/search?q=package:%5Echromium$+file:testing/buildbot/chromium.gpu.fyi.json)
+  * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
+  * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
+  * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/)
+  * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
+
+* [linux_vr](https://ci.chromium.org/p/chromium/builders/try/linux_vr) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux_vr)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux_vr))
+
+  Path regular expressions:
+  * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+
+* [mac_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/try/mac_optional_gpu_tests_rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+mac_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+mac_optional_gpu_tests_rel))
+
+  Path regular expressions:
+  * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+  * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
+  * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+  * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
+  * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
+  * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
+  * [`//services/shape_detection/.+`](https://cs.chromium.org/chromium/src/services/shape_detection/)
+  * [`//testing/buildbot/chromium.gpu.fyi.json`](https://cs.chromium.org/search?q=package:%5Echromium$+file:testing/buildbot/chromium.gpu.fyi.json)
+  * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
+  * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
+  * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/)
+  * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
+
+* [win_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/try/win_optional_gpu_tests_rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+win_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+win_optional_gpu_tests_rel))
+
+  Path regular expressions:
+  * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+  * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
+  * [`//device/vr/.+`](https://cs.chromium.org/chromium/src/device/vr/)
+  * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+  * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
+  * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
+  * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
+  * [`//testing/buildbot/chromium.gpu.fyi.json`](https://cs.chromium.org/search?q=package:%5Echromium$+file:testing/buildbot/chromium.gpu.fyi.json)
+  * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
+  * [`//third_party/blink/renderer/modules/vr/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/vr/)
+  * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
+  * [`//third_party/blink/renderer/modules/xr/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/xr/)
+  * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/)
+  * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
+
+
+## Experimental builders
+These builders are run on some percentage of builds. Their results are ignored
+by CQ. These are often used to test new configurations before they are added
+as required builders.
+
+* [android-marshmallow-arm64-coverage-rel](https://ci.chromium.org/p/chromium/builders/try/android-marshmallow-arm64-coverage-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android-marshmallow-arm64-coverage-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android-marshmallow-arm64-coverage-rel))
+  * Experiment percentage: 20
+
+* [android-pie-arm64-rel](https://ci.chromium.org/p/chromium/builders/try/android-pie-arm64-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android-pie-arm64-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android-pie-arm64-rel))
+  * Experiment percentage: 50
+
+* [chromeos-kevin-experimental-rel](https://ci.chromium.org/p/chromium/builders/try/chromeos-kevin-experimental-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+chromeos-kevin-experimental-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+chromeos-kevin-experimental-rel))
+  * Experiment percentage: 5
+
+* [fuchsia-compile-x64-dbg](https://ci.chromium.org/p/chromium/builders/try/fuchsia-compile-x64-dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+fuchsia-compile-x64-dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+fuchsia-compile-x64-dbg))
+  * Experiment percentage: 5
+
+* [ios-device](https://ci.chromium.org/p/chromium/builders/try/ios-device) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+ios-device)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+ios-device))
+  * Experiment percentage: 10
+
+* [ios-device-xcode-clang](https://ci.chromium.org/p/chromium/builders/try/ios-device-xcode-clang) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+ios-device-xcode-clang)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+ios-device-xcode-clang))
+  * Experiment percentage: 10
+
+* [ios-simulator-xcode-clang](https://ci.chromium.org/p/chromium/builders/try/ios-simulator-xcode-clang) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+ios-simulator-xcode-clang)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+ios-simulator-xcode-clang))
+  * Experiment percentage: 10
+
diff --git a/infra/config/generated/cr-buildbucket-dev.cfg b/infra/config/generated/cr-buildbucket-dev.cfg
index 192a573..d41cbcf 100644
--- a/infra/config/generated/cr-buildbucket-dev.cfg
+++ b/infra/config/generated/cr-buildbucket-dev.cfg
@@ -1,207 +1,207 @@
-# Defines buckets on cr-buildbucket-dev.appspot.com, used by to schedule builds
-# on buildbot. In particular, CQ uses some of these buckets to schedule tryjobs.
+# Auto-generated by lucicfg.
+# Do not modify manually.
 #
-# See http://luci-config.appspot.com/schemas/projects:buildbucket.cfg for
-# schema of this file and documentation. Also see README.md in this dir.
-#
-# Please keep this list sorted by bucket name.
+# For the schema of this file, see BuildbucketCfg message:
+#   https://luci-config.appspot.com/schemas/projects:buildbucket.cfg
 
-acl_sets {
-  # This is pure-LUCI w/o buildbot.
-  name: "default"
-  acls {
-    role: READER
-    group: "all"
-  }
-  acls {
-    role: SCHEDULER
-    identity: "luci-scheduler-dev@appspot.gserviceaccount.com"
-  }
-}
-
-acl_sets {
-  name: "tryserver"
-  acls {
-    role: READER
-    group: "all"
-  }
-  acls {
-    role: SCHEDULER
-    group: "project-chromium-tryjob-access"
-  }
-  acls {
-    role: SCHEDULER
-    group: "service-account-cq"
-  }
-  acls {
-    role: SCHEDULER
-    identity: "findit-for-me@appspot.gserviceaccount.com"
-  }
-  acls {
-    role: WRITER
-    group: "service-account-chromium-tryserver"
-  }
-}
-
-builder_mixins {
-  name: "swarm-ci"
-  dimensions: "cpu:x86-64"
-  recipe {
-    name: "swarming/staging"
-    properties: "mastername:chromium.swarm"
-  }
-}
-
-builder_mixins {
-  name: "xcode-mac-9a235"
-  caches: {
-    name: "xcode_mac_9a235"
-    path: "xcode_mac_9a235.app"
-  }
-  recipe {
-    properties_j: <<END
-    $depot_tools/osx_sdk: {
-      "sdk_version": "9a235"
-    }
-    END
-  }
-}
-
-buckets {
-  name: "luci.chromium.ci"
-
-  acl_sets: "default"
-  acls {
-    role: SCHEDULER
-    # Support builder triggering other builders in the same bucket.
-    identity: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
-  }
-  acls {
-    # Allow task force to bump next build number.
+buckets: <
+  name: "ci"
+  acls: <
     role: WRITER
     group: "google/luci-task-force@google.com"
-  }
-
-  swarming {
-    hostname: "chromium-swarm-dev.appspot.com"
-
-    builder_defaults {
-      category: "Chromium"
-      execution_timeout_secs: 10800  # 3h
-      service_account: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      build_numbers: YES
-      recipe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        name: "chromium"
-      }
-    }
-    builders {
+  >
+  acls: <
+    group: "all"
+  >
+  acls: <
+    role: SCHEDULER
+    identity: "user:chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
+  >
+  acls: <
+    role: SCHEDULER
+    identity: "user:luci-scheduler-dev@appspot.gserviceaccount.com"
+  >
+  swarming: <
+    builders: <
       name: "Android N5 Swarm"
-      dimensions: "os:Ubuntu-16.04"
-      mixins: "swarm-ci"
-    }
-    builders {
-      name: "Android N5X Swarm"
-      dimensions: "os:Ubuntu-16.04"
-      mixins: "swarm-ci"
-    }
-    builders {
-      name: "ChromeOS Swarm"
-      dimensions: "os:Ubuntu-16.04"
-      mixins: "swarm-ci"
-    }
-    builders {
-      name: "Linux Swarm"
-      dimensions: "os:Ubuntu-16.04"
-      mixins: "swarm-ci"
-    }
-    builders {
-      name: "Mac Swarm"
-      dimensions: "os:Mac-10.13"
-      mixins: "swarm-ci"
-    }
-    builders {
-      name: "Windows Swarm"
-      dimensions: "os:Windows-10"
-      mixins: "swarm-ci"
-    }
-  }
-}
-
-buckets {
-    name: "luci.chromium.cron"
-    acl_sets: "default"
-    acls {
-      role: SCHEDULER
-      identity: "snapshot-builder@chops-service-accounts.iam.gserviceaccount.com"
-   }
-
-    swarming {
-      hostname: "chromium-swarm-dev.appspot.com"
-
-      builders {
-        name: "Snapshot Builder"
-        dimensions: "os:Ubuntu-16.04"
-        execution_timeout_secs: 3600
-        service_account: "snapshot-builder@chops-service-accounts.iam.gserviceaccount.com"
-        recipe {
-          cipd_package: "infra/recipe_bundles/chromium.googlesource.com/infra/infra"
-          cipd_version: "refs/heads/master"
-          name: "snapshots/builder"
-        }
-      }
-
-      builders {
-        name: "Snapshots"
-        execution_timeout_secs: 3600
-        recipe {
-          cipd_package: "infra/recipe_bundles/chromium.googlesource.com/infra/infra"
-          cipd_version: "refs/heads/master"
-          name: "snapshots/snapshot"
-        }
-      }
-    }
-}
-
-buckets {
-  name: "try"
-
-  acl_sets: "tryserver"
-
-  swarming {
-    hostname: "chromium-swarm-dev.appspot.com"
-
-    builder_defaults {
-      category: "Chromium CQ"
-      service_account: "chromium-try-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
-      # Max. pending time for builds. CQ considers builds pending >2h as timed
-      # out: http://shortn/_8PaHsdYmlq. Keep this in sync.
-      expiration_secs: 7200 # 2h
-      execution_timeout_secs: 14400  # 4h
+      swarming_host: "chromium-swarm-dev.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
-      build_numbers: YES
-      # Adds dimension: "builder:<builder name>" to ensure builder affinity.
-      # To "assign" a bot to a builder, bot config of swarming service should
-      # add the same dimension to the bot.
-      auto_builder_dimension: YES
-      recipe {
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      recipe: <
+        name: "swarming/staging"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
-        name: "chromium_trybot"
-        properties_j: "$kitchen:{\"git_auth\": true, \"devshell\": true}"
-      }
-    }
-
-    builders {
+        properties_j: "mastername:\"chromium.swarm\""
+      >
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
+    >
+    builders: <
+      name: "Android N5X Swarm"
+      swarming_host: "chromium-swarm-dev.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      recipe: <
+        name: "swarming/staging"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "mastername:\"chromium.swarm\""
+      >
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
+    >
+    builders: <
+      name: "ChromeOS Swarm"
+      swarming_host: "chromium-swarm-dev.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      recipe: <
+        name: "swarming/staging"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "mastername:\"chromium.swarm\""
+      >
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
+    >
+    builders: <
+      name: "Linux Swarm"
+      swarming_host: "chromium-swarm-dev.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      recipe: <
+        name: "swarming/staging"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "mastername:\"chromium.swarm\""
+      >
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
+    >
+    builders: <
+      name: "Mac Swarm"
+      swarming_host: "chromium-swarm-dev.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Mac-10.13"
+      recipe: <
+        name: "swarming/staging"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "mastername:\"chromium.swarm\""
+      >
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
+    >
+    builders: <
+      name: "Windows Swarm"
+      swarming_host: "chromium-swarm-dev.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows-10"
+      recipe: <
+        name: "swarming/staging"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "mastername:\"chromium.swarm\""
+      >
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
+    >
+  >
+>
+buckets: <
+  name: "cron"
+  acls: <
+    group: "all"
+  >
+  acls: <
+    role: SCHEDULER
+    identity: "user:luci-scheduler-dev@appspot.gserviceaccount.com"
+  >
+  acls: <
+    role: SCHEDULER
+    identity: "user:snapshot-builder@chops-service-accounts.iam.gserviceaccount.com"
+  >
+  swarming: <
+    builders: <
+      name: "Snapshot Builder"
+      swarming_host: "chromium-swarm-dev.appspot.com"
+      dimensions: "os:Ubuntu-16.04"
+      recipe: <
+        name: "snapshots/builder"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/infra/infra"
+        cipd_version: "refs/heads/master"
+      >
+      execution_timeout_secs: 3600
+      service_account: "snapshot-builder@chops-service-accounts.iam.gserviceaccount.com"
+    >
+    builders: <
+      name: "Snapshots"
+      swarming_host: "chromium-swarm-dev.appspot.com"
+      recipe: <
+        name: "snapshots/snapshot"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/infra/infra"
+        cipd_version: "refs/heads/master"
+      >
+      execution_timeout_secs: 3600
+    >
+  >
+>
+buckets: <
+  name: "try"
+  acls: <
+    role: WRITER
+    group: "service-account-chromium-tryserver"
+  >
+  acls: <
+    group: "all"
+  >
+  acls: <
+    role: SCHEDULER
+    identity: "user:findit-for-me@appspot.gserviceaccount.com"
+  >
+  acls: <
+    role: SCHEDULER
+    group: "project-chromium-tryjob-access"
+  >
+  acls: <
+    role: SCHEDULER
+    group: "service-account-cq"
+  >
+  swarming: <
+    builders: <
       name: "mac_upload_clang"
+      swarming_host: "chromium-swarm-dev.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builder:mac_upload_clang"
       dimensions: "os:Mac"
-      mixins: "xcode-mac-9a235"
-      recipe {
+      recipe: <
         name: "chromium_upload_clang"
-        properties: "mastername:tryserver.chromium.mac"
-      }
-    }
-  }
-}
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "$depot_tools/osx_sdk:{\"sdk_version\":\"9a235\"}"
+        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "mastername:\"tryserver.chromium.mac\""
+      >
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      caches: <
+        name: "xcode_mac_9a235"
+        path: "xcode_mac_9a235.app"
+      >
+      build_numbers: YES
+      service_account: "chromium-try-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
+    >
+  >
+>
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 681e0ae..a0bc942d 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -2581,6 +2581,7 @@
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.gpu\""
       >
@@ -4034,6 +4035,7 @@
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.mac\""
       >
@@ -4052,6 +4054,7 @@
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.mac\""
       >
@@ -4234,66 +4237,6 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
     >
     builders: <
-      name: "Mac FYI 10.14 Release (AMD)"
-      swarming_host: "chromium-swarm.appspot.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builderless:1"
-      dimensions: "cores:2"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
-      dimensions: "ssd:0"
-      recipe: <
-        name: "chromium"
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
-        properties_j: "mastername:\"chromium.gpu.fyi\""
-      >
-      execution_timeout_secs: 21600
-      build_numbers: YES
-      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
-    >
-    builders: <
-      name: "Mac FYI 10.14 Release (Intel)"
-      swarming_host: "chromium-swarm.appspot.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builderless:1"
-      dimensions: "cores:2"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
-      dimensions: "ssd:0"
-      recipe: <
-        name: "chromium"
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
-        properties_j: "mastername:\"chromium.gpu.fyi\""
-      >
-      execution_timeout_secs: 21600
-      build_numbers: YES
-      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
-    >
-    builders: <
-      name: "Mac FYI 10.14 Release (NVIDIA)"
-      swarming_host: "chromium-swarm.appspot.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builderless:1"
-      dimensions: "cores:2"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
-      dimensions: "ssd:0"
-      recipe: <
-        name: "chromium"
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
-        properties_j: "mastername:\"chromium.gpu.fyi\""
-      >
-      execution_timeout_secs: 21600
-      build_numbers: YES
-      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
-    >
-    builders: <
       name: "Mac FYI Debug (Intel)"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -9122,6 +9065,7 @@
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
         properties_j: "$build/code_coverage:{\"use_clang_coverage\":true}"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.fyi\""
       >
@@ -9141,6 +9085,7 @@
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.fyi\""
       >
@@ -9179,6 +9124,7 @@
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.fyi\""
       >
@@ -9199,6 +9145,7 @@
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.fyi\""
       >
@@ -9841,6 +9788,34 @@
       >
     >
     builders: <
+      name: "android-opus-kitkat-arm-rel"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "ssd:0"
+      recipe: <
+        name: "chromium_trybot"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "mastername:\"tryserver.chromium.android\""
+      >
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      caches: <
+        name: "win_toolchain"
+        path: "win_toolchain"
+      >
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage: <
+        value: 5
+      >
+    >
+    builders: <
       name: "android-oreo-arm64-cts-networkservice-dbg"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -14900,7 +14875,7 @@
         name: "chromium_trybot"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
-        properties_j: "$build/goma:{\"jobs\":150}"
+        properties_j: "$build/goma:{\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"tryserver.chromium.mac\""
       >
@@ -15058,6 +15033,7 @@
         name: "chromium_trybot"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"tryserver.chromium.mac\""
       >
@@ -15084,6 +15060,7 @@
         name: "chromium_trybot"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"tryserver.chromium.mac\""
       >
@@ -15191,7 +15168,7 @@
         name: "chromium_trybot"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
-        properties_j: "$build/goma:{\"jobs\":150}"
+        properties_j: "$build/goma:{\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"tryserver.chromium.mac\""
       >
@@ -15218,6 +15195,7 @@
         name: "chromium_trybot"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"tryserver.chromium.mac\""
       >
@@ -15244,6 +15222,7 @@
         name: "chromium_trybot"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"tryserver.chromium.mac\""
       >
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index 501fade4..bd9d7fe 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -3123,6 +3123,45 @@
     category: "week3b|linux"
     short_name: "pie"
   }
+  builders: {
+    name: "buildbucket/luci.chromium.ci/mac-code-coverage-generation"
+    category: "week3c|mac"
+    short_name: "code"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/mac-hermetic-upgrade-rel"
+    category: "week3c|mac"
+    short_name: "herm"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/mac-jumbo-rel"
+    category: "week3c|mac"
+    short_name: "jumbo"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/mac-mojo-rel"
+    category: "week3c|mac"
+    short_name: "mojo"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/mac-osxbeta-rel"
+    category: "week3c|mac"
+    short_name: "osx"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Mac Builder"
+    category: "week3c|mac"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Mac Builder (dbg)"
+    category: "week3c|mac"
+    short_name: "dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/GPU Mac Builder"
+    category: "week3c|mac"
+    short_name: "gpu"
+  }
   builders {
     name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux ASan"
     category: "week4|linux"
@@ -3546,11 +3585,6 @@
     short_name: "rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Mac FYI 10.14 Release (Intel)"
-    category: "Mac|Intel|10.14"
-    short_name: "rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Mac Pro FYI Release (AMD)"
     category: "Mac|AMD|Pro"
     short_name: "rel"
@@ -3576,11 +3610,6 @@
     short_name: "exp"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Mac FYI 10.14 Release (AMD)"
-    category: "Mac|AMD|10.14"
-    short_name: "rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Mac FYI Retina Debug (NVIDIA)"
     category: "Mac|Nvidia"
     short_name: "dbg"
@@ -3596,11 +3625,6 @@
     short_name: "exp"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Mac FYI 10.14 Release (NVIDIA)"
-    category: "Mac|Nvidia|10.14"
-    short_name: "rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Mac FYI GPU ASAN Release"
     category: "Mac"
     short_name: "asn"
@@ -4067,6 +4091,9 @@
   refs: "refs/heads/master"
   manifest_name: "REVISION"
   builders {
+    name: "buildbucket/luci.chromium.try/android-opus-kitkat-arm-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android_archive_rel_ng"
   }
   builders {
diff --git a/infra/config/generated/luci-notify.cfg b/infra/config/generated/luci-notify.cfg
index 7e5000b..4174ba7 100644
--- a/infra/config/generated/luci-notify.cfg
+++ b/infra/config/generated/luci-notify.cfg
@@ -91,12 +91,10 @@
     name: "Libfuzzer Upload Linux UBSan"
     bucket: "ci"
   }
-  # TODO(crbug.com/790372): Enable notifications for the mac bot when it gets
-  # flipped to luci.
-  #builders {
-  #  name: "Libfuzzer Upload Mac ASan"
-  #  bucket: "ci"
-  #}
+  builders {
+   name: "Libfuzzer Upload Mac ASan"
+   bucket: "ci"
+  }
   builders {
     name: "Libfuzzer Upload Windows ASan"
     bucket: "ci"
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index dc87013..fc64b784 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -2310,39 +2310,6 @@
 }
 
 job {
-  id: "Mac FYI 10.14 Release (AMD)"
-  # Triggered by "GPU FYI Mac Builder"
-  acl_sets: "triggered-by-parent-builders"
-  buildbucket: {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "Mac FYI 10.14 Release (AMD)"
-  }
-}
-
-job {
-  id: "Mac FYI 10.14 Release (Intel)"
-  # Triggered by "GPU FYI Mac Builder"
-  acl_sets: "triggered-by-parent-builders"
-  buildbucket: {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "Mac FYI 10.14 Release (Intel)"
-  }
-}
-
-job {
-  id: "Mac FYI 10.14 Release (NVIDIA)"
-  # Triggered by "GPU FYI Mac Builder"
-  acl_sets: "triggered-by-parent-builders"
-  buildbucket: {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "Mac FYI 10.14 Release (NVIDIA)"
-  }
-}
-
-job {
   id: "Mac FYI Experimental Release (Intel)"
   # Triggered by "GPU FYI Mac Builder".
   acl_sets: "triggered-by-parent-builders"
diff --git a/infra/config/lib/builders.star b/infra/config/lib/builders.star
index f404b52..1857aadb 100644
--- a/infra/config/lib/builders.star
+++ b/infra/config/lib/builders.star
@@ -20,6 +20,10 @@
 """
 
 
+################################################################################
+# Constants for use with the builder function                                  #
+################################################################################
+
 # The cpu constants to be used with the builder function
 cpu = struct(
     X86 = 'x86',
@@ -95,6 +99,10 @@
 )
 
 
+################################################################################
+# Implementation details                                                       #
+################################################################################
+
 _DEFAULT_BUILDERLESS_OSES = [os.LINUX_TRUSTY, os.LINUX_XENIAL]
 
 
@@ -145,6 +153,10 @@
   return code_coverage or None
 
 
+################################################################################
+# Builder defaults and function                                                #
+################################################################################
+
 # The module-level defaults to use with the builder function
 defaults = struct(
     # Our custom arguments
@@ -256,10 +268,10 @@
       to be used by the builder. Sets the 'jobs' field of the '$build/goma'
       property will be set according to the enum member. By default, the 'jobs'
       considered None.
-    * use_clang_covergae - a boolean indicating whether clang coverage should be
+    * use_clang_coverage - a boolean indicating whether clang coverage should be
       used. If True, the 'use_clang_coverage" field will be set in the
       '$build/code_coverage' property. By default, considered False.
-    * use_java_covergae - a boolean indicating whether java coverage should be
+    * use_java_coverage - a boolean indicating whether java coverage should be
       used. If True, the 'use_java_coverage" field will be set in the
       '$build/code_coverage' property. By default, considered False.
     * kwargs - Additional keyword arguments to forward on to `luci.builder`.
@@ -274,13 +286,13 @@
 
   properties = kwargs.pop('properties', {})
   if '$kitchen' in properties:
-    fail('Explicitly specifying "$kitchen" property is not supported: '
+    fail('Setting "$kitchen" property is not supported: '
          + 'use configure_kitchen instead')
   if '$build/goma' in properties:
-    fail('Explicitly specifying "$build/goma" property is not supported: '
+    fail('Setting "$build/goma" property is not supported: '
          + 'use goma_backend, goma_dbug, goma_enable_ats and goma_jobs instead')
   if '$build/code_coverage' in properties:
-    fail('Explicitly specifying "$build/code_coverage" property is not supported: '
+    fail('Setting "$build/code_coverage" property is not supported: '
          + 'use use_clang_coverage and use_java_coverage instead')
   properties = dict(properties)
 
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index 501fade4..bd9d7fe 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -3123,6 +3123,45 @@
     category: "week3b|linux"
     short_name: "pie"
   }
+  builders: {
+    name: "buildbucket/luci.chromium.ci/mac-code-coverage-generation"
+    category: "week3c|mac"
+    short_name: "code"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/mac-hermetic-upgrade-rel"
+    category: "week3c|mac"
+    short_name: "herm"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/mac-jumbo-rel"
+    category: "week3c|mac"
+    short_name: "jumbo"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/mac-mojo-rel"
+    category: "week3c|mac"
+    short_name: "mojo"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/mac-osxbeta-rel"
+    category: "week3c|mac"
+    short_name: "osx"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Mac Builder"
+    category: "week3c|mac"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Mac Builder (dbg)"
+    category: "week3c|mac"
+    short_name: "dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/GPU Mac Builder"
+    category: "week3c|mac"
+    short_name: "gpu"
+  }
   builders {
     name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux ASan"
     category: "week4|linux"
@@ -3546,11 +3585,6 @@
     short_name: "rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Mac FYI 10.14 Release (Intel)"
-    category: "Mac|Intel|10.14"
-    short_name: "rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Mac Pro FYI Release (AMD)"
     category: "Mac|AMD|Pro"
     short_name: "rel"
@@ -3576,11 +3610,6 @@
     short_name: "exp"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Mac FYI 10.14 Release (AMD)"
-    category: "Mac|AMD|10.14"
-    short_name: "rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Mac FYI Retina Debug (NVIDIA)"
     category: "Mac|Nvidia"
     short_name: "dbg"
@@ -3596,11 +3625,6 @@
     short_name: "exp"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Mac FYI 10.14 Release (NVIDIA)"
-    category: "Mac|Nvidia|10.14"
-    short_name: "rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Mac FYI GPU ASAN Release"
     category: "Mac"
     short_name: "asn"
@@ -4067,6 +4091,9 @@
   refs: "refs/heads/master"
   manifest_name: "REVISION"
   builders {
+    name: "buildbucket/luci.chromium.try/android-opus-kitkat-arm-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android_archive_rel_ng"
   }
   builders {
diff --git a/infra/config/luci-notify.cfg b/infra/config/luci-notify.cfg
index 7e5000b..4174ba7 100644
--- a/infra/config/luci-notify.cfg
+++ b/infra/config/luci-notify.cfg
@@ -91,12 +91,10 @@
     name: "Libfuzzer Upload Linux UBSan"
     bucket: "ci"
   }
-  # TODO(crbug.com/790372): Enable notifications for the mac bot when it gets
-  # flipped to luci.
-  #builders {
-  #  name: "Libfuzzer Upload Mac ASan"
-  #  bucket: "ci"
-  #}
+  builders {
+   name: "Libfuzzer Upload Mac ASan"
+   bucket: "ci"
+  }
   builders {
     name: "Libfuzzer Upload Windows ASan"
     bucket: "ci"
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg
index dc87013..fc64b784 100644
--- a/infra/config/luci-scheduler.cfg
+++ b/infra/config/luci-scheduler.cfg
@@ -2310,39 +2310,6 @@
 }
 
 job {
-  id: "Mac FYI 10.14 Release (AMD)"
-  # Triggered by "GPU FYI Mac Builder"
-  acl_sets: "triggered-by-parent-builders"
-  buildbucket: {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "Mac FYI 10.14 Release (AMD)"
-  }
-}
-
-job {
-  id: "Mac FYI 10.14 Release (Intel)"
-  # Triggered by "GPU FYI Mac Builder"
-  acl_sets: "triggered-by-parent-builders"
-  buildbucket: {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "Mac FYI 10.14 Release (Intel)"
-  }
-}
-
-job {
-  id: "Mac FYI 10.14 Release (NVIDIA)"
-  # Triggered by "GPU FYI Mac Builder"
-  acl_sets: "triggered-by-parent-builders"
-  buildbucket: {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "Mac FYI 10.14 Release (NVIDIA)"
-  }
-}
-
-job {
   id: "Mac FYI Experimental Release (Intel)"
   # Triggered by "GPU FYI Mac Builder".
   acl_sets: "triggered-by-parent-builders"
diff --git a/infra/config/main.star b/infra/config/main.star
index 63b3f3f..bd588ea0 100755
--- a/infra/config/main.star
+++ b/infra/config/main.star
@@ -7,6 +7,7 @@
     config_dir = 'generated',
     tracked_files = [
         'commit-queue.cfg',
+        'cq-builders.md',
         'cr-buildbucket.cfg',
         'luci-logdog.cfg',
         'luci-milo.cfg',
@@ -21,9 +22,16 @@
 # Copy the not-yet migrated files to the generated outputs
 # TODO(https://crbug.com/1011908) Migrate the configuration in these files to starlark
 [lucicfg.emit(dest = f, data = io.read_file(f)) for f in (
-    'commit-queue.cfg',
     'luci-milo.cfg',
+    # TODO(https://crbug.com/1015148) lucicfg generates luci-notify.cfg very
+    # differently from our hand-written file and doesn't do any normalization
+    # for luci-notify.cfg so the semantic diff is large and confusing
     'luci-notify.cfg',
+    # TODO(https://crbug.com/819899) There are a number of noop jobs for dummy
+    # builders defined due to legacy requirements that trybots mirror CI bots
+    # and noop scheduler jobs cannot be created in lucicfg, so the trybots need
+    # to be updated to not rely on dummy builders and the noop jobs need to be
+    # removed
     'luci-scheduler.cfg',
 )]
 
@@ -53,6 +61,12 @@
     ],
 )
 
+luci.cq(
+    submit_max_burst = 2,
+    submit_burst_delay = time.minute,
+    status_host = 'chromium-cq-status.appspot.com',
+)
+
 luci.logdog(
     gs_bucket = 'chromium-luci-logdog',
 )
@@ -62,3 +76,5 @@
 exec('//buckets/try.star')
 exec('//buckets/webrtc.star')
 exec('//buckets/webrtc.fyi.star')
+
+exec('//cq-builders-md.star')
diff --git a/ios/build/bots/scripts/test_runner.py b/ios/build/bots/scripts/test_runner.py
index 47938e2d..03b966f 100644
--- a/ios/build/bots/scripts/test_runner.py
+++ b/ios/build/bots/scripts/test_runner.py
@@ -217,7 +217,7 @@
       if ps.is_running():
         LOGGER.info(
             'Process %s is still alive! %s process might block it.',
-            proc.name, proc_name)
+            psutil.Process(proc.pid).name(), proc_name)
         running_processes = [
             p for p in psutil.process_iter()
             # Use as_dict() to avoid API changes across versions of psutil.
@@ -253,7 +253,7 @@
       if no output occurs in specified timeout. Sometimes proc generates
       child process that may block its parent and for such cases
       proc_name refers to the name of child process.
-      If proc_name is not specified, proc.name will be used to kill process.
+      If proc_name is not specified, process name will be used to kill process.
     Parser: A parser.
     timeout: A timeout(in seconds) to subprocess.stdout.readline method.
   """
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 22418d7..39323b1e 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -200,6 +200,7 @@
     "//ios/chrome/browser/geolocation:geolocation_internal",
     "//ios/chrome/browser/history",
     "//ios/chrome/browser/mailto:feature_flags",
+    "//ios/chrome/browser/main",
     "//ios/chrome/browser/memory",
     "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/metrics:metrics_internal",
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index b26515b9..c3f3ceaf 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -86,6 +86,7 @@
 #include "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
 #include "ios/chrome/browser/ios_chrome_io_thread.h"
 #include "ios/chrome/browser/mailto/features.h"
+#include "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/memory/memory_debugger_manager.h"
 #include "ios/chrome/browser/metrics/first_user_action_recorder.h"
 #import "ios/chrome/browser/metrics/previous_session_info.h"
@@ -1227,8 +1228,6 @@
 
   NSString* fieldTrialValueKey =
       base::SysUTF8ToNSString(app_group::kChromeExtensionFieldTrialPreference);
-  NSNumber* copiedContentBehaviorValue = [NSNumber
-      numberWithBool:base::FeatureList::IsEnabled(kCopiedContentBehavior)];
 
   // Add other field trial values here if they are needed by extensions.
   // The general format is
@@ -1239,10 +1238,6 @@
   //   }
   // }
   NSDictionary* fieldTrialValues = @{
-    base::SysUTF8ToNSString(kCopiedContentBehavior.name) : @{
-      kFieldTrialValueKey : copiedContentBehaviorValue,
-      kFieldTrialVersionKey : kCopiedContentBehaviorVersion
-    }
   };
   [sharedDefaults setObject:fieldTrialValues forKey:fieldTrialValueKey];
 }
@@ -1605,9 +1600,10 @@
   DCHECK(!self.signinInteractionCoordinator.isSettingsViewPresented);
   if (_settingsNavigationController)
     return;
-  _settingsNavigationController = [SettingsNavigationController
-      newAutofillProfilleController:_mainBrowserState
-                           delegate:self];
+  Browser* browser = self.interfaceProvider.mainInterface.browser;
+  _settingsNavigationController =
+      [SettingsNavigationController autofillProfileControllerForBrowser:browser
+                                                               delegate:self];
   [baseViewController presentViewController:_settingsNavigationController
                                    animated:YES
                                  completion:nil];
@@ -1623,11 +1619,12 @@
     DCHECK(!self.signinInteractionCoordinator.isSettingsViewPresented);
     if (_settingsNavigationController)
       return;
-    _settingsNavigationController = [SettingsNavigationController
-        newUserFeedbackController:_mainBrowserState
-                         delegate:self
-               feedbackDataSource:self
-                       dispatcher:self];
+    Browser* browser = self.interfaceProvider.mainInterface.browser;
+    _settingsNavigationController =
+        [SettingsNavigationController userFeedbackControllerForBrowser:browser
+                                                              delegate:self
+                                                    feedbackDataSource:self
+                                                            dispatcher:self];
     [baseViewController presentViewController:_settingsNavigationController
                                      animated:YES
                                    completion:nil];
@@ -1726,9 +1723,11 @@
         showAccountsSettingsFromViewController:baseViewController];
     return;
   }
-  _settingsNavigationController = [SettingsNavigationController
-      newAccountsController:self.currentBrowserState
-                   delegate:self];
+
+  Browser* browser = self.interfaceProvider.mainInterface.browser;
+  _settingsNavigationController =
+      [SettingsNavigationController accountsControllerForBrowser:browser
+                                                        delegate:self];
   [baseViewController presentViewController:_settingsNavigationController
                                    animated:YES
                                  completion:nil];
@@ -1751,11 +1750,10 @@
     return;
   }
 
-  ios::ChromeBrowserState* originalBrowserState =
-      self.currentBrowserState->GetOriginalChromeBrowserState();
-  _settingsNavigationController = [SettingsNavigationController
-      newGoogleServicesController:originalBrowserState
-                         delegate:self];
+  Browser* browser = self.interfaceProvider.mainInterface.browser;
+  _settingsNavigationController =
+      [SettingsNavigationController googleServicesControllerForBrowser:browser
+                                                              delegate:self];
   [baseViewController presentViewController:_settingsNavigationController
                                    animated:YES
                                  completion:nil];
@@ -1770,9 +1768,10 @@
         showSyncPassphraseSettingsFromViewController:baseViewController];
     return;
   }
-  _settingsNavigationController = [SettingsNavigationController
-      newSyncEncryptionPassphraseController:_mainBrowserState
-                                   delegate:self];
+  Browser* browser = self.interfaceProvider.mainInterface.browser;
+  _settingsNavigationController =
+      [SettingsNavigationController syncPassphraseControllerForBrowser:browser
+                                                              delegate:self];
   [baseViewController presentViewController:_settingsNavigationController
                                    animated:YES
                                  completion:nil];
@@ -1787,9 +1786,10 @@
         showSavedPasswordsSettingsFromViewController:baseViewController];
     return;
   }
+  Browser* browser = self.interfaceProvider.mainInterface.browser;
   _settingsNavigationController =
-      [SettingsNavigationController newSavePasswordsController:_mainBrowserState
-                                                      delegate:self];
+      [SettingsNavigationController savePasswordsControllerForBrowser:browser
+                                                             delegate:self];
   [baseViewController presentViewController:_settingsNavigationController
                                    animated:YES
                                  completion:nil];
@@ -1804,9 +1804,10 @@
         showProfileSettingsFromViewController:baseViewController];
     return;
   }
-  _settingsNavigationController = [SettingsNavigationController
-      newAutofillProfilleController:_mainBrowserState
-                           delegate:self];
+  Browser* browser = self.interfaceProvider.mainInterface.browser;
+  _settingsNavigationController =
+      [SettingsNavigationController autofillProfileControllerForBrowser:browser
+                                                               delegate:self];
   [baseViewController presentViewController:_settingsNavigationController
                                    animated:YES
                                  completion:nil];
@@ -1821,9 +1822,10 @@
         showCreditCardSettingsFromViewController:baseViewController];
     return;
   }
+  Browser* browser = self.interfaceProvider.mainInterface.browser;
   _settingsNavigationController = [SettingsNavigationController
-      newAutofillCreditCardController:_mainBrowserState
-                             delegate:self];
+      autofillCreditCardControllerForBrowser:browser
+                                    delegate:self];
   [baseViewController presentViewController:_settingsNavigationController
                                    animated:YES
                                  completion:nil];
@@ -2264,9 +2266,10 @@
   [[DeferredInitializationRunner sharedInstance]
       runBlockIfNecessary:kPrefObserverInit];
   DCHECK(_localStatePrefObserverBridge);
-  _settingsNavigationController = [SettingsNavigationController
-      newSettingsMainControllerWithBrowserState:_mainBrowserState
-                                       delegate:self];
+  Browser* browser = self.interfaceProvider.mainInterface.browser;
+  _settingsNavigationController =
+      [SettingsNavigationController mainSettingsControllerForBrowser:browser
+                                                            delegate:self];
   [baseViewController presentViewController:_settingsNavigationController
                                    animated:YES
                                  completion:nil];
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 39b252b..95cd8a8 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -2089,9 +2089,6 @@
       <message name="IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP_BLOCK" desc="Text on the button, when tapped will block opening an external application for this browsing session. [iOS only]">
           Block
       </message>
-      <message name="IDS_IOS_PASTE_AND_GO" desc="Text in the editing menu shown when long-pressing the omnibox. Enables pasting the current contents of the clipboard and searching the text or navigating to the url. [iOS only]">
-          Paste and Go
-      </message>
       <message name="IDS_IOS_NAVIGATION_BAR_ADD_BUTTON" desc="Label of the button allowing the user to add a new credit card to the list of their saved credit cards.">
           Add
       </message>
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn b/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
index ddd80121..e71f4b6e 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
+++ b/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//ios/build/config.gni")
+
 source_set("feature_flags") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
@@ -12,3 +14,33 @@
     "//base",
   ]
 }
+
+source_set("breadcrumbs") {
+  deps = [
+    "//base",
+  ]
+
+  sources = [
+    "breadcrumb_manager.cc",
+    "breadcrumb_manager.h",
+    "breadcrumb_manager_observer.h",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  deps = [
+    ":breadcrumbs",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "breadcrumb_manager_observer_unittest.mm",
+    "breadcrumb_manager_unittest.mm",
+  ]
+}
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/OWNERS b/ios/chrome/browser/crash_report/breadcrumbs/OWNERS
new file mode 100644
index 0000000..f75309d
--- /dev/null
+++ b/ios/chrome/browser/crash_report/breadcrumbs/OWNERS
@@ -0,0 +1,5 @@
+eugenebut@chromium.org
+michaeldo@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.cc b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.cc
new file mode 100644
index 0000000..3017053c
--- /dev/null
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.cc
@@ -0,0 +1,96 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/time/time_to_iso8601.h"
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer.h"
+
+namespace {
+
+// The minimum number of event buckets to keep, even if they are expired.
+const int kMinEventsBuckets = 2;
+
+// Returns a Time used to bucket events for easier discarding of expired events.
+base::Time EventBucket(const base::Time& time) {
+  base::Time::Exploded exploded;
+  time.LocalExplode(&exploded);
+  exploded.millisecond = 0;
+  exploded.second = 0;
+
+  base::Time bucket_time;
+  bool converted = base::Time::FromLocalExploded(exploded, &bucket_time);
+  DCHECK(converted);
+  return bucket_time;
+}
+
+}  // namespace
+
+std::list<std::string> BreadcrumbManager::GetEvents(size_t event_count_limit) {
+  DropOldEvents();
+
+  std::list<std::string> events;
+  for (auto it = event_buckets_.rbegin(); it != event_buckets_.rend(); ++it) {
+    std::list<std::string> bucket_events = it->second;
+    for (auto event_it = bucket_events.rbegin();
+         event_it != bucket_events.rend(); ++event_it) {
+      std::string event = *event_it;
+      events.push_front(event);
+      if (event_count_limit > 0 && events.size() >= event_count_limit) {
+        return events;
+      }
+    }
+  }
+  return events;
+}
+
+void BreadcrumbManager::AddEvent(const std::string& event) {
+  base::Time time = base::Time::Now();
+  base::Time bucket_time = EventBucket(time);
+
+  // If bucket exists, it will be at the end of the list.
+  if (event_buckets_.empty() || event_buckets_.back().first != bucket_time) {
+    std::pair<base::Time, std::list<std::string>> bucket(
+        bucket_time, std::list<std::string>());
+    event_buckets_.push_back(bucket);
+  }
+
+  std::string timestamp = base::TimeToISO8601(time);
+  std::string event_log =
+      base::StringPrintf("%s %s", timestamp.c_str(), event.c_str());
+  event_buckets_.back().second.push_back(event_log);
+
+  DropOldEvents();
+
+  for (auto& observer : observers_) {
+    observer.EventAdded(this, event_log);
+  }
+}
+
+void BreadcrumbManager::DropOldEvents() {
+  static const base::TimeDelta kMessageExpirationTime =
+      base::TimeDelta::FromMinutes(20);
+
+  base::Time now = base::Time::Now();
+  while (event_buckets_.size() > kMinEventsBuckets) {
+    base::Time oldest_bucket_time = event_buckets_.front().first;
+    if (now - oldest_bucket_time < kMessageExpirationTime) {
+      break;
+    }
+    event_buckets_.pop_front();
+  }
+}
+
+BreadcrumbManager::BreadcrumbManager() = default;
+
+BreadcrumbManager::~BreadcrumbManager() = default;
+
+void BreadcrumbManager::AddObserver(BreadcrumbManagerObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void BreadcrumbManager::RemoveObserver(BreadcrumbManagerObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h
new file mode 100644
index 0000000..b527372
--- /dev/null
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h
@@ -0,0 +1,56 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_MANAGER_H_
+#define IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_MANAGER_H_
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "base/observer_list.h"
+#import "base/time/time.h"
+
+class BreadcrumbManagerObserver;
+
+// Stores events logged with |AddEvent| in memory which can later be retrieved
+// with |GetEvents|. Events will be silently dropped after a certain amount of
+// time has passed unless no more recent events are available. The internal
+// management of events aims to keep relevant events available while clearing
+// stale data.
+class BreadcrumbManager {
+ public:
+  // Returns a list of the collected breadcrumb events which are still relevant
+  // up to |event_count_limit|. Passing zero for |event_count_limit| signifies
+  // no limit. Events returned will have a timestamp prepended to the original
+  // |event| string representing when |AddEvent| was called.
+  std::list<std::string> GetEvents(size_t event_count_limit);
+
+  // Logs a breadcrumb event with message data |event|.
+  void AddEvent(const std::string& event);
+
+  // Adds and removes observers.
+  void AddObserver(BreadcrumbManagerObserver* observer);
+  void RemoveObserver(BreadcrumbManagerObserver* observer);
+
+  BreadcrumbManager();
+  virtual ~BreadcrumbManager();
+
+ private:
+  // Drops events which are considered stale. Note that stale events are not
+  // guaranteed to be removed. Explicitly, stale events will be retained while
+  // newer events are limited.
+  void DropOldEvents();
+
+  // List of events, paired with the time which they were logged to minute
+  // resolution. Newer events are at the end of the list.
+  std::list<std::pair<base::Time, std::list<std::string>>> event_buckets_;
+
+  base::ObserverList<BreadcrumbManagerObserver, /*check_empty=*/true>
+      observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(BreadcrumbManager);
+};
+
+#endif  // IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_MANAGER_H_
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer.h b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer.h
new file mode 100644
index 0000000..03e4c58
--- /dev/null
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_MANAGER_OBSERVER_H_
+#define IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_MANAGER_OBSERVER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/observer_list_types.h"
+
+class BreadcrumbManager;
+
+class BreadcrumbManagerObserver : public base::CheckedObserver {
+ public:
+  // Called when a new |event| has been added to |manager|. Similar to
+  // |BreadcrumbManager::GetEvents|, |event| will have the timestamp at which it
+  // was logged prepended to the string which was passed to
+  // |BreadcrumbManager::AddEvent|.
+  virtual void EventAdded(BreadcrumbManager* manager,
+                          const std::string& event) {}
+
+ protected:
+  BreadcrumbManagerObserver() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BreadcrumbManagerObserver);
+};
+
+#endif  // IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_MANAGER_OBSERVER_H_
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer_unittest.mm b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer_unittest.mm
new file mode 100644
index 0000000..2050b12
--- /dev/null
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer_unittest.mm
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer.h"
+
+#import "base/macros.h"
+#import "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+class FakeBreadcrumbManagerObserver : public BreadcrumbManagerObserver {
+ public:
+  FakeBreadcrumbManagerObserver() {}
+  ~FakeBreadcrumbManagerObserver() override = default;
+
+  // BreadcrumbManagerObserver
+  void EventAdded(BreadcrumbManager* manager,
+                  const std::string& event) override {
+    last_received_manager_ = manager;
+    last_received_event_ = event;
+  }
+
+  BreadcrumbManager* last_received_manager_ = nullptr;
+  std::string last_received_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeBreadcrumbManagerObserver);
+};
+}
+
+class BreadcrumbManagerObserverTest : public PlatformTest {
+ protected:
+  BreadcrumbManagerObserverTest() { manager_.AddObserver(&observer_); }
+  ~BreadcrumbManagerObserverTest() override {
+    manager_.RemoveObserver(&observer_);
+  }
+
+  BreadcrumbManager manager_;
+  FakeBreadcrumbManagerObserver observer_;
+};
+
+// Tests that |BreadcrumbManagerObserver::EventAdded| is called when an event to
+// added to |manager_|.
+TEST_F(BreadcrumbManagerObserverTest, EventAdded) {
+  ASSERT_FALSE(observer_.last_received_manager_);
+  ASSERT_TRUE(observer_.last_received_event_.empty());
+
+  std::string event = "event";
+  manager_.AddEvent(event);
+
+  EXPECT_EQ(&manager_, observer_.last_received_manager_);
+  // A timestamp will be prepended to the event passed to |AddEvent|.
+  EXPECT_NE(std::string::npos, observer_.last_received_event_.find(event));
+}
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_unittest.mm b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_unittest.mm
new file mode 100644
index 0000000..5a31796
--- /dev/null
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_unittest.mm
@@ -0,0 +1,90 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h"
+
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test fixture for testing BreadcrumbManager class.
+class BreadcrumbManagerTest : public PlatformTest {
+ protected:
+  BreadcrumbManagerTest() {}
+
+  base::test::TaskEnvironment task_env_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  BreadcrumbManager breadcrumb_manager_;
+};
+
+// Tests that an event is logged and returned.
+TEST_F(BreadcrumbManagerTest, AddEvent) {
+  std::string event_message = "event";
+  breadcrumb_manager_.AddEvent(event_message);
+  std::list<std::string> events = breadcrumb_manager_.GetEvents(0);
+  ASSERT_EQ(1ul, events.size());
+  // Events returned from |GetEvents| will have a timestamp prepended.
+  EXPECT_NE(std::string::npos, events.front().find(event_message));
+}
+
+// Tests that returned events returned by |GetEvents| are limited by the
+// |event_count_limit| parameter.
+TEST_F(BreadcrumbManagerTest, EventCountLimited) {
+  breadcrumb_manager_.AddEvent("event1");
+  breadcrumb_manager_.AddEvent("event2");
+  breadcrumb_manager_.AddEvent("event3");
+  breadcrumb_manager_.AddEvent("event4");
+
+  std::list<std::string> events = breadcrumb_manager_.GetEvents(2);
+  ASSERT_EQ(2ul, events.size());
+  EXPECT_NE(std::string::npos, events.front().find("event3"));
+  events.pop_front();
+  EXPECT_NE(std::string::npos, events.front().find("event4"));
+}
+
+// Tests that old events are dropped.
+TEST_F(BreadcrumbManagerTest, OldEventsDropped) {
+  // Log an event from one and two hours ago.
+  breadcrumb_manager_.AddEvent("event1");
+  task_env_.FastForwardBy(base::TimeDelta::FromHours(1));
+  breadcrumb_manager_.AddEvent("event2");
+  task_env_.FastForwardBy(base::TimeDelta::FromHours(1));
+
+  // Log three events separated by three minutes to ensure they receive their
+  // own event bucket. Otherwise, some old events may be returned to ensure a
+  // minimum number of available events. See |MinimumEventsReturned| test below.
+  breadcrumb_manager_.AddEvent("event3");
+  task_env_.FastForwardBy(base::TimeDelta::FromMinutes(3));
+  breadcrumb_manager_.AddEvent("event4");
+  task_env_.FastForwardBy(base::TimeDelta::FromMinutes(3));
+  breadcrumb_manager_.AddEvent("event5");
+
+  std::list<std::string> events = breadcrumb_manager_.GetEvents(0);
+  ASSERT_EQ(3ul, events.size());
+  // Validate the three most recent events are the ones which were returned.
+  EXPECT_NE(std::string::npos, events.front().find("event3"));
+  events.pop_front();
+  EXPECT_NE(std::string::npos, events.front().find("event4"));
+  events.pop_front();
+  EXPECT_NE(std::string::npos, events.front().find("event5"));
+}
+
+// Tests that expired events are returned if not enough new events exist.
+TEST_F(BreadcrumbManagerTest, MinimumEventsReturned) {
+  // Log an event from one and two hours ago.
+  breadcrumb_manager_.AddEvent("event1");
+  task_env_.FastForwardBy(base::TimeDelta::FromHours(1));
+  breadcrumb_manager_.AddEvent("event2");
+  task_env_.FastForwardBy(base::TimeDelta::FromHours(1));
+  breadcrumb_manager_.AddEvent("event3");
+
+  std::list<std::string> events = breadcrumb_manager_.GetEvents(0);
+  EXPECT_EQ(2ul, events.size());
+}
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index a997824..a02048f 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -273,6 +273,11 @@
      flag_descriptions::kAutofillCreditCardUploadName,
      flag_descriptions::kAutofillCreditCardUploadDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(autofill::features::kAutofillUpstream)},
+    {"enable-sync-device-info-in-transport-mode",
+     flag_descriptions::kSyncDeviceInfoInTransportModeName,
+     flag_descriptions::kSyncDeviceInfoInTransportModeDescription,
+     flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(switches::kSyncDeviceInfoInTransportMode)},
     {"use-sync-sandbox", flag_descriptions::kSyncSandboxName,
      flag_descriptions::kSyncSandboxDescription, flags_ui::kOsIos,
      SINGLE_VALUE_TYPE_AND_VALUE(
@@ -418,9 +423,6 @@
      flag_descriptions::kEnableClipboardProviderImageSuggestionsDescription,
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(omnibox::kEnableClipboardProviderImageSuggestions)},
-    {"copied-content-behavior", flag_descriptions::kCopiedContentBehaviorName,
-     flag_descriptions::kCopiedContentBehaviorName, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(kCopiedContentBehavior)},
     {"snapshot-draw-view", flag_descriptions::kSnapshotDrawViewName,
      flag_descriptions::kSnapshotDrawViewDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kSnapshotDrawView)},
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index e2e4073b..64885a2 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -124,12 +124,6 @@
 const char kConfirmInfobarMessagesUIDescription[] =
     "When enabled Confirm Infobars use the new Messages UI.";
 
-const char kCopiedContentBehaviorName[] =
-    "Enable differentiating between copied urls, text, and images";
-const char kCopiedContentBehaviorDescription[] =
-    "When enabled, places that handled copied urls (omnibox long-press, toolbar"
-    "menus) will differentiate between copied urls, text, and images.";
-
 const char kCreditCardScannerName[] = "Enable the 'Use Camera' button";
 const char kCreditCardScannerDescription[] =
     "Allow a user to scan a credit card using the credit card camera scanner."
@@ -347,6 +341,12 @@
     "When enabled, the startup sign-in promo is always displayed when starting "
     "Chrome.";
 
+const char kSyncDeviceInfoInTransportModeName[] =
+    "Enable syncing DeviceInfo in transport-only sync mode.";
+const char kSyncDeviceInfoInTransportModeDescription[] =
+    "When enabled, allows syncing DeviceInfo datatype for users who are "
+    "signed-in but not necessary sync-ing.";
+
 const char kSyncSandboxName[] = "Use Chrome Sync sandbox";
 const char kSyncSandboxDescription[] =
     "Connects to the testing server for Chrome Sync.";
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 649dad8..d7253278 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -97,11 +97,6 @@
 extern const char kConfirmInfobarMessagesUIName[];
 extern const char kConfirmInfobarMessagesUIDescription[];
 
-// Title and description for the flag to diffentiate between copied
-// urls, strings, and images.
-extern const char kCopiedContentBehaviorName[];
-extern const char kCopiedContentBehaviorDescription[];
-
 // Title and description for the flag to scan a new credit card using the
 // camera.
 extern const char kCreditCardScannerName[];
@@ -300,6 +295,11 @@
 extern const char kForceStartupSigninPromoName[];
 extern const char kForceStartupSigninPromoDescription[];
 
+// Title and description for the flag to allow syncing DeviceInfo in
+// transport-only mode.
+extern const char kSyncDeviceInfoInTransportModeName[];
+extern const char kSyncDeviceInfoInTransportModeDescription[];
+
 // Title and description for the flag to control if Chrome Sync should use the
 // sandbox servers.
 extern const char kSyncSandboxName[];
diff --git a/ios/chrome/browser/infobars/infobar_metrics_recorder.mm b/ios/chrome/browser/infobars/infobar_metrics_recorder.mm
index 933f70a..2543699 100644
--- a/ios/chrome/browser/infobars/infobar_metrics_recorder.mm
+++ b/ios/chrome/browser/infobars/infobar_metrics_recorder.mm
@@ -51,6 +51,19 @@
 const char kInfobarPasswordUpdateBadgeTappedHistogram[] =
     "Mobile.Messages.Badge.Tapped.InfobarTypePasswordUpdate";
 
+// Histogram names for InfobarTypeSaveCard.
+// Banner.
+const char kInfobarSaveCardBannerEventHistogram[] =
+    "Mobile.Messages.Banner.Event.InfobarTypeSaveCard";
+const char kInfobarSaveCardBannerDismissTypeHistogram[] =
+    "Mobile.Messages.Banner.Dismiss.InfobarTypeSaveCard";
+// Modal.
+const char kInfobarSaveCardModalEventHistogram[] =
+    "Mobile.Messages.Modal.Event.InfobarTypeSaveCard";
+// Badge.
+const char kInfobarSaveCardBadgeTappedHistogram[] =
+    "Mobile.Messages.Badge.Tapped.InfobarTypeSaveCard";
+
 }  // namespace
 
 @interface InfobarMetricsRecorder ()
@@ -83,6 +96,9 @@
       UMA_HISTOGRAM_ENUMERATION(kInfobarPasswordUpdateBannerEventHistogram,
                                 event);
       break;
+    case InfobarType::kInfobarTypeSaveCard:
+      UMA_HISTOGRAM_ENUMERATION(kInfobarSaveCardBannerEventHistogram, event);
+      break;
   }
 }
 
@@ -100,6 +116,10 @@
       UMA_HISTOGRAM_ENUMERATION(
           kInfobarPasswordUpdateBannerDismissTypeHistogram, dismissType);
       break;
+    case InfobarType::kInfobarTypeSaveCard:
+      UMA_HISTOGRAM_ENUMERATION(kInfobarSaveCardBannerDismissTypeHistogram,
+                                dismissType);
+      break;
   }
 }
 
@@ -120,6 +140,9 @@
       UMA_HISTOGRAM_ENUMERATION(kInfobarPasswordUpdateModalEventHistogram,
                                 event);
       break;
+    case InfobarType::kInfobarTypeSaveCard:
+      UMA_HISTOGRAM_ENUMERATION(kInfobarSaveCardModalEventHistogram, event);
+      break;
   }
 }
 
@@ -136,6 +159,9 @@
       UMA_HISTOGRAM_ENUMERATION(kInfobarPasswordUpdateBadgeTappedHistogram,
                                 state);
       break;
+    case InfobarType::kInfobarTypeSaveCard:
+      UMA_HISTOGRAM_ENUMERATION(kInfobarSaveCardBadgeTappedHistogram, state);
+      break;
   }
 }
 
diff --git a/ios/chrome/browser/infobars/infobar_type.h b/ios/chrome/browser/infobars/infobar_type.h
index 2955658f..5971ab04 100644
--- a/ios/chrome/browser/infobars/infobar_type.h
+++ b/ios/chrome/browser/infobars/infobar_type.h
@@ -14,6 +14,8 @@
   kInfobarTypePasswordSave = 1,
   // Message Infobar for Updating a password.
   kInfobarTypePasswordUpdate = 2,
+  // Message Infobar for Saving a Credit Card.
+  kInfobarTypeSaveCard = 3,
 };
 
 #endif  // IOS_CHROME_BROWSER_INFOBARS_INFOBAR_TYPE_H_
diff --git a/ios/chrome/browser/sessions/session_service_ios.mm b/ios/chrome/browser/sessions/session_service_ios.mm
index 82c4465e..1bd12f5b 100644
--- a/ios/chrome/browser/sessions/session_service_ios.mm
+++ b/ios/chrome/browser/sessions/session_service_ios.mm
@@ -13,11 +13,13 @@
 #include "base/logging.h"
 #import "base/mac/foundation_util.h"
 #include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/post_task.h"
 #include "base/threading/scoped_blocking_call.h"
+#include "base/time/time.h"
 #import "ios/chrome/browser/sessions/session_ios.h"
 #import "ios/chrome/browser/sessions/session_window_ios.h"
 #import "ios/web/public/session/crw_navigation_item_storage.h"
@@ -130,7 +132,11 @@
 
 - (SessionIOS*)loadSessionFromDirectory:(NSString*)directory {
   NSString* sessionPath = [[self class] sessionPathForDirectory:directory];
-  return [self loadSessionFromPath:sessionPath];
+  base::TimeTicks start_time = base::TimeTicks::Now();
+  SessionIOS* session = [self loadSessionFromPath:sessionPath];
+  UmaHistogramTimes("Session.WebStates.ReadFromFileTime",
+                    base::TimeTicks::Now() - start_time);
+  return session;
 }
 
 - (SessionIOS*)loadSessionFromPath:(NSString*)sessionPath {
@@ -281,12 +287,15 @@
   NSDataWritingOptions options =
       NSDataWritingAtomic | NSDataWritingFileProtectionComplete;
 
+  base::TimeTicks start_time = base::TimeTicks::Now();
   if (![sessionData writeToFile:sessionPath options:options error:&error]) {
     NOTREACHED() << "Error writing session file: "
                  << base::SysNSStringToUTF8(sessionPath) << ": "
                  << base::SysNSStringToUTF8([error description]);
     return;
   }
+  UmaHistogramTimes("Session.WebStates.WriteToFileTime",
+                    base::TimeTicks::Now() - start_time);
 }
 
 @end
diff --git a/ios/chrome/browser/tabs/tab_model_unittest.mm b/ios/chrome/browser/tabs/tab_model_unittest.mm
index 1e76a11..4a2deef9 100644
--- a/ios/chrome/browser/tabs/tab_model_unittest.mm
+++ b/ios/chrome/browser/tabs/tab_model_unittest.mm
@@ -666,14 +666,9 @@
   [tab_model_ restoreSessionWindow:session_window forInitialRestore:NO];
 
   EXPECT_EQ(0U, [tab_model_ count]);
-  EXPECT_TRUE([[NSFileManager defaultManager] removeItemAtPath:state_path
-                                                         error:nullptr]);
 }
 
 TEST_P(TabModelTest, DISABLED_PersistSelectionChange) {
-  NSString* stashPath =
-      base::SysUTF8ToNSString(chrome_browser_state_->GetStatePath().value());
-
   // Reset the TabModel with a custom SessionServiceIOS (to control whether
   // data is saved to disk).
   TestSessionService* test_session_service = [[TestSessionService alloc] init];
@@ -723,10 +718,6 @@
 
   EXPECT_EQ(tab_model_.webStateList->GetWebStateAt(1),
             tab_model_.webStateList->GetActiveWebState());
-
-  // Clean up.
-  EXPECT_TRUE([[NSFileManager defaultManager] removeItemAtPath:stashPath
-                                                         error:nullptr]);
 }
 
 INSTANTIATE_TEST_SUITE_P(ProgrammaticTabModelTest,
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
index 1eec6d2..2e70e25 100644
--- a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
+++ b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
@@ -270,13 +270,13 @@
                         viewController:viewController];
     return;
   }
-  _navigationController =
-      [SettingsNavigationController newImportDataController:browserState
-                                                   delegate:self
-                                         importDataDelegate:self
-                                                  fromEmail:lastSignedInEmail
-                                                    toEmail:[identity userEmail]
-                                                 isSignedIn:isSignedIn];
+  _navigationController = [SettingsNavigationController
+      importDataControllerForBrowserState:browserState
+                                 delegate:self
+                       importDataDelegate:self
+                                fromEmail:lastSignedInEmail
+                                  toEmail:[identity userEmail]
+                               isSignedIn:isSignedIn];
   [[_delegate presentingViewController]
       presentViewController:_navigationController
                    animated:YES
diff --git a/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn b/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn
index dcd02ea7..18fd99c 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn
+++ b/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn
@@ -12,9 +12,12 @@
     "infobar_coordinator_implementation.h",
     "infobar_password_coordinator.h",
     "infobar_password_coordinator.mm",
+    "infobar_save_card_coordinator.h",
+    "infobar_save_card_coordinator.mm",
   ]
   deps = [
     "//base",
+    "//components/autofill/core/browser",
     "//components/infobars/core",
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/infobars:public",
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.h b/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.h
new file mode 100644
index 0000000..134289a
--- /dev/null
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_INFOBARS_COORDINATORS_INFOBAR_SAVE_CARD_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_INFOBARS_COORDINATORS_INFOBAR_SAVE_CARD_COORDINATOR_H_
+
+#import "ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.h"
+
+namespace autofill {
+class AutofillSaveCardInfoBarDelegateMobile;
+}  // namespace autofill
+
+// Coordinator that creates and manages the SaveCardInfobar.
+@interface InfobarSaveCardCoordinator : InfobarCoordinator
+
+// Designated initializer. |saveCardInfoBarDelegate| is used to configure the
+// Infobar and subsequently perform related actions.
+- (instancetype)initWithInfoBarDelegate:
+    (autofill::AutofillSaveCardInfoBarDelegateMobile*)saveCardInfoBarDelegate
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithInfoBarDelegate:
+                    (infobars::InfoBarDelegate*)infoBarDelegate
+                           badgeSupport:(BOOL)badgeSupport
+                                   type:(InfobarType)infobarType NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_INFOBARS_COORDINATORS_INFOBAR_SAVE_CARD_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.mm
new file mode 100644
index 0000000..91b443a
--- /dev/null
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.mm
@@ -0,0 +1,113 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.h"
+
+#include "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
+#include "ios/chrome/browser/infobars/infobar_controller_delegate.h"
+#import "ios/chrome/browser/infobars/infobar_type.h"
+#import "ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.h"
+#import "ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator_implementation.h"
+#import "ios/chrome/browser/ui/infobars/infobar_container.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface InfobarSaveCardCoordinator () <InfobarCoordinatorImplementation>
+
+// InfobarBannerViewController owned by this Coordinator.
+@property(nonatomic, strong) InfobarBannerViewController* bannerViewController;
+// ModalViewController owned by this Coordinator.
+// TODO(crbug.com/1014652): Replace with CustomVC later on.
+@property(nonatomic, strong) UIViewController* modalViewController;
+// Delegate that holds the Infobar information and actions.
+@property(nonatomic, readonly)
+    autofill::AutofillSaveCardInfoBarDelegateMobile* saveCardInfoBarDelegate;
+
+@end
+
+@implementation InfobarSaveCardCoordinator
+// Synthesize since readonly property from superclass is changed to readwrite.
+@synthesize bannerViewController = _bannerViewController;
+// Synthesize since readonly property from superclass is changed to readwrite.
+@synthesize modalViewController = _modalViewController;
+
+- (instancetype)initWithInfoBarDelegate:
+    (autofill::AutofillSaveCardInfoBarDelegateMobile*)saveCardInfoBarDelegate {
+  self = [super initWithInfoBarDelegate:saveCardInfoBarDelegate
+                           badgeSupport:YES
+                                   type:InfobarType::kInfobarTypeSaveCard];
+  if (self) {
+    _saveCardInfoBarDelegate = saveCardInfoBarDelegate;
+  }
+  return self;
+}
+
+#pragma mark - ChromeCoordinator
+
+- (void)start {
+  if (!self.started) {
+    self.started = YES;
+    // TODO(crbug.com/1014652): Configure and present Banner.
+  }
+}
+
+- (void)stop {
+  [super stop];
+  if (self.started) {
+    self.started = NO;
+    // RemoveInfoBar() will delete the InfobarIOS that owns this Coordinator
+    // from memory.
+    self.delegate->RemoveInfoBar();
+    _saveCardInfoBarDelegate = nil;
+    [self.infobarContainer childCoordinatorStopped:self];
+  }
+}
+
+#pragma mark - InfobarCoordinatorImplementation
+
+- (void)performInfobarAction {
+  // TODO(crbug.com/1014652): Continue implementation.
+}
+
+- (void)infobarWasDismissed {
+  // Release these strong ViewControllers at the time of infobar dismissal.
+  self.bannerViewController = nil;
+  self.modalViewController = nil;
+  if (self.saveCardInfoBarDelegate)
+    self.saveCardInfoBarDelegate->InfoBarDismissed();
+}
+
+#pragma mark Banner
+
+- (void)infobarBannerWasPresented {
+  // TODO(crbug.com/1014652): Continue implementation.
+}
+
+- (void)dismissBannerWhenInteractionIsFinished {
+  // TODO(crbug.com/1014652): Continue implementation.
+}
+
+- (void)infobarBannerWillBeDismissed:(BOOL)userInitiated {
+  // TODO(crbug.com/1014652): Continue implementation.
+}
+
+#pragma mark Modal
+
+- (BOOL)configureModalViewController {
+  // TODO(crbug.com/1014652): Continue implementation.
+  return NO;
+}
+
+- (void)infobarModalPresentedFromBanner:(BOOL)presentedFromBanner {
+  // TODO(crbug.com/1014652): Continue implementation.
+}
+
+- (CGFloat)infobarModalHeightForWidth:(CGFloat)width {
+  // TODO(crbug.com/1014652): Continue implementation.
+  return 0.0;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
index 3c86e641..86338de 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
@@ -451,25 +451,18 @@
     // when it's the first time setting the first responder.
     dispatch_async(dispatch_get_main_queue(), ^{
       UIMenuController* menu = [UIMenuController sharedMenuController];
-      if (base::FeatureList::IsEnabled(kCopiedContentBehavior)) {
-        UIMenuItem* searchCopiedImage = [[UIMenuItem alloc]
-            initWithTitle:l10n_util::GetNSString((IDS_IOS_SEARCH_COPIED_IMAGE))
-                   action:@selector(searchCopiedImage:)];
-        UIMenuItem* visitCopiedLink = [[UIMenuItem alloc]
-            initWithTitle:l10n_util::GetNSString(IDS_IOS_VISIT_COPIED_LINK)
-                   action:@selector(visitCopiedLink:)];
-        UIMenuItem* searchCopiedText = [[UIMenuItem alloc]
-            initWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_TEXT)
-                   action:@selector(searchCopiedText:)];
-        [menu setMenuItems:@[
-          searchCopiedImage, visitCopiedLink, searchCopiedText
-        ]];
-      } else {
-        UIMenuItem* pasteAndGo = [[UIMenuItem alloc]
-            initWithTitle:l10n_util::GetNSString(IDS_IOS_PASTE_AND_GO)
-                   action:@selector(pasteAndGo:)];
-        [menu setMenuItems:@[ pasteAndGo ]];
-      }
+      UIMenuItem* searchCopiedImage = [[UIMenuItem alloc]
+          initWithTitle:l10n_util::GetNSString((IDS_IOS_SEARCH_COPIED_IMAGE))
+                 action:@selector(searchCopiedImage:)];
+      UIMenuItem* visitCopiedLink = [[UIMenuItem alloc]
+          initWithTitle:l10n_util::GetNSString(IDS_IOS_VISIT_COPIED_LINK)
+                 action:@selector(visitCopiedLink:)];
+      UIMenuItem* searchCopiedText = [[UIMenuItem alloc]
+          initWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_TEXT)
+                 action:@selector(searchCopiedText:)];
+      [menu setMenuItems:@[
+        searchCopiedImage, visitCopiedLink, searchCopiedText
+      ]];
 
       [menu setTargetRect:self.locationBarSteadyView.frame inView:self.view];
       [menu setMenuVisible:YES animated:YES];
@@ -487,18 +480,11 @@
     return YES;
   }
 
-  // remove along with flag kCopiedContentBehavior
-  if (action == @selector(pasteAndGo:)) {
-    DCHECK(!base::FeatureList::IsEnabled(kCopiedContentBehavior));
-    return UIPasteboard.generalPasteboard.string.length > 0;
-  }
-
   if (action == @selector(searchCopiedImage:) ||
       action == @selector(visitCopiedLink:) ||
       action == @selector(searchCopiedText:)) {
     ClipboardRecentContent* clipboardRecentContent =
         ClipboardRecentContent::GetInstance();
-    DCHECK(base::FeatureList::IsEnabled(kCopiedContentBehavior));
     if (self.searchByImageEnabled &&
         clipboardRecentContent->GetRecentImageFromClipboard().has_value()) {
       return action == @selector(searchCopiedImage:);
@@ -519,7 +505,6 @@
 }
 
 - (void)searchCopiedImage:(id)sender {
-  DCHECK(base::FeatureList::IsEnabled(kCopiedContentBehavior));
   if (base::Optional<gfx::Image> optionalImage =
           ClipboardRecentContent::GetInstance()
               ->GetRecentImageFromClipboard()) {
@@ -540,18 +525,14 @@
 // so we need two different selectors.
 - (void)pasteAndGo:(id)sender {
   NSString* query;
-  if (base::FeatureList::IsEnabled(kCopiedContentBehavior)) {
-    ClipboardRecentContent* clipboardRecentContent =
-        ClipboardRecentContent::GetInstance();
-    if (base::Optional<GURL> optionalUrl =
-            clipboardRecentContent->GetRecentURLFromClipboard()) {
-      query = base::SysUTF8ToNSString(optionalUrl.value().spec());
-    } else if (base::Optional<base::string16> optionalText =
-                   clipboardRecentContent->GetRecentTextFromClipboard()) {
-      query = base::SysUTF16ToNSString(optionalText.value());
-    }
-  } else {
-    query = UIPasteboard.generalPasteboard.string;
+  ClipboardRecentContent* clipboardRecentContent =
+      ClipboardRecentContent::GetInstance();
+  if (base::Optional<GURL> optionalUrl =
+          clipboardRecentContent->GetRecentURLFromClipboard()) {
+    query = base::SysUTF8ToNSString(optionalUrl.value().spec());
+  } else if (base::Optional<base::string16> optionalText =
+                 clipboardRecentContent->GetRecentTextFromClipboard()) {
+    query = base::SysUTF16ToNSString(optionalText.value());
   }
   [self.dispatcher loadQuery:query immediately:YES];
 }
diff --git a/ios/chrome/browser/ui/main/browser_interface_provider.h b/ios/chrome/browser/ui/main/browser_interface_provider.h
index b9cdb50..9e46939 100644
--- a/ios/chrome/browser/ui/main/browser_interface_provider.h
+++ b/ios/chrome/browser/ui/main/browser_interface_provider.h
@@ -9,6 +9,7 @@
 
 #include "base/ios/block_types.h"
 
+class Browser;
 @class BrowserCoordinator;
 @class BrowserViewController;
 @class TabModel;
@@ -39,6 +40,8 @@
 @property(nonatomic, readonly) BrowserViewController* bvc;
 // The tab model to which the current tab belongs.
 @property(nonatomic, readonly) TabModel* tabModel;
+// The active browser.
+@property(nonatomic, readonly) Browser* browser;
 // The browser state for this interface.
 @property(nonatomic, readonly) ios::ChromeBrowserState* browserState;
 // YES if the tab view is available for user interaction.
diff --git a/ios/chrome/browser/ui/main/browser_view_wrangler.mm b/ios/chrome/browser/ui/main/browser_view_wrangler.mm
index 61b9769..89d3303 100644
--- a/ios/chrome/browser/ui/main/browser_view_wrangler.mm
+++ b/ios/chrome/browser/ui/main/browser_view_wrangler.mm
@@ -61,6 +61,10 @@
   return self.coordinator.tabModel;
 }
 
+- (Browser*)browser {
+  return self.coordinator.browser;
+}
+
 - (ios::ChromeBrowserState*)browserState {
   return self.coordinator.viewController.browserState;
 }
diff --git a/ios/chrome/browser/ui/main/test/stub_browser_interface.h b/ios/chrome/browser/ui/main/test/stub_browser_interface.h
index a53b1e1..8f0a755b 100644
--- a/ios/chrome/browser/ui/main/test/stub_browser_interface.h
+++ b/ios/chrome/browser/ui/main/test/stub_browser_interface.h
@@ -9,6 +9,7 @@
 
 #import "ios/chrome/browser/ui/main/browser_interface_provider.h"
 
+class Browser;
 @class BrowserViewController;
 namespace ios {
 class ChromeBrowserState;
@@ -21,6 +22,7 @@
 @property(nonatomic, readwrite) UIViewController* viewController;
 @property(nonatomic, readwrite) BrowserViewController* bvc;
 @property(nonatomic, readwrite) TabModel* tabModel;
+@property(nonatomic, readwrite) Browser* browser;
 @property(nonatomic, readwrite) ios::ChromeBrowserState* browserState;
 @property(nonatomic, readwrite) BOOL incognito;
 @end
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn
index daa5d6d..ffd3f637 100644
--- a/ios/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -191,6 +191,7 @@
 
 source_set("eg_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
+  defines = [ "CHROME_EARL_GREY_1" ]
   testonly = true
   sources = [
     "new_tab_page_egtest.mm",
@@ -212,6 +213,29 @@
   libs = [ "XCTest.framework" ]
 }
 
+source_set("eg2_tests") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+  sources = [
+    "new_tab_page_egtest.mm",
+  ]
+  deps = [
+    "//base/test:test_support",
+    "//components/strings",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
+    "//ios/chrome/test/earl_grey:eg_test_support+eg2",
+    "//ios/testing/earl_grey:eg_test_support+eg2",
+    "//ios/third_party/earl_grey2:test_lib",
+    "//ui/base",
+  ]
+  libs = [ "UIKit.framework" ]
+}
+
 source_set("perf_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm b/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm
index 6359d1904..4422c44 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm
@@ -2,22 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import <EarlGrey/EarlGrey.h>
-#import <XCTest/XCTest.h>
-
 #import "base/test/ios/wait_util.h"
 #include "base/test/scoped_command_line.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/chrome_switches.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
-#include "ios/chrome/browser/ui/util/ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
-#import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#import "ios/testing/earl_grey/earl_grey_test.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -28,8 +22,8 @@
 
 // Pauses until the history label has disappeared.  History should not show on
 // incognito.
-void WaitForHistoryToDisappear() {
-  [[GREYCondition
+BOOL WaitForHistoryToDisappear() {
+  return [[GREYCondition
       conditionWithName:@"Wait for history to disappear"
                   block:^BOOL {
                     NSError* error = nil;
@@ -60,7 +54,7 @@
 // Tests that all items are accessible on the incognito page.
 - (void)testAccessibilityOnIncognitoTab {
   [ChromeEarlGrey openNewIncognitoTab];
-  WaitForHistoryToDisappear();
+  GREYAssert(WaitForHistoryToDisappear(), @"History did not disappear.");
   [ChromeEarlGrey verifyAccessibilityForCurrentScreen];
   [ChromeEarlGrey closeAllIncognitoTabs];
 }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
index 9185d37..5147d226 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
@@ -92,24 +92,16 @@
 
   // Add Paste and Go option to the editing menu
   UIMenuController* menu = [UIMenuController sharedMenuController];
-  if (base::FeatureList::IsEnabled(kCopiedContentBehavior)) {
-    UIMenuItem* searchCopiedImage = [[UIMenuItem alloc]
-        initWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_IMAGE)
-               action:@selector(searchCopiedImage:)];
-    UIMenuItem* visitCopiedLink = [[UIMenuItem alloc]
-        initWithTitle:l10n_util::GetNSString(IDS_IOS_VISIT_COPIED_LINK)
-               action:@selector(visitCopiedLink:)];
-    UIMenuItem* searchCopiedText = [[UIMenuItem alloc]
-        initWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_TEXT)
-               action:@selector(searchCopiedText:)];
-    [menu
-        setMenuItems:@[ searchCopiedImage, visitCopiedLink, searchCopiedText ]];
-  } else {
-    UIMenuItem* pasteAndGo = [[UIMenuItem alloc]
-        initWithTitle:l10n_util::GetNSString(IDS_IOS_PASTE_AND_GO)
-               action:NSSelectorFromString(@"pasteAndGo:")];
-    [menu setMenuItems:@[ pasteAndGo ]];
-  }
+  UIMenuItem* searchCopiedImage = [[UIMenuItem alloc]
+      initWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_IMAGE)
+             action:@selector(searchCopiedImage:)];
+  UIMenuItem* visitCopiedLink = [[UIMenuItem alloc]
+      initWithTitle:l10n_util::GetNSString(IDS_IOS_VISIT_COPIED_LINK)
+             action:@selector(visitCopiedLink:)];
+  UIMenuItem* searchCopiedText = [[UIMenuItem alloc]
+      initWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_TEXT)
+             action:@selector(searchCopiedText:)];
+  [menu setMenuItems:@[ searchCopiedImage, visitCopiedLink, searchCopiedText ]];
 
   self.textField.placeholderTextColor = [self placeholderAndClearButtonColor];
   self.textField.placeholder = l10n_util::GetNSString(IDS_OMNIBOX_EMPTY_HINT);
@@ -309,12 +301,6 @@
 #pragma mark - UIMenuItem
 
 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
-  // Remove with flag kCopiedContentBehavior
-  if (action == @selector(pasteAndGo:)) {
-    DCHECK(!base::FeatureList::IsEnabled(kCopiedContentBehavior));
-    return UIPasteboard.generalPasteboard.string.length > 0;
-  }
-
   if (action == @selector(searchCopiedImage:) ||
       action == @selector(visitCopiedLink:) ||
       action == @selector(searchCopiedText:)) {
@@ -336,7 +322,6 @@
 }
 
 - (void)searchCopiedImage:(id)sender {
-  DCHECK(base::FeatureList::IsEnabled(kCopiedContentBehavior));
   if (base::Optional<gfx::Image> optionalImage =
           ClipboardRecentContent::GetInstance()
               ->GetRecentImageFromClipboard()) {
@@ -358,18 +343,14 @@
 // so we need two different selectors.
 - (void)pasteAndGo:(id)sender {
   NSString* query;
-  if (base::FeatureList::IsEnabled(kCopiedContentBehavior)) {
-    ClipboardRecentContent* clipboardRecentContent =
-        ClipboardRecentContent::GetInstance();
-    if (base::Optional<GURL> optionalUrl =
-            clipboardRecentContent->GetRecentURLFromClipboard()) {
-      query = base::SysUTF8ToNSString(optionalUrl.value().spec());
-    } else if (base::Optional<base::string16> optionalText =
-                   clipboardRecentContent->GetRecentTextFromClipboard()) {
-      query = base::SysUTF16ToNSString(optionalText.value());
-    }
-  } else {
-    query = UIPasteboard.generalPasteboard.string;
+  ClipboardRecentContent* clipboardRecentContent =
+      ClipboardRecentContent::GetInstance();
+  if (base::Optional<GURL> optionalUrl =
+          clipboardRecentContent->GetRecentURLFromClipboard()) {
+    query = base::SysUTF8ToNSString(optionalUrl.value().spec());
+  } else if (base::Optional<base::string16> optionalText =
+                 clipboardRecentContent->GetRecentTextFromClipboard()) {
+    query = base::SysUTF16ToNSString(optionalText.value());
   }
   [self.dispatcher loadQuery:query immediately:YES];
   [self.dispatcher cancelOmniboxEdit];
diff --git a/ios/chrome/browser/ui/passwords/password_breach_view_controller.mm b/ios/chrome/browser/ui/passwords/password_breach_view_controller.mm
index 1a49da6e..20879896 100644
--- a/ios/chrome/browser/ui/passwords/password_breach_view_controller.mm
+++ b/ios/chrome/browser/ui/passwords/password_breach_view_controller.mm
@@ -291,7 +291,7 @@
       [UIFontMetrics metricsForTextStyle:UIFontTextStyleTitle1];
   title.font = [fontMetrics scaledFontForFont:font];
   title.textColor = [UIColor colorNamed:kTextPrimaryColor];
-  title.text = self.titleString;
+  title.text = self.titleString.capitalizedString;
   title.textAlignment = NSTextAlignmentCenter;
   title.translatesAutoresizingMaskIntoConstraints = NO;
   title.adjustsFontForContentSizeCategory = YES;
@@ -340,7 +340,7 @@
   [primaryActionButton addTarget:self
                           action:@selector(didTapPrimaryActionButton)
                 forControlEvents:UIControlEventTouchUpInside];
-  [primaryActionButton setTitle:self.primaryActionString
+  [primaryActionButton setTitle:self.primaryActionString.capitalizedString
                        forState:UIControlStateNormal];
   primaryActionButton.contentEdgeInsets =
       UIEdgeInsetsMake(kButtonVerticalInsets, 0, kButtonVerticalInsets, 0);
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.mm
index ad008fa..895b26b9 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.mm
@@ -136,18 +136,14 @@
     case PopupMenuActionPasteAndGo: {
       RecordAction(UserMetricsAction("MobileMenuPasteAndGo"));
       NSString* query;
-      if (base::FeatureList::IsEnabled(kCopiedContentBehavior)) {
-        ClipboardRecentContent* clipboardRecentContent =
-            ClipboardRecentContent::GetInstance();
-        if (base::Optional<GURL> optional_url =
-                clipboardRecentContent->GetRecentURLFromClipboard()) {
-          query = base::SysUTF8ToNSString(optional_url.value().spec());
-        } else if (base::Optional<base::string16> optional_text =
-                       clipboardRecentContent->GetRecentTextFromClipboard()) {
-          query = base::SysUTF16ToNSString(optional_text.value());
-        }
-      } else {
-        query = [UIPasteboard generalPasteboard].string;
+      ClipboardRecentContent* clipboardRecentContent =
+          ClipboardRecentContent::GetInstance();
+      if (base::Optional<GURL> optional_url =
+              clipboardRecentContent->GetRecentURLFromClipboard()) {
+        query = base::SysUTF8ToNSString(optional_url.value().spec());
+      } else if (base::Optional<base::string16> optional_text =
+                     clipboardRecentContent->GetRecentTextFromClipboard()) {
+        query = base::SysUTF16ToNSString(optional_text.value());
       }
       [self.dispatcher loadQuery:query immediately:YES];
       break;
@@ -175,7 +171,6 @@
       [self.dispatcher showQRScanner];
       break;
     case PopupMenuActionSearchCopiedImage: {
-      DCHECK(base::FeatureList::IsEnabled(kCopiedContentBehavior));
       RecordAction(UserMetricsAction("MobileMenuSearchCopiedImage"));
       ClipboardRecentContent* clipboardRecentContent =
           ClipboardRecentContent::GetInstance();
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
index a282489a..ec770b0b 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
@@ -705,32 +705,23 @@
   // represent a section in the popup menu. Having one sub array means having
   // all the items in the same section.
   PopupMenuToolsItem* copiedContentItem = nil;
-  if (base::FeatureList::IsEnabled(kCopiedContentBehavior)) {
-    ClipboardRecentContent* clipboardRecentContent =
-        ClipboardRecentContent::GetInstance();
+  ClipboardRecentContent* clipboardRecentContent =
+      ClipboardRecentContent::GetInstance();
 
-    if (search_engines::SupportsSearchByImage(self.templateURLService) &&
-        clipboardRecentContent->GetRecentImageFromClipboard()) {
-      copiedContentItem = CreateTableViewItem(
-          IDS_IOS_TOOLS_MENU_SEARCH_COPIED_IMAGE,
-          PopupMenuActionSearchCopiedImage, @"popup_menu_paste_and_go",
-          kToolsMenuCopiedImageSearch);
-    } else if (clipboardRecentContent->GetRecentURLFromClipboard()) {
-      copiedContentItem = CreateTableViewItem(
-          IDS_IOS_TOOLS_MENU_VISIT_COPIED_LINK, PopupMenuActionPasteAndGo,
-          @"popup_menu_paste_and_go", kToolsMenuPasteAndGo);
-    } else if (clipboardRecentContent->GetRecentTextFromClipboard()) {
-      copiedContentItem = CreateTableViewItem(
-          IDS_IOS_TOOLS_MENU_SEARCH_COPIED_TEXT, PopupMenuActionPasteAndGo,
-          @"popup_menu_paste_and_go", kToolsMenuPasteAndGo);
-    }
-  } else {
-    NSString* pasteboardString = [UIPasteboard generalPasteboard].string;
-    if (pasteboardString) {
-      copiedContentItem = CreateTableViewItem(
-          IDS_IOS_TOOLS_MENU_PASTE_AND_GO, PopupMenuActionPasteAndGo,
-          @"popup_menu_paste_and_go", kToolsMenuPasteAndGo);
-    }
+  if (search_engines::SupportsSearchByImage(self.templateURLService) &&
+      clipboardRecentContent->GetRecentImageFromClipboard()) {
+    copiedContentItem = CreateTableViewItem(
+        IDS_IOS_TOOLS_MENU_SEARCH_COPIED_IMAGE,
+        PopupMenuActionSearchCopiedImage, @"popup_menu_paste_and_go",
+        kToolsMenuCopiedImageSearch);
+  } else if (clipboardRecentContent->GetRecentURLFromClipboard()) {
+    copiedContentItem = CreateTableViewItem(
+        IDS_IOS_TOOLS_MENU_VISIT_COPIED_LINK, PopupMenuActionPasteAndGo,
+        @"popup_menu_paste_and_go", kToolsMenuPasteAndGo);
+  } else if (clipboardRecentContent->GetRecentTextFromClipboard()) {
+    copiedContentItem = CreateTableViewItem(
+        IDS_IOS_TOOLS_MENU_SEARCH_COPIED_TEXT, PopupMenuActionPasteAndGo,
+        @"popup_menu_paste_and_go", kToolsMenuPasteAndGo);
   }
   if (copiedContentItem) {
     if (base::FeatureList::IsEnabled(kToolbarNewTabButton)) {
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 4d118cb..2426085 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -130,6 +130,7 @@
     "//ios/chrome/browser/feature_engagement",
     "//ios/chrome/browser/history",
     "//ios/chrome/browser/mailto:feature_flags",
+    "//ios/chrome/browser/main",
     "//ios/chrome/browser/passwords",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/signin",
@@ -221,6 +222,7 @@
     "//google_apis",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/prefs:browser_prefs",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/signin:test_support",
@@ -281,6 +283,7 @@
     "//ios/chrome/browser/browsing_data:counters",
     "//ios/chrome/browser/content_settings",
     "//ios/chrome/browser/mailto:feature_flags",
+    "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/passwords",
     "//ios/chrome/browser/prefs:browser_prefs",
     "//ios/chrome/browser/search_engines",
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_egtest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_egtest.mm
index 72d7f6c..f965837 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_egtest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_egtest.mm
@@ -89,13 +89,16 @@
 
 @implementation AutofillAddCreditCardTestCase
 
-- (void)setUp {
-  [super setUp];
+- (void)launchAppForTestMethod {
   [[AppLaunchManager sharedManager]
       ensureAppLaunchedWithFeaturesEnabled:{kSettingsAddPaymentMethod,
                                             kCreditCardScanner}
                                   disabled:{}
                               forceRestart:NO];
+}
+
+- (void)setUp {
+  [super setUp];
   GREYAssertTrue([ChromeEarlGrey isSettingsAddPaymentMethodEnabled],
                  @"SettingsAddPaymentMethod should be enabled");
   GREYAssertTrue([ChromeEarlGrey isCreditCardScannerEnabled],
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.h b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
index fc2feb4..eed5dda2 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.h
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
@@ -9,6 +9,7 @@
 
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 
+class Browser;
 @protocol BrowserCommands;
 @protocol ImportDataControllerDelegate;
 @protocol UserFeedbackDataSource;
@@ -54,87 +55,100 @@
 @interface SettingsNavigationController
     : UINavigationController<ApplicationSettingsCommands>
 
+// Returns the  browser state associated with this view controller;
+@property(nonatomic, readonly) ios::ChromeBrowserState* mainBrowserState;
+
 // Creates a new SettingsTableViewController and the chrome around it.
-// |browserState| is used to personalize some settings aspects and should not be
+// |browser| is the browser where settings are being displayed and should not be
 // nil nor Off-the-Record. |delegate| may be nil.
-+ (SettingsNavigationController*)
-newSettingsMainControllerWithBrowserState:(ios::ChromeBrowserState*)browserState
-                                 delegate:
-                                     (id<SettingsNavigationControllerDelegate>)
++ (instancetype)
+    mainSettingsControllerForBrowser:(Browser*)browser
+                            delegate:(id<SettingsNavigationControllerDelegate>)
                                          delegate;
 
 // Creates a new AccountsTableViewController and the chrome around it.
-// |browserState| is used to personalize some settings aspects and should not be
+// |browser| is the browser where settings are being displayed and should not be
 // nil. |delegate| may be nil.
-+ (SettingsNavigationController*)
-newAccountsController:(ios::ChromeBrowserState*)browserState
-             delegate:(id<SettingsNavigationControllerDelegate>)delegate;
++ (instancetype)
+    accountsControllerForBrowser:(Browser*)browser
+                        delegate:
+                            (id<SettingsNavigationControllerDelegate>)delegate;
 
 // Creates a new GoogleServicesSettingsCollectionViewController and the chrome
-// around it. |browserState| is used to personalize some settings aspects and
+// around it. |browser| is the browser where settings are being displayed and
 // should not be nil. |delegate| may be nil.
-+ (SettingsNavigationController*)
-    newGoogleServicesController:(ios::ChromeBrowserState*)browserState
-                       delegate:
-                           (id<SettingsNavigationControllerDelegate>)delegate;
++ (instancetype)
+    googleServicesControllerForBrowser:(Browser*)browser
+                              delegate:
+                                  (id<SettingsNavigationControllerDelegate>)
+                                      delegate;
 
 // Creates a new SyncEncryptionPassphraseCollectionViewController and the chrome
-// around it. |browserState| is used to personalize some settings aspects and
+// around it. |browser| is the browser where settings are being displayed and
 // should not be nil. |delegate| may be nil.
-+ (SettingsNavigationController*)
-newSyncEncryptionPassphraseController:(ios::ChromeBrowserState*)browserState
++ (instancetype)
+    syncPassphraseControllerForBrowser:(Browser*)browser
+                              delegate:
+                                  (id<SettingsNavigationControllerDelegate>)
+                                      delegate;
+
+// Creates a new SavePasswordsCollectionViewController and the chrome around it.
+// |browser| is the browser where settings are being displayed and should not be
+// nil. |delegate| may be nil.
++ (instancetype)
+    savePasswordsControllerForBrowser:(Browser*)browser
                              delegate:(id<SettingsNavigationControllerDelegate>)
                                           delegate;
 
-// Creates a new SavePasswordsCollectionViewController and the chrome around it.
-// |browserState| is used to personalize some settings aspects and should not be
-// nil. |delegate| may be nil.
-+ (SettingsNavigationController*)
-newSavePasswordsController:(ios::ChromeBrowserState*)browserState
-                  delegate:(id<SettingsNavigationControllerDelegate>)delegate;
-
 // Creates and displays a new UIViewController for user to report an issue.
-// |browserState| is used to personalize some settings aspects and should not
-// be nil. |dataSource| is used to populate the UIViewController. |dispatcher|,
+// |browser| is the browser where settings are being displayed and should not be
+// nil. |dataSource| is used to populate the UIViewController. |dispatcher|,
 // which can be nil, is an object that can perform operations for the view
 // controller. |delegate| may be nil.
-+ (SettingsNavigationController*)
-    newUserFeedbackController:(ios::ChromeBrowserState*)browserState
-                     delegate:(id<SettingsNavigationControllerDelegate>)delegate
-           feedbackDataSource:(id<UserFeedbackDataSource>)dataSource
-                   dispatcher:(id<ApplicationCommands>)dispatcher;
++ (instancetype)
+    userFeedbackControllerForBrowser:(Browser*)browser
+                            delegate:(id<SettingsNavigationControllerDelegate>)
+                                         delegate
+                  feedbackDataSource:(id<UserFeedbackDataSource>)dataSource
+                          dispatcher:(id<ApplicationCommands>)dispatcher;
 
-// Creates and displays a new ImportDataTableViewController. |browserState|
+// Creates and displays a new ImportDataTableViewController. |browser|
 // should not be nil.
-+ (SettingsNavigationController*)
-newImportDataController:(ios::ChromeBrowserState*)browserState
-               delegate:(id<SettingsNavigationControllerDelegate>)delegate
-     importDataDelegate:(id<ImportDataControllerDelegate>)importDataDelegate
-              fromEmail:(NSString*)fromEmail
-                toEmail:(NSString*)toEmail
-             isSignedIn:(BOOL)isSignedIn;
++ (instancetype)
+    importDataControllerForBrowserState:(ios::ChromeBrowserState*)browserState
+                               delegate:
+                                   (id<SettingsNavigationControllerDelegate>)
+                                       delegate
+                     importDataDelegate:
+                         (id<ImportDataControllerDelegate>)importDataDelegate
+                              fromEmail:(NSString*)fromEmail
+                                toEmail:(NSString*)toEmail
+                             isSignedIn:(BOOL)isSignedIn;
 
 // Creates a new AutofillProfileTableViewController and the chrome around
-// it. |browserState| is used to personalize some settings aspects and should
+// it. |browser| is the browser where settings are being displayed and should
 // not be nil. |delegate| may be nil.
-+ (SettingsNavigationController*)
-newAutofillProfilleController:(ios::ChromeBrowserState*)browserState
-                     delegate:
-                         (id<SettingsNavigationControllerDelegate>)delegate;
++ (instancetype)
+    autofillProfileControllerForBrowser:(Browser*)browser
+                               delegate:
+                                   (id<SettingsNavigationControllerDelegate>)
+                                       delegate;
 
 // Creates a new AutofillCreditCardCollectionViewController and the chrome
-// around it. |browserState| is used to personalize some settings aspects and
+// around it. |browser| is the browser where settings are being displayed and
 // should not be nil. |delegate| may be nil.
-+ (SettingsNavigationController*)
-newAutofillCreditCardController:(ios::ChromeBrowserState*)browserState
-                       delegate:
-                           (id<SettingsNavigationControllerDelegate>)delegate;
++ (instancetype)
+    autofillCreditCardControllerForBrowser:(Browser*)browser
+                                  delegate:
+                                      (id<SettingsNavigationControllerDelegate>)
+                                          delegate;
 
 // Initializes the UINavigationController with |rootViewController|.
 - (instancetype)
-initWithRootViewController:(UIViewController*)rootViewController
-              browserState:(ios::ChromeBrowserState*)browserState
-                  delegate:(id<SettingsNavigationControllerDelegate>)delegate
+    initWithRootViewController:(UIViewController*)rootViewController
+                  browserState:(ios::ChromeBrowserState*)browserState
+                      delegate:
+                          (id<SettingsNavigationControllerDelegate>)delegate
     NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)initWithRootViewController:(UIViewController*)rootViewController
@@ -150,9 +164,6 @@
 // owned by SettingsNavigationController.
 - (UIBarButtonItem*)doneButton;
 
-// Returns the current main browser state.
-- (ios::ChromeBrowserState*)mainBrowserState;
-
 // Notifies this |SettingsNavigationController| of a dismissal such
 // that it has a possibility to do necessary clean up.
 - (void)cleanUpSettings;
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
index 733f38f3..9986e9c30 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -7,6 +7,7 @@
 #include "base/mac/foundation_util.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/main/browser.h"
 #include "ios/chrome/browser/sync/sync_setup_service.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #import "ios/chrome/browser/ui/icons/chrome_icon.h"
@@ -61,47 +62,49 @@
 
 @end
 
-@implementation SettingsNavigationController {
-  ios::ChromeBrowserState* mainBrowserState_;  // weak
-}
+@implementation SettingsNavigationController
 
 #pragma mark - SettingsNavigationController methods.
 
-+ (SettingsNavigationController*)
-newSettingsMainControllerWithBrowserState:(ios::ChromeBrowserState*)browserState
-                                 delegate:
-                                     (id<SettingsNavigationControllerDelegate>)
++ (instancetype)
+    mainSettingsControllerForBrowser:(Browser*)browser
+                            delegate:(id<SettingsNavigationControllerDelegate>)
                                          delegate {
+  DCHECK(browser);
   SettingsTableViewController* controller = [[SettingsTableViewController alloc]
-      initWithBrowserState:browserState
-                dispatcher:[delegate dispatcherForSettings]];
+      initWithBrowser:browser
+           dispatcher:[delegate dispatcherForSettings]];
   SettingsNavigationController* nc = [[SettingsNavigationController alloc]
       initWithRootViewController:controller
-                    browserState:browserState
+                    browserState:browser->GetBrowserState()
                         delegate:delegate];
   [controller navigationItem].rightBarButtonItem = [nc doneButton];
   return nc;
 }
 
-+ (SettingsNavigationController*)
-newAccountsController:(ios::ChromeBrowserState*)browserState
-             delegate:(id<SettingsNavigationControllerDelegate>)delegate {
-  AccountsTableViewController* controller =
-      [[AccountsTableViewController alloc] initWithBrowserState:browserState
-                                      closeSettingsOnAddAccount:YES];
++ (instancetype)
+    accountsControllerForBrowser:(Browser*)browser
+                        delegate:
+                            (id<SettingsNavigationControllerDelegate>)delegate {
+  DCHECK(browser);
+  AccountsTableViewController* controller = [[AccountsTableViewController alloc]
+           initWithBrowserState:browser->GetBrowserState()
+      closeSettingsOnAddAccount:YES];
   controller.dispatcher = [delegate dispatcherForSettings];
   SettingsNavigationController* nc = [[SettingsNavigationController alloc]
       initWithRootViewController:controller
-                    browserState:browserState
+                    browserState:browser->GetBrowserState()
                         delegate:delegate];
   [controller navigationItem].leftBarButtonItem = [nc cancelButton];
   return nc;
 }
 
-+ (SettingsNavigationController*)
-    newGoogleServicesController:(ios::ChromeBrowserState*)browserState
-                       delegate:
-                           (id<SettingsNavigationControllerDelegate>)delegate {
++ (instancetype)
+    googleServicesControllerForBrowser:(Browser*)browser
+                              delegate:
+                                  (id<SettingsNavigationControllerDelegate>)
+                                      delegate {
+  DCHECK(browser);
   // GoogleServicesSettings uses a coordinator to be presented, therefore the
   // view controller is not accessible. Prefer creating a
   // |SettingsNavigationController| with a nil root view controller and then
@@ -109,17 +112,59 @@
   // root view controller.
   SettingsNavigationController* nc = [[SettingsNavigationController alloc]
       initWithRootViewController:nil
-                    browserState:browserState
+                    browserState:browser->GetBrowserState()
                         delegate:delegate];
   [nc showGoogleServices];
   return nc;
 }
 
-+ (SettingsNavigationController*)
-    newUserFeedbackController:(ios::ChromeBrowserState*)browserState
-                     delegate:(id<SettingsNavigationControllerDelegate>)delegate
-           feedbackDataSource:(id<UserFeedbackDataSource>)dataSource
-                   dispatcher:(id<ApplicationCommands>)dispatcher {
++ (instancetype)
+    syncPassphraseControllerForBrowser:(Browser*)browser
+                              delegate:
+                                  (id<SettingsNavigationControllerDelegate>)
+                                      delegate {
+  DCHECK(browser);
+  SyncEncryptionPassphraseTableViewController* controller =
+      [[SyncEncryptionPassphraseTableViewController alloc]
+          initWithBrowserState:browser->GetBrowserState()];
+  controller.dispatcher = [delegate dispatcherForSettings];
+  SettingsNavigationController* nc = [[SettingsNavigationController alloc]
+      initWithRootViewController:controller
+                    browserState:browser->GetBrowserState()
+                        delegate:delegate];
+  [controller navigationItem].leftBarButtonItem = [nc cancelButton];
+  return nc;
+}
+
++ (instancetype)
+    savePasswordsControllerForBrowser:(Browser*)browser
+                             delegate:(id<SettingsNavigationControllerDelegate>)
+                                          delegate {
+  DCHECK(browser);
+  PasswordsTableViewController* controller =
+      [[PasswordsTableViewController alloc]
+          initWithBrowserState:browser->GetBrowserState()];
+  controller.dispatcher = [delegate dispatcherForSettings];
+
+  SettingsNavigationController* nc = [[SettingsNavigationController alloc]
+      initWithRootViewController:controller
+                    browserState:browser->GetBrowserState()
+                        delegate:delegate];
+  [controller navigationItem].rightBarButtonItem = [nc doneButton];
+
+  // Make sure the cancel button is always present, as the Save Passwords screen
+  // isn't just shown from Settings.
+  [controller navigationItem].leftBarButtonItem = [nc cancelButton];
+  return nc;
+}
+
++ (instancetype)
+    userFeedbackControllerForBrowser:(Browser*)browser
+                            delegate:(id<SettingsNavigationControllerDelegate>)
+                                         delegate
+                  feedbackDataSource:(id<UserFeedbackDataSource>)dataSource
+                          dispatcher:(id<ApplicationCommands>)dispatcher {
+  DCHECK(browser);
   DCHECK(ios::GetChromeBrowserProvider()
              ->GetUserFeedbackProvider()
              ->IsUserFeedbackEnabled());
@@ -130,7 +175,7 @@
   DCHECK(controller);
   SettingsNavigationController* nc = [[SettingsNavigationController alloc]
       initWithRootViewController:controller
-                    browserState:browserState
+                    browserState:browser->GetBrowserState()
                         delegate:delegate];
   // If the controller overrides overrideUserInterfaceStyle, respect that in the
   // SettingsNavigationController.
@@ -140,48 +185,16 @@
   return nc;
 }
 
-+ (SettingsNavigationController*)
-newSyncEncryptionPassphraseController:(ios::ChromeBrowserState*)browserState
-                             delegate:(id<SettingsNavigationControllerDelegate>)
-                                          delegate {
-  SyncEncryptionPassphraseTableViewController* controller =
-      [[SyncEncryptionPassphraseTableViewController alloc]
-          initWithBrowserState:browserState];
-  controller.dispatcher = [delegate dispatcherForSettings];
-  SettingsNavigationController* nc = [[SettingsNavigationController alloc]
-      initWithRootViewController:controller
-                    browserState:browserState
-                        delegate:delegate];
-  [controller navigationItem].leftBarButtonItem = [nc cancelButton];
-  return nc;
-}
-
-+ (SettingsNavigationController*)
-newSavePasswordsController:(ios::ChromeBrowserState*)browserState
-                  delegate:(id<SettingsNavigationControllerDelegate>)delegate {
-  PasswordsTableViewController* controller =
-      [[PasswordsTableViewController alloc] initWithBrowserState:browserState];
-  controller.dispatcher = [delegate dispatcherForSettings];
-
-  SettingsNavigationController* nc = [[SettingsNavigationController alloc]
-      initWithRootViewController:controller
-                    browserState:browserState
-                        delegate:delegate];
-  [controller navigationItem].rightBarButtonItem = [nc doneButton];
-
-  // Make sure the cancel button is always present, as the Save Passwords screen
-  // isn't just shown from Settings.
-  [controller navigationItem].leftBarButtonItem = [nc cancelButton];
-  return nc;
-}
-
-+ (SettingsNavigationController*)
-newImportDataController:(ios::ChromeBrowserState*)browserState
-               delegate:(id<SettingsNavigationControllerDelegate>)delegate
-     importDataDelegate:(id<ImportDataControllerDelegate>)importDataDelegate
-              fromEmail:(NSString*)fromEmail
-                toEmail:(NSString*)toEmail
-             isSignedIn:(BOOL)isSignedIn {
++ (instancetype)
+    importDataControllerForBrowserState:(ios::ChromeBrowserState*)browserState
+                               delegate:
+                                   (id<SettingsNavigationControllerDelegate>)
+                                       delegate
+                     importDataDelegate:
+                         (id<ImportDataControllerDelegate>)importDataDelegate
+                              fromEmail:(NSString*)fromEmail
+                                toEmail:(NSString*)toEmail
+                             isSignedIn:(BOOL)isSignedIn {
   UIViewController* controller =
       [[ImportDataTableViewController alloc] initWithDelegate:importDataDelegate
                                                     fromEmail:fromEmail
@@ -199,18 +212,20 @@
   return nc;
 }
 
-+ (SettingsNavigationController*)
-newAutofillProfilleController:(ios::ChromeBrowserState*)browserState
-                     delegate:
-                         (id<SettingsNavigationControllerDelegate>)delegate {
++ (instancetype)
+    autofillProfileControllerForBrowser:(Browser*)browser
+                               delegate:
+                                   (id<SettingsNavigationControllerDelegate>)
+                                       delegate {
+  DCHECK(browser);
   AutofillProfileTableViewController* controller =
       [[AutofillProfileTableViewController alloc]
-          initWithBrowserState:browserState];
+          initWithBrowserState:browser->GetBrowserState()];
   controller.dispatcher = [delegate dispatcherForSettings];
 
   SettingsNavigationController* nc = [[SettingsNavigationController alloc]
       initWithRootViewController:controller
-                    browserState:browserState
+                    browserState:browser->GetBrowserState()
                         delegate:delegate];
 
   // Make sure the cancel button is always present, as the Autofill screen
@@ -219,18 +234,20 @@
   return nc;
 }
 
-+ (SettingsNavigationController*)
-newAutofillCreditCardController:(ios::ChromeBrowserState*)browserState
-                       delegate:
-                           (id<SettingsNavigationControllerDelegate>)delegate {
++ (instancetype)
+    autofillCreditCardControllerForBrowser:(Browser*)browser
+                                  delegate:
+                                      (id<SettingsNavigationControllerDelegate>)
+                                          delegate {
+  DCHECK(browser);
   AutofillCreditCardTableViewController* controller =
       [[AutofillCreditCardTableViewController alloc]
-          initWithBrowserState:browserState];
+          initWithBrowserState:browser->GetBrowserState()];
   controller.dispatcher = [delegate dispatcherForSettings];
 
   SettingsNavigationController* nc = [[SettingsNavigationController alloc]
       initWithRootViewController:controller
-                    browserState:browserState
+                    browserState:browser->GetBrowserState()
                         delegate:delegate];
 
   // Make sure the cancel button is always present, as the Autofill screen
@@ -242,14 +259,13 @@
 #pragma mark - Lifecycle
 
 - (instancetype)
-initWithRootViewController:(UIViewController*)rootViewController
-              browserState:(ios::ChromeBrowserState*)browserState
-                  delegate:(id<SettingsNavigationControllerDelegate>)delegate {
-  DCHECK(browserState);
-  DCHECK(!browserState->IsOffTheRecord());
+    initWithRootViewController:(UIViewController*)rootViewController
+                  browserState:(ios::ChromeBrowserState*)browserState
+                      delegate:
+                          (id<SettingsNavigationControllerDelegate>)delegate {
   self = [super initWithRootViewController:rootViewController];
   if (self) {
-    mainBrowserState_ = browserState;
+    _mainBrowserState = browserState;
     _settingsNavigationDelegate = delegate;
     self.modalPresentationStyle = UIModalPresentationFormSheet;
     // Set the presentationController delegate. This is used for swipe down to
@@ -350,7 +366,7 @@
   self.googleServicesSettingsCoordinator =
       [[GoogleServicesSettingsCoordinator alloc]
           initWithBaseViewController:self
-                        browserState:mainBrowserState_
+                        browserState:self.mainBrowserState
                                 mode:GoogleServicesSettingsModeSettings];
   self.googleServicesSettingsCoordinator.dispatcher =
       [self.settingsNavigationDelegate dispatcherForSettings];
@@ -473,7 +489,7 @@
 - (void)showAccountsSettingsFromViewController:
     (UIViewController*)baseViewController {
   AccountsTableViewController* controller = [[AccountsTableViewController alloc]
-           initWithBrowserState:mainBrowserState_
+           initWithBrowserState:self.mainBrowserState
       closeSettingsOnAddAccount:NO];
   controller.dispatcher =
       [self.settingsNavigationDelegate dispatcherForSettings];
@@ -491,7 +507,7 @@
     (UIViewController*)baseViewController {
   SyncEncryptionPassphraseTableViewController* controller =
       [[SyncEncryptionPassphraseTableViewController alloc]
-          initWithBrowserState:mainBrowserState_];
+          initWithBrowserState:self.mainBrowserState];
   controller.dispatcher =
       [self.settingsNavigationDelegate dispatcherForSettings];
   [self pushViewController:controller animated:YES];
@@ -502,7 +518,7 @@
     (UIViewController*)baseViewController {
   PasswordsTableViewController* controller =
       [[PasswordsTableViewController alloc]
-          initWithBrowserState:mainBrowserState_];
+          initWithBrowserState:self.mainBrowserState];
   controller.dispatcher =
       [self.settingsNavigationDelegate dispatcherForSettings];
   [self pushViewController:controller animated:YES];
@@ -513,7 +529,7 @@
     (UIViewController*)baseViewController {
   AutofillProfileTableViewController* controller =
       [[AutofillProfileTableViewController alloc]
-          initWithBrowserState:mainBrowserState_];
+          initWithBrowserState:self.mainBrowserState];
   controller.dispatcher =
       [self.settingsNavigationDelegate dispatcherForSettings];
   [self pushViewController:controller animated:YES];
@@ -524,18 +540,12 @@
     (UIViewController*)baseViewController {
   AutofillCreditCardTableViewController* controller =
       [[AutofillCreditCardTableViewController alloc]
-          initWithBrowserState:mainBrowserState_];
+          initWithBrowserState:self.mainBrowserState];
   controller.dispatcher =
       [self.settingsNavigationDelegate dispatcherForSettings];
   [self pushViewController:controller animated:YES];
 }
 
-#pragma mark - Profile
-
-- (ios::ChromeBrowserState*)mainBrowserState {
-  return mainBrowserState_;
-}
-
 #pragma mark - UIResponder
 
 - (BOOL)canBecomeFirstResponder {
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller_unittest.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller_unittest.mm
index 3a7f8de..a4beb22 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller_unittest.mm
@@ -12,6 +12,7 @@
 #include "components/search_engines/template_url_service.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
+#include "ios/chrome/browser/main/test_browser.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/signin/authentication_service.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
@@ -51,6 +52,9 @@
         ios::TemplateURLServiceFactory::GetInstance(),
         ios::TemplateURLServiceFactory::GetDefaultFactory());
     chrome_browser_state_ = test_cbs_builder.Build();
+    WebStateList* web_state_list = nullptr;
+    browser_ = std::make_unique<TestBrowser>(chrome_browser_state_.get(),
+                                             web_state_list);
 
     mockDelegate_ = [OCMockObject
         niceMockForProtocol:@protocol(SettingsNavigationControllerDelegate)];
@@ -80,6 +84,7 @@
   web::WebTaskEnvironment task_environment_;
   IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+  std::unique_ptr<Browser> browser_;
   id mockDelegate_;
   NSString* initialValueForSpdyProxyEnabled_;
 };
@@ -90,9 +95,8 @@
   @autoreleasepool {
     SettingsNavigationController* settingsController =
         [SettingsNavigationController
-            newSettingsMainControllerWithBrowserState:chrome_browser_state_
-                                                          .get()
-                                             delegate:nil];
+            mainSettingsControllerForBrowser:browser_.get()
+                                    delegate:nil];
     UIViewController* viewController =
         [[UIViewController alloc] initWithNibName:nil bundle:nil];
     [settingsController pushViewController:viewController animated:NO];
@@ -112,9 +116,8 @@
   @autoreleasepool {
     SettingsNavigationController* settingsController =
         [SettingsNavigationController
-            newSettingsMainControllerWithBrowserState:chrome_browser_state_
-                                                          .get()
-                                             delegate:nil];
+            mainSettingsControllerForBrowser:browser_.get()
+                                    delegate:nil];
     EXPECT_EQ(1U, [[settingsController viewControllers] count]);
 
     EXPECT_FALSE([settingsController popViewControllerAnimated:NO]);
@@ -130,9 +133,8 @@
   @autoreleasepool {
     SettingsNavigationController* settingsController =
         [SettingsNavigationController
-            newSettingsMainControllerWithBrowserState:chrome_browser_state_
-                                                          .get()
-                                             delegate:mockDelegate_];
+            mainSettingsControllerForBrowser:browser_.get()
+                                    delegate:mockDelegate_];
     UIViewController* viewController =
         [[UIViewController alloc] initWithNibName:nil bundle:nil];
     [settingsController pushViewController:viewController animated:NO];
@@ -153,9 +155,8 @@
   @autoreleasepool {
     SettingsNavigationController* settingsController =
         [SettingsNavigationController
-            newSettingsMainControllerWithBrowserState:chrome_browser_state_
-                                                          .get()
-                                             delegate:mockDelegate_];
+            mainSettingsControllerForBrowser:browser_.get()
+                                    delegate:mockDelegate_];
     EXPECT_EQ(1U, [[settingsController viewControllers] count]);
     [[mockDelegate_ expect] closeSettings];
     [settingsController popViewControllerOrCloseSettingsAnimated:NO];
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.h b/ios/chrome/browser/ui/settings/settings_table_view_controller.h
index 5668faa..e3b45bb 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.h
@@ -9,12 +9,10 @@
 #import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h"
 
 @protocol ApplicationCommands;
+class Browser;
 @protocol BrowserCommands;
 @protocol SettingsMainPageCommands;
 @class SigninInteractionController;
-namespace ios {
-class ChromeBrowserState;
-}  // namespace ios
 
 // The accessibility identifier of the settings TableView.
 extern NSString* const kSettingsTableViewId;
@@ -40,11 +38,12 @@
 @property(weak, nonatomic) id<SettingsMainPageCommands>
     settingsMainPageDispatcher;
 
-// Initializes a new SettingsTableViewController. |browserState| must not
-// be nil and must not be an off-the-record browser state.
-- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
-                          dispatcher:(id<ApplicationCommands, BrowserCommands>)
-                                         dispatcher NS_DESIGNATED_INITIALIZER;
+// Initializes a new SettingsTableViewController. |browser| must not
+// be nil and must not be associated with an off the record browser state.
+- (instancetype)initWithBrowser:(Browser*)browser
+                     dispatcher:
+                         (id<ApplicationCommands, BrowserCommands>)dispatcher
+    NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)initWithTableViewStyle:(UITableViewStyle)style
                            appBarStyle:
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index b7267ac..f9e28c9b 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -24,6 +24,7 @@
 #include "components/sync/driver/sync_service.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/main/browser.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/search_engines/search_engine_observer_bridge.h"
@@ -170,7 +171,9 @@
     SigninPresenter,
     SigninPromoViewConsumer,
     SyncObserverModelBridge> {
-  // The current browser state that hold the settings. Never off the record.
+  // The browser where the settings are being displayed.
+  Browser* _browser;
+  // The browser state for |_browser|. Never off the record.
   ios::ChromeBrowserState* _browserState;  // weak
   // Bridge for TemplateURLServiceObserver.
   std::unique_ptr<SearchEngineObserverBridge> _searchEngineObserverBridge;
@@ -244,17 +247,19 @@
 
 #pragma mark Initialization
 
-- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
-                          dispatcher:(id<ApplicationCommands, BrowserCommands>)
-                                         dispatcher {
-  DCHECK(!browserState->IsOffTheRecord());
+- (instancetype)initWithBrowser:(Browser*)browser
+                     dispatcher:
+                         (id<ApplicationCommands, BrowserCommands>)dispatcher {
+  DCHECK(browser);
+  DCHECK(!browser->GetBrowserState()->IsOffTheRecord());
   UITableViewStyle style = base::FeatureList::IsEnabled(kSettingsRefresh)
                                ? UITableViewStylePlain
                                : UITableViewStyleGrouped;
   self = [super initWithTableViewStyle:style
                            appBarStyle:ChromeTableViewControllerStyleNoAppBar];
   if (self) {
-    _browserState = browserState;
+    _browser = browser;
+    _browserState = _browser->GetBrowserState();
     self.title = l10n_util::GetNSStringWithFixup(IDS_IOS_SETTINGS_TITLE);
     _searchEngineObserverBridge.reset(new SearchEngineObserverBridge(
         self,
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index 95275988..2750f6d 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -16,9 +16,6 @@
 const base::Feature kSnapshotDrawView{"SnapshotDrawView",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kCopiedContentBehavior{"CopiedContentBehavior",
-                                           base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kSettingsRefresh{"SettingsRefresh",
                                      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 cde1b3b..97912cd 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -17,11 +17,6 @@
 // Feature to take snapshots using |-drawViewHierarchy:|.
 extern const base::Feature kSnapshotDrawView;
 
-// Feature to rework handling of copied content (url/string/image) in the ui.
-// This feature is used in extensions. If you modify it significantly, you may
-// want to update the version in |app_group_field_trial_version|.
-extern const base::Feature kCopiedContentBehavior;
-
 // Feature to apply UI Refresh theme to the settings.
 extern const base::Feature kSettingsRefresh;
 
diff --git a/ios/chrome/browser/ui/webui/sync_internals/BUILD.gn b/ios/chrome/browser/ui/webui/sync_internals/BUILD.gn
index 1fb9f69..67b18a2 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/BUILD.gn
+++ b/ios/chrome/browser/ui/webui/sync_internals/BUILD.gn
@@ -14,6 +14,7 @@
     "//components/browser_sync",
     "//components/resources",
     "//components/sync",
+    "//components/sync/driver:resources",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/signin",
diff --git a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.cc b/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.cc
index 92afd10..1036f667 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.cc
+++ b/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "components/grit/components_resources.h"
+#include "components/grit/sync_driver_resources.h"
 #include "components/sync/driver/about_sync_util.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
diff --git a/ios/chrome/browser/web_state_list/web_state_list.h b/ios/chrome/browser/web_state_list/web_state_list.h
index 86e3108..8f9a349 100644
--- a/ios/chrome/browser/web_state_list/web_state_list.h
+++ b/ios/chrome/browser/web_state_list/web_state_list.h
@@ -159,6 +159,12 @@
   // Removes an observer from the model.
   void RemoveObserver(WebStateListObserver* observer);
 
+  // Performs mutating operations on the WebStateList as batched operation.
+  // The observers will be notified by WillBeginBatchOperation() before the
+  // |operation| callback is executed and by BatchOperationEnded() after it
+  // has completed.
+  void PerformBatchOperation(base::OnceCallback<void(WebStateList*)> operation);
+
   // Invalid index.
   static const int kInvalidIndex = -1;
 
diff --git a/ios/chrome/browser/web_state_list/web_state_list.mm b/ios/chrome/browser/web_state_list/web_state_list.mm
index 06c8828c..2f47b72 100644
--- a/ios/chrome/browser/web_state_list/web_state_list.mm
+++ b/ios/chrome/browser/web_state_list/web_state_list.mm
@@ -333,13 +333,13 @@
 }
 
 void WebStateList::CloseAllWebStates(int close_flags) {
-  const bool user_action = IsClosingFlagSet(close_flags, CLOSE_USER_ACTION);
-  for (auto& observer : observers_)
-    observer.WillCloseAllWebStates(this, user_action);
-  while (!empty())
-    CloseWebStateAt(count() - 1, close_flags);
-  for (auto& observer : observers_)
-    observer.DidCloseAllWebStates(this, user_action);
+  PerformBatchOperation(base::BindOnce(
+      [](int close_flags, WebStateList* web_state_list) {
+        while (!web_state_list->empty())
+          web_state_list->CloseWebStateAt(web_state_list->count() - 1,
+                                          close_flags);
+      },
+      close_flags));
 }
 
 void WebStateList::ActivateWebStateAt(int index) {
@@ -358,6 +358,16 @@
   observers_.RemoveObserver(observer);
 }
 
+void WebStateList::PerformBatchOperation(
+    base::OnceCallback<void(WebStateList*)> operation) {
+  for (auto& observer : observers_)
+    observer.WillBeginBatchOperation(this);
+  if (!operation.is_null())
+    std::move(operation).Run(this);
+  for (auto& observer : observers_)
+    observer.BatchOperationEnded(this);
+}
+
 void WebStateList::ClearOpenersReferencing(int index) {
   web::WebState* old_web_state = web_state_wrappers_[index]->web_state();
   for (auto& web_state_wrapper : web_state_wrappers_) {
diff --git a/ios/chrome/browser/web_state_list/web_state_list_observer.h b/ios/chrome/browser/web_state_list/web_state_list_observer.h
index 79320fe7..a40612e 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_observer.h
+++ b/ios/chrome/browser/web_state_list/web_state_list_observer.h
@@ -85,15 +85,17 @@
                                    int active_index,
                                    int reason);
 
-  // Invoked before all WebStates are closed in the WebStateList. If the
-  // WebState is closed due to user action, |user_action| will be true.
-  virtual void WillCloseAllWebStates(WebStateList* web_state_list,
-                                     bool user_action);
+  // Invoked before a batched operations begins. The observer can use this
+  // notification if it is interested in considering all those individual
+  // operations as a single mutation of the WebStateList (e.g. considering
+  // insertion of multiple tabs as a restoration operation).
+  virtual void WillBeginBatchOperation(WebStateList* web_state_list);
 
-  // Invoked after all WebStates are closed in the WebStateList. If the WebState
-  // is closed due to user action, |user_action| will be true.
-  virtual void DidCloseAllWebStates(WebStateList* web_state_list,
-                                    bool user_action);
+  // Invoked after the completion of batched operations. The observer can
+  // investigate the state of the WebStateList to detect any changes that
+  // were performed on it during the batch (e.g. detect that all tabs were
+  // closed at once).
+  virtual void BatchOperationEnded(WebStateList* web_state_list);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(WebStateListObserver);
diff --git a/ios/chrome/browser/web_state_list/web_state_list_observer.mm b/ios/chrome/browser/web_state_list/web_state_list_observer.mm
index e9ccd1a..168cec0 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_observer.mm
+++ b/ios/chrome/browser/web_state_list/web_state_list_observer.mm
@@ -46,8 +46,7 @@
                                                int active_index,
                                                int reason) {}
 
-void WebStateListObserver::WillCloseAllWebStates(WebStateList* web_state_list,
-                                                 bool user_action) {}
+void WebStateListObserver::WillBeginBatchOperation(
+    WebStateList* web_state_list) {}
 
-void WebStateListObserver::DidCloseAllWebStates(WebStateList* web_state_list,
-                                                bool user_action) {}
+void WebStateListObserver::BatchOperationEnded(WebStateList* web_state_list) {}
diff --git a/ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h b/ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h
index 6cc2d06d..dd0d5f9b 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h
+++ b/ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h
@@ -68,58 +68,58 @@
                     atIndex:(int)atIndex
                      reason:(int)reason;
 
-// Invoked before all the WebStates are closed in the WebStateList. If the
-// WebState is closed due to user action, |userAction| will be true.
-- (void)webStateList:(WebStateList*)webStateList
-    willCloseAllWebStatesUserAction:(BOOL)userAction;
+// Invoked before a batched operations begins. The observer can use this
+// notification if it is interested in considering all those individual
+// operations as a single mutation of the WebStateList (e.g. considering
+// insertion of multiple tabs as a restoration operation).
+- (void)webStateListWillBeginBatchOperation:(WebStateList*)webStateList;
 
-// Invoked after all the WebStates are closed in the WebStateList. If the
-// WebState is closed due to user action, |userAction| will be true.
-- (void)webStateList:(WebStateList*)webStateList
-    didCloseAllWebStatesUserAction:(BOOL)userAction;
+// Invoked after the completion of batched operations. The observer can
+// investigate the state of the WebStateList to detect any changes that
+// were performed on it during the batch (e.g. detect that all tabs were
+// closed at once).
+- (void)webStateListBatchOperationEnded:(WebStateList*)webStateList;
 
 @end
 
 // Observer that bridges WebStateList events to an Objective-C observer that
 // implements the WebStateListObserver protocol (the observer is *not* owned).
-class WebStateListObserverBridge : public WebStateListObserver {
+class WebStateListObserverBridge final : public WebStateListObserver {
  public:
   explicit WebStateListObserverBridge(id<WebStateListObserving> observer);
-  ~WebStateListObserverBridge() override;
+  ~WebStateListObserverBridge() final;
 
  private:
   // WebStateListObserver implementation.
   void WebStateInsertedAt(WebStateList* web_state_list,
                           web::WebState* web_state,
                           int index,
-                          bool activating) override;
+                          bool activating) final;
   void WebStateMoved(WebStateList* web_state_list,
                      web::WebState* web_state,
                      int from_index,
-                     int to_index) override;
+                     int to_index) final;
   void WebStateReplacedAt(WebStateList* web_state_list,
                           web::WebState* old_web_state,
                           web::WebState* new_web_state,
-                          int index) override;
+                          int index) final;
   void WillDetachWebStateAt(WebStateList* web_state_list,
                             web::WebState* web_state,
-                            int index) override;
+                            int index) final;
   void WebStateDetachedAt(WebStateList* web_state_list,
                           web::WebState* web_state,
-                          int index) override;
+                          int index) final;
   void WillCloseWebStateAt(WebStateList* web_state_list,
                            web::WebState* web_state,
                            int index,
-                           bool user_action) override;
+                           bool user_action) final;
   void WebStateActivatedAt(WebStateList* web_state_list,
                            web::WebState* old_web_state,
                            web::WebState* new_web_state,
                            int active_index,
-                           int reason) override;
-  void WillCloseAllWebStates(WebStateList* web_state_list,
-                             bool user_action) override;
-  void DidCloseAllWebStates(WebStateList* web_state_list,
-                            bool user_action) override;
+                           int reason) final;
+  void WillBeginBatchOperation(WebStateList* web_state_list) final;
+  void BatchOperationEnded(WebStateList* web_state_list) final;
 
   __weak id<WebStateListObserving> observer_ = nil;
 
diff --git a/ios/chrome/browser/web_state_list/web_state_list_observer_bridge.mm b/ios/chrome/browser/web_state_list/web_state_list_observer_bridge.mm
index 3b290ba6..930a82e8 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_observer_bridge.mm
+++ b/ios/chrome/browser/web_state_list/web_state_list_observer_bridge.mm
@@ -121,24 +121,20 @@
                        reason:reason];
 }
 
-void WebStateListObserverBridge::WillCloseAllWebStates(
-    WebStateList* web_state_list,
-    bool user_action) {
-  const SEL selector = @selector(webStateList:willCloseAllWebStatesUserAction:);
+void WebStateListObserverBridge::WillBeginBatchOperation(
+    WebStateList* web_state_list) {
+  const SEL selector = @selector(webStateListWillBeginBatchOperation:);
   if (![observer_ respondsToSelector:selector])
     return;
 
-  [observer_ webStateList:web_state_list
-      willCloseAllWebStatesUserAction:(user_action ? YES : NO)];
+  [observer_ webStateListWillBeginBatchOperation:web_state_list];
 }
 
-void WebStateListObserverBridge::DidCloseAllWebStates(
-    WebStateList* web_state_list,
-    bool user_action) {
-  const SEL selector = @selector(webStateList:didCloseAllWebStatesUserAction:);
+void WebStateListObserverBridge::BatchOperationEnded(
+    WebStateList* web_state_list) {
+  const SEL selector = @selector(webStateListBatchOperationEnded:);
   if (![observer_ respondsToSelector:selector])
     return;
 
-  [observer_ webStateList:web_state_list
-      didCloseAllWebStatesUserAction:(user_action ? YES : NO)];
+  [observer_ webStateListBatchOperationEnded:web_state_list];
 }
diff --git a/ios/chrome/browser/web_state_list/web_state_list_serialization.mm b/ios/chrome/browser/web_state_list/web_state_list_serialization.mm
index f21dca8..d56339e9 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_serialization.mm
+++ b/ios/chrome/browser/web_state_list/web_state_list_serialization.mm
@@ -27,47 +27,12 @@
 // the WebStates stored in the WebStateList.
 NSString* const kOpenerIndexKey = @"OpenerIndex";
 NSString* const kOpenerNavigationIndexKey = @"OpenerNavigationIndex";
-}  // namespace
 
-SessionWindowIOS* SerializeWebStateList(WebStateList* web_state_list) {
-  NSMutableArray<CRWSessionStorage*>* serialized_session =
-      [NSMutableArray arrayWithCapacity:web_state_list->count()];
-
-  for (int index = 0; index < web_state_list->count(); ++index) {
-    web::WebState* web_state = web_state_list->GetWebStateAt(index);
-    WebStateOpener opener = web_state_list->GetOpenerOfWebStateAt(index);
-
-    web::SerializableUserDataManager* user_data_manager =
-        web::SerializableUserDataManager::FromWebState(web_state);
-
-    int opener_index = WebStateList::kInvalidIndex;
-    if (opener.opener) {
-      opener_index = web_state_list->GetIndexOfWebState(opener.opener);
-      DCHECK_NE(opener_index, WebStateList::kInvalidIndex);
-      user_data_manager->AddSerializableData(@(opener_index), kOpenerIndexKey);
-      user_data_manager->AddSerializableData(@(opener.navigation_index),
-                                             kOpenerNavigationIndexKey);
-    } else {
-      user_data_manager->AddSerializableData([NSNull null], kOpenerIndexKey);
-      user_data_manager->AddSerializableData([NSNull null],
-                                             kOpenerNavigationIndexKey);
-    }
-
-    [serialized_session addObject:web_state->BuildSessionStorage()];
-  }
-
-  NSUInteger selectedIndex =
-      web_state_list->active_index() != WebStateList::kInvalidIndex
-          ? static_cast<NSUInteger>(web_state_list->active_index())
-          : static_cast<NSUInteger>(NSNotFound);
-
-  return [[SessionWindowIOS alloc] initWithSessions:[serialized_session copy]
-                                      selectedIndex:selectedIndex];
-}
-
-void DeserializeWebStateList(WebStateList* web_state_list,
-                             SessionWindowIOS* session_window,
-                             const WebStateFactory& web_state_factory) {
+// Helper for DeserializeWebStateList allowing the mutation to appears as a
+// single batched operation.
+void DeserializeWebStateListHelper(SessionWindowIOS* session_window,
+                                   const WebStateFactory& web_state_factory,
+                                   WebStateList* web_state_list) {
   const int old_count = web_state_list->count();
   for (CRWSessionStorage* session in session_window.sessions) {
     std::unique_ptr<web::WebState> web_state = web_state_factory.Run(session);
@@ -110,3 +75,48 @@
         old_count + static_cast<int>(session_window.selectedIndex));
   }
 }
+}  // namespace
+
+SessionWindowIOS* SerializeWebStateList(WebStateList* web_state_list) {
+  NSMutableArray<CRWSessionStorage*>* serialized_session =
+      [NSMutableArray arrayWithCapacity:web_state_list->count()];
+
+  for (int index = 0; index < web_state_list->count(); ++index) {
+    web::WebState* web_state = web_state_list->GetWebStateAt(index);
+    WebStateOpener opener = web_state_list->GetOpenerOfWebStateAt(index);
+
+    web::SerializableUserDataManager* user_data_manager =
+        web::SerializableUserDataManager::FromWebState(web_state);
+
+    int opener_index = WebStateList::kInvalidIndex;
+    if (opener.opener) {
+      opener_index = web_state_list->GetIndexOfWebState(opener.opener);
+      DCHECK_NE(opener_index, WebStateList::kInvalidIndex);
+      user_data_manager->AddSerializableData(@(opener_index), kOpenerIndexKey);
+      user_data_manager->AddSerializableData(@(opener.navigation_index),
+                                             kOpenerNavigationIndexKey);
+    } else {
+      user_data_manager->AddSerializableData([NSNull null], kOpenerIndexKey);
+      user_data_manager->AddSerializableData([NSNull null],
+                                             kOpenerNavigationIndexKey);
+    }
+
+    [serialized_session addObject:web_state->BuildSessionStorage()];
+  }
+
+  NSUInteger selectedIndex =
+      web_state_list->active_index() != WebStateList::kInvalidIndex
+          ? static_cast<NSUInteger>(web_state_list->active_index())
+          : static_cast<NSUInteger>(NSNotFound);
+
+  return [[SessionWindowIOS alloc] initWithSessions:[serialized_session copy]
+                                      selectedIndex:selectedIndex];
+}
+
+void DeserializeWebStateList(WebStateList* web_state_list,
+                             SessionWindowIOS* session_window,
+                             const WebStateFactory& web_state_factory) {
+  web_state_list->PerformBatchOperation(
+      base::BindOnce(&DeserializeWebStateListHelper,
+                     base::Unretained(session_window), web_state_factory));
+}
diff --git a/ios/chrome/browser/web_state_list/web_state_list_unittest.mm b/ios/chrome/browser/web_state_list/web_state_list_unittest.mm
index 4444972..57224865 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_unittest.mm
+++ b/ios/chrome/browser/web_state_list/web_state_list_unittest.mm
@@ -38,8 +38,8 @@
     web_state_replaced_called_ = false;
     web_state_detached_called_ = false;
     web_state_activated_called_ = false;
-    will_close_all_webstates_called_ = false;
-    did_close_all_webstates_called_ = false;
+    batch_operation_started_ = false;
+    batch_operation_ended_ = false;
   }
 
   // Returns whether WebStateInsertedAt was invoked.
@@ -59,15 +59,11 @@
     return web_state_activated_called_;
   }
 
-  // Returns whether WillCloseAllWebStates was invoked.
-  bool will_close_all_webstates_called() const {
-    return will_close_all_webstates_called_;
-  }
+  // Returns whether WillBeginBatchOperation was invoked.
+  bool batch_operation_started() const { return batch_operation_started_; }
 
-  // Returns whether DidCloseAllWebStates was invoked.
-  bool did_close_all_webstates_called() const {
-    return did_close_all_webstates_called_;
-  }
+  // Returns whether BatchOperationEnded was invoked.
+  bool batch_operation_ended() const { return batch_operation_ended_; }
 
   // WebStateListObserver implementation.
   void WebStateInsertedAt(WebStateList* web_state_list,
@@ -108,14 +104,12 @@
     web_state_activated_called_ = true;
   }
 
-  void WillCloseAllWebStates(WebStateList* web_state_list,
-                             bool user_action) override {
-    will_close_all_webstates_called_ = true;
+  void WillBeginBatchOperation(WebStateList* web_state_list) override {
+    batch_operation_started_ = true;
   }
 
-  void DidCloseAllWebStates(WebStateList* web_state_list,
-                            bool user_action) override {
-    did_close_all_webstates_called_ = true;
+  void BatchOperationEnded(WebStateList* web_state_list) override {
+    batch_operation_ended_ = true;
   }
 
  private:
@@ -124,8 +118,8 @@
   bool web_state_replaced_called_ = false;
   bool web_state_detached_called_ = false;
   bool web_state_activated_called_ = false;
-  bool will_close_all_webstates_called_ = false;
-  bool did_close_all_webstates_called_ = false;
+  bool batch_operation_started_ = false;
+  bool batch_operation_ended_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(WebStateListTestObserver);
 };
@@ -733,8 +727,8 @@
   EXPECT_EQ(0, web_state_list_.count());
 
   EXPECT_TRUE(observer_.web_state_detached_called());
-  EXPECT_TRUE(observer_.will_close_all_webstates_called());
-  EXPECT_TRUE(observer_.did_close_all_webstates_called());
+  EXPECT_TRUE(observer_.batch_operation_started());
+  EXPECT_TRUE(observer_.batch_operation_ended());
 }
 
 // Test closing one webstate.
@@ -751,6 +745,28 @@
 
   EXPECT_EQ(2, web_state_list_.count());
   EXPECT_TRUE(observer_.web_state_detached_called());
-  EXPECT_FALSE(observer_.will_close_all_webstates_called());
-  EXPECT_FALSE(observer_.did_close_all_webstates_called());
+  EXPECT_FALSE(observer_.batch_operation_started());
+  EXPECT_FALSE(observer_.batch_operation_ended());
+}
+
+// Test that batch operation can be empty.
+TEST_F(WebStateListTest, PerformBatchOperation_EmptyCallback) {
+  observer_.ResetStatistics();
+
+  web_state_list_.PerformBatchOperation({});
+
+  EXPECT_TRUE(observer_.batch_operation_started());
+  EXPECT_TRUE(observer_.batch_operation_ended());
+}
+
+// Test that batch operation WebStateList is the correct one.
+TEST_F(WebStateListTest, PerformBatchOperation_CorrectWebStateList) {
+  WebStateList* captured_web_state_list = nullptr;
+  web_state_list_.PerformBatchOperation(base::BindOnce(
+      [](WebStateList** captured_web_state_list, WebStateList* web_state_list) {
+        *captured_web_state_list = web_state_list;
+      },
+      &captured_web_state_list));
+
+  EXPECT_EQ(captured_web_state_list, &web_state_list_);
 }
diff --git a/ios/chrome/common/app_group/app_group_field_trial_version.h b/ios/chrome/common/app_group/app_group_field_trial_version.h
index 7d5e395..464ae05 100644
--- a/ios/chrome/common/app_group/app_group_field_trial_version.h
+++ b/ios/chrome/common/app_group/app_group_field_trial_version.h
@@ -17,7 +17,4 @@
 // The dictionary key for the trial version.
 extern NSString* const kFieldTrialVersionKey;
 
-// The current version of the copied content behavior feature.
-extern NSNumber* const kCopiedContentBehaviorVersion;
-
 #endif /* IOS_CHROME_COMMON_APP_GROUP_APP_GROUP_FIELD_TRIAL_VERSION_H_ */
diff --git a/ios/chrome/common/app_group/app_group_field_trial_version.mm b/ios/chrome/common/app_group/app_group_field_trial_version.mm
index 060794e..2082eb8 100644
--- a/ios/chrome/common/app_group/app_group_field_trial_version.mm
+++ b/ios/chrome/common/app_group/app_group_field_trial_version.mm
@@ -10,5 +10,3 @@
 
 NSString* const kFieldTrialValueKey = @"FieldTrialValue";
 NSString* const kFieldTrialVersionKey = @"FieldTrialVersion";
-
-NSNumber* const kCopiedContentBehaviorVersion = [NSNumber numberWithInt:0];
diff --git a/ios/chrome/search_widget_extension/search_widget_view_controller.mm b/ios/chrome/search_widget_extension/search_widget_view_controller.mm
index ffcd02a8..7be3dc4 100644
--- a/ios/chrome/search_widget_extension/search_widget_view_controller.mm
+++ b/ios/chrome/search_widget_extension/search_widget_view_controller.mm
@@ -30,7 +30,6 @@
 @property(nonatomic, copy, nullable) NSDictionary* fieldTrialValues;
 // Whether the current default search engine supports search by image
 @property(nonatomic, assign) BOOL supportsSearchByImage;
-@property(nonatomic, readonly) BOOL copiedContentBehaviorEnabled;
 @property(nonatomic, strong) AppGroupCommand* command;
 
 @end
@@ -117,14 +116,15 @@
   UIImage* copiedImage;
   CopiedContentType type = CopiedContentTypeNone;
 
-  if (UIImage* image = [self getCopiedImageUsingFlag]) {
+  if (UIImage* image = [self getCopiedImageFromClipboard]) {
     copiedImage = image;
     type = CopiedContentTypeImage;
   } else if (NSURL* url =
                  [self.clipboardRecentContent recentURLFromClipboard]) {
     copiedText = url.absoluteString;
     type = CopiedContentTypeURL;
-  } else if (NSString* text = [self getCopiedTextUsingFlag]) {
+  } else if (NSString* text =
+                 [self.clipboardRecentContent recentTextFromClipboard]) {
     copiedText = text;
     type = CopiedContentTypeString;
   }
@@ -134,21 +134,10 @@
                         copiedImage:copiedImage];
 }
 
-// Helper method to encapsulate both checking the flag and getting the copied
-// text.
-// TODO(crbug.com/932116): Can be removed when the flag is cleaned up.
-- (NSString*)getCopiedTextUsingFlag {
-  if (!self.copiedContentBehaviorEnabled) {
-    return nil;
-  }
-  return [self.clipboardRecentContent recentTextFromClipboard];
-}
-
-// Helper method to encapsulate both checking the flag and getting the copied
-// image.
-// TODO(crbug.com/932116): Can be removed when the flag is cleaned up.
-- (UIImage*)getCopiedImageUsingFlag {
-  if (!self.copiedContentBehaviorEnabled || !self.supportsSearchByImage) {
+// Helper method to encapsulate checking whether the current search engine
+// supports search-by-image and getting the copied image.
+- (UIImage*)getCopiedImageFromClipboard {
+  if (!self.supportsSearchByImage) {
     return nil;
   }
   return [self.clipboardRecentContent recentImageFromClipboard];
@@ -285,16 +274,4 @@
   }
 }
 
-- (BOOL)copiedContentBehaviorEnabled {
-  NSDictionary* storedData = self.fieldTrialValues[@"CopiedContentBehavior"];
-  NSNumber* storedVersion =
-      base::mac::ObjCCast<NSNumber>(storedData[kFieldTrialVersionKey]);
-  if (!storedVersion ||
-      ![kCopiedContentBehaviorVersion isEqualToNumber:storedVersion]) {
-    return NO;
-  }
-
-  return [storedData[kFieldTrialValueKey] boolValue];
-}
-
 @end
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index f94e2a2..55768c1 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -165,6 +165,7 @@
     "//ios/chrome/browser/browsing_data:unit_tests",
     "//ios/chrome/browser/complex_tasks:unit_tests",
     "//ios/chrome/browser/crash_report:unit_tests",
+    "//ios/chrome/browser/crash_report/breadcrumbs:unit_tests",
     "//ios/chrome/browser/device_sharing:unit_tests",
     "//ios/chrome/browser/download:unit_tests",
     "//ios/chrome/browser/drag_and_drop:unit_tests",
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index e1a2116..db3ac08 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -42,6 +42,7 @@
     "//ios/chrome/browser/ui/activity_services:eg2_tests",
     "//ios/chrome/browser/ui/download:eg2_tests",
     "//ios/chrome/browser/ui/integration_tests:eg2_tests",
+    "//ios/chrome/browser/ui/ntp:eg2_tests",
     "//ios/chrome/browser/ui/omnibox/popup:eg2_tests",
     "//ios/chrome/browser/ui/omnibox/popup/shortcuts:eg2_tests",
     "//ios/chrome/browser/ui/open_in:eg2_tests",
@@ -53,34 +54,6 @@
   ]
 }
 
-source_set("shared_helper_headers") {
-  testonly = true
-  sources = [
-    "//ios/chrome/test/earl_grey2/chrome_earl_grey_edo.h",
-  ]
-}
-
-source_set("eg_app_support+eg2") {
-  testonly = true
-  configs += [ "//build/config/compiler:enable_arc" ]
-
-  sources = [
-    "chrome_earl_grey_edo.mm",
-  ]
-
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//ios/chrome/test/app:test_support",
-    "//ios/testing/earl_grey:eg_app_support+eg2",
-    "//ios/third_party/earl_grey2:app_framework+link",
-  ]
-
-  public_deps = [
-    ":shared_helper_headers",
-  ]
-}
-
 source_set("eg2_tests") {
   defines = [ "CHROME_EARL_GREY_2" ]
   configs += [
@@ -94,7 +67,6 @@
   ]
 
   deps = [
-    ":shared_helper_headers",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
diff --git a/ios/chrome/test/earl_grey2/chrome_earl_grey_edo.h b/ios/chrome/test/earl_grey2/chrome_earl_grey_edo.h
deleted file mode 100644
index 8c637aca..0000000
--- a/ios/chrome/test/earl_grey2/chrome_earl_grey_edo.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_TEST_EARL_GREY2_CHROME_EARL_GREY_EDO_H_
-#define IOS_CHROME_TEST_EARL_GREY2_CHROME_EARL_GREY_EDO_H_
-
-#import <CommonLib/DistantObject/GREYHostApplicationDistantObject.h>
-
-@interface GREYHostApplicationDistantObject (RemoteTest)
-
-// Returns the number of main tabs.
-- (NSUInteger)GetMainTabCount;
-
-@end
-
-#endif  // IOS_CHROME_TEST_EARL_GREY2_CHROME_EARL_GREY_EDO_H_
diff --git a/ios/chrome/test/earl_grey2/chrome_earl_grey_edo.mm b/ios/chrome/test/earl_grey2/chrome_earl_grey_edo.mm
deleted file mode 100644
index 848b674..0000000
--- a/ios/chrome/test/earl_grey2/chrome_earl_grey_edo.mm
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/test/earl_grey2/chrome_earl_grey_edo.h"
-
-#import "ios/chrome/test/app/tab_test_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation GREYHostApplicationDistantObject (RemoteTest)
-
-- (NSUInteger)GetMainTabCount {
-  return chrome_test_util::GetMainTabCount();
-}
-@end
diff --git a/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni b/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni
index 323b588..445f78d 100644
--- a/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni
+++ b/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni
@@ -91,7 +91,6 @@
       "//ios/chrome/app:main",
       "//ios/chrome/test/earl_grey:eg_app_support+eg2",
       "//ios/chrome/test/earl_grey:hooks",
-      "//ios/chrome/test/earl_grey2:eg_app_support+eg2",
       "//ios/testing:http_server_bundle_data",
       "//ios/third_party/earl_grey2:app_framework+link",
     ]
diff --git a/ios/chrome/test/earl_grey2/smoke_egtest.mm b/ios/chrome/test/earl_grey2/smoke_egtest.mm
index eee5e5f..ae30d45 100644
--- a/ios/chrome/test/earl_grey2/smoke_egtest.mm
+++ b/ios/chrome/test/earl_grey2/smoke_egtest.mm
@@ -12,7 +12,6 @@
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
-#import "ios/chrome/test/earl_grey2/chrome_earl_grey_edo.h"
 #import "ios/testing/earl_grey/app_launch_manager.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 #import "ios/web/common/features.h"
@@ -43,10 +42,8 @@
   [[EarlGrey selectElementWithMatcher:newTabButtonMatcher]
       performAction:grey_tap()];
 
-  // Get tab count.
-  NSUInteger tabCount =
-      [[GREYHostApplicationDistantObject sharedInstance] GetMainTabCount];
-  GREYAssertEqual(2, tabCount, @"Expected 2 tabs.");
+  // Wait until tab opened and test if there're 2 tabs in total.
+  [ChromeEarlGrey waitForMainTabCount:2];
 }
 
 // Tests that helpers from chrome_matchers.h are available for use in tests.
diff --git a/ios/web_view/internal/web_view_web_client_unittest.mm b/ios/web_view/internal/web_view_web_client_unittest.mm
index e280296..0992221 100644
--- a/ios/web_view/internal/web_view_web_client_unittest.mm
+++ b/ios/web_view/internal/web_view_web_client_unittest.mm
@@ -31,6 +31,9 @@
 TEST_F(WebViewWebClientTest, WKWebViewEarlyPageScriptAutofillController) {
   // WebView scripts rely on __gCrWeb object presence.
   WKWebView* web_view = web::BuildWKWebView(CGRectZero, GetBrowserState());
+  // Add |web_view| to the windowed container to keep the WKWebView processes
+  // from being suspended.
+  [GetWebClient()->GetWindowedContainer() addSubview:web_view];
   web::test::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
 
   web::ScopedTestingWebClient web_client(std::make_unique<WebViewWebClient>());
diff --git a/jingle/glue/network_service_async_socket.cc b/jingle/glue/network_service_async_socket.cc
index cd42e0c..7c25bb4 100644
--- a/jingle/glue/network_service_async_socket.cc
+++ b/jingle/glue/network_service_async_socket.cc
@@ -166,7 +166,7 @@
   socket_factory_->CreateProxyResolvingSocket(
       GURL("https://" + address.ToString()), std::move(options),
       net::MutableNetworkTrafficAnnotationTag(traffic_annotation_),
-      mojo::MakeRequest(&socket_), std::move(socket_observer),
+      socket_.BindNewPipeAndPassReceiver(), std::move(socket_observer),
       base::BindOnce(&NetworkServiceAsyncSocket::ProcessConnectDone,
                      base::Unretained(this),
                      std::move(socket_observer_receiver)));
@@ -451,7 +451,7 @@
   write_watcher_.reset();
   write_close_watcher_.reset();
 
-  socket_ = nullptr;
+  socket_.reset();
   tls_socket_.reset();
   socket_observer_receiver_.reset();
   socket_factory_ = nullptr;
diff --git a/jingle/glue/network_service_async_socket.h b/jingle/glue/network_service_async_socket.h
index 3652947..12bba04 100644
--- a/jingle/glue/network_service_async_socket.h
+++ b/jingle/glue/network_service_async_socket.h
@@ -210,7 +210,7 @@
   network::mojom::ProxyResolvingSocketFactoryPtr socket_factory_;
   // The handle to the proxy resolving socket for the current connection, if one
   // exists.
-  network::mojom::ProxyResolvingSocketPtr socket_;
+  mojo::Remote<network::mojom::ProxyResolvingSocket> socket_;
   // TLS socket, if StartTls has been called.
   mojo::Remote<network::mojom::TLSClientSocket> tls_socket_;
 
diff --git a/jingle/glue/network_service_async_socket_unittest.cc b/jingle/glue/network_service_async_socket_unittest.cc
index 7a54836..5dc770e 100644
--- a/jingle/glue/network_service_async_socket_unittest.cc
+++ b/jingle/glue/network_service_async_socket_unittest.cc
@@ -24,6 +24,7 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/unique_receiver_set.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "net/base/address_list.h"
 #include "net/base/host_port_pair.h"
@@ -192,23 +193,23 @@
       const GURL& url,
       network::mojom::ProxyResolvingSocketOptionsPtr options,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
-      network::mojom::ProxyResolvingSocketRequest request,
+      mojo::PendingReceiver<network::mojom::ProxyResolvingSocket> receiver,
       mojo::PendingRemote<network::mojom::SocketObserver> observer,
       CreateProxyResolvingSocketCallback callback) override {
     auto socket = std::make_unique<MockProxyResolvingSocket>();
     socket_raw_ = socket.get();
-    proxy_resolving_socket_bindings_.AddBinding(std::move(socket),
-                                                std::move(request));
+    proxy_resolving_socket_receivers_.Add(std::move(socket),
+                                          std::move(receiver));
     socket_raw_->Connect(std::move(observer), std::move(callback));
   }
 
   MockProxyResolvingSocket* socket() { return socket_raw_; }
 
  private:
-  mojo::StrongBindingSet<network::mojom::ProxyResolvingSocket>
-      proxy_resolving_socket_bindings_;
+  mojo::UniqueReceiverSet<network::mojom::ProxyResolvingSocket>
+      proxy_resolving_socket_receivers_;
 
-  // Owned by |proxy_resolving_socket_bindings_|.
+  // Owned by |proxy_resolving_socket_receivers_|.
   MockProxyResolvingSocket* socket_raw_;
 
   DISALLOW_COPY_AND_ASSIGN(MockProxyResolvingSocketFactory);
diff --git a/media/blink/watch_time_reporter_unittest.cc b/media/blink/watch_time_reporter_unittest.cc
index 4ef3a921..9244054 100644
--- a/media/blink/watch_time_reporter_unittest.cc
+++ b/media/blink/watch_time_reporter_unittest.cc
@@ -16,6 +16,7 @@
 #include "media/blink/watch_time_reporter.h"
 #include "media/mojo/mojom/media_metrics_provider.mojom.h"
 #include "media/mojo/mojom/watch_time_recorder.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -240,8 +241,8 @@
     }
     void AcquireLearningTaskController(
         const std::string& taskName,
-        media::learning::mojom::LearningTaskControllerRequest request)
-        override {}
+        mojo::PendingReceiver<media::learning::mojom::LearningTaskController>
+            receiver) override {}
     void Initialize(bool is_mse, mojom::MediaURLScheme url_scheme) override {}
     void OnError(PipelineStatus status) override {}
     void SetIsAdMedia() override {}
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 6c3788a..a9c544d 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -68,6 +68,8 @@
     "video/file_video_capture_device.h",
     "video/file_video_capture_device_factory.cc",
     "video/file_video_capture_device_factory.h",
+    "video/gpu_memory_buffer_utils.cc",
+    "video/gpu_memory_buffer_utils.h",
     "video/video_capture_buffer_handle.cc",
     "video/video_capture_buffer_handle.h",
     "video/video_capture_device.cc",
@@ -83,6 +85,7 @@
     "//base",
     "//base:i18n",
     "//gpu/command_buffer/client",
+    "//gpu/ipc/common:common",
     "//media",
     "//media/capture/mojom:image_capture",
     "//media/capture/mojom:image_capture_types",
diff --git a/media/capture/video/OWNERS b/media/capture/video/OWNERS
index 0a386e7..832f5a4 100644
--- a/media/capture/video/OWNERS
+++ b/media/capture/video/OWNERS
@@ -1,8 +1,8 @@
-chfremer@chromium.org
 tommi@chromium.org
 guidou@chromium.org
 
-# Original (legacy) owner.
+# Original (legacy) owners.
+chfremer@chromium.org
 emircan@chromium.org
 mcasas@chromium.org
 
diff --git a/media/capture/video/fake_video_capture_device.cc b/media/capture/video/fake_video_capture_device.cc
index f003ba21..4d8eba3b 100644
--- a/media/capture/video/fake_video_capture_device.cc
+++ b/media/capture/video/fake_video_capture_device.cc
@@ -18,9 +18,11 @@
 #include "base/threading/thread_checker.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/audio/fake_audio_input_stream.h"
 #include "media/base/video_frame.h"
 #include "media/capture/mojom/image_capture_types.h"
+#include "media/capture/video/gpu_memory_buffer_utils.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkFont.h"
@@ -79,9 +81,10 @@
              : PixelFormatMatchType::INCOMPATIBLE;
 }
 
-const VideoCaptureFormat& FindClosestSupportedFormat(
+VideoCaptureFormat FindClosestSupportedFormat(
     const VideoCaptureFormat& requested_format,
-    const VideoCaptureFormats& supported_formats) {
+    const VideoCaptureFormats& supported_formats,
+    bool video_capture_use_gmb) {
   DCHECK(!supported_formats.empty());
   int best_index = 0;
   PixelFormatMatchType best_format_match = PixelFormatMatchType::INCOMPATIBLE;
@@ -115,7 +118,15 @@
       best_index = i;
     }
   }
-  return supported_formats[best_index];
+
+  VideoCaptureFormat format = supported_formats[best_index];
+  // We use NV12 as the underlying opaque pixel format for GpuMemoryBuffer
+  // frames.
+  if (video_capture_use_gmb) {
+    format.pixel_format = PIXEL_FORMAT_NV12;
+  }
+
+  return format;
 }
 
 gfx::ColorSpace GetDefaultColorSpace(VideoPixelFormat format) {
@@ -231,13 +242,37 @@
   std::vector<unsigned char> jpeg_buffer_;
 };
 
+// Delivers frames using GpuMemoryBuffer buffers reserved from the client buffer
+// pool via OnIncomingCapturedBuffer();
+class GpuMemoryBufferFrameDeliverer : public FrameDeliverer {
+ public:
+  GpuMemoryBufferFrameDeliverer(
+      std::unique_ptr<PacmanFramePainter> frame_painter,
+      gpu::GpuMemoryBufferSupport* gmb_support);
+  ~GpuMemoryBufferFrameDeliverer() override;
+
+  // Implementation of FrameDeliveryStrategy
+  void PaintAndDeliverNextFrame(base::TimeDelta timestamp_to_paint) override;
+
+ private:
+  gpu::GpuMemoryBufferSupport* gmb_support_;
+};
+
 FrameDelivererFactory::FrameDelivererFactory(
     FakeVideoCaptureDevice::DeliveryMode delivery_mode,
-    const FakeDeviceState* device_state)
-    : delivery_mode_(delivery_mode), device_state_(device_state) {}
+    const FakeDeviceState* device_state,
+    std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support)
+    : delivery_mode_(delivery_mode),
+      device_state_(device_state),
+      gmb_support_(gmb_support
+                       ? std::move(gmb_support)
+                       : std::make_unique<gpu::GpuMemoryBufferSupport>()) {}
+
+FrameDelivererFactory::~FrameDelivererFactory() = default;
 
 std::unique_ptr<FrameDeliverer> FrameDelivererFactory::CreateFrameDeliverer(
-    const VideoCaptureFormat& format) {
+    const VideoCaptureFormat& format,
+    bool video_capture_use_gmb) {
   PacmanFramePainter::Format painter_format;
   switch (format.pixel_format) {
     case PIXEL_FORMAT_I420:
@@ -249,6 +284,9 @@
     case PIXEL_FORMAT_MJPEG:
       painter_format = PacmanFramePainter::Format::SK_N32;
       break;
+    case PIXEL_FORMAT_NV12:
+      painter_format = PacmanFramePainter::Format::NV12;
+      break;
     default:
       NOTREACHED();
       painter_format = PacmanFramePainter::Format::I420;
@@ -266,6 +304,11 @@
     delivery_mode =
         FakeVideoCaptureDevice::DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS;
   }
+  if (video_capture_use_gmb) {
+    DLOG(INFO) << "Forcing GpuMemoryBufferFrameDeliverer";
+    delivery_mode =
+        FakeVideoCaptureDevice::DeliveryMode::USE_GPU_MEMORY_BUFFERS;
+  }
 
   switch (delivery_mode) {
     case FakeVideoCaptureDevice::DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS:
@@ -279,6 +322,9 @@
     case FakeVideoCaptureDevice::DeliveryMode::USE_CLIENT_PROVIDED_BUFFERS:
       return std::make_unique<ClientBufferFrameDeliverer>(
           std::move(frame_painter));
+    case FakeVideoCaptureDevice::DeliveryMode::USE_GPU_MEMORY_BUFFERS:
+      return std::make_unique<GpuMemoryBufferFrameDeliverer>(
+          std::move(frame_painter), gmb_support_.get());
   }
   NOTREACHED();
   return nullptr;
@@ -330,6 +376,8 @@
             target_buffer[offset * sizeof(uint32_t) + 3] = value >> 8;
             break;
           case Format::I420:
+          case Format::NV12:
+            // I420 and NV12 has the same Y plane dimension.
             target_buffer[offset] = value >> 8;
             break;
         }
@@ -346,12 +394,16 @@
   SkColorType colorspace = kAlpha_8_SkColorType;
   switch (pixel_format_) {
     case Format::I420:
+    case Format::NV12:
       // Skia doesn't support painting in I420. Instead, paint an 8bpp
       // monochrome image to the beginning of |target_buffer|. This section of
       // |target_buffer| corresponds to the Y-plane of the YUV image. Do not
       // touch the U or V planes of |target_buffer|. Assuming they have been
       // initialized to 0, which corresponds to a green color tone, the result
       // will be an green-ish monochrome frame.
+      //
+      // NV12 has the same Y plane dimension as I420 and we don't touch UV
+      // plane.
       colorspace = kAlpha_8_SkColorType;
       break;
     case Format::SK_N32:
@@ -480,13 +532,15 @@
     std::unique_ptr<VideoCaptureDevice::Client> client) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  const VideoCaptureFormat& selected_format =
-      FindClosestSupportedFormat(params.requested_format, supported_formats_);
+  bool video_capture_use_gmb =
+      (params.buffer_type == VideoCaptureBufferType::kGpuMemoryBuffer);
+  VideoCaptureFormat selected_format = FindClosestSupportedFormat(
+      params.requested_format, supported_formats_, video_capture_use_gmb);
 
   beep_time_ = base::TimeDelta();
   elapsed_time_ = base::TimeDelta();
-  frame_deliverer_ =
-      frame_deliverer_factory_->CreateFrameDeliverer(selected_format);
+  frame_deliverer_ = frame_deliverer_factory_->CreateFrameDeliverer(
+      selected_format, video_capture_use_gmb);
   device_state_->format.frame_size = selected_format.frame_size;
   frame_deliverer_->Initialize(device_state_->format.pixel_format,
                                std::move(client), device_state_.get());
@@ -738,6 +792,45 @@
       CalculateTimeSinceFirstInvocation(now));
 }
 
+GpuMemoryBufferFrameDeliverer::GpuMemoryBufferFrameDeliverer(
+    std::unique_ptr<PacmanFramePainter> frame_painter,
+    gpu::GpuMemoryBufferSupport* gmb_support)
+    : FrameDeliverer(std::move(frame_painter)), gmb_support_(gmb_support) {}
+
+GpuMemoryBufferFrameDeliverer::~GpuMemoryBufferFrameDeliverer() = default;
+
+void GpuMemoryBufferFrameDeliverer::PaintAndDeliverNextFrame(
+    base::TimeDelta timestamp_to_paint) {
+  if (!client())
+    return;
+
+  std::unique_ptr<gfx::GpuMemoryBuffer> gmb;
+  VideoCaptureDevice::Client::Buffer capture_buffer;
+  const gfx::Size& buffer_size = device_state()->format.frame_size;
+  auto reserve_result = AllocateNV12GpuMemoryBuffer(
+      client(), buffer_size, gmb_support_, &gmb, &capture_buffer);
+  if (reserve_result != VideoCaptureDevice::Client::ReserveResult::kSucceeded) {
+    client()->OnFrameDropped(
+        ConvertReservationFailureToFrameDropReason(reserve_result));
+    return;
+  }
+  ScopedNV12GpuMemoryBufferMapping scoped_mapping(std::move(gmb));
+  memset(scoped_mapping.y_plane(), 0,
+         scoped_mapping.y_stride() * buffer_size.height());
+  memset(scoped_mapping.uv_plane(), 0,
+         scoped_mapping.uv_stride() * (buffer_size.height() / 2));
+  frame_painter()->PaintFrame(timestamp_to_paint, scoped_mapping.y_plane());
+
+  base::TimeTicks now = base::TimeTicks::Now();
+  VideoCaptureFormat modified_format = device_state()->format;
+  // When GpuMemoryBuffer is used, the frame data is opaque to the CPU for most
+  // of the time.  Currently the only supported underlying format is NV12.
+  modified_format.pixel_format = PIXEL_FORMAT_NV12;
+  client()->OnIncomingCapturedBuffer(std::move(capture_buffer), modified_format,
+                                     now,
+                                     CalculateTimeSinceFirstInvocation(now));
+}
+
 void FakeVideoCaptureDevice::BeepAndScheduleNextCapture(
     base::TimeTicks expected_execution_time) {
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/media/capture/video/fake_video_capture_device.h b/media/capture/video/fake_video_capture_device.h
index e5ceb56..1d41929 100644
--- a/media/capture/video/fake_video_capture_device.h
+++ b/media/capture/video/fake_video_capture_device.h
@@ -14,6 +14,10 @@
 #include "base/threading/thread_checker.h"
 #include "media/capture/video/video_capture_device.h"
 
+namespace gpu {
+class GpuMemoryBufferSupport;
+}  // namespace gpu
+
 namespace media {
 
 struct FakeDeviceState;
@@ -25,7 +29,7 @@
 // as a frame count and timer.
 class PacmanFramePainter {
  public:
-  enum class Format { I420, SK_N32, Y16 };
+  enum class Format { I420, SK_N32, Y16, NV12 };
 
   PacmanFramePainter(Format pixel_format,
                      const FakeDeviceState* fake_device_state);
@@ -50,7 +54,8 @@
  public:
   enum class DeliveryMode {
     USE_DEVICE_INTERNAL_BUFFERS,
-    USE_CLIENT_PROVIDED_BUFFERS
+    USE_CLIENT_PROVIDED_BUFFERS,
+    USE_GPU_MEMORY_BUFFERS,
   };
 
   enum class DisplayMediaType { ANY, MONITOR, WINDOW, BROWSER };
@@ -134,15 +139,20 @@
 // A dependency needed by FakeVideoCaptureDevice.
 class FrameDelivererFactory {
  public:
-  FrameDelivererFactory(FakeVideoCaptureDevice::DeliveryMode delivery_mode,
-                        const FakeDeviceState* device_state);
+  FrameDelivererFactory(
+      FakeVideoCaptureDevice::DeliveryMode delivery_mode,
+      const FakeDeviceState* device_state,
+      std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support);
+  ~FrameDelivererFactory();
 
   std::unique_ptr<FrameDeliverer> CreateFrameDeliverer(
-      const VideoCaptureFormat& format);
+      const VideoCaptureFormat& format,
+      bool video_capture_use_gmb);
 
  private:
   const FakeVideoCaptureDevice::DeliveryMode delivery_mode_;
   const FakeDeviceState* device_state_ = nullptr;
+  std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support_;
 };
 
 struct FakePhotoDeviceConfig {
diff --git a/media/capture/video/fake_video_capture_device_factory.cc b/media/capture/video/fake_video_capture_device_factory.cc
index 8af8625..c1884d5 100644
--- a/media/capture/video/fake_video_capture_device_factory.cc
+++ b/media/capture/video/fake_video_capture_device_factory.cc
@@ -12,6 +12,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/base/media_switches.h"
 
 namespace {
@@ -114,7 +115,8 @@
 // static
 std::unique_ptr<VideoCaptureDevice>
 FakeVideoCaptureDeviceFactory::CreateDeviceWithSettings(
-    const FakeVideoCaptureDeviceSettings& settings) {
+    const FakeVideoCaptureDeviceSettings& settings,
+    std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support) {
   if (settings.supported_formats.empty())
     return CreateErrorDevice();
 
@@ -147,8 +149,8 @@
 
   return std::make_unique<FakeVideoCaptureDevice>(
       settings.supported_formats,
-      std::make_unique<FrameDelivererFactory>(settings.delivery_mode,
-                                              device_state.get()),
+      std::make_unique<FrameDelivererFactory>(
+          settings.delivery_mode, device_state.get(), std::move(gmb_support)),
       std::move(photo_device), std::move(device_state));
 }
 
@@ -157,13 +159,14 @@
 FakeVideoCaptureDeviceFactory::CreateDeviceWithDefaultResolutions(
     VideoPixelFormat pixel_format,
     FakeVideoCaptureDevice::DeliveryMode delivery_mode,
-    float frame_rate) {
+    float frame_rate,
+    std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support) {
   FakeVideoCaptureDeviceSettings settings;
   settings.delivery_mode = delivery_mode;
   for (const gfx::Size& resolution : kDefaultResolutions)
     settings.supported_formats.emplace_back(resolution, frame_rate,
                                             pixel_format);
-  return CreateDeviceWithSettings(settings);
+  return CreateDeviceWithSettings(settings, std::move(gmb_support));
 }
 
 // static
diff --git a/media/capture/video/fake_video_capture_device_factory.h b/media/capture/video/fake_video_capture_device_factory.h
index 4be92beb..8461cae 100644
--- a/media/capture/video/fake_video_capture_device_factory.h
+++ b/media/capture/video/fake_video_capture_device_factory.h
@@ -14,6 +14,10 @@
 #include "media/capture/video/fake_video_capture_device.h"
 #include "media/capture/video/video_capture_device_factory.h"
 
+namespace gpu {
+class GpuMemoryBufferSupport;
+}  // namespace gpu
+
 namespace media {
 
 struct CAPTURE_EXPORT FakeVideoCaptureDeviceSettings {
@@ -49,12 +53,14 @@
   ~FakeVideoCaptureDeviceFactory() override;
 
   static std::unique_ptr<VideoCaptureDevice> CreateDeviceWithSettings(
-      const FakeVideoCaptureDeviceSettings& settings);
+      const FakeVideoCaptureDeviceSettings& settings,
+      std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support = nullptr);
 
   static std::unique_ptr<VideoCaptureDevice> CreateDeviceWithDefaultResolutions(
       VideoPixelFormat pixel_format,
       FakeVideoCaptureDevice::DeliveryMode delivery_mode,
-      float frame_rate);
+      float frame_rate,
+      std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support = nullptr);
 
   // Creates a device that reports OnError() when AllocateAndStart() is called.
   static std::unique_ptr<VideoCaptureDevice> CreateErrorDevice();
diff --git a/media/capture/video/fake_video_capture_device_unittest.cc b/media/capture/video/fake_video_capture_device_unittest.cc
index c584ef5..60622e8 100644
--- a/media/capture/video/fake_video_capture_device_unittest.cc
+++ b/media/capture/video/fake_video_capture_device_unittest.cc
@@ -17,91 +17,24 @@
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread.h"
 #include "build/build_config.h"
+#include "media/base/bind_to_current_loop.h"
 #include "media/base/media_switches.h"
 #include "media/capture/video/fake_video_capture_device_factory.h"
 #include "media/capture/video/mock_video_capture_device_client.h"
 #include "media/capture/video/video_capture_device.h"
 #include "media/capture/video_capture_types.h"
+#include "media/video/fake_gpu_memory_buffer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
-using ::testing::Bool;
 using ::testing::Combine;
-using ::testing::Invoke;
-using ::testing::SaveArg;
 using ::testing::Values;
 
 namespace media {
 
 namespace {
 
-class StubBufferHandle : public VideoCaptureBufferHandle {
- public:
-  StubBufferHandle(size_t mapped_size, uint8_t* data)
-      : mapped_size_(mapped_size), data_(data) {}
-
-  size_t mapped_size() const override { return mapped_size_; }
-  uint8_t* data() const override { return data_; }
-  const uint8_t* const_data() const override { return data_; }
-
- private:
-  const size_t mapped_size_;
-  uint8_t* const data_;
-};
-
-class StubBufferHandleProvider
-    : public VideoCaptureDevice::Client::Buffer::HandleProvider {
- public:
-  StubBufferHandleProvider(size_t mapped_size, uint8_t* data)
-      : mapped_size_(mapped_size), data_(data) {}
-
-  ~StubBufferHandleProvider() override = default;
-
-  base::UnsafeSharedMemoryRegion DuplicateAsUnsafeRegion() override {
-    NOTREACHED();
-    return {};
-  }
-
-  mojo::ScopedSharedBufferHandle DuplicateAsMojoBuffer() override {
-    NOTREACHED();
-    return mojo::ScopedSharedBufferHandle();
-  }
-
-  std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess()
-      override {
-    return std::make_unique<StubBufferHandle>(mapped_size_, data_);
-  }
-
-  gfx::GpuMemoryBufferHandle GetGpuMemoryBufferHandle() override {
-    return gfx::GpuMemoryBufferHandle();
-  }
-
- private:
-  const size_t mapped_size_;
-  uint8_t* const data_;
-};
-
-class StubReadWritePermission
-    : public VideoCaptureDevice::Client::Buffer::ScopedAccessPermission {
- public:
-  StubReadWritePermission(uint8_t* data) : data_(data) {}
-  ~StubReadWritePermission() override { delete[] data_; }
-
- private:
-  uint8_t* const data_;
-};
-
-VideoCaptureDevice::Client::Buffer CreateStubBuffer(int buffer_id,
-                                                    size_t mapped_size) {
-  auto* buffer = new uint8_t[mapped_size];
-  const int arbitrary_frame_feedback_id = 0;
-  return VideoCaptureDevice::Client::Buffer(
-      buffer_id, arbitrary_frame_feedback_id,
-      std::make_unique<StubBufferHandleProvider>(mapped_size, buffer),
-      std::make_unique<StubReadWritePermission>(buffer));
-}
-
 class ImageCaptureClient : public base::RefCounted<ImageCaptureClient> {
  public:
   // GMock doesn't support move-only arguments, so we use this forward method.
@@ -149,43 +82,10 @@
   void SetUp() override { EXPECT_CALL(*client_, OnError(_, _, _)).Times(0); }
 
   std::unique_ptr<MockVideoCaptureDeviceClient> CreateClient() {
-    auto result = std::make_unique<NiceMockVideoCaptureDeviceClient>();
-    ON_CALL(*result, ReserveOutputBuffer(_, _, _, _))
-        .WillByDefault(
-            Invoke([](const gfx::Size& dimensions, VideoPixelFormat format, int,
-                      VideoCaptureDevice::Client::Buffer* buffer) {
-              EXPECT_GT(dimensions.GetArea(), 0);
-              const VideoCaptureFormat frame_format(dimensions, 0.0, format);
-              *buffer = CreateStubBuffer(0, frame_format.ImageAllocationSize());
-              return VideoCaptureDevice::Client::ReserveResult::kSucceeded;
-            }));
-    ON_CALL(*result, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _))
-        .WillByDefault(Invoke(
-            [this](const uint8_t*, int,
-                   const media::VideoCaptureFormat& frame_format,
-                   const gfx::ColorSpace&, int, bool, base::TimeTicks,
-                   base::TimeDelta, int) { OnFrameCaptured(frame_format); }));
-    ON_CALL(*result, OnIncomingCapturedGfxBuffer(_, _, _, _, _, _))
-        .WillByDefault(
-            Invoke([this](gfx::GpuMemoryBuffer*,
-                          const media::VideoCaptureFormat& frame_format, int,
-                          base::TimeTicks, base::TimeDelta,
-                          int) { OnFrameCaptured(frame_format); }));
-    ON_CALL(*result, DoOnIncomingCapturedBuffer(_, _, _, _))
-        .WillByDefault(
-            Invoke([this](media::VideoCaptureDevice::Client::Buffer&,
-                          const media::VideoCaptureFormat& frame_format,
-                          base::TimeTicks,
-                          base::TimeDelta) { OnFrameCaptured(frame_format); }));
-    ON_CALL(*result, DoOnIncomingCapturedBufferExt(_, _, _, _, _, _, _))
-        .WillByDefault(Invoke(
-            [this](media::VideoCaptureDevice::Client::Buffer&,
-                   const media::VideoCaptureFormat& frame_format,
-                   const gfx::ColorSpace&, base::TimeTicks, base::TimeDelta,
-                   gfx::Rect, const media::VideoFrameMetadata&) {
-              OnFrameCaptured(frame_format);
-            }));
-    return result;
+    return MockVideoCaptureDeviceClient::CreateMockClientWithBufferAllocator(
+        BindToCurrentLoop(base::BindRepeating(
+            &FakeVideoCaptureDeviceTestBase::OnFrameCaptured,
+            base::Unretained(this))));
   }
 
   void OnFrameCaptured(const VideoCaptureFormat& format) {
@@ -220,9 +120,12 @@
 // Tests that a frame is delivered with the expected settings.
 // Sweeps through a fixed set of requested/expected resolutions.
 TEST_P(FakeVideoCaptureDeviceTest, CaptureUsing) {
-  if (testing::get<1>(GetParam()) ==
+  const auto pixel_format = testing::get<0>(GetParam());
+  const auto delivery_mode = testing::get<1>(GetParam());
+  const auto frame_rate = testing::get<2>(GetParam());
+  if (delivery_mode ==
           FakeVideoCaptureDevice::DeliveryMode::USE_CLIENT_PROVIDED_BUFFERS &&
-      testing::get<0>(GetParam()) == PIXEL_FORMAT_MJPEG) {
+      pixel_format == PIXEL_FORMAT_MJPEG) {
     // Unsupported case
     return;
   }
@@ -232,8 +135,8 @@
 
   std::unique_ptr<VideoCaptureDevice> device =
       FakeVideoCaptureDeviceFactory::CreateDeviceWithDefaultResolutions(
-          testing::get<0>(GetParam()), testing::get<1>(GetParam()),
-          testing::get<2>(GetParam()));
+          pixel_format, delivery_mode, frame_rate,
+          std::make_unique<FakeGpuMemoryBufferSupport>());
   ASSERT_TRUE(device);
 
   // First: Requested, Second: Expected
@@ -252,13 +155,23 @@
 
     VideoCaptureParams capture_params;
     capture_params.requested_format.frame_size = resolution.first;
-    capture_params.requested_format.frame_rate = testing::get<2>(GetParam());
+    capture_params.requested_format.frame_rate = frame_rate;
+    if (delivery_mode ==
+        FakeVideoCaptureDevice::DeliveryMode::USE_GPU_MEMORY_BUFFERS) {
+      capture_params.buffer_type = VideoCaptureBufferType::kGpuMemoryBuffer;
+    }
     device->AllocateAndStart(capture_params, std::move(client));
 
     WaitForCapturedFrame();
     EXPECT_EQ(resolution.second.width(), last_format().frame_size.width());
     EXPECT_EQ(resolution.second.height(), last_format().frame_size.height());
-    EXPECT_EQ(last_format().pixel_format, testing::get<0>(GetParam()));
+    if (delivery_mode ==
+        FakeVideoCaptureDevice::DeliveryMode::USE_GPU_MEMORY_BUFFERS) {
+      // NV12 is the only opaque format backing GpuMemoryBuffer.
+      EXPECT_EQ(last_format().pixel_format, PIXEL_FORMAT_NV12);
+    } else {
+      EXPECT_EQ(last_format().pixel_format, pixel_format);
+    }
     EXPECT_EQ(last_format().frame_rate, testing::get<2>(GetParam()));
     device->StopAndDeAllocate();
   }
@@ -271,7 +184,8 @@
         Values(PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16, PIXEL_FORMAT_MJPEG),
         Values(
             FakeVideoCaptureDevice::DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS,
-            FakeVideoCaptureDevice::DeliveryMode::USE_CLIENT_PROVIDED_BUFFERS),
+            FakeVideoCaptureDevice::DeliveryMode::USE_CLIENT_PROVIDED_BUFFERS,
+            FakeVideoCaptureDevice::DeliveryMode::USE_GPU_MEMORY_BUFFERS),
         Values(20, 29.97, 30, 50, 60)));
 
 TEST_F(FakeVideoCaptureDeviceTest, GetDeviceSupportedFormats) {
diff --git a/media/capture/video/file_video_capture_device.cc b/media/capture/video/file_video_capture_device.cc
index 9735610..4b5af3b 100644
--- a/media/capture/video/file_video_capture_device.cc
+++ b/media/capture/video/file_video_capture_device.cc
@@ -17,8 +17,10 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/capture/mojom/image_capture_types.h"
 #include "media/capture/video/blob_utils.h"
+#include "media/capture/video/gpu_memory_buffer_utils.h"
 #include "media/capture/video_capture_types.h"
 #include "media/parsers/jpeg_parser.h"
+#include "third_party/libyuv/include/libyuv.h"
 
 namespace media {
 
@@ -300,8 +302,14 @@
   return file_parser;
 }
 
-FileVideoCaptureDevice::FileVideoCaptureDevice(const base::FilePath& file_path)
-    : capture_thread_("CaptureThread"), file_path_(file_path) {}
+FileVideoCaptureDevice::FileVideoCaptureDevice(
+    const base::FilePath& file_path,
+    std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support)
+    : capture_thread_("CaptureThread"),
+      file_path_(file_path),
+      gmb_support_(gmb_support
+                       ? std::move(gmb_support)
+                       : std::make_unique<gpu::GpuMemoryBufferSupport>()) {}
 
 FileVideoCaptureDevice::~FileVideoCaptureDevice() {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -392,6 +400,9 @@
 
   client_ = std::move(client);
 
+  if (params.buffer_type == VideoCaptureBufferType::kGpuMemoryBuffer)
+    video_capture_use_gmb_ = true;
+
   DCHECK(!file_parser_);
   file_parser_ = GetVideoFileParser(file_path_, &capture_format_);
   if (!file_parser_) {
@@ -431,12 +442,50 @@
   const base::TimeTicks current_time = base::TimeTicks::Now();
   if (first_ref_time_.is_null())
     first_ref_time_ = current_time;
-  // Leave the color space unset for compatibility purposes but this
-  // information should be retrieved from the container when possible.
-  client_->OnIncomingCapturedData(frame_ptr, frame_size, capture_format_,
-                                  gfx::ColorSpace(), 0 /* clockwise_rotation */,
-                                  false /* flip_y */, current_time,
-                                  current_time - first_ref_time_);
+
+  if (video_capture_use_gmb_) {
+    const gfx::Size& buffer_size = capture_format_.frame_size;
+    std::unique_ptr<gfx::GpuMemoryBuffer> gmb;
+    VideoCaptureDevice::Client::Buffer capture_buffer;
+    auto reserve_result = AllocateNV12GpuMemoryBuffer(
+        client_.get(), buffer_size, gmb_support_.get(), &gmb, &capture_buffer);
+    if (reserve_result !=
+        VideoCaptureDevice::Client::ReserveResult::kSucceeded) {
+      client_->OnFrameDropped(
+          ConvertReservationFailureToFrameDropReason(reserve_result));
+      return;
+    }
+    ScopedNV12GpuMemoryBufferMapping scoped_mapping(std::move(gmb));
+    const uint8_t* src_y_plane = frame_ptr;
+    const uint8_t* src_u_plane =
+        frame_ptr +
+        VideoFrame::PlaneSize(PIXEL_FORMAT_I420, 0, buffer_size).GetArea();
+    const uint8_t* src_v_plane =
+        frame_ptr +
+        VideoFrame::PlaneSize(PIXEL_FORMAT_I420, 0, buffer_size).GetArea() +
+        VideoFrame::PlaneSize(PIXEL_FORMAT_I420, 1, buffer_size).GetArea();
+    libyuv::I420ToNV12(
+        src_y_plane, buffer_size.width(), src_u_plane, buffer_size.width() / 2,
+        src_v_plane, buffer_size.width() / 2, scoped_mapping.y_plane(),
+        scoped_mapping.y_stride(), scoped_mapping.uv_plane(),
+        scoped_mapping.uv_stride(), buffer_size.width(), buffer_size.height());
+
+    VideoCaptureFormat modified_format = capture_format_;
+    // When GpuMemoryBuffer is used, the frame data is opaque to the CPU for
+    // most of the time.  Currently the only supported underlying format is
+    // NV12.
+    modified_format.pixel_format = PIXEL_FORMAT_NV12;
+    client_->OnIncomingCapturedBuffer(std::move(capture_buffer),
+                                      modified_format, current_time,
+                                      current_time - first_ref_time_);
+  } else {
+    // Leave the color space unset for compatibility purposes but this
+    // information should be retrieved from the container when possible.
+    client_->OnIncomingCapturedData(
+        frame_ptr, frame_size, capture_format_, gfx::ColorSpace(),
+        0 /* clockwise_rotation */, false /* flip_y */, current_time,
+        current_time - first_ref_time_);
+  }
 
   // Process waiting photo callbacks
   while (!take_photo_callbacks_.empty()) {
diff --git a/media/capture/video/file_video_capture_device.h b/media/capture/video/file_video_capture_device.h
index 7149498f..7b37900 100644
--- a/media/capture/video/file_video_capture_device.h
+++ b/media/capture/video/file_video_capture_device.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_checker.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/capture/video/video_capture_device.h"
 
 namespace media {
@@ -43,7 +44,9 @@
 
   // Constructor of the class, with a fully qualified file path as input, which
   // represents the Y4M or MJPEG file to stream repeatedly.
-  explicit FileVideoCaptureDevice(const base::FilePath& file_path);
+  explicit FileVideoCaptureDevice(
+      const base::FilePath& file_path,
+      std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support = nullptr);
 
   // VideoCaptureDevice implementation, class methods.
   ~FileVideoCaptureDevice() override;
@@ -88,6 +91,10 @@
   // The system time when we receive the first frame.
   base::TimeTicks first_ref_time_;
 
+  // Whether GpuMemoryBuffer-based video capture buffer is enabled or not.
+  bool video_capture_use_gmb_ = false;
+  std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support_;
+
   // Guards the below variables from concurrent access between methods running
   // on the main thread and |capture_thread_|.
   base::Lock lock_;
diff --git a/media/capture/video/file_video_capture_device_unittest.cc b/media/capture/video/file_video_capture_device_unittest.cc
index 117dbeea..423f251 100644
--- a/media/capture/video/file_video_capture_device_unittest.cc
+++ b/media/capture/video/file_video_capture_device_unittest.cc
@@ -14,6 +14,7 @@
 #include "media/base/test_data_util.h"
 #include "media/capture/video/file_video_capture_device.h"
 #include "media/capture/video/mock_video_capture_device_client.h"
+#include "media/video/fake_gpu_memory_buffer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -57,17 +58,36 @@
     EXPECT_CALL(*client_, OnError(_, _, _)).Times(0);
     EXPECT_CALL(*client_, OnStarted());
     device_ = std::make_unique<FileVideoCaptureDevice>(
-        GetTestDataFilePath("bear.mjpeg"));
+        GetTestDataFilePath("bear.mjpeg"),
+        std::make_unique<FakeGpuMemoryBufferSupport>());
     device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
   }
 
   void TearDown() override { device_->StopAndDeAllocate(); }
 
+  std::unique_ptr<MockVideoCaptureDeviceClient> CreateClient() {
+    return MockVideoCaptureDeviceClient::CreateMockClientWithBufferAllocator(
+        BindToCurrentLoop(
+            base::BindRepeating(&FileVideoCaptureDeviceTest::OnFrameCaptured,
+                                base::Unretained(this))));
+  }
+
+  void OnFrameCaptured(const VideoCaptureFormat& format) {
+    last_format_ = format;
+    run_loop_->Quit();
+  }
+
+  void WaitForCapturedFrame() {
+    run_loop_.reset(new base::RunLoop());
+    run_loop_->Run();
+  }
+
   std::unique_ptr<NiceMockVideoCaptureDeviceClient> client_;
   MockImageCaptureClient image_capture_client_;
   std::unique_ptr<VideoCaptureDevice> device_;
   VideoCaptureFormat last_format_;
   base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<base::RunLoop> run_loop_;
 };
 
 TEST_F(FileVideoCaptureDeviceTest, GetPhotoState) {
@@ -105,4 +125,17 @@
   run_loop.Run();
 }
 
+TEST_F(FileVideoCaptureDeviceTest, CaptureWithGpuMemoryBuffer) {
+  auto client = CreateClient();
+  VideoCaptureParams params;
+  params.buffer_type = VideoCaptureBufferType::kGpuMemoryBuffer;
+  auto device = std::make_unique<FileVideoCaptureDevice>(
+      GetTestDataFilePath("bear.mjpeg"),
+      std::make_unique<FakeGpuMemoryBufferSupport>());
+  device->AllocateAndStart(params, std::move(client));
+  WaitForCapturedFrame();
+  EXPECT_EQ(last_format_.pixel_format, PIXEL_FORMAT_NV12);
+  device->StopAndDeAllocate();
+}
+
 }  // namespace media
diff --git a/media/capture/video/gpu_memory_buffer_utils.cc b/media/capture/video/gpu_memory_buffer_utils.cc
new file mode 100644
index 0000000..1deb1245
--- /dev/null
+++ b/media/capture/video/gpu_memory_buffer_utils.cc
@@ -0,0 +1,68 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/capture/video/gpu_memory_buffer_utils.h"
+
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+namespace media {
+
+ScopedNV12GpuMemoryBufferMapping::ScopedNV12GpuMemoryBufferMapping(
+    std::unique_ptr<gfx::GpuMemoryBuffer> gmb)
+    : gmb_(std::move(gmb)) {
+  gmb_->Map();
+}
+
+ScopedNV12GpuMemoryBufferMapping::~ScopedNV12GpuMemoryBufferMapping() {
+  gmb_->Unmap();
+}
+
+uint8_t* ScopedNV12GpuMemoryBufferMapping::y_plane() {
+  return static_cast<uint8_t*>(gmb_->memory(0));
+}
+
+uint8_t* ScopedNV12GpuMemoryBufferMapping::uv_plane() {
+  return static_cast<uint8_t*>(gmb_->memory(1));
+}
+
+size_t ScopedNV12GpuMemoryBufferMapping::y_stride() {
+  return gmb_->stride(0);
+}
+
+size_t ScopedNV12GpuMemoryBufferMapping::uv_stride() {
+  return gmb_->stride(1);
+}
+
+VideoCaptureDevice::Client::ReserveResult AllocateNV12GpuMemoryBuffer(
+    VideoCaptureDevice::Client* capture_client,
+    const gfx::Size& buffer_size,
+    gpu::GpuMemoryBufferSupport* gmb_support,
+    std::unique_ptr<gfx::GpuMemoryBuffer>* out_gpu_memory_buffer,
+    VideoCaptureDevice::Client::Buffer* out_capture_buffer) {
+  CHECK(out_gpu_memory_buffer);
+  CHECK(out_capture_buffer);
+
+  // When GpuMemoryBuffer is used, the frame data is opaque to the CPU for most
+  // of the time.  Currently the only supported underlying format is NV12.
+  constexpr VideoPixelFormat kOpaqueVideoFormat = PIXEL_FORMAT_NV12;
+  constexpr gfx::BufferFormat kOpaqueGfxFormat =
+      gfx::BufferFormat::YUV_420_BIPLANAR;
+
+  const int arbitrary_frame_feedback_id = 0;
+  const auto reserve_result = capture_client->ReserveOutputBuffer(
+      buffer_size, kOpaqueVideoFormat, arbitrary_frame_feedback_id,
+      out_capture_buffer);
+  if (reserve_result != VideoCaptureDevice::Client::ReserveResult::kSucceeded) {
+    return reserve_result;
+  }
+
+  *out_gpu_memory_buffer = gmb_support->CreateGpuMemoryBufferImplFromHandle(
+      out_capture_buffer->handle_provider->GetGpuMemoryBufferHandle(),
+      buffer_size, kOpaqueGfxFormat,
+      gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, base::NullCallback());
+  return reserve_result;
+}
+
+}  // namespace media
diff --git a/media/capture/video/gpu_memory_buffer_utils.h b/media/capture/video/gpu_memory_buffer_utils.h
new file mode 100644
index 0000000..b5891fe
--- /dev/null
+++ b/media/capture/video/gpu_memory_buffer_utils.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_CAPTURE_VIDEO_GPU_MEMORY_BUFFER_UTILS_H_
+#define MEDIA_CAPTURE_VIDEO_GPU_MEMORY_BUFFER_UTILS_H_
+
+#include <memory>
+
+#include "media/capture/video/video_capture_device.h"
+
+namespace gfx {
+class GpuMemoryBuffer;
+}  // namespace gfx
+
+namespace gpu {
+class GpuMemoryBufferSupport;
+}  // namespace gpu
+
+// Utility class and function for creating and accessing video capture client
+// buffers backed with GpuMemoryBuffer buffers.
+namespace media {
+
+class ScopedNV12GpuMemoryBufferMapping {
+ public:
+  explicit ScopedNV12GpuMemoryBufferMapping(
+      std::unique_ptr<gfx::GpuMemoryBuffer> gmb);
+  ~ScopedNV12GpuMemoryBufferMapping();
+  uint8_t* y_plane();
+  uint8_t* uv_plane();
+  size_t y_stride();
+  size_t uv_stride();
+
+ private:
+  std::unique_ptr<gfx::GpuMemoryBuffer> gmb_;
+};
+
+VideoCaptureDevice::Client::ReserveResult AllocateNV12GpuMemoryBuffer(
+    VideoCaptureDevice::Client* capture_client,
+    const gfx::Size& buffer_size,
+    gpu::GpuMemoryBufferSupport* gmb_support,
+    std::unique_ptr<gfx::GpuMemoryBuffer>* out_gpu_memory_buffer,
+    VideoCaptureDevice::Client::Buffer* out_capture_buffer);
+
+}  // namespace media
+
+#endif  // MEDIA_CAPTURE_VIDEO_GPU_MEMORY_BUFFER_UTILS_H_
diff --git a/media/capture/video/mock_video_capture_device_client.cc b/media/capture/video/mock_video_capture_device_client.cc
index b113c6a..f0ba5f9 100644
--- a/media/capture/video/mock_video_capture_device_client.cc
+++ b/media/capture/video/mock_video_capture_device_client.cc
@@ -4,8 +4,81 @@
 
 #include "media/capture/video/mock_video_capture_device_client.h"
 
+using testing::_;
+using testing::Invoke;
+
 namespace media {
 
+namespace {
+
+class StubBufferHandle : public VideoCaptureBufferHandle {
+ public:
+  StubBufferHandle(size_t mapped_size, uint8_t* data)
+      : mapped_size_(mapped_size), data_(data) {}
+
+  size_t mapped_size() const override { return mapped_size_; }
+  uint8_t* data() const override { return data_; }
+  const uint8_t* const_data() const override { return data_; }
+
+ private:
+  const size_t mapped_size_;
+  uint8_t* const data_;
+};
+
+class StubBufferHandleProvider
+    : public VideoCaptureDevice::Client::Buffer::HandleProvider {
+ public:
+  StubBufferHandleProvider(size_t mapped_size, uint8_t* data)
+      : mapped_size_(mapped_size), data_(data) {}
+
+  ~StubBufferHandleProvider() override = default;
+
+  base::UnsafeSharedMemoryRegion DuplicateAsUnsafeRegion() override {
+    NOTREACHED();
+    return {};
+  }
+
+  mojo::ScopedSharedBufferHandle DuplicateAsMojoBuffer() override {
+    NOTREACHED();
+    return mojo::ScopedSharedBufferHandle();
+  }
+
+  std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess()
+      override {
+    return std::make_unique<StubBufferHandle>(mapped_size_, data_);
+  }
+
+  gfx::GpuMemoryBufferHandle GetGpuMemoryBufferHandle() override {
+    return gfx::GpuMemoryBufferHandle();
+  }
+
+ private:
+  const size_t mapped_size_;
+  uint8_t* const data_;
+};
+
+class StubReadWritePermission
+    : public VideoCaptureDevice::Client::Buffer::ScopedAccessPermission {
+ public:
+  StubReadWritePermission(uint8_t* data) : data_(data) {}
+  ~StubReadWritePermission() override { delete[] data_; }
+
+ private:
+  uint8_t* const data_;
+};
+
+VideoCaptureDevice::Client::Buffer CreateStubBuffer(int buffer_id,
+                                                    size_t mapped_size) {
+  auto* buffer = new uint8_t[mapped_size];
+  const int arbitrary_frame_feedback_id = 0;
+  return VideoCaptureDevice::Client::Buffer(
+      buffer_id, arbitrary_frame_feedback_id,
+      std::make_unique<StubBufferHandleProvider>(mapped_size, buffer),
+      std::make_unique<StubReadWritePermission>(buffer));
+}
+
+}  // namespace
+
 MockVideoCaptureDeviceClient::MockVideoCaptureDeviceClient() = default;
 MockVideoCaptureDeviceClient::~MockVideoCaptureDeviceClient() = default;
 
@@ -28,4 +101,55 @@
                                 timestamp, visible_rect, additional_metadata);
 }
 
+// static
+std::unique_ptr<MockVideoCaptureDeviceClient>
+MockVideoCaptureDeviceClient::CreateMockClientWithBufferAllocator(
+    FakeFrameCapturedCallback frame_captured_callback) {
+  auto result = std::make_unique<NiceMockVideoCaptureDeviceClient>();
+  result->fake_frame_captured_callback_ = std::move(frame_captured_callback);
+
+  auto* raw_result_ptr = result.get();
+  ON_CALL(*result, ReserveOutputBuffer(_, _, _, _))
+      .WillByDefault(
+          Invoke([](const gfx::Size& dimensions, VideoPixelFormat format, int,
+                    VideoCaptureDevice::Client::Buffer* buffer) {
+            EXPECT_GT(dimensions.GetArea(), 0);
+            const VideoCaptureFormat frame_format(dimensions, 0.0, format);
+            *buffer = CreateStubBuffer(0, frame_format.ImageAllocationSize());
+            return VideoCaptureDevice::Client::ReserveResult::kSucceeded;
+          }));
+  ON_CALL(*result, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _))
+      .WillByDefault(
+          Invoke([raw_result_ptr](const uint8_t*, int,
+                                  const media::VideoCaptureFormat& frame_format,
+                                  const gfx::ColorSpace&, int, bool,
+                                  base::TimeTicks, base::TimeDelta, int) {
+            raw_result_ptr->fake_frame_captured_callback_.Run(frame_format);
+          }));
+  ON_CALL(*result, OnIncomingCapturedGfxBuffer(_, _, _, _, _, _))
+      .WillByDefault(
+          Invoke([raw_result_ptr](gfx::GpuMemoryBuffer*,
+                                  const media::VideoCaptureFormat& frame_format,
+                                  int, base::TimeTicks, base::TimeDelta, int) {
+            raw_result_ptr->fake_frame_captured_callback_.Run(frame_format);
+          }));
+  ON_CALL(*result, DoOnIncomingCapturedBuffer(_, _, _, _))
+      .WillByDefault(
+          Invoke([raw_result_ptr](media::VideoCaptureDevice::Client::Buffer&,
+                                  const media::VideoCaptureFormat& frame_format,
+                                  base::TimeTicks, base::TimeDelta) {
+            raw_result_ptr->fake_frame_captured_callback_.Run(frame_format);
+          }));
+  ON_CALL(*result, DoOnIncomingCapturedBufferExt(_, _, _, _, _, _, _))
+      .WillByDefault(
+          Invoke([raw_result_ptr](media::VideoCaptureDevice::Client::Buffer&,
+                                  const media::VideoCaptureFormat& frame_format,
+                                  const gfx::ColorSpace&, base::TimeTicks,
+                                  base::TimeDelta, gfx::Rect,
+                                  const media::VideoFrameMetadata&) {
+            raw_result_ptr->fake_frame_captured_callback_.Run(frame_format);
+          }));
+  return result;
+}
+
 }  // namespace media
diff --git a/media/capture/video/mock_video_capture_device_client.h b/media/capture/video/mock_video_capture_device_client.h
index d096673e..137df2b 100644
--- a/media/capture/video/mock_video_capture_device_client.h
+++ b/media/capture/video/mock_video_capture_device_client.h
@@ -10,6 +10,9 @@
 
 namespace media {
 
+using FakeFrameCapturedCallback =
+    base::RepeatingCallback<void(const VideoCaptureFormat&)>;
+
 class MockVideoCaptureDeviceClient : public VideoCaptureDevice::Client {
  public:
   MockVideoCaptureDeviceClient();
@@ -68,6 +71,13 @@
                     base::TimeDelta timestamp,
                     gfx::Rect visible_rect,
                     const media::VideoFrameMetadata& additional_metadata));
+
+  static std::unique_ptr<MockVideoCaptureDeviceClient>
+  CreateMockClientWithBufferAllocator(
+      FakeFrameCapturedCallback frame_captured_callback);
+
+ protected:
+  FakeFrameCapturedCallback fake_frame_captured_callback_;
 };
 
 using NiceMockVideoCaptureDeviceClient =
diff --git a/media/capture/video/win/video_capture_device_factory_win.cc b/media/capture/video/win/video_capture_device_factory_win.cc
index f48982e..09d32beea 100644
--- a/media/capture/video/win/video_capture_device_factory_win.cc
+++ b/media/capture/video/win/video_capture_device_factory_win.cc
@@ -95,6 +95,8 @@
     "04ca:7047", "04ca:7048",
     // HP Elitebook 840 G1
     "04f2:b3ed", "04f2:b3ca", "05c8:035d", "05c8:0369",
+    // HP HD Camera. See https://crbug.com/1011888.
+    "04ca:7095",
     // RBG/IR camera for Windows Hello Face Auth. See https://crbug.com/984864.
     "13d3:5257"};
 
diff --git a/media/gpu/chromeos/fourcc.cc b/media/gpu/chromeos/fourcc.cc
index 11f2411..dd5c4a7 100644
--- a/media/gpu/chromeos/fourcc.cc
+++ b/media/gpu/chromeos/fourcc.cc
@@ -296,6 +296,31 @@
   return !(lhs == rhs);
 }
 
+bool Fourcc::IsMultiPlanar() const {
+  switch (value_) {
+    case INVALID:
+    case AR24:
+    case AB24:
+    case XR24:
+    case XB24:
+    case RGB4:
+    case YU12:
+    case YV12:
+    case YUYV:
+    case NV12:
+    case NV21:
+      return false;
+    case YM12:
+    case YM21:
+    case NM12:
+    case NM21:
+    case YM16:
+    case MT21:
+    case MM21:
+      return true;
+  }
+}
+
 std::string Fourcc::ToString() const {
   return FourccToString(static_cast<uint32_t>(value_));
 }
diff --git a/media/gpu/chromeos/fourcc.h b/media/gpu/chromeos/fourcc.h
index 212ae378..a8204042 100644
--- a/media/gpu/chromeos/fourcc.h
+++ b/media/gpu/chromeos/fourcc.h
@@ -156,6 +156,9 @@
   uint32_t ToVAFourCC() const;
 #endif  // BUILDFLAG(USE_VAAPI)
 
+  // Returns whether |value_| is multi planar format.
+  bool IsMultiPlanar() const;
+
   // Outputs human readable fourcc string, e.g. "NV12".
   std::string ToString() const;
 
diff --git a/media/gpu/image_processor_test.cc b/media/gpu/image_processor_test.cc
index d653e49b..a917c21 100644
--- a/media/gpu/image_processor_test.cc
+++ b/media/gpu/image_processor_test.cc
@@ -89,12 +89,17 @@
         Fourcc::FromVideoPixelFormat(input_image.PixelFormat());
     Fourcc output_fourcc =
         Fourcc::FromVideoPixelFormat(output_image.PixelFormat());
-    ImageProcessor::PortConfig input_config(input_fourcc, input_image.Size(),
-                                            {}, input_image.Size(),
-                                            input_storage_types);
-    ImageProcessor::PortConfig output_config(output_fourcc, output_image.Size(),
-                                             {}, output_image.Size(),
-                                             output_storage_types);
+    auto input_layout = test::CreateVideoFrameLayout(input_image.PixelFormat(),
+                                                     input_image.Size());
+    auto output_layout = test::CreateVideoFrameLayout(
+        output_image.PixelFormat(), output_image.Size());
+    LOG_ASSERT(input_layout && output_layout);
+    ImageProcessor::PortConfig input_config(
+        input_fourcc, input_image.Size(), input_layout->planes(),
+        input_image.Size(), input_storage_types);
+    ImageProcessor::PortConfig output_config(
+        output_fourcc, output_image.Size(), output_layout->planes(),
+        output_image.Size(), output_storage_types);
     // TODO(crbug.com/917951): Select more appropriate number of buffers.
     constexpr size_t kNumBuffers = 1;
     LOG_ASSERT(output_image.IsMetadataLoaded());
diff --git a/media/gpu/libyuv_image_processor.cc b/media/gpu/libyuv_image_processor.cc
index 9bd914d..f495c5e 100644
--- a/media/gpu/libyuv_image_processor.cc
+++ b/media/gpu/libyuv_image_processor.cc
@@ -126,12 +126,12 @@
   }
 
   auto processor = base::WrapUnique(new LibYUVImageProcessor(
-      ImageProcessor::PortConfig(input_config.fourcc, input_config.size, {},
-                                 input_config.visible_size,
+      ImageProcessor::PortConfig(input_config.fourcc, input_config.size,
+                                 input_config.planes, input_config.visible_size,
                                  {input_storage_type}),
-      ImageProcessor::PortConfig(output_config.fourcc, output_config.size, {},
-                                 output_config.visible_size,
-                                 {output_storage_type}),
+      ImageProcessor::PortConfig(
+          output_config.fourcc, output_config.size, output_config.planes,
+          output_config.visible_size, {output_storage_type}),
       std::move(video_frame_mapper),
       media::BindToCurrentLoop(std::move(error_cb))));
   if (res == SupportResult::SupportedWithPivot) {
diff --git a/media/gpu/test/image_processor/image_processor_client.cc b/media/gpu/test/image_processor/image_processor_client.cc
index b4ca656..a3a787c 100644
--- a/media/gpu/test/image_processor/image_processor_client.cc
+++ b/media/gpu/test/image_processor/image_processor_client.cc
@@ -31,6 +31,22 @@
 
 namespace media {
 namespace test {
+namespace {
+base::Optional<VideoFrameLayout> CreateLayout(
+    const ImageProcessor::PortConfig& config) {
+  const VideoPixelFormat pixel_format = config.fourcc.ToVideoPixelFormat();
+  if (config.planes.empty())
+    return base::nullopt;
+
+  if (config.fourcc.IsMultiPlanar()) {
+    return VideoFrameLayout::CreateWithPlanes(pixel_format, config.size,
+                                              config.planes);
+  } else {
+    return VideoFrameLayout::CreateMultiPlanar(pixel_format, config.size,
+                                               config.planes);
+  }
+}
+}  // namespace
 
 // static
 std::unique_ptr<ImageProcessorClient> ImageProcessorClient::Create(
@@ -108,25 +124,6 @@
   done->Signal();
 }
 
-namespace {
-
-base::Optional<VideoFrameLayout> CreateLayout(
-    const ImageProcessor::PortConfig& config) {
-  // V4L2 specific format hack:
-  // If VDA's output format is V4L2_PIX_FMT_MT21C, which is a platform specific
-  // format and now is only used for MT8173 VDA output and its image processor
-  // input, we set VideoFrameLayout for image processor's input with format
-  // PIXEL_FORMAT_NV12 as NV12's layout is the same as MT21.
-  const VideoPixelFormat pixel_format = config.fourcc.ToVideoPixelFormat();
-  if (config.planes.size() <= 1) {
-    return VideoFrameLayout::Create(pixel_format, config.size);
-  }
-  return VideoFrameLayout::CreateMultiPlanar(pixel_format, config.size,
-                                             config.planes);
-}
-
-}  // namespace
-
 scoped_refptr<VideoFrame> ImageProcessorClient::CreateInputFrame(
     const Image& input_image) const {
   DCHECK_CALLED_ON_VALID_THREAD(test_main_thread_checker_);
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index dbc406c..d1cc32a5 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -1609,20 +1609,10 @@
 }
 
 // static
-bool V4L2Device::IsMultiPlanarV4L2PixFmt(uint32_t pix_fmt) {
-  constexpr uint32_t kMultiV4L2PixFmts[] = {
-      V4L2_PIX_FMT_NV12M,   V4L2_PIX_FMT_MT21C,   V4L2_PIX_FMT_MM21,
-      V4L2_PIX_FMT_YUV420M, V4L2_PIX_FMT_YVU420M, V4L2_PIX_FMT_YUV422M,
-  };
-  return std::find(std::cbegin(kMultiV4L2PixFmts), std::cend(kMultiV4L2PixFmts),
-                   pix_fmt) != std::cend(kMultiV4L2PixFmts);
-}
-
-// static
 size_t V4L2Device::GetNumPlanesOfV4L2PixFmt(uint32_t pix_fmt) {
-  if (IsMultiPlanarV4L2PixFmt(pix_fmt)) {
-    return VideoFrame::NumPlanes(
-        Fourcc::FromV4L2PixFmt(pix_fmt).ToVideoPixelFormat());
+  Fourcc fourcc = Fourcc::FromV4L2PixFmt(pix_fmt);
+  if (fourcc.IsMultiPlanar()) {
+    return VideoFrame::NumPlanes(fourcc.ToVideoPixelFormat());
   }
   return 1u;
 }
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index 862ed5f..e875111f 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -410,9 +410,6 @@
   static base::Optional<VideoFrameLayout> V4L2FormatToVideoFrameLayout(
       const struct v4l2_format& format);
 
-  // Returns whether |pix_fmt| is multi planar.
-  static bool IsMultiPlanarV4L2PixFmt(uint32_t pix_fmt);
-
   // Returns number of planes of |pix_fmt|.
   static size_t GetNumPlanesOfV4L2PixFmt(uint32_t pix_fmt);
 
diff --git a/media/learning/mojo/public/cpp/mojo_learning_task_controller.cc b/media/learning/mojo/public/cpp/mojo_learning_task_controller.cc
index 46e9a16..c559d38 100644
--- a/media/learning/mojo/public/cpp/mojo_learning_task_controller.cc
+++ b/media/learning/mojo/public/cpp/mojo_learning_task_controller.cc
@@ -13,8 +13,8 @@
 
 MojoLearningTaskController::MojoLearningTaskController(
     const LearningTask& task,
-    mojom::LearningTaskControllerPtr controller_ptr)
-    : task_(task), controller_ptr_(std::move(controller_ptr)) {}
+    mojo::PendingRemote<mojom::LearningTaskController> controller)
+    : task_(task), controller_(std::move(controller)) {}
 
 MojoLearningTaskController::~MojoLearningTaskController() = default;
 
@@ -24,17 +24,17 @@
     const base::Optional<TargetValue>& default_target) {
   // We don't need to keep track of in-flight observations, since the service
   // side handles it for us.
-  controller_ptr_->BeginObservation(id, features, default_target);
+  controller_->BeginObservation(id, features, default_target);
 }
 
 void MojoLearningTaskController::CompleteObservation(
     base::UnguessableToken id,
     const ObservationCompletion& completion) {
-  controller_ptr_->CompleteObservation(id, completion);
+  controller_->CompleteObservation(id, completion);
 }
 
 void MojoLearningTaskController::CancelObservation(base::UnguessableToken id) {
-  controller_ptr_->CancelObservation(id);
+  controller_->CancelObservation(id);
 }
 
 const LearningTask& MojoLearningTaskController::GetLearningTask() {
diff --git a/media/learning/mojo/public/cpp/mojo_learning_task_controller.h b/media/learning/mojo/public/cpp/mojo_learning_task_controller.h
index 81d432e..fdfb5a3f 100644
--- a/media/learning/mojo/public/cpp/mojo_learning_task_controller.h
+++ b/media/learning/mojo/public/cpp/mojo_learning_task_controller.h
@@ -11,6 +11,8 @@
 #include "base/macros.h"
 #include "media/learning/common/learning_task_controller.h"
 #include "media/learning/mojo/public/mojom/learning_task_controller.mojom.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace media {
 namespace learning {
@@ -20,9 +22,10 @@
     : public LearningTaskController {
  public:
   // |task| will be provided by GetLearningTask().  Hopefully, it matches
-  // whatever |controller_ptr| uses.
-  MojoLearningTaskController(const LearningTask& task,
-                             mojom::LearningTaskControllerPtr controller_ptr);
+  // whatever |controller| uses.
+  MojoLearningTaskController(
+      const LearningTask& task,
+      mojo::PendingRemote<mojom::LearningTaskController> controller);
   ~MojoLearningTaskController() override;
 
   // LearningTaskController
@@ -37,7 +40,7 @@
 
  private:
   LearningTask task_;
-  mojom::LearningTaskControllerPtr controller_ptr_;
+  mojo::Remote<mojom::LearningTaskController> controller_;
 
   DISALLOW_COPY_AND_ASSIGN(MojoLearningTaskController);
 };
diff --git a/media/learning/mojo/public/cpp/mojo_learning_task_controller_unittest.cc b/media/learning/mojo/public/cpp/mojo_learning_task_controller_unittest.cc
index 93a16f1..77c9dd4 100644
--- a/media/learning/mojo/public/cpp/mojo_learning_task_controller_unittest.cc
+++ b/media/learning/mojo/public/cpp/mojo_learning_task_controller_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread.h"
 #include "media/learning/mojo/public/cpp/mojo_learning_task_controller.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
@@ -59,21 +59,16 @@
 
  public:
   MojoLearningTaskControllerTest()
-      : learning_controller_binding_(&fake_learning_controller_) {}
+      : learning_controller_receiver_(&fake_learning_controller_) {}
   ~MojoLearningTaskControllerTest() override = default;
 
   void SetUp() override {
     // Create a LearningTask.
     task_.name = "MyLearningTask";
 
-    // Create a fake learner provider mojo impl.
-    mojom::LearningTaskControllerPtr learning_controller_ptr;
-    learning_controller_binding_.Bind(
-        mojo::MakeRequest(&learning_controller_ptr));
-
     // Tell |learning_controller_| to forward to the fake learner impl.
     learning_controller_ = std::make_unique<MojoLearningTaskController>(
-        task_, std::move(learning_controller_ptr));
+        task_, learning_controller_receiver_.BindNewPipeAndPassRemote());
   }
 
   // Mojo stuff.
@@ -81,7 +76,7 @@
 
   LearningTask task_;
   FakeMojoLearningTaskController fake_learning_controller_;
-  mojo::Binding<mojom::LearningTaskController> learning_controller_binding_;
+  mojo::Receiver<mojom::LearningTaskController> learning_controller_receiver_;
 
   // The learner under test.
   std::unique_ptr<MojoLearningTaskController> learning_controller_;
diff --git a/media/mojo/README.md b/media/mojo/README.md
index d5097dc..c318e13 100644
--- a/media/mojo/README.md
+++ b/media/mojo/README.md
@@ -81,13 +81,19 @@
 `MediaInterfaceProxy` is a central hub for handling media player mojo interface
 requests. By default it will forward all the requests to the
 [`MediaService`](#MediaService). But it also has the flexibility to handle some
-special or more complicated use cases. For example, on desktop platforms, when
-library CDM is enabled, the `media::mojom::ContentDecryptionModule` request will
-be forwarded to the [`CdmService`](#CdmService) running in its own CDM (utility)
-process. For another example, on Android, the `media::mojom::Renderer` request
-is handled in the `RenderFrameHostImpl` context directly by creating
-`MediaPlayerRenderer` in the browser process, even though the `MediaService` is
-configured to run in the GPU process.
+special or more complicated use cases. For example:
+* On desktop platforms, when library CDM is enabled, the
+  `media::mojom::ContentDecryptionModule` request will be forwarded to the
+  [`CdmService`](#CdmService) running in its own CDM (utility) process.
+* On Android, the `media::mojom::Renderer` request is handled in the
+  `RenderFrameHostImpl` context directly by creating `MediaPlayerRenderer` in
+  the browser process, even though the `MediaService` is configured to run in
+  the GPU process.
+* On Chromecast, the `media::mojom::Renderer` and
+  `media::mojom::ContentDecryptionModule` requests are handled by
+  [`MediaRendererService`](#MediaRendererService) which runs in the browser
+  process. The `media::mojom::VideoDecoder` request is handled by the default
+  `MediaService` which runs in the GPU process.
 
 Note that `media::mojom::InterfaceFactory` interface is reused in the
 communication between `MediaInterfaceProxy` and `MediaService` (see
@@ -244,6 +250,19 @@
 Note that `CdmService` only supports `media::mojom::CDM` and does NOT support
 other media player mojo interfaces.
 
+### MediaRendererService
+
+`MediaRendererService` supports `media::mojom::Renderer` and
+`media::mojom::CDM`. It's hosted in a different process than the default
+`MediaService`. It's registered in `ServiceManagerContext` using
+'kMediaRendererServiceName`. This allows to run `media::mojom::VideoDecoder` and
+`media::mojom::Renderer` in two different processes. Currently Chromecast use
+this to support `CastRenderer` `CDM` in browser process and GPU accelerated
+video decoder in GPU process. The main goals are:
+1. Allow two pages to hold their own video pipeline simultaneously, because
+   `CastRenderer` only support one video pipeline at a time.
+2. Support GPU accelerated video decoder for RTC path.
+
 ### Mojo CDM and Mojo Decryptor
 
 Mojo CDM is special among all media player mojo interfaces because it is needed
diff --git a/media/mojo/clients/mojo_android_overlay.cc b/media/mojo/clients/mojo_android_overlay.cc
index ae2d8a4..805b030 100644
--- a/media/mojo/clients/mojo_android_overlay.cc
+++ b/media/mojo/clients/mojo_android_overlay.cc
@@ -5,15 +5,16 @@
 #include "media/mojo/clients/mojo_android_overlay.h"
 
 #include "gpu/ipc/common/gpu_surface_lookup.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/cpp/connect.h"
 
 namespace media {
 
 MojoAndroidOverlay::MojoAndroidOverlay(
-    mojom::AndroidOverlayProviderPtr provider_ptr,
+    mojo::PendingRemote<mojom::AndroidOverlayProvider> pending_provider,
     AndroidOverlayConfig config,
     const base::UnguessableToken& routing_token)
-    : config_(std::move(config)), binding_(this) {
+    : config_(std::move(config)) {
   // Fill in details of |config| into |mojo_config|.  Our caller could do this
   // too, but since we want to retain |config_| anyway, we do it here.
   mojom::AndroidOverlayConfigPtr mojo_config =
@@ -23,14 +24,15 @@
   mojo_config->secure = config_.secure;
   mojo_config->power_efficient = config_.power_efficient;
 
-  mojom::AndroidOverlayClientPtr ptr;
-  binding_.Bind(mojo::MakeRequest(&ptr));
-  provider_ptr->CreateOverlay(mojo::MakeRequest(&overlay_ptr_), std::move(ptr),
-                              std::move(mojo_config));
+  mojo::Remote<mojom::AndroidOverlayProvider> provider(
+      std::move(pending_provider));
+  provider->CreateOverlay(overlay_.BindNewPipeAndPassReceiver(),
+                          receiver_.BindNewPipeAndPassRemote(),
+                          std::move(mojo_config));
 }
 
 MojoAndroidOverlay::~MojoAndroidOverlay() {
-  // Dropping |overlay_ptr_| will signal to the implementation that we're done
+  // Dropping |overlay_| will signal to the implementation that we're done
   // with the surface.  If a synchronous destroy is pending, then it can be
   // allowed to continue.
 }
@@ -40,7 +42,7 @@
   if (!received_surface_)
     return;
 
-  overlay_ptr_->ScheduleLayout(rect);
+  overlay_->ScheduleLayout(rect);
 }
 
 const base::android::JavaRef<jobject>& MojoAndroidOverlay::GetJavaSurface()
@@ -68,7 +70,7 @@
 }
 
 void MojoAndroidOverlay::OnDestroyed() {
-  // Note that |overlay_ptr_| might not be bound yet, or we might not have ever
+  // Note that |overlay_| might not be bound yet, or we might not have ever
   // gotten a surface.  Regardless, the overlay cannot be used.
 
   if (!received_surface_)
@@ -76,7 +78,7 @@
   else
     RunSurfaceDestroyedCallbacks();
 
-  // Note: we do not delete |overlay_ptr_| here.  Our client must delete us to
+  // Note: we do not delete |overlay_| here.  Our client must delete us to
   // signal that we should do that, since it still might be in use.
 }
 
diff --git a/media/mojo/clients/mojo_android_overlay.h b/media/mojo/clients/mojo_android_overlay.h
index 05aceb14..851870e 100644
--- a/media/mojo/clients/mojo_android_overlay.h
+++ b/media/mojo/clients/mojo_android_overlay.h
@@ -9,7 +9,9 @@
 #include "base/unguessable_token.h"
 #include "media/base/android/android_overlay.h"
 #include "media/mojo/mojom/android_overlay.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace media {
 
@@ -17,9 +19,10 @@
 class MojoAndroidOverlay : public AndroidOverlay,
                            public mojom::AndroidOverlayClient {
  public:
-  MojoAndroidOverlay(mojom::AndroidOverlayProviderPtr provider_ptr,
-                     AndroidOverlayConfig config,
-                     const base::UnguessableToken& routing_token);
+  MojoAndroidOverlay(
+      mojo::PendingRemote<mojom::AndroidOverlayProvider> pending_provider,
+      AndroidOverlayConfig config,
+      const base::UnguessableToken& routing_token);
 
   ~MojoAndroidOverlay() override;
 
@@ -34,8 +37,8 @@
 
  private:
   AndroidOverlayConfig config_;
-  mojom::AndroidOverlayPtr overlay_ptr_;
-  mojo::Binding<mojom::AndroidOverlayClient> binding_;
+  mojo::Remote<mojom::AndroidOverlay> overlay_;
+  mojo::Receiver<mojom::AndroidOverlayClient> receiver_{this};
   gl::ScopedJavaSurface surface_;
 
   // Have we received OnSurfaceReady yet?
diff --git a/media/mojo/clients/mojo_android_overlay_unittest.cc b/media/mojo/clients/mojo_android_overlay_unittest.cc
index 59bc88f6..914c020b4 100644
--- a/media/mojo/clients/mojo_android_overlay_unittest.cc
+++ b/media/mojo/clients/mojo_android_overlay_unittest.cc
@@ -13,8 +13,10 @@
 #include "gpu/ipc/common/gpu_surface_tracker.h"
 #include "media/base/mock_filters.h"
 #include "media/mojo/clients/mojo_android_overlay.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/android/scoped_java_surface.h"
@@ -52,25 +54,27 @@
       : public StrictMock<mojom::AndroidOverlayProvider> {
    public:
     // These argument types lack default constructors, so gmock can't mock them.
-    void CreateOverlay(mojom::AndroidOverlayRequest overlay_request,
-                       mojom::AndroidOverlayClientPtr client,
-                       mojom::AndroidOverlayConfigPtr config) override {
-      overlay_request_ = std::move(overlay_request);
-      client_ = std::move(client);
+    void CreateOverlay(
+        mojo::PendingReceiver<mojom::AndroidOverlay> overlay_receiver,
+        mojo::PendingRemote<mojom::AndroidOverlayClient> client,
+        mojom::AndroidOverlayConfigPtr config) override {
+      overlay_receiver_ = std::move(overlay_receiver);
+      client_.Bind(std::move(client));
       config_ = std::move(config);
       OverlayCreated();
     }
 
     MOCK_METHOD0(OverlayCreated, void(void));
 
-    mojom::AndroidOverlayRequest overlay_request_;
-    mojom::AndroidOverlayClientPtr client_;
+    mojo::PendingReceiver<mojom::AndroidOverlay> overlay_receiver_;
+    mojo::Remote<mojom::AndroidOverlayClient> client_;
     mojom::AndroidOverlayConfigPtr config_;
   };
 
  public:
   MojoAndroidOverlayTest()
-      : provider_binding_(&mock_provider_), overlay_binding_(&mock_overlay_) {}
+      : provider_receiver_(&mock_provider_),
+        overlay_receiver_(&mock_overlay_) {}
 
   ~MojoAndroidOverlayTest() override {}
 
@@ -102,16 +106,15 @@
   }
 
   // Create an overlay in |overlay_client_| using the current config, but do
-  // not bind anything to |overlay_request_| yet.
+  // not bind anything to |overlay_receiver_| yet.
   void CreateOverlay() {
     EXPECT_CALL(mock_provider_, OverlayCreated());
 
     base::UnguessableToken routing_token = base::UnguessableToken::Create();
-    mojom::AndroidOverlayProviderPtr provider_ptr;
-    provider_binding_.Bind(mojo::MakeRequest(&provider_ptr));
 
-    overlay_client_.reset(new MojoAndroidOverlay(
-        std::move(provider_ptr), std::move(config_), routing_token));
+    overlay_client_.reset(
+        new MojoAndroidOverlay(provider_receiver_.BindNewPipeAndPassRemote(),
+                               std::move(config_), routing_token));
     overlay_client_->AddSurfaceDestroyedCallback(base::Bind(
         &MockClientCallbacks::OnDestroyed, base::Unretained(&callbacks_)));
     base::RunLoop().RunUntilIdle();
@@ -122,7 +125,7 @@
     CreateOverlay();
 
     // Bind an overlay to the request.
-    overlay_binding_.Bind(std::move(mock_provider_.overlay_request_));
+    overlay_receiver_.Bind(std::move(mock_provider_.overlay_receiver_));
     base::RunLoop().RunUntilIdle();
   }
 
@@ -161,12 +164,12 @@
   // |interface_provider_| will bind it.
   MockAndroidOverlayProvider mock_provider_;
 
-  // Binding for |mock_provider_|.
-  mojo::Binding<mojom::AndroidOverlayProvider> provider_binding_;
+  // Receiver for |mock_provider_|.
+  mojo::Receiver<mojom::AndroidOverlayProvider> provider_receiver_;
 
   // The mock overlay impl that |mock_provider_| will provide.
   MockAndroidOverlay mock_overlay_;
-  mojo::Binding<mojom::AndroidOverlay> overlay_binding_;
+  mojo::Receiver<mojom::AndroidOverlay> overlay_receiver_;
 
   // The client under test.
   std::unique_ptr<AndroidOverlay> overlay_client_;
diff --git a/media/mojo/clients/mojo_audio_decoder.cc b/media/mojo/clients/mojo_audio_decoder.cc
index cfae4952..82373666 100644
--- a/media/mojo/clients/mojo_audio_decoder.cc
+++ b/media/mojo/clients/mojo_audio_decoder.cc
@@ -21,9 +21,9 @@
 
 MojoAudioDecoder::MojoAudioDecoder(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    mojom::AudioDecoderPtr remote_decoder)
+    mojo::PendingRemote<mojom::AudioDecoder> remote_decoder)
     : task_runner_(task_runner),
-      remote_decoder_info_(remote_decoder.PassInterface()),
+      pending_remote_decoder_(std::move(remote_decoder)),
       writer_capacity_(
           GetDefaultDecoderBufferConverterCapacity(DemuxerStream::AUDIO)),
       client_binding_(this) {
@@ -54,7 +54,7 @@
     BindRemoteDecoder();
 
   // This could happen during reinitialization.
-  if (remote_decoder_.encountered_error()) {
+  if (!remote_decoder_.is_connected()) {
     DVLOG(1) << __func__ << ": Connection error happened.";
     task_runner_->PostTask(FROM_HERE,
                            base::BindOnce(std::move(init_cb), false));
@@ -89,7 +89,7 @@
   DVLOG(3) << __func__;
   DCHECK(task_runner_->BelongsToCurrentThread());
 
-  if (remote_decoder_.encountered_error()) {
+  if (!remote_decoder_.is_connected()) {
     task_runner_->PostTask(
         FROM_HERE, base::BindOnce(decode_cb, DecodeStatus::DECODE_ERROR));
     return;
@@ -115,7 +115,7 @@
   DVLOG(2) << __func__;
   DCHECK(task_runner_->BelongsToCurrentThread());
 
-  if (remote_decoder_.encountered_error()) {
+  if (!remote_decoder_.is_connected()) {
     if (decode_cb_) {
       task_runner_->PostTask(
           FROM_HERE,
@@ -143,11 +143,11 @@
   DVLOG(1) << __func__;
   DCHECK(task_runner_->BelongsToCurrentThread());
 
-  remote_decoder_.Bind(std::move(remote_decoder_info_));
+  remote_decoder_.Bind(std::move(pending_remote_decoder_));
 
   // Using base::Unretained(this) is safe because |this| owns |remote_decoder_|,
   // and the error handler can't be invoked once |remote_decoder_| is destroyed.
-  remote_decoder_.set_connection_error_handler(
+  remote_decoder_.set_disconnect_handler(
       base::Bind(&MojoAudioDecoder::OnConnectionError, base::Unretained(this)));
 
   mojom::AudioDecoderClientAssociatedPtrInfo client_ptr_info;
@@ -173,7 +173,7 @@
 void MojoAudioDecoder::OnConnectionError() {
   DVLOG(1) << __func__;
   DCHECK(task_runner_->BelongsToCurrentThread());
-  DCHECK(remote_decoder_.encountered_error());
+  DCHECK(!remote_decoder_.is_connected());
 
   if (init_cb_) {
     std::move(init_cb_).Run(false);
diff --git a/media/mojo/clients/mojo_audio_decoder.h b/media/mojo/clients/mojo_audio_decoder.h
index 5e88c808..eafee14 100644
--- a/media/mojo/clients/mojo_audio_decoder.h
+++ b/media/mojo/clients/mojo_audio_decoder.h
@@ -14,6 +14,8 @@
 #include "media/mojo/mojom/media_types.mojom.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -27,7 +29,7 @@
 class MojoAudioDecoder : public AudioDecoder, public mojom::AudioDecoderClient {
  public:
   MojoAudioDecoder(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-                   mojom::AudioDecoderPtr remote_decoder);
+                   mojo::PendingRemote<mojom::AudioDecoder> remote_decoder);
   ~MojoAudioDecoder() final;
 
   // AudioDecoder implementation.
@@ -69,12 +71,12 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   // This class is constructed on one thread and used exclusively on another
-  // thread. This member is used to safely pass the AudioDecoderPtr from one
-  // thread to another. It is set in the constructor and is consumed in
-  // Initialize().
-  mojom::AudioDecoderPtrInfo remote_decoder_info_;
+  // thread. This member is used to safely pass the
+  // mojo::PendingRemote<AudioDecoder> from one thread to another. It is set in
+  // the constructor and is consumed in Initialize().
+  mojo::PendingRemote<mojom::AudioDecoder> pending_remote_decoder_;
 
-  mojom::AudioDecoderPtr remote_decoder_;
+  mojo::Remote<mojom::AudioDecoder> remote_decoder_;
 
   std::unique_ptr<MojoDecoderBufferWriter> mojo_decoder_buffer_writer_;
 
diff --git a/media/mojo/clients/mojo_audio_decoder_unittest.cc b/media/mojo/clients/mojo_audio_decoder_unittest.cc
index 62ba66f9..22efe050 100644
--- a/media/mojo/clients/mojo_audio_decoder_unittest.cc
+++ b/media/mojo/clients/mojo_audio_decoder_unittest.cc
@@ -23,7 +23,9 @@
 #include "media/mojo/mojom/audio_decoder.mojom.h"
 #include "media/mojo/services/mojo_audio_decoder_service.h"
 #include "media/mojo/services/mojo_cdm_service_context.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -59,12 +61,12 @@
     service_task_runner_ = service_thread_.task_runner();
 
     // Setup the mojo connection.
-    mojom::AudioDecoderPtr remote_audio_decoder;
+    mojo::PendingRemote<mojom::AudioDecoder> remote_audio_decoder;
     service_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&MojoAudioDecoderTest::ConnectToService,
                        base::Unretained(this),
-                       base::Passed(mojo::MakeRequest(&remote_audio_decoder))));
+                       remote_audio_decoder.InitWithNewPipeAndPassReceiver()));
     mojo_audio_decoder_.reset(
         new MojoAudioDecoder(task_environment_.GetMainThreadTaskRunner(),
                              std::move(remote_audio_decoder)));
@@ -106,7 +108,7 @@
     run_loop_->QuitWhenIdle();
   }
 
-  void ConnectToService(mojom::AudioDecoderRequest request) {
+  void ConnectToService(mojo::PendingReceiver<mojom::AudioDecoder> receiver) {
     DCHECK(service_task_runner_->BelongsToCurrentThread());
 
     std::unique_ptr<StrictMock<MockAudioDecoder>> mock_audio_decoder(
@@ -123,10 +125,10 @@
     EXPECT_CALL(*mock_audio_decoder_, Reset_(_))
         .WillRepeatedly(RunOnceCallback<0>());
 
-    mojo::MakeStrongBinding(
+    mojo::MakeSelfOwnedReceiver(
         std::make_unique<MojoAudioDecoderService>(
             &mojo_cdm_service_context_, std::move(mock_audio_decoder)),
-        std::move(request));
+        std::move(receiver));
   }
 
   void SetWriterCapacity(uint32_t capacity) {
diff --git a/media/mojo/clients/mojo_decoder_factory.cc b/media/mojo/clients/mojo_decoder_factory.cc
index 74869f03..b19440d1 100644
--- a/media/mojo/clients/mojo_decoder_factory.cc
+++ b/media/mojo/clients/mojo_decoder_factory.cc
@@ -15,7 +15,7 @@
 #include "media/mojo/clients/mojo_video_decoder.h"
 #include "media/mojo/mojom/audio_decoder.mojom.h"
 #include "media/mojo/mojom/interface_factory.mojom.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace media {
 
@@ -32,11 +32,12 @@
     MediaLog* media_log,
     std::vector<std::unique_ptr<AudioDecoder>>* audio_decoders) {
 #if BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER)
-  mojom::AudioDecoderPtr audio_decoder_ptr;
-  interface_factory_->CreateAudioDecoder(mojo::MakeRequest(&audio_decoder_ptr));
+  mojo::PendingRemote<mojom::AudioDecoder> audio_decoder;
+  interface_factory_->CreateAudioDecoder(
+      audio_decoder.InitWithNewPipeAndPassReceiver());
 
   audio_decoders->push_back(std::make_unique<MojoAudioDecoder>(
-      task_runner, std::move(audio_decoder_ptr)));
+      task_runner, std::move(audio_decoder)));
 #endif
 }
 
diff --git a/media/mojo/mojom/android_overlay.mojom b/media/mojo/mojom/android_overlay.mojom
index 7e93634..4e861e08 100644
--- a/media/mojo/mojom/android_overlay.mojom
+++ b/media/mojo/mojom/android_overlay.mojom
@@ -16,8 +16,9 @@
 interface AndroidOverlayProvider {
   // Create an overlay and send it to |client|, using |config| as the initial
   // configuration.  |overlay| will hold the overlay object.
-  CreateOverlay(AndroidOverlay& overlay, AndroidOverlayClient client,
-    AndroidOverlayConfig config);
+  CreateOverlay(pending_receiver<AndroidOverlay> overlay,
+                pending_remote<AndroidOverlayClient> client,
+                AndroidOverlayConfig config);
 };
 
 // One overlay instance.  This will be provided by the provider to clients
diff --git a/media/mojo/mojom/constants.mojom b/media/mojo/mojom/constants.mojom
index d724101..3c25566 100644
--- a/media/mojo/mojom/constants.mojom
+++ b/media/mojo/mojom/constants.mojom
@@ -9,6 +9,13 @@
 // media/mojo/services/media_manifest.json
 const string kMediaServiceName = "media";
 
+// The default service name for MediaService hosting Renderer and CDM service.
+// It's used when the default MediaService is hosted in a different process.
+// This must match the name in media/mojo/service/media_manifest.json
+// Currently this is used on Chromecast and the service is hosted in browser
+// process.
+const string kMediaRendererServiceName = "media_renderer";
+
 // The default service name for MediaService hosting only the CDM service.
 // This must match the name in media/mojo/services/cdm_manifest.json
 const string kCdmServiceName = "cdm";
diff --git a/media/mojo/mojom/interface_factory.mojom b/media/mojo/mojom/interface_factory.mojom
index 751cec6..a7e69871 100644
--- a/media/mojo/mojom/interface_factory.mojom
+++ b/media/mojo/mojom/interface_factory.mojom
@@ -17,7 +17,7 @@
 // A factory for creating media mojo interfaces. Renderers can only access
 // ContentDecryptionModules created with the same factory.
 interface InterfaceFactory {
-  CreateAudioDecoder(AudioDecoder& audio_decoder);
+  CreateAudioDecoder(pending_receiver<AudioDecoder> audio_decoder);
   CreateVideoDecoder(VideoDecoder& video_decoder);
 
   // Creates a regular media::Renderer (DefaultRendererFactory).
diff --git a/media/mojo/mojom/media_metrics_provider.mojom b/media/mojo/mojom/media_metrics_provider.mojom
index 9817ada..e8b76cf 100644
--- a/media/mojo/mojom/media_metrics_provider.mojom
+++ b/media/mojo/mojom/media_metrics_provider.mojom
@@ -64,7 +64,8 @@
 
   // Returns a LearningTaskController for the given |taskName|.
   AcquireLearningTaskController(
-    string taskName, media.learning.mojom.LearningTaskController& controller);
+    string taskName,
+    pending_receiver<media.learning.mojom.LearningTaskController> controller);
 
   // Reports that bytes have been received by the media player.
   AddBytesReceived(uint64 bytes_received);
diff --git a/media/mojo/services/interface_factory_impl.cc b/media/mojo/services/interface_factory_impl.cc
index 5218d57..fc4775b 100644
--- a/media/mojo/services/interface_factory_impl.cc
+++ b/media/mojo/services/interface_factory_impl.cc
@@ -25,11 +25,11 @@
 #include "media/mojo/services/mojo_video_decoder_service.h"
 #endif  // BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER)
 
-#if BUILDFLAG(ENABLE_MOJO_RENDERER)
+#if BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER)
 #include "base/bind_helpers.h"
 #include "media/base/renderer.h"
 #include "media/mojo/services/mojo_renderer_service.h"
-#endif  // BUILDFLAG(ENABLE_MOJO_RENDERER)
+#endif  // BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER)
 
 #if BUILDFLAG(ENABLE_MOJO_CDM)
 #include "media/base/cdm_factory.h"
@@ -62,7 +62,7 @@
 // mojom::InterfaceFactory implementation.
 
 void InterfaceFactoryImpl::CreateAudioDecoder(
-    mojo::InterfaceRequest<mojom::AudioDecoder> request) {
+    mojo::PendingReceiver<mojom::AudioDecoder> receiver) {
   DVLOG(2) << __func__;
 #if BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER)
   scoped_refptr<base::SingleThreadTaskRunner> task_runner(
@@ -75,10 +75,10 @@
     return;
   }
 
-  audio_decoder_bindings_.AddBinding(
+  audio_decoder_receivers_.Add(
       std::make_unique<MojoAudioDecoderService>(&cdm_service_context_,
                                                 std::move(audio_decoder)),
-      std::move(request));
+      std::move(receiver));
 #endif  // BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER)
 }
 
@@ -129,7 +129,6 @@
     const base::UnguessableToken& overlay_plane_id,
     media::mojom::RendererRequest request) {
   DVLOG(2) << __func__;
-#if BUILDFLAG(ENABLE_MOJO_RENDERER)
   auto renderer = mojo_media_client_->CreateCastRenderer(
       host_interfaces_.get(), base::ThreadTaskRunnerHandle::Get(), &media_log_,
       overlay_plane_id);
@@ -153,7 +152,6 @@
       base::IgnoreResult(
           &mojo::StrongBindingSet<mojom::Renderer>::RemoveBinding),
       base::Unretained(&renderer_bindings_), binding_id));
-#endif  // BUILDFLAG(ENABLE_MOJO_RENDERER)
 }
 #endif
 
@@ -230,7 +228,7 @@
 
 bool InterfaceFactoryImpl::IsEmpty() {
 #if BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER)
-  if (!audio_decoder_bindings_.empty())
+  if (!audio_decoder_receivers_.empty())
     return false;
 #endif  // BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER)
 
@@ -268,7 +266,7 @@
       &InterfaceFactoryImpl::OnBindingConnectionError, base::Unretained(this));
 
 #if BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER)
-  audio_decoder_bindings_.set_connection_error_handler(connection_error_cb);
+  audio_decoder_receivers_.set_disconnect_handler(connection_error_cb);
 #endif  // BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER)
 
 #if BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER)
diff --git a/media/mojo/services/interface_factory_impl.h b/media/mojo/services/interface_factory_impl.h
index cf69f77..1814b047 100644
--- a/media/mojo/services/interface_factory_impl.h
+++ b/media/mojo/services/interface_factory_impl.h
@@ -40,7 +40,8 @@
   ~InterfaceFactoryImpl() final;
 
   // mojom::InterfaceFactory implementation.
-  void CreateAudioDecoder(mojom::AudioDecoderRequest request) final;
+  void CreateAudioDecoder(
+      mojo::PendingReceiver<mojom::AudioDecoder> receiver) final;
   void CreateVideoDecoder(mojom::VideoDecoderRequest request) final;
   void CreateDefaultRenderer(const std::string& audio_device_id,
                              mojom::RendererRequest request) final;
@@ -87,18 +88,18 @@
   MojoCdmServiceContext cdm_service_context_;
 
 #if BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER)
-  mojo::StrongBindingSet<mojom::AudioDecoder> audio_decoder_bindings_;
+  mojo::UniqueReceiverSet<mojom::AudioDecoder> audio_decoder_receivers_;
 #endif  // BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER)
 
 #if BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER)
   mojo::StrongBindingSet<mojom::VideoDecoder> video_decoder_bindings_;
 #endif  // BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER)
 
-#if BUILDFLAG(ENABLE_MOJO_RENDERER)
+#if BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER)
   // TODO(xhwang): Use MojoMediaLog for Renderer.
   NullMediaLog media_log_;
   mojo::StrongBindingSet<mojom::Renderer> renderer_bindings_;
-#endif  // BUILDFLAG(ENABLE_MOJO_RENDERER)
+#endif  // BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER)
 
 #if BUILDFLAG(ENABLE_MOJO_CDM)
   std::unique_ptr<CdmFactory> cdm_factory_;
diff --git a/media/mojo/services/media_manifest.cc b/media/mojo/services/media_manifest.cc
index 9bf1eb0..801b47ce4 100644
--- a/media/mojo/services/media_manifest.cc
+++ b/media/mojo/services/media_manifest.cc
@@ -45,4 +45,26 @@
   return *manifest;
 }
 
+const service_manager::Manifest& GetMediaRendererManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest {
+    service_manager::ManifestBuilder()
+        .WithServiceName(mojom::kMediaRendererServiceName)
+        .WithDisplayName("Media Renderer Service")
+        .WithOptions(
+            service_manager::ManifestOptionsBuilder()
+                .WithExecutionMode(
+                    service_manager::Manifest::ExecutionMode::kInProcessBuiltin)
+                .Build())
+        .ExposeCapability(
+            "media:media",
+            service_manager::Manifest::InterfaceList<mojom::MediaService>())
+#if defined(IS_CHROMECAST)
+        .RequireCapability(chromecast::mojom::kChromecastServiceName,
+                           "multizone")
+#endif
+        .Build()
+  };
+  return *manifest;
+}
+
 }  // namespace media
diff --git a/media/mojo/services/media_manifest.h b/media/mojo/services/media_manifest.h
index 95c810b..8c0427b 100644
--- a/media/mojo/services/media_manifest.h
+++ b/media/mojo/services/media_manifest.h
@@ -10,6 +10,7 @@
 namespace media {
 
 const service_manager::Manifest& GetMediaManifest();
+const service_manager::Manifest& GetMediaRendererManifest();
 
 }  // namespace media
 
diff --git a/media/mojo/services/media_metrics_provider.cc b/media/mojo/services/media_metrics_provider.cc
index 5fc4750..46097e5 100644
--- a/media/mojo/services/media_metrics_provider.cc
+++ b/media/mojo/services/media_metrics_provider.cc
@@ -14,6 +14,7 @@
 #include "media/learning/mojo/mojo_learning_task_controller_service.h"
 #include "media/mojo/services/video_decode_stats_recorder.h"
 #include "media/mojo/services/watch_time_recorder.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
@@ -309,7 +310,8 @@
 
 void MediaMetricsProvider::AcquireLearningTaskController(
     const std::string& taskName,
-    media::learning::mojom::LearningTaskControllerRequest request) {
+    mojo::PendingReceiver<media::learning::mojom::LearningTaskController>
+        receiver) {
   learning::LearningSession* session = learning_session_cb_.Run();
   if (!session) {
     DVLOG(3) << __func__ << " Ignoring request, unable to get LearningSession.";
@@ -324,10 +326,10 @@
     return;
   }
 
-  mojo::MakeStrongBinding(
+  mojo::MakeSelfOwnedReceiver(
       std::make_unique<learning::MojoLearningTaskControllerService>(
           controller->GetLearningTask(), std::move(controller)),
-      std::move(request));
+      std::move(receiver));
 }
 
 void MediaMetricsProvider::AddBytesReceived(uint64_t bytes_received) {
diff --git a/media/mojo/services/media_metrics_provider.h b/media/mojo/services/media_metrics_provider.h
index f023618..c63816f2 100644
--- a/media/mojo/services/media_metrics_provider.h
+++ b/media/mojo/services/media_metrics_provider.h
@@ -16,6 +16,7 @@
 #include "media/mojo/mojom/media_metrics_provider.mojom.h"
 #include "media/mojo/services/media_mojo_export.h"
 #include "media/mojo/services/video_decode_perf_history.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
 namespace media {
@@ -107,7 +108,8 @@
       mojom::VideoDecodeStatsRecorderRequest request) override;
   void AcquireLearningTaskController(
       const std::string& taskName,
-      media::learning::mojom::LearningTaskControllerRequest request) override;
+      mojo::PendingReceiver<media::learning::mojom::LearningTaskController>
+          receiver) override;
   void AddBytesReceived(uint64_t bytes_received) override;
 
   void ReportPipelineUMA();
diff --git a/media/mojo/services/watch_time_recorder.cc b/media/mojo/services/watch_time_recorder.cc
index d3e60fc..9af4c1e 100644
--- a/media/mojo/services/watch_time_recorder.cc
+++ b/media/mojo/services/watch_time_recorder.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/string_piece.h"
 #include "media/base/limits.h"
+#include "media/base/video_decoder.h"
 #include "media/base/watch_time_keys.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
@@ -26,24 +27,26 @@
 // List of known AudioDecoder implementations; recorded to UKM, always add new
 // values to the end and do not reorder or delete values from this list.
 enum class AudioDecoderName : int {
-  kUnknown = 0,     // Decoder name string is not recognized or n/a.
-  kFFmpeg = 1,      // FFmpegAudioDecoder
-  kMojo = 2,        // MojoAudioDecoder
-  kDecrypting = 3,  // DecryptingAudioDecoder
+  kUnknown = 0,      // Decoder name string is not recognized or n/a.
+  kFFmpeg = 1,       // FFmpegAudioDecoder
+  kMojo = 2,         // MojoAudioDecoder
+  kDecrypting = 3,   // DecryptingAudioDecoder
+  kMediaPlayer = 4,  // MediaPlayer
 };
 
 // List of known VideoDecoder implementations; recorded to UKM, always add new
 // values to the end and do not reorder or delete values from this list.
 enum class VideoDecoderName : int {
-  kUnknown = 0,     // Decoder name string is not recognized or n/a.
-  kGpu = 1,         // GpuVideoDecoder
-  kFFmpeg = 2,      // FFmpegVideoDecoder
-  kVpx = 3,         // VpxVideoDecoder
-  kAom = 4,         // AomVideoDecoder
-  kMojo = 5,        // MojoVideoDecoder
-  kDecrypting = 6,  // DecryptingVideoDecoder
-  kDav1d = 7,       // Dav1dVideoDecoder
-  kFuchsia = 8,     // FuchsiaVideoDecoder
+  kUnknown = 0,      // Decoder name string is not recognized or n/a.
+  kGpu = 1,          // GpuVideoDecoder
+  kFFmpeg = 2,       // FFmpegVideoDecoder
+  kVpx = 3,          // VpxVideoDecoder
+  kAom = 4,          // AomVideoDecoder
+  kMojo = 5,         // MojoVideoDecoder
+  kDecrypting = 6,   // DecryptingVideoDecoder
+  kDav1d = 7,        // Dav1dVideoDecoder
+  kFuchsia = 8,      // FuchsiaVideoDecoder
+  kMediaPlayer = 9,  // MediaPlayer
 };
 
 static AudioDecoderName ConvertAudioDecoderNameToEnum(const std::string& name) {
@@ -56,6 +59,8 @@
       return AudioDecoderName::kMojo;
     case 0xd39a2eda:
       return AudioDecoderName::kDecrypting;
+    case 0x667dc202:
+      return AudioDecoderName::kMediaPlayer;
     default:
       DLOG_IF(WARNING, !name.empty())
           << "Unknown decoder name encountered; metrics need updating: "
@@ -84,6 +89,8 @@
       return VideoDecoderName::kDav1d;
     case 0x27b31c6a:
       return VideoDecoderName::kFuchsia;
+    case 0x667dc202:
+      return VideoDecoderName::kMediaPlayer;
     default:
       DLOG_IF(WARNING, !name.empty())
           << "Unknown decoder name encountered; metrics need updating: "
diff --git a/media/mojo/services/watch_time_recorder_unittest.cc b/media/mojo/services/watch_time_recorder_unittest.cc
index bcc5fe3..c0030d19 100644
--- a/media/mojo/services/watch_time_recorder_unittest.cc
+++ b/media/mojo/services/watch_time_recorder_unittest.cc
@@ -1247,7 +1247,7 @@
       "FFmpegAudioDecoder", "FFmpegVideoDecoder",     "GpuVideoDecoder",
       "MojoVideoDecoder",   "MojoAudioDecoder",       "VpxVideoDecoder",
       "AomVideoDecoder",    "DecryptingAudioDecoder", "DecryptingVideoDecoder",
-      "Dav1dVideoDecoder",  "FuchsiaVideoDecoder"};
+      "Dav1dVideoDecoder",  "FuchsiaVideoDecoder",    "MediaPlayer"};
   printf("%18s = 0\n", "None");
   for (const auto& name : kDecoderNames)
     printf("%18s = 0x%x\n", name.c_str(), base::PersistentHash(name));
diff --git a/media/video/fake_gpu_memory_buffer.cc b/media/video/fake_gpu_memory_buffer.cc
index 177365e..586f1f83 100644
--- a/media/video/fake_gpu_memory_buffer.cc
+++ b/media/video/fake_gpu_memory_buffer.cc
@@ -14,6 +14,36 @@
 
 namespace media {
 
+namespace {
+
+class FakeGpuMemoryBufferImpl : public gpu::GpuMemoryBufferImpl {
+ public:
+  FakeGpuMemoryBufferImpl(const gfx::Size& size, gfx::BufferFormat format)
+      : gpu::GpuMemoryBufferImpl(
+            gfx::GpuMemoryBufferId(),
+            size,
+            format,
+            gpu::GpuMemoryBufferImpl::DestructionCallback()),
+        fake_gmb_(std::make_unique<media::FakeGpuMemoryBuffer>(size, format)) {}
+
+  // gfx::GpuMemoryBuffer implementation
+  bool Map() override { return fake_gmb_->Map(); }
+  void* memory(size_t plane) override { return fake_gmb_->memory(plane); }
+  void Unmap() override { fake_gmb_->Unmap(); }
+  int stride(size_t plane) const override { return fake_gmb_->stride(plane); }
+  gfx::GpuMemoryBufferType GetType() const override {
+    return fake_gmb_->GetType();
+  }
+  gfx::GpuMemoryBufferHandle CloneHandle() const override {
+    return fake_gmb_->CloneHandle();
+  }
+
+ private:
+  std::unique_ptr<media::FakeGpuMemoryBuffer> fake_gmb_;
+};
+
+}  // namespace
+
 #if defined(OS_LINUX)
 base::ScopedFD GetDummyFD() {
   base::ScopedFD fd(open("/dev/zero", O_RDWR));
@@ -123,4 +153,14 @@
     uint64_t tracing_process_id,
     int importance) const {}
 
+std::unique_ptr<gpu::GpuMemoryBufferImpl>
+FakeGpuMemoryBufferSupport::CreateGpuMemoryBufferImplFromHandle(
+    gfx::GpuMemoryBufferHandle handle,
+    const gfx::Size& size,
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage,
+    gpu::GpuMemoryBufferImpl::DestructionCallback callback) {
+  return std::make_unique<FakeGpuMemoryBufferImpl>(size, format);
+}
+
 }  // namespace media
diff --git a/media/video/fake_gpu_memory_buffer.h b/media/video/fake_gpu_memory_buffer.h
index caf6d9ea..9ca8cda13 100644
--- a/media/video/fake_gpu_memory_buffer.h
+++ b/media/video/fake_gpu_memory_buffer.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 
 namespace media {
@@ -43,6 +44,16 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(FakeGpuMemoryBuffer);
 };
 
+class FakeGpuMemoryBufferSupport : public gpu::GpuMemoryBufferSupport {
+ public:
+  std::unique_ptr<gpu::GpuMemoryBufferImpl> CreateGpuMemoryBufferImplFromHandle(
+      gfx::GpuMemoryBufferHandle handle,
+      const gfx::Size& size,
+      gfx::BufferFormat format,
+      gfx::BufferUsage usage,
+      gpu::GpuMemoryBufferImpl::DestructionCallback callback) override;
+};
+
 }  // namespace media
 
 #endif  // MEDIA_VIDEO_FAKE_GPU_MEMORY_BUFFER_H_
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 1835e96..5a1c624 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -7000,3 +7000,17 @@
   ]
   seed_corpus = "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer_corpus/"
 }
+
+if (is_linux) {
+  fuzzer_test("net_base_address_tracker_linux_fuzzer") {
+    sources = [
+      "base/address_tracker_linux_fuzzer.cc",
+    ]
+    deps = [
+      ":net_fuzzer_test_support",
+      ":test_support",
+      "//base",
+      "//net",
+    ]
+  }
+}
diff --git a/net/base/address_tracker_linux.cc b/net/base/address_tracker_linux.cc
index 9820b6d0..5c255885 100644
--- a/net/base/address_tracker_linux.cc
+++ b/net/base/address_tracker_linux.cc
@@ -313,15 +313,15 @@
     UpdateCurrentConnectionType();
 }
 
-void AddressTrackerLinux::HandleMessage(char* buffer,
+void AddressTrackerLinux::HandleMessage(const char* buffer,
                                         size_t length,
                                         bool* address_changed,
                                         bool* link_changed,
                                         bool* tunnel_changed) {
   DCHECK(buffer);
-  for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer);
-       NLMSG_OK(header, length);
-       header = NLMSG_NEXT(header, length)) {
+  for (const struct nlmsghdr* header =
+           reinterpret_cast<const struct nlmsghdr*>(buffer);
+       NLMSG_OK(header, length); header = NLMSG_NEXT(header, length)) {
     switch (header->nlmsg_type) {
       case NLMSG_DONE:
         return;
diff --git a/net/base/address_tracker_linux.h b/net/base/address_tracker_linux.h
index f937eabf..f3d38fc 100644
--- a/net/base/address_tracker_linux.h
+++ b/net/base/address_tracker_linux.h
@@ -119,7 +119,7 @@
   // |*link_changed| to true if |online_links_| changed, sets |*tunnel_changed|
   // to true if |online_links_| changed with regards to a tunnel interface while
   // reading the message from |buffer|.
-  void HandleMessage(char* buffer,
+  void HandleMessage(const char* buffer,
                      size_t length,
                      bool* address_changed,
                      bool* link_changed,
diff --git a/net/base/address_tracker_linux_fuzzer.cc b/net/base/address_tracker_linux_fuzzer.cc
new file mode 100644
index 0000000..449a242
--- /dev/null
+++ b/net/base/address_tracker_linux_fuzzer.cc
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/bind_helpers.h"
+#include "net/base/address_tracker_linux.h"
+
+namespace net {
+namespace internal {
+class AddressTrackerLinuxTest {
+ public:
+  static void TestHandleMessage(const char* buffer, size_t length) {
+    std::unordered_set<std::string> ignored_interfaces;
+    AddressTrackerLinux tracker(base::DoNothing(), base::DoNothing(),
+                                base::DoNothing(), ignored_interfaces);
+    bool address_changed, link_changed, tunnel_changed;
+    tracker.HandleMessage(buffer, length, &address_changed, &link_changed,
+                          &tunnel_changed);
+  }
+};
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size == 0)
+    return 0;
+  AddressTrackerLinuxTest::TestHandleMessage(
+      reinterpret_cast<const char*>(data), size);
+  return 0;
+}
+
+}  // namespace internal
+}  // namespace net
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index e3a1e809..e66cebf2 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -386,8 +386,7 @@
 namespace {
 
 // A header string containing any of the following fields will cause
-// an error. The list comes from the XMLHttpRequest standard.
-// https://fetch.spec.whatwg.org/#forbidden-header-name
+// an error. The list comes from the fetch standard.
 const char* const kForbiddenHeaderFields[] = {
     "accept-charset",
     "accept-encoding",
diff --git a/net/http/http_util.h b/net/http/http_util.h
index 2301d28..14b638d 100644
--- a/net/http/http_util.h
+++ b/net/http/http_util.h
@@ -95,7 +95,9 @@
   static bool IsMethodIdempotent(base::StringPiece method);
 
   // Returns true if it is safe to allow users and scripts to specify the header
-  // named |name|.
+  // named |name|. Returns true for headers not in the list at
+  // https://fetch.spec.whatwg.org/#forbidden-header-name. Does not check header
+  // validity.
   static bool IsSafeHeader(base::StringPiece name);
 
   // Returns true if |name| is a valid HTTP header name.
diff --git a/net/quic/mock_crypto_client_stream.cc b/net/quic/mock_crypto_client_stream.cc
index 3de9f46..ab42d351 100644
--- a/net/quic/mock_crypto_client_stream.cc
+++ b/net/quic/mock_crypto_client_stream.cc
@@ -14,6 +14,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
 
 using quic::CLIENT;
 using quic::ConnectionCloseBehavior;
@@ -102,8 +103,7 @@
     case ZERO_RTT: {
       encryption_established_ = true;
       handshake_confirmed_ = false;
-      crypto_negotiated_params_->key_exchange = kC255;
-      crypto_negotiated_params_->aead = kAESG;
+      FillCryptoParams();
       if (proof_verify_details_) {
         reinterpret_cast<QuicSpdyClientSessionBase*>(session())
             ->OnProofVerifyDetailsAvailable(*proof_verify_details_);
@@ -143,8 +143,7 @@
     case CONFIRM_HANDSHAKE: {
       encryption_established_ = true;
       handshake_confirmed_ = true;
-      crypto_negotiated_params_->key_exchange = kC255;
-      crypto_negotiated_params_->aead = kAESG;
+      FillCryptoParams();
       if (proof_verify_details_) {
         reinterpret_cast<QuicSpdyClientSessionBase*>(session())
             ->OnProofVerifyDetailsAvailable(*proof_verify_details_);
@@ -309,4 +308,17 @@
   session()->OnConfigNegotiated();
 }
 
+void MockCryptoClientStream::FillCryptoParams() {
+  if (session()->connection()->version().handshake_protocol ==
+      quic::PROTOCOL_QUIC_CRYPTO) {
+    crypto_negotiated_params_->key_exchange = kC255;
+    crypto_negotiated_params_->aead = kAESG;
+    return;
+  }
+  crypto_negotiated_params_->cipher_suite = TLS1_CK_AES_128_GCM_SHA256 & 0xffff;
+  crypto_negotiated_params_->key_exchange_group = SSL_CURVE_X25519;
+  crypto_negotiated_params_->peer_signature_algorithm =
+      SSL_SIGN_ECDSA_SECP256R1_SHA256;
+}
+
 }  // namespace net
diff --git a/net/quic/mock_crypto_client_stream.h b/net/quic/mock_crypto_client_stream.h
index ab3aa44..0717400 100644
--- a/net/quic/mock_crypto_client_stream.h
+++ b/net/quic/mock_crypto_client_stream.h
@@ -79,6 +79,10 @@
  private:
   void SetConfigNegotiated();
 
+  // Called from CryptoConnect to set appropriate values in
+  // |crypto_negotiated_params_|.
+  void FillCryptoParams();
+
   HandshakeMode handshake_mode_;
   bool encryption_established_;
   bool handshake_confirmed_;
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 4b1f8fa..5d985df8 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -790,7 +790,7 @@
   DCHECK(!(migrate_session_early_v2_ && go_away_on_path_degrading_));
   DCHECK(!(allow_port_migration_ && go_away_on_path_degrading_));
 
-  quic::QuicSpdyClientSessionBase::set_max_allowed_push_id(max_allowed_push_id);
+  quic::QuicSpdyClientSessionBase::SetMaxAllowedPushId(max_allowed_push_id);
   default_network_ = default_network;
   auto* socket_raw = socket.get();
   sockets_.push_back(std::move(socket));
@@ -1204,25 +1204,49 @@
   ssl_info->cert_status = cert_verify_result_->cert_status;
   ssl_info->cert = cert_verify_result_->verified_cert;
 
-  // Map QUIC AEADs to the corresponding TLS 1.3 cipher. OpenSSL's cipher suite
-  // numbers begin with a stray 0x03, so mask them off.
-  quic::QuicTag aead = crypto_stream_->crypto_negotiated_params().aead;
+  ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes;
+  ssl_info->is_issued_by_known_root =
+      cert_verify_result_->is_issued_by_known_root;
+  ssl_info->pkp_bypassed = pkp_bypassed_;
+
+  ssl_info->client_cert_sent = false;
+  ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL;
+  ssl_info->pinning_failure_log = pinning_failure_log_;
+  ssl_info->is_fatal_cert_error = is_fatal_cert_error_;
+
+  ssl_info->UpdateCertificateTransparencyInfo(*ct_verify_result_);
+
+  const auto& crypto_params = crypto_stream_->crypto_negotiated_params();
   uint16_t cipher_suite;
-  switch (aead) {
-    case quic::kAESG:
-      cipher_suite = TLS1_CK_AES_128_GCM_SHA256 & 0xffff;
-      break;
-    case quic::kCC20:
-      cipher_suite = TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff;
-      break;
-    default:
-      NOTREACHED();
-      return false;
+  if (crypto_params.cipher_suite) {
+    cipher_suite = crypto_params.cipher_suite;
+  } else {
+    // Map QUIC AEADs to the corresponding TLS 1.3 cipher. OpenSSL's cipher
+    // suite numbers begin with a stray 0x03, so mask them off.
+    quic::QuicTag aead = crypto_params.aead;
+    switch (aead) {
+      case quic::kAESG:
+        cipher_suite = TLS1_CK_AES_128_GCM_SHA256 & 0xffff;
+        break;
+      case quic::kCC20:
+        cipher_suite = TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff;
+        break;
+      default:
+        NOTREACHED();
+        return false;
+    }
   }
   int ssl_connection_status = 0;
   SSLConnectionStatusSetCipherSuite(cipher_suite, &ssl_connection_status);
   SSLConnectionStatusSetVersion(SSL_CONNECTION_VERSION_QUIC,
                                 &ssl_connection_status);
+  ssl_info->connection_status = ssl_connection_status;
+
+  if (crypto_params.cipher_suite) {
+    ssl_info->key_exchange_group = crypto_params.key_exchange_group;
+    ssl_info->peer_signature_algorithm = crypto_params.peer_signature_algorithm;
+    return true;
+  }
 
   // Report the QUIC key exchange as the corresponding TLS curve.
   switch (crypto_stream_->crypto_negotiated_params().key_exchange) {
@@ -1258,19 +1282,6 @@
       return false;
   }
 
-  ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes;
-  ssl_info->is_issued_by_known_root =
-      cert_verify_result_->is_issued_by_known_root;
-  ssl_info->pkp_bypassed = pkp_bypassed_;
-
-  ssl_info->connection_status = ssl_connection_status;
-  ssl_info->client_cert_sent = false;
-  ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL;
-  ssl_info->pinning_failure_log = pinning_failure_log_;
-  ssl_info->is_fatal_cert_error = is_fatal_cert_error_;
-
-  ssl_info->UpdateCertificateTransparencyInfo(*ct_verify_result_);
-
   return true;
 }
 
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 509dedb..e1e79932 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -368,7 +368,7 @@
 QUIC_FLAG(
     bool,
     FLAGS_quic_reloadable_flag_quic_framer_doesnt_create_initial_encrypter,
-    false)
+    true)
 
 // If true, server drops client initial packets in datagrams < 1200 bytes.
 QUIC_FLAG(bool,
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index 172e430..564a8912 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -1157,6 +1157,10 @@
   header_.source_connection_id_included = HasSourceConnectionId();
   header_.reset_flag = false;
   header_.version_flag = ShouldIncludeVersion(should_include_version);
+  if (quic::VersionHasIetfInvariantHeader(version_.transport_version)) {
+    header_.form = header_.version_flag ? quic::IETF_QUIC_LONG_HEADER_PACKET
+                                        : quic::IETF_QUIC_SHORT_HEADER_PACKET;
+  }
   header_.long_packet_type = long_header_type_;
   header_.packet_number_length = GetPacketNumberLength();
   header_.packet_number = quic::QuicPacketNumber(packet_number);
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 2bb9b46..379530c 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -967,7 +967,7 @@
 }
 
 spdy::SpdySerializedFrame SpdyTestUtil::ConstructSpdyReplyError(int stream_id) {
-  return ConstructSpdyReplyError("500", nullptr, 0, 1);
+  return ConstructSpdyReplyError("500", nullptr, 0, stream_id);
 }
 
 spdy::SpdySerializedFrame SpdyTestUtil::ConstructSpdyGetReply(
diff --git a/remoting/client/notification/notification_client.cc b/remoting/client/notification/notification_client.cc
index 6c613f2..cef9183 100644
--- a/remoting/client/notification/notification_client.cc
+++ b/remoting/client/notification/notification_client.cc
@@ -19,6 +19,10 @@
 #include "remoting/client/notification/version_range.h"
 #include "ui/base/l10n/l10n_util.h"
 
+#if defined(OS_ANDROID)
+#include "base/android/locale_utils.h"
+#endif
+
 namespace remoting {
 
 namespace {
@@ -190,8 +194,16 @@
           std::make_unique<GstaticJsonFetcher>(network_task_runner),
           kCurrentPlatform,
           kCurrentVersion,
+#if defined(OS_ANDROID)
+          // GetApplicationLocale() returns empty string on Android since we
+          // don't pack any .pak file into the apk, so we need to get the locale
+          // string directly.
+          base::android::GetDefaultLocaleString(),
+#else
           l10n_util::GetApplicationLocale(""),
-          kShouldIgnoreDevMessages) {}
+#endif
+          kShouldIgnoreDevMessages) {
+}
 
 NotificationClient::~NotificationClient() = default;
 
diff --git a/remoting/host/xsession_chooser_ui.inc b/remoting/host/xsession_chooser_ui.inc
index 30207688..6f08930 100644
--- a/remoting/host/xsession_chooser_ui.inc
+++ b/remoting/host/xsession_chooser_ui.inc
@@ -27,12 +27,12 @@
         <placeholder/>
       </child>
       <child internal-child="vbox">
-        <object class="GtkBox">
+        <object class="GtkBox" id="layout_box">
           <property name="can_focus">False</property>
           <property name="orientation">vertical</property>
           <property name="spacing">2</property>
           <child internal-child="action_area">
-            <object class="GtkButtonBox">
+            <object class="GtkButtonBox" id="button_box">
               <property name="can_focus">False</property>
               <property name="layout_style">end</property>
               <child>
@@ -69,7 +69,7 @@
             </packing>
           </child>
           <child>
-            <object class="GtkScrolledWindow">
+            <object class="GtkScrolledWindow" id="scroll_container">
               <property name="visible">True</property>
               <property name="can_focus">True</property>
               <property name="shadow_type">in</property>
@@ -79,14 +79,14 @@
                   <property name="can_focus">True</property>
                   <property name="model">session_store</property>
                   <child internal-child="selection">
-                    <object class="GtkTreeSelection">
+                    <object class="GtkTreeSelection" id="session_selection">
                       <property name="mode">browse</property>
                     </object>
                   </child>
                   <child>
                     <object class="GtkTreeViewColumn" id="name_column">
                       <child>
-                        <object class="GtkCellRendererText"/>
+                        <object class="GtkCellRendererText" id="name_text"/>
                         <attributes>
                           <attribute name="text">1</attribute>
                         </attributes>
@@ -96,7 +96,7 @@
                   <child>
                     <object class="GtkTreeViewColumn" id="comment_column">
                       <child>
-                        <object class="GtkCellRendererText"/>
+                        <object class="GtkCellRendererText" id="comment_text"/>
                         <attributes>
                           <attribute name="text">2</attribute>
                         </attributes>
diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd
index 58db62ef..3792bcd97 100644
--- a/remoting/resources/remoting_strings.grd
+++ b/remoting/resources/remoting_strings.grd
@@ -1321,6 +1321,21 @@
         <message name="IDS_HOST_AUTHENTICATION_PROMPT" desc="The prompt to be displayed in the authentication dialog when making changes to the system.  The system will add a sentence asking for an administrator's name and password.  Mac-only.">
           <ph name="PRODUCT_NAME">$1<ex>Chrome Remote Desktop</ex></ph> wants to make changes.
         </message>
+        <message name="IDS_MAC_PERMISSION_WIZARD_TITLE" desc="The title of the dialog/wizard that asks the user to grant various OS permissions to the Chrome Remote Desktop service. Mac-only.">
+          Grant permission to <ph name="PRODUCT_NAME">$1<ex>Chrome Remote Desktop</ex></ph>
+        </message>
+        <message name="IDS_MAC_PERMISSION_WIZARD_NEXT_BUTTON" desc="The text on the button in the Mac permission wizard that takes the user to the next stage of the wizard. Mac-only.">
+          Next
+        </message>
+        <message name="IDS_MAC_PERMISSION_WIZARD_OK_BUTTON" desc="The text on the button on the final part of the Mac permission wizard that exits the wizard. Mac-only.">
+          OK
+        </message>
+        <message name="IDS_MAC_PERMISSION_WIZARD_CANCEL_BUTTON" desc="The text on the button in the Mac permission wizard that allows the user to cancel the permission setup and exits the wizard. Mac-only.">
+          Cancel
+        </message>
+        <message name="IDS_MAC_PERMISSION_WIZARD_FINAL_TEXT" desc="The text shown on the final screen of the permission wizard that lets the user know they have successfully granted the permissions necessary for Chrome Remote Desktop to work properly. Mac-only.">
+          You're all set!
+        </message>
         <message name="IDS_ACCESSIBILITY_PERMISSION_DIALOG_TITLE" desc="The title of the prompt that asks the user to grant 'Accessibility' permission to the Chrome Remote Desktop service. This permission is part of the 'Privacy and Security' preferences pane. Mac-only.">
           Grant 'Accessibility' permission to <ph name="PRODUCT_NAME">$1<ex>Chrome Remote Desktop</ex></ph>
         </message>
diff --git a/services/data_decoder/bundled_exchanges_parser.cc b/services/data_decoder/bundled_exchanges_parser.cc
index 3fab28b..043795ed 100644
--- a/services/data_decoder/bundled_exchanges_parser.cc
+++ b/services/data_decoder/bundled_exchanges_parser.cc
@@ -596,16 +596,12 @@
     // Step 23. "Assert: metadata has an entry with the key "primaryUrl"."
     DCHECK(!metadata_->primary_url.is_empty());
 
-    // Step 24. "If metadata doesn't have entries with keys "requests" and
-    // "manifest", return a "format error" with fallbackUrl."
+    // Step 24. "If metadata doesn't have an entry with the key "requests",
+    // return a "format error" with fallbackUrl."
     if (metadata_->requests.empty()) {
       RunErrorCallbackAndDestroy("Bundle must have an index section.");
       return;
     }
-    if (metadata_->manifest_url.is_empty()) {
-      RunErrorCallbackAndDestroy("Bundle must have a manifest URL.");
-      return;
-    }
 
     // Step 25. "Return metadata."
     RunSuccessCallbackAndDestroy();
diff --git a/services/data_decoder/bundled_exchanges_parser_unittest.cc b/services/data_decoder/bundled_exchanges_parser_unittest.cc
index 2a666c1..d44c50c 100644
--- a/services/data_decoder/bundled_exchanges_parser_unittest.cc
+++ b/services/data_decoder/bundled_exchanges_parser_unittest.cc
@@ -635,7 +635,8 @@
                       "payload");
   TestDataSource data_source(builder.CreateBundle());
 
-  ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source));
+  mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first;
+  ASSERT_TRUE(metadata);
 }
 
 TEST_F(BundledExchangeParserTest, InvalidManifestURL) {
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc
index cb92f0e..03ac494 100644
--- a/services/network/cors/cors_url_loader.cc
+++ b/services/network/cors/cors_url_loader.cc
@@ -464,7 +464,10 @@
   preflight_controller_->PerformPreflightCheck(
       base::BindOnce(&CorsURLLoader::StartNetworkRequest,
                      weak_factory_.GetWeakPtr()),
-      request_, tainted_, net::NetworkTrafficAnnotationTag(traffic_annotation_),
+      request_,
+      PreflightController::WithTrustedHeaderClient(
+          options_ & mojom::kURLLoadOptionUseHeaderClient),
+      tainted_, net::NetworkTrafficAnnotationTag(traffic_annotation_),
       network_loader_factory_);
 }
 
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc
index 1602b6d..57d5ecf 100644
--- a/services/network/cors/cors_url_loader_factory.cc
+++ b/services/network/cors/cors_url_loader_factory.cc
@@ -30,6 +30,8 @@
 
 namespace cors {
 
+bool CorsURLLoaderFactory::allow_external_preflights_for_testing_ = false;
+
 CorsURLLoaderFactory::CorsURLLoaderFactory(
     NetworkContext* context,
     mojom::URLLoaderFactoryParamsPtr params,
@@ -280,12 +282,14 @@
     return false;
   }
 
-  // kURLLoadOptionAsCorsPreflight should be set only by the network service.
-  // Otherwise the network service will be confused.
-  if (options & mojom::kURLLoadOptionAsCorsPreflight) {
-    mojo::ReportBadMessage(
-        "CorsURLLoaderFactory: kURLLoadOptionAsCorsPreflight is set");
-    return false;
+  if (!allow_external_preflights_for_testing_) {
+    // kURLLoadOptionAsCorsPreflight should be set only by the network service.
+    // Otherwise the network service will be confused.
+    if (options & mojom::kURLLoadOptionAsCorsPreflight) {
+      mojo::ReportBadMessage(
+          "CorsURLLoaderFactory: kURLLoadOptionAsCorsPreflight is set");
+      return false;
+    }
   }
 
   // TODO(yhirano): If the request mode is "no-cors", the redirect mode should
diff --git a/services/network/cors/cors_url_loader_factory.h b/services/network/cors/cors_url_loader_factory.h
index 2116a281..db80082 100644
--- a/services/network/cors/cors_url_loader_factory.h
+++ b/services/network/cors/cors_url_loader_factory.h
@@ -56,6 +56,11 @@
   // URLLoaders.
   void ClearBindings();
 
+  // Set whether the factory allows CORS preflights. See IsSane.
+  static void SetAllowExternalPreflightsForTesting(bool allow) {
+    allow_external_preflights_for_testing_ = allow;
+  }
+
  private:
   // Implements mojom::URLLoaderFactory.
   void CreateLoaderAndStart(mojom::URLLoaderRequest request,
@@ -104,6 +109,8 @@
   // additional allowed access list.
   std::unique_ptr<OriginAccessList> factory_bound_origin_access_list_;
 
+  static bool allow_external_preflights_for_testing_;
+
   DISALLOW_COPY_AND_ASSIGN(CorsURLLoaderFactory);
 };
 
diff --git a/services/network/cors/preflight_controller.cc b/services/network/cors/preflight_controller.cc
index eb89fba0..48a08012 100644
--- a/services/network/cors/preflight_controller.cc
+++ b/services/network/cors/preflight_controller.cc
@@ -90,6 +90,7 @@
 
   preflight_request->credentials_mode = mojom::CredentialsMode::kOmit;
   preflight_request->load_flags = RetrieveCacheFlags(request.load_flags);
+  preflight_request->resource_type = request.resource_type;
   preflight_request->fetch_window_id = request.fetch_window_id;
   preflight_request->render_frame_id = request.render_frame_id;
 
@@ -190,6 +191,7 @@
   PreflightLoader(PreflightController* controller,
                   CompletionCallback completion_callback,
                   const ResourceRequest& request,
+                  WithTrustedHeaderClient with_trusted_header_client,
                   bool tainted,
                   const net::NetworkTrafficAnnotationTag& annotation_tag)
       : controller_(controller),
@@ -200,6 +202,11 @@
         CreatePreflightRequest(request, tainted,
                                controller->extra_safelisted_header_names()),
         annotation_tag);
+    uint32_t options = mojom::kURLLoadOptionAsCorsPreflight;
+    if (with_trusted_header_client) {
+      options |= mojom::kURLLoadOptionUseHeaderClient;
+    }
+    loader_->SetURLLoaderFactoryOptions(options);
   }
 
   void Request(mojom::URLLoaderFactory* loader_factory) {
@@ -210,8 +217,6 @@
     loader_->SetOnResponseStartedCallback(base::BindRepeating(
         &PreflightLoader::HandleResponseHeader, base::Unretained(this)));
 
-    // TODO(yhirano): Set kURLLoadOptionAsCorsPreflight.
-    // TODO(yhirano): Set kURLLoadOptionUseHeaderClient.
     loader_->DownloadToString(
         loader_factory,
         base::BindOnce(&PreflightLoader::HandleResponseBody,
@@ -331,6 +336,7 @@
 void PreflightController::PerformPreflightCheck(
     CompletionCallback callback,
     const ResourceRequest& request,
+    WithTrustedHeaderClient with_trusted_header_client,
     bool tainted,
     const net::NetworkTrafficAnnotationTag& annotation_tag,
     mojom::URLLoaderFactory* loader_factory) {
@@ -346,7 +352,8 @@
   }
 
   auto emplaced_pair = loaders_.emplace(std::make_unique<PreflightLoader>(
-      this, std::move(callback), request, tainted, annotation_tag));
+      this, std::move(callback), request, with_trusted_header_client, tainted,
+      annotation_tag));
   (*emplaced_pair.first)->Request(loader_factory);
 }
 
diff --git a/services/network/cors/preflight_controller.h b/services/network/cors/preflight_controller.h
index 596de37..eacbefe 100644
--- a/services/network/cors/preflight_controller.h
+++ b/services/network/cors/preflight_controller.h
@@ -14,6 +14,7 @@
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "base/util/type_safety/strong_alias.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/cors/cors_error_status.h"
 #include "services/network/public/cpp/cors/preflight_cache.h"
@@ -34,6 +35,8 @@
  public:
   using CompletionCallback =
       base::OnceCallback<void(int net_error, base::Optional<CorsErrorStatus>)>;
+  using WithTrustedHeaderClient =
+      util::StrongAlias<class WithTrustedHeaderClientTag, bool>;
   // Creates a CORS-preflight ResourceRequest for a specified |request| for a
   // URL that is originally requested.
   static std::unique_ptr<ResourceRequest> CreatePreflightRequestForTesting(
@@ -57,6 +60,7 @@
   void PerformPreflightCheck(
       CompletionCallback callback,
       const ResourceRequest& resource_request,
+      WithTrustedHeaderClient with_trusted_header_client,
       bool tainted,
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
       mojom::URLLoaderFactory* loader_factory);
diff --git a/services/network/cors/preflight_controller_unittest.cc b/services/network/cors/preflight_controller_unittest.cc
index bf8ad9de..539a256 100644
--- a/services/network/cors/preflight_controller_unittest.cc
+++ b/services/network/cors/preflight_controller_unittest.cc
@@ -18,11 +18,13 @@
 #include "net/test/embedded_test_server/http_response.h"
 #include "net/test/embedded_test_server/request_handler_util.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/cors/cors_url_loader_factory.h"
 #include "services/network/network_service.h"
 #include "services/network/public/cpp/cors/cors.h"
 #include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/origin.h"
 
@@ -32,6 +34,8 @@
 
 namespace {
 
+using WithTrustedHeaderClient = PreflightController::WithTrustedHeaderClient;
+
 TEST(PreflightControllerCreatePreflightRequestTest, LexicographicalOrder) {
   ResourceRequest request;
   request.mode = mojom::RequestMode::kCors;
@@ -219,10 +223,39 @@
   EXPECT_EQ(request.render_frame_id, preflight->render_frame_id);
 }
 
+TEST(PreflightControllerOptionsTest, CheckOptions) {
+  base::test::TaskEnvironment task_environment_(
+      base::test::TaskEnvironment::MainThreadType::IO);
+  TestURLLoaderFactory url_loader_factory;
+  PreflightController preflight_controller(
+      {} /* extra_safelisted_header_names */);
+
+  network::ResourceRequest request;
+  request.url = GURL("https://example.com/");
+  request.request_initiator = url::Origin();
+  preflight_controller.PerformPreflightCheck(
+      base::BindOnce([](int, base::Optional<CorsErrorStatus>) {}), request,
+      WithTrustedHeaderClient(false), false /* tainted */,
+      TRAFFIC_ANNOTATION_FOR_TESTS, &url_loader_factory);
+
+  preflight_controller.PerformPreflightCheck(
+      base::BindOnce([](int, base::Optional<CorsErrorStatus>) {}), request,
+      WithTrustedHeaderClient(true), false /* tainted */,
+      TRAFFIC_ANNOTATION_FOR_TESTS, &url_loader_factory);
+
+  ASSERT_EQ(2, url_loader_factory.NumPending());
+  EXPECT_EQ(mojom::kURLLoadOptionAsCorsPreflight,
+            url_loader_factory.GetPendingRequest(0)->options);
+  EXPECT_EQ(mojom::kURLLoadOptionAsCorsPreflight |
+                mojom::kURLLoadOptionUseHeaderClient,
+            url_loader_factory.GetPendingRequest(1)->options);
+}
+
 class PreflightControllerTest : public testing::Test {
  public:
   PreflightControllerTest()
       : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {
+    CorsURLLoaderFactory::SetAllowExternalPreflightsForTesting(true);
     mojo::Remote<mojom::NetworkService> network_service_remote;
     network_service_ = NetworkService::Create(
         network_service_remote.BindNewPipeAndPassReceiver());
@@ -239,6 +272,9 @@
         url_loader_factory_remote_.BindNewPipeAndPassReceiver(),
         std::move(params));
   }
+  ~PreflightControllerTest() override {
+    CorsURLLoaderFactory::SetAllowExternalPreflightsForTesting(false);
+  }
 
  protected:
   void HandleRequestCompletion(int net_error,
@@ -257,8 +293,8 @@
     preflight_controller_->PerformPreflightCheck(
         base::BindOnce(&PreflightControllerTest::HandleRequestCompletion,
                        base::Unretained(this)),
-        request, tainted, TRAFFIC_ANNOTATION_FOR_TESTS,
-        url_loader_factory_remote_.get());
+        request, WithTrustedHeaderClient(false), tainted,
+        TRAFFIC_ANNOTATION_FOR_TESTS, url_loader_factory_remote_.get());
     run_loop_->Run();
   }
 
diff --git a/services/network/host_resolver.cc b/services/network/host_resolver.cc
index 86a8c91..d654ed4 100644
--- a/services/network/host_resolver.cc
+++ b/services/network/host_resolver.cc
@@ -107,10 +107,11 @@
   DCHECK(insertion_result);
 }
 
-void HostResolver::MdnsListen(const net::HostPortPair& host,
-                              net::DnsQueryType query_type,
-                              mojom::MdnsListenClientPtr response_client,
-                              MdnsListenCallback callback) {
+void HostResolver::MdnsListen(
+    const net::HostPortPair& host,
+    net::DnsQueryType query_type,
+    mojo::PendingRemote<mojom::MdnsListenClient> response_client,
+    MdnsListenCallback callback) {
 #if !BUILDFLAG(ENABLE_MDNS)
   NOTREACHED();
 #endif  // !BUILDFLAG(ENABLE_MDNS)
diff --git a/services/network/host_resolver.h b/services/network/host_resolver.h
index 02f10cd..c0d9eb28 100644
--- a/services/network/host_resolver.h
+++ b/services/network/host_resolver.h
@@ -55,7 +55,7 @@
       mojo::PendingRemote<mojom::ResolveHostClient> response_client) override;
   void MdnsListen(const net::HostPortPair& host,
                   net::DnsQueryType query_type,
-                  mojom::MdnsListenClientPtr response_client,
+                  mojo::PendingRemote<mojom::MdnsListenClient> response_client,
                   MdnsListenCallback callback) override;
 
   size_t GetNumOutstandingRequestsForTesting() const;
diff --git a/services/network/host_resolver_mdns_listener.cc b/services/network/host_resolver_mdns_listener.cc
index e856eb1..d444c6f1d 100644
--- a/services/network/host_resolver_mdns_listener.cc
+++ b/services/network/host_resolver_mdns_listener.cc
@@ -23,11 +23,12 @@
 
 HostResolverMdnsListener::~HostResolverMdnsListener() {
   internal_listener_ = nullptr;
-  response_client_ = nullptr;
+  response_client_.reset();
 }
 
-int HostResolverMdnsListener::Start(mojom::MdnsListenClientPtr response_client,
-                                    base::OnceClosure cancellation_callback) {
+int HostResolverMdnsListener::Start(
+    mojo::PendingRemote<mojom::MdnsListenClient> response_client,
+    base::OnceClosure cancellation_callback) {
   DCHECK(internal_listener_);
   DCHECK(!response_client_.is_bound());
 
@@ -35,10 +36,10 @@
   if (rv != net::OK)
     return rv;
 
-  response_client_ = std::move(response_client);
+  response_client_.Bind(std::move(response_client));
   // Unretained |this| reference is safe because connection error cannot occur
   // if |response_client_| goes out of scope.
-  response_client_.set_connection_error_handler(base::BindOnce(
+  response_client_.set_disconnect_handler(base::BindOnce(
       &HostResolverMdnsListener::OnConnectionError, base::Unretained(this)));
 
   cancellation_callback_ = std::move(cancellation_callback);
diff --git a/services/network/host_resolver_mdns_listener.h b/services/network/host_resolver_mdns_listener.h
index 7f14e25..c829a94 100644
--- a/services/network/host_resolver_mdns_listener.h
+++ b/services/network/host_resolver_mdns_listener.h
@@ -11,6 +11,8 @@
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/ip_endpoint.h"
 #include "net/dns/host_resolver.h"
 #include "net/dns/public/dns_query_type.h"
@@ -30,7 +32,7 @@
                            net::DnsQueryType query_type);
   ~HostResolverMdnsListener() override;
 
-  int Start(mojom::MdnsListenClientPtr response_client,
+  int Start(mojo::PendingRemote<mojom::MdnsListenClient> response_client,
             base::OnceClosure cancellation_callback);
 
   // net::HostResolver::MdnsListenerDelegate implementation
@@ -54,7 +56,7 @@
   void OnConnectionError();
 
   std::unique_ptr<net::HostResolver::MdnsListener> internal_listener_;
-  mojom::MdnsListenClientPtr response_client_;
+  mojo::Remote<mojom::MdnsListenClient> response_client_;
 
   base::OnceClosure cancellation_callback_;
 
diff --git a/services/network/host_resolver_unittest.cc b/services/network/host_resolver_unittest.cc
index 85a5207..6d1d69b 100644
--- a/services/network/host_resolver_unittest.cc
+++ b/services/network/host_resolver_unittest.cc
@@ -14,7 +14,6 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/test/task_environment.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -124,8 +123,9 @@
   using UpdateType = net::HostResolver::MdnsListener::Delegate::UpdateType;
   using UpdateKey = std::pair<UpdateType, net::DnsQueryType>;
 
-  explicit TestMdnsListenClient(mojom::MdnsListenClientPtr* interface_ptr)
-      : binding_(this, mojo::MakeRequest(interface_ptr)) {}
+  explicit TestMdnsListenClient(
+      mojo::PendingRemote<mojom::MdnsListenClient>* remote)
+      : receiver_(this, remote->InitWithNewPipeAndPassReceiver()) {}
 
   void OnAddressResult(UpdateType update_type,
                        net::DnsQueryType result_type,
@@ -177,7 +177,7 @@
   }
 
  private:
-  mojo::Binding<mojom::MdnsListenClient> binding_;
+  mojo::Receiver<mojom::MdnsListenClient> receiver_;
 
   std::multimap<UpdateKey, net::IPEndPoint> address_results_;
   std::multimap<UpdateKey, std::string> text_results_;
@@ -1268,7 +1268,7 @@
   auto inner_resolver = std::make_unique<net::MockHostResolver>();
   HostResolver resolver(inner_resolver.get(), &net_log);
 
-  mojom::MdnsListenClientPtr pending_response_client;
+  mojo::PendingRemote<mojom::MdnsListenClient> pending_response_client;
   TestMdnsListenClient response_client(&pending_response_client);
 
   int error = net::ERR_FAILED;
@@ -1306,7 +1306,7 @@
   auto inner_resolver = std::make_unique<net::MockHostResolver>();
   HostResolver resolver(inner_resolver.get(), &net_log);
 
-  mojom::MdnsListenClientPtr pending_response_client;
+  mojo::PendingRemote<mojom::MdnsListenClient> pending_response_client;
   TestMdnsListenClient response_client(&pending_response_client);
 
   int error = net::ERR_FAILED;
@@ -1348,7 +1348,7 @@
   auto inner_resolver = std::make_unique<net::MockHostResolver>();
   HostResolver resolver(inner_resolver.get(), &net_log);
 
-  mojom::MdnsListenClientPtr pending_response_client;
+  mojo::PendingRemote<mojom::MdnsListenClient> pending_response_client;
   TestMdnsListenClient response_client(&pending_response_client);
 
   int error = net::ERR_FAILED;
@@ -1386,7 +1386,7 @@
   auto inner_resolver = std::make_unique<net::MockHostResolver>();
   HostResolver resolver(inner_resolver.get(), &net_log);
 
-  mojom::MdnsListenClientPtr pending_response_client;
+  mojo::PendingRemote<mojom::MdnsListenClient> pending_response_client;
   TestMdnsListenClient response_client(&pending_response_client);
 
   int error = net::ERR_FAILED;
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index b69794a..fad799b 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -179,6 +179,7 @@
   // Use a fixed proxy config, to avoid dependencies on local network
   // configuration.
   params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect();
+  params->enable_cors = features::ShouldEnableOutOfBlinkCorsForTesting();
   return params;
 }
 
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 02edbed..3954b14 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -92,10 +92,13 @@
 
 std::unique_ptr<net::NetworkChangeNotifier> CreateNetworkChangeNotifierIfNeeded(
     net::NetworkChangeNotifier::ConnectionType initial_connection_type,
-    net::NetworkChangeNotifier::ConnectionSubtype initial_connection_subtype) {
+    net::NetworkChangeNotifier::ConnectionSubtype initial_connection_subtype,
+    bool mock_network_change_notifier) {
   // There is a global singleton net::NetworkChangeNotifier if NetworkService
   // is running inside of the browser process.
   if (!net::NetworkChangeNotifier::HasNetworkChangeNotifier()) {
+    if (mock_network_change_notifier)
+      return net::NetworkChangeNotifier::CreateMock();
 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
     // On Android and ChromeOS, network change events are synced from the
     // browser process.
@@ -227,7 +230,8 @@
     Initialize(mojom::NetworkServiceParams::New());
 }
 
-void NetworkService::Initialize(mojom::NetworkServiceParamsPtr params) {
+void NetworkService::Initialize(mojom::NetworkServiceParamsPtr params,
+                                bool mock_network_change_notifier) {
   if (initialized_)
     return;
 
@@ -268,7 +272,8 @@
           net::NetworkChangeNotifier::ConnectionType(
               params->initial_connection_type),
           net::NetworkChangeNotifier::ConnectionSubtype(
-              params->initial_connection_subtype)));
+              params->initial_connection_subtype),
+          mock_network_change_notifier));
 
   trace_net_log_observer_.WatchForTraceStart(net_log_);
 
diff --git a/services/network/network_service.h b/services/network/network_service.h
index 6abc2edb..1218342 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -79,7 +79,8 @@
 
   // Allows the browser process to synchronously initialize the NetworkService.
   // TODO(jam): remove this once the old path is gone.
-  void Initialize(mojom::NetworkServiceParamsPtr params);
+  void Initialize(mojom::NetworkServiceParamsPtr params,
+                  bool mock_network_change_notifier = false);
 
   // Creates a NetworkService instance on the current thread.
   static std::unique_ptr<NetworkService> Create(
diff --git a/services/network/proxy_resolving_socket_factory_mojo.cc b/services/network/proxy_resolving_socket_factory_mojo.cc
index 9c38dc1..2d673a7 100644
--- a/services/network/proxy_resolving_socket_factory_mojo.cc
+++ b/services/network/proxy_resolving_socket_factory_mojo.cc
@@ -27,7 +27,7 @@
     const GURL& url,
     mojom::ProxyResolvingSocketOptionsPtr options,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
-    mojom::ProxyResolvingSocketRequest request,
+    mojo::PendingReceiver<mojom::ProxyResolvingSocket> receiver,
     mojo::PendingRemote<mojom::SocketObserver> observer,
     CreateProxyResolvingSocketCallback callback) {
   std::unique_ptr<net::StreamSocket> net_socket =
@@ -43,8 +43,7 @@
       static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation),
       std::move(observer), &tls_socket_factory_);
   ProxyResolvingSocketMojo* socket_raw = socket.get();
-  proxy_resolving_socket_bindings_.AddBinding(std::move(socket),
-                                              std::move(request));
+  proxy_resolving_socket_receivers_.Add(std::move(socket), std::move(receiver));
   socket_raw->Connect(std::move(callback));
 }
 
diff --git a/services/network/proxy_resolving_socket_factory_mojo.h b/services/network/proxy_resolving_socket_factory_mojo.h
index 52f86ac1..b32c0824 100644
--- a/services/network/proxy_resolving_socket_factory_mojo.h
+++ b/services/network/proxy_resolving_socket_factory_mojo.h
@@ -10,8 +10,9 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/strong_binding_set.h"
+#include "mojo/public/cpp/bindings/unique_receiver_set.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/proxy_resolving_client_socket_factory.h"
 #include "services/network/public/mojom/proxy_resolving_socket.mojom.h"
@@ -34,15 +35,15 @@
       const GURL& url,
       mojom::ProxyResolvingSocketOptionsPtr options,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
-      mojom::ProxyResolvingSocketRequest request,
+      mojo::PendingReceiver<mojom::ProxyResolvingSocket> receiver,
       mojo::PendingRemote<mojom::SocketObserver> observer,
       CreateProxyResolvingSocketCallback callback) override;
 
  private:
   ProxyResolvingClientSocketFactory factory_impl_;
   TLSSocketFactory tls_socket_factory_;
-  mojo::StrongBindingSet<mojom::ProxyResolvingSocket>
-      proxy_resolving_socket_bindings_;
+  mojo::UniqueReceiverSet<mojom::ProxyResolvingSocket>
+      proxy_resolving_socket_receivers_;
 
   DISALLOW_COPY_AND_ASSIGN(ProxyResolvingSocketFactoryMojo);
 };
diff --git a/services/network/proxy_resolving_socket_mojo_unittest.cc b/services/network/proxy_resolving_socket_mojo_unittest.cc
index 3f99630a..b10b9d9 100644
--- a/services/network/proxy_resolving_socket_mojo_unittest.cc
+++ b/services/network/proxy_resolving_socket_mojo_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/task_environment.h"
 #include "jingle/glue/fake_ssl_client_socket.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "net/base/net_errors.h"
@@ -108,7 +109,7 @@
   }
 
   int CreateSocketSync(
-      mojom::ProxyResolvingSocketRequest request,
+      mojo::PendingReceiver<mojom::ProxyResolvingSocket> receiver,
       mojo::PendingRemote<mojom::SocketObserver> socket_observer,
       net::IPEndPoint* peer_addr_out,
       const GURL& url,
@@ -123,7 +124,7 @@
     factory_ptr_->CreateProxyResolvingSocket(
         url, std::move(options),
         net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
-        std::move(request), std::move(socket_observer),
+        std::move(receiver), std::move(socket_observer),
         base::BindLambdaForTesting(
             [&](int result, const base::Optional<net::IPEndPoint>& local_addr,
                 const base::Optional<net::IPEndPoint>& peer_addr,
@@ -210,11 +211,11 @@
     socket_data.set_connect_data(
         net::MockConnect(net::ASYNC, net::OK, remote_addr));
     mock_client_socket_factory()->AddSocketDataProvider(&socket_data);
-    mojom::ProxyResolvingSocketPtr socket;
+    mojo::PendingRemote<mojom::ProxyResolvingSocket> socket;
     mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
     mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
     net::IPEndPoint actual_remote_addr;
-    EXPECT_EQ(net::OK, CreateSocketSync(mojo::MakeRequest(&socket),
+    EXPECT_EQ(net::OK, CreateSocketSync(socket.InitWithNewPipeAndPassReceiver(),
                                         mojo::NullRemote() /* socket_observer*/,
                                         &actual_remote_addr, kDestination,
                                         &client_socket_receive_handle,
@@ -257,13 +258,14 @@
         test.is_error_sync ? net::SYNCHRONOUS : net::ASYNC, net::ERR_FAILED));
     mock_client_socket_factory()->AddSocketDataProvider(&socket_data);
 
-    mojom::ProxyResolvingSocketPtr socket;
+    mojo::PendingRemote<mojom::ProxyResolvingSocket> socket;
     mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
     mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
-    int status = CreateSocketSync(
-        mojo::MakeRequest(&socket), mojo::NullRemote() /* socket_observer*/,
-        nullptr /* peer_addr_out */, kDestination,
-        &client_socket_receive_handle, &client_socket_send_handle);
+    int status = CreateSocketSync(socket.InitWithNewPipeAndPassReceiver(),
+                                  mojo::NullRemote() /* socket_observer*/,
+                                  nullptr /* peer_addr_out */, kDestination,
+                                  &client_socket_receive_handle,
+                                  &client_socket_send_handle);
     if (test.is_direct) {
       EXPECT_EQ(net::ERR_FAILED, status);
     } else {
@@ -277,7 +279,7 @@
 // Tests writing to and reading from a mojom::ProxyResolvingSocket.
 TEST_P(ProxyResolvingSocketTest, BasicReadWrite) {
   Init("DIRECT");
-  mojom::ProxyResolvingSocketPtr socket;
+  mojo::PendingRemote<mojom::ProxyResolvingSocket> socket;
   const char kTestMsg[] = "abcdefghij";
   const size_t kMsgSize = strlen(kTestMsg);
   const int kNumIterations = 3;
@@ -305,7 +307,7 @@
   mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
   mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
   const GURL kDestination("http://example.com");
-  EXPECT_EQ(net::OK, CreateSocketSync(mojo::MakeRequest(&socket),
+  EXPECT_EQ(net::OK, CreateSocketSync(socket.InitWithNewPipeAndPassReceiver(),
                                       mojo::NullRemote() /* socket_observer */,
                                       nullptr /* peer_addr_out */, kDestination,
                                       &client_socket_receive_handle,
@@ -366,11 +368,11 @@
   data_provider.set_connect_data(net::MockConnect(net::ASYNC, net::OK));
   mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
 
-  mojom::ProxyResolvingSocketPtr socket;
+  mojo::PendingRemote<mojom::ProxyResolvingSocket> socket;
   mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
   mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
   net::IPEndPoint actual_remote_addr;
-  EXPECT_EQ(net::OK, CreateSocketSync(mojo::MakeRequest(&socket),
+  EXPECT_EQ(net::OK, CreateSocketSync(socket.InitWithNewPipeAndPassReceiver(),
                                       mojo::NullRemote() /* socket_observer*/,
                                       &actual_remote_addr, kDestination,
                                       &client_socket_receive_handle,
@@ -393,13 +395,14 @@
   data_provider.set_connect_data(net::MockConnect(net::ASYNC, net::OK));
   mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
   const GURL kDestination("http://example.com");
-  mojom::ProxyResolvingSocketPtr socket;
+  mojo::PendingRemote<mojom::ProxyResolvingSocket> socket;
   base::RunLoop run_loop;
   int net_error = net::OK;
   factory()->CreateProxyResolvingSocket(
       kDestination, nullptr,
       net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
-      mojo::MakeRequest(&socket), mojo::NullRemote() /* observer */,
+      socket.InitWithNewPipeAndPassReceiver(),
+      mojo::NullRemote() /* observer */,
       base::BindLambdaForTesting(
           [&](int result, const base::Optional<net::IPEndPoint>& local_addr,
               const base::Optional<net::IPEndPoint>& peer_addr,
@@ -430,15 +433,15 @@
 
   const GURL kDestination("http://example.com");
 
-  mojom::ProxyResolvingSocketPtr socket;
+  mojo::PendingRemote<mojom::ProxyResolvingSocket> socket;
   mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
   mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
   TestSocketObserver test_observer;
 
   int status = CreateSocketSync(
-      mojo::MakeRequest(&socket), test_observer.GetObserverRemote(),
-      nullptr /* peer_addr_out */, kDestination, &client_socket_receive_handle,
-      &client_socket_send_handle);
+      socket.InitWithNewPipeAndPassReceiver(),
+      test_observer.GetObserverRemote(), nullptr /* peer_addr_out */,
+      kDestination, &client_socket_receive_handle, &client_socket_send_handle);
   EXPECT_EQ(net::OK, status);
 
   EXPECT_EQ(kMsg, Read(&client_socket_receive_handle, kMsgLen));
diff --git a/services/network/public/cpp/cors/cors.cc b/services/network/public/cpp/cors/cors.cc
index 944cf9e..16ac81c 100644
--- a/services/network/public/cpp/cors/cors.cc
+++ b/services/network/public/cpp/cors/cors.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_util.h"
 #include "net/base/mime_util.h"
 #include "net/http/http_request_headers.h"
+#include "net/http/http_util.h"
 #include "services/network/public/cpp/request_mode.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -510,7 +511,7 @@
   size_t safe_list_value_size = 0;
 
   for (const auto& header : headers) {
-    if (IsForbiddenHeader(header.key))
+    if (!net::HttpUtil::IsSafeHeader(header.key))
       continue;
 
     const std::string name = base::ToLowerASCII(header.key);
@@ -542,47 +543,6 @@
          lower_method == "connect";
 }
 
-bool IsForbiddenHeader(const std::string& name) {
-  // http://fetch.spec.whatwg.org/#forbidden-header-name
-  // "A forbidden header name is a header name that is one of:
-  //   `Accept-Charset`, `Accept-Encoding`, `Access-Control-Request-Headers`,
-  //   `Access-Control-Request-Method`, `Connection`, `Content-Length`,
-  //   `Cookie`, `Cookie2`, `Date`, `DNT`, `Expect`, `Host`, `Keep-Alive`,
-  //   `Origin`, `Referer`, `TE`, `Trailer`, `Transfer-Encoding`, `Upgrade`,
-  //   `User-Agent`, `Via`
-  // or starts with `Proxy-` or `Sec-` (including when it is just `Proxy-` or
-  // `Sec-`)."
-  static const base::NoDestructor<base::flat_set<base::StringPiece>>
-      kForbiddenNames(
-          base::flat_set<base::StringPiece>{"accept-charset",
-                                            "accept-encoding",
-                                            "access-control-request-headers",
-                                            "access-control-request-method",
-                                            "connection",
-                                            "content-length",
-                                            "cookie",
-                                            "cookie2",
-                                            "date",
-                                            "dnt",
-                                            "expect",
-                                            "host",
-                                            "keep-alive",
-                                            "origin",
-                                            "referer",
-                                            "te",
-                                            "trailer",
-                                            "transfer-encoding",
-                                            "upgrade",
-                                            "user-agent",
-                                            "via"});
-  const std::string lower_name = base::ToLowerASCII(name);
-  if (StartsWith(lower_name, "proxy-", base::CompareCase::SENSITIVE) ||
-      StartsWith(lower_name, "sec-", base::CompareCase::SENSITIVE)) {
-    return true;
-  }
-  return kForbiddenNames->contains(lower_name);
-}
-
 bool IsOkStatus(int status) {
   return status >= 200 && status < 300;
 }
diff --git a/services/network/public/cpp/cors/cors.h b/services/network/public/cpp/cors/cors.h
index 73a4817..c76913b 100644
--- a/services/network/public/cpp/cors/cors.h
+++ b/services/network/public/cpp/cors/cors.h
@@ -153,10 +153,6 @@
 // this implementation internally.
 COMPONENT_EXPORT(NETWORK_CPP) bool IsForbiddenMethod(const std::string& name);
 
-// Checks forbidden header in the fetch spec.
-// See https://fetch.spec.whatwg.org/#forbidden-header-name.
-COMPONENT_EXPORT(NETWORK_CPP) bool IsForbiddenHeader(const std::string& name);
-
 // https://fetch.spec.whatwg.org/#ok-status aka a successful 2xx status code,
 // https://tools.ietf.org/html/rfc7231#section-6.3 . We opt to use the Fetch
 // term in naming the predicate.
diff --git a/services/network/public/cpp/simple_url_loader.cc b/services/network/public/cpp/simple_url_loader.cc
index 10c12d97..1cc4198 100644
--- a/services/network/public/cpp/simple_url_loader.cc
+++ b/services/network/public/cpp/simple_url_loader.cc
@@ -228,6 +228,7 @@
       uint64_t offset = 0,
       uint64_t length = std::numeric_limits<uint64_t>::max()) override;
   void SetRetryOptions(int max_retries, int retry_mode) override;
+  void SetURLLoaderFactoryOptions(uint32_t options) override;
   void SetTimeoutDuration(base::TimeDelta timeout_duration) override;
 
   int NetError() const override;
@@ -342,6 +343,7 @@
   // Information related to retrying.
   int remaining_retries_ = 0;
   int retry_mode_ = RETRY_NEVER;
+  uint32_t url_loader_factory_options_ = 0;
 
   // The next values contain all the information required to restart the
   // request.
@@ -1359,6 +1361,12 @@
 #endif  // DCHECK_IS_ON()
 }
 
+void SimpleURLLoaderImpl::SetURLLoaderFactoryOptions(uint32_t options) {
+  // Check if a request has not yet been started.
+  DCHECK(!body_handler_);
+  url_loader_factory_options_ = options;
+}
+
 void SimpleURLLoaderImpl::SetTimeoutDuration(base::TimeDelta timeout_duration) {
   DCHECK(!request_state_->body_started);
   DCHECK(timeout_duration >= base::TimeDelta());
@@ -1514,7 +1522,7 @@
   }
   url_loader_factory->CreateLoaderAndStart(
       mojo::MakeRequest(&url_loader_), 0 /* routing_id */, 0 /* request_id */,
-      0 /* options */, *resource_request_, std::move(client_ptr),
+      url_loader_factory_options_, *resource_request_, std::move(client_ptr),
       net::MutableNetworkTrafficAnnotationTag(annotation_tag_));
 
   // Note that this ends up restarting the timer on each retry.
diff --git a/services/network/public/cpp/simple_url_loader.h b/services/network/public/cpp/simple_url_loader.h
index 918b70a..88d602b0 100644
--- a/services/network/public/cpp/simple_url_loader.h
+++ b/services/network/public/cpp/simple_url_loader.h
@@ -322,6 +322,11 @@
   // was added to the ResourceRequest passed to Create() by the consumer.
   virtual void SetRetryOptions(int max_retries, int retry_mode) = 0;
 
+  // Sets options for URLLoaderFactory::CreateLoaderAndStart. See
+  // //network/public/mojom/url_loader_factory.mojom. This should be
+  // called before the request is started.
+  virtual void SetURLLoaderFactoryOptions(uint32_t options) = 0;
+
   // The amount of time to wait before giving up on a given network request and
   // considering it an error. If not set, then the request is allowed to take
   // as much time as it wants.
diff --git a/services/network/public/mojom/host_resolver.mojom b/services/network/public/mojom/host_resolver.mojom
index 8038f83..c7d4812 100644
--- a/services/network/public/mojom/host_resolver.mojom
+++ b/services/network/public/mojom/host_resolver.mojom
@@ -287,7 +287,8 @@
   // notifications if the HostResolver or parent NetworkContext are destroyed.
   MdnsListen(HostPortPair host,
              DnsQueryType query_type,
-             MdnsListenClient response_client) => (int32 result);
+             pending_remote<MdnsListenClient> response_client)
+      => (int32 result);
 };
 
 // A client interface that subscribes to DNS config change events from
diff --git a/services/network/public/mojom/proxy_resolving_socket.mojom b/services/network/public/mojom/proxy_resolving_socket.mojom
index 2e1fedb1..2fb1a98 100644
--- a/services/network/public/mojom/proxy_resolving_socket.mojom
+++ b/services/network/public/mojom/proxy_resolving_socket.mojom
@@ -67,7 +67,7 @@
   CreateProxyResolvingSocket(url.mojom.Url url,
       ProxyResolvingSocketOptions? options,
       MutableNetworkTrafficAnnotationTag traffic_annotation,
-      ProxyResolvingSocket& socket,
+      pending_receiver<ProxyResolvingSocket> socket,
       pending_remote<SocketObserver>? observer)
      => (int32 result,
          network.mojom.IPEndPoint? local_addr,
diff --git a/services/network/tls_client_socket_unittest.cc b/services/network/tls_client_socket_unittest.cc
index 512811e2..a81fe69 100644
--- a/services/network/tls_client_socket_unittest.cc
+++ b/services/network/tls_client_socket_unittest.cc
@@ -16,7 +16,6 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/net_errors.h"
@@ -62,12 +61,12 @@
   // One of the two fields will be set, depending on the mode.
   struct SocketHandle {
     mojo::Remote<mojom::TCPConnectedSocket> tcp_socket;
-    mojom::ProxyResolvingSocketPtr proxy_socket;
+    mojo::Remote<mojom::ProxyResolvingSocket> proxy_socket;
   };
 
   struct SocketRequest {
     mojo::PendingReceiver<mojom::TCPConnectedSocket> tcp_socket_receiver;
-    mojom::ProxyResolvingSocketRequest proxy_socket_request;
+    mojo::PendingReceiver<mojom::ProxyResolvingSocket> proxy_socket_receiver;
   };
 
   // Initializes the test fixture. If |use_mock_sockets|, mock client socket
@@ -118,7 +117,8 @@
       result.tcp_socket_receiver =
           handle->tcp_socket.BindNewPipeAndPassReceiver();
     else
-      result.proxy_socket_request = mojo::MakeRequest(&handle->proxy_socket);
+      result.proxy_socket_receiver =
+          handle->proxy_socket.BindNewPipeAndPassReceiver();
     return result;
   }
 
@@ -136,7 +136,7 @@
           std::move(request.tcp_socket_receiver), remote_addr);
     } else {
       return CreateProxyResolvingSocketSync(
-          std::move(request.proxy_socket_request), remote_addr);
+          std::move(request.proxy_socket_receiver), remote_addr);
     }
   }
 
@@ -166,15 +166,16 @@
     return net_error;
   }
 
-  int CreateProxyResolvingSocketSync(mojom::ProxyResolvingSocketRequest request,
-                                     const net::IPEndPoint& remote_addr) {
+  int CreateProxyResolvingSocketSync(
+      mojo::PendingReceiver<mojom::ProxyResolvingSocket> receiver,
+      const net::IPEndPoint& remote_addr) {
     GURL url("https://" + remote_addr.ToString());
     base::RunLoop run_loop;
     int net_error = net::ERR_FAILED;
     proxy_resolving_factory_->CreateProxyResolvingSocket(
         url, nullptr /* options */,
         net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
-        std::move(request), mojo::NullRemote() /* observer */,
+        std::move(receiver), mojo::NullRemote() /* observer */,
         base::BindLambdaForTesting(
             [&](int result,
                 const base::Optional<net::IPEndPoint>& actual_local_addr,
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index f02d359..a2501a6c 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -477,6 +477,20 @@
   if (keepalive_ && keepalive_statistics_recorder_)
     keepalive_statistics_recorder_->OnLoadStarted(factory_params_->process_id);
 
+  if (keepalive_) {
+    const size_t url_size = request.url.spec().size();
+    size_t headers_size = 0;
+    for (const auto& pair : merged_headers.GetHeaderVector()) {
+      headers_size += (pair.key.size() + pair.value.size());
+    }
+
+    UMA_HISTOGRAM_COUNTS_10000("Net.KeepaliveRequest.UrlSize", url_size);
+    UMA_HISTOGRAM_COUNTS_10000("Net.KeepaliveRequest.HeadersSize",
+                               headers_size);
+    UMA_HISTOGRAM_COUNTS_10000("Net.KeepaliveRequest.UrlPlusHeadersSize",
+                               url_size + headers_size);
+  }
+
   // Resolve elements from request_body and prepare upload data.
   if (request.request_body.get()) {
     OpenFilesForUpload(request);
diff --git a/services/service_manager/public/cpp/BUILD.gn b/services/service_manager/public/cpp/BUILD.gn
index 0c59834..344e326 100644
--- a/services/service_manager/public/cpp/BUILD.gn
+++ b/services/service_manager/public/cpp/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//mojo/public/tools/bindings/mojom.gni")
+
 component("cpp") {
   output_name = "service_manager_cpp"
 
@@ -30,6 +32,8 @@
     "service_context_ref.h",
     "service_keepalive.cc",
     "service_keepalive.h",
+    "standalone_connector_impl.cc",
+    "standalone_connector_impl.h",
   ]
 
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
@@ -111,13 +115,23 @@
 
   sources = [
     "manifest_unittest.cc",
+    "standalone_connector_impl_unittest.cc",
   ]
 
   deps = [
     ":cpp",
+    ":test_mojom",
     "//base",
+    "//base/test:test_support",
     "//services/service_manager/public/mojom",
     "//testing/gmock",
     "//testing/gtest",
   ]
 }
+
+mojom("test_mojom") {
+  testonly = true
+  sources = [
+    "standalone_connector_impl_unittest.test-mojom",
+  ]
+}
diff --git a/services/service_manager/public/cpp/standalone_connector_impl.cc b/services/service_manager/public/cpp/standalone_connector_impl.cc
new file mode 100644
index 0000000..fc91298
--- /dev/null
+++ b/services/service_manager/public/cpp/standalone_connector_impl.cc
@@ -0,0 +1,74 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/service_manager/public/cpp/standalone_connector_impl.h"
+
+#include "base/logging.h"
+
+namespace service_manager {
+
+StandaloneConnectorImpl::StandaloneConnectorImpl(Delegate* delegate)
+    : delegate_(delegate) {
+  DCHECK(delegate_);
+}
+
+StandaloneConnectorImpl::~StandaloneConnectorImpl() = default;
+
+mojo::PendingRemote<mojom::Connector> StandaloneConnectorImpl::MakeRemote() {
+  mojo::PendingRemote<mojom::Connector> remote;
+  receivers_.Add(this, remote.InitWithNewPipeAndPassReceiver());
+  return remote;
+}
+
+void StandaloneConnectorImpl::BindInterface(
+    const ServiceFilter& filter,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe,
+    mojom::BindInterfacePriority priority,
+    BindInterfaceCallback callback) {
+  delegate_->OnConnect(
+      filter.service_name(),
+      mojo::GenericPendingReceiver(interface_name, std::move(interface_pipe)));
+  std::move(callback).Run(mojom::ConnectResult::SUCCEEDED, base::nullopt);
+}
+
+void StandaloneConnectorImpl::QueryService(const std::string& service_name,
+                                           QueryServiceCallback callback) {
+  NOTIMPLEMENTED()
+      << "QueryService is not supported by StandaloneConnectorImpl.";
+  std::move(callback).Run(nullptr);
+}
+
+void StandaloneConnectorImpl::WarmService(const ServiceFilter& filter,
+                                          WarmServiceCallback callback) {
+  NOTIMPLEMENTED()
+      << "WarmService is not supported by StandaloneConnectorImpl.";
+  std::move(callback).Run(mojom::ConnectResult::INVALID_ARGUMENT,
+                          base::nullopt);
+}
+
+void StandaloneConnectorImpl::RegisterServiceInstance(
+    const Identity& identity,
+    mojo::ScopedMessagePipeHandle service_pipe,
+    mojo::PendingReceiver<mojom::ProcessMetadata> metadata_receiver,
+    RegisterServiceInstanceCallback callback) {
+  NOTIMPLEMENTED()
+      << "RegisterServiceInstance is not supported by StandaloneConnectorImpl.";
+  std::move(callback).Run(mojom::ConnectResult::INVALID_ARGUMENT);
+}
+
+void StandaloneConnectorImpl::Clone(mojom::ConnectorRequest request) {
+  receivers_.Add(this, std::move(request));
+}
+
+void StandaloneConnectorImpl::FilterInterfaces(
+    const std::string& spec,
+    const Identity& source,
+    mojom::InterfaceProviderRequest source_request,
+    mojom::InterfaceProviderPtr target) {
+  NOTIMPLEMENTED()
+      << "FilterInterfaces is not supported by StandaloneConnectorImpl.";
+}
+
+}  // namespace service_manager
diff --git a/services/service_manager/public/cpp/standalone_connector_impl.h b/services/service_manager/public/cpp/standalone_connector_impl.h
new file mode 100644
index 0000000..c33b0ab
--- /dev/null
+++ b/services/service_manager/public/cpp/standalone_connector_impl.h
@@ -0,0 +1,82 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_SERVICE_MANAGER_PUBLIC_CPP_STANDALONE_CONNECTOR_IMPL_H_
+#define SERVICES_SERVICE_MANAGER_PUBLIC_CPP_STANDALONE_CONNECTOR_IMPL_H_
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "services/service_manager/public/mojom/connector.mojom.h"
+
+namespace service_manager {
+
+// StandaloneConnectorImpl is a helper class which can be used to provide a
+// backend for |Connector| objects to route requests somewhere other than the
+// Service Manager.
+//
+// This exists to aid in transitioning code away from Service Manager APIs.
+// Typically an instance of this class would live in the browser process, with a
+// Delegate implementation that knows how to bind any interfaces that its
+// clients might request.
+class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) StandaloneConnectorImpl
+    : private mojom::Connector {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+
+    // Invoked whenever a client asks to have an interface bound by the service
+    // named |service_name|. The interface endpoint to bind is contained in
+    // |receiver|.
+    virtual void OnConnect(const std::string& service_name,
+                           mojo::GenericPendingReceiver receiver) = 0;
+  };
+
+  explicit StandaloneConnectorImpl(Delegate* delegate);
+  ~StandaloneConnectorImpl() override;
+
+  // Produces a new remote Connector endpoint whose connection requests are
+  // routed to this object's Delegate. The returned PendingRemote can be passed
+  // around in messages, and it can eventually be used to construct a new
+  // concrete Connector object.
+  //
+  // Note that Connectors bound to this remote or clones of it will only support
+  // the basic operations of |BindInterface/Connect()|, and |Clone()|.
+  mojo::PendingRemote<mojom::Connector> MakeRemote();
+
+ private:
+  // mojom::Connector implementation:
+  void BindInterface(const ServiceFilter& filter,
+                     const std::string& interface_name,
+                     mojo::ScopedMessagePipeHandle interface_pipe,
+                     mojom::BindInterfacePriority priority,
+                     BindInterfaceCallback callback) override;
+  void QueryService(const std::string& service_name,
+                    QueryServiceCallback callback) override;
+  void WarmService(const ServiceFilter& filter,
+                   WarmServiceCallback callback) override;
+  void RegisterServiceInstance(
+      const Identity& identity,
+      mojo::ScopedMessagePipeHandle service_pipe,
+      mojo::PendingReceiver<mojom::ProcessMetadata> metadata_receiver,
+      RegisterServiceInstanceCallback callback) override;
+  void Clone(mojom::ConnectorRequest request) override;
+  void FilterInterfaces(const std::string& spec,
+                        const Identity& source,
+                        mojom::InterfaceProviderRequest source_request,
+                        mojom::InterfaceProviderPtr target) override;
+
+  Delegate* const delegate_;
+
+  mojo::ReceiverSet<mojom::Connector> receivers_;
+
+  DISALLOW_COPY_AND_ASSIGN(StandaloneConnectorImpl);
+};
+
+}  // namespace service_manager
+
+#endif  // SERVICES_SERVICE_MANAGER_PUBLIC_CPP_STANDALONE_CONNECTOR_IMPL_H_
diff --git a/services/service_manager/public/cpp/standalone_connector_impl_unittest.cc b/services/service_manager/public/cpp/standalone_connector_impl_unittest.cc
new file mode 100644
index 0000000..64c4191
--- /dev/null
+++ b/services/service_manager/public/cpp/standalone_connector_impl_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/service_manager/public/cpp/standalone_connector_impl.h"
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
+#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/standalone_connector_impl_unittest.test-mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace service_manager {
+namespace standalone_connector_impl_unittest {
+
+class TestConnectorDelegate : public StandaloneConnectorImpl::Delegate {
+ public:
+  template <typename Handler>
+  TestConnectorDelegate(Handler handler)
+      : TestConnectorDelegate(base::BindLambdaForTesting(handler)) {}
+
+  ~TestConnectorDelegate() override = default;
+
+ private:
+  using Callback = base::RepeatingCallback<void(const std::string&,
+                                                mojo::GenericPendingReceiver)>;
+
+  explicit TestConnectorDelegate(Callback callback)
+      : callback_(std::move(callback)) {}
+
+  // StandaloneConnectorImpl::Delegate implementation:
+  void OnConnect(const std::string& service_name,
+                 mojo::GenericPendingReceiver receiver) override {
+    callback_.Run(service_name, std::move(receiver));
+  }
+
+  const Callback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestConnectorDelegate);
+};
+
+class StandaloneConnectorImplTest : public testing::Test {
+ public:
+  StandaloneConnectorImplTest() = default;
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+};
+
+TEST_F(StandaloneConnectorImplTest, Connect) {
+  const std::string kFooServiceName = "foo_service";
+  const std::string kBarServiceName = "bar_service";
+
+  int requests_processed = 0;
+  base::Optional<base::RunLoop> loop;
+  TestConnectorDelegate delegate([&](const std::string& service_name,
+                                     mojo::GenericPendingReceiver receiver) {
+    ASSERT_TRUE(receiver);
+    if (service_name == kFooServiceName) {
+      EXPECT_EQ(mojom::Foo::Name_, *receiver.interface_name());
+    } else {
+      EXPECT_EQ(kBarServiceName, service_name);
+      EXPECT_EQ(mojom::Bar::Name_, *receiver.interface_name());
+    }
+
+    ++requests_processed;
+    loop->Quit();
+  });
+
+  StandaloneConnectorImpl impl(&delegate);
+  Connector connector(impl.MakeRemote());
+
+  mojo::Remote<mojom::Foo> foo;
+  connector.Connect(kFooServiceName, foo.BindNewPipeAndPassReceiver());
+  loop.emplace();
+  loop->Run();
+  EXPECT_EQ(1, requests_processed);
+
+  mojo::Remote<mojom::Bar> bar;
+  connector.Connect(kBarServiceName, bar.BindNewPipeAndPassReceiver());
+  loop.emplace();
+  loop->Run();
+  EXPECT_EQ(2, requests_processed);
+}
+
+TEST_F(StandaloneConnectorImplTest, Clone) {
+  const std::string kFooServiceName = "foo_service";
+  base::RunLoop loop;
+  TestConnectorDelegate delegate([&](const std::string& service_name,
+                                     mojo::GenericPendingReceiver receiver) {
+    ASSERT_TRUE(receiver);
+    EXPECT_EQ(kFooServiceName, service_name);
+    EXPECT_EQ(mojom::Foo::Name_, *receiver.interface_name());
+    loop.Quit();
+  });
+  StandaloneConnectorImpl impl(&delegate);
+  Connector connector(impl.MakeRemote());
+
+  auto clone = connector.Clone();
+  mojo::Remote<mojom::Foo> foo;
+  clone->Connect(kFooServiceName, foo.BindNewPipeAndPassReceiver());
+  loop.Run();
+}
+
+}  // namespace standalone_connector_impl_unittest
+}  // namespace service_manager
diff --git a/services/service_manager/public/cpp/standalone_connector_impl_unittest.test-mojom b/services/service_manager/public/cpp/standalone_connector_impl_unittest.test-mojom
new file mode 100644
index 0000000..89bb0d4
--- /dev/null
+++ b/services/service_manager/public/cpp/standalone_connector_impl_unittest.test-mojom
@@ -0,0 +1,9 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module service_manager.standalone_connector_impl_unittest.mojom;
+
+interface Foo {};
+
+interface Bar {};
diff --git a/services/tracing/perfetto/consumer_host.cc b/services/tracing/perfetto/consumer_host.cc
index 0257b39..d16f584 100644
--- a/services/tracing/perfetto/consumer_host.cc
+++ b/services/tracing/perfetto/consumer_host.cc
@@ -196,8 +196,7 @@
 
   for (const auto& state_change : events.instance_state_changes()) {
     if (state_change.state() !=
-        perfetto::ObservableEvents::DataSourceInstanceStateChange::
-            DATA_SOURCE_INSTANCE_STATE_STARTED) {
+        perfetto::ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STARTED) {
       continue;
     }
 
diff --git a/services/tracing/perfetto/perfetto_tracing_coordinator.cc b/services/tracing/perfetto/perfetto_tracing_coordinator.cc
index 3c658c3..2525407d 100644
--- a/services/tracing/perfetto/perfetto_tracing_coordinator.cc
+++ b/services/tracing/perfetto/perfetto_tracing_coordinator.cc
@@ -198,8 +198,7 @@
   void OnObservableEvents(const perfetto::ObservableEvents& events) override {
     for (const auto& state_change : events.instance_state_changes()) {
       if (state_change.state() !=
-          perfetto::ObservableEvents::DataSourceInstanceStateChange::
-              DATA_SOURCE_INSTANCE_STATE_STARTED) {
+          perfetto::ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STARTED) {
         continue;
       }
 
diff --git a/services/tracing/perfetto/test_utils.cc b/services/tracing/perfetto/test_utils.cc
index 8ad1e60..49bb350 100644
--- a/services/tracing/perfetto/test_utils.cc
+++ b/services/tracing/perfetto/test_utils.cc
@@ -189,8 +189,7 @@
   for (const auto& source : data_source_names) {
     data_sources_.emplace_back(DataSourceStatus{
         source,
-        perfetto::ObservableEvents::DataSourceInstanceStateChange::
-            DataSourceInstanceState::DATA_SOURCE_INSTANCE_STATE_STOPPED});
+        perfetto::ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STOPPED});
   }
   CHECK(!data_sources_.empty());
   consumer_endpoint_ = service->ConnectConsumer(this, /*uid=*/0);
@@ -282,8 +281,7 @@
 void MockConsumer::CheckForAllDataSourcesStarted() {
   for (auto& data_source_status : data_sources_) {
     if (data_source_status.state !=
-        perfetto::ObservableEvents::DataSourceInstanceStateChange::
-            DATA_SOURCE_INSTANCE_STATE_STARTED) {
+        perfetto::ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STARTED) {
       return;
     }
   }
@@ -296,8 +294,7 @@
 void MockConsumer::CheckForAllDataSourcesStopped() {
   for (auto& data_source_status : data_sources_) {
     if (data_source_status.state !=
-        perfetto::ObservableEvents::DataSourceInstanceStateChange::
-            DATA_SOURCE_INSTANCE_STATE_STOPPED) {
+        perfetto::ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STOPPED) {
       return;
     }
   }
diff --git a/services/tracing/perfetto/test_utils.h b/services/tracing/perfetto/test_utils.h
index 89e0f1b..f8e0887b 100644
--- a/services/tracing/perfetto/test_utils.h
+++ b/services/tracing/perfetto/test_utils.h
@@ -140,8 +140,7 @@
  private:
   struct DataSourceStatus {
     std::string name;
-    perfetto::ObservableEvents::DataSourceInstanceStateChange::
-        DataSourceInstanceState state;
+    perfetto::ObservableEvents::DataSourceInstanceState state;
   };
 
   void CheckForAllDataSourcesStarted();
diff --git a/services/tracing/public/cpp/perfetto/perfetto_config.cc b/services/tracing/public/cpp/perfetto/perfetto_config.cc
index 8bbfa038..7be9141d 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_config.cc
+++ b/services/tracing/public/cpp/perfetto/perfetto_config.cc
@@ -50,11 +50,11 @@
     case base::trace_event::RECORD_UNTIL_FULL:
     case base::trace_event::RECORD_AS_MUCH_AS_POSSIBLE:
       buffer_config->set_fill_policy(
-          perfetto::TraceConfig::BufferConfig::FillPolicy::DISCARD);
+          perfetto::TraceConfig::BufferConfig::DISCARD);
       break;
     case base::trace_event::RECORD_CONTINUOUSLY:
       buffer_config->set_fill_policy(
-          perfetto::TraceConfig::BufferConfig::FillPolicy::RING_BUFFER);
+          perfetto::TraceConfig::BufferConfig::RING_BUFFER);
       break;
     case base::trace_event::ECHO_TO_CONSOLE:
       NOTREACHED();
diff --git a/services/video_capture/OWNERS b/services/video_capture/OWNERS
index da0857b..7e7e66b 100644
--- a/services/video_capture/OWNERS
+++ b/services/video_capture/OWNERS
@@ -1,6 +1,7 @@
-chfremer@chromium.org
+guidou@chromium.org
 
-# Original (legacy) owner.
+# Original (legacy) owners.
+chfremer@chromium.org
 per-file *video*=mcasas@chromium.org
 
 # COMPONENT: Blink>GetUserMedia
diff --git a/services/viz/privileged/mojom/gl/gpu_service.mojom b/services/viz/privileged/mojom/gl/gpu_service.mojom
index f5b73dc..c4da750 100644
--- a/services/viz/privileged/mojom/gl/gpu_service.mojom
+++ b/services/viz/privileged/mojom/gl/gpu_service.mojom
@@ -87,6 +87,15 @@
 
   GetVideoMemoryUsageStats() => (gpu.mojom.VideoMemoryUsageStats stats);
 
+  // Starts tracking the peak GPU memory until GetPeakMemoryUsage is called.
+  // To be associated with |sequence_num|, repeated calls with the same value
+  // are ignored.
+  StartPeakMemoryMonitor(uint32 sequence_num);
+
+  // Ends tracking the peak GPU memory for the associated |sequence_num|.
+  // Returning the value of the peak seen since StartPeakMemoryMonitorProcess.
+  GetPeakMemoryUsage(uint32 sequence_num) => (uint64 memory_usage);
+
   [EnableIf=is_win]
   RequestCompleteGpuInfo() => (gpu.mojom.DxDiagNode dx_diagnostics);
   [EnableIf=is_win]
diff --git a/services/viz/privileged/mojom/viz_main.mojom b/services/viz/privileged/mojom/viz_main.mojom
index dfcfe79..6e59aed 100644
--- a/services/viz/privileged/mojom/viz_main.mojom
+++ b/services/viz/privileged/mojom/viz_main.mojom
@@ -24,10 +24,10 @@
   uint32 activation_deadline_in_frames = 4;
 
   // Host to viz interface.
-  FrameSinkManager& frame_sink_manager;
+  pending_receiver<FrameSinkManager> frame_sink_manager;
 
   // Viz to host interface.
-  FrameSinkManagerClient frame_sink_manager_client;
+  pending_remote<FrameSinkManagerClient> frame_sink_manager_client;
 };
 
 struct VizDevToolsParams {
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index d835528d..502c3361 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -20692,6 +20692,42 @@
           ]
         },
         "test": "monochrome_public_smoke_test"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--additional-apk=//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "monochrome_public_test_ar_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_os": "NMF26U",
+              "device_os_type": "userdebug",
+              "device_type": "marlin",
+              "os": "Android"
+            }
+          ]
+        },
+        "test": "monochrome_public_test_ar_apk"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index c3586656..ec6e3de 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -11168,1904 +11168,6 @@
       }
     ]
   },
-  "Mac FYI 10.14 Release (AMD)": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "shards": 4
-        },
-        "test": "angle_end2end_tests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "args": [
-          "--enable-gpu",
-          "--test-launcher-bot-mode",
-          "--test-launcher-jobs=1",
-          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "tab_capture_end2end_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--use-cmd-decoder=validating"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gl_tests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gl_unittests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gles2_conform_test"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=*Detection*",
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "services_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "swiftshader_unittests"
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "context_lost_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "depth_capture",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "depth_capture_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gpu_process_launch_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "hardware_accelerated_feature_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--expected-vendor-id",
-          "1002",
-          "--expected-device-id",
-          "6821"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "info_collection_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "maps",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--dont-restore-color-profile-after-test",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "maps_pixel_test",
-        "precommit_args": [
-          "--review-patch-issue",
-          "${patch_issue}",
-          "--review-patch-set",
-          "${patch_set}",
-          "--buildbucket-build-id",
-          "${buildbucket_build_id}"
-        ],
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
-        }
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--dont-restore-color-profile-after-test",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "pixel_skia_gold_test",
-        "precommit_args": [
-          "--review-patch-issue",
-          "${patch_issue}",
-          "--review-patch-set",
-          "${patch_set}",
-          "--buildbucket-build-id",
-          "${buildbucket_build_id}"
-        ],
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
-        }
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--dont-restore-color-profile-after-test"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "screenshot_sync_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "trace_test",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl2_conformance_gl_passthrough_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 20
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu --use-cmd-decoder=validating",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl2_conformance_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 20
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl_conformance_gl_passthrough_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 2
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu --use-cmd-decoder=validating"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl_conformance_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 2
-        }
-      }
-    ]
-  },
-  "Mac FYI 10.14 Release (Intel)": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "shards": 4
-        },
-        "test": "angle_end2end_tests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "args": [
-          "--enable-gpu",
-          "--test-launcher-bot-mode",
-          "--test-launcher-jobs=1",
-          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "tab_capture_end2end_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--use-cmd-decoder=validating"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gl_tests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gl_unittests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gles2_conform_test"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=*Detection*",
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "services_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "swiftshader_unittests"
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "context_lost_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "depth_capture",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "depth_capture_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gpu_process_launch_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "hardware_accelerated_feature_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--expected-vendor-id",
-          "8086",
-          "--expected-device-id",
-          "0a2e"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "info_collection_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "maps",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--dont-restore-color-profile-after-test",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "maps_pixel_test",
-        "precommit_args": [
-          "--review-patch-issue",
-          "${patch_issue}",
-          "--review-patch-set",
-          "${patch_set}",
-          "--buildbucket-build-id",
-          "${buildbucket_build_id}"
-        ],
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
-        }
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--dont-restore-color-profile-after-test",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "pixel_skia_gold_test",
-        "precommit_args": [
-          "--review-patch-issue",
-          "${patch_issue}",
-          "--review-patch-set",
-          "${patch_set}",
-          "--buildbucket-build-id",
-          "${buildbucket_build_id}"
-        ],
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
-        }
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--dont-restore-color-profile-after-test"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "screenshot_sync_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "trace_test",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl2_conformance_gl_passthrough_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 20
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu --use-cmd-decoder=validating",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl2_conformance_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 20
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl_conformance_gl_passthrough_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 2
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu --use-cmd-decoder=validating"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl_conformance_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 2
-        }
-      }
-    ]
-  },
-  "Mac FYI 10.14 Release (NVIDIA)": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "shards": 4
-        },
-        "test": "angle_end2end_tests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "args": [
-          "--enable-gpu",
-          "--test-launcher-bot-mode",
-          "--test-launcher-jobs=1",
-          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "tab_capture_end2end_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--use-cmd-decoder=validating"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gl_tests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gl_unittests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gles2_conform_test"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=*Detection*",
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "services_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "swiftshader_unittests"
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "context_lost_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "depth_capture",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "depth_capture_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gpu_process_launch_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "hardware_accelerated_feature_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--expected-vendor-id",
-          "10de",
-          "--expected-device-id",
-          "0fe9"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "info_collection_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "maps",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--dont-restore-color-profile-after-test",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "maps_pixel_test",
-        "precommit_args": [
-          "--review-patch-issue",
-          "${patch_issue}",
-          "--review-patch-set",
-          "${patch_set}",
-          "--buildbucket-build-id",
-          "${buildbucket_build_id}"
-        ],
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
-        }
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--dont-restore-color-profile-after-test",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "pixel_skia_gold_test",
-        "precommit_args": [
-          "--review-patch-issue",
-          "${patch_issue}",
-          "--review-patch-set",
-          "${patch_set}",
-          "--buildbucket-build-id",
-          "${buildbucket_build_id}"
-        ],
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
-        }
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--dont-restore-color-profile-after-test"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "screenshot_sync_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "trace_test",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl2_conformance_gl_passthrough_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 20
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu --use-cmd-decoder=validating",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl2_conformance_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 20
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl_conformance_gl_passthrough_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 2
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu --use-cmd-decoder=validating"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl_conformance_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "os": "Mac-10.14.6",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "expiration": 21600,
-          "idempotent": false,
-          "shards": 2
-        }
-      }
-    ]
-  },
   "Mac FYI Debug (Intel)": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/filters/bfcache.android_browsertests.filter b/testing/buildbot/filters/bfcache.android_browsertests.filter
index a0e1f63..2d8f82a 100644
--- a/testing/buildbot/filters/bfcache.android_browsertests.filter
+++ b/testing/buildbot/filters/bfcache.android_browsertests.filter
@@ -1,18 +1 @@
 # These tests currently fail when run with --enable-features=BackForwardCache
-
-# WebPayment.
-# https://crbug.com/1006256
--HasEnrolledInstrumentQueryQuotaTest.*
--HasEnrolledInstrumentTest.*
--HybridRequestSkipUITest.*
--PaymentHandlerEnableDelegationsTest.*
--PaymentHandlerExploitTest.*
--PaymentRequestCanMakePaymentQueryCCTest.*
--PaymentRequestCanMakePaymentQueryPMITest.*
--PaymentRequestCanMakePaymentQueryTest.*
-
-# Flaky for unknown reasons.
-# TODO(ahemery): Was not able to reproduce locally on api_level 21 and 28,
-# reach out to more knowledgeable android folks.
--AndroidBrowserTest.Smoke
--ImportantSitesUtilBrowserTest.DSENotConsideredImportantInRegularMode
diff --git a/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter b/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter
index 4946795..d6ef6877 100644
--- a/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter
+++ b/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter
@@ -7,28 +7,11 @@
 -org.chromium.chrome.browser.FeaturesAnnotationsTest.testFeaturesSetExistingFlags
 -org.chromium.chrome.browser.FeaturesAnnotationsTest.testFeaturesDoNotRemoveExistingFlags
 
-# (Android only) UkmTest (4 tests)
--org.chromium.chrome.browser.sync.UkmTest.*
+# Not triaged yet. See https://crbug.com/1006267
+# Reproduce with --enable-features=BackForwardCache,BackForwardCacheNoTimeEviction
+-org.chromium.chrome.browser.offlinepages.OfflinePageAutoFetchTest
+-org.chromium.chrome.browser.offlinepages.indicator.OfflineIndicatorControllerTest
 
 # Not triaged yet. See https://crbug.com/1006267
--org.chromium.chrome.browser.SafeBrowsingTest.interstitialPage
--org.chromium.chrome.browser.WarmupManagerTest.testCreateAndTakeSpareRenderer
--org.chromium.chrome.browser.autofill_assistant.AutofillAssistantAutostartTest.testAutostart
--org.chromium.chrome.browser.customtabs.DetachedResourceRequestTest.testSafeBrowsingSubresource
--org.chromium.chrome.browser.customtabs.DetachedResourceRequestTest.testSafeBrowsingSubresourceBeforeNative
+# This fails even without bfcache flags but keep it disabled just for now.
 -org.chromium.chrome.browser.externalnav.UrlOverridingTest.testRedirectionFromIntent
--org.chromium.chrome.browser.incognito.IncognitoTabLauncherTest.testLaunchIncognitoNewTab
--org.chromium.chrome.browser.incognito.IncognitoTabLauncherTest.testLaunchIncognitoNewTab_omniboxFocused
--org.chromium.chrome.browser.infobar.InfoBarTest.testInfoBarForGeolocationDisappearsOnBack
--org.chromium.chrome.browser.keyboard_accessory.PasswordGenerationIntegrationTest.testAutomaticGenerationUsePassword
--org.chromium.chrome.browser.keyboard_accessory.PasswordGenerationIntegrationTest.testManualGenerationUsePassword
--org.chromium.chrome.browser.notifications.NotificationPlatformBridgeIntentTest.testLaunchNotificationPreferencesForCategory
--org.chromium.chrome.browser.notifications.NotificationPlatformBridgeIntentTest.testLaunchNotificationPreferencesForWebsite
--org.chromium.chrome.browser.offlinepages.*
--org.chromium.chrome.browser.profiling_host.ProfilingProcessHostAndroidTest.testModeBrowser
--org.chromium.chrome.browser.tabmodel.IncognitoTabModelTest.testRecreateInIncognito
--org.chromium.chrome.browser.toolbar.top.BrandColorTest.testBrandColorInterstitial
-
-# Autofill / password manager.
-# https://crbug.com/1011799
--org.chromium.chrome.browser.password_manager.OnboardingDialogIntegrationTest.*
diff --git a/testing/buildbot/filters/bfcache.content_browsertests.filter b/testing/buildbot/filters/bfcache.content_browsertests.filter
index 968a4ee2..93d4d4d 100644
--- a/testing/buildbot/filters/bfcache.content_browsertests.filter
+++ b/testing/buildbot/filters/bfcache.content_browsertests.filter
@@ -55,11 +55,6 @@
 # happen when the BackForwardCache is used to store the old document.
 -RenderFrameHostManagerTest.RenderViewInitAfterNewProxyAndProcessKill
 
-# FATAL:casting.h Security DCHECK failed: IsA<Derived>(from).
-# In content::RenderFrameProxy::OnSetFrameOwnerProperties()
-# https://crbug.com/999849
--WebContentsSplitCacheWithFrameOriginBrowserTest.SplitCache*
-
 # Histogram "PrefetchedSignedExchangeCache.Count" is recorded when the document
 # in the RenderFrameHost is replaced, or when the RenderFrameHost is deleted.
 # With the BackForwardCache, the RenderFrameHost is not deleted, so the
@@ -106,7 +101,3 @@
 -RenderViewHostTest.IsFocusedElementEditable
 -TouchInputBrowserTest.*
 -TouchpadPinchBrowserTest.*
-
-# Started failing around when https://crrev.com/c/1819251 was submitted.
-# https://crbug.com/1013550
--SessionHistoryTest.GoBackToCrossSitePostWithRedirect
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index c714f626..c2aa3237 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -126,13 +126,6 @@
 #   },
 
 {
-  'amd_8870m': {
-    'swarming': {
-      'dimensions': {
-        'gpu': '1002:6821',
-      },
-    },
-  },
   'android_q': {
     'swarming': {
       'dimensions': {
@@ -389,13 +382,6 @@
       },
     },
   },
-  'mac_10.14.6': {
-    'swarming': {
-      'dimensions': {
-        'os': 'Mac-10.14.6',
-      },
-    },
-  },
   'mac_10.15_beta': {
     'swarming': {
       'dimensions': {
@@ -529,13 +515,6 @@
       },
     },
   },
-  'nvidia_750m_mac_edition': {
-    'swarming': {
-      'dimensions': {
-        'gpu': '10de:0fe9',
-      },
-    },
-  },
   'oreo_fleet': {
     'swarming': {
       'dimensions': {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 43c1f77f..f542834 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -861,6 +861,13 @@
   },
   'monochrome_public_test_ar_apk': {
     'modifications': {
+      'Nougat Phone Tester': {
+        'args': [
+          # ArCore is not installed as a system app on N, so we can install it
+          # normally instead of replacing the system version.
+          '--additional-apk=//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk'
+        ],
+      },
       # chromium.android
       # We need to match the Playstore version as well because AR tests fail on
       # old versions of the Playstore.
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index f8b6afc..2d94169 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -5242,6 +5242,7 @@
     ],
 
     'android_nougat_gtests': [
+      'android_ar_gtests',
       'android_ddready_vr_gtests',
       'android_monochrome_smoke_tests',
       'android_smoke_tests',
diff --git a/testing/buildbot/tryserver.chromium.android.json b/testing/buildbot/tryserver.chromium.android.json
index bd8f7a3..571ac3f3 100644
--- a/testing/buildbot/tryserver.chromium.android.json
+++ b/testing/buildbot/tryserver.chromium.android.json
@@ -382,6 +382,7 @@
       }
     ]
   },
+  "android-opus-kitkat-arm-rel": {},
   "android_blink_rel": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index c5e59ab..dcb2a07d 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2826,47 +2826,6 @@
           'gtest_tests': 'gpu_angle_deqp_linux_nvidia_gtests',
         },
       },
-      'Mac FYI 10.14 Release (AMD)': {
-        'os_type': 'mac',
-        'browser_config': 'release',
-        'mixins': [
-          'amd_8870m',
-          'gpu_pool',
-          'limited_capacity_bot',
-          'mac_10.14.6',
-        ],
-        'test_suites': {
-          'gtest_tests': 'gpu_fyi_mac_release_gtests',
-          'gpu_telemetry_tests': 'gpu_fyi_mac_release_telemetry_tests',
-        },
-      },
-      'Mac FYI 10.14 Release (Intel)': {
-        'os_type': 'mac',
-        'browser_config': 'release',
-        'mixins': [
-          'intel_iris_5100',
-          'limited_capacity_bot',
-          'mac_10.14.6',
-        ],
-        'test_suites': {
-          'gtest_tests': 'gpu_fyi_mac_release_gtests',
-          'gpu_telemetry_tests': 'gpu_fyi_mac_release_telemetry_tests',
-        },
-      },
-      'Mac FYI 10.14 Release (NVIDIA)': {
-        'os_type': 'mac',
-        'browser_config': 'release',
-        'mixins': [
-          'gpu_pool',
-          'mac_10.14.6',
-          'limited_capacity_bot',
-          'nvidia_750m_mac_edition',
-        ],
-        'test_suites': {
-          'gtest_tests': 'gpu_fyi_mac_release_gtests',
-          'gpu_telemetry_tests': 'gpu_fyi_mac_release_telemetry_tests',
-        },
-      },
       'Mac FYI Debug (Intel)': {
         'os_type': 'mac',
         'browser_config': 'debug',
@@ -4444,6 +4403,13 @@
         },
         'os_type': 'android',
       },
+      'android-opus-kitkat-arm-rel': {
+        'mixins': [
+          'kitkat',
+          'hammerhead',
+        ],
+        'os_type': 'android',
+      },
       'android_blink_rel': {
         'mixins': [
           'kitkat',
diff --git a/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json b/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json
index d5c127d8..667fa79 100644
--- a/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json
+++ b/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json
@@ -1,196 +1,196 @@
 {
   "win": {
     "balls_css_transition_2_properties": {
-      "avg": 107.983,
-      "ci_095": 100
+      "avg": 88.519,
+      "ci_095": 26.992
     },
     "many_planets_deep": {
-      "avg": 25.015,
-        "ci_095": 100
+      "avg": 16.679,
+      "ci_095": 0.169
     },
     "web_animation_value_type_transform_complex": {
-      "avg": 42.981,
-        "ci_095": 100
+      "avg": 47.464,
+      "ci_095": 1.228
     },
     "nvidia_vertex_buffer_object": {
-      "avg": 25.166,
-        "ci_095": 100
+      "avg": 17.918,
+      "ci_095": 3.238
     },
     "runway": {
-      "avg": 74.211,
-        "ci_095": 100
+      "avg": 28.237,
+      "ci_095": 28.279
     },
     "css_animations_simultaneous_inline_style": {
       "avg": 25.100,
-        "ci_095": 100
+      "ci_095": 0.711
     },
     "aquarium_20k": {
-      "avg": 113.156,
-        "ci_095": 100
+      "avg": 123.389,
+      "ci_095": 4.392
     },
     "canvas_to_blob": {
-      "avg": 39.239,
-        "ci_095": 100
+      "avg": 30.395,
+      "ci_095": 12.237
     },
     "cc_poster_circle": {
-      "avg": 25.111,
-        "ci_095": 100
+      "avg": 16.678,
+      "ci_095": 0.067
     },
     "js_poster_circle": {
-      "avg": 25.016,
-        "ci_095": 100
+      "avg": 16.679,
+      "ci_095": 0.113
     },
     "web_animations_staggered_triggering_page": {
-      "avg": 25.033,
-        "ci_095": 100
+      "avg": 16.862,
+      "ci_095": 0.245
     },
     "aquarium": {
-      "avg": 25.014,
-        "ci_095": 100
+      "avg": 16.699,
+      "ci_095": 0.304
     },
     "css_value_type_shadow": {
-      "avg": 31.638,
-        "ci_095": 100
+      "avg": 47.344,
+      "ci_095": 1.018
     },
     "filter_terrain_svg": {
-      "avg": 41.496,
-        "ci_095": 100
+      "avg": 30.466,
+      "ci_095": 0.212
     },
     "css_transitions_inline_style": {
-      "avg": 25.312,
-        "ci_095": 100
+      "avg": 19.283,
+      "ci_095": 3.280
     }
   },
   "mac": {
     "mix_blend_mode_animation_screen": {
-      "avg": 148.760,
-      "ci_095": 100
+      "avg": 96.183,
+      "ci_095": 5.162
     },
     "twitch_2018": {
-      "avg": 28.110,
-      "ci_095": 100
+      "avg": 33.002,
+      "ci_095": 13.557
     },
     "balls_javascript_canvas": {
-      "avg": 40.563,
-      "ci_095": 100
+      "avg": 40.793,
+      "ci_095": 3.571
     },
     "transform_transitions_js_block": {
-      "avg": 25.090,
-      "ci_095": 100
+      "avg": 16.758,
+      "ci_095": 0.01
     },
     "web_animations_staggered_infinite_iterations": {
-      "avg": 25.006,
-      "ci_095": 100
+      "avg": 16.692,
+      "ci_095": 0.02
     },
     "fill_shapes": {
-      "avg": 41.667,
-      "ci_095": 100
+      "avg": 36.591,
+      "ci_095": 3.295
     },
     "css_value_type_shadow": {
-      "avg": 69.304,
-      "ci_095": 100
+      "avg": 65.745,
+      "ci_095": 19.875
     },
     "animometer_webgl_attrib_arrays": {
-      "avg": 27.124,
-      "ci_095": 100
+      "avg": 16.862,
+      "ci_095": 0.19
     },
     "web_animation_value_type_transform_simple": {
-      "avg": 55.002,
-      "ci_095": 100
+      "avg": 48.286,
+      "ci_095": 5.704
     },
     "canvas_05000_pixels_per_second": {
-      "avg": 24.997,
-      "ci_095": 100
+      "avg": 16.685,
+      "ci_095": 0.012
     },
     "bouncing_clipped_rectangles": {
-      "avg": 592.146,
-      "ci_095": 100
+      "avg": 537.025,
+      "ci_095": 72.711
     },
     "ie_chalkboard": {
-      "avg": 68.514,
-      "ci_095": 100
+      "avg": 66.825,
+      "ci_095": 15.637
     },
     "new_tilings": {
-      "avg": 31.733,
-      "ci_095": 100
+      "avg": 23.163,
+      "ci_095": 1.583
     },
     "chip_tune": {
-      "avg": 25.006,
-      "ci_095": 100
+      "avg": 16.737,
+      "ci_095": 0.044
     },
     "extra_large_texture_uploads": {
-      "avg": 77.757,
-      "ci_095": 100
+      "avg": 58.721,
+      "ci_095": 1.167
     },
     "css_value_type_filter": {
-      "avg": 92.894,
-      "ci_095": 100
+      "avg": 86.254,
+      "ci_095": 12.391
     }
   },
   "android": {
     "mix_blend_mode_animation_screen": {
-      "avg": 400.414,
-      "ci_095": 100
+      "avg": 511.313,
+      "ci_095": 233.908
     },
     "twitch_2018": {
-      "avg": 34.498,
-      "ci_095": 100
+      "avg": 19.843,
+      "ci_095": 6.112
     },
     "balls_javascript_canvas": {
-      "avg": 276.974,
-      "ci_095": 100
+      "avg": 191.168,
+      "ci_095": 18.935
     },
     "transform_transitions_js_block": {
-      "avg": 25.179,
-      "ci_095": 100
+      "avg": 16.652,
+      "ci_095": 0.065
     },
     "web_animations_staggered_infinite_iterations": {
-      "avg": 48.752,
-      "ci_095": 100
+      "avg": 47.653,
+      "ci_095": 4.68
     },
     "text_10000_pixels_per_second": {
-      "avg": 25.470,
-      "ci_095": 100
+      "avg": 16.813,
+      "ci_095": 0.543
     },
     "motion_mark_canvas_fill_shapes": {
-      "avg": 243.536,
-      "ci_095": 100
+      "avg": 143.318,
+      "ci_095": 31.915
     },
     "css_value_type_shadow": {
-      "avg": 591.356,
-      "ci_095": 100
+      "avg": 229.602,
+      "ci_095": 32.759
     },
     "animometer_webgl_attrib_arrays": {
-      "avg": 337.719,
-      "ci_095": 300
+      "avg": 412.827,
+      "ci_095": 110.526
     },
     "canvas_05000_pixels_per_second": {
-      "avg": 25.322,
-      "ci_095": 100
+      "avg": 16.857,
+      "ci_095": 0.609
     },
     "bouncing_clipped_rectangles": {
-      "avg": 481.911,
-      "ci_095": 100
+      "avg": 396.708,
+      "ci_095": 106.785
     },
     "ie_chalkboard": {
-      "avg": 217.131,
-      "ci_095": 100
+      "avg": 1429.4,
+      "ci_095": 1327.254
     },
     "new_tilings": {
-      "avg": 25.052,
-      "ci_095": 100
+      "avg": 16.793,
+      "ci_095": 0.388
     },
     "extra_large_texture_uploads": {
-      "avg": 452.959,
-      "ci_095": 100
+      "avg": 191.029,
+      "ci_095": 22.701
     },
     "web_animation_value_type_transform_simple": {
-      "avg": 331.928,
-      "ci_095": 100
+      "avg": 308.001,
+      "ci_095": 54.104
     },
     "css_value_type_filter": {
-      "avg": 909.449,
-      "ci_095": 100
+      "avg": 581.157,
+      "ci_095": 145.986
     }
   }
 }
\ No newline at end of file
diff --git a/testing/test.gni b/testing/test.gni
index 9fa5468..88244f54 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -99,6 +99,7 @@
         "min_sdk_version",
         "proguard_configs",
         "proguard_enabled",
+        "shared_resources",
         "srcjar_deps",
         "target_sdk_version",
         "use_default_launcher",
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 2c44110..5651a117 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1511,6 +1511,19 @@
                     ]
                 }
             ]
+        },
+        {
+            "platforms": [
+                "linux"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_V2",
+                    "enable_features": [
+                        "CertVerifierBuiltin"
+                    ]
+                }
+            ]
         }
     ],
     "ChromeCleanupDistribution": [
@@ -3484,6 +3497,24 @@
             ]
         }
     ],
+    "MixedContentShieldRemoval": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "MixedContentSiteSetting"
+                    ]
+                }
+            ]
+        }
+    ],
     "NTPLaunchAfterInactivity": [
         {
             "platforms": [
diff --git a/third_party/android_platform/development/scripts/stack_core.py b/third_party/android_platform/development/scripts/stack_core.py
index ccf00e56..e5edf64 100755
--- a/third_party/android_platform/development/scripts/stack_core.py
+++ b/third_party/android_platform/development/scripts/stack_core.py
@@ -100,7 +100,7 @@
 # This pattern is used to find shared library offset in APK.
 # Example:
 #    (offset 0x568000)
-_SHARED_LIB_OFFSET_IN_APK = re.compile(' \(offset 0x(?P<offset>[0-9a-f]{0,16})\)$')
+_SHARED_LIB_OFFSET_IN_APK = re.compile(' \(offset 0x(?P<offset>[0-9a-f]{0,16})\)')
 
 def PrintTraceLines(trace_lines):
   """Print back trace."""
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 4d1bb66..8ced46ef 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -509,7 +509,6 @@
     "web/web_text_decoration_type.h",
     "web/web_text_direction.h",
     "web/web_tree_scope_type.h",
-    "web/web_triggering_event_info.h",
     "web/web_user_gesture_indicator.h",
     "web/web_user_gesture_token.h",
     "web/web_user_media_request.h",
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index b15109d..be6da1f 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -104,6 +104,7 @@
     "messaging/transferable_message.h",
     "messaging/transferable_message_mojom_traits.h",
     "mime_util/mime_util.h",
+    "navigation/triggering_event_info.h",
     "notifications/notification_constants.h",
     "notifications/notification_mojom_traits.h",
     "notifications/notification_resources.h",
@@ -128,6 +129,7 @@
     "service_worker/service_worker_types.h",
     "service_worker/service_worker_utils.h",
     "sms/sms_receiver_outcome.h",
+    "sudden_termination_disabler_type.h",
     "user_agent/user_agent_metadata.h",
     "web_cache/web_cache_resource_type_stats.h",
     "web_package/signed_exchange_consts.h",
diff --git a/third_party/blink/public/web/web_triggering_event_info.h b/third_party/blink/public/common/navigation/triggering_event_info.h
similarity index 61%
rename from third_party/blink/public/web/web_triggering_event_info.h
rename to third_party/blink/public/common/navigation/triggering_event_info.h
index da8183e..9232d7f 100644
--- a/third_party/blink/public/web/web_triggering_event_info.h
+++ b/third_party/blink/public/common/navigation/triggering_event_info.h
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_TRIGGERING_EVENT_INFO_H_
-#define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_TRIGGERING_EVENT_INFO_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_NAVIGATION_TRIGGERING_EVENT_INFO_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_NAVIGATION_TRIGGERING_EVENT_INFO_H_
 
 namespace blink {
 
-// Extra info sometimes associated with a navigation. Mirrors
-// theWebTriggeringEventInfoEnum.
-enum class WebTriggeringEventInfo {
+// Extra info sometimes associated with a navigation.
+enum class TriggeringEventInfo {
   kUnknown,
 
   // The navigation was not triggered via a JS Event.
@@ -26,4 +25,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_TRIGGERING_EVENT_INFO_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_NAVIGATION_TRIGGERING_EVENT_INFO_H_
diff --git a/third_party/blink/public/common/sudden_termination_disabler_type.h b/third_party/blink/public/common/sudden_termination_disabler_type.h
new file mode 100644
index 0000000..a13f0e9
--- /dev/null
+++ b/third_party/blink/public/common/sudden_termination_disabler_type.h
@@ -0,0 +1,18 @@
+// 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 THIRD_PARTY_BLINK_PUBLIC_COMMON_SUDDEN_TERMINATION_DISABLER_TYPE_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_SUDDEN_TERMINATION_DISABLER_TYPE_H_
+
+namespace blink {
+
+// Used when elements preventing the sudden termination of the frame become
+// present or become absent.
+using SuddenTerminationDisablerType = uint8_t;
+const SuddenTerminationDisablerType kBeforeUnloadHandler = 1 << 0;
+const SuddenTerminationDisablerType kUnloadHandler = 1 << 1;
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_SUDDEN_TERMINATION_DISABLER_TYPE_H_
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index cda5f317..e88219cb 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -576,11 +576,6 @@
     return nullptr;
   }
 
-  virtual scoped_refptr<base::SingleThreadTaskRunner>
-  GetWebRtcSignalingTaskRunner() {
-    return nullptr;
-  }
-
   // May return null if WebRTC functionality is not implemented.
   virtual std::unique_ptr<cricket::PortAllocator> CreateWebRtcPortAllocator(
       WebLocalFrame* frame);
diff --git a/third_party/blink/public/platform/web_sudden_termination_disabler_type.h b/third_party/blink/public/platform/web_sudden_termination_disabler_type.h
deleted file mode 100644
index 7e4a39ed..0000000
--- a/third_party/blink/public/platform/web_sudden_termination_disabler_type.h
+++ /dev/null
@@ -1,18 +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 THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_SUDDEN_TERMINATION_DISABLER_TYPE_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_SUDDEN_TERMINATION_DISABLER_TYPE_H_
-
-namespace blink {
-
-// Used when elements preventing the sudden termination of the frame become
-// present or become absent.
-using WebSuddenTerminationDisablerType = uint8_t;
-const WebSuddenTerminationDisablerType kBeforeUnloadHandler = 1 << 0;
-const WebSuddenTerminationDisablerType kUnloadHandler = 1 << 1;
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_SUDDEN_TERMINATION_DISABLER_TYPE_H_
diff --git a/third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h b/third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h
index 55454164b..a53758e 100644
--- a/third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h
+++ b/third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h
@@ -157,8 +157,8 @@
       int sdp_mline_index,
       const std::string& sdp) override;
 
-  scoped_refptr<base::SingleThreadTaskRunner> GetWebRtcSignalingThread()
-      const override;
+  scoped_refptr<base::SingleThreadTaskRunner> GetWebRtcSignalingTaskRunner()
+      override;
 
   // If |fail| is true, subsequent calls to CreateSessionDescription will
   // return nullptr. This can be used to fake a blob of SDP that fails to be
diff --git a/third_party/blink/public/web/modules/peerconnection/peer_connection_dependency_factory.h b/third_party/blink/public/web/modules/peerconnection/peer_connection_dependency_factory.h
index fde6670..851ddd9a 100644
--- a/third_party/blink/public/web/modules/peerconnection/peer_connection_dependency_factory.h
+++ b/third_party/blink/public/web/modules/peerconnection/peer_connection_dependency_factory.h
@@ -122,8 +122,8 @@
   // Returns the rtc::Thread instance associated with the WebRTC worker thread.
   // TODO(bugs.webrtc.org/9419): Remove once WebRTC can be built as a component.
   rtc::Thread* GetWebRtcWorkerThreadRtcThread();
-  virtual scoped_refptr<base::SingleThreadTaskRunner> GetWebRtcSignalingThread()
-      const;
+  virtual scoped_refptr<base::SingleThreadTaskRunner>
+  GetWebRtcSignalingTaskRunner();
 
  protected:
   virtual const scoped_refptr<webrtc::PeerConnectionFactoryInterface>&
diff --git a/third_party/blink/public/web/web_ax_enums.h b/third_party/blink/public/web/web_ax_enums.h
index 0438482..42e333c 100644
--- a/third_party/blink/public/web/web_ax_enums.h
+++ b/third_party/blink/public/web/web_ax_enums.h
@@ -73,6 +73,13 @@
   kWebAXRestrictionDisabled,
 };
 
+// Autofill state.
+enum WebAXAutofillState {
+  kNoSuggestions = 0,
+  kAutofillAvailable,
+  kAutocompleteAvailable,
+};
+
 //
 // Sparse accessibility attributes
 //
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h
index 2c70318..8e83e466 100644
--- a/third_party/blink/public/web/web_ax_object.h
+++ b/third_party/blink/public/web/web_ax_object.h
@@ -383,7 +383,7 @@
   BLINK_EXPORT WebString ToString() const;
 
   BLINK_EXPORT void HandleAutofillStateChanged(
-      bool suggestions_available) const;
+      const WebAXAutofillState state) const;
 
 #if INSIDE_BLINK
   BLINK_EXPORT WebAXObject(AXObject*);
diff --git a/third_party/blink/public/web/web_frame.h b/third_party/blink/public/web/web_frame.h
index 12966c8..4dc7a53e 100644
--- a/third_party/blink/public/web/web_frame.h
+++ b/third_party/blink/public/web/web_frame.h
@@ -53,7 +53,6 @@
 enum class WebSandboxFlags;
 struct FramePolicy;
 struct WebFrameOwnerProperties;
-struct WebRect;
 
 // Frames may be rendered in process ('local') or out of process ('remote').
 // A remote frame is always cross-site; a local frame may be either same-site or
@@ -119,14 +118,6 @@
   // navigation.  This matches the in-process frame behavior.
   void SetFrameOwnerProperties(const WebFrameOwnerProperties&);
 
-  // Geometry -----------------------------------------------------------
-
-  // NOTE: These routines do not force page layout so their results may
-  // not be accurate if the page layout is out-of-date.
-
-  // Returns the visible content rect (minus scrollbars, in absolute coordinate)
-  virtual WebRect VisibleContentRect() const = 0;
-
   // Whether to collapse the frame's owner element in the embedder document,
   // that is, to remove it from the layout as if it did not exist. Only works
   // for <iframe> owner elements.
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 026cefa..023e015 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -236,9 +236,6 @@
 
   // Navigation State -------------------------------------------------------
 
-  // Returns true if the current frame's load event has not completed.
-  bool IsLoading() const override = 0;
-
   // Returns true if there is a pending redirect or location change
   // within specified interval. This could be caused by:
   // * an HTTP Refresh header
@@ -272,13 +269,6 @@
   // Notify the frame that the screen orientation has changed.
   virtual void SendOrientationChangeEvent() = 0;
 
-  // Printing ------------------------------------------------------------
-
-  // Returns true on success and sets the out parameter to the print preset
-  // options for the document.
-  virtual bool GetPrintPresetOptionsForPlugin(const WebNode&,
-                                              WebPrintPresetOptions*) = 0;
-
   // CSS3 Paged Media ----------------------------------------------------
 
   // Returns true if page box (margin boxes and page borders) is visible.
@@ -674,6 +664,9 @@
   // Returns true if the contents (minus scrollbars) has non-zero area.
   virtual bool HasVisibleContent() const = 0;
 
+  // Returns the visible content rect (minus scrollbars, in absolute coordinate)
+  virtual WebRect VisibleContentRect() const = 0;
+
   // Printing ------------------------------------------------------------
 
   // Dispatch |beforeprint| event, and execute event handlers. They might detach
@@ -709,6 +702,11 @@
   // This function should be called after pairs of PrintBegin() and PrintEnd().
   virtual void DispatchAfterPrintEvent() = 0;
 
+  // Returns true on success and sets the out parameter to the print preset
+  // options for the document.
+  virtual bool GetPrintPresetOptionsForPlugin(const WebNode&,
+                                              WebPrintPresetOptions*) = 0;
+
   // Focus --------------------------------------------------------------
 
   // Advance the focus of the WebView to next text input element from current
@@ -773,13 +771,12 @@
   explicit WebLocalFrame(WebTreeScopeType scope) : WebFrame(scope) {}
 
   // Inherited from WebFrame, but intentionally hidden: it never makes sense
-  // to call these on a WebLocalFrame.
+  // to directly call these on a WebLocalFrame.
   bool IsWebLocalFrame() const override = 0;
   WebLocalFrame* ToWebLocalFrame() override = 0;
   bool IsWebRemoteFrame() const override = 0;
   WebRemoteFrame* ToWebRemoteFrame() override = 0;
 
- private:
   virtual void AddMessageToConsoleImpl(const WebConsoleMessage&,
                                        bool discard_duplicates) = 0;
 };
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index 7159bd20..dc80421 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -42,6 +42,8 @@
 #include "third_party/blink/public/common/frame/user_activation_update_type.h"
 #include "third_party/blink/public/common/loader/loading_behavior_flag.h"
 #include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
+#include "third_party/blink/public/common/sudden_termination_disabler_type.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-shared.h"
 #include "third_party/blink/public/mojom/use_counter/css_property_id.mojom-shared.h"
 #include "third_party/blink/public/platform/blame_context.h"
@@ -56,7 +58,6 @@
 #include "third_party/blink/public/platform/web_scroll_types.h"
 #include "third_party/blink/public/platform/web_set_sink_id_callbacks.h"
 #include "third_party/blink/public/platform/web_source_location.h"
-#include "third_party/blink/public/platform/web_sudden_termination_disabler_type.h"
 #include "third_party/blink/public/platform/web_url_error.h"
 #include "third_party/blink/public/platform/web_url_loader_factory.h"
 #include "third_party/blink/public/platform/web_url_request.h"
@@ -76,7 +77,6 @@
 #include "third_party/blink/public/web/web_navigation_policy.h"
 #include "third_party/blink/public/web/web_navigation_type.h"
 #include "third_party/blink/public/web/web_text_direction.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
 #include "ui/accessibility/ax_enums.mojom-shared.h"
 #include "ui/events/types/scroll_types.h"
 #include "v8/include/v8.h"
@@ -774,9 +774,9 @@
   // Called when elements preventing the sudden termination of the frame
   // become present or stop being present. |type| is the type of element
   // (BeforeUnload handler, Unload handler).
-  virtual void SuddenTerminationDisablerChanged(
-      bool present,
-      WebSuddenTerminationDisablerType) {}
+  virtual void SuddenTerminationDisablerChanged(bool present,
+                                                SuddenTerminationDisablerType) {
+  }
 
   // Navigator Content Utils  --------------------------------------------
 
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index 39df65d8c..d3e8c0b 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -14,6 +14,7 @@
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/network/public/mojom/ip_address_space.mojom-shared.h"
 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
 #include "third_party/blink/public/platform/web_common.h"
@@ -35,7 +36,6 @@
 #include "third_party/blink/public/web/web_navigation_timings.h"
 #include "third_party/blink/public/web/web_navigation_type.h"
 #include "third_party/blink/public/web/web_origin_policy.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
 
 #if INSIDE_BLINK
 #include "base/memory/scoped_refptr.h"
@@ -99,9 +99,8 @@
   // |BlockingDownloadsInSandboxWithoutUserActivation| is enabled.
   bool blocking_downloads_in_sandbox_without_user_activation_enabled = false;
 
-  // Event information. See WebTriggeringEventInfo.
-  WebTriggeringEventInfo triggering_event_info =
-      WebTriggeringEventInfo::kUnknown;
+  // Event information. See TriggeringEventInfo.
+  TriggeringEventInfo triggering_event_info = TriggeringEventInfo::kUnknown;
 
   // If the navigation is a result of form submit, the form element is provided.
   WebFormElement form;
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
index 1b6b02bf3..042a358 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
@@ -325,8 +325,9 @@
               if (!ReadUint32(&is_premultiplied) || is_premultiplied > 1)
                 return nullptr;
               break;
-            default:
-              NOTREACHED();
+            case ImageSerializationTag::kImageDataStorageFormatTag:
+              // Does not apply to ImageBitmap.
+              return nullptr;
           }
         } while (!is_done);
       } else if (!ReadUint32(&origin_clean) || origin_clean > 1 ||
@@ -384,8 +385,12 @@
                       &image_data_storage_format))
                 return nullptr;
               break;
-            default:
-              NOTREACHED();
+            case ImageSerializationTag::kCanvasPixelFormatTag:
+            case ImageSerializationTag::kOriginCleanTag:
+            case ImageSerializationTag::kIsPremultipliedTag:
+            case ImageSerializationTag::kCanvasOpacityModeTag:
+              // Does not apply to ImageData.
+              return nullptr;
           }
         } while (!is_done);
       }
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
index 7443dc9..981efb4bf 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
@@ -855,6 +855,19 @@
                      new_image_data->BufferBase()->Data())[0]);
 }
 
+TEST(V8ScriptValueSerializerTest, InvalidImageDataDecodeV18) {
+  V8TestingScope scope;
+  ScriptState* script_state = scope.GetScriptState();
+  {
+    // Nonsense image serialization tag (kOriginCleanTag).
+    scoped_refptr<SerializedScriptValue> input =
+        SerializedValue({0xff, 0x12, 0xff, 0x0d, 0x5c, 0x23, 0x02, 0x00, 0x00,
+                         0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00});
+    EXPECT_TRUE(
+        V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
+  }
+}
+
 MessagePort* MakeMessagePort(ExecutionContext* execution_context,
                              ::MojoHandle* unowned_handle_out = nullptr) {
   auto* port = MakeGarbageCollected<MessagePort>(*execution_context);
@@ -1252,6 +1265,14 @@
     EXPECT_TRUE(
         V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
   }
+  {
+    // Nonsense image serialization tag (kImageDataStorageFormatTag).
+    scoped_refptr<SerializedScriptValue> input =
+        SerializedValue({0xff, 0x12, 0xff, 0x0d, 0x5c, 0x67, 0x03, 0x00, 0x00,
+                         0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00});
+    EXPECT_TRUE(
+        V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
+  }
 }
 
 TEST(V8ScriptValueSerializerTest, TransferImageBitmap) {
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/code_node.py b/third_party/blink/renderer/bindings/scripts/bind_gen/code_node.py
index c0559e4..68bacee 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/code_node.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/code_node.py
@@ -46,7 +46,7 @@
             if key in kwargs:
                 return kwargs[key]
             else:
-                return '{' + key + '}'
+                return "{" + key + "}"
 
     _template_formatter = _LooseFormatter()
     _gensym_seq_id = 0
@@ -93,7 +93,7 @@
         will be "abc ${gensym1} xyz" when |sym| is 'gensym1'.
         """
         cls._gensym_seq_id += 1
-        return 'gensym{}'.format(cls._gensym_seq_id)
+        return "gensym{}".format(cls._gensym_seq_id)
 
     def __init__(self,
                  outer=None,
@@ -158,7 +158,7 @@
 
         Only limited subclasses may override this method.
         """
-        assert self._template_text
+        assert self._template_text is not None
         return renderer.render(
             caller=self,
             template_text=self._template_text,
@@ -262,15 +262,20 @@
         state.code_symbols_used[symbol_node.name] = symbol_node
 
 
-class SimpleNode(CodeNode):
+class LiteralNode(CodeNode):
     """
-    Represents a simple node that never contains a branch nor sequence.
-
-    Technically, you can make this node contain branches and sequences with
-    using template text and variable bindings, however, it's not supported.
+    Represents a literal text, which will be rendered as is without any template
+    magic applied.
     """
 
-    def __init__(self, template_text, template_vars=None, renderer=None):
+    def __init__(self, literal_text, renderer=None):
+        assert isinstance(literal_text, str)
+
+        literal_text_gensym = CodeNode.gensym()
+        template_text = CodeNode.format_template(
+            "${{{literal_text}}}", literal_text=literal_text_gensym)
+        template_vars = {literal_text_gensym: literal_text}
+
         CodeNode.__init__(
             self,
             template_text=template_text,
@@ -278,25 +283,41 @@
             renderer=renderer)
 
 
+class TextNode(CodeNode):
+    """
+    Represents a template text node.
+
+    TextNode is designed to be a leaf node of a code node tree.  TextNode
+    represents a template text while LiteralNode represents a literal text.
+    All template magics will be applied to |template_text|.
+    """
+
+    def __init__(self, template_text, renderer=None):
+        CodeNode.__init__(self, template_text=template_text, renderer=renderer)
+
+
 class SequenceNode(CodeNode):
     """
     Represents a sequence of nodes.
-
-    If SymbolNodes are used inside this node, this node will attempt to insert
-    corresponding SymbolDefinitionNodes appropriately.
     """
 
-    def __init__(self, renderer=None):
-        element_nodes_symbol = CodeNode.gensym()
+    def __init__(self, code_nodes=None, separator="\n", renderer=None):
+        assert isinstance(separator, str)
+
+        element_nodes_gensym = CodeNode.gensym()
         element_nodes = []
         template_text = CodeNode.format_template(
             """\
 % for node in {element_nodes}:
-${node}
-% endfor\
+${node}\\
+% if not loop.last:
+{separator}\\
+% endif
+% endfor
 """,
-            element_nodes=element_nodes_symbol)
-        template_vars = {element_nodes_symbol: element_nodes}
+            element_nodes=element_nodes_gensym,
+            separator=separator)
+        template_vars = {element_nodes_gensym: element_nodes}
 
         CodeNode.__init__(
             self,
@@ -306,6 +327,9 @@
 
         self._element_nodes = element_nodes
 
+        if code_nodes is not None:
+            self.extend(code_nodes)
+
     def __getitem__(self, index):
         return self._element_nodes[index]
 
@@ -315,20 +339,6 @@
     def __len__(self):
         return len(self._element_nodes)
 
-    def _render(self, renderer, last_render_state):
-        # Sort nodes in order to generate reproducible code.
-        symbol_nodes = sorted(
-            last_render_state.code_symbols_used.itervalues(),
-            key=lambda node: node.name)
-        for symbol_node in symbol_nodes:
-            self._insert_symbol_definition(symbol_node)
-
-        return super(SequenceNode, self)._render(
-            renderer=renderer, last_render_state=last_render_state)
-
-    def _insert_symbol_definition(self, symbol_node):
-        self.insert(0, symbol_node.create_definition_node())
-
     def append(self, node):
         assert isinstance(node, CodeNode)
         assert node.outer is None and node.prev is None
@@ -369,24 +379,46 @@
     """
     Represents a code symbol such as a local variable of generated code.
 
-    Used combined with SequenceNode, SymbolDefinitionNode(s) will be
-    automatically inserted iff this symbol is referenced.
+    Using a SymbolNode combined with SymbolScopeNode, SymbolDefinitionNode(s)
+    will be automatically inserted iff this symbol is referenced.
     """
 
-    def __init__(self, name, definition_node_constructor):
+    def __init__(self,
+                 name,
+                 template_text=None,
+                 definition_node_constructor=None):
         """
         Args:
             name: The name of this code symbol.
+            template_text: Template text to be used to define the code symbol.
             definition_node_constructor: A callable that creates and returns a
-                new definition node.  This node will be passed as the argument.
+                new definition node.  This SymbolNode will be passed as the
+                argument.
+                Either of |template_text| or |definition_node_constructor| must
+                be given.
         """
         assert isinstance(name, str) and name
-        assert callable(definition_node_constructor)
+        assert (template_text is not None
+                or definition_node_constructor is not None)
+        assert template_text is None or definition_node_constructor is None
+        if template_text is not None:
+            assert isinstance(template_text, str)
+        if definition_node_constructor is not None:
+            assert callable(definition_node_constructor)
 
         CodeNode.__init__(self)
 
         self._name = name
-        self._definition_node_constructor = definition_node_constructor
+
+        if template_text is not None:
+
+            def constructor(symbol_node):
+                return SymbolDefinitionNode(
+                    symbol_node, template_text=template_text)
+
+            self._definition_node_constructor = constructor
+        else:
+            self._definition_node_constructor = definition_node_constructor
 
     def _render(self, renderer, last_render_state):
         if not renderer.last_caller.is_code_symbol_defined(self):
@@ -426,7 +458,7 @@
     def _render(self, renderer, last_render_state):
         if (self.upstream
                 and self.upstream.is_code_symbol_defined(self._symbol_node)):
-            return ''
+            return ""
 
         return super(SymbolDefinitionNode, self)._render(
             renderer=renderer, last_render_state=last_render_state)
@@ -436,3 +468,35 @@
             return True
         return super(SymbolDefinitionNode,
                      self).is_code_symbol_defined(symbol_node)
+
+
+class SymbolScopeNode(SequenceNode):
+    """
+    Represents a sequence of nodes.
+
+    If SymbolNodes are rendered inside this node, this node will attempt to
+    insert corresponding SymbolDefinitionNodes appropriately.
+    """
+
+    def _render(self, renderer, last_render_state):
+        # Sort nodes in order to render reproducible results.
+        symbol_nodes = sorted(
+            last_render_state.code_symbols_used.itervalues(),
+            key=lambda symbol_node: symbol_node.name)
+        for symbol_node in symbol_nodes:
+            self._insert_symbol_definition(symbol_node)
+
+        return super(SymbolScopeNode, self)._render(
+            renderer=renderer, last_render_state=last_render_state)
+
+    def _insert_symbol_definition(self, symbol_node):
+        self.insert(0, symbol_node.create_definition_node())
+
+    def register_code_symbol(self, symbol_node):
+        """Registers a SymbolNode and makes it available in this scope."""
+        assert isinstance(symbol_node, SymbolNode)
+        self.add_template_var(symbol_node.name, symbol_node)
+
+    def register_code_symbols(self, symbol_nodes):
+        for symbol_node in symbol_nodes:
+            self.register_code_symbol(symbol_node)
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/code_node_test.py b/third_party/blink/renderer/bindings/scripts/bind_gen/code_node_test.py
index 31baea2a..831213e 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/code_node_test.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/code_node_test.py
@@ -4,16 +4,17 @@
 
 import unittest
 
+from .code_node import LiteralNode
 from .code_node import SequenceNode
-from .code_node import SimpleNode
-from .code_node import SymbolDefinitionNode
 from .code_node import SymbolNode
+from .code_node import SymbolScopeNode
+from .code_node import TextNode
 from .mako_renderer import MakoRenderer
 
 
 class CodeNodeTest(unittest.TestCase):
     def render(self, node):
-        prev = ''
+        prev = ""
         current = str(node)
         while current != prev:
             prev = current
@@ -22,68 +23,89 @@
 
     def assertRenderResult(self, node, expected):
         def simplify(s):
-            return ' '.join(s.split())
+            return " ".join(s.split())
 
         actual = simplify(self.render(node))
         expected = simplify(expected)
 
         self.assertEqual(actual, expected)
 
+    def test_literal_node(self):
+        """
+        Tests that, in LiteralNode, the special characters of template (%, ${},
+        etc) are not processed.
+        """
+        renderer = MakoRenderer()
+        root = LiteralNode("<% x = 42 %>${x}", renderer=renderer)
+        self.assertRenderResult(root, "<% x = 42 %>${x}")
+
+    def test_empty_literal_node(self):
+        renderer = MakoRenderer()
+        root = LiteralNode("", renderer=renderer)
+        self.assertRenderResult(root, "")
+
+    def test_text_node(self):
+        """Tests that the template language works in TextNode."""
+        renderer = MakoRenderer()
+        root = TextNode("<% x = 42 %>${x}", renderer=renderer)
+        self.assertRenderResult(root, "42")
+
+    def test_empty_text_node(self):
+        renderer = MakoRenderer()
+        root = TextNode("", renderer=renderer)
+        self.assertRenderResult(root, "")
+
     def test_list_operations_of_sequence_node(self):
+        """
+        Tests that list operations (insert, append, and extend) of SequenceNode
+        work just same as Python built-in list.
+        """
         renderer = MakoRenderer()
         root = SequenceNode(renderer=renderer)
         root.extend([
-            SimpleNode(template_text="2"),
-            SimpleNode(template_text="4"),
+            LiteralNode("2"),
+            LiteralNode("4"),
         ])
-        root.insert(1, SimpleNode(template_text="3"))
-        root.insert(0, SimpleNode(template_text="1"))
-        root.insert(100, SimpleNode(template_text="5"))
-        root.append(SimpleNode(template_text="6"))
+        root.insert(1, LiteralNode("3"))
+        root.insert(0, LiteralNode("1"))
+        root.insert(100, LiteralNode("5"))
+        root.append(LiteralNode("6"))
         self.assertRenderResult(root, "1 2 3 4 5 6")
 
     def test_nested_sequence(self):
+        """Tests nested SequenceNodes."""
         renderer = MakoRenderer()
         root = SequenceNode(renderer=renderer)
         nested = SequenceNode()
         nested.extend([
-            SimpleNode(template_text="2"),
-            SimpleNode(template_text="3"),
-            SimpleNode(template_text="4"),
+            LiteralNode("2"),
+            LiteralNode("3"),
+            LiteralNode("4"),
         ])
         root.extend([
-            SimpleNode(template_text="1"),
+            LiteralNode("1"),
             nested,
-            SimpleNode(template_text="5"),
+            LiteralNode("5"),
         ])
         self.assertRenderResult(root, "1 2 3 4 5")
 
     def test_symbol_definition_chains(self):
+        """
+        Tests that use of SymbolNode inserts necessary SymbolDefinitionNode
+        appropriately.
+        """
         renderer = MakoRenderer()
-        root = SequenceNode(renderer=renderer)
+        root = SymbolScopeNode(renderer=renderer)
 
-        def make_symbol(name, template_text):
-            def constructor(symbol_node):
-                return SymbolDefinitionNode(
-                    symbol_node, template_text=template_text)
+        root.register_code_symbols([
+            SymbolNode("var1", "int ${var1} = ${var2} + ${var3};"),
+            SymbolNode("var2", "int ${var2} = ${var5};"),
+            SymbolNode("var3", "int ${var3} = ${var4};"),
+            SymbolNode("var4", "int ${var4} = 1;"),
+            SymbolNode("var5", "int ${var5} = 2;"),
+        ])
 
-            return SymbolNode(
-                name=name, definition_node_constructor=constructor)
-
-        root.add_template_vars({
-            'var1':
-            make_symbol('var1', "int var1 = ${var2} + ${var3};"),
-            'var2':
-            make_symbol('var2', "int var2 = ${var5};"),
-            'var3':
-            make_symbol('var3', "int var3 = ${var4};"),
-            'var4':
-            make_symbol('var4', "int var4 = 1;"),
-            'var5':
-            make_symbol('var5', "int var5 = 2;"),
-        })
-
-        root.append(SimpleNode(template_text="(void)${var1};"))
+        root.append(TextNode("(void)${var1};"))
 
         self.assertRenderResult(
             root, """
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/example.py b/third_party/blink/renderer/bindings/scripts/bind_gen/example.py
index f33dd24..b1bf4f3 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/example.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/example.py
@@ -15,25 +15,17 @@
     filename = 'v8_example.cc'
     filepath = os.path.join(output_dirs['core'], filename)
 
-    root_node = code_node.SequenceNode(renderer=renderer)
+    root_node = code_node.SymbolScopeNode(renderer=renderer)
     root_node.extend([
-        code_node.SimpleNode(template_text="${z} = ${x} + ${y};"),
-        code_node.SimpleNode(template_text="print ${z};"),
+        code_node.TextNode("${z} = ${x} + ${y};"),
+        code_node.TextNode("print ${z};"),
     ])
 
-    def make_symbol(name, template_text):
-        def constructor(symbol_node):
-            return code_node.SymbolDefinitionNode(
-                symbol_node, template_text=template_text)
-
-        return code_node.SymbolNode(
-            name=name, definition_node_constructor=constructor)
-
-    root_node.add_template_vars({
-        'x': make_symbol('x', "int ${x} = 1;"),
-        'y': make_symbol('y', "int ${y} = 2;"),
-        'z': make_symbol('z', "int ${z};"),
-    })
+    root_node.register_code_symbols([
+        code_node.SymbolNode('x', "int ${x} = 1;"),
+        code_node.SymbolNode('y', "int ${y} = 2;"),
+        code_node.SymbolNode('z', "int ${z};"),
+    ])
 
     prev = ''
     current = str(root_node)
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/mako_renderer.py b/third_party/blink/renderer/bindings/scripts/bind_gen/mako_renderer.py
index 793dde1..a9f6b05 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/mako_renderer.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/mako_renderer.py
@@ -36,17 +36,17 @@
             caller: An object to be pushed onto the call stack.
         """
 
-        assert not (template_path is None and template_text is None)
-        assert not (template_path and template_text)
+        assert template_path is not None or template_text is not None
+        assert template_path is None or template_text is None
         assert isinstance(template_vars, dict)
         assert caller is not None
 
         self._caller_stack.append(caller)
 
         try:
-            if template_path:
+            if template_path is not None:
                 template = self._template_lookup.get_template(template_path)
-            elif template_text:
+            elif template_text is not None:
                 template = mako.template.Template(text=template_text)
             text = template.render(**template_vars)
         finally:
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index e7e5834..a47c7811 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -6,7 +6,6 @@
 import("//build/config/compiler/compiler.gni")
 import("//build/config/dcheck_always_on.gni")
 import("//build/config/ui.gni")
-import("//build/split_static_library.gni")
 import("//build/toolchain/toolchain.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
 import("//third_party/blink/renderer/bindings/bindings.gni")
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc
index 44e37c6..5d561f8 100644
--- a/third_party/blink/renderer/core/animation/animation.cc
+++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -344,9 +344,9 @@
     if (!hold_time_ || hold_time_ != new_current_time)
       outdated = true;
     hold_time_ = new_current_time;
-    if (paused_ || !playback_rate_) {
+    if (paused_) {
       start_time_ = base::nullopt;
-    } else if (is_limited && !start_time_ &&
+    } else if (is_limited && !start_time_ && playback_rate_ &&
                reason == kTimingUpdateForAnimationFrame) {
       start_time_ = CalculateStartTime(new_current_time);
     }
@@ -762,11 +762,7 @@
   //    * both the start time of animation is unresolved and it does not have a
   //      pending play task,
   //    then paused.
-  // TODO(crbug.com/958433): Presently using a paused_ flag that tracks an
-  // animation being in a paused state (including a pending pause). The above
-  // rules do not yet work verbatim due to subtle timing discrepancies on when
-  // start_time gets resolved.
-  if (paused_)
+  if (pending_pause_ || (!start_time_ && !pending_play_))
     return kPaused;
 
   // 3.  For animation, current time is resolved and either of the following
diff --git a/third_party/blink/renderer/core/animation/animation_test.cc b/third_party/blink/renderer/core/animation/animation_test.cc
index 05a43554..291be88 100644
--- a/third_party/blink/renderer/core/animation/animation_test.cc
+++ b/third_party/blink/renderer/core/animation/animation_test.cc
@@ -437,7 +437,8 @@
   animation->setPlaybackRate(0);
   EXPECT_EQ("running", animation->playState());
   SimulateAwaitReady();
-  EXPECT_FALSE(animation->startTime());
+  EXPECT_EQ("running", animation->playState());
+  EXPECT_EQ(0, animation->startTime());
 
   SimulateFrame(10000);
   EXPECT_EQ("running", animation->playState());
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index 6da3c4c..bda1f9c 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -156,6 +156,7 @@
     "css_reflect_value.h",
     "css_reflection_direction.h",
     "css_resolution_units.h",
+    "css_resource_fetch_restriction.h",
     "css_rule.cc",
     "css_rule.h",
     "css_rule_list.h",
diff --git a/third_party/blink/renderer/core/css/css_resource_fetch_restriction.h b/third_party/blink/renderer/core/css/css_resource_fetch_restriction.h
new file mode 100644
index 0000000..c55a92a9
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_resource_fetch_restriction.h
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_RESOURCE_FETCH_RESTRICTION_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_RESOURCE_FETCH_RESTRICTION_H_
+
+namespace blink {
+
+enum class ResourceFetchRestriction { kNone, kOnlyDataUrls };
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_RESOURCE_FETCH_RESTRICTION_H_
diff --git a/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc b/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc
index ef7d851..e98cef6 100644
--- a/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc
+++ b/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc
@@ -303,8 +303,9 @@
     // styles but we do it. If we ever stop doing that then this code and the
     // PushInvalidationSetsForContainerNode above need to move out of the
     // if-block.
-    if (InvalidatesSlotted() && IsHTMLSlotElement(element))
-      InvalidateSlotDistributedElements(ToHTMLSlotElement(element));
+    auto* html_slot_element = DynamicTo<HTMLSlotElement>(element);
+    if (html_slot_element && InvalidatesSlotted())
+      InvalidateSlotDistributedElements(*html_slot_element);
 
     if (InsertionPointCrossing() && element.IsV0InsertionPoint()) {
       element.SetNeedsStyleRecalc(kSubtreeStyleChange,
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_context.cc b/third_party/blink/renderer/core/css/parser/css_parser_context.cc
index b802a2d..2c9766d 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_context.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_context.cc
@@ -46,7 +46,8 @@
                        other->use_legacy_background_size_shorthand_behavior_,
                        other->secure_context_mode_,
                        other->should_check_content_security_policy_,
-                       use_counter_document) {}
+                       use_counter_document,
+                       other->resource_fetch_restriction_) {}
 
 CSSParserContext::CSSParserContext(
     const CSSParserContext* other,
@@ -67,7 +68,8 @@
           other->use_legacy_background_size_shorthand_behavior_,
           other->secure_context_mode_,
           other->should_check_content_security_policy_,
-          use_counter_document) {}
+          use_counter_document,
+          other->resource_fetch_restriction_) {}
 
 CSSParserContext::CSSParserContext(CSSParserMode mode,
                                    SecureContextMode secure_context_mode,
@@ -84,7 +86,8 @@
                        false,
                        secure_context_mode,
                        kDoNotCheckContentSecurityPolicy,
-                       use_counter_document) {}
+                       use_counter_document,
+                       ResourceFetchRestriction::kNone) {}
 
 CSSParserContext::CSSParserContext(const Document& document)
     : CSSParserContext(document,
@@ -100,7 +103,8 @@
     bool origin_clean,
     network::mojom::ReferrerPolicy referrer_policy_override,
     const WTF::TextEncoding& charset,
-    SelectorProfile profile)
+    SelectorProfile profile,
+    enum ResourceFetchRestriction resource_fetch_restriction)
     : CSSParserContext(
           base_url_override,
           origin_clean,
@@ -123,7 +127,8 @@
           ContentSecurityPolicy::ShouldBypassMainWorld(&document)
               ? kDoNotCheckContentSecurityPolicy
               : kCheckContentSecurityPolicy,
-          &document) {}
+          &document,
+          resource_fetch_restriction) {}
 
 CSSParserContext::CSSParserContext(const ExecutionContext& context)
     : CSSParserContext(context.Url(),
@@ -140,7 +145,8 @@
                        ContentSecurityPolicy::ShouldBypassMainWorld(&context)
                            ? kDoNotCheckContentSecurityPolicy
                            : kCheckContentSecurityPolicy,
-                       DynamicTo<Document>(context)) {}
+                       DynamicTo<Document>(context),
+                       ResourceFetchRestriction::kNone) {}
 
 CSSParserContext::CSSParserContext(
     const KURL& base_url,
@@ -154,7 +160,8 @@
     bool use_legacy_background_size_shorthand_behavior,
     SecureContextMode secure_context_mode,
     ContentSecurityPolicyDisposition policy_disposition,
-    const Document* use_counter_document)
+    const Document* use_counter_document,
+    enum ResourceFetchRestriction resource_fetch_restriction)
     : base_url_(base_url),
       should_check_content_security_policy_(policy_disposition),
       origin_clean_(origin_clean),
@@ -167,7 +174,8 @@
           use_legacy_background_size_shorthand_behavior),
       secure_context_mode_(secure_context_mode),
       charset_(charset),
-      document_(use_counter_document) {}
+      document_(use_counter_document),
+      resource_fetch_restriction_(resource_fetch_restriction) {}
 
 bool CSSParserContext::operator==(const CSSParserContext& other) const {
   return base_url_ == other.base_url_ && origin_clean_ == other.origin_clean_ &&
@@ -176,7 +184,8 @@
          is_html_document_ == other.is_html_document_ &&
          use_legacy_background_size_shorthand_behavior_ ==
              other.use_legacy_background_size_shorthand_behavior_ &&
-         secure_context_mode_ == other.secure_context_mode_;
+         secure_context_mode_ == other.secure_context_mode_ &&
+         resource_fetch_restriction_ == other.resource_fetch_restriction_;
 }
 
 const CSSParserContext* StrictCSSParserContext(
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_context.h b/third_party/blink/renderer/core/css/parser/css_parser_context.h
index 824421e6..17f30d7 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_context.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_context.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
+#include "third_party/blink/renderer/core/css/css_resource_fetch_restriction.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_mode.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/web_feature_forward.h"
@@ -55,7 +56,9 @@
                    bool origin_clean,
                    network::mojom::ReferrerPolicy referrer_policy_override,
                    const WTF::TextEncoding& charset = WTF::TextEncoding(),
-                   SelectorProfile = kLiveProfile);
+                   SelectorProfile = kLiveProfile,
+                   ResourceFetchRestriction resource_fetch_restriction =
+                       ResourceFetchRestriction::kNone);
 
   // This is used for workers, where we don't have a document.
   CSSParserContext(const ExecutionContext& context);
@@ -71,7 +74,8 @@
                    bool use_legacy_background_size_shorthand_behavior,
                    SecureContextMode,
                    ContentSecurityPolicyDisposition,
-                   const Document* use_counter_document);
+                   const Document* use_counter_document,
+                   ResourceFetchRestriction resource_fetch_restriction);
 
   bool operator==(const CSSParserContext&) const;
   bool operator!=(const CSSParserContext& other) const {
@@ -84,6 +88,9 @@
   const WTF::TextEncoding& Charset() const { return charset_; }
   const Referrer& GetReferrer() const { return referrer_; }
   bool IsHTMLDocument() const { return is_html_document_; }
+  enum ResourceFetchRestriction ResourceFetchRestriction() const {
+    return resource_fetch_restriction_;
+  }
   bool IsLiveProfile() const { return profile_ == kLiveProfile; }
 
   bool IsOriginClean() const;
@@ -148,6 +155,10 @@
   WTF::TextEncoding charset_;
 
   WeakMember<const Document> document_;
+
+  // Flag indicating whether images with a URL scheme other than "data" are
+  // allowed.
+  const enum ResourceFetchRestriction resource_fetch_restriction_;
 };
 
 CORE_EXPORT const CSSParserContext* StrictCSSParserContext(SecureContextMode);
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 2ca537f6..19fd2ee 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -392,12 +392,48 @@
   }
 }
 
+void StyleEngine::AddTextTrack(TextTrack* text_track) {
+  text_tracks_.insert(text_track);
+}
+
+void StyleEngine::RemoveTextTrack(TextTrack* text_track) {
+  text_tracks_.erase(text_track);
+}
+
+void StyleEngine::MediaQueryAffectingValueChanged(
+    HeapHashSet<Member<TextTrack>>& text_tracks) {
+  if (text_tracks.IsEmpty())
+    return;
+
+  for (auto text_track : text_tracks) {
+    bool style_needs_recalc = false;
+    auto style_sheets = text_track->GetCSSStyleSheets();
+    for (const auto& sheet : style_sheets) {
+      StyleSheetContents* contents = sheet->Contents();
+      if (contents->HasMediaQueries()) {
+        style_needs_recalc = true;
+        contents->ClearRuleSet();
+      }
+    }
+
+    if (style_needs_recalc) {
+      // Use kSubtreeTreeStyleChange instead of RuleSet style invalidation
+      // because it won't be expensive for tracks and we won't have dynamic
+      // changes.
+      text_track->Owner()->SetNeedsStyleRecalc(
+          kSubtreeStyleChange,
+          StyleChangeReasonForTracing::Create(style_change_reason::kShadow));
+    }
+  }
+}
+
 void StyleEngine::MediaQueryAffectingValueChanged() {
   if (ClearMediaQueryDependentRuleSets(active_user_style_sheets_))
     MarkUserStyleDirty();
   if (GetDocumentStyleSheetCollection().MediaQueryAffectingValueChanged())
     SetNeedsActiveStyleUpdate(GetDocument());
   MediaQueryAffectingValueChanged(active_tree_scopes_);
+  MediaQueryAffectingValueChanged(text_tracks_);
   if (resolver_)
     resolver_->UpdateMediaType();
 }
@@ -1196,8 +1232,9 @@
   Element* element = ElementTraversal::FirstChild(*stay_within);
   while (element) {
     ScheduleRuleSetInvalidationsForElement(*element, rule_sets);
-    if (invalidate_slotted && IsHTMLSlotElement(element))
-      InvalidateSlottedElements(ToHTMLSlotElement(*element));
+    auto* html_slot_element = DynamicTo<HTMLSlotElement>(element);
+    if (html_slot_element && invalidate_slotted)
+      InvalidateSlottedElements(*html_slot_element);
 
     if (invalidation_scope == kInvalidateAllScopes) {
       if (ShadowRoot* shadow_root = element->GetShadowRoot()) {
@@ -2017,6 +2054,7 @@
   visitor->Trace(sheet_to_text_cache_);
   visitor->Trace(tracker_);
   visitor->Trace(meta_color_scheme_);
+  visitor->Trace(text_tracks_);
   FontSelectorClient::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 200011e..bbca1d3 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -49,6 +49,7 @@
 #include "third_party/blink/renderer/core/css/style_recalc_root.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/tree_ordered_list.h"
+#include "third_party/blink/renderer/core/html/track/text_track.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
 #include "third_party/blink/renderer/platform/fonts/font_selector_client.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -110,6 +111,9 @@
 
   CSSStyleSheet* InspectorStyleSheet() const { return inspector_style_sheet_; }
 
+  void AddTextTrack(TextTrack*);
+  void RemoveTextTrack(TextTrack*);
+
   const ActiveStyleSheetVector ActiveStyleSheetsForInspector();
 
   bool NeedsActiveStyleUpdate() const;
@@ -395,6 +399,7 @@
   typedef HeapHashSet<Member<TreeScope>> UnorderedTreeScopeSet;
 
   void MediaQueryAffectingValueChanged(UnorderedTreeScopeSet&);
+  void MediaQueryAffectingValueChanged(HeapHashSet<Member<TextTrack>>&);
   const RuleFeatureSet& GetRuleFeatureSet() const {
     DCHECK(IsMaster());
     DCHECK(global_rule_set_);
@@ -575,6 +580,8 @@
   friend class NodeTest;
   friend class StyleEngineTest;
   friend class WhitespaceAttacherTest;
+
+  HeapHashSet<Member<TextTrack>> text_tracks_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index f1c4bcd..ca1c90a 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -471,6 +471,9 @@
 
   layout_object->SetNeedsLayoutAndPrefWidthsRecalc(
       layout_invalidation_reason::kDisplayLock);
+
+  if (auto* view = layout_object->GetFrameView())
+    view->SetNeedsForcedResizeObservations();
 }
 
 void DisplayLockContext::StartUpdateIfNeeded() {
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc b/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
index 88e3fa6..7de9a7d 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
@@ -100,7 +100,8 @@
       elements_to_activate.insert(nearest_locked_ancestor);
   }
   for (Element* element : elements_to_activate) {
-    element->ActivateDisplayLockIfNeeded(DisplayLockActivationReason::kUser);
+    element->ActivateDisplayLockIfNeeded(
+        DisplayLockActivationReason::kViewport);
   }
   return !elements_to_activate.IsEmpty();
 }
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index bb739d7..6a7a98f1 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -1392,7 +1392,7 @@
   DCHECK(!NeedsReattachLayoutTree());
 
   if (IsActiveSlotOrActiveV0InsertionPoint()) {
-    if (auto* slot = ToHTMLSlotElementOrNull(this)) {
+    if (auto* slot = DynamicTo<HTMLSlotElement>(this)) {
       slot->RebuildDistributedChildrenLayoutTrees(whitespace_attacher);
     } else {
       To<V0InsertionPoint>(this)->RebuildDistributedChildrenLayoutTrees(
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 6f03eb1f..24b28a86 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -4041,7 +4041,12 @@
       return;
     }
   }
-  ActivateDisplayLockIfNeeded(DisplayLockActivationReason::kUser);
+  // If script called focus(), then the type would be none. This means we are
+  // activating because of a script action (kUser). Otherwise, this is a
+  // viewport activation (kViewport).
+  ActivateDisplayLockIfNeeded(params.type == kWebFocusTypeNone
+                                  ? DisplayLockActivationReason::kUser
+                                  : DisplayLockActivationReason::kViewport);
   DispatchActivateInvisibleEventIfNeeded();
   if (IsInsideInvisibleSubtree()) {
     // The element stays invisible because the default event action is
@@ -4195,7 +4200,7 @@
          ((SupportsFocus() && tabIndex() >= 0) ||
           (RuntimeEnabledFeatures::KeyboardFocusableScrollersEnabled() &&
            IsScrollableNode(this))) &&
-         !DisplayLockPreventsActivation(DisplayLockActivationReason::kUser);
+         !DisplayLockPreventsActivation(DisplayLockActivationReason::kViewport);
 }
 
 bool Element::IsMouseFocusable() const {
@@ -4204,7 +4209,7 @@
   DCHECK(!GetDocument().IsActive() ||
          !GetDocument().NeedsLayoutTreeUpdateForNode(*this));
   return isConnected() && !IsInert() && IsFocusableStyle() && SupportsFocus() &&
-         !DisplayLockPreventsActivation(DisplayLockActivationReason::kUser);
+         !DisplayLockPreventsActivation(DisplayLockActivationReason::kViewport);
 }
 
 bool Element::IsAutofocusable() const {
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index d73f2f7..fc0f084 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -802,6 +802,7 @@
   virtual bool IsOutOfRange() const { return false; }
   virtual bool IsClearButtonElement() const { return false; }
   virtual bool IsScriptElement() const { return false; }
+  virtual bool IsVTTCueBackgroundBox() const { return false; }
 
   // Elements that may have an insertion mode other than "in body" should
   // override this and return true.
diff --git a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
index 24ce6bf..630a7c1 100644
--- a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
+++ b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
@@ -346,8 +346,9 @@
   // Call default event handlers. While the DOM does have a concept of
   // preventing default handling, the detail of which handlers are called is an
   // internal implementation detail and not part of the DOM.
-  if (!event_->defaultPrevented() && !event_->DefaultHandled() &&
-      is_trusted_or_click) {
+  if (event_->defaultPrevented()) {
+    node_->DidPreventDefault(*event_);
+  } else if (!event_->DefaultHandled() && is_trusted_or_click) {
     // Non-bubbling events call only one default event handler, the one for the
     // target.
     node_->WillCallDefaultEventHandler(*event_);
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index c7bebcbd..de7ab12 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -3119,7 +3119,7 @@
       slot->DidSlotChange(slot_change_type);
   } else if (IsInV1ShadowTree()) {
     // Checking for fallback content if the node is in a v1 shadow tree.
-    if (auto* parent_slot = ToHTMLSlotElementOrNull(parentElement())) {
+    if (auto* parent_slot = DynamicTo<HTMLSlotElement>(parentElement())) {
       DCHECK(parent_slot->SupportsAssignment());
       // The parent_slot's assigned nodes might not be calculated because they
       // are lazy evaluated later at UpdateDistribution() so we have to check it
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index 3772c430..d1c1b97 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -819,6 +819,10 @@
   }
   virtual void PostDispatchEventHandler(Event&, EventDispatchHandlingState*) {}
 
+  // TODO(crbug.com/1013385): Remove DidPreventDefault. It is here as a
+  //   temporary fix for form double-submit.
+  virtual void DidPreventDefault(const Event&) {}
+
   void DispatchScopedEvent(Event&);
 
   virtual void HandleLocalEvents(Event&);
diff --git a/third_party/blink/renderer/core/dom/shadow_root_v0.cc b/third_party/blink/renderer/core/dom/shadow_root_v0.cc
index c4cf987..55c50953e 100644
--- a/third_party/blink/renderer/core/dom/shadow_root_v0.cc
+++ b/third_party/blink/renderer/core/dom/shadow_root_v0.cc
@@ -65,7 +65,7 @@
   Clear();
   for (Node* child = parent.firstChild(); child; child = child->nextSibling()) {
     // Re-distribution across v0 and v1 shadow trees is not supported
-    if (IsHTMLSlotElement(child))
+    if (IsA<HTMLSlotElement>(child))
       continue;
 
     if (IsActiveV0InsertionPoint(*child)) {
diff --git a/third_party/blink/renderer/core/dom/slot_assignment.cc b/third_party/blink/renderer/core/dom/slot_assignment.cc
index 5ae4e837..d8dbcc1b 100644
--- a/third_party/blink/renderer/core/dom/slot_assignment.cc
+++ b/third_party/blink/renderer/core/dom/slot_assignment.cc
@@ -358,7 +358,7 @@
     const AtomicString& slot_name) {
   if (Element* slot =
           slot_map_->GetCachedFirstElementWithoutAccessingNodeTree(slot_name)) {
-    return ToHTMLSlotElement(slot);
+    return To<HTMLSlotElement>(slot);
   }
   return nullptr;
 }
diff --git a/third_party/blink/renderer/core/dom/slot_assignment_test.cc b/third_party/blink/renderer/core/dom/slot_assignment_test.cc
index 39054a0..db21a59 100644
--- a/third_party/blink/renderer/core/dom/slot_assignment_test.cc
+++ b/third_party/blink/renderer/core/dom/slot_assignment_test.cc
@@ -155,8 +155,7 @@
   Element* host = GetDocument().QuerySelector("#host");
   Element* host_child = GetDocument().QuerySelector("#host-child");
   ShadowRoot* shadow_root = host->OpenShadowRoot();
-  HTMLSlotElement* slot =
-      ToHTMLSlotElementOrNull(shadow_root->QuerySelector("slot"));
+  auto* slot = DynamicTo<HTMLSlotElement>(shadow_root->QuerySelector("slot"));
   ASSERT_NE(nullptr, slot);
 
   EXPECT_EQ(slot, host_child->AssignedSlot());
diff --git a/third_party/blink/renderer/core/dom/tree_ordered_map.cc b/third_party/blink/renderer/core/dom/tree_ordered_map.cc
index d01c5b44..0c426cfb 100644
--- a/third_party/blink/renderer/core/dom/tree_ordered_map.cc
+++ b/third_party/blink/renderer/core/dom/tree_ordered_map.cc
@@ -67,8 +67,8 @@
 
 inline bool KeyMatchesSlotName(const AtomicString& key,
                                const Element& element) {
-  return IsHTMLSlotElement(element) &&
-         ToHTMLSlotElement(element).GetName() == key;
+  auto* html_slot_element = DynamicTo<HTMLSlotElement>(element);
+  return html_slot_element && html_slot_element->GetName() == key;
 }
 
 void TreeOrderedMap::Add(const AtomicString& key, Element& element) {
@@ -188,7 +188,7 @@
 HTMLSlotElement* TreeOrderedMap::GetSlotByName(const AtomicString& key,
                                                const TreeScope& scope) const {
   if (Element* slot = Get<KeyMatchesSlotName>(key, scope))
-    return ToHTMLSlotElement(slot);
+    return To<HTMLSlotElement>(slot);
   return nullptr;
 }
 
diff --git a/third_party/blink/renderer/core/dom/tree_scope.cc b/third_party/blink/renderer/core/dom/tree_scope.cc
index ac7b798..dfd9b4f5 100644
--- a/third_party/blink/renderer/core/dom/tree_scope.cc
+++ b/third_party/blink/renderer/core/dom/tree_scope.cc
@@ -279,7 +279,7 @@
     return false;
   // In some cases the hit test doesn't return slot elements, so we can only
   // get it through its child and can't skip it.
-  if (IsHTMLSlotElement(*parent))
+  if (IsA<HTMLSlotElement>(*parent))
     return true;
   // SVG text content elements has no background, and are thus not
   // hit during the background phase of hit-testing. Because of that
diff --git a/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc b/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc
index 4bd70fd..c7d74ca 100644
--- a/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc
@@ -1503,7 +1503,7 @@
                              kDoNotAnnotateForInterchange,
                              ConvertBlocksToInlines::kConvert,
                              kDoNotResolveURLs, constraining_ancestor),
-                "")
+                "", kDisallowScriptingAndPluginContent)
           : nullptr;
 
   // A non-empty paragraph's style is moved when we copy and move it.  We don't
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index e6958a5..d65002cf 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -494,7 +494,7 @@
     bool has_transient_activation,
     WebFrameLoadType frame_load_type,
     bool is_client_redirect,
-    WebTriggeringEventInfo triggering_event_info,
+    TriggeringEventInfo triggering_event_info,
     HTMLFormElement* form,
     ContentSecurityPolicyDisposition
         should_check_main_world_content_security_policy,
@@ -1007,7 +1007,7 @@
 
 void LocalFrameClientImpl::SuddenTerminationDisablerChanged(
     bool present,
-    WebSuddenTerminationDisablerType type) {
+    SuddenTerminationDisablerType type) {
   if (web_frame_->Client()) {
     web_frame_->Client()->SuddenTerminationDisablerChanged(present, type);
   }
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.h b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
index c9a2e7a..eca82e31 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
@@ -124,7 +124,7 @@
       bool has_transient_activation,
       WebFrameLoadType,
       bool is_client_redirect,
-      WebTriggeringEventInfo,
+      TriggeringEventInfo,
       HTMLFormElement*,
       ContentSecurityPolicyDisposition should_bypass_main_world_csp,
       mojo::PendingRemote<mojom::blink::BlobURLToken>,
@@ -231,9 +231,8 @@
 
   unsigned BackForwardLength() override;
 
-  void SuddenTerminationDisablerChanged(
-      bool present,
-      WebSuddenTerminationDisablerType) override;
+  void SuddenTerminationDisablerChanged(bool present,
+                                        SuddenTerminationDisablerType) override;
 
   BlameContext* GetFrameBlameContext() override;
 
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 f7a4868..4c25b573 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -7112,8 +7112,7 @@
       MouseEvent::Create(nullptr, event_type_names::kClick, mouse_initializer);
   FrameLoadRequest frame_request(document, ResourceRequest(destination));
   frame_request.SetNavigationPolicy(NavigationPolicyFromEvent(event));
-  frame_request.SetTriggeringEventInfo(
-      WebTriggeringEventInfo::kFromTrustedEvent);
+  frame_request.SetTriggeringEventInfo(TriggeringEventInfo::kFromTrustedEvent);
   std::unique_ptr<UserGestureIndicator> gesture =
       LocalFrame::NotifyUserActivation(frame);
   web_frame_client.IgnoreNavigations();
diff --git a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
index 31222c6..fb9ebfd 100644
--- a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
@@ -153,11 +153,6 @@
   self_keep_alive_.Clear();
 }
 
-WebRect WebRemoteFrameImpl::VisibleContentRect() const {
-  NOTREACHED();
-  return WebRect();
-}
-
 WebView* WebRemoteFrameImpl::View() const {
   if (!GetFrame()) {
     return nullptr;
diff --git a/third_party/blink/renderer/core/exported/web_remote_frame_impl.h b/third_party/blink/renderer/core/exported/web_remote_frame_impl.h
index 843a8fe..c908607 100644
--- a/third_party/blink/renderer/core/exported/web_remote_frame_impl.h
+++ b/third_party/blink/renderer/core/exported/web_remote_frame_impl.h
@@ -44,7 +44,6 @@
 
   // WebFrame methods:
   void Close() override;
-  WebRect VisibleContentRect() const override;
   WebView* View() const override;
   void StopLoading() override;
 
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index e910319..9cd3e52 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -119,7 +119,7 @@
 static void UpdateSuddenTerminationStatus(
     LocalDOMWindow* dom_window,
     bool added_listener,
-    WebSuddenTerminationDisablerType disabler_type) {
+    SuddenTerminationDisablerType disabler_type) {
   Platform::Current()->SuddenTerminationChanged(!added_listener);
   if (dom_window->GetFrame() && dom_window->GetFrame()->Client())
     dom_window->GetFrame()->Client()->SuddenTerminationDisablerChanged(
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index 598e7073..0fa9c50f 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -39,6 +39,8 @@
 #include "services/network/public/mojom/ip_address_space.mojom-blink-forward.h"
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/common/loader/loading_behavior_flag.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
+#include "third_party/blink/public/common/sudden_termination_disabler_type.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/mojom/frame/navigation_initiator.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/portal/portal.mojom-blink-forward.h"
@@ -48,14 +50,12 @@
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "third_party/blink/public/platform/web_effective_connection_type.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
-#include "third_party/blink/public/platform/web_sudden_termination_disabler_type.h"
 #include "third_party/blink/public/platform/web_worker_fetch_context.h"
 #include "third_party/blink/public/web/web_frame_load_type.h"
 #include "third_party/blink/public/web/web_history_commit_type.h"
 #include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/public/web/web_manifest_manager.h"
 #include "third_party/blink/public/web/web_navigation_params.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/icon_url.h"
@@ -163,7 +163,7 @@
       bool has_transient_activation,
       WebFrameLoadType,
       bool is_client_redirect,
-      WebTriggeringEventInfo,
+      TriggeringEventInfo,
       HTMLFormElement*,
       ContentSecurityPolicyDisposition
           should_check_main_world_content_security_policy,
@@ -369,9 +369,9 @@
 
   virtual bool IsLocalFrameClientImpl() const { return false; }
 
-  virtual void SuddenTerminationDisablerChanged(
-      bool present,
-      WebSuddenTerminationDisablerType) {}
+  virtual void SuddenTerminationDisablerChanged(bool present,
+                                                SuddenTerminationDisablerType) {
+  }
 
   // Overwrites the given URL to use an HTML5 embed if possible. An empty URL is
   // returned if the URL is not overriden.
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 58d776a..b108116e 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1362,10 +1362,10 @@
                                         bool should_scroll) {
   // We want to create the anchor even if we don't need to scroll. This ensures
   // all the side effects like setting CSS :target are correctly set.
-  FragmentAnchor* anchor =
-      FragmentAnchor::TryCreate(url, *frame_, same_document_navigation);
+  FragmentAnchor* anchor = FragmentAnchor::TryCreate(
+      url, *frame_, same_document_navigation, should_scroll);
 
-  if (anchor && should_scroll) {
+  if (anchor) {
     fragment_anchor_ = anchor;
     fragment_anchor_->Installed();
 
@@ -2093,6 +2093,12 @@
   // phase of this cycle.
 }
 
+void LocalFrameView::SetNeedsForcedResizeObservations() {
+  if (auto* controller =
+          GetFrame().GetDocument()->GetResizeObserverController())
+    controller->SetNeedsForcedResizeObservations();
+}
+
 void LocalFrameView::NotifyResizeObservers() {
   TRACE_EVENT0("blink,benchmark", "LocalFrameView::NotifyResizeObservers");
   // Controller exists only if ResizeObserver was created.
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 1685421e..638ba2a 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -686,6 +686,10 @@
   void RegisterForLifecycleNotifications(LifecycleNotificationObserver*);
   void UnregisterFromLifecycleNotifications(LifecycleNotificationObserver*);
 
+  // Sets whether all ResizeObservers should check all their ResizeObservations
+  // for a resize. This is needed when exiting a display lock.
+  void SetNeedsForcedResizeObservations();
+
  protected:
   void FrameRectsChanged(const IntRect&) override;
   void SelfVisibleChanged() override;
diff --git a/third_party/blink/renderer/core/frame/mhtml_archive_test.cc b/third_party/blink/renderer/core/frame/mhtml_archive_test.cc
index 2ad126c..87d5b91b6 100644
--- a/third_party/blink/renderer/core/frame/mhtml_archive_test.cc
+++ b/third_party/blink/renderer/core/frame/mhtml_archive_test.cc
@@ -28,10 +28,12 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include "third_party/blink/renderer/platform/mhtml/mhtml_archive.h"
+
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/mhtml/mhtml_archive.h"
+#include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-blink.h"
 #include "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
 #include "third_party/blink/renderer/platform/mhtml/serialized_resource.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
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 72585dc..c9dbed9 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
@@ -93,24 +93,67 @@
     : public GarbageCollected<WebLocalFrameImpl>,
       public WebNavigationControl {
  public:
-  // WebFrame methods:
-  // TODO(dcheng): Fix sorting here; a number of method have been moved to
-  // WebLocalFrame but not correctly updated here.
+  // WebFrame overrides:
   void Close() override;
+  WebView* View() const override;
+  v8::Local<v8::Object> GlobalProxy() const override;
+  void StopLoading() override;
+  bool IsLoading() const override;
+
+  // WebLocalFrame overrides:
+  WebLocalFrameImpl* CreateLocalChild(WebTreeScopeType,
+                                      WebLocalFrameClient*,
+                                      blink::InterfaceRegistry*,
+                                      mojo::ScopedMessagePipeHandle) override;
+  WebLocalFrameClient* Client() const override { return client_; }
+  void SetAutofillClient(WebAutofillClient*) override;
+  WebAutofillClient* AutofillClient() override;
+  void SetContentCaptureClient(WebContentCaptureClient*) override;
+  WebContentCaptureClient* ContentCaptureClient() const override;
+  void DispatchUnloadEvent() override;
+  WebVector<WebIconURL> IconURLs(int icon_types_mask) const override;
+  WebDocument GetDocument() const override;
   WebString AssignedName() const override;
   void SetName(const WebString&) override;
-  WebVector<WebIconURL> IconURLs(int icon_types_mask) const override;
-  WebSize GetScrollOffset() const override;
-  void SetScrollOffset(const WebSize&) override;
-  WebSize DocumentSize() const override;
-  bool HasVisibleContent() const override;
-  WebRect VisibleContentRect() const override;
-  WebView* View() const override;
-  WebDocument GetDocument() const override;
-  WebPerformance Performance() const override;
-  bool IsAdSubframe() const override;
-  void SetIsAdSubframe(blink::mojom::AdFrameType ad_frame_type) override;
-  void DispatchUnloadEvent() override;
+  void NotifyUserActivation() override;
+  bool IsLocalRoot() const override;
+  bool IsProvisional() const override;
+  WebLocalFrameImpl* LocalRoot() override;
+  WebFrameWidget* FrameWidget() const override;
+  WebFrame* FindFrameByName(const WebString& name) override;
+  bool ScrollTo(const gfx::Point& scrollPosition,
+                bool animate,
+                base::OnceClosure on_finish) override;
+  void SendPings(const WebURL& destination_url) override;
+  void StartReload(WebFrameLoadType) override;
+  void StartNavigation(const WebURLRequest&) override;
+  void EnableViewSourceMode(bool enable) override;
+  bool IsViewSourceModeEnabled() const override;
+  WebDocumentLoader* GetDocumentLoader() const override;
+  void ReportContentSecurityPolicyViolation(
+      const blink::WebContentSecurityPolicyViolation&) override;
+  void SetReferrerForRequest(WebURLRequest&, const WebURL& referrer) override;
+  bool IsNavigationScheduledWithin(base::TimeDelta interval) const override;
+  void BlinkFeatureUsageReport(
+      const std::set<blink::mojom::WebFeature>& features) override;
+  void BlinkFeatureUsageReport(blink::mojom::WebFeature feature) override;
+  void MixedContentFound(const WebURL& main_resource_url,
+                         const WebURL& mixed_content_url,
+                         mojom::RequestContextType,
+                         bool was_allowed,
+                         bool had_redirect,
+                         const WebSourceLocation&) override;
+  void SendOrientationChangeEvent() override;
+  bool IsPageBoxVisible(int page_index) override;
+  bool HasCustomPageSizeStyle(int page_index) override;
+  void PageSizeAndMarginsInPixels(int page_index,
+                                  WebDoubleSize& page_size,
+                                  int& margin_top,
+                                  int& margin_right,
+                                  int& margin_bottom,
+                                  int& margin_left) override;
+  WebString PageProperty(const WebString& property_name,
+                         int page_index) override;
   void ExecuteScript(const WebScriptSource&) override;
   void ExecuteScriptInIsolatedWorld(int32_t world_id,
                                     const WebScriptSource&) override;
@@ -120,14 +163,14 @@
   void ClearIsolatedWorldCSPForTesting(int32_t world_id) override;
   void SetIsolatedWorldInfo(int32_t world_id,
                             const WebIsolatedWorldInfo&) override;
-  void Alert(const WebString& message) override;
-  bool Confirm(const WebString& message) override;
-  WebString Prompt(const WebString& message,
-                   const WebString& default_value) override;
-
-  void CollectGarbageForTesting();
   v8::Local<v8::Value> ExecuteScriptAndReturnValue(
       const WebScriptSource&) override;
+  v8::MaybeLocal<v8::Value> CallFunctionEvenIfScriptDisabled(
+      v8::Local<v8::Function>,
+      v8::Local<v8::Value>,
+      int argc,
+      v8::Local<v8::Value> argv[]) override;
+  v8::Local<v8::Context> MainWorldScriptContext() const override;
   void RequestExecuteScriptAndReturnValue(const WebScriptSource&,
                                           bool user_gesture,
                                           WebScriptExecutionCallback*) override;
@@ -144,24 +187,10 @@
       bool user_gesture,
       ScriptExecutionType,
       WebScriptExecutionCallback*) override;
-  v8::MaybeLocal<v8::Value> CallFunctionEvenIfScriptDisabled(
-      v8::Local<v8::Function>,
-      v8::Local<v8::Value>,
-      int argc,
-      v8::Local<v8::Value> argv[]) override;
-  v8::Local<v8::Context> MainWorldScriptContext() const override;
-  v8::Local<v8::Object> GlobalProxy() const override;
-  void StartReload(WebFrameLoadType) override;
-  void ReloadImage(const WebNode&) override;
-  void StartNavigation(const WebURLRequest&) override;
-  void CheckCompleted() override;
-  void StopLoading() override;
-  WebDocumentLoader* GetDocumentLoader() const override;
-  void EnableViewSourceMode(bool enable) override;
-  bool IsViewSourceModeEnabled() const override;
-  void SetReferrerForRequest(WebURLRequest&, const WebURL& referrer) override;
-  WebAssociatedURLLoader* CreateAssociatedURLLoader(
-      const WebAssociatedURLLoaderOptions&) override;
+  void Alert(const WebString& message) override;
+  bool Confirm(const WebString& message) override;
+  WebString Prompt(const WebString& message,
+                   const WebString& default_value) override;
   void BindDevToolsAgent(
       mojo::ScopedInterfaceEndpointHandle devtools_agent_host_ptr_info,
       mojo::ScopedInterfaceEndpointHandle devtools_agent_request) override;
@@ -182,13 +211,6 @@
                               WebTextDirection& end) const override;
   bool IsSelectionAnchorFirst() const override;
   void SetTextDirection(WebTextDirection) override;
-  void SetTextCheckClient(WebTextCheckClient*) override;
-  void SetSpellCheckPanelHostClient(WebSpellCheckPanelHostClient*) override;
-  void ReplaceMisspelledRange(const WebString&) override;
-  void RemoveSpellingMarkers() override;
-  void RemoveSpellingMarkersUnderWords(
-      const WebVector<WebString>& words) override;
-  void SetContentSettingsClient(WebContentSettingsClient*) override;
   bool HasSelection() const override;
   WebRange SelectionRange() const override;
   WebString SelectionAsText() const override;
@@ -199,7 +221,6 @@
                    HandleVisibilityBehavior,
                    blink::mojom::SelectionMenuBehavior) override;
   WebString RangeAsText(const WebRange&) override;
-  void MoveRangeSelectionExtent(const WebPoint&) override;
   void MoveRangeSelection(
       const WebPoint& base,
       const WebPoint& extent,
@@ -211,78 +232,30 @@
       int composition_end,
       const WebVector<WebImeTextSpan>& ime_text_spans) override;
   void ExtendSelectionAndDelete(int before, int after) override;
+  void SetCaretVisible(bool) override;
+  void MoveRangeSelectionExtent(const WebPoint&) override;
+  void ReplaceSelection(const WebString&) override;
   void DeleteSurroundingText(int before, int after) override;
   void DeleteSurroundingTextInCodePoints(int before, int after) override;
-  void SetCaretVisible(bool) override;
-  void DispatchBeforePrintEvent() override;
-  int PrintBegin(const WebPrintParams&,
-                 const WebNode& constrain_to_node) override;
-  float PrintPage(int page_to_print, cc::PaintCanvas*) override;
-  float GetPrintPageShrink(int page) override;
-  void PrintEnd() override;
-  void DispatchAfterPrintEvent() override;
-  bool GetPrintPresetOptionsForPlugin(const WebNode&,
-                                      WebPrintPresetOptions*) override;
-  bool HasCustomPageSizeStyle(int page_index) override;
-  bool IsPageBoxVisible(int page_index) override;
-  void PageSizeAndMarginsInPixels(int page_index,
-                                  WebDoubleSize& page_size,
-                                  int& margin_top,
-                                  int& margin_right,
-                                  int& margin_bottom,
-                                  int& margin_left) override;
-  WebString PageProperty(const WebString& property_name,
-                         int page_index) override;
-  void PrintPagesForTesting(cc::PaintCanvas*, const WebSize&) override;
-
-  void DispatchMessageEventWithOriginCheck(
-      const WebSecurityOrigin& intended_target_origin,
-      const WebDOMMessageEvent&) override;
-
-  WebRect GetSelectionBoundsRectForTesting() const override;
-
-  WebString GetLayerTreeAsTextForTesting(
-      bool show_debug_info = false) const override;
-
-  WebLocalFrameClient* Client() const override { return client_; }
-
-  // WebLocalFrame methods:
-  WebLocalFrameImpl* CreateLocalChild(WebTreeScopeType,
-                                      WebLocalFrameClient*,
-                                      blink::InterfaceRegistry*,
-                                      mojo::ScopedMessagePipeHandle) override;
-  void SetAutofillClient(WebAutofillClient*) override;
-  WebAutofillClient* AutofillClient() override;
-  void SetContentCaptureClient(WebContentCaptureClient*) override;
-  WebContentCaptureClient* ContentCaptureClient() const override;
-  bool IsLocalRoot() const override;
-  bool IsProvisional() const override;
-  WebLocalFrameImpl* LocalRoot() override;
-  WebFrame* FindFrameByName(const WebString& name) override;
-  bool ScrollTo(const gfx::Point& scrollPosition,
-                bool animate,
-                base::OnceClosure on_finish) override;
-  void SendPings(const WebURL& destination_url) override;
-  void ReportContentSecurityPolicyViolation(
-      const blink::WebContentSecurityPolicyViolation&) override;
-  bool IsLoading() const override;
-  bool IsNavigationScheduledWithin(base::TimeDelta interval) const override;
-  void NotifyUserActivation() override;
-  void BlinkFeatureUsageReport(
-      const std::set<blink::mojom::WebFeature>& features) override;
-  void BlinkFeatureUsageReport(blink::mojom::WebFeature feature) override;
-  void MixedContentFound(const WebURL& main_resource_url,
-                         const WebURL& mixed_content_url,
-                         mojom::RequestContextType,
-                         bool was_allowed,
-                         bool had_redirect,
-                         const WebSourceLocation&) override;
-  void SendOrientationChangeEvent() override;
-  WebSandboxFlags EffectiveSandboxFlagsForTesting() const override;
-  bool IsAllowedToDownloadWithoutUserActivation() const override;
+  void ExtractSmartClipData(WebRect rect_in_viewport,
+                            WebString& clip_text,
+                            WebString& clip_html,
+                            WebRect& clip_rect) override;
+  void SetTextCheckClient(WebTextCheckClient*) override;
+  void SetSpellCheckPanelHostClient(WebSpellCheckPanelHostClient*) override;
+  WebSpellCheckPanelHostClient* SpellCheckPanelHostClient() const override {
+    return spell_check_panel_host_client_;
+  }
+  void ReplaceMisspelledRange(const WebString&) override;
+  void RemoveSpellingMarkers() override;
+  void RemoveSpellingMarkersUnderWords(
+      const WebVector<WebString>& words) override;
+  void SetContentSettingsClient(WebContentSettingsClient*) override;
+  void ReloadImage(const WebNode&) override;
   void DidCallAddSearchProvider() override;
   void DidCallIsSearchProviderInstalled() override;
-  void ReplaceSelection(const WebString&) override;
+  WebSandboxFlags EffectiveSandboxFlagsForTesting() const override;
+  bool IsAllowedToDownloadWithoutUserActivation() const override;
   bool FindForTesting(int identifier,
                       const WebString& search_text,
                       bool match_case,
@@ -292,21 +265,12 @@
                       bool wrap_within_frame) override;
   void SetTickmarks(const WebVector<WebRect>&) override;
   WebNode ContextMenuNode() const override;
-  WebFrameWidget* FrameWidget() const override;
   void CopyImageAt(const WebPoint&) override;
   void SaveImageAt(const WebPoint&) override;
+  void DispatchMessageEventWithOriginCheck(
+      const WebSecurityOrigin& intended_target_origin,
+      const WebDOMMessageEvent&) override;
   void UsageCountChromeLoadTimes(const WebString& metric) override;
-  FrameScheduler* Scheduler() const override;
-  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override;
-  WebInputMethodController* GetInputMethodController() override;
-  void ExtractSmartClipData(WebRect rect_in_viewport,
-                            WebString& clip_text,
-                            WebString& clip_html,
-                            WebRect& clip_rect) override;
-  void AdvanceFocusInForm(WebFocusType) override;
-  bool ShouldSuppressKeyboardForFocusedElement() override;
-  void PerformMediaPlayerAction(const WebPoint&,
-                                const WebMediaPlayerAction&) override;
   void OnPortalActivated(const base::UnguessableToken& portal_token,
                          mojo::ScopedInterfaceEndpointHandle portal_pipe,
                          mojo::ScopedInterfaceEndpointHandle portal_client_pipe,
@@ -316,10 +280,45 @@
       TransferableMessage message,
       const WebSecurityOrigin& source_origin,
       const base::Optional<WebSecurityOrigin>& target_origin) override;
-  void AddMessageToConsoleImpl(const WebConsoleMessage&,
-                               bool discard_duplicates) override;
+  FrameScheduler* Scheduler() const override;
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override;
+  WebInputMethodController* GetInputMethodController() override;
+  WebAssociatedURLLoader* CreateAssociatedURLLoader(
+      const WebAssociatedURLLoaderOptions&) override;
+  void CheckCompleted() override;
+  WebSize GetScrollOffset() const override;
+  void SetScrollOffset(const WebSize&) override;
+  WebSize DocumentSize() const override;
+  bool HasVisibleContent() const override;
+  WebRect VisibleContentRect() const override;
+  void DispatchBeforePrintEvent() override;
+  int PrintBegin(const WebPrintParams&,
+                 const WebNode& constrain_to_node) override;
+  float GetPrintPageShrink(int page) override;
+  float PrintPage(int page_to_print, cc::PaintCanvas*) override;
+  void PrintEnd() override;
+  void DispatchAfterPrintEvent() override;
+  bool GetPrintPresetOptionsForPlugin(const WebNode&,
+                                      WebPrintPresetOptions*) override;
+  void AdvanceFocusInForm(WebFocusType) override;
+  bool ShouldSuppressKeyboardForFocusedElement() override;
+  WebPerformance Performance() const override;
+  bool IsAdSubframe() const override;
+  void SetIsAdSubframe(blink::mojom::AdFrameType ad_frame_type) override;
+  WebString GetLayerTreeAsTextForTesting(
+      bool show_debug_info = false) const override;
+  void PrintPagesForTesting(cc::PaintCanvas*, const WebSize&) override;
+  WebRect GetSelectionBoundsRectForTesting() const override;
+  void PerformMediaPlayerAction(const WebPoint&,
+                                const WebMediaPlayerAction&) override;
+  void SetLifecycleState(mojom::FrameLifecycleState state) override;
+  void WasHidden() override;
+  void WasShown() override;
+  void SetAllowsCrossBrowsingInstanceFrameLookup() override;
 
-  // WebNavigationControl methods:
+  void CollectGarbageForTesting();
+
+  // WebNavigationControl overrides:
   bool DispatchBeforeUnloadEvent(bool) override;
   void CommitNavigation(
       std::unique_ptr<WebNavigationParams> navigation_params,
@@ -337,16 +336,12 @@
   void RenderFallbackContent() const override;
   void SetCommittedFirstRealLoad() override;
   bool HasCommittedFirstRealLoad() override;
-  void DidDropNavigation() override;
-  void MarkAsLoading() override;
-  bool IsClientNavigationInitialHistoryLoad() override;
   bool WillStartNavigation(
       const WebNavigationInfo&,
       bool is_history_navigation_in_new_child_frame) override;
-
-  void SetLifecycleState(mojom::FrameLifecycleState state) override;
-  void WasHidden() override;
-  void WasShown() override;
+  void DidDropNavigation() override;
+  void MarkAsLoading() override;
+  bool IsClientNavigationInitialHistoryLoad() override;
 
   void InitializeCoreFrame(
       Page&,
@@ -433,10 +428,6 @@
     return text_check_client_;
   }
 
-  WebSpellCheckPanelHostClient* SpellCheckPanelHostClient() const override {
-    return spell_check_panel_host_client_;
-  }
-
   FindInPage* GetFindInPage() const { return find_in_page_; }
 
   TextFinder* GetTextFinder() const;
@@ -458,7 +449,10 @@
 
   virtual void Trace(blink::Visitor*);
 
-  void SetAllowsCrossBrowsingInstanceFrameLookup() override;
+ protected:
+  // WebLocalFrame protected overrides:
+  void AddMessageToConsoleImpl(const WebConsoleMessage&,
+                               bool discard_duplicates) override;
 
  private:
   friend LocalFrameClientImpl;
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
index 9f30981..4923001 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
@@ -116,10 +116,6 @@
   StartListeningForDidProcessTask();
 }
 
-void CanvasRenderingContext::NeedsFinalizeFrame() {
-  StartListeningForDidProcessTask();
-}
-
 void CanvasRenderingContext::DidProcessTask(
     const base::PendingTask& /* pending_task */) {
   StopListeningForDidProcessTask();
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
index 165e8f9..14497e1 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
@@ -134,7 +134,6 @@
   // the contents of the canvas (called didDraw). It marks the completion
   // of a presentable frame.
   virtual void FinalizeFrame() {}
-  void NeedsFinalizeFrame();
 
   // Thread::TaskObserver implementation
   void DidProcessTask(const base::PendingTask&) override;
diff --git a/third_party/blink/renderer/core/html/forms/html_button_element.cc b/third_party/blink/renderer/core/html/forms/html_button_element.cc
index 9295436..5e2afac3 100644
--- a/third_party/blink/renderer/core/html/forms/html_button_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_button_element.cc
@@ -27,6 +27,7 @@
 
 #include "third_party/blink/renderer/core/dom/attribute.h"
 #include "third_party/blink/renderer/core/events/keyboard_event.h"
+#include "third_party/blink/renderer/core/events/mouse_event.h"
 #include "third_party/blink/renderer/core/html/forms/form_data.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
 #include "third_party/blink/renderer/core/html_names.h"
@@ -109,6 +110,13 @@
 }
 
 void HTMLButtonElement::DefaultEventHandler(Event& event) {
+  DefaultEventHandlerInternal(event);
+
+  if (event.type() == event_type_names::kDOMActivate && formOwner())
+    formOwner()->DidActivateSubmitButton(this);
+}
+
+void HTMLButtonElement::DefaultEventHandlerInternal(Event& event) {
   if (event.type() == event_type_names::kDOMActivate &&
       !IsDisabledFormControl()) {
     if (Form() && type_ == SUBMIT) {
@@ -219,4 +227,16 @@
   return request;
 }
 
+EventDispatchHandlingState* HTMLButtonElement::PreDispatchEventHandler(
+    Event& event) {
+  if (Form() && CanBeSuccessfulSubmitButton())
+    Form()->WillActivateSubmitButton(this);
+  return nullptr;
+}
+
+void HTMLButtonElement::DidPreventDefault(const Event& event) {
+  if (auto* form = formOwner())
+    form->DidActivateSubmitButton(this);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/html_button_element.h b/third_party/blink/renderer/core/html/forms/html_button_element.h
index e1685752..0aaf454 100644
--- a/third_party/blink/renderer/core/html/forms/html_button_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_button_element.h
@@ -78,6 +78,13 @@
   bool IsOptionalFormControl() const override { return true; }
   bool RecalcWillValidate() const override;
 
+  // TODO(crbug.com/1013385): Remove PreDispatchEventHandler, DidPreventDefault,
+  //   and DefaultEventHandlerInternal. They are here to temporarily fix form
+  //   double-submit.
+  EventDispatchHandlingState* PreDispatchEventHandler(Event&) override;
+  void DidPreventDefault(const Event&) final;
+  void DefaultEventHandlerInternal(Event&);
+
   Type type_;
   bool is_activated_submit_;
 };
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc
index 1c89d714..2340e4bc 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -94,6 +94,7 @@
   visitor->Trace(listed_elements_);
   visitor->Trace(image_elements_);
   visitor->Trace(planned_navigation_);
+  visitor->Trace(activated_submit_button_);
   HTMLElement::Trace(visitor);
 }
 
@@ -331,10 +332,27 @@
     planned_navigation_ = nullptr;
     Submit(event, submit_button);
   }
+  if (!planned_navigation_ || activated_submit_button_)
+    return;
+  base::AutoReset<bool> submit_scope(&is_submitting_, true);
+  SubmitForm(planned_navigation_);
+  planned_navigation_ = nullptr;
+}
+
+void HTMLFormElement::WillActivateSubmitButton(
+    HTMLFormControlElement* element) {
+  if (!activated_submit_button_)
+    activated_submit_button_ = element;
+}
+
+void HTMLFormElement::DidActivateSubmitButton(HTMLFormControlElement* element) {
+  if (activated_submit_button_ != element)
+    return;
+  activated_submit_button_ = nullptr;
   if (!planned_navigation_)
     return;
   base::AutoReset<bool> submit_scope(&is_submitting_, true);
-  ScheduleFormSubmission(planned_navigation_);
+  SubmitForm(planned_navigation_);
   planned_navigation_ = nullptr;
 }
 
@@ -449,14 +467,14 @@
   }
   if (form_submission->Method() == FormSubmission::kDialogMethod) {
     SubmitDialog(form_submission);
-  } else if (in_user_js_submit_event_) {
+  } else if (in_user_js_submit_event_ || activated_submit_button_) {
     // Need to postpone the submission in order to make this cancelable by
     // another submission request.
     planned_navigation_ = form_submission;
   } else {
     // This runs JavaScript code if action attribute value is javascript:
     // protocol.
-    ScheduleFormSubmission(form_submission);
+    SubmitForm(form_submission);
   }
 }
 
@@ -489,13 +507,16 @@
   return &form_data;
 }
 
-void HTMLFormElement::ScheduleFormSubmission(FormSubmission* submission) {
+// Actually submit the form - navigate now.
+void HTMLFormElement::SubmitForm(FormSubmission* submission) {
   DCHECK(submission->Method() == FormSubmission::kPostMethod ||
          submission->Method() == FormSubmission::kGetMethod);
   DCHECK(submission->Data());
   DCHECK(submission->Form());
   if (submission->Action().IsEmpty())
     return;
+  if (!GetDocument().IsActive())
+    return;
   if (GetDocument().IsSandboxed(WebSandboxFlags::kForms)) {
     // FIXME: This message should be moved off the console once a solution to
     // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
@@ -598,6 +619,11 @@
   listed_elements_are_dirty_ = true;
   listed_elements_.clear();
   RemoveFromPastNamesMap(e.ToHTMLElement());
+
+  if (activated_submit_button_ != &e)
+    return;
+  activated_submit_button_ = nullptr;
+  planned_navigation_ = nullptr;
 }
 
 bool HTMLFormElement::IsURLAttribute(const Attribute& attribute) const {
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.h b/third_party/blink/renderer/core/html/forms/html_form_element.h
index eaab0fd48..ae78e4f5 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.h
@@ -118,6 +118,12 @@
 
   unsigned UniqueRendererFormId() const { return unique_renderer_form_id_; }
 
+  // TODO(crbug.com/1013385): Remove WillActivateSubmitButton,
+  //   DidActivateSubmitButton, and RemovedAssociatedControlElement. They are
+  //   here temporarily to fix form double-submit.
+  void WillActivateSubmitButton(HTMLFormControlElement* element);
+  void DidActivateSubmitButton(HTMLFormControlElement* element);
+
  private:
   InsertionNotificationRequest InsertedInto(ContainerNode&) override;
   void RemovedFrom(ContainerNode&) override;
@@ -136,7 +142,7 @@
   void SubmitDialog(FormSubmission*);
   void Submit(Event*, HTMLFormControlElement* submit_button);
 
-  void ScheduleFormSubmission(FormSubmission*);
+  void SubmitForm(FormSubmission*);
 
   void CollectListedElements(Node& root, ListedElement::List&) const;
   void CollectImageElements(Node& root, HeapVector<Member<HTMLImageElement>>&);
@@ -182,6 +188,8 @@
   bool has_elements_associated_by_form_attribute_ : 1;
   bool did_finish_parsing_children_ : 1;
   bool is_in_reset_function_ : 1;
+
+  Member<HTMLFormControlElement> activated_submit_button_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.cc b/third_party/blink/renderer/core/html/forms/html_input_element.cc
index 4f2db6f..107d2dc 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.cc
@@ -1282,6 +1282,8 @@
       ToMouseEvent(event).button() !=
           static_cast<int16_t>(WebPointerProperties::Button::kLeft))
     return nullptr;
+  if (formOwner() && CanBeSuccessfulSubmitButton())
+    formOwner()->WillActivateSubmitButton(this);
   return input_type_view_->WillDispatchClick();
 }
 
@@ -1294,7 +1296,19 @@
                                      *static_cast<ClickHandlingState*>(state));
 }
 
-void HTMLInputElement::DefaultEventHandler(Event& evt) {
+void HTMLInputElement::DidPreventDefault(const Event& event) {
+  if (auto* form = formOwner())
+    form->DidActivateSubmitButton(this);
+}
+
+void HTMLInputElement::DefaultEventHandler(Event& event) {
+  DefaultEventHandlerInternal(event);
+
+  if (event.type() == event_type_names::kDOMActivate && formOwner())
+    formOwner()->DidActivateSubmitButton(this);
+}
+
+void HTMLInputElement::DefaultEventHandlerInternal(Event& evt) {
   if (evt.IsMouseEvent() && evt.type() == event_type_names::kClick &&
       ToMouseEvent(evt).button() ==
           static_cast<int16_t>(WebPointerProperties::Button::kLeft)) {
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.h b/third_party/blink/renderer/core/html/forms/html_input_element.h
index e814d2e..0795e67 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.h
@@ -372,6 +372,11 @@
 
   EventDispatchHandlingState* PreDispatchEventHandler(Event&) final;
   void PostDispatchEventHandler(Event&, EventDispatchHandlingState*) final;
+  // TODO(crbug.com/1013385): Remove DidPreventDefault and
+  //   DefaultEventHandlerInternal. They are here as a temporary fix for form
+  //   double-submit.
+  void DidPreventDefault(const Event&) final;
+  void DefaultEventHandlerInternal(Event& evt);
 
   bool IsURLAttribute(const Attribute&) const final;
   bool HasLegalLinkAttribute(const QualifiedName&) const final;
diff --git a/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css b/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css
index a943dbb..dfa50b7 100644
--- a/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css
+++ b/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css
@@ -126,6 +126,10 @@
 }
 
 @media (forced-colors: active) {
+    .controls-refresh .color-suggestion-picker-main {
+        border: 1px solid WindowText;
+    }
+
     .controls-refresh .color-swatch {
         forced-color-adjust: none;
         border-color: WindowText;
diff --git a/third_party/blink/renderer/core/html/forms/resources/color_picker.css b/third_party/blink/renderer/core/html/forms/resources/color_picker.css
index dd930e2..ed9cc009 100644
--- a/third_party/blink/renderer/core/html/forms/resources/color_picker.css
+++ b/third_party/blink/renderer/core/html/forms/resources/color_picker.css
@@ -199,10 +199,15 @@
   }
   color-well {
     border-bottom: 1px solid WindowText;
+    border-radius: 0;
   }
   color-selection-ring {
     forced-color-adjust: none;
   }
+  color-picker {
+    border: 1px solid WindowText;
+    border-radius: 2px;
+  }
   format-toggler {
     border: 1px solid WindowText;
   }
diff --git a/third_party/blink/renderer/core/html/forms/resources/color_picker.js b/third_party/blink/renderer/core/html/forms/resources/color_picker.js
index 108e4da4..83717cc 100644
--- a/third_party/blink/renderer/core/html/forms/resources/color_picker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/color_picker.js
@@ -874,7 +874,17 @@
    * @param {number} y
    */
   hslImageDataAtPoint_(x, y) {
-    const offset = Math.round(y * this.width + x) * 3;
+    let offset = Math.round(y * this.width + x) * 3;
+    // It is possible that the computed offset is larger than the hslImageData
+    // array's length. This can happen at certain zoom levels (ex. 150%), where
+    // the height of the color well is not a round number. The getImageData API
+    // only works with integer values and will truncate decimal values. As
+    // such, if the color well's selection ring is placed at the bottom of the
+    // color well at such a zoom level, a valid data point for the ring's
+    // position will not be found in the hslImageData array. When this happens,
+    // we just report the color at the end of the hslImageData array. This will
+    // be the same color that is seen at the bottom of the color well (black).
+    offset = Math.min(offset, this.hslImageData.length - 3);
     return this.hslImageData.slice(offset, offset + 3);
   }
 
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index 2a47c9e0..a284d4bb 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -455,8 +455,8 @@
                       WebFeature::kHTMLAnchorElementHrefTranslateAttribute);
   }
   frame_request.SetTriggeringEventInfo(
-      event.isTrusted() ? WebTriggeringEventInfo::kFromTrustedEvent
-                        : WebTriggeringEventInfo::kFromUntrustedEvent);
+      event.isTrusted() ? TriggeringEventInfo::kFromTrustedEvent
+                        : TriggeringEventInfo::kFromUntrustedEvent);
   frame_request.SetInputStartTime(event.PlatformTimeStamp());
 
   frame->MaybeLogAdClickNavigation();
diff --git a/third_party/blink/renderer/core/html/html_meter_element.cc b/third_party/blink/renderer/core/html/html_meter_element.cc
index fb366609..be1ba8f1 100644
--- a/third_party/blink/renderer/core/html/html_meter_element.cc
+++ b/third_party/blink/renderer/core/html/html_meter_element.cc
@@ -182,7 +182,15 @@
 
   value_ = MakeGarbageCollected<HTMLDivElement>(GetDocument());
   UpdateValueAppearance(0);
-  bar->AppendChild(value_);
+
+  if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) {
+    auto* clip = MakeGarbageCollected<HTMLDivElement>(GetDocument());
+    clip->SetShadowPseudoId(AtomicString("-internal-meter-clip"));
+    bar->AppendChild(clip);
+    clip->AppendChild(value_);
+  } else {
+    bar->AppendChild(value_);
+  }
 
   inner->AppendChild(bar);
 
diff --git a/third_party/blink/renderer/core/html/html_object_element_test.cc b/third_party/blink/renderer/core/html/html_object_element_test.cc
index 541fd72f..a36f03b 100644
--- a/third_party/blink/renderer/core/html/html_object_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_object_element_test.cc
@@ -42,7 +42,7 @@
   object->RenderFallbackContent(nullptr);
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
   GetDocument().GetStyleEngine().RecalcStyle();
-  EXPECT_TRUE(IsHTMLSlotElement(slot));
+  EXPECT_TRUE(IsA<HTMLSlotElement>(slot));
   EXPECT_TRUE(object->UseFallbackContent());
   EXPECT_TRUE(object->GetComputedStyle());
   EXPECT_TRUE(slot->GetComputedStyle());
diff --git a/third_party/blink/renderer/core/html/html_slot_element.h b/third_party/blink/renderer/core/html/html_slot_element.h
index 0b75211..42c9f0e 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.h
+++ b/third_party/blink/renderer/core/html/html_slot_element.h
@@ -202,7 +202,7 @@
 
 inline const HTMLSlotElement* ToHTMLSlotElementIfSupportsAssignmentOrNull(
     const Node& node) {
-  if (auto* slot = ToHTMLSlotElementOrNull(node)) {
+  if (auto* slot = DynamicTo<HTMLSlotElement>(node)) {
     if (slot->SupportsAssignment())
       return slot;
   }
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index e20f303e..c513a75 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -1462,6 +1462,12 @@
   if (track->TrackType() == TextTrack::kTrackElement)
     track->SetHasBeenConfigured(true);
 
+  if (track->IsRendered()) {
+    GetDocument().GetStyleEngine().AddTextTrack(track);
+  } else {
+    GetDocument().GetStyleEngine().RemoveTextTrack(track);
+  }
+
   ConfigureTextTrackDisplay();
 
   DCHECK(textTracks()->Contains(track));
diff --git a/third_party/blink/renderer/core/html/resources/controls_refresh.css b/third_party/blink/renderer/core/html/resources/controls_refresh.css
index 2f2209e..98b3f6c 100644
--- a/third_party/blink/renderer/core/html/resources/controls_refresh.css
+++ b/third_party/blink/renderer/core/html/resources/controls_refresh.css
@@ -92,19 +92,21 @@
   box-sizing: border-box;
 }
 
+meter::-internal-meter-clip {
+  clip-path: inset(0px round 20px);
+  height: 100%;
+}
+
 meter::-webkit-meter-optimum-value {
   background: #107c10;
-  border-radius: 20px 0px 0px 20px;
 }
 
 meter::-webkit-meter-suboptimum-value {
   background: #ffb900;
-  border-radius: 20px 0px 0px 20px;
 }
 
 meter::-webkit-meter-even-less-good-value {
   background: #d83b01;
-  border-radius: 20px 0px 0px 20px;
 }
 
 input[type="date" i]::-webkit-calendar-picker-indicator,
diff --git a/third_party/blink/renderer/core/html/track/html_track_element.cc b/third_party/blink/renderer/core/html/track/html_track_element.cc
index 66efed10d..36fb6fc 100644
--- a/third_party/blink/renderer/core/html/track/html_track_element.cc
+++ b/third_party/blink/renderer/core/html/track/html_track_element.cc
@@ -26,6 +26,7 @@
 #include "third_party/blink/renderer/core/html/track/html_track_element.h"
 
 #include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/renderer/core/css/css_style_sheet.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
@@ -190,7 +191,7 @@
     return;
 
   if (track_)
-    track_->RemoveAllCues();
+    track_->Reset();
 
   url_ = url;
 
@@ -287,6 +288,13 @@
   HeapVector<Member<TextTrackCue>> new_cues;
   loader_->GetNewCues(new_cues);
 
+  HeapVector<Member<CSSStyleSheet>> new_sheets;
+  loader_->GetNewStyleSheets(new_sheets);
+
+  if (!new_sheets.IsEmpty()) {
+    track_->SetCSSStyleSheets(std::move(new_sheets));
+  }
+
   track_->AddListOfCues(new_cues);
 }
 
diff --git a/third_party/blink/renderer/core/html/track/text_track.cc b/third_party/blink/renderer/core/html/track/text_track.cc
index d2cb695e..703706b3 100644
--- a/third_party/blink/renderer/core/html/track/text_track.cc
+++ b/third_party/blink/renderer/core/html/track/text_track.cc
@@ -165,7 +165,7 @@
   return nullptr;
 }
 
-void TextTrack::RemoveAllCues() {
+void TextTrack::Reset() {
   if (!cues_)
     return;
 
@@ -178,6 +178,8 @@
   cues_->RemoveAll();
   if (active_cues_)
     active_cues_->RemoveAll();
+
+  style_sheets_.clear();
 }
 
 void TextTrack::AddListOfCues(
@@ -239,6 +241,12 @@
     GetCueTimeline()->AddCue(this, cue);
 }
 
+void TextTrack::SetCSSStyleSheets(
+    HeapVector<Member<CSSStyleSheet>> style_sheets) {
+  DCHECK(style_sheets_.IsEmpty());
+  style_sheets_ = std::move(style_sheets);
+}
+
 void TextTrack::removeCue(TextTrackCue* cue, ExceptionState& exception_state) {
   DCHECK(cue);
 
@@ -372,6 +380,7 @@
   visitor->Trace(cues_);
   visitor->Trace(active_cues_);
   visitor->Trace(track_list_);
+  visitor->Trace(style_sheets_);
   TrackBase::Trace(visitor);
   EventTargetWithInlineData::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/core/html/track/text_track.h b/third_party/blink/renderer/core/html/track/text_track.h
index 1abd4c2..d8e3bc5 100644
--- a/third_party/blink/renderer/core/html/track/text_track.h
+++ b/third_party/blink/renderer/core/html/track/text_track.h
@@ -28,6 +28,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_TRACK_TEXT_TRACK_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/css/css_style_sheet.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/html/track/track_base.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -126,7 +127,7 @@
 
   virtual bool IsDefault() const { return false; }
 
-  void RemoveAllCues();
+  void Reset();
 
   // EventTarget methods
   const AtomicString& InterfaceName() const override;
@@ -134,6 +135,12 @@
 
   void Trace(Visitor*) override;
 
+  const HeapVector<Member<CSSStyleSheet>>& GetCSSStyleSheets() const {
+    return style_sheets_;
+  }
+
+  void SetCSSStyleSheets(HeapVector<Member<CSSStyleSheet>>);
+
  protected:
   void AddListOfCues(HeapVector<Member<TextTrackCue>>&);
 
@@ -143,6 +150,7 @@
   TextTrackCueList* EnsureTextTrackCueList();
   Member<TextTrackCueList> cues_;
   Member<TextTrackCueList> active_cues_;
+  HeapVector<Member<CSSStyleSheet>> style_sheets_;
 
   Member<TextTrackList> track_list_;
   AtomicString mode_;
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_cue.cc b/third_party/blink/renderer/core/html/track/vtt/vtt_cue.cc
index 95e245b..2a786fe 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_cue.cc
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_cue.cc
@@ -140,6 +140,20 @@
   SetShadowPseudoId(AtomicString("-webkit-media-text-track-display"));
 }
 
+VTTCueBackgroundBox::VTTCueBackgroundBox(Document& document)
+    : HTMLDivElement(document) {
+  SetShadowPseudoId(TextTrackCue::CueShadowPseudoId());
+}
+
+void VTTCueBackgroundBox::Trace(blink::Visitor* visitor) {
+  visitor->Trace(track_);
+  HTMLDivElement::Trace(visitor);
+}
+
+void VTTCueBackgroundBox::SetTrack(TextTrack* track) {
+  track_ = track;
+}
+
 void VTTCueBox::ApplyCSSProperties(
     const VTTDisplayParameters& display_parameters) {
   // http://dev.w3.org/html5/webvtt/#applying-css-properties-to-webvtt-node-objects
@@ -238,11 +252,10 @@
       writing_direction_(kHorizontal),
       cue_alignment_(kCenter),
       vtt_node_tree_(nullptr),
-      cue_background_box_(MakeGarbageCollected<HTMLDivElement>(document)),
+      cue_background_box_(MakeGarbageCollected<VTTCueBackgroundBox>(document)),
       snap_to_lines_(true),
       display_tree_should_change_(true) {
   UseCounter::Count(document, WebFeature::kVTTCue);
-  cue_background_box_->SetShadowPseudoId(CueShadowPseudoId());
 }
 
 VTTCue::~VTTCue() = default;
@@ -441,9 +454,11 @@
 }
 
 void VTTCue::CreateVTTNodeTree() {
-  if (!vtt_node_tree_)
-    vtt_node_tree_ =
-        VTTParser::CreateDocumentFragmentFromCueText(GetDocument(), text_);
+  if (!vtt_node_tree_) {
+    vtt_node_tree_ = VTTParser::CreateDocumentFragmentFromCueText(
+        GetDocument(), text_, this->track());
+    cue_background_box_->SetTrack(this->track());
+  }
 }
 
 void VTTCue::CopyVTTNodeToDOMTree(ContainerNode* vtt_node,
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_cue.h b/third_party/blink/renderer/core/html/track/vtt/vtt_cue.h
index 2ff8998..fe16a16a 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_cue.h
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_cue.h
@@ -75,6 +75,26 @@
   float snap_to_lines_position_;
 };
 
+class VTTCueBackgroundBox final : public HTMLDivElement {
+ public:
+  explicit VTTCueBackgroundBox(Document&);
+  bool IsVTTCueBackgroundBox() const override { return true; }
+  void SetTrack(TextTrack*);
+  void Trace(Visitor*) override;
+
+  const TextTrack* GetTrack() const { return track_; }
+
+ private:
+  Member<TextTrack> track_;
+};
+
+template <>
+struct DowncastTraits<VTTCueBackgroundBox> {
+  static bool AllowFrom(const Element& element) {
+    return element.IsVTTCueBackgroundBox();
+  }
+};
+
 class VTTCue final : public TextTrackCue {
   DEFINE_WRAPPERTYPEINFO();
 
@@ -192,7 +212,7 @@
 
   Member<VTTRegion> region_;
   Member<DocumentFragment> vtt_node_tree_;
-  Member<HTMLDivElement> cue_background_box_;
+  Member<VTTCueBackgroundBox> cue_background_box_;
   Member<VTTCueBox> display_tree_;
 
   bool snap_to_lines_ : 1;
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_element.cc b/third_party/blink/renderer/core/html/track/vtt/vtt_element.cc
index db766921..90d64e2 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_element.cc
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_element.cc
@@ -76,6 +76,7 @@
   auto* clone = MakeGarbageCollected<VTTElement>(
       static_cast<VTTNodeType>(web_vtt_node_type_), &factory);
   clone->SetLanguage(language_);
+  clone->SetTrack(track_);
   return *clone;
 }
 
@@ -132,4 +133,13 @@
           style_change_reason::kPseudoClass, style_change_extra_data::g_past));
 }
 
+void VTTElement::SetTrack(TextTrack* track) {
+  track_ = track;
+}
+
+void VTTElement::Trace(blink::Visitor* visitor) {
+  visitor->Trace(track_);
+  Element::Trace(visitor);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_element.h b/third_party/blink/renderer/core/html/track/vtt/vtt_element.h
index 250ecd56..25a52b5 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_element.h
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_element.h
@@ -27,6 +27,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_TRACK_VTT_VTT_ELEMENT_H_
 
 #include "third_party/blink/renderer/core/html/html_element.h"
+#include "third_party/blink/renderer/core/html/track/text_track.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
@@ -78,7 +79,13 @@
     return voice_attr;
   }
 
+  const TextTrack* GetTrack() const { return track_; }
+
+  void SetTrack(TextTrack*);
+  void Trace(blink::Visitor*) override;
+
  private:
+  Member<TextTrack> track_;
   unsigned is_past_node_ : 1;
   unsigned web_vtt_node_type_ : 4;
 
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc b/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc
index 4b1220f..d6db892 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc
@@ -30,10 +30,14 @@
 
 #include "third_party/blink/renderer/core/html/track/vtt/vtt_parser.h"
 
+#include "third_party/blink/renderer/core/css/parser/css_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
+#include "third_party/blink/renderer/core/css/style_sheet_contents.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/processing_instruction.h"
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
+#include "third_party/blink/renderer/core/html/track/text_track.h"
 #include "third_party/blink/renderer/core/html/track/vtt/vtt_element.h"
 #include "third_party/blink/renderer/core/html/track/vtt/vtt_region.h"
 #include "third_party/blink/renderer/core/html/track/vtt/vtt_scanner.h"
@@ -52,6 +56,7 @@
 
 const unsigned kFileIdentifierLength = 6;
 const unsigned kRegionIdentifierLength = 6;
+const unsigned kStyleIdentifierLength = 5;
 
 bool VTTParser::ParsePercentageValue(VTTScanner& value_scanner,
                                      double& percentage) {
@@ -103,6 +108,12 @@
   output_cues.swap(cue_list_);
 }
 
+void VTTParser::GetNewStyleSheets(
+    HeapVector<Member<CSSStyleSheet>>& output_sheets) {
+  DCHECK(output_sheets.IsEmpty());
+  output_sheets.swap(style_sheets_);
+}
+
 void VTTParser::ParseBytes(const char* data, size_t length) {
   String text_data = decoder_->Decode(data, length);
   line_reader_.Append(text_data);
@@ -146,6 +157,11 @@
         state_ = CollectRegionSettings(line);
         break;
 
+      case kStyle:
+        // Collect style sheet
+        state_ = CollectStyleSheet(line);
+        break;
+
       case kId:
         // Steps 17 - 20 - Allow any number of line terminators, then initialize
         // new cue values.
@@ -222,13 +238,55 @@
   return kRegion;
 }
 
+VTTParser::ParseState VTTParser::CollectStyleSheet(const String& line) {
+  if (line.IsEmpty() || line.Contains("-->")) {
+    auto* parser_context = MakeGarbageCollected<CSSParserContext>(
+        *document_, NullURL(), true /* origin_clean */,
+        document_->GetReferrerPolicy(), UTF8Encoding(),
+        CSSParserContext::kLiveProfile,
+        ResourceFetchRestriction::kOnlyDataUrls);
+    auto* style_sheet_contents =
+        MakeGarbageCollected<StyleSheetContents>(parser_context);
+    CSSParser::ParseSheet(
+        parser_context, style_sheet_contents, current_content_.ToString(),
+        CSSDeferPropertyParsing::kNo, false /* allow_import_rules */);
+    auto* style_sheet =
+        MakeGarbageCollected<CSSStyleSheet>(style_sheet_contents);
+    style_sheet->SetAssociatedDocument(document_);
+    style_sheet->SetIsConstructed(true);
+    style_sheet->SetTitle("");
+    style_sheets_.push_back(style_sheet);
+
+    return CheckAndRecoverCue(line);
+  }
+
+  if (!current_content_.IsEmpty())
+    current_content_.Append('\n');
+  current_content_.Append(line);
+
+  return kStyle;
+}
+
 VTTParser::ParseState VTTParser::CollectWebVTTBlock(const String& line) {
   // collect a WebVTT block parsing. (WebVTT parser algorithm step 14)
 
-  // If Region support is enabled.
-  if (RuntimeEnabledFeatures::WebVTTRegionsEnabled() &&
-      CheckAndCreateRegion(line))
-    return kRegion;
+  if (!previous_line_.Contains("-->")) {
+    // If Region support is enabled.
+    if (RuntimeEnabledFeatures::WebVTTRegionsEnabled() &&
+        CheckAndCreateRegion(line))
+      return kRegion;
+
+    // line starts with the substring "STYLE" and remaining characters
+    // zero or more U+0020 SPACE characters or U+0009 CHARACTER TABULATION
+    // (tab) characters expected other than these characters it is invalid.
+    if (RuntimeEnabledFeatures::EmbeddedVTTStylesheetsEnabled() &&
+        line.StartsWith("STYLE") &&
+        StringView(line, kStyleIdentifierLength)
+            .IsAllSpecialCharacters<IsASpace>()) {
+      current_content_.Clear();
+      return kStyle;
+    }
+  }
 
   // Handle cue block.
   ParseState state = CheckAndRecoverCue(line);
@@ -260,11 +318,9 @@
 }
 
 bool VTTParser::CheckAndCreateRegion(const String& line) {
-  if (previous_line_.Contains("-->"))
-    return false;
   // line starts with the substring "REGION" and remaining characters
   // zero or more U+0020 SPACE characters or U+0009 CHARACTER TABULATION
-  // (tab) characters expected other than these charecters it is invalid.
+  // (tab) characters expected other than these characters it is invalid.
   if (line.StartsWith("REGION") && StringView(line, kRegionIdentifierLength)
                                        .IsAllSpecialCharacters<IsASpace>()) {
     current_region_ = VTTRegion::Create();
@@ -367,7 +423,8 @@
   STACK_ALLOCATED();
 
  public:
-  explicit VTTTreeBuilder(Document& document) : document_(&document) {}
+  explicit VTTTreeBuilder(Document& document, TextTrack* track)
+      : document_(&document), track_(track) {}
 
   DocumentFragment* BuildFromString(const String& cue_text);
 
@@ -379,6 +436,7 @@
   Member<ContainerNode> current_node_;
   Vector<AtomicString> language_stack_;
   Member<Document> document_;
+  Member<TextTrack> track_;
 };
 
 DocumentFragment* VTTTreeBuilder::BuildFromString(const String& cue_text) {
@@ -406,8 +464,9 @@
 
 DocumentFragment* VTTParser::CreateDocumentFragmentFromCueText(
     Document& document,
-    const String& cue_text) {
-  VTTTreeBuilder tree_builder(document);
+    const String& cue_text,
+    TextTrack* track) {
+  VTTTreeBuilder tree_builder(document, track);
   return tree_builder.BuildFromString(cue_text);
 }
 
@@ -546,6 +605,8 @@
         break;
 
       auto* child = MakeGarbageCollected<VTTElement>(node_type, &document);
+      child->SetTrack(track_);
+
       if (!token_.Classes().IsEmpty())
         child->setAttribute(kClassAttr, token_.Classes());
 
@@ -611,6 +672,7 @@
   visitor->Trace(client_);
   visitor->Trace(cue_list_);
   visitor->Trace(region_map_);
+  visitor->Trace(style_sheets_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_parser.h b/third_party/blink/renderer/core/html/track/vtt/vtt_parser.h
index 0e8adb4..ede0025d 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_parser.h
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_parser.h
@@ -68,7 +68,8 @@
     kTimingsAndSettings,
     kCueText,
     kRegion,
-    kBadCue
+    kBadCue,
+    kStyle
   };
 
   VTTParser(VTTParserClient*, Document&);
@@ -100,7 +101,8 @@
 
   // Create the DocumentFragment representation of the WebVTT cue text.
   static DocumentFragment* CreateDocumentFragmentFromCueText(Document&,
-                                                             const String&);
+                                                             const String&,
+                                                             TextTrack*);
 
   // Input data to the parser to parse.
   void ParseBytes(const char* data, size_t length);
@@ -109,6 +111,9 @@
   // Transfers ownership of last parsed cues to caller.
   void GetNewCues(HeapVector<Member<TextTrackCue>>&);
 
+  // Transfers ownership of last parsed style sheets to caller.
+  void GetNewStyleSheets(HeapVector<Member<CSSStyleSheet>>&);
+
   void Trace(Visitor*);
 
  private:
@@ -126,9 +131,9 @@
   ParseState CollectRegionSettings(const String&);
   ParseState CollectWebVTTBlock(const String&);
   ParseState CheckAndRecoverCue(const String& line);
+  ParseState CollectStyleSheet(const String& line);
   bool CheckAndCreateRegion(const String& line);
   bool CheckAndStoreRegion(const String& line);
-
   void CreateNewCue();
   void ResetCueValues();
 
@@ -144,7 +149,7 @@
   String current_settings_;
   Member<VTTRegion> current_region_;
   Member<VTTParserClient> client_;
-
+  HeapVector<Member<CSSStyleSheet>> style_sheets_;
   HeapVector<Member<TextTrackCue>> cue_list_;
 
   VTTRegionMap region_map_;
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index 22d0b28..5835de3 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -47,7 +47,12 @@
 #include "third_party/blink/renderer/core/editing/editor.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
+#include "third_party/blink/renderer/core/editing/local_caret_rect.h"
 #include "third_party/blink/renderer/core/editing/selection_controller.h"
+#include "third_party/blink/renderer/core/editing/selection_template.h"
+#include "third_party/blink/renderer/core/editing/text_affinity.h"
+#include "third_party/blink/renderer/core/editing/visible_position.h"
+#include "third_party/blink/renderer/core/editing/visible_selection.h"
 #include "third_party/blink/renderer/core/events/gesture_event.h"
 #include "third_party/blink/renderer/core/events/keyboard_event.h"
 #include "third_party/blink/renderer/core/events/mouse_event.h"
@@ -93,6 +98,8 @@
 #include "third_party/blink/renderer/core/style/cursor_data.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
 #include "third_party/blink/renderer/platform/graphics/image.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
@@ -122,6 +129,37 @@
   return false;
 }
 
+IntPoint GetSelectionStartpoint(const PositionWithAffinity& position) {
+  const LocalCaretRect& local_caret_rect = LocalCaretRectOfPosition(position);
+  const IntRect rect = AbsoluteCaretBoundsOf(position);
+  // In a multiline edit, rect.MaxY() would end up on the next line, so
+  // take the midpoint in order to use this corner point directly.
+  if (local_caret_rect.layout_object->IsHorizontalWritingMode())
+    return {rect.X(), (rect.Y() + rect.MaxY()) / 2};
+
+  // When text is vertical, rect.MaxX() would end up on the next line, so
+  // take the midpoint in order to use this corner point directly.
+  return {(rect.X() + rect.MaxX()) / 2, rect.Y()};
+}
+
+IntPoint GetSelectionEndpoint(const PositionWithAffinity& position) {
+  const LocalCaretRect& local_caret_rect = LocalCaretRectOfPosition(position);
+  const IntRect rect = AbsoluteCaretBoundsOf(position);
+  // In a multiline edit, rect.MaxY() would end up on the next line, so
+  // take the midpoint in order to use this corner point directly.
+  if (local_caret_rect.layout_object->IsHorizontalWritingMode())
+    return {rect.X(), (rect.Y() + rect.MaxY()) / 2};
+
+  // When text is vertical, rect.MaxX() would end up on the next line, so
+  // take the midpoint in order to use this corner point directly.
+  return {(rect.X() + rect.MaxX()) / 2, rect.Y()};
+}
+
+bool ContainsEvenAtEdge(const IntRect& rect, const IntPoint& point) {
+  return point.X() >= rect.X() && point.X() <= rect.MaxX() &&
+         point.Y() >= rect.Y() && point.Y() <= rect.MaxY();
+}
+
 }  // namespace
 
 // The amount of time to wait for a cursor update on style and layout changes
@@ -2029,15 +2067,41 @@
   if (!override_target_element && ShouldShowContextMenuAtSelection(selection)) {
     DCHECK(!doc->NeedsLayoutTreeUpdate());
 
-    IntRect first_rect =
-        FirstRectForRange(selection.ComputeVisibleSelectionInDOMTree()
-                              .ToNormalizedEphemeralRange());
+    // Enclose the selection rect fully between the handles. If the handles are
+    // on the same line, the selection rect is empty.
+    const SelectionInDOMTree& visible_selection =
+        selection.ComputeVisibleSelectionInDOMTree().AsSelection();
+    const PositionWithAffinity start_position(
+        visible_selection.ComputeStartPosition(), visible_selection.Affinity());
+    const IntPoint start_point = GetSelectionStartpoint(start_position);
+    const PositionWithAffinity end_position(
+        visible_selection.ComputeEndPosition(), visible_selection.Affinity());
+    const IntPoint end_point = GetSelectionEndpoint(end_position);
 
-    int x = first_rect.X();
-    // In a multiline edit, firstRect.maxY() would end up on the next line, so
-    // take the midpoint.
-    int y = (first_rect.MaxY() + first_rect.Y()) / 2;
-    location_in_root_frame = view->ConvertToRootFrame(IntPoint(x, y));
+    int left = std::min(start_point.X(), end_point.X());
+    int top = std::min(start_point.Y(), end_point.Y());
+    int right = std::max(start_point.X(), end_point.X());
+    int bottom = std::max(start_point.Y(), end_point.Y());
+
+    // Intersect the selection rect and the visible bounds of focused_element.
+    if (focused_element) {
+      IntRect clipped_rect = view->ViewportToFrame(
+          focused_element->VisibleBoundsInVisualViewport());
+      left = std::max(clipped_rect.X(), left);
+      top = std::max(clipped_rect.Y(), top);
+      right = std::min(clipped_rect.MaxX(), right);
+      bottom = std::min(clipped_rect.MaxY(), bottom);
+    }
+    IntRect selection_rect = IntRect(left, top, right - left, bottom - top);
+
+    if (ContainsEvenAtEdge(selection_rect, start_point)) {
+      location_in_root_frame = view->ConvertToRootFrame(start_point);
+    } else if (ContainsEvenAtEdge(selection_rect, end_point)) {
+      location_in_root_frame = view->ConvertToRootFrame(end_point);
+    } else {
+      location_in_root_frame =
+          view->ConvertToRootFrame(selection_rect.Center());
+    }
   } else if (focused_element) {
     IntRect clipped_rect = focused_element->BoundsInViewport();
     location_in_root_frame =
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index c66835d..d43fce5 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -1562,7 +1562,7 @@
           BuildArrayForDistributedNodes(insertion_point));
       force_push_children = true;
     }
-    if (auto* slot = ToHTMLSlotElementOrNull(*element)) {
+    if (auto* slot = DynamicTo<HTMLSlotElement>(*element)) {
       if (node->IsInShadowTree()) {
         value->setDistributedNodes(BuildDistributedNodesForSlot(slot));
         force_push_children = true;
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 7628b902a..7ab8fb87 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
@@ -192,6 +192,11 @@
   if (!root || !root->GetNode() || !root->GetNode()->isConnected() ||
       !root->IsBox())
     return;
+  if (RuntimeEnabledFeatures::
+          IntersectionObserverDocumentScrollingElementRootEnabled() &&
+      root->GetNode() == root->GetDocument().scrollingElement()) {
+    root = root->GetDocument().GetLayoutView();
+  }
   zoom = root->StyleRef().EffectiveZoom();
   local_root_rect = InitializeRootRect(root, margin);
   TransformState transform_state(TransformState::kApplyTransformDirection);
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index d135c40..00137ff 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -37,7 +37,7 @@
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h"
-#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
 #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_outline_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
@@ -55,15 +55,6 @@
 
 namespace {
 
-// TODO(layout-dev): Once we generate fragment for all inline element, we should
-// use |LayoutObject::EnclosingBlockFlowFragment()|.
-const NGPhysicalBoxFragment* ContainingBlockFlowFragmentOf(
-    const LayoutInline& node) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return nullptr;
-  return node.ContainingBlockFlowFragment();
-}
-
 // TODO(xiaochengh): Deduplicate with a similar function in ng_paint_fragment.cc
 // ::before, ::after and ::first-letter can be hit test targets.
 bool CanBeHitTestTargetPseudoNodeStyle(const ComputedStyle& style) {
@@ -787,12 +778,10 @@
 void LayoutInline::CollectLineBoxRects(
     const PhysicalRectCollector& yield) const {
   if (IsInLayoutNGInlineFormattingContext()) {
-    const auto* box_fragment = ContainingBlockFlowFragmentOf(*this);
-    if (!box_fragment)
-      return;
-    for (const auto& fragment :
-         NGInlineFragmentTraversal::SelfFragmentsOf(*box_fragment, this))
-      yield(fragment.RectInContainerBox());
+    NGInlineCursor cursor;
+    cursor.MoveTo(*this);
+    for (; cursor; cursor.MoveToNextForSameLayoutObject())
+      yield(cursor.CurrentRect());
     return;
   }
   if (!AlwaysCreateLineBoxes()) {
@@ -936,15 +925,11 @@
 base::Optional<PhysicalOffset> LayoutInline::FirstLineBoxTopLeftInternal()
     const {
   if (IsInLayoutNGInlineFormattingContext()) {
-    const NGPhysicalBoxFragment* box_fragment =
-        ContainingBlockFlowFragmentOf(*this);
-    if (!box_fragment)
+    NGInlineCursor cursor;
+    cursor.MoveTo(*this);
+    if (!cursor)
       return base::nullopt;
-    const auto& fragments =
-        NGInlineFragmentTraversal::SelfFragmentsOf(*box_fragment, this);
-    if (fragments.IsEmpty())
-      return base::nullopt;
-    return fragments.front().offset_to_container_box;
+    return cursor.CurrentOffset();
   }
   if (const InlineBox* first_box = FirstLineBoxIncludingCulling()) {
     LayoutPoint location = first_box->Location();
@@ -1083,17 +1068,15 @@
     DCHECK(ContainingNGBlockFlow());
     DCHECK(container_fragment->IsDescendantOfNotSelf(
         *ContainingNGBlockFlow()->PaintFragment()));
-    const auto& traversal_root =
-        To<NGPhysicalContainerFragment>(container_fragment->PhysicalFragment());
-    DCHECK(traversal_root.IsInline() || traversal_root.IsLineBox());
-    PhysicalOffset root_offset =
-        container_fragment->InlineOffsetToContainerBox();
-    const auto& descendants =
-        NGInlineFragmentTraversal::SelfFragmentsOf(traversal_root, this);
-    for (const auto& descendant : descendants) {
-      PhysicalRect rect = descendant.RectInContainerBox();
-      rect.Move(root_offset);
-      yield(rect);
+    DCHECK(container_fragment->PhysicalFragment().IsInline() ||
+           container_fragment->PhysicalFragment().IsLineBox());
+    NGInlineCursor cursor;
+    cursor.MoveTo(*this);
+    for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
+      if (!cursor.CurrentPaintFragment()->IsDescendantOfNotSelf(
+              *container_fragment))
+        continue;
+      yield(cursor.CurrentRect());
     }
   } else {
     DCHECK(!ContainingNGBlockFlow());
@@ -1142,15 +1125,11 @@
 
 PhysicalRect LayoutInline::PhysicalLinesBoundingBox() const {
   if (IsInLayoutNGInlineFormattingContext()) {
-    const NGPhysicalBoxFragment* box_fragment =
-        ContainingBlockFlowFragmentOf(*this);
-    if (!box_fragment)
-      return PhysicalRect();
+    NGInlineCursor cursor;
+    cursor.MoveTo(*this);
     PhysicalRect bounding_box;
-    auto children =
-        NGInlineFragmentTraversal::SelfFragmentsOf(*box_fragment, this);
-    for (const auto& child : children)
-      bounding_box.UniteIfNonZero(child.RectInContainerBox());
+    for (; cursor; cursor.MoveToNextForSameLayoutObject())
+      bounding_box.UniteIfNonZero(cursor.CurrentRect());
     return bounding_box;
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
index 2b69300..92d4f89f 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
@@ -348,12 +348,19 @@
 }
 
 void NGInlineCursor::MoveTo(const LayoutObject& layout_object) {
+  DCHECK(layout_object.IsInLayoutNGInlineFormattingContext()) << layout_object;
   InternalMoveTo(layout_object);
   if (IsNotNull() || !layout_object.IsLayoutInline()) {
     layout_inline_ = nullptr;
     return;
   }
   // In case of |layout_object| is cullred inline.
+  if (!fragment_items_ && !root_paint_fragment_) {
+    root_paint_fragment_ =
+        layout_object.RootInlineFormattingContext()->PaintFragment();
+    if (!root_paint_fragment_)
+      return MakeNull();
+  }
   layout_inline_ = ToLayoutInline(&layout_object);
   MoveToFirst();
   while (IsNotNull() && !IsInclusiveDescendantOf(layout_object))
@@ -437,8 +444,12 @@
   }
   if (current_paint_fragment_) {
     if (auto* paint_fragment =
-            current_paint_fragment_->NextForSameLayoutObject())
+            current_paint_fragment_->NextForSameLayoutObject()) {
+      // |paint_fragment| can be in another fragment tree rooted by
+      // |root_paint_fragment_|, e.g. "multicol-span-all-restyle-002.html"
+      root_paint_fragment_ = paint_fragment->Root();
       return MoveTo(*paint_fragment);
+    }
     return MakeNull();
   }
   if (current_item_) {
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
index 0939dc6..0124e6e 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
@@ -283,6 +283,13 @@
     }
   }
 
+  if (UNLIKELY(RuntimeEnabledFeatures::LayoutNGFragmentPaintEnabled())) {
+    if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) {
+      NGBoxFragmentPainter(*fragment, PaintFragment()).Paint(paint_info);
+      return;
+    }
+  }
+
   if (const NGPaintFragment* paint_fragment = PaintFragment())
     NGBoxFragmentPainter(*paint_fragment).Paint(paint_info);
   else
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
index 50d1b09..a39b2b4d 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
@@ -74,12 +74,16 @@
 }
 
 void LayoutNGFieldset::Paint(const PaintInfo& paint_info) const {
-  // TODO(kojii): This override shiould not be needed when painting fragment is
-  // enabled in parent classes.
-  if (const NGPhysicalBoxFragment* fragment = CurrentFragment())
-    NGBoxFragmentPainter(*fragment, PaintFragment()).Paint(paint_info);
-  else
-    LayoutNGBlockFlow::Paint(paint_info);
+  // TODO(crbug.com/988015): This override should not be needed when painting
+  // fragment is enabled in parent classes.
+  if (!RuntimeEnabledFeatures::LayoutNGFragmentPaintEnabled()) {
+    if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) {
+      NGBoxFragmentPainter(*fragment, PaintFragment()).Paint(paint_info);
+      return;
+    }
+  }
+
+  LayoutNGBlockFlow::Paint(paint_info);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
index c284780..466e4d6 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -555,6 +555,20 @@
       spanner_style, content_box_size_.inline_size,
       ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction());
 
+  if (break_token) {
+    // Truncate block-start margins at fragmentainer breaks, and also make sure
+    // that we don't repeat them at the beginning of every fragment generated
+    // from the spanner node.
+    margins.block_start = LayoutUnit();
+
+    if (break_token->IsBreakBefore()) {
+      // TODO(mstensho): Passing a break-before token shouldn't be a problem,
+      // but it would cause problems for the NGPaintFragment code. Just pass
+      // nullptr. Won't make any difference anyway.
+      break_token = nullptr;
+    }
+  }
+
   // Collapse the block-start margin of this spanner with the block-end margin
   // of an immediately preceding spanner, if any.
   margin_strut->Append(margins.block_start, /* is_quirky */ false);
@@ -564,11 +578,6 @@
   // context and out of space).
   LayoutUnit block_offset = intrinsic_block_size_ + margin_strut->Sum();
   auto spanner_space = CreateConstraintSpaceForSpanner(block_offset);
-  // TODO(mstensho): Passing a break-before token shouldn't be a problem, but it
-  // would cause problems for the NGPaintFragment code. Just pass nullptr. Won't
-  // make any difference anyway.
-  if (break_token && break_token->IsBreakBefore())
-    break_token = nullptr;
   auto result = spanner_node.Layout(spanner_space, break_token);
   NGFragment fragment(ConstraintSpace().GetWritingMode(),
                       result->PhysicalFragment());
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
index 651d133..e5de35ad 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
@@ -4445,6 +4445,41 @@
   EXPECT_EQ(expectation, dump);
 }
 
+TEST_F(NGColumnLayoutAlgorithmTest, BreakInsideSpannerWithMargins) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #parent {
+        columns: 3;
+        column-gap: 10px;
+        width: 320px;
+        column-fill: auto;
+        height: 100px;
+      }
+    </style>
+    <div id="container">
+      <div id="parent">
+        <div style="columns:2;">
+          <div style="column-span:all; margin-top:10px; margin-bottom:20px; width:33px; height:100px;"></div>
+          <div style="column-span:all; width:44px; height:10px;"></div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,10 size:33x90
+      offset:110,0 size:100x40
+        offset:0,0 size:100x40
+          offset:0,0 size:33x10
+          offset:0,30 size:44x10
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
 TEST_F(NGColumnLayoutAlgorithmTest, InvalidSpanners) {
   // Spanners cannot exist inside new formatting context roots. They will just
   // be treated as normal column content then.
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context_test.cc b/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
index cc4d4bf0..d9b69e5 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
+++ b/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
@@ -32,6 +32,7 @@
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/websocket_handshake_throttle.h"
 #include "third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h"
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 5d63d9d..a5e7afe 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1373,8 +1373,6 @@
                           ? WebFeature::kSignedExchangeInnerResponseInMainFrame
                           : WebFeature::kSignedExchangeInnerResponseInSubFrame);
   }
-
-  GetLocalFrameClient().DidCreateNewDocument();
 }
 
 void DocumentLoader::WillCommitNavigation() {
@@ -1436,11 +1434,6 @@
   // Needs to run before dispatching preloads, as it may evict the memory cache.
   probe::DidCommitLoad(frame_, this);
 
-  // Links with media values need more information (like viewport information).
-  // This happens after the first chunk is parsed in HTMLDocumentParser.
-  DispatchLinkHeaderPreloads(base::nullopt /* viewport */,
-                             PreloadHelper::kOnlyLoadNonMedia);
-
   frame_->GetPage()->DidCommitLoad(frame_);
   GetUseCounterHelper().DidCommitLoad(frame_);
 
@@ -1600,6 +1593,11 @@
 void DocumentLoader::CreateParserPostCommit() {
   Document* document = frame_->GetDocument();
 
+  // Links with media values need more information (like viewport information).
+  // This happens after the first chunk is parsed in HTMLDocumentParser.
+  DispatchLinkHeaderPreloads(base::nullopt /* viewport */,
+                             PreloadHelper::kOnlyLoadNonMedia);
+
   if (!loading_url_as_javascript_ &&
       !GetFrameLoader().StateMachine()->CreatingInitialEmptyDocument()) {
     // When the embedder gets notified (above) that the new navigation has
diff --git a/third_party/blink/renderer/core/loader/empty_clients.cc b/third_party/blink/renderer/core/loader/empty_clients.cc
index 6a885e6..216a525 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.cc
+++ b/third_party/blink/renderer/core/loader/empty_clients.cc
@@ -106,7 +106,7 @@
     bool,
     WebFrameLoadType,
     bool,
-    WebTriggeringEventInfo,
+    TriggeringEventInfo,
     HTMLFormElement*,
     ContentSecurityPolicyDisposition,
     mojo::PendingRemote<mojom::blink::BlobURLToken>,
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 96be63db..4d8d3f8 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -299,7 +299,7 @@
       bool,
       WebFrameLoadType,
       bool,
-      WebTriggeringEventInfo,
+      TriggeringEventInfo,
       HTMLFormElement*,
       ContentSecurityPolicyDisposition,
       mojo::PendingRemote<mojom::blink::BlobURLToken>,
diff --git a/third_party/blink/renderer/core/loader/form_submission.cc b/third_party/blink/renderer/core/loader/form_submission.cc
index ccb7d076a..d5e9b38 100644
--- a/third_party/blink/renderer/core/loader/form_submission.cc
+++ b/third_party/blink/renderer/core/loader/form_submission.cc
@@ -160,12 +160,12 @@
       boundary_(boundary) {
   if (event) {
     triggering_event_info_ = event->isTrusted()
-                                 ? WebTriggeringEventInfo::kFromTrustedEvent
-                                 : WebTriggeringEventInfo::kFromUntrustedEvent;
+                                 ? TriggeringEventInfo::kFromTrustedEvent
+                                 : TriggeringEventInfo::kFromUntrustedEvent;
     if (event->UnderlyingEvent())
       event = event->UnderlyingEvent();
   } else {
-    triggering_event_info_ = WebTriggeringEventInfo::kNotFromEvent;
+    triggering_event_info_ = TriggeringEventInfo::kNotFromEvent;
   }
   navigation_policy_ = NavigationPolicyFromEvent(event);
 }
diff --git a/third_party/blink/renderer/core/loader/form_submission.h b/third_party/blink/renderer/core/loader/form_submission.h
index e262e389..7e4465a2 100644
--- a/third_party/blink/renderer/core/loader/form_submission.h
+++ b/third_party/blink/renderer/core/loader/form_submission.h
@@ -32,7 +32,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_FORM_SUBMISSION_H_
 
 #include "base/macros.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "third_party/blink/renderer/core/loader/frame_load_request.h"
 #include "third_party/blink/renderer/core/loader/navigation_policy.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -132,7 +132,7 @@
   scoped_refptr<EncodedFormData> form_data_;
   String boundary_;
   NavigationPolicy navigation_policy_;
-  WebTriggeringEventInfo triggering_event_info_;
+  TriggeringEventInfo triggering_event_info_;
   String result_;
 };
 
diff --git a/third_party/blink/renderer/core/loader/frame_load_request.h b/third_party/blink/renderer/core/loader/frame_load_request.h
index dfc7593..a68c671 100644
--- a/third_party/blink/renderer/core/loader/frame_load_request.h
+++ b/third_party/blink/renderer/core/loader/frame_load_request.h
@@ -27,9 +27,9 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_FRAME_LOAD_REQUEST_H_
 
 #include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
 #include "third_party/blink/public/web/web_window_features.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/frame_types.h"
@@ -80,11 +80,11 @@
     navigation_policy_ = navigation_policy;
   }
 
-  WebTriggeringEventInfo TriggeringEventInfo() const {
+  TriggeringEventInfo GetTriggeringEventInfo() const {
     return triggering_event_info_;
   }
-  void SetTriggeringEventInfo(WebTriggeringEventInfo info) {
-    DCHECK(info != WebTriggeringEventInfo::kUnknown);
+  void SetTriggeringEventInfo(TriggeringEventInfo info) {
+    DCHECK(info != TriggeringEventInfo::kUnknown);
     triggering_event_info_ = info;
   }
 
@@ -151,8 +151,8 @@
   ClientNavigationReason client_navigation_reason_ =
       ClientNavigationReason::kNone;
   NavigationPolicy navigation_policy_ = kNavigationPolicyCurrentTab;
-  WebTriggeringEventInfo triggering_event_info_ =
-      WebTriggeringEventInfo::kNotFromEvent;
+  TriggeringEventInfo triggering_event_info_ =
+      TriggeringEventInfo::kNotFromEvent;
   Member<HTMLFormElement> form_;
   ShouldSendReferrer should_send_referrer_;
   ContentSecurityPolicyDisposition
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index ff1d0747..0f57ce5 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -213,7 +213,8 @@
   provisional_document_loader_->StartLoading();
 
   CommitDocumentLoader(provisional_document_loader_.Release(), base::nullopt,
-                       false);
+                       false /* dispatch_did_start */, base::DoNothing::Once(),
+                       false /* dispatch_did_commit */);
 
   frame_->GetDocument()->CancelParsing();
 
@@ -594,8 +595,8 @@
 void FrameLoader::StartNavigation(const FrameLoadRequest& passed_request,
                                   WebFrameLoadType frame_load_type) {
   CHECK(!IsBackForwardLoadType(frame_load_type));
-  DCHECK(passed_request.TriggeringEventInfo() !=
-         WebTriggeringEventInfo::kUnknown);
+  DCHECK(passed_request.GetTriggeringEventInfo() !=
+         TriggeringEventInfo::kUnknown);
   DCHECK(frame_->GetDocument());
   if (HTMLFrameOwnerElement* element = frame_->DeprecatedLocalOwner())
     element->CancelPendingLazyLoad();
@@ -654,14 +655,14 @@
     document_loader_->CommitSameDocumentNavigation(
         url, frame_load_type, nullptr, request.ClientRedirect(),
         origin_document,
-        request.TriggeringEventInfo() != WebTriggeringEventInfo::kNotFromEvent,
+        request.GetTriggeringEventInfo() != TriggeringEventInfo::kNotFromEvent,
         nullptr /* extra_data */);
     return;
   }
 
   WebNavigationType navigation_type = DetermineNavigationType(
       frame_load_type, resource_request.HttpBody() || request.Form(),
-      request.TriggeringEventInfo() != WebTriggeringEventInfo::kNotFromEvent);
+      request.GetTriggeringEventInfo() != TriggeringEventInfo::kNotFromEvent);
   resource_request.SetRequestContext(
       DetermineRequestContextFromNavigationType(navigation_type));
   request.SetFrameType(frame_->IsMainFrame()
@@ -766,7 +767,7 @@
       nullptr /* document_loader */, navigation_type,
       request.GetNavigationPolicy(), has_transient_activation, frame_load_type,
       request.ClientRedirect() == ClientRedirectPolicy::kClientRedirect,
-      request.TriggeringEventInfo(), request.Form(),
+      request.GetTriggeringEventInfo(), request.Form(),
       request.ShouldCheckMainWorldContentSecurityPolicy(),
       request.GetBlobURLToken(), request.GetInputStartTime(),
       request.HrefTranslate().GetString(), std::move(initiator_csp),
@@ -935,10 +936,6 @@
     progress_tracker_->ProgressStarted();
     provisional_document_loader_ = provisional_document_loader;
     frame_->GetFrameScheduler()->DidStartProvisionalLoad(frame_->IsMainFrame());
-    {
-      FrameNavigationDisabler navigation_disabler(*frame_);
-      Client()->DispatchDidStartProvisionalLoad(provisional_document_loader_);
-    }
     probe::DidStartProvisionalLoad(frame_);
     virtual_time_pauser_.PauseVirtualTime();
 
@@ -949,7 +946,6 @@
       return;
   }
 
-  std::move(call_before_attaching_new_document).Run();
   tls_version_warning_origins_.clear();
 
   // Following the call to StartLoading, the provisional DocumentLoader state
@@ -961,7 +957,9 @@
       DocumentLoader::HistoryNavigationType::kDifferentDocument);
 
   CommitDocumentLoader(provisional_document_loader_.Release(), unload_timing,
-                       !is_javascript_url);
+                       true /* dispatch_did_start */,
+                       std::move(call_before_attaching_new_document),
+                       !is_javascript_url /* dispatch_did_commit */);
 
   TakeObjectSnapshot();
 }
@@ -1078,7 +1076,9 @@
 void FrameLoader::CommitDocumentLoader(
     DocumentLoader* document_loader,
     const base::Optional<Document::UnloadEventTiming>& unload_timing,
-    bool dispatch_did_commit_load) {
+    bool dispatch_did_start,
+    base::OnceClosure call_before_attaching_new_document,
+    bool dispatch_did_commit) {
   document_loader_ = document_loader;
   CHECK(document_loader_);
 
@@ -1100,11 +1100,25 @@
 
   document_loader_->CommitNavigation();
 
-  if (dispatch_did_commit_load) {
-    Client()->DispatchDidCommitLoad(
-        document_loader_->GetHistoryItem(),
-        DocumentLoader::LoadTypeToCommitType(document_loader_->LoadType()),
-        document_loader_->GetGlobalObjectReusePolicy());
+  {
+    FrameNavigationDisabler navigation_disabler(*frame_);
+    // TODO(https://crbug.com/855189): replace DispatchDidStartProvisionalLoad,
+    // call_before_attaching_new_document and DispatchDidCommitLoad with a
+    // single call.
+    if (dispatch_did_start)
+      Client()->DispatchDidStartProvisionalLoad(document_loader_);
+    std::move(call_before_attaching_new_document).Run();
+    Client()->DidCreateNewDocument();
+    if (dispatch_did_commit) {
+      // TODO(https://crbug.com/855189): Do not make exceptions
+      // for javascript urls.
+      Client()->DispatchDidCommitLoad(
+          document_loader_->GetHistoryItem(),
+          DocumentLoader::LoadTypeToCommitType(document_loader_->LoadType()),
+          document_loader_->GetGlobalObjectReusePolicy());
+    }
+    // TODO(dgozman): make DidCreateScriptContext notification call currently
+    // triggered by installing new document happen here, after commit.
   }
 
   // Load the document if needed.
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h
index 4b5ddfe5..c16d98c8 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.h
+++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -263,9 +263,12 @@
   void TakeObjectSnapshot() const;
 
   // Commits the given |document_loader|.
-  void CommitDocumentLoader(DocumentLoader* document_loader,
-                            const base::Optional<Document::UnloadEventTiming>&,
-                            bool dispatch_did_commit_load);
+  void CommitDocumentLoader(
+      DocumentLoader* document_loader,
+      const base::Optional<Document::UnloadEventTiming>&,
+      bool dispatch_did_start,
+      base::OnceClosure call_before_attaching_new_document,
+      bool dispatch_did_commit);
 
   LocalFrameClient* Client() const;
 
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker.cc b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
index d103a0a..868e662 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker.cc
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
@@ -765,7 +765,7 @@
     const KURL& mixed_content_url) {
   String message = String::Format(
       "Mixed Content: The page at '%s' was loaded over HTTPS, but requested an "
-      "insecure element '%s'. As part of an experiment this request was "
+      "insecure element '%s'. This request was "
       "automatically upgraded to HTTPS, For more information see "
       "https://chromium.googlesource.com/chromium/src/+/master/docs/security/"
       "autoupgrade-mixed.md",
@@ -782,8 +782,8 @@
     const KURL& mixed_content_url) {
   String message = String::Format(
       "Mixed Content: The page at '%s' was loaded over HTTPS, but attempted "
-      "to connect to the insecure WebSocket endpoint '%s'. As part of an "
-      "experiment this request was automatically upgraded to HTTPS, For more "
+      "to connect to the insecure WebSocket endpoint '%s'. "
+      "This request was automatically upgraded to HTTPS, For more "
       "information see "
       "https://chromium.googlesource.com/chromium/src/+/master/docs/security/"
       "autoupgrade-mixed.md",
diff --git a/third_party/blink/renderer/core/loader/text_track_loader.cc b/third_party/blink/renderer/core/loader/text_track_loader.cc
index c90e386..3fb26f8 100644
--- a/third_party/blink/renderer/core/loader/text_track_loader.cc
+++ b/third_party/blink/renderer/core/loader/text_track_loader.cc
@@ -150,6 +150,13 @@
     cue_parser_->GetNewCues(output_cues);
 }
 
+void TextTrackLoader::GetNewStyleSheets(
+    HeapVector<Member<CSSStyleSheet>>& output_sheets) {
+  DCHECK(cue_parser_);
+  if (cue_parser_)
+    cue_parser_->GetNewStyleSheets(output_sheets);
+}
+
 void TextTrackLoader::Trace(blink::Visitor* visitor) {
   visitor->Trace(client_);
   visitor->Trace(cue_parser_);
diff --git a/third_party/blink/renderer/core/loader/text_track_loader.h b/third_party/blink/renderer/core/loader/text_track_loader.h
index 7488812..2621d7f 100644
--- a/third_party/blink/renderer/core/loader/text_track_loader.h
+++ b/third_party/blink/renderer/core/loader/text_track_loader.h
@@ -60,6 +60,7 @@
   State LoadState() { return state_; }
 
   void GetNewCues(HeapVector<Member<TextTrackCue>>& output_cues);
+  void GetNewStyleSheets(HeapVector<Member<CSSStyleSheet>>& output_sheets);
 
   void Trace(blink::Visitor*) override;
 
diff --git a/third_party/blink/renderer/core/messaging/blink_transferable_message.cc b/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
index 118eace..e64ed125 100644
--- a/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
+++ b/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
@@ -82,10 +82,12 @@
 
     for (auto& item : message.array_buffer_contents_array) {
       mojo_base::BigBuffer& big_buffer = item->contents;
-      auto handle = WTF::ArrayBufferContents::CreateDataHandle(
-          big_buffer.size(), WTF::ArrayBufferContents::kZeroInitialize);
-      WTF::ArrayBufferContents contents(std::move(handle),
-                                        WTF::ArrayBufferContents::kNotShared);
+      WTF::ArrayBufferContents contents(
+          big_buffer.size(), 1, WTF::ArrayBufferContents::kNotShared,
+          WTF::ArrayBufferContents::kDontInitialize);
+      // Check if we allocated the backing store of the ArrayBufferContents
+      // correctly.
+      CHECK_EQ(contents.DataLength(), big_buffer.size());
       memcpy(contents.Data(), big_buffer.data(), big_buffer.size());
       array_buffer_contents_array.push_back(std::move(contents));
     }
diff --git a/third_party/blink/renderer/core/messaging/blink_transferable_message_mojom_traits.cc b/third_party/blink/renderer/core/messaging/blink_transferable_message_mojom_traits.cc
index e426bbf..9c17e06 100644
--- a/third_party/blink/renderer/core/messaging/blink_transferable_message_mojom_traits.cc
+++ b/third_party/blink/renderer/core/messaging/blink_transferable_message_mojom_traits.cc
@@ -82,13 +82,13 @@
   if (!data.ReadContents(&contents_view))
     return false;
   auto contents_data = contents_view.data();
-  auto handle = WTF::ArrayBufferContents::CreateDataHandle(
-      contents_data.size(), WTF::ArrayBufferContents::kZeroInitialize);
-  if (!handle)
-    return false;
 
   WTF::ArrayBufferContents array_buffer_contents(
-      std::move(handle), WTF::ArrayBufferContents::kNotShared);
+      contents_data.size(), 1, WTF::ArrayBufferContents::kNotShared,
+      WTF::ArrayBufferContents::kDontInitialize);
+  if (contents_data.size() != array_buffer_contents.DataLength()) {
+    return false;
+  }
   memcpy(array_buffer_contents.Data(), contents_data.data(),
          contents_data.size());
   *out = std::move(array_buffer_contents);
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 7baf33b..7235fca 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
@@ -94,6 +94,7 @@
         web_view_helper_.LocalMainFrame()->GetDocument());
   }
 
+  WebView* GetWebView() { return web_view_helper_.GetWebView(); }
   Page* GetPage() { return web_view_helper_.GetWebView()->GetPage(); }
   WebLocalFrameImpl* LocalMainFrame() {
     return web_view_helper_.LocalMainFrame();
@@ -543,4 +544,48 @@
   EXPECT_EQ(context_menu_data.selected_text, "Blue text");
 }
 
+TEST_F(ContextMenuControllerTest, ShowNonLocatedContextMenuEvent) {
+  GetDocument()->documentElement()->SetInnerHTMLFromString(
+      "<input id='sample' type='text' size='5' value='Sample Input Text'>");
+
+  Document* document = GetDocument();
+  Element* input_element = document->getElementById("sample");
+  document->UpdateStyleAndLayout();
+
+  // Select the 'Sample' of |input|.
+  DOMRect* rect = input_element->getBoundingClientRect();
+  WebGestureEvent gesture_event(
+      WebInputEvent::kGestureLongPress, WebInputEvent::kNoModifiers,
+      base::TimeTicks::Now(), WebGestureDevice::kTouchscreen);
+  gesture_event.SetPositionInWidget(WebFloatPoint(rect->left(), rect->top()));
+  GetWebView()->MainFrameWidget()->HandleInputEvent(
+      WebCoalescedInputEvent(gesture_event));
+
+  WebContextMenuData context_menu_data =
+      GetWebFrameClient().GetContextMenuData();
+  EXPECT_EQ(context_menu_data.selected_text, "Sample");
+
+  // Adjust the selection from the start of |input| to the middle.
+  LayoutPoint middle_point((rect->left() + rect->right()) / 2,
+                           (rect->top() + rect->bottom()) / 2);
+  LocalMainFrame()->MoveRangeSelectionExtent(
+      WebPoint(middle_point.X().ToInt(), middle_point.Y().ToInt()));
+  GetWebView()->MainFrameWidget()->ShowContextMenu(kMenuSourceTouchHandle);
+
+  context_menu_data = GetWebFrameClient().GetContextMenuData();
+  EXPECT_NE(context_menu_data.selected_text, "");
+
+  // Scroll the value of |input| to end.
+  input_element->setScrollLeft(input_element->scrollWidth());
+
+  // Select all the value of |input| to ensure the start of selection is
+  // invisible.
+  LocalMainFrame()->MoveRangeSelectionExtent(
+      WebPoint(rect->right(), rect->bottom()));
+  GetWebView()->MainFrameWidget()->ShowContextMenu(kMenuSourceTouchHandle);
+
+  context_menu_data = GetWebFrameClient().GetContextMenuData();
+  EXPECT_EQ(context_menu_data.selected_text, "Sample Input Text");
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/focus_controller.cc b/third_party/blink/renderer/core/page/focus_controller.cc
index ca5ecf8..fa56cba 100644
--- a/third_party/blink/renderer/core/page/focus_controller.cc
+++ b/third_party/blink/renderer/core/page/focus_controller.cc
@@ -139,7 +139,7 @@
     Element* owner = nullptr;
     if (node.AssignedSlot())
       owner = node.AssignedSlot();
-    else if (IsHTMLSlotElement(node.parentNode()))
+    else if (IsA<HTMLSlotElement>(node.parentNode()))
       owner = node.ParentOrShadowHostElement();
     else if (&node == node.ContainingTreeScope().RootNode())
       owner = TreeOwner(&node);
@@ -230,7 +230,7 @@
     const Element* current,
     FocusController::OwnerMap& owner_map)
     : current_(current) {
-  if (HTMLSlotElement* slot = ToHTMLSlotElementOrNull(scoping_root_node)) {
+  if (auto* slot = DynamicTo<HTMLSlotElement>(scoping_root_node)) {
     if (slot->AssignedNodes().IsEmpty()) {
       navigation_ = MakeGarbageCollected<FocusNavigation>(scoping_root_node,
                                                           *slot, owner_map);
@@ -239,8 +239,7 @@
       // the shadow tree.
       DCHECK(scoping_root_node.ContainingShadowRoot());
       navigation_ = MakeGarbageCollected<FocusNavigation>(
-          scoping_root_node.ContainingShadowRoot()->host(),
-          ToHTMLSlotElement(scoping_root_node), owner_map);
+          scoping_root_node.ContainingShadowRoot()->host(), *slot, owner_map);
     }
   } else {
     navigation_ =
@@ -295,7 +294,7 @@
   if (IsShadowHost(element))
     return ScopedFocusNavigation::OwnedByShadowHost(element, owner_map);
   return ScopedFocusNavigation::OwnedByHTMLSlotElement(
-      ToHTMLSlotElement(element), owner_map);
+      To<HTMLSlotElement>(element), owner_map);
 }
 
 ScopedFocusNavigation ScopedFocusNavigation::OwnedByShadowHost(
@@ -327,7 +326,7 @@
     const Element& element) {
   Element* parent = const_cast<Element*>(element.parentElement());
   while (parent) {
-    if (auto* slot = ToHTMLSlotElementOrNull(parent))
+    if (auto* slot = DynamicTo<HTMLSlotElement>(parent))
       return slot->AssignedNodes().IsEmpty() ? slot : nullptr;
     parent = parent->parentElement();
   }
@@ -343,10 +342,10 @@
     const Element& current) {
   Element* parent = current.parentElement();
   while (parent) {
-    if (IsHTMLSlotElement(parent) &&
-        ToHTMLSlotElement(parent)->AssignedNodes().IsEmpty()) {
+    auto* html_slot_element = DynamicTo<HTMLSlotElement>(parent);
+    if (html_slot_element && html_slot_element->AssignedNodes().IsEmpty()) {
       return !SlotScopedTraversal::IsSlotScoped(current) &&
-             ToHTMLSlotElement(parent) == slot;
+             html_slot_element == slot;
     }
     parent = parent->parentElement();
   }
@@ -434,7 +433,7 @@
 
 inline bool IsNonFocusableFocusScopeOwner(Element& element) {
   return IsNonKeyboardFocusableShadowHost(element) ||
-         IsHTMLSlotElement(element);
+         IsA<HTMLSlotElement>(element);
 }
 
 inline bool IsShadowHostDelegatesFocus(const Element& element) {
diff --git a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
index 6a35ba8..28b4377 100644
--- a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
@@ -34,7 +34,8 @@
 }  // namespace
 
 ElementFragmentAnchor* ElementFragmentAnchor::TryCreate(const KURL& url,
-                                                        LocalFrame& frame) {
+                                                        LocalFrame& frame,
+                                                        bool should_scroll) {
   DCHECK(frame.GetDocument());
   Document& doc = *frame.GetDocument();
 
@@ -83,6 +84,10 @@
   if (!anchor_node)
     return nullptr;
 
+  // Element fragment anchors only need to be kept alive if they need scrolling.
+  if (!should_scroll)
+    return nullptr;
+
   return MakeGarbageCollected<ElementFragmentAnchor>(*anchor_node, frame);
 }
 
diff --git a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h
index bef1028..f51ce60 100644
--- a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h
+++ b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h
@@ -29,7 +29,9 @@
   // Parses the URL fragment and, if possible, creates and returns a fragment
   // based on an Element in the page. Returns nullptr otherwise. Produces side
   // effects related to fragment targeting in the page in either case.
-  static ElementFragmentAnchor* TryCreate(const KURL& url, LocalFrame& frame);
+  static ElementFragmentAnchor* TryCreate(const KURL& url,
+                                          LocalFrame& frame,
+                                          bool should_scroll);
 
   ElementFragmentAnchor(Node& anchor_node, LocalFrame& frame);
   ~ElementFragmentAnchor() override = default;
diff --git a/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc
index 850f81d..db3ebbc 100644
--- a/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc
@@ -15,7 +15,8 @@
 
 FragmentAnchor* FragmentAnchor::TryCreate(const KURL& url,
                                           LocalFrame& frame,
-                                          bool same_document_navigation) {
+                                          bool same_document_navigation,
+                                          bool should_scroll) {
   DCHECK(frame.GetDocument());
 
   FragmentAnchor* anchor = nullptr;
@@ -28,7 +29,7 @@
   bool text_fragment_anchor_created = false;
   if (text_fragment_identifiers_enabled) {
     anchor = TextFragmentAnchor::TryCreateFragmentDirective(
-        url, frame, same_document_navigation);
+        url, frame, same_document_navigation, should_scroll);
     text_fragment_anchor_created = anchor;
   }
 
@@ -60,7 +61,7 @@
 
   bool element_id_anchor_found = false;
   if (!anchor) {
-    anchor = ElementFragmentAnchor::TryCreate(url, frame);
+    anchor = ElementFragmentAnchor::TryCreate(url, frame, should_scroll);
     element_id_anchor_found = anchor;
   }
 
diff --git a/third_party/blink/renderer/core/page/scrolling/fragment_anchor.h b/third_party/blink/renderer/core/page/scrolling/fragment_anchor.h
index 011da8b5..7550d56 100644
--- a/third_party/blink/renderer/core/page/scrolling/fragment_anchor.h
+++ b/third_party/blink/renderer/core/page/scrolling/fragment_anchor.h
@@ -32,7 +32,8 @@
   // will be performed, for example, setting/clearing :target and svgView().
   static FragmentAnchor* TryCreate(const KURL& url,
                                    LocalFrame& frame,
-                                   bool same_document_navigation);
+                                   bool same_document_navigation,
+                                   bool should_scroll);
 
   FragmentAnchor() = default;
   virtual ~FragmentAnchor() = default;
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
index 451a3f4..3d8b5cc 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
@@ -77,7 +77,8 @@
 TextFragmentAnchor* TextFragmentAnchor::TryCreateFragmentDirective(
     const KURL& url,
     LocalFrame& frame,
-    bool same_document_navigation) {
+    bool same_document_navigation,
+    bool should_scroll) {
   DCHECK(RuntimeEnabledFeatures::TextFragmentIdentifiersEnabled(
       frame.GetDocument()));
 
@@ -96,13 +97,16 @@
     return nullptr;
   }
 
-  return MakeGarbageCollected<TextFragmentAnchor>(selectors, frame);
+  return MakeGarbageCollected<TextFragmentAnchor>(selectors, frame,
+                                                  should_scroll);
 }
 
 TextFragmentAnchor::TextFragmentAnchor(
     const Vector<TextFragmentSelector>& text_fragment_selectors,
-    LocalFrame& frame)
+    LocalFrame& frame,
+    bool should_scroll)
     : frame_(&frame),
+      should_scroll_(should_scroll),
       metrics_(MakeGarbageCollected<TextFragmentAnchorMetrics>(
           frame_->GetDocument())) {
   DCHECK(!text_fragment_selectors.IsEmpty());
@@ -134,7 +138,7 @@
   if (user_scrolled_ && !did_scroll_into_view_)
     metrics_->ScrollCancelled();
 
-  first_match_needs_scroll_ = !user_scrolled_;
+  first_match_needs_scroll_ = should_scroll_ && !user_scrolled_;
 
   {
     // FindMatch might cause scrolling and set user_scrolled_ so reset it when
@@ -263,8 +267,8 @@
     dismissed_ = true;
 
     DCHECK(!element_fragment_anchor_);
-    element_fragment_anchor_ =
-        ElementFragmentAnchor::TryCreate(frame_->GetDocument()->Url(), *frame_);
+    element_fragment_anchor_ = ElementFragmentAnchor::TryCreate(
+        frame_->GetDocument()->Url(), *frame_, should_scroll_);
     if (element_fragment_anchor_) {
       // Schedule a frame so we can invoke the element anchor in
       // PerformPreRafActions.
@@ -284,7 +288,7 @@
   if (!did_find_match_ || dismissed_)
     return true;
 
-  DCHECK(did_scroll_into_view_ || user_scrolled_);
+  DCHECK(!should_scroll_ || did_scroll_into_view_ || user_scrolled_);
 
   frame_->GetDocument()->Markers().RemoveMarkersOfTypes(
       DocumentMarker::MarkerTypes::TextFragment());
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
index 7bcef31..27d3da5 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
@@ -36,11 +36,13 @@
   static TextFragmentAnchor* TryCreateFragmentDirective(
       const KURL& url,
       LocalFrame& frame,
-      bool same_document_navigation);
+      bool same_document_navigation,
+      bool should_scroll);
 
   TextFragmentAnchor(
       const Vector<TextFragmentSelector>& text_fragment_selectors,
-      LocalFrame& frame);
+      LocalFrame& frame,
+      bool should_scroll);
   ~TextFragmentAnchor() override = default;
 
   bool Invoke() override;
@@ -89,6 +91,10 @@
   // Whether the text fragment anchor has been dismissed yet. This should be
   // kept alive until dismissed so we can remove text highlighting.
   bool dismissed_ = false;
+  // Whether we should scroll the anchor into view. This will be false for
+  // history navigations and reloads, where we want to restore the highlight but
+  // not scroll into view again.
+  bool should_scroll_ = false;
 
   Member<TextFragmentAnchorMetrics> metrics_;
 
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
index 9df966c..dbc344b 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/location.h"
+#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
@@ -1545,6 +1546,46 @@
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
 }
 
+// Ensure we restore the text highlight on page reload
+TEST_F(TextFragmentAnchorTest, HighlightOnReload) {
+  SimRequest request("https://example.com/test.html#:~:text=test", "text/html");
+  LoadURL("https://example.com/test.html#:~:text=test");
+  const String& html = R"HTML(
+    <!DOCTYPE html>
+    <style>
+      body {
+        height: 1200px;
+      }
+      p {
+        position: absolute;
+        top: 1000px;
+      }
+    </style>
+    <p id="text">This is a test page</p>
+  )HTML";
+  request.Complete(html);
+
+  Compositor().BeginFrame();
+  RunAsyncMatchingTasks();
+
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+
+  // Tap to dismiss the highlight.
+  SimulateClick(10, 10);
+  EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
+
+  // Reload the page and expect the highlight to be restored.
+  SimRequest reload_request("https://example.com/test.html#:~:text=test",
+                            "text/html");
+  MainFrame().StartReload(WebFrameLoadType::kReload);
+  reload_request.Complete(html);
+
+  Compositor().BeginFrame();
+  RunAsyncMatchingTasks();
+
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/slot_scoped_traversal_test.cc b/third_party/blink/renderer/core/page/slot_scoped_traversal_test.cc
index 629825c..e711cfd 100644
--- a/third_party/blink/renderer/core/page/slot_scoped_traversal_test.cc
+++ b/third_party/blink/renderer/core/page/slot_scoped_traversal_test.cc
@@ -75,7 +75,7 @@
 
   Element* host = GetDocument().QuerySelector("#host");
   ShadowRoot* shadow_root = host->OpenShadowRoot();
-  auto* slot = ToHTMLSlotElement(shadow_root->QuerySelector("slot"));
+  auto* slot = To<HTMLSlotElement>(shadow_root->QuerySelector("slot"));
 
   EXPECT_EQ(nullptr, SlotScopedTraversal::FirstAssignedToSlot(*slot));
   EXPECT_EQ(nullptr, SlotScopedTraversal::LastAssignedToSlot(*slot));
@@ -109,7 +109,7 @@
   Element* inner1 = GetDocument().QuerySelector("#inner1");
   Element* inner2 = GetDocument().QuerySelector("#inner2");
   ShadowRoot* shadow_root = host->OpenShadowRoot();
-  auto* slot = ToHTMLSlotElement(shadow_root->QuerySelector("slot"));
+  auto* slot = To<HTMLSlotElement>(shadow_root->QuerySelector("slot"));
 
   EXPECT_EQ(inner1, SlotScopedTraversal::FirstAssignedToSlot(*slot));
   EXPECT_EQ(inner2, SlotScopedTraversal::LastAssignedToSlot(*slot));
@@ -162,9 +162,9 @@
   slot_element[2] = shadow_root->QuerySelector("#unnamedslot");
 
   HTMLSlotElement* slot[3];
-  slot[0] = ToHTMLSlotElement(slot_element[0]);
-  slot[1] = ToHTMLSlotElement(slot_element[1]);
-  slot[2] = ToHTMLSlotElement(slot_element[2]);
+  slot[0] = To<HTMLSlotElement>(slot_element[0]);
+  slot[1] = To<HTMLSlotElement>(slot_element[1]);
+  slot[2] = To<HTMLSlotElement>(slot_element[2]);
 
   {
     // <slot id='slot0'> : Expected assigned nodes: inner0, inner4
@@ -234,7 +234,7 @@
     AttachOpenShadowRoot(*inner[i], shadow_html);
 
     ShadowRoot* shadow_root = host->OpenShadowRoot();
-    auto* slot = ToHTMLSlotElement(shadow_root->QuerySelector("slot"));
+    auto* slot = To<HTMLSlotElement>(shadow_root->QuerySelector("slot"));
 
     switch (i) {
       case 0: {
@@ -340,7 +340,7 @@
     }
 
     ShadowRoot* shadow_root = host->OpenShadowRoot();
-    auto* slot = ToHTMLSlotElement(shadow_root->QuerySelector("slot"));
+    auto* slot = To<HTMLSlotElement>(shadow_root->QuerySelector("slot"));
 
     switch (i) {
       case 0: {
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
index deb12e7..5774633 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
@@ -138,12 +138,20 @@
   if (direction == SpatialNavigationDirection::kNone)
     return false;
 
+  // If the focus has already moved by a previous handler, return false.
+  const Element* focused = GetFocusedElement();
+  if (focused && focused != event->target()) {
+    // SpatNav does not need to handle this arrow key because
+    // the webpage had a key-handler that already moved focus.
+    return false;
+  }
+
   // In focusless mode, the user must explicitly move focus in and out of an
   // editable so we can avoid advancing interest and we should swallow the
   // event. This prevents double-handling actions for things like search box
   // suggestions.
   if (RuntimeEnabledFeatures::FocuslessSpatialNavigationEnabled()) {
-    if (Element* focused = GetFocusedElement()) {
+    if (focused) {
       if (HasEditableStyle(*focused) || focused->IsTextControl())
         return true;
     }
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 5f70df08..bccf48421 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -762,9 +762,21 @@
   }
 
   if (box_decoration_data.ShouldPaintShadow()) {
-    PaintInsetBoxShadowWithBorderRect(paint_info, paint_rect, style,
-                                      border_edges.line_left,
-                                      border_edges.line_right);
+    if (layout_box.IsTableCell()) {
+      PhysicalRect inner_rect = paint_rect;
+      inner_rect.Contract(layout_box.BorderBoxOutsets());
+      // PaintInsetBoxShadowWithInnerRect doesn't subtract borders before
+      // painting. We have to use it here after subtracting collapsed borders
+      // above. PaintInsetBoxShadowWithBorderRect below subtracts the borders
+      // specified on the style object, which doesn't account for border
+      // collapsing.
+      BoxPainterBase::PaintInsetBoxShadowWithInnerRect(paint_info, inner_rect,
+                                                       style);
+    } else {
+      PaintInsetBoxShadowWithBorderRect(paint_info, paint_rect, style,
+                                        border_edges.line_left,
+                                        border_edges.line_right);
+    }
   }
 
   // The theme will tell us whether or not we should also paint the CSS
diff --git a/third_party/blink/renderer/core/resize_observer/resize_observation.cc b/third_party/blink/renderer/core/resize_observer/resize_observation.cc
index df02d02f..7897445 100644
--- a/third_party/blink/renderer/core/resize_observer/resize_observation.cc
+++ b/third_party/blink/renderer/core/resize_observer/resize_observation.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/resize_observer/resize_observation.h"
 
+#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
 #include "third_party/blink/renderer/core/resize_observer/resize_observer.h"
 #include "third_party/blink/renderer/core/svg/svg_element.h"
@@ -21,11 +22,26 @@
 }
 
 bool ResizeObservation::ObservationSizeOutOfSync() {
-  return element_size_changed_ && observation_size_ != ComputeTargetSize();
+  if (!element_size_changed_ || observation_size_ == ComputeTargetSize())
+    return false;
+
+  // Skip resize observations on locked elements.
+  if (UNLIKELY(
+          DisplayLockUtilities::IsInLockedSubtreeCrossingFrames(*target_))) {
+    return false;
+  }
+
+  return true;
 }
 
 void ResizeObservation::SetObservationSize(const LayoutSize& observation_size) {
   observation_size_ = observation_size;
+
+  // Don't clear the dirty bit while locked. This allows us to make sure to
+  // compare sizes when becoming unlocked.
+  if (UNLIKELY(DisplayLockUtilities::IsInLockedSubtreeCrossingFrames(*target_)))
+    return;
+
   element_size_changed_ = false;
 }
 
diff --git a/third_party/blink/renderer/core/resize_observer/resize_observer_controller.cc b/third_party/blink/renderer/core/resize_observer/resize_observer_controller.cc
index d2cf42f..e1ae9359 100644
--- a/third_party/blink/renderer/core/resize_observer/resize_observer_controller.cc
+++ b/third_party/blink/renderer/core/resize_observer/resize_observer_controller.cc
@@ -27,6 +27,14 @@
   return shallowest;
 }
 
+void ResizeObserverController::SetNeedsForcedResizeObservations() {
+  for (auto& observer : observers_) {
+    // Set ElementSizeChanged as a way of forcing the observer to check all
+    // observations.
+    observer->ElementSizeChanged();
+  }
+}
+
 bool ResizeObserverController::SkippedObservations() {
   for (auto& observer : observers_) {
     if (observer->SkippedObservations())
diff --git a/third_party/blink/renderer/core/resize_observer/resize_observer_controller.h b/third_party/blink/renderer/core/resize_observer/resize_observer_controller.h
index 9cca2129..27511e1 100644
--- a/third_party/blink/renderer/core/resize_observer/resize_observer_controller.h
+++ b/third_party/blink/renderer/core/resize_observer/resize_observer_controller.h
@@ -37,6 +37,8 @@
   void ClearObservations();
   void ObserverChanged() { observers_changed_ = true; }
 
+  void SetNeedsForcedResizeObservations();
+
   void Trace(blink::Visitor*);
 
   // For testing only.
diff --git a/third_party/blink/renderer/core/script/import_map.cc b/third_party/blink/renderer/core/script/import_map.cc
index 423f40ad..0aed827a 100644
--- a/third_party/blink/renderer/core/script/import_map.cc
+++ b/third_party/blink/renderer/core/script/import_map.cc
@@ -596,28 +596,60 @@
   return NullURL();
 }
 
-String ImportMap::ToString() const {
-  StringBuilder builder;
+static void SpecifierMapToString(StringBuilder& builder,
+                                 bool support_builtin_modules,
+                                 const ImportMap::SpecifierMap& specifier_map) {
   builder.Append("{");
   bool is_first_key = true;
-  for (const auto& it : imports_) {
+  for (const auto& it : specifier_map) {
     if (!is_first_key)
       builder.Append(",");
     is_first_key = false;
-    builder.Append("\n  ");
     builder.Append(it.key.EncodeForDebugging());
-    builder.Append(": [");
-    bool is_first_value = true;
-    for (const auto& v : it.value) {
-      if (!is_first_value)
-        builder.Append(",");
-      is_first_value = false;
-      builder.Append("\n    ");
-      builder.Append(v.GetString().EncodeForDebugging());
+    builder.Append(":");
+    if (support_builtin_modules) {
+      builder.Append("[");
+      bool is_first_value = true;
+      for (const auto& v : it.value) {
+        if (!is_first_value)
+          builder.Append(",");
+        is_first_value = false;
+        builder.Append(v.GetString().EncodeForDebugging());
+      }
+      builder.Append("]");
+    } else {
+      if (it.value.size() == 0) {
+        builder.Append("[]");
+      } else {
+        DCHECK_EQ(it.value.size(), 1u);
+        builder.Append(it.value[0].GetString().EncodeForDebugging());
+      }
     }
-    builder.Append("\n  ]");
   }
-  builder.Append("\n}\n");
+  builder.Append("}");
+}
+
+String ImportMap::ToString() const {
+  StringBuilder builder;
+  builder.Append("{\"imports\":");
+  SpecifierMapToString(builder, support_builtin_modules_, imports_);
+
+  builder.Append(",\"scopes\":{");
+
+  bool is_first_scope = true;
+  for (const auto& entry : scopes_) {
+    if (!is_first_scope)
+      builder.Append(",");
+    is_first_scope = false;
+    builder.Append(entry.first.EncodeForDebugging());
+    builder.Append(":");
+    SpecifierMapToString(builder, support_builtin_modules_, entry.second);
+  }
+
+  builder.Append("}");
+
+  builder.Append("}");
+
   return builder.ToString();
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_a_element.cc b/third_party/blink/renderer/core/svg/svg_a_element.cc
index f44723c..ab8b85d3 100644
--- a/third_party/blink/renderer/core/svg/svg_a_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_a_element.cc
@@ -48,8 +48,6 @@
 
 namespace blink {
 
-using namespace html_names;
-
 SVGAElement::SVGAElement(Document& document)
     : SVGGraphicsElement(svg_names::kATag, document),
       SVGURIReference(this),
@@ -140,8 +138,8 @@
           &GetDocument(), ResourceRequest(GetDocument().CompleteURL(url)));
       frame_request.SetNavigationPolicy(NavigationPolicyFromEvent(&event));
       frame_request.SetTriggeringEventInfo(
-          event.isTrusted() ? WebTriggeringEventInfo::kFromTrustedEvent
-                            : WebTriggeringEventInfo::kFromUntrustedEvent);
+          event.isTrusted() ? TriggeringEventInfo::kFromTrustedEvent
+                            : TriggeringEventInfo::kFromUntrustedEvent);
       frame_request.GetResourceRequest().SetHasUserGesture(
           LocalFrame::HasTransientUserActivation(GetDocument().GetFrame()));
 
@@ -183,7 +181,7 @@
 }
 
 bool SVGAElement::IsURLAttribute(const Attribute& attribute) const {
-  return attribute.GetName().LocalName() == kHrefAttr ||
+  return attribute.GetName().LocalName() == html_names::kHrefAttr ||
          SVGGraphicsElement::IsURLAttribute(attribute);
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_element.cc b/third_party/blink/renderer/core/svg/svg_element.cc
index adfd4d2..c38dc8b 100644
--- a/third_party/blink/renderer/core/svg/svg_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_element.cc
@@ -64,9 +64,6 @@
 
 namespace blink {
 
-using namespace html_names;
-using namespace svg_names;
-
 SVGElement::SVGElement(const QualifiedName& tag_name,
                        Document& document,
                        ConstructionType construction_type)
@@ -317,12 +314,12 @@
   Element::InsertedInto(root_parent);
   UpdateRelativeLengthsInformation();
 
-  const AtomicString& nonce_value = FastGetAttribute(kNonceAttr);
+  const AtomicString& nonce_value = FastGetAttribute(html_names::kNonceAttr);
   if (!nonce_value.IsEmpty()) {
     setNonce(nonce_value);
     if (InActiveDocument() &&
         GetDocument().GetContentSecurityPolicy()->HasHeaderDeliveredPolicy()) {
-      setAttribute(kNonceAttr, g_empty_atom);
+      setAttribute(html_names::kNonceAttr, g_empty_atom);
     }
   }
   return kInsertionDone;
@@ -377,64 +374,64 @@
     // This is a list of all base CSS and SVG CSS properties which are exposed
     // as SVG XML attributes
     const QualifiedName* const attr_names[] = {
-        &kAlignmentBaselineAttr,
-        &kBaselineShiftAttr,
-        &kBufferedRenderingAttr,
-        &kClipAttr,
-        &kClipPathAttr,
-        &kClipRuleAttr,
+        &svg_names::kAlignmentBaselineAttr,
+        &svg_names::kBaselineShiftAttr,
+        &svg_names::kBufferedRenderingAttr,
+        &svg_names::kClipAttr,
+        &svg_names::kClipPathAttr,
+        &svg_names::kClipRuleAttr,
         &svg_names::kColorAttr,
-        &kColorInterpolationAttr,
-        &kColorInterpolationFiltersAttr,
-        &kColorRenderingAttr,
-        &kCursorAttr,
+        &svg_names::kColorInterpolationAttr,
+        &svg_names::kColorInterpolationFiltersAttr,
+        &svg_names::kColorRenderingAttr,
+        &svg_names::kCursorAttr,
         &svg_names::kDirectionAttr,
-        &kDisplayAttr,
-        &kDominantBaselineAttr,
-        &kFillAttr,
-        &kFillOpacityAttr,
-        &kFillRuleAttr,
-        &kFilterAttr,
-        &kFloodColorAttr,
-        &kFloodOpacityAttr,
-        &kFontFamilyAttr,
-        &kFontSizeAttr,
-        &kFontStretchAttr,
-        &kFontStyleAttr,
-        &kFontVariantAttr,
-        &kFontWeightAttr,
-        &kImageRenderingAttr,
-        &kLetterSpacingAttr,
-        &kLightingColorAttr,
-        &kMarkerEndAttr,
-        &kMarkerMidAttr,
-        &kMarkerStartAttr,
-        &kMaskAttr,
-        &kMaskTypeAttr,
-        &kOpacityAttr,
-        &kOverflowAttr,
-        &kPaintOrderAttr,
-        &kPointerEventsAttr,
-        &kShapeRenderingAttr,
-        &kStopColorAttr,
-        &kStopOpacityAttr,
-        &kStrokeAttr,
-        &kStrokeDasharrayAttr,
-        &kStrokeDashoffsetAttr,
-        &kStrokeLinecapAttr,
-        &kStrokeLinejoinAttr,
-        &kStrokeMiterlimitAttr,
-        &kStrokeOpacityAttr,
-        &kStrokeWidthAttr,
-        &kTextAnchorAttr,
-        &kTextDecorationAttr,
-        &kTextRenderingAttr,
-        &kTransformOriginAttr,
-        &kUnicodeBidiAttr,
-        &kVectorEffectAttr,
-        &kVisibilityAttr,
-        &kWordSpacingAttr,
-        &kWritingModeAttr,
+        &svg_names::kDisplayAttr,
+        &svg_names::kDominantBaselineAttr,
+        &svg_names::kFillAttr,
+        &svg_names::kFillOpacityAttr,
+        &svg_names::kFillRuleAttr,
+        &svg_names::kFilterAttr,
+        &svg_names::kFloodColorAttr,
+        &svg_names::kFloodOpacityAttr,
+        &svg_names::kFontFamilyAttr,
+        &svg_names::kFontSizeAttr,
+        &svg_names::kFontStretchAttr,
+        &svg_names::kFontStyleAttr,
+        &svg_names::kFontVariantAttr,
+        &svg_names::kFontWeightAttr,
+        &svg_names::kImageRenderingAttr,
+        &svg_names::kLetterSpacingAttr,
+        &svg_names::kLightingColorAttr,
+        &svg_names::kMarkerEndAttr,
+        &svg_names::kMarkerMidAttr,
+        &svg_names::kMarkerStartAttr,
+        &svg_names::kMaskAttr,
+        &svg_names::kMaskTypeAttr,
+        &svg_names::kOpacityAttr,
+        &svg_names::kOverflowAttr,
+        &svg_names::kPaintOrderAttr,
+        &svg_names::kPointerEventsAttr,
+        &svg_names::kShapeRenderingAttr,
+        &svg_names::kStopColorAttr,
+        &svg_names::kStopOpacityAttr,
+        &svg_names::kStrokeAttr,
+        &svg_names::kStrokeDasharrayAttr,
+        &svg_names::kStrokeDashoffsetAttr,
+        &svg_names::kStrokeLinecapAttr,
+        &svg_names::kStrokeLinejoinAttr,
+        &svg_names::kStrokeMiterlimitAttr,
+        &svg_names::kStrokeOpacityAttr,
+        &svg_names::kStrokeWidthAttr,
+        &svg_names::kTextAnchorAttr,
+        &svg_names::kTextDecorationAttr,
+        &svg_names::kTextRenderingAttr,
+        &svg_names::kTransformOriginAttr,
+        &svg_names::kUnicodeBidiAttr,
+        &svg_names::kVectorEffectAttr,
+        &svg_names::kVisibilityAttr,
+        &svg_names::kWordSpacingAttr,
+        &svg_names::kWritingModeAttr,
     };
     for (size_t i = 0; i < base::size(attr_names); i++) {
       CSSPropertyID property_id = cssPropertyID(attr_names[i]->LocalName());
@@ -666,59 +663,59 @@
       const AnimatedPropertyType prop_type;
     };
     const AttrToTypeEntry attr_to_types[] = {
-        {kAlignmentBaselineAttr, kAnimatedString},
-        {kBaselineShiftAttr, kAnimatedString},
-        {kBufferedRenderingAttr, kAnimatedString},
-        {kClipPathAttr, kAnimatedString},
-        {kClipRuleAttr, kAnimatedString},
+        {svg_names::kAlignmentBaselineAttr, kAnimatedString},
+        {svg_names::kBaselineShiftAttr, kAnimatedString},
+        {svg_names::kBufferedRenderingAttr, kAnimatedString},
+        {svg_names::kClipPathAttr, kAnimatedString},
+        {svg_names::kClipRuleAttr, kAnimatedString},
         {svg_names::kColorAttr, kAnimatedColor},
-        {kColorInterpolationAttr, kAnimatedString},
-        {kColorInterpolationFiltersAttr, kAnimatedString},
-        {kColorRenderingAttr, kAnimatedString},
-        {kCursorAttr, kAnimatedString},
-        {kDisplayAttr, kAnimatedString},
-        {kDominantBaselineAttr, kAnimatedString},
-        {kFillAttr, kAnimatedColor},
-        {kFillOpacityAttr, kAnimatedNumber},
-        {kFillRuleAttr, kAnimatedString},
-        {kFilterAttr, kAnimatedString},
-        {kFloodColorAttr, kAnimatedColor},
-        {kFloodOpacityAttr, kAnimatedNumber},
-        {kFontFamilyAttr, kAnimatedString},
-        {kFontSizeAttr, kAnimatedLength},
-        {kFontStretchAttr, kAnimatedString},
-        {kFontStyleAttr, kAnimatedString},
-        {kFontVariantAttr, kAnimatedString},
-        {kFontWeightAttr, kAnimatedString},
-        {kImageRenderingAttr, kAnimatedString},
-        {kLetterSpacingAttr, kAnimatedLength},
-        {kLightingColorAttr, kAnimatedColor},
-        {kMarkerEndAttr, kAnimatedString},
-        {kMarkerMidAttr, kAnimatedString},
-        {kMarkerStartAttr, kAnimatedString},
-        {kMaskAttr, kAnimatedString},
-        {kMaskTypeAttr, kAnimatedString},
-        {kOpacityAttr, kAnimatedNumber},
-        {kOverflowAttr, kAnimatedString},
-        {kPaintOrderAttr, kAnimatedString},
-        {kPointerEventsAttr, kAnimatedString},
-        {kShapeRenderingAttr, kAnimatedString},
-        {kStopColorAttr, kAnimatedColor},
-        {kStopOpacityAttr, kAnimatedNumber},
-        {kStrokeAttr, kAnimatedColor},
-        {kStrokeDasharrayAttr, kAnimatedLengthList},
-        {kStrokeDashoffsetAttr, kAnimatedLength},
-        {kStrokeLinecapAttr, kAnimatedString},
-        {kStrokeLinejoinAttr, kAnimatedString},
-        {kStrokeMiterlimitAttr, kAnimatedNumber},
-        {kStrokeOpacityAttr, kAnimatedNumber},
-        {kStrokeWidthAttr, kAnimatedLength},
-        {kTextAnchorAttr, kAnimatedString},
-        {kTextDecorationAttr, kAnimatedString},
-        {kTextRenderingAttr, kAnimatedString},
-        {kVectorEffectAttr, kAnimatedString},
-        {kVisibilityAttr, kAnimatedString},
-        {kWordSpacingAttr, kAnimatedLength},
+        {svg_names::kColorInterpolationAttr, kAnimatedString},
+        {svg_names::kColorInterpolationFiltersAttr, kAnimatedString},
+        {svg_names::kColorRenderingAttr, kAnimatedString},
+        {svg_names::kCursorAttr, kAnimatedString},
+        {svg_names::kDisplayAttr, kAnimatedString},
+        {svg_names::kDominantBaselineAttr, kAnimatedString},
+        {svg_names::kFillAttr, kAnimatedColor},
+        {svg_names::kFillOpacityAttr, kAnimatedNumber},
+        {svg_names::kFillRuleAttr, kAnimatedString},
+        {svg_names::kFilterAttr, kAnimatedString},
+        {svg_names::kFloodColorAttr, kAnimatedColor},
+        {svg_names::kFloodOpacityAttr, kAnimatedNumber},
+        {svg_names::kFontFamilyAttr, kAnimatedString},
+        {svg_names::kFontSizeAttr, kAnimatedLength},
+        {svg_names::kFontStretchAttr, kAnimatedString},
+        {svg_names::kFontStyleAttr, kAnimatedString},
+        {svg_names::kFontVariantAttr, kAnimatedString},
+        {svg_names::kFontWeightAttr, kAnimatedString},
+        {svg_names::kImageRenderingAttr, kAnimatedString},
+        {svg_names::kLetterSpacingAttr, kAnimatedLength},
+        {svg_names::kLightingColorAttr, kAnimatedColor},
+        {svg_names::kMarkerEndAttr, kAnimatedString},
+        {svg_names::kMarkerMidAttr, kAnimatedString},
+        {svg_names::kMarkerStartAttr, kAnimatedString},
+        {svg_names::kMaskAttr, kAnimatedString},
+        {svg_names::kMaskTypeAttr, kAnimatedString},
+        {svg_names::kOpacityAttr, kAnimatedNumber},
+        {svg_names::kOverflowAttr, kAnimatedString},
+        {svg_names::kPaintOrderAttr, kAnimatedString},
+        {svg_names::kPointerEventsAttr, kAnimatedString},
+        {svg_names::kShapeRenderingAttr, kAnimatedString},
+        {svg_names::kStopColorAttr, kAnimatedColor},
+        {svg_names::kStopOpacityAttr, kAnimatedNumber},
+        {svg_names::kStrokeAttr, kAnimatedColor},
+        {svg_names::kStrokeDasharrayAttr, kAnimatedLengthList},
+        {svg_names::kStrokeDashoffsetAttr, kAnimatedLength},
+        {svg_names::kStrokeLinecapAttr, kAnimatedString},
+        {svg_names::kStrokeLinejoinAttr, kAnimatedString},
+        {svg_names::kStrokeMiterlimitAttr, kAnimatedNumber},
+        {svg_names::kStrokeOpacityAttr, kAnimatedNumber},
+        {svg_names::kStrokeWidthAttr, kAnimatedLength},
+        {svg_names::kTextAnchorAttr, kAnimatedString},
+        {svg_names::kTextDecorationAttr, kAnimatedString},
+        {svg_names::kTextRenderingAttr, kAnimatedString},
+        {svg_names::kVectorEffectAttr, kAnimatedString},
+        {svg_names::kVisibilityAttr, kAnimatedString},
+        {svg_names::kWordSpacingAttr, kAnimatedLength},
     };
     for (size_t i = 0; i < base::size(attr_to_types); i++)
       css_property_map.Set(attr_to_types[i].attr, attr_to_types[i].prop_type);
@@ -1165,7 +1162,7 @@
                                       svg_names::kZAttr,
                                   }));
 
-  if (name == kClassAttr)
+  if (name == html_names::kClassAttr)
     return true;
 
   return animatable_attributes.Contains(name);
diff --git a/third_party/blink/renderer/core/timing/performance_long_task_timing.cc b/third_party/blink/renderer/core/timing/performance_long_task_timing.cc
index 73d7f79..8fdafa9 100644
--- a/third_party/blink/renderer/core/timing/performance_long_task_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_long_task_timing.cc
@@ -23,21 +23,10 @@
     const SubTaskAttribution::EntriesVector& sub_task_attributions)
     : PerformanceEntry(name, start_time, end_time) {
   // Only one possible container type exists currently: "iframe".
-  if (RuntimeEnabledFeatures::LongTaskV2Enabled()) {
-    for (auto&& it : sub_task_attributions) {
-      TaskAttributionTiming* attribution_entry = TaskAttributionTiming::Create(
-          it->subTaskName(), "iframe", culprit_frame_src, culprit_frame_id,
-          culprit_frame_name, it->highResStartTime(),
-          it->highResStartTime() + it->highResDuration(), it->scriptURL());
-      attribution_.push_back(*attribution_entry);
-    }
-  } else {
-    // Only one possible task type exists currently: "script".
-    TaskAttributionTiming* attribution_entry =
-        TaskAttributionTiming::Create("unknown", "iframe", culprit_frame_src,
-                                      culprit_frame_id, culprit_frame_name);
-    attribution_.push_back(*attribution_entry);
-  }
+  TaskAttributionTiming* attribution_entry =
+      TaskAttributionTiming::Create("unknown", "iframe", culprit_frame_src,
+                                    culprit_frame_id, culprit_frame_name);
+  attribution_.push_back(*attribution_entry);
 }
 
 PerformanceLongTaskTiming::~PerformanceLongTaskTiming() = default;
diff --git a/third_party/blink/renderer/core/timing/task_attribution_timing.cc b/third_party/blink/renderer/core/timing/task_attribution_timing.cc
index 3ed5fab..8854754 100644
--- a/third_party/blink/renderer/core/timing/task_attribution_timing.cc
+++ b/third_party/blink/renderer/core/timing/task_attribution_timing.cc
@@ -14,16 +14,12 @@
                                              const String& container_type,
                                              const String& container_src,
                                              const String& container_id,
-                                             const String& container_name,
-                                             double start_time,
-                                             double finish_time,
-                                             const String& script_url)
-    : PerformanceEntry(name, start_time, finish_time),
+                                             const String& container_name)
+    : PerformanceEntry(name, 0.0, 0.0),
       container_type_(container_type),
       container_src_(container_src),
       container_id_(container_id),
-      container_name_(container_name),
-      script_url_(script_url) {}
+      container_name_(container_name) {}
 
 TaskAttributionTiming::~TaskAttributionTiming() = default;
 
@@ -35,10 +31,6 @@
   return PerformanceEntry::EntryType::kTaskAttribution;
 }
 
-String TaskAttributionTiming::scriptURL() const {
-  return script_url_;
-}
-
 String TaskAttributionTiming::containerType() const {
   return container_type_;
 }
diff --git a/third_party/blink/renderer/core/timing/task_attribution_timing.h b/third_party/blink/renderer/core/timing/task_attribution_timing.h
index 2d9b0fc..41b6c10 100644
--- a/third_party/blink/renderer/core/timing/task_attribution_timing.h
+++ b/third_party/blink/renderer/core/timing/task_attribution_timing.h
@@ -16,29 +16,13 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  // Used when the LongTaskV2 flag is enabled.
-  static TaskAttributionTiming* Create(const AtomicString& type,
-                                       const String& container_type,
-                                       const String& container_src,
-                                       const String& container_id,
-                                       const String& container_name,
-                                       double start_time,
-                                       double finish_time,
-                                       const String& script_url) {
-    return MakeGarbageCollected<TaskAttributionTiming>(
-        type, container_type, container_src, container_id, container_name,
-        start_time, finish_time, script_url);
-  }
-
-  // Used when the LongTaskV2 flag is disabled.
   static TaskAttributionTiming* Create(const AtomicString& type,
                                        const String& container_type,
                                        const String& container_src,
                                        const String& container_id,
                                        const String& container_name) {
     return MakeGarbageCollected<TaskAttributionTiming>(
-        type, container_type, container_src, container_id, container_name, 0.0,
-        0.0, g_empty_string);
+        type, container_type, container_src, container_id, container_name);
   }
 
   AtomicString entryType() const override;
@@ -48,7 +32,6 @@
   String containerSrc() const;
   String containerId() const;
   String containerName() const;
-  String scriptURL() const;
 
   void Trace(blink::Visitor*) override;
 
@@ -56,10 +39,7 @@
                         const String& container_type,
                         const String& container_src,
                         const String& container_id,
-                        const String& container_name,
-                        double start_time,
-                        double finish_time,
-                        const String& script_url);
+                        const String& container_name);
   ~TaskAttributionTiming() override;
 
  private:
@@ -69,7 +49,6 @@
   String container_src_;
   String container_id_;
   String container_name_;
-  String script_url_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/task_attribution_timing.idl b/third_party/blink/renderer/core/timing/task_attribution_timing.idl
index aeb42ed0..4867f19 100644
--- a/third_party/blink/renderer/core/timing/task_attribution_timing.idl
+++ b/third_party/blink/renderer/core/timing/task_attribution_timing.idl
@@ -5,7 +5,6 @@
 // https://w3c.github.io/longtasks/#sec-TaskAttributionTiming
 [Exposed=Window]
 interface TaskAttributionTiming : PerformanceEntry {
-    [RuntimeEnabled=LongTaskV2] readonly attribute DOMString scriptURL;
     readonly attribute DOMString containerType;
     readonly attribute DOMString containerSrc;
     readonly attribute DOMString containerId;
diff --git a/third_party/blink/renderer/core/workers/worker_thread_test.cc b/third_party/blink/renderer/core/workers/worker_thread_test.cc
index ff3e2686e..2023f78d 100644
--- a/third_party/blink/renderer/core/workers/worker_thread_test.cc
+++ b/third_party/blink/renderer/core/workers/worker_thread_test.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/synchronization/waitable_event.h"
+#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_cache_options.h"
diff --git a/third_party/blink/renderer/core/workers/worker_thread_test_helper.h b/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
index e537c41..cbb54419 100644
--- a/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
+++ b/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "base/synchronization/waitable_event.h"
-#include "services/network/public/mojom/ip_address_space.mojom-blink-forward.h"
+#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_cache_options.h"
diff --git a/third_party/blink/renderer/devtools/front_end/Images/whatsnew.png b/third_party/blink/renderer/devtools/front_end/Images/whatsnew.png
index 397d4aa9..1d81b8f 100644
--- a/third_party/blink/renderer/devtools/front_end/Images/whatsnew.png
+++ b/third_party/blink/renderer/devtools/front_end/Images/whatsnew.png
Binary files differ
diff --git a/third_party/blink/renderer/devtools/front_end/browser_debugger/EventListenerBreakpointsSidebarPane.js b/third_party/blink/renderer/devtools/front_end/browser_debugger/EventListenerBreakpointsSidebarPane.js
index f68a3e54..037bc86 100644
--- a/third_party/blink/renderer/devtools/front_end/browser_debugger/EventListenerBreakpointsSidebarPane.js
+++ b/third_party/blink/renderer/devtools/front_end/browser_debugger/EventListenerBreakpointsSidebarPane.js
@@ -8,8 +8,8 @@
   constructor() {
     super(true);
     this._categoriesTreeOutline = new UI.TreeOutlineInShadow();
-    this._categoriesTreeOutline.element.tabIndex = 0;
     this._categoriesTreeOutline.registerRequiredCSS('browser_debugger/eventListenerBreakpoints.css');
+    this._categoriesTreeOutline.setShowSelectionOnKeyboardFocus(/* show */ true);
     this.contentElement.appendChild(this._categoriesTreeOutline.element);
 
     /** @type {!Map<string, !BrowserDebugger.EventListenerBreakpointsSidebarPane.Item>} */
@@ -21,6 +21,10 @@
         this._createCategory(category);
       }
     }
+    if (categories.length > 0) {
+      const firstCategory = this._categories.get(categories[0]);
+      firstCategory.element.select();
+    }
 
     /** @type {!Map<!SDK.DOMDebuggerModel.EventListenerBreakpoint, !BrowserDebugger.EventListenerBreakpointsSidebarPane.Item>} */
     this._breakpoints = new Map();
@@ -34,14 +38,28 @@
   }
 
   /**
+   * @override
+   */
+  focus() {
+    this._categoriesTreeOutline.forceSelect();
+  }
+
+  /**
    * @param {string} name
    */
   _createCategory(name) {
     const labelNode = UI.CheckboxLabel.create(name);
     labelNode.checkboxElement.addEventListener('click', this._categoryCheckboxClicked.bind(this, name), true);
+    labelNode.checkboxElement.tabIndex = -1;
 
     const treeElement = new UI.TreeElement(labelNode);
-    treeElement.selectable = false;
+    treeElement.listItemElement.addEventListener('keydown', event => {
+      if (event.key === ' ') {
+        this._categories.get(name).checkbox.click();
+        event.consume(true);
+      }
+    });
+    UI.ARIAUtils.setChecked(treeElement.listItemElement, false);
     this._categoriesTreeOutline.appendChild(treeElement);
 
     this._categories.set(name, {element: treeElement, checkbox: labelNode.checkboxElement});
@@ -54,10 +72,17 @@
     const labelNode = UI.CheckboxLabel.create(breakpoint.title());
     labelNode.classList.add('source-code');
     labelNode.checkboxElement.addEventListener('click', this._breakpointCheckboxClicked.bind(this, breakpoint), true);
+    labelNode.checkboxElement.tabIndex = -1;
 
     const treeElement = new UI.TreeElement(labelNode);
+    treeElement.listItemElement.addEventListener('keydown', event => {
+      if (event.key === ' ') {
+        this._breakpoints.get(breakpoint).checkbox.click();
+        event.consume(true);
+      }
+    });
+    UI.ARIAUtils.setChecked(treeElement.listItemElement, false);
     treeElement.listItemElement.createChild('div', 'breakpoint-hit-marker');
-    treeElement.selectable = false;
     this._categories.get(breakpoint.category()).element.appendChild(treeElement);
 
     this._breakpoints.set(breakpoint, {element: treeElement, checkbox: labelNode.checkboxElement});
@@ -70,6 +95,7 @@
 
     if (!details || details.reason !== SDK.DebuggerModel.BreakReason.EventListener || !details.auxData) {
       if (this._highlightedElement) {
+        UI.ARIAUtils.setDescription(this._highlightedElement, '');
         this._highlightedElement.classList.remove('breakpoint-hit');
         delete this._highlightedElement;
       }
@@ -84,6 +110,7 @@
     UI.viewManager.showView('sources.eventListenerBreakpoints');
     this._categories.get(breakpoint.category()).element.expand();
     this._highlightedElement = this._breakpoints.get(breakpoint).element.listItemElement;
+    UI.ARIAUtils.setDescription(this._highlightedElement, ls`breakpoint hit`);
     this._highlightedElement.classList.add('breakpoint-hit');
   }
 
@@ -93,6 +120,8 @@
   _categoryCheckboxClicked(category) {
     const item = this._categories.get(category);
     const enabled = item.checkbox.checked;
+    UI.ARIAUtils.setChecked(item.element.listItemElement, enabled);
+
     for (const breakpoint of this._breakpoints.keys()) {
       if (breakpoint.category() === category) {
         breakpoint.setEnabled(enabled);
@@ -107,6 +136,7 @@
   _breakpointCheckboxClicked(breakpoint) {
     const item = this._breakpoints.get(breakpoint);
     breakpoint.setEnabled(item.checkbox.checked);
+    UI.ARIAUtils.setChecked(item.element.listItemElement, item.checkbox.checked);
 
     let hasEnabled = false;
     let hasDisabled = false;
@@ -120,9 +150,14 @@
       }
     }
 
-    const checkbox = this._categories.get(breakpoint.category()).checkbox;
-    checkbox.checked = hasEnabled;
-    checkbox.indeterminate = hasEnabled && hasDisabled;
+    const category = this._categories.get(breakpoint.category());
+    category.checkbox.checked = hasEnabled;
+    category.checkbox.indeterminate = hasEnabled && hasDisabled;
+    if (category.checkbox.indeterminate) {
+      UI.ARIAUtils.setCheckboxAsIndeterminate(category.element.listItemElement);
+    } else {
+      UI.ARIAUtils.setChecked(category.element.listItemElement, hasEnabled);
+    }
   }
 };
 
diff --git a/third_party/blink/renderer/devtools/front_end/browser_debugger/ObjectEventListenersSidebarPane.js b/third_party/blink/renderer/devtools/front_end/browser_debugger/ObjectEventListenersSidebarPane.js
index 37bdd75c..be9118f 100644
--- a/third_party/blink/renderer/devtools/front_end/browser_debugger/ObjectEventListenersSidebarPane.js
+++ b/third_party/blink/renderer/devtools/front_end/browser_debugger/ObjectEventListenersSidebarPane.js
@@ -8,12 +8,13 @@
 BrowserDebugger.ObjectEventListenersSidebarPane = class extends UI.VBox {
   constructor() {
     super();
-    this._refreshButton = new UI.ToolbarButton(Common.UIString('Refresh'), 'largeicon-refresh');
+    this._refreshButton = new UI.ToolbarButton(ls`Refresh global listeners`, 'largeicon-refresh');
     this._refreshButton.addEventListener(UI.ToolbarButton.Events.Click, this._refreshClick, this);
     this._refreshButton.setEnabled(false);
 
     this._eventListenersView = new EventListeners.EventListenersView(this.update.bind(this));
     this._eventListenersView.show(this.element);
+    this.setDefaultFocusedChild(this._eventListenersView);
   }
 
   /**
diff --git a/third_party/blink/renderer/devtools/front_end/browser_debugger/browser_debugger_strings.grdp b/third_party/blink/renderer/devtools/front_end/browser_debugger/browser_debugger_strings.grdp
index 58fccd7..7cd1d3ed 100644
--- a/third_party/blink/renderer/devtools/front_end/browser_debugger/browser_debugger_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/browser_debugger/browser_debugger_strings.grdp
@@ -12,6 +12,9 @@
   <message name="IDS_DEVTOOLS_39c145d69ad05e44e74017346c116251" desc="A context menu item in the DOMBreakpoints Sidebar Pane of the JavaScript Debugging pane in the Sources panel or the DOM Breakpoints pane in the Elements panel">
     Remove all DOM breakpoints
   </message>
+  <message name="IDS_DEVTOOLS_3ea566249a507705d9a7ff4d3bd31440" desc="Screen reader description of a hit breakpoint in the Sources panel">
+    breakpoint hit
+  </message>
   <message name="IDS_DEVTOOLS_59eaf6955f44a94237b6d26911c1d983" desc="A context menu item in the DOMBreakpoints Sidebar Pane of the JavaScript Debugging pane in the Sources panel or the DOM Breakpoints pane in the Elements panel">
     Break on
   </message>
@@ -21,6 +24,9 @@
   <message name="IDS_DEVTOOLS_66b74432bdc2797086f419010cc5ff86" desc="Title of the 'DOM Breakpoints' tool in the bottom sidebar of the Sources tool">
     DOM Breakpoints
   </message>
+  <message name="IDS_DEVTOOLS_9f76c421048cb58ab03988d2ce1c813e" desc="Label for a button in the sources panel that refreshes the list of global event listeners.">
+    Refresh global listeners
+  </message>
   <message name="IDS_DEVTOOLS_b839f802a330e4d4145cb182e6767f45" desc="Text in XHRBreakpoints Sidebar Pane of the JavaScript Debugging pane in the Sources panel or the DOM Breakpoints pane in the Elements panel">
     Any XHR or fetch
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js b/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js
index 75cfd6d..b382d33 100644
--- a/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js
+++ b/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js
@@ -135,7 +135,6 @@
     /** @type {!Map.<string, !ColorPicker.Spectrum.Palette>} */
     this._palettes = new Map();
     this._palettePanel = this.contentElement.createChild('div', 'palette-panel');
-    this._palettePanel.tabIndex = -1;
     this._palettePanel.addEventListener('keydown', this._onPalettePanelKeydown.bind(this));
     this._palettePanelShowing = false;
     this._paletteSectionContainer = this.contentElement.createChild('div', 'spectrum-palette-container');
@@ -321,6 +320,9 @@
       colorElement.addEventListener(
           'mousedown',
           this._paletteColorSelected.bind(this, palette.colors[i], palette.colorNames[i], palette.matchUserFormat));
+      colorElement.addEventListener(
+          'keydown',
+          this._onPaletteColorKeydown.bind(this, palette.colors[i], palette.colorNames[i], palette.matchUserFormat));
       if (palette.mutable) {
         colorElement.__mutable = true;
         colorElement.__color = palette.colors[i];
@@ -336,6 +338,9 @@
       }
       this._paletteContainer.appendChild(colorElement);
     }
+    if (this._paletteContainer.childNodes.length > 0) {
+      this._paletteContainer.childNodes[0].tabIndex = 0;
+    }
     this._paletteContainerMutable = palette.mutable;
 
     if (palette.mutable) {
@@ -621,6 +626,26 @@
   }
 
   /**
+   * @param {string} colorText
+   * @param {(string|undefined)} colorName
+   * @param {boolean} matchUserFormat
+   * @param {!Event} event
+   */
+  _onPaletteColorKeydown(colorText, colorName, matchUserFormat, event) {
+    if (isEnterOrSpaceKey(event)) {
+      this._paletteColorSelected(colorText, colorName, matchUserFormat);
+      // If this is a long keypress on color palette of type Material then, it needs to handled by _showLightnessShades on same element. So, just stopPropagation instead of consuming it.
+      event.stopPropagation();
+    } else if (event.key === 'ArrowLeft' && event.target.previousElementSibling) {
+      event.target.previousElementSibling.focus();
+      event.consume(true);
+    } else if (event.key === 'ArrowRight' && event.target.nextElementSibling) {
+      event.target.nextElementSibling.focus();
+      event.consume(true);
+    }
+  }
+
+  /**
    * @param {!Common.Event} event
    */
   _addColorToCustomPalette(event) {
diff --git a/third_party/blink/renderer/devtools/front_end/color_picker/spectrum.css b/third_party/blink/renderer/devtools/front_end/color_picker/spectrum.css
index 59978d1..fad47459 100644
--- a/third_party/blink/renderer/devtools/front_end/color_picker/spectrum.css
+++ b/third_party/blink/renderer/devtools/front_end/color_picker/spectrum.css
@@ -373,11 +373,13 @@
     z-index: 14;
 }
 
-.spectrum-palette-color:hover:not(.spectrum-shades-shown) > .spectrum-palette-color-shadow {
+.spectrum-palette-color:hover:not(.spectrum-shades-shown) > .spectrum-palette-color-shadow,
+.spectrum-palette-color:focus:not(.spectrum-shades-shown) > .spectrum-palette-color-shadow {
     opacity: 0.2;
 }
 
-.spectrum-palette-color:hover:not(.spectrum-shades-shown) > .spectrum-palette-color-shadow:first-child {
+.spectrum-palette-color:hover:not(.spectrum-shades-shown) > .spectrum-palette-color-shadow:first-child,
+.spectrum-palette-color:focus:not(.spectrum-shades-shown) > .spectrum-palette-color-shadow:first-child {
     opacity: 0.6;
     top: -3px;
     left: 1px;
diff --git a/third_party/blink/renderer/devtools/front_end/components/Linkifier.js b/third_party/blink/renderer/devtools/front_end/components/Linkifier.js
index 2c46bbc..bc6a339 100644
--- a/third_party/blink/renderer/devtools/front_end/components/Linkifier.js
+++ b/third_party/blink/renderer/devtools/front_end/components/Linkifier.js
@@ -527,7 +527,15 @@
     if (UI.isBeingEdited(/** @type {!Node} */ (event.target)) || link.hasSelection()) {
       return false;
     }
-    const actions = Linkifier._linkActions(link);
+    return Components.Linkifier.invokeFirstAction(link);
+  }
+
+  /**
+   * @param {!Element} link
+   * @return {boolean}
+   */
+  static invokeFirstAction(link) {
+    const actions = Components.Linkifier._linkActions(link);
     if (actions.length) {
       actions[0].handler.call(null);
       return true;
diff --git a/third_party/blink/renderer/devtools/front_end/data_grid/DataGrid.js b/third_party/blink/renderer/devtools/front_end/data_grid/DataGrid.js
index 80a64e7..deaad64 100644
--- a/third_party/blink/renderer/devtools/front_end/data_grid/DataGrid.js
+++ b/third_party/blink/renderer/devtools/front_end/data_grid/DataGrid.js
@@ -1107,7 +1107,8 @@
       }
     }
 
-    if (target.isSelfOrDescendant(this._headerTableBody)) {
+    const isContextMenuKey = (event.button === 0);
+    if (!isContextMenuKey && target.isSelfOrDescendant(this._headerTableBody)) {
       if (this._headerContextMenuCallback) {
         this._headerContextMenuCallback(contextMenu);
       } else {
@@ -1116,7 +1117,16 @@
       return;
     }
 
-    const gridNode = this.dataGridNodeFromNode(target);
+    const gridNode = isContextMenuKey ? this.selectedNode : this.dataGridNodeFromNode(target);
+    if (isContextMenuKey && this.selectedNode) {
+      const boundingRowRect = this.selectedNode.existingElement().getBoundingClientRect();
+      if (boundingRowRect) {
+        const x = (boundingRowRect.right + boundingRowRect.left) / 2;
+        const y = (boundingRowRect.bottom + boundingRowRect.top) / 2;
+        contextMenu.setX(x);
+        contextMenu.setY(y);
+      }
+    }
     if (this._refreshCallback && (!gridNode || gridNode !== this.creationNode)) {
       contextMenu.defaultSection().appendItem(Common.UIString('Refresh'), this._refreshCallback.bind(this));
     }
@@ -1125,6 +1135,16 @@
       if (this._editCallback) {
         if (gridNode === this.creationNode) {
           contextMenu.defaultSection().appendItem(Common.UIString('Add new'), this._startEditing.bind(this, target));
+        } else if (isContextMenuKey) {
+          const firstEditColumnIndex = this._nextEditableColumn(-1);
+          if (firstEditColumnIndex > -1) {
+            const firstColumn = this._visibleColumnsArray[firstEditColumnIndex];
+            if (firstColumn && firstColumn.editable) {
+              contextMenu.defaultSection().appendItem(
+                  ls`Edit "${firstColumn.title}"`,
+                  this._startEditingColumnOfDataGridNode.bind(this, gridNode, firstEditColumnIndex));
+            }
+          }
         } else {
           const columnId = this.columnIdFromNode(target);
           if (columnId && this._columns[columnId].editable) {
diff --git a/third_party/blink/renderer/devtools/front_end/elements/ElementsPanel.js b/third_party/blink/renderer/devtools/front_end/elements/ElementsPanel.js
index b597ad7..d75af07 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/ElementsPanel.js
+++ b/third_party/blink/renderer/devtools/front_end/elements/ElementsPanel.js
@@ -993,6 +993,24 @@
       function onNodeResolved(resolvedNode) {
         panel._pendingNodeReveal = false;
 
+        // A detached node could still have a parent and ownerDocument
+        // properties, which means stepping up through the hierarchy to ensure
+        // that the root node is the document itself. Any break implies
+        // detachment.
+        let currentNode = resolvedNode;
+        while (currentNode.parentNode) {
+          currentNode = currentNode.parentNode;
+        }
+        const isDetached = !(currentNode instanceof SDK.DOMDocument);
+
+        const isDocument = node instanceof SDK.DOMDocument;
+        if (!isDocument && isDetached) {
+          const msg = ls`Node cannot be found in the current page.`;
+          Common.console.warn(msg);
+          reject(new Error(msg));
+          return;
+        }
+
         if (resolvedNode) {
           panel.revealAndSelectNode(resolvedNode, !omitFocus).then(resolve);
           return;
diff --git a/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp b/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
index 109bf48..3da0626c 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
@@ -123,6 +123,9 @@
   <message name="IDS_DEVTOOLS_5c6236c705b0086fbbdb2df3133f8a06" desc="Title of a setting under the Elements category in Settings">
     Reveal DOM node on hover
   </message>
+  <message name="IDS_DEVTOOLS_5e1078f1bee74e1e385b7813280b0c74" desc="Text when node can not be found in page">
+    Node cannot be found in the current page.
+  </message>
   <message name="IDS_DEVTOOLS_5eeb03a9c080c299d4804ac765c818c2" desc="No matches element text content in Styles Sidebar Pane of the Elements panel">
     No matching selector or style
   </message>
@@ -279,4 +282,4 @@
   <message name="IDS_DEVTOOLS_fd9bcae718daadf263a2e73c7745f0a7" desc="Tree element expand all button element button text content in Elements Tree Outline of the Elements panel">
     Show All Nodes (<ph name="VISIBLECHILDREN_LENGTH___EXPANDEDCHILDCOUNT">$1d<ex>3</ex></ph> More)
   </message>
-</grit-part>
\ No newline at end of file
+</grit-part>
diff --git a/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js b/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
index 7022c9c41..21a3796 100644
--- a/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
+++ b/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
@@ -27,12 +27,24 @@
     this.element.appendChild(this._treeOutline.element);
     this._emptyHolder = createElementWithClass('div', 'gray-info-message');
     this._emptyHolder.textContent = Common.UIString('No event listeners');
+    this._emptyHolder.tabIndex = -1;
     this._linkifier = new Components.Linkifier();
     /** @type {!Map<string, !EventListeners.EventListenersTreeElement>} */
     this._treeItemMap = new Map();
   }
 
   /**
+   * @override
+   */
+  focus() {
+    if (!this._emptyHolder.parentNode) {
+      this._treeOutline.forceSelect();
+    } else {
+      this._emptyHolder.focus();
+    }
+  }
+
+  /**
    * @param {!Array<?SDK.RemoteObject>} objects
    * @return {!Promise<undefined>}
    */
diff --git a/third_party/blink/renderer/devtools/front_end/help/ReleaseNoteText.js b/third_party/blink/renderer/devtools/front_end/help/ReleaseNoteText.js
index 727bb003..e0395077 100644
--- a/third_party/blink/renderer/devtools/front_end/help/ReleaseNoteText.js
+++ b/third_party/blink/renderer/devtools/front_end/help/ReleaseNoteText.js
@@ -12,6 +12,50 @@
 
 /** @type {!Array<!Help.ReleaseNote>} */
 Help.releaseNoteText = [
+
+  {
+    version: 21,
+    header: 'Highlights from the Chrome 79 update',
+    highlights: [
+      {
+        title: 'Debug why a cookie was blocked',
+        subtitle: 'Click a resource in the Network panel and go to the updated Cookies tab.',
+        link: 'https://developers.google.com/web/updates/2019/10/devtools#blockedcookies',
+      },
+      {
+        title: 'View cookie values',
+        subtitle: 'Click a row in the Cookies pane in the Application panel to see the cookie\'s value.',
+        link: 'https://developers.google.com/web/updates/2019/10/devtools#cookiepreviews',
+      },
+      {
+        title: 'Simulate prefers-color-scheme and prefers-reduced-motion preferences',
+        subtitle: 'Open the Rendering tab to force your site into dark or light mode or set motion preferences.',
+        link: 'https://developers.google.com/web/updates/2019/10/devtools#userpreferences',
+      },
+      {
+        title: 'Code coverage updates',
+        subtitle: 'More accessible colors, a filter text box, and a new integration with the Sources panel.',
+        link: 'https://developers.google.com/web/updates/2019/10/devtools#coverage',
+      },
+      {
+        title: 'Debug why a network resource was requested',
+        subtitle: 'Click a resource in the Network panel and go to the new Initiator tab.',
+        link: 'https://developers.google.com/web/updates/2019/10/devtools#initiator',
+      },
+      {
+        title: 'Console and Sources panels respect indentation preferences again',
+        subtitle: 'Set your preference in Settings > Preferences > Sources > Default Indentation.',
+        link: 'https://developers.google.com/web/updates/2019/10/devtools#indentation',
+      },
+      {
+        title: 'New shortcuts for cursor navigation',
+        subtitle: 'Press Control+P or Control+N to move your cursor to the line above or below.',
+        link: 'https://developers.google.com/web/updates/2019/10/devtools#console',
+      },
+    ],
+    link: 'https://developers.google.com/web/updates/2019/10/devtools',
+  },
+
   {
     version: 20,
     header: 'Highlights from the Chrome 78 update',
diff --git a/third_party/blink/renderer/devtools/front_end/layer_viewer/LayerDetailsView.js b/third_party/blink/renderer/devtools/front_end/layer_viewer/LayerDetailsView.js
index e2cd8b7..268a15e 100644
--- a/third_party/blink/renderer/devtools/front_end/layer_viewer/LayerDetailsView.js
+++ b/third_party/blink/renderer/devtools/front_end/layer_viewer/LayerDetailsView.js
@@ -230,7 +230,8 @@
     this._compositingReasonsCell.removeChildren();
     const list = this._compositingReasonsCell.createChild('ul');
     for (let i = 0; i < compositingReasons.length; ++i) {
-      let text = LayerViewer.LayerDetailsView.CompositingReasonDetail[compositingReasons[i]] || compositingReasons[i];
+      // The reason is coming straight from third_party/blink/renderer/platform/graphics/compositing_reasons.cc
+      let text = compositingReasons[i];
       // If the text is more than one word but does not terminate with period, add the period.
       if (/\s.*[^.]$/.test(text)) {
         text += '.';
@@ -248,57 +249,6 @@
   PaintProfilerRequested: Symbol('PaintProfilerRequested')
 };
 
-/**
- * @type {!Object.<string, string>}
- */
-LayerViewer.LayerDetailsView.CompositingReasonDetail = {
-  'transform3D': Common.UIString('Composition due to association with an element with a CSS 3D transform.'),
-  'video': Common.UIString('Composition due to association with a <video> element.'),
-  'canvas': Common.UIString('Composition due to the element being a <canvas> element.'),
-  'plugin': Common.UIString('Composition due to association with a plugin.'),
-  'iFrame': Common.UIString('Composition due to association with an <iframe> element.'),
-  'backfaceVisibilityHidden':
-      Common.UIString('Composition due to association with an element with a "backface-visibility: hidden" style.'),
-  'animation': Common.UIString('Composition due to association with an animated element.'),
-  'filters': Common.UIString('Composition due to association with an element with CSS filters applied.'),
-  'scrollDependentPosition': Common.UIString(
-      'Composition due to association with an element with a "position: fixed" or "position: sticky" style.'),
-  'overflowScrollingTouch':
-      Common.UIString('Composition due to association with an element with a "overflow-scrolling: touch" style.'),
-  'blending':
-      Common.UIString('Composition due to association with an element that has blend mode other than "normal".'),
-  'assumedOverlap':
-      Common.UIString('Composition due to association with an element that may overlap other composited elements.'),
-  'overlap': Common.UIString('Composition due to association with an element overlapping other composited elements.'),
-  'negativeZIndexChildren':
-      Common.UIString('Composition due to association with an element with descendants that have a negative z-index.'),
-  'transformWithCompositedDescendants':
-      Common.UIString('Composition due to association with an element with composited descendants.'),
-  'opacityWithCompositedDescendants': Common.UIString(
-      'Composition due to association with an element with opacity applied and composited descendants.'),
-  'maskWithCompositedDescendants':
-      Common.UIString('Composition due to association with a masked element and composited descendants.'),
-  'reflectionWithCompositedDescendants':
-      Common.UIString('Composition due to association with an element with a reflection and composited descendants.'),
-  'filterWithCompositedDescendants': Common.UIString(
-      'Composition due to association with an element with CSS filters applied and composited descendants.'),
-  'blendingWithCompositedDescendants': Common.UIString(
-      'Composition due to association with an element with CSS blending applied and composited descendants.'),
-  'clipsCompositingDescendants':
-      Common.UIString('Composition due to association with an element clipping compositing descendants.'),
-  'perspective': Common.UIString('Composition due to association with an element with perspective applied.'),
-  'preserve3D':
-      Common.UIString('Composition due to association with an element with a "transform-style: preserve-3d" style.'),
-  'root': Common.UIString('Root layer.'),
-  'layerForClip': Common.UIString('Layer for clip.'),
-  'layerForScrollbar': Common.UIString('Layer for scrollbar.'),
-  'layerForScrollingContainer': Common.UIString('Layer for scrolling container.'),
-  'layerForForeground': Common.UIString('Layer for foreground.'),
-  'layerForBackground': Common.UIString('Layer for background.'),
-  'layerForMask': Common.UIString('Layer for mask.'),
-  'layerForVideoOverlay': Common.UIString('Layer for video overlay.'),
-};
-
 LayerViewer.LayerDetailsView._slowScrollRectNames = new Map([
   [SDK.Layer.ScrollRectType.NonFastScrollable, Common.UIString('Non fast scrollable')],
   [SDK.Layer.ScrollRectType.TouchEventHandler, Common.UIString('Touch event handler')],
diff --git a/third_party/blink/renderer/devtools/front_end/layer_viewer/layer_viewer_strings.grdp b/third_party/blink/renderer/devtools/front_end/layer_viewer/layer_viewer_strings.grdp
index f3daa15..b110d59 100644
--- a/third_party/blink/renderer/devtools/front_end/layer_viewer/layer_viewer_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/layer_viewer/layer_viewer_strings.grdp
@@ -1,50 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
-  <message name="IDS_DEVTOOLS_05245b74ad79bb210a68107f7de0c27f" desc="Text in Layer Details View of the Layers panel">
-    Layer for scrolling container.
-  </message>
-  <message name="IDS_DEVTOOLS_08415c56f6c6c2efb0218629836e31bc" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with a &quot;backface-visibility: hidden&quot; style.
-  </message>
-  <message name="IDS_DEVTOOLS_0aa3ae694efd06789ae1867c80d30222" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with a &quot;position: fixed&quot; or &quot;position: sticky&quot; style.
-  </message>
   <message name="IDS_DEVTOOLS_0b4eb322e51b61340668247151340346" desc="Tooltip text that appears when hovering over largeicon rotate button in Transform Controller of the Layers panel">
     Rotate mode (V)
   </message>
-  <message name="IDS_DEVTOOLS_0dd70cbbb618cf0ea39d7e01238ca1b9" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an animated element.
-  </message>
-  <message name="IDS_DEVTOOLS_135d78fcc62ff8df8028c33c768900c5" desc="Text in Layer Details View of the Layers panel">
-    Layer for background.
-  </message>
   <message name="IDS_DEVTOOLS_219e2fb2d37a581ab2bae57397b601f1" desc="Text in Layer Details View of the Layers panel">
     &lt;unnamed&gt;
   </message>
-  <message name="IDS_DEVTOOLS_233612553595af00e5d227e136252422" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with composited descendants.
-  </message>
   <message name="IDS_DEVTOOLS_25d066be1268d5c07cae5cc26e06ad2b" desc="Text in Layer Details View of the Layers panel">
     Slow scroll regions
   </message>
-  <message name="IDS_DEVTOOLS_28b5b4891eb05cc50f537526b24038a9" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with a masked element and composited descendants.
-  </message>
-  <message name="IDS_DEVTOOLS_2a2924d1d77c00d27ce2509c34861804" desc="Text in Layer Details View of the Layers panel">
-    Layer for clip.
-  </message>
-  <message name="IDS_DEVTOOLS_2ca2308d45c04266f0fb51786e407287" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with CSS filters applied and composited descendants.
-  </message>
   <message name="IDS_DEVTOOLS_3228417bf9ed949e7447e7c8dcb17090" desc="Text in Layer Details View of the Layers panel">
     Memory estimate
   </message>
-  <message name="IDS_DEVTOOLS_37de7295335e95cb7caf78303a1edad3" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with a CSS 3D transform.
-  </message>
-  <message name="IDS_DEVTOOLS_398b889c147f3b2cb3c674cfb07315b5" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with opacity applied and composited descendants.
-  </message>
   <message name="IDS_DEVTOOLS_441f5e043a240bac8dca50f90f7be3de" desc="Text in DView of the Layers panel">
     repaints on scroll
   </message>
@@ -66,18 +33,12 @@
   <message name="IDS_DEVTOOLS_4ee55709b19d806bb2f83a264a8a0f75" desc="Sticky box rect element text content in Layer Details View of the Layers panel">
     Sticky Box <ph name="STICKYBOXRECT_WIDTH">$1d<ex>10</ex></ph> × <ph name="STICKYBOXRECT_HEIGHT">$2d<ex>10</ex></ph> (at <ph name="STICKYBOXRECT_X">$3d<ex>10</ex></ph>, <ph name="STICKYBOXRECT_Y">$4d<ex>10</ex></ph>)
   </message>
-  <message name="IDS_DEVTOOLS_5337a3314b26561ccf364e1777455933" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with a &lt;video&gt; element.
-  </message>
   <message name="IDS_DEVTOOLS_5639e2813d9549835010567a48349127" desc="Details text content in Layer Tree Outline of the Layers panel">
     ''' (<ph name="THIS__LAYER_WIDTH__">$1d<ex>10</ex></ph> × <ph name="THIS__LAYER_HEIGHT__">$2d<ex>10</ex></ph>)
   </message>
   <message name="IDS_DEVTOOLS_602f4c0713480f019b24f09218dcc7e0" desc="A context menu item in the DView of the Layers panel">
     Reset View
   </message>
-  <message name="IDS_DEVTOOLS_697e0fb75c6cf44734697517d44b5922" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element clipping compositing descendants.
-  </message>
   <message name="IDS_DEVTOOLS_6fac3a4381f0fb07bbf498a08d301d40" desc="Text in Layer Details View of the Layers panel">
     Repaints on scroll
   </message>
@@ -90,21 +51,12 @@
   <message name="IDS_DEVTOOLS_71084df87a4cc161f7542a0f5295a950" desc="Text in Layer Details View of the Layers panel">
     Select a layer to see its details
   </message>
-  <message name="IDS_DEVTOOLS_73c57dbc070bac535d75b4abe9f614a3" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element overlapping other composited elements.
-  </message>
   <message name="IDS_DEVTOOLS_74248c725e00bf9fe04df4e35b249a19" desc="Text in Paint Profiler View of the Layers panel">
     Misc
   </message>
   <message name="IDS_DEVTOOLS_74ae7b35b54cd38a932ccc4f1d1c3775" desc="Text in Layer Details View of the Layers panel">
     Touch event handler
   </message>
-  <message name="IDS_DEVTOOLS_791faaf55ae39199cc0b4edde837f07a" desc="Text in Layer Details View of the Layers panel">
-    Layer for video overlay.
-  </message>
-  <message name="IDS_DEVTOOLS_7eb830853b9ed22a567e4d7582eafe7c" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with CSS filters applied.
-  </message>
   <message name="IDS_DEVTOOLS_82505980aa6b0cabf4cb8013a8a43b62" desc="Text in Layer Details View of the Layers panel">
     Non fast scrollable
   </message>
@@ -114,30 +66,15 @@
   <message name="IDS_DEVTOOLS_86ee74baff479d85d18f2cda9f8a9518" desc="Text in Paint Profiler View of the Layers panel">
     Bitmap
   </message>
-  <message name="IDS_DEVTOOLS_8810c4e0c498b81a6e4adaeabcb9f624" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with a &quot;overflow-scrolling: touch&quot; style.
-  </message>
-  <message name="IDS_DEVTOOLS_894982f4a3bc0e770cece61dd12dde55" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an &lt;iframe&gt; element.
-  </message>
   <message name="IDS_DEVTOOLS_932a3ef55ce8133be7b33ded2992fd97" desc="Text in Layer Details View of the Layers panel">
     Compositing Reasons
   </message>
-  <message name="IDS_DEVTOOLS_93c3341232864688bcea4d53dff1bf10" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with perspective applied.
-  </message>
-  <message name="IDS_DEVTOOLS_991bd14abd9e6004de4e7b9bc0769d9d" desc="Text in Layer Details View of the Layers panel">
-    Layer for foreground.
-  </message>
   <message name="IDS_DEVTOOLS_9b8379825de60ccb900acf83934e1409" desc="Text in Layer View Host of the Layers panel">
     Show internal layers
   </message>
   <message name="IDS_DEVTOOLS_a673813c8d6c5f511cc4d80830fc4503" desc="Text in DView of the Layers panel">
     Can&apos;t display layers,
   </message>
-  <message name="IDS_DEVTOOLS_aefd09cd861bbb01d381cfd74856e877" desc="Text in Layer Details View of the Layers panel">
-    Composition due to the element being a &lt;canvas&gt; element.
-  </message>
   <message name="IDS_DEVTOOLS_b336b2cbaa7391e6e0dfef888d35a837" desc="Text in DView of the Layers panel">
     touch event listener
   </message>
@@ -162,21 +99,6 @@
   <message name="IDS_DEVTOOLS_ce1efcf7f040106cc8cb2f24c9efee35" desc="A context menu item in the DView of the Layers panel">
     Show Paint Profiler
   </message>
-  <message name="IDS_DEVTOOLS_cf4db365a841e7208aad0d474cbfa8e3" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with descendants that have a negative z-index.
-  </message>
-  <message name="IDS_DEVTOOLS_d446f65fe70f9a640908576792497e25" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with a reflection and composited descendants.
-  </message>
-  <message name="IDS_DEVTOOLS_dd4d335a4deb975546acc9fb5aa3ffce" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element that may overlap other composited elements.
-  </message>
-  <message name="IDS_DEVTOOLS_dff689f21faf977ee865e312c011c84a" desc="Text in Layer Details View of the Layers panel">
-    Root layer.
-  </message>
-  <message name="IDS_DEVTOOLS_e397c87a940fc2c10225a407c7f124a7" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with a plugin.
-  </message>
   <message name="IDS_DEVTOOLS_e763043c4af617c6f33da361d990a77b" desc="Tooltip text that appears when hovering over the largeicon center button in the Transform Controller of the Layers panel">
     Reset transform (0)
   </message>
@@ -186,21 +108,6 @@
   <message name="IDS_DEVTOOLS_ed3a481889f16ef0aab2349f5d8b5f12" desc="Containing block rect element text content in Layer Details View of the Layers panel">
     Containing Block <ph name="CONTAININGBLOCKRECT_WIDTH">$1d<ex>10</ex></ph> × <ph name="CONTAININGBLOCKRECT_HEIGHT">$2d<ex>10</ex></ph> (at <ph name="CONTAININGBLOCKRECT_X">$3d<ex>10</ex></ph>, <ph name="CONTAININGBLOCKRECT_Y">$4d<ex>10</ex></ph>)
   </message>
-  <message name="IDS_DEVTOOLS_f1eb415fdb8a15856c29cc2b679e416b" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with a &quot;transform-style: preserve-3d&quot; style.
-  </message>
-  <message name="IDS_DEVTOOLS_f2a4f55a424726ae504f2e694e3dc2a2" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element with CSS blending applied and composited descendants.
-  </message>
-  <message name="IDS_DEVTOOLS_fa055d01c0a331d642c2f55a39d4e1bb" desc="Text in Layer Details View of the Layers panel">
-    Composition due to association with an element that has blend mode other than &quot;normal&quot;.
-  </message>
-  <message name="IDS_DEVTOOLS_fb8ed4e0cd9d6ac3630df425c80d9036" desc="Text in Layer Details View of the Layers panel">
-    Layer for mask.
-  </message>
-  <message name="IDS_DEVTOOLS_fd63c694ddc8073cf10bb8025aa9aaaf" desc="Text in Layer Details View of the Layers panel">
-    Layer for scrollbar.
-  </message>
   <message name="IDS_DEVTOOLS_a3ed19b48390aad906de764e0476142d" desc="Text for a checkbox in the toolbar of the Layers panel to show the paints of the page">
     Paints
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/main/Main.js b/third_party/blink/renderer/devtools/front_end/main/Main.js
index 7d62c1a..d87913f 100644
--- a/third_party/blink/renderer/devtools/front_end/main/Main.js
+++ b/third_party/blink/renderer/devtools/front_end/main/Main.js
@@ -614,10 +614,21 @@
       contextMenu.headerSection().appendCustomItem(dockItemElement);
     }
 
+
+    const button = this._item.element;
+
     /**
      * @param {string} side
+     * @suppressGlobalPropertiesCheck
      */
     function setDockSide(side) {
+      const hadKeyboardFocus = document.deepActiveElement().hasAttribute('data-keyboard-focus');
+      Components.dockController.once(Components.DockController.Events.AfterDockSideChanged).then(() => {
+        button.focus();
+        if (hadKeyboardFocus) {
+          UI.markAsFocusedByKeyboard(button);
+        }
+      });
       Components.dockController.setDockSide(side);
       contextMenu.discard();
     }
diff --git a/third_party/blink/renderer/devtools/front_end/main/module.json b/third_party/blink/renderer/devtools/front_end/main/module.json
index 2f51ce7..f1625822 100644
--- a/third_party/blink/renderer/devtools/front_end/main/module.json
+++ b/third_party/blink/renderer/devtools/front_end/main/module.json
@@ -185,6 +185,14 @@
                 {
                     "platform": "mac",
                     "shortcut": "Meta+G"
+                },
+                {
+                    "platform": "windows,linux",
+                    "shortcut": "Ctrl+G"
+                },
+                {
+                    "platform": "windows,linux",
+                    "shortcut": "F3"
                 }
             ]
         },
@@ -196,6 +204,14 @@
                 {
                     "platform": "mac",
                     "shortcut": "Meta+Shift+G"
+                },
+                {
+                    "platform": "windows,linux",
+                    "shortcut": "Ctrl+Shift+G"
+                },
+                {
+                    "platform": "windows,linux",
+                    "shortcut": "Shift+F3"
                 }
             ]
         },
diff --git a/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js b/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js
index a5d90213..957bd8b 100644
--- a/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js
+++ b/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js
@@ -74,15 +74,28 @@
    * @return {!Element}
    */
   static defaultObjectPresentation(object, linkifier, skipProto, readOnly) {
-    const componentRoot = createElementWithClass('span', 'source-code');
-    const shadowRoot = UI.createShadowRootWithCoreStyles(componentRoot, 'object_ui/objectValue.css');
-    shadowRoot.appendChild(
-        ObjectUI.ObjectPropertiesSection.createValueElement(object, false /* wasThrown */, true /* showPreview */));
+    const objectPropertiesSection =
+        ObjectUI.ObjectPropertiesSection.defaultObjectPropertiesSection(object, linkifier, skipProto, readOnly);
     if (!object.hasChildren) {
-      return componentRoot;
+      return objectPropertiesSection.titleElement;
+    } else {
+      return objectPropertiesSection.element;
     }
+  }
 
-    const objectPropertiesSection = new ObjectUI.ObjectPropertiesSection(object, componentRoot, linkifier);
+  /**
+   * @param {!SDK.RemoteObject} object
+   * @param {!Components.Linkifier=} linkifier
+   * @param {boolean=} skipProto
+   * @param {boolean=} readOnly
+   * @return {!ObjectUI.ObjectPropertiesSection}
+   */
+  static defaultObjectPropertiesSection(object, linkifier, skipProto, readOnly) {
+    const titleElement = createElementWithClass('span', 'source-code');
+    const shadowRoot = UI.createShadowRootWithCoreStyles(titleElement, 'object_ui/objectValue.css');
+    shadowRoot.appendChild(
+        ObjectUI.ObjectPropertiesSection.createValueElement(object, /* wasThrown */ false, /* showPreview */ true));
+    const objectPropertiesSection = new ObjectUI.ObjectPropertiesSection(object, titleElement, linkifier);
     objectPropertiesSection.editable = false;
     if (skipProto) {
       objectPropertiesSection.skipProto();
@@ -91,7 +104,7 @@
       objectPropertiesSection.setEditable(false);
     }
 
-    return objectPropertiesSection.element;
+    return objectPropertiesSection;
   }
 
   /**
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotView.js b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotView.js
index 0d15f157..bc61696 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotView.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotView.js
@@ -1757,6 +1757,65 @@
     super();
     this._heapProfilerModel = heapProfilerModel;
     this._linkifier = new Components.Linkifier();
+    /** @type {!Array<!Element>} */
+    this._frameElements = [];
+  }
+
+  /**
+   * @param {!Element} link
+   * @param {!Event} event
+   */
+  _onContextMenu(link, event) {
+    const contextMenu = new UI.ContextMenu(event);
+    if (!contextMenu.containsTarget(link)) {
+      contextMenu.appendApplicableItems(link);
+    }
+    contextMenu.show();
+    event.consume(true);
+  }
+
+  /**
+   * @param {!Event} event
+   */
+  _onStackViewKeydown(event) {
+    const target = /** @type {?Element} */ (event.target);
+    if (!target) {
+      return;
+    }
+    if (isEnterKey(event)) {
+      const link = target._linkElement;
+      if (!link) {
+        return;
+      }
+      if (Components.Linkifier.invokeFirstAction(link)) {
+        event.consume(true);
+      }
+      return;
+    }
+
+    let navDown;
+    if (event.key === 'ArrowUp') {
+      navDown = false;
+    } else if (event.key === 'ArrowDown') {
+      navDown = true;
+    } else {
+      return;
+    }
+
+    const index = this._frameElements.indexOf(target);
+    if (index === -1) {
+      return;
+    }
+    const nextIndex = navDown ? index + 1 : index - 1;
+    if (nextIndex < 0 || nextIndex >= this._frameElements.length) {
+      return;
+    }
+
+    const nextFrame = this._frameElements[nextIndex];
+    nextFrame.tabIndex = 0;
+    target.tabIndex = -1;
+    nextFrame.focus();
+    event.consume(true);
   }
 
   /**
@@ -1775,8 +1834,11 @@
     }
 
     const stackDiv = this.element.createChild('div', 'heap-allocation-stack');
+    stackDiv.addEventListener('keydown', this._onStackViewKeydown.bind(this), false);
     for (const frame of frames) {
       const frameDiv = stackDiv.createChild('div', 'stack-frame');
+      this._frameElements.push(frameDiv);
+      frameDiv.tabIndex = -1;
       const name = frameDiv.createChild('div');
       name.textContent = UI.beautifyFunctionName(frame.functionName);
       if (!frame.scriptId) {
@@ -1786,11 +1848,15 @@
           this._heapProfilerModel ? this._heapProfilerModel.target() : null, String(frame.scriptId), frame.scriptName,
           frame.line - 1, frame.column - 1);
       frameDiv.appendChild(urlElement);
+      frameDiv._linkElement = urlElement;
+      frameDiv.addEventListener('contextmenu', this._onContextMenu.bind(this, urlElement));
     }
+    this._frameElements[0].tabIndex = 0;
   }
 
   clear() {
     this.element.removeChildren();
+    this._frameElements = [];
     this._linkifier.reset();
   }
 };
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/ProfileDataGrid.js b/third_party/blink/renderer/devtools/front_end/profiler/ProfileDataGrid.js
index 7e7ca9d6..c69445bb 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/ProfileDataGrid.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/ProfileDataGrid.js
@@ -51,6 +51,8 @@
     this.functionName = UI.beautifyFunctionName(profileNode.functionName);
     this._deoptReason = profileNode.deoptReason || '';
     this.url = profileNode.url;
+    /** @type {?Element} */
+    this.linkElement = null;
   }
 
   /**
@@ -188,6 +190,7 @@
         }
         urlElement.style.maxWidth = '75%';
         cell.appendChild(urlElement);
+        this.linkElement = urlElement;
         break;
 
       default:
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js b/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js
index 3f74445b..43e5e6a 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js
@@ -32,6 +32,7 @@
     this.dataGrid.addEventListener(DataGrid.DataGrid.Events.SortingChanged, this._sortProfile, this);
     this.dataGrid.addEventListener(DataGrid.DataGrid.Events.SelectedNode, this._nodeSelected.bind(this, true));
     this.dataGrid.addEventListener(DataGrid.DataGrid.Events.DeselectedNode, this._nodeSelected.bind(this, false));
+    this.dataGrid.setRowContextMenuCallback(this._populateContextMenu.bind(this));
 
     this.viewSelectComboBox = new UI.ToolbarComboBox(this._changeView.bind(this), ls`Profile view mode`);
 
@@ -175,6 +176,17 @@
   }
 
   /**
+   * @param {!UI.ContextMenu} contextMenu
+   * @param {!DataGrid.DataGridNode} gridNode
+   */
+  _populateContextMenu(contextMenu, gridNode) {
+    const node = /** @type {!Profiler.ProfileDataGridNode} */ (gridNode);
+    if (node.linkElement && !contextMenu.containsTarget(node.linkElement)) {
+      contextMenu.appendApplicableItems(node.linkElement);
+    }
+  }
+
+  /**
    * @override
    */
   willHide() {
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/heapProfiler.css b/third_party/blink/renderer/devtools/front_end/profiler/heapProfiler.css
index 45027e4..9db15fd1 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/heapProfiler.css
+++ b/third_party/blink/renderer/devtools/front_end/profiler/heapProfiler.css
@@ -189,10 +189,27 @@
     padding: 2px;
 }
 
+.heap-allocation-stack .stack-frame:hover:not(:focus) {
+    background-color: rgba(0, 0, 0, 0.1);
+}
+
+.heap-allocation-stack .stack-frame:focus {
+    background-color: var(--selection-bg-color);
+    color: var(--selection-fg-color);
+}
+
+.heap-allocation-stack .stack-frame:focus:hover {
+    background-color: var(--accent-color-hover);
+}
+
 .heap-allocation-stack .stack-frame .devtools-link {
     color: rgb(33%, 33%, 33%);
 }
 
+.heap-allocation-stack .stack-frame:focus .devtools-link {
+    color: var(--selection-fg-color);
+}
+
 .no-heap-allocation-stack {
     padding: 5px;
 }
diff --git a/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js b/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js
index e0f5b9a..61114e51 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js
@@ -252,6 +252,22 @@
     this._updateData(false);
   }
 
+  /**
+   * @param {!UI.ContextMenu} contextMenu
+   * @param {!DataGrid.DataGridNode} gridNode
+   */
+  _populateContextMenu(contextMenu, gridNode) {
+    const node = /** @type {!Resources.IDBDataGridNode} */ (gridNode);
+    if (node.valueObjectPresentation) {
+      contextMenu.revealSection().appendItem(ls`Expand Recursively`, () => {
+        node.valueObjectPresentation.objectTreeElement().expandRecursively();
+      });
+      contextMenu.revealSection().appendItem(ls`Collapse`, () => {
+        node.valueObjectPresentation.objectTreeElement().collapse();
+      });
+    }
+  }
+
   refreshData() {
     this._updateData(true);
   }
@@ -268,6 +284,7 @@
       this._dataGrid.asWidget().detach();
     }
     this._dataGrid = this._createDataGrid();
+    this._dataGrid.setRowContextMenuCallback(this._populateContextMenu.bind(this));
     this._dataGrid.asWidget().show(this.element);
 
     this._skipCount = 0;
@@ -443,6 +460,8 @@
   constructor(data) {
     super(data, false);
     this.selectable = true;
+    /** @type {?ObjectUI.ObjectPropertiesSection} */
+    this.valueObjectPresentation = null;
   }
 
   /**
@@ -455,10 +474,17 @@
 
     switch (columnIdentifier) {
       case 'value':
+        cell.removeChildren();
+        const objectPropSection = ObjectUI.ObjectPropertiesSection.defaultObjectPropertiesSection(
+            value, undefined /* linkifier */, true /* skipProto */, true /* readOnly */);
+        cell.appendChild(objectPropSection.element);
+        this.valueObjectPresentation = objectPropSection;
+        break;
       case 'key':
       case 'primaryKey':
         cell.removeChildren();
-        const objectElement = ObjectUI.ObjectPropertiesSection.defaultObjectPresentation(value, undefined, true, true);
+        const objectElement = ObjectUI.ObjectPropertiesSection.defaultObjectPresentation(
+            value, undefined /* linkifier */, true /* skipProto */, true /* readOnly */);
         cell.appendChild(objectElement);
         break;
       default:
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
index 01b793a..7a9e2cc 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
@@ -243,6 +243,7 @@
     if (!section) {
       const title = registration.scopeURL;
       const uiSection = this._getReportViewForOrigin(registration.securityOrigin).appendSection(title);
+      uiSection.setUiGroupTitle(ls`Service worker for ${title}`);
       uiSection[this._registrationSymbol] = registration;
       section = new Resources.ServiceWorkersView.Section(
           /** @type {!SDK.ServiceWorkerManager} */ (this._manager), uiSection, registration);
diff --git a/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp b/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
index 5327b2f..c0ba9070 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
@@ -52,6 +52,9 @@
   <message name="IDS_DEVTOOLS_29104d3ede0231043a4b92a90b9c2873" desc="Title of needs refresh in indexed dbviews of the application panel">
     Some entries may have been modified
   </message>
+  <message name="IDS_DEVTOOLS_2b31634e3cfef1bfdd7d0d2cdfdc3f9d" desc="Text in Context menu for collapsing objects in IndexedDB tables">
+    Collapse
+  </message>
   <message name="IDS_DEVTOOLS_2bf0a735c3ff964861a2b55319edd355" desc="Text in Clear Storage View of the Application panel">
     Unregister service workers
   </message>
@@ -70,6 +73,9 @@
   <message name="IDS_DEVTOOLS_368d9ac76af05f714092bc808a426bfc" desc="Text in App Manifest View of the Application panel">
     Background color
   </message>
+  <message name="IDS_DEVTOOLS_36e256f915286539621b57d8c80f0105" desc="Text in Context menu for expanding objects in IndexedDB tables">
+    Expand Recursively
+  </message>
   <message name="IDS_DEVTOOLS_37acadd70183e7ed00222ff38248a6ff" desc="Quota row text content in Clear Storage View of the Application panel">
     <ph name="NUMBER_BYTESTOSTRING_RESPONSE_USAGE_">$1s<ex>30 MB</ex></ph> used out of <ph name="NUMBER_BYTESTOSTRING_RESPONSE_QUOTA_">$2s<ex>100 MB</ex></ph> storage quota. '''
   </message>
@@ -203,6 +209,9 @@
   <message name="IDS_DEVTOOLS_9999106349673567ecaf1c32c07301a5" desc="Text in Service Workers View of the Application panel">
     #<ph name="WAITING_ID">$1s<ex>2</ex></ph> waiting to activate
   </message>
+  <message name="IDS_DEVTOOLS_99ccbbe2741019f0bbc87f4eb83dbc1d" desc="Screen reader title for a section of the Service Workers view of the Application panel">
+    Service worker for <ph name="TITLE"><ex>https://example.com</ex>$1s</ph>
+  </message>
   <message name="IDS_DEVTOOLS_9b2a7456cec10d8b5ab8ce656598320b" desc="Text in Indexed DBViews of the Application panel">
     Key path: '''
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/timeline_model/TracingLayerTree.js b/third_party/blink/renderer/devtools/front_end/timeline_model/TracingLayerTree.js
index 9ec0ad7..7e0c5cc 100644
--- a/third_party/blink/renderer/devtools/front_end/timeline_model/TracingLayerTree.js
+++ b/third_party/blink/renderer/devtools/front_end/timeline_model/TracingLayerTree.js
@@ -190,7 +190,12 @@
     this._parent = null;
     this._quad = payload.layer_quad || [];
     this._createScrollRects(payload);
-    this._compositingReasons = payload.compositing_reasons || [];
+
+    // Keep payload.compositing_reasons as a default
+    // but use the newer payload.debug_info.compositing_reasons
+    // if the first one is not set.
+    this._compositingReasons =
+        payload.compositing_reasons || (payload.debug_info && payload.debug_info.compositing_reasons) || [];
     this._drawsContent = !!payload.draws_content;
     this._gpuMemoryUsage = payload.gpu_memory_usage;
     this._paints = [];
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js b/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js
index 476f9b75..cc6157f 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js
@@ -473,6 +473,20 @@
   }
 
   /**
+   * @param {number} x
+   */
+  setX(x) {
+    this._x = x;
+  }
+
+  /**
+   * @param {number} y
+   */
+  setY(y) {
+    this._y = y;
+  }
+
+  /**
    * @param {number} id
    * @param {function(?)} handler
    */
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ReportView.js b/third_party/blink/renderer/devtools/front_end/ui/ReportView.js
index 0be2ec8..be188a1 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ReportView.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/ReportView.js
@@ -151,6 +151,15 @@
   }
 
   /**
+   * Declares the overall container to be a group and assigns a title.
+   * @param {string} groupTitle
+   */
+  setUiGroupTitle(groupTitle) {
+    UI.ARIAUtils.markAsGroup(this.element);
+    UI.ARIAUtils.setAccessibleName(this.element, groupTitle);
+  }
+
+  /**
    * @return {!UI.Toolbar}
    */
   createToolbar() {
diff --git a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
index 2fa9109..aa22ef7 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
@@ -836,9 +836,14 @@
   if (!UI._keyboardFocus) {
     return;
   }
+
+  UI.markAsFocusedByKeyboard(element);
+}
+
+UI.markAsFocusedByKeyboard = function(element) {
   element.setAttribute('data-keyboard-focus', 'true');
   element.addEventListener('blur', () => element.removeAttribute('data-keyboard-focus'), {once: true, capture: true});
-}
+};
 
 /**
  * @unrestricted
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 9d49a32..dd4f45e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -116,8 +116,7 @@
 AXLayoutObject::AXLayoutObject(LayoutObject* layout_object,
                                AXObjectCacheImpl& ax_object_cache)
     : AXNodeObject(layout_object->GetNode(), ax_object_cache),
-      layout_object_(layout_object),
-      is_autofill_available_(false) {
+      layout_object_(layout_object) {
 // TODO(aleventhal) Get correct current state of autofill.
 #if DCHECK_IS_ON()
   layout_object_->SetHasAXObject(true);
@@ -2347,11 +2346,15 @@
   }
 }
 
-void AXLayoutObject::HandleAutofillStateChanged(bool available) {
-  if (is_autofill_available_ != available) {
-    is_autofill_available_ = available;
-    AXObjectCache().MarkAXObjectDirty(this, false);
-  }
+bool AXLayoutObject::IsAutofillAvailable() const {
+  // Autofill state is stored in AXObjectCache.
+  WebAXAutofillState state = AXObjectCache().GetAutofillState(AXObjectID());
+  return state == WebAXAutofillState::kAutofillAvailable;
+}
+
+void AXLayoutObject::HandleAutofillStateChanged(WebAXAutofillState state) {
+  // Autofill state is stored in AXObjectCache.
+  AXObjectCache().SetAutofillState(AXObjectID(), state);
 }
 
 void AXLayoutObject::TextChanged() {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
index db47d6c..fef4861 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
@@ -77,7 +77,7 @@
   bool IsAXLayoutObject() const override { return true; }
 
   // Check object role or purpose.
-  bool IsAutofillAvailable() override { return is_autofill_available_; }
+  bool IsAutofillAvailable() const override;
   bool IsDefault() const override;
   bool IsEditable() const override;
   bool IsRichlyEditable() const override;
@@ -186,8 +186,8 @@
   // Notifications that this object may have changed.
   void HandleActiveDescendantChanged() override;
   void HandleAriaExpandedChanged() override;
-  // Called when autofill becomes available/unavailable on a form control.
-  void HandleAutofillStateChanged(bool) override;
+  // Called when autofill/autocomplete state changes on a form control.
+  void HandleAutofillStateChanged(WebAXAutofillState state) override;
   void TextChanged() override;
 
   // For a table.
@@ -239,8 +239,6 @@
   TextDecorationStyleToAXTextDecorationStyle(
       const ETextDecorationStyle text_decoration_style);
 
-  bool is_autofill_available_;
-
   DISALLOW_COPY_AND_ASSIGN(AXLayoutObject);
 };
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index dadedf3..5b2ea42 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -519,7 +519,7 @@
 
   if (IsHTMLSummaryElement(*GetNode())) {
     ContainerNode* parent = LayoutTreeBuilderTraversal::Parent(*GetNode());
-    if (parent && IsHTMLSlotElement(parent))
+    if (IsA<HTMLSlotElement>(parent))
       parent = LayoutTreeBuilderTraversal::Parent(*parent);
     if (parent && IsA<HTMLDetailsElement>(parent))
       return ax::mojom::Role::kDisclosureTriangle;
@@ -1376,6 +1376,11 @@
 }
 
 String AXNodeObject::AutoComplete() const {
+  // Check cache for auto complete state.
+  if (AXObjectCache().GetAutofillState(AXObjectID()) ==
+      WebAXAutofillState::kAutocompleteAvailable)
+    return "list";
+
   if (IsNativeTextControl() || IsARIATextControl()) {
     const AtomicString& aria_auto_complete =
         GetAOMPropertyOrARIAAttribute(AOMStringProperty::kAutocomplete)
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 7cd716c..5395fdee 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -1212,11 +1212,6 @@
   if (!GetNode())
     return false;
 
-  // Disallow inert nodes from the tree to ensure the dialog is always the
-  // first child of the root.
-  if (GetNode()->IsInert())
-    return false;
-
   // If the node is part of the user agent shadow dom, or has the explicit
   // internal Role::kIgnored, they aren't interesting for paragraph navigation
   // or LabelledBy/DescribedBy relationships.
@@ -2562,8 +2557,10 @@
 
 void AXObject::ClearChildren() {
   // Detach all weak pointers from objects to their parents.
-  for (const auto& child : children_)
-    child->DetachFromParent();
+  for (const auto& child : children_) {
+    if (child->parent_ == this)
+      child->DetachFromParent();
+  }
 
   children_.clear();
   have_children_ = false;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 78dd43b..fc0cb6b 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -35,6 +35,7 @@
 #include <ostream>
 
 #include "base/macros.h"
+#include "third_party/blink/public/web/web_ax_enums.h"
 #include "third_party/blink/renderer/core/accessibility/axid.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/editing/markers/document_marker.h"
@@ -469,7 +470,7 @@
   }
 
   // Check object state.
-  virtual bool IsAutofillAvailable() { return false; }
+  virtual bool IsAutofillAvailable() const { return false; }
   virtual bool IsClickable() const;
   virtual AccessibilityExpanded IsExpanded() const {
     return kExpandedUndefined;
@@ -1000,7 +1001,7 @@
   // Notifications that this object may have changed.
   virtual void ChildrenChanged() {}
   virtual void HandleActiveDescendantChanged() {}
-  virtual void HandleAutofillStateChanged(bool) {}
+  virtual void HandleAutofillStateChanged(WebAXAutofillState) {}
   virtual void HandleAriaExpandedChanged() {}
   virtual void SelectionChanged();
   virtual void TextChanged() {}
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index ae2f688..94d449f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -646,7 +646,7 @@
   if (!ax_id)
     return;
 
-  // first fetch object to operate some cleanup functions on it
+  // First, fetch object to operate some cleanup functions on it.
   AXObject* obj = objects_.at(ax_id);
   if (!obj)
     return;
@@ -654,7 +654,7 @@
   obj->Detach();
   RemoveAXID(obj);
 
-  // finally remove the object
+  // Finally, remove the object.
   if (!objects_.Take(ax_id))
     return;
 
@@ -744,6 +744,7 @@
   DCHECK(ids_in_use_.Contains(obj_id));
   object->SetAXObjectID(0);
   ids_in_use_.erase(obj_id);
+  autofill_state_map_.erase(obj_id);
 
   relation_cache_->RemoveAXID(obj_id);
 }
@@ -1908,4 +1909,19 @@
   return ax::mojom::EventFrom::kPage;
 }
 
+WebAXAutofillState AXObjectCacheImpl::GetAutofillState(AXID id) const {
+  auto iter = autofill_state_map_.find(id);
+  if (iter == autofill_state_map_.end())
+    return WebAXAutofillState::kNoSuggestions;
+  return iter->value;
+}
+
+void AXObjectCacheImpl::SetAutofillState(AXID id, WebAXAutofillState state) {
+  WebAXAutofillState previous_state = GetAutofillState(id);
+  if (state != previous_state) {
+    autofill_state_map_.Set(id, state);
+    MarkAXObjectDirty(ObjectFromAXID(id), false);
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
index c387aae9..dbbf20bc 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -37,6 +37,8 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/permissions/permission_status.mojom-blink.h"
+#include "third_party/blink/public/web/web_ax_enums.h"
 #include "third_party/blink/renderer/core/accessibility/ax_object_cache_base.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
@@ -258,6 +260,9 @@
 
   void set_is_handling_action(bool value) { is_handling_action_ = value; }
 
+  WebAXAutofillState GetAutofillState(AXID id) const;
+  void SetAutofillState(AXID id, WebAXAutofillState state);
+
  protected:
   void PostPlatformNotification(
       AXObject*,
@@ -394,6 +399,9 @@
 
   bool is_handling_action_ = false;
 
+  // Maps ids to their object's autofill state.
+  HashMap<AXID, WebAXAutofillState> autofill_state_map_;
+
   DISALLOW_COPY_AND_ASSIGN(AXObjectCacheImpl);
 };
 
diff --git a/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc b/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
index 909f84af..87a4e50 100644
--- a/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
+++ b/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
 #include "third_party/blink/renderer/core/dom/node.h"
 #include "third_party/blink/renderer/core/dom/node_list.h"
+#include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/inspector/identifiers_factory.h"
@@ -724,6 +725,8 @@
   Document* document = inspected_frames_->Root()->GetDocument();
   if (!document)
     return Response::Error("No document.");
+  if (document->View()->NeedsLayout() || document->NeedsLayoutTreeUpdate())
+    document->UpdateStyleAndLayout();
   *nodes = std::make_unique<protocol::Array<protocol::Accessibility::AXNode>>();
   AXContext ax_context(*document);
   AXObjectCacheImpl& cache = ToAXObjectCacheImpl(ax_context.GetAXObjectCache());
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc
index 6e95e414..72d6025a 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc
@@ -59,7 +59,7 @@
 WorkletAnimationId AnimationWorklet::NextWorkletAnimationId() {
   // Id starts from 1. This way it safe to use it as key in hashmap with default
   // key traits.
-  return WorkletAnimationId(worklet_id_, ++last_animation_id_);
+  return {.worklet_id = worklet_id_, .animation_id = ++last_animation_id_};
 }
 
 void AnimationWorklet::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
index 73cb238a..f4602a70 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
@@ -154,6 +154,18 @@
     UpdateAnimation(isolate, animator, animation.worklet_animation_id,
                     animation.current_time, output);
   }
+
+  for (const auto& worklet_animation_id : input.peeked_animations) {
+    int id = worklet_animation_id.animation_id;
+    Animator* animator = animators_.at(id);
+    if (!animator || !predicate(animator))
+      continue;
+
+    AnimationWorkletDispatcherOutput::AnimationState animation_output(
+        worklet_animation_id);
+    animator->GetLocalTimes(animation_output.local_times);
+    output->animations.push_back(animation_output);
+  }
 }
 
 void AnimationWorkletGlobalScope::RegisterWithProxyClientIfNeeded() {
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
index bac3f884..2e0dd835 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
@@ -798,10 +798,34 @@
   return (timeline_time - start_time_.value()) * playback_rate_;
 }
 
+bool WorkletAnimation::NeedsPeek(base::TimeDelta current_time) {
+  bool local_time_is_set = false;
+  for (auto& time : local_times_) {
+    if (time) {
+      local_time_is_set = true;
+      break;
+    }
+  }
+
+  // If any of the local times has been set, a previous peek must have
+  // completed. Request a new peek only if the input time changes.
+  if (local_time_is_set)
+    return last_peek_request_time_ != current_time;
+
+  return true;
+}
+
 void WorkletAnimation::UpdateInputState(
     AnimationWorkletDispatcherInput* input_state) {
   base::Optional<base::TimeDelta> current_time = CurrentTime();
   if (!running_on_main_thread_) {
+    if (!current_time)
+      return;
+    base::TimeDelta current_time_value = current_time.value();
+    if (!NeedsPeek(current_time_value))
+      return;
+    last_peek_request_time_ = current_time_value;
+    input_state->Peek(id_);
     return;
   }
   bool was_active = IsActive(last_play_state_);
@@ -836,20 +860,14 @@
     const AnimationWorkletOutput::AnimationState& state) {
   DCHECK(state.worklet_animation_id == id_);
   // The local times for composited effects, i.e. not running on main, are
-  // updated via posting animation events from the compositor thread to the main
-  // thread (see WorkletAnimation::NotifyLocalTimeUpdated).
-  DCHECK(local_times_.size() == state.local_times.size() &&
-         running_on_main_thread_);
+  // peeked and set via the main thread. If an animator is not ready upon
+  // peeking state.local_times will be empty.
+  DCHECK(local_times_.size() == state.local_times.size() ||
+         !running_on_main_thread_);
   for (wtf_size_t i = 0; i < state.local_times.size(); ++i)
     local_times_[i] = state.local_times[i];
 }
 
-void WorkletAnimation::NotifyLocalTimeUpdated(
-    base::Optional<base::TimeDelta> local_time) {
-  DCHECK(!running_on_main_thread_);
-  local_times_[0] = local_time;
-}
-
 void WorkletAnimation::Dispose() {
   DCHECK(IsMainThread());
   if (timeline_->IsScrollTimeline())
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
index 6b0cf23..b11658dd 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
@@ -114,8 +114,6 @@
   void NotifyAnimationStarted(double monotonic_time, int group) override {}
   void NotifyAnimationFinished(double monotonic_time, int group) override {}
   void NotifyAnimationAborted(double monotonic_time, int group) override {}
-  void NotifyLocalTimeUpdated(
-      base::Optional<base::TimeDelta> local_time) override;
 
   Document* GetDocument() const override { return document_.Get(); }
   AnimationTimeline* GetTimeline() const override { return timeline_; }
@@ -127,6 +125,8 @@
   }
   bool IsActiveAnimation() const override;
 
+  bool NeedsPeek(base::TimeDelta current_time);
+
   void UpdateInputState(AnimationWorkletDispatcherInput* input_state) override;
   void SetOutputState(
       const AnimationWorkletOutput::AnimationState& state) override;
@@ -218,6 +218,8 @@
   // We use this to skip updating if current time has not changed since last
   // update.
   base::Optional<base::TimeDelta> last_input_update_current_time_;
+  // Time the main thread sends a peek request.
+  base::Optional<base::TimeDelta> last_peek_request_time_;
 
   Member<Document> document_;
 
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
index 4f65234..f4870f42 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
@@ -202,6 +202,62 @@
   EXPECT_TIME_NEAR(70, worklet_animation->currentTime(is_null));
 }
 
+TEST_F(WorkletAnimationTest, MainThreadSendsPeekRequestTest) {
+  WorkletAnimationId id = worklet_animation_->GetWorkletAnimationId();
+
+  SimulateFrame(111.0);
+  worklet_animation_->play(ASSERT_NO_EXCEPTION);
+  worklet_animation_->UpdateCompositingState();
+
+  // Only peek if animation is running on compositor.
+  worklet_animation_->SetRunningOnMainThreadForTesting(false);
+
+  std::unique_ptr<AnimationWorkletDispatcherInput> state =
+      std::make_unique<AnimationWorkletDispatcherInput>();
+  worklet_animation_->UpdateInputState(state.get());
+  std::unique_ptr<AnimationWorkletInput> input =
+      state->TakeWorkletState(id.worklet_id);
+  EXPECT_EQ(input->peeked_animations.size(), 1u);
+  EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
+  EXPECT_EQ(input->updated_animations.size(), 0u);
+  EXPECT_EQ(input->removed_animations.size(), 0u);
+  state.reset(new AnimationWorkletDispatcherInput);
+
+  // Local times not set yet. Need to peek again.
+  AnimationWorkletOutput::AnimationState output(id);
+  worklet_animation_->SetOutputState(output);
+  worklet_animation_->UpdateInputState(state.get());
+  input = state->TakeWorkletState(id.worklet_id);
+  EXPECT_EQ(input->peeked_animations.size(), 1u);
+  EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
+  EXPECT_EQ(input->updated_animations.size(), 0u);
+  EXPECT_EQ(input->removed_animations.size(), 0u);
+  state.reset(new AnimationWorkletDispatcherInput);
+
+  // Last peek request fulfilled. No need to peek.
+  WebVector<base::Optional<base::TimeDelta>> local_times;
+  local_times.emplace_back(base::TimeDelta());
+  AnimationWorkletOutput::AnimationState output_with_value(id);
+  output_with_value.local_times = local_times.ReleaseVector();
+  worklet_animation_->SetOutputState(output_with_value);
+  worklet_animation_->UpdateInputState(state.get());
+  input = state->TakeWorkletState(id.worklet_id);
+  EXPECT_FALSE(input);
+  state.reset(new AnimationWorkletDispatcherInput);
+
+  // Input time changes. Need to peek again.
+  GetDocument().GetAnimationClock().UpdateTime(
+      GetDocument().Timeline().ZeroTime() + ToTimeDelta(111.0 + 123.4));
+
+  worklet_animation_->UpdateInputState(state.get());
+  input = state->TakeWorkletState(id.worklet_id);
+  EXPECT_EQ(input->peeked_animations.size(), 1u);
+  EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
+  EXPECT_EQ(input->updated_animations.size(), 0u);
+  EXPECT_EQ(input->removed_animations.size(), 0u);
+  state.reset(new AnimationWorkletDispatcherInput);
+}
+
 // Verifies correctness of current time when playback rate is set while the
 // animation is in idle state.
 TEST_F(WorkletAnimationTest, DocumentTimelineSetPlaybackRate) {
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
index 8dd4f192..5ad18e2 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
@@ -364,7 +364,6 @@
   mutable UsageCounters usage_counters_;
 
   virtual void FinalizeFrame() {}
-  virtual void NeedsFinalizeFrame() {}
 
   float GetFontBaseline(const SimpleFontData&) const;
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
index 39680ab..614e5a3b 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
@@ -201,9 +201,6 @@
   CanvasColorParams ColorParamsForTest() const { return ColorParams(); }
 
  protected:
-  void NeedsFinalizeFrame() override {
-    CanvasRenderingContext::NeedsFinalizeFrame();
-  }
   CanvasColorParams ColorParams() const override;
   bool WritePixels(const SkImageInfo& orig_info,
                    const void* pixels,
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
index 9b29eb3..04974f6 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
@@ -125,9 +125,6 @@
   bool HasRecordedDrawCommands() { return have_recorded_draw_commands_; }
 
  protected:
-  void NeedsFinalizeFrame() override {
-    CanvasRenderingContext::NeedsFinalizeFrame();
-  }
   CanvasColorParams ColorParams() const override;
   bool WritePixels(const SkImageInfo& orig_info,
                    const void* pixels,
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 2c2ec09..1be7647 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -1631,11 +1631,12 @@
   other = temp;
 }
 
-void WebAXObject::HandleAutofillStateChanged(bool suggestions_available) const {
+void WebAXObject::HandleAutofillStateChanged(
+    const blink::WebAXAutofillState state) const {
   if (IsDetached() || !private_->IsAXLayoutObject())
     return;
 
-  private_->HandleAutofillStateChanged(suggestions_available);
+  private_->HandleAutofillStateChanged(state);
 }
 
 WebString WebAXObject::ToString() const {
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_renderer_factory_impl.cc b/third_party/blink/renderer/modules/mediastream/media_stream_renderer_factory_impl.cc
index 57715ca..5b431533 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_renderer_factory_impl.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_renderer_factory_impl.cc
@@ -129,8 +129,10 @@
     DVLOG(1) << "Creating WebRtcAudioRenderer for remote WebRTC track.";
 
     renderer = new WebRtcAudioRenderer(
-        Platform::Current()->GetWebRtcSignalingTaskRunner(), web_stream,
-        web_frame, GetSessionIdForWebRtcAudioRenderer(), device_id.Utf8());
+        PeerConnectionDependencyFactory::GetInstance()
+            ->GetWebRtcSignalingTaskRunner(),
+        web_stream, web_frame, GetSessionIdForWebRtcAudioRenderer(),
+        device_id.Utf8());
 
     if (!audio_device->SetAudioRenderer(renderer.get())) {
       WebRtcLogMessage("Error: SetAudioRenderer failed for remote track.");
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.cc b/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.cc
index 99bd863..5110bb82 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.cc
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.cc
@@ -75,6 +75,12 @@
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise result = resolver->Promise();
 
+  if (!mojo_ptr_) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError));
+    return result;
+  }
+
   mojo_ptr_->GetDirectory(
       name, options->create(),
       WTF::Bind(
@@ -131,6 +137,12 @@
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise result = resolver->Promise();
 
+  if (!mojo_ptr_) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError));
+    return result;
+  }
+
   mojo_ptr_->RemoveEntry(
       name, options->recursive(),
       WTF::Bind(
@@ -187,13 +199,18 @@
 mojo::PendingRemote<mojom::blink::NativeFileSystemTransferToken>
 NativeFileSystemDirectoryHandle::Transfer() {
   mojo::PendingRemote<mojom::blink::NativeFileSystemTransferToken> result;
-  mojo_ptr_->Transfer(result.InitWithNewPipeAndPassReceiver());
+  if (mojo_ptr_)
+    mojo_ptr_->Transfer(result.InitWithNewPipeAndPassReceiver());
   return result;
 }
 
 void NativeFileSystemDirectoryHandle::QueryPermissionImpl(
     bool writable,
     base::OnceCallback<void(mojom::blink::PermissionStatus)> callback) {
+  if (!mojo_ptr_) {
+    std::move(callback).Run(mojom::blink::PermissionStatus::DENIED);
+    return;
+  }
   mojo_ptr_->GetPermissionStatus(writable, std::move(callback));
 }
 
@@ -201,6 +218,15 @@
     bool writable,
     base::OnceCallback<void(mojom::blink::NativeFileSystemErrorPtr,
                             mojom::blink::PermissionStatus)> callback) {
+  if (!mojo_ptr_) {
+    std::move(callback).Run(
+        mojom::blink::NativeFileSystemError::New(
+            mojom::blink::NativeFileSystemStatus::kInvalidState,
+            base::File::Error::FILE_ERROR_FAILED, "Context Destroyed"),
+        mojom::blink::PermissionStatus::DENIED);
+    return;
+  }
+
   mojo_ptr_->RequestPermission(writable, std::move(callback));
 }
 
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
index 9087f64b..9799925 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
@@ -36,6 +36,12 @@
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise result = resolver->Promise();
 
+  if (!mojo_ptr_) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError));
+    return result;
+  }
+
   mojo_ptr_->CreateFileWriter(
       options->keepExistingData(),
       WTF::Bind(
@@ -62,6 +68,12 @@
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise result = resolver->Promise();
 
+  if (!mojo_ptr_) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError));
+    return result;
+  }
+
   mojo_ptr_->AsBlob(WTF::Bind(
       [](ScriptPromiseResolver* resolver, const String& name,
          NativeFileSystemErrorPtr result,
@@ -80,7 +92,8 @@
 mojo::PendingRemote<mojom::blink::NativeFileSystemTransferToken>
 NativeFileSystemFileHandle::Transfer() {
   mojo::PendingRemote<mojom::blink::NativeFileSystemTransferToken> result;
-  mojo_ptr_->Transfer(result.InitWithNewPipeAndPassReceiver());
+  if (mojo_ptr_)
+    mojo_ptr_->Transfer(result.InitWithNewPipeAndPassReceiver());
   return result;
 }
 
@@ -91,6 +104,10 @@
 void NativeFileSystemFileHandle::QueryPermissionImpl(
     bool writable,
     base::OnceCallback<void(mojom::blink::PermissionStatus)> callback) {
+  if (!mojo_ptr_) {
+    std::move(callback).Run(mojom::blink::PermissionStatus::DENIED);
+    return;
+  }
   mojo_ptr_->GetPermissionStatus(writable, std::move(callback));
 }
 
@@ -98,6 +115,15 @@
     bool writable,
     base::OnceCallback<void(mojom::blink::NativeFileSystemErrorPtr,
                             mojom::blink::PermissionStatus)> callback) {
+  if (!mojo_ptr_) {
+    std::move(callback).Run(
+        mojom::blink::NativeFileSystemError::New(
+            mojom::blink::NativeFileSystemStatus::kInvalidState,
+            base::File::Error::FILE_ERROR_FAILED, "Context Destroyed"),
+        mojom::blink::PermissionStatus::DENIED);
+    return;
+  }
+
   mojo_ptr_->RequestPermission(writable, std::move(callback));
 }
 
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h b/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h
index 8235d71..1491d21 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h
@@ -42,6 +42,8 @@
   ScriptPromise requestPermission(ScriptState*,
                                   const FileSystemHandlePermissionDescriptor*);
 
+  // Grab a handle to a transfer token. This may return an invalid PendingRemote
+  // if the context is already destroyed.
   virtual mojo::PendingRemote<mojom::blink::NativeFileSystemTransferToken>
   Transfer() = 0;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
index 38c1327..2e9aa9dd 100644
--- a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
@@ -59,7 +59,7 @@
         base::WaitableEvent::InitialState::NOT_SIGNALED);
 
     std::unique_ptr<blink::TrackObserver> track_observer;
-    mock_factory_->GetWebRtcSignalingThread()->PostTask(
+    mock_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
         FROM_HERE,
         base::BindOnce(
             [](scoped_refptr<base::SingleThreadTaskRunner> main_thread,
@@ -113,7 +113,7 @@
     base::WaitableEvent waitable_event(
         base::WaitableEvent::ResetPolicy::MANUAL,
         base::WaitableEvent::InitialState::NOT_SIGNALED);
-    mock_factory_->GetWebRtcSignalingThread()->PostTask(
+    mock_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
         FROM_HERE,
         base::BindOnce(
             [](blink::MockWebRtcVideoTrack* video_track,
diff --git a/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.cc b/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.cc
index 317a3239..61cfc628 100644
--- a/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.cc
+++ b/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.cc
@@ -379,7 +379,7 @@
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
-MockPeerConnectionDependencyFactory::GetWebRtcSignalingThread() const {
+MockPeerConnectionDependencyFactory::GetWebRtcSignalingTaskRunner() {
   return signaling_thread_.task_runner();
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc b/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc
index 006b344a..65ff7e67 100644
--- a/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc
+++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc
@@ -603,8 +603,9 @@
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
-PeerConnectionDependencyFactory::GetWebRtcSignalingThread() const {
+PeerConnectionDependencyFactory::GetWebRtcSignalingTaskRunner() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  EnsureInitialized();
   return chrome_signaling_thread_.IsRunning()
              ? chrome_signaling_thread_.task_runner()
              : nullptr;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl_test.cc
index f2b6faa8..c0bba3eb 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl_test.cc
@@ -53,7 +53,7 @@
   // posted to the current thread in the meantime while waiting.
   void SyncWithSignalingThread() const {
     base::RunLoop run_loop;
-    dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+    dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
         FROM_HERE, run_loop.QuitClosure());
     run_loop.Run();
   }
@@ -63,7 +63,7 @@
     std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
         track_ref;
     base::RunLoop run_loop;
-    dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+    dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
         FROM_HERE,
         base::BindOnce(&RTCRtpReceiverImplTest::CreateReceiverOnSignalingThread,
                        base::Unretained(this), std::move(webrtc_track),
@@ -73,7 +73,7 @@
     DCHECK(mock_webrtc_receiver_);
     DCHECK(track_ref);
     blink::RtpReceiverState state(
-        main_thread_, dependency_factory_->GetWebRtcSignalingThread(),
+        main_thread_, dependency_factory_->GetWebRtcSignalingTaskRunner(),
         mock_webrtc_receiver_.get(), std::move(track_ref), {});
     state.Initialize();
     return std::make_unique<RTCRtpReceiverImpl>(peer_connection_.get(),
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl_test.cc
index e952807c..e2f91b3 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl_test.cc
@@ -60,7 +60,7 @@
   // posted to the current thread in the meantime while waiting.
   void SyncWithSignalingThread() const {
     base::RunLoop run_loop;
-    dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+    dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
         FROM_HERE, run_loop.QuitClosure());
     run_loop.Run();
   }
@@ -89,10 +89,10 @@
       track_ref = track_map_->GetOrCreateLocalTrackAdapter(web_track);
       DCHECK(track_ref->is_initialized());
     }
-    RtpSenderState sender_state(main_thread_,
-                                dependency_factory_->GetWebRtcSignalingThread(),
-                                mock_webrtc_sender_.get(), std::move(track_ref),
-                                std::vector<std::string>());
+    RtpSenderState sender_state(
+        main_thread_, dependency_factory_->GetWebRtcSignalingTaskRunner(),
+        mock_webrtc_sender_.get(), std::move(track_ref),
+        std::vector<std::string>());
     sender_state.Initialize();
     return std::make_unique<RTCRtpSenderImpl>(
         peer_connection_.get(), track_map_, std::move(sender_state));
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl_test.cc
index 5dcdfcf..c621050 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl_test.cc
@@ -54,13 +54,13 @@
   // posted to the current thread in the meantime while waiting.
   void SyncWithSignalingThread() const {
     base::RunLoop run_loop;
-    dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+    dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
         FROM_HERE, run_loop.QuitClosure());
     run_loop.Run();
   }
 
   scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner() const {
-    return dependency_factory_->GetWebRtcSignalingThread();
+    return dependency_factory_->GetWebRtcSignalingTaskRunner();
   }
 
   std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
diff --git a/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer_test.cc b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer_test.cc
index ea08fe2..9d9ca8ea 100644
--- a/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer_test.cc
@@ -65,7 +65,7 @@
   }
 
   scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner() const {
-    return dependency_factory_->GetWebRtcSignalingThread();
+    return dependency_factory_->GetWebRtcSignalingTaskRunner();
   }
 
   std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
diff --git a/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc
index c1071210..9165ecc 100644
--- a/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc
+++ b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc
@@ -166,7 +166,7 @@
   webrtc::AudioSourceInterface* source_interface = nullptr;
   local_track_audio_sink_.reset(new blink::WebRtcAudioSink(
       web_track_.Id().Utf8(), source_interface,
-      factory_->GetWebRtcSignalingThread(), main_thread_));
+      factory_->GetWebRtcSignalingTaskRunner(), main_thread_));
 
   if (auto* media_stream_source = blink::ProcessedLocalAudioSource::From(
           blink::MediaStreamAudioSource::From(web_track_.Source()))) {
@@ -300,7 +300,7 @@
   DCHECK(remote_audio_track_adapter_);
   DCHECK_EQ(web_track_.Source().GetType(),
             blink::WebMediaStreamSource::kTypeAudio);
-  factory_->GetWebRtcSignalingThread()->PostTask(
+  factory_->GetWebRtcSignalingTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(&WebRtcMediaStreamTrackAdapter::
                          UnregisterRemoteAudioTrackAdapterOnSignalingThread,
diff --git a/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_map_test.cc b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_map_test.cc
index 3392959..34a97f0 100644
--- a/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_map_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_map_test.cc
@@ -36,7 +36,7 @@
   void TearDown() override { blink::WebHeap::CollectAllGarbageForTesting(); }
 
   scoped_refptr<base::SingleThreadTaskRunner> signaling_thread() const {
-    return dependency_factory_->GetWebRtcSignalingThread();
+    return dependency_factory_->GetWebRtcSignalingTaskRunner();
   }
 
   blink::WebMediaStreamTrack CreateLocalTrack(const std::string& id) {
diff --git a/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_test.cc b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_test.cc
index ef399fdb7..54e53ff 100644
--- a/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_test.cc
@@ -95,7 +95,7 @@
     base::WaitableEvent waitable_event(
         base::WaitableEvent::ResetPolicy::MANUAL,
         base::WaitableEvent::InitialState::NOT_SIGNALED);
-    dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+    dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
         FROM_HERE, base::BindOnce(&WebRtcMediaStreamTrackAdapterTest::
                                       RunMessageLoopUntilIdleOnSignalingThread,
                                   base::Unretained(this), &waitable_event));
@@ -106,7 +106,7 @@
 
   void RunMessageLoopUntilIdleOnSignalingThread(
       base::WaitableEvent* waitable_event) {
-    DCHECK(dependency_factory_->GetWebRtcSignalingThread()
+    DCHECK(dependency_factory_->GetWebRtcSignalingTaskRunner()
                ->BelongsToCurrentThread());
     base::RunLoop().RunUntilIdle();
     waitable_event->Signal();
@@ -163,7 +163,7 @@
 TEST_F(WebRtcMediaStreamTrackAdapterTest, RemoteAudioTrack) {
   scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
       blink::MockWebRtcAudioTrack::Create("remote_audio_track");
-  dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+  dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(
           &WebRtcMediaStreamTrackAdapterTest::CreateRemoteTrackAdapter,
@@ -188,7 +188,7 @@
 TEST_F(WebRtcMediaStreamTrackAdapterTest, RemoteVideoTrack) {
   scoped_refptr<blink::MockWebRtcVideoTrack> webrtc_track =
       blink::MockWebRtcVideoTrack::Create("remote_video_track");
-  dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+  dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(
           &WebRtcMediaStreamTrackAdapterTest::CreateRemoteTrackAdapter,
@@ -213,7 +213,7 @@
 TEST_F(WebRtcMediaStreamTrackAdapterTest, RemoteTrackExplicitlyInitialized) {
   scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
       blink::MockWebRtcAudioTrack::Create("remote_audio_track");
-  dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+  dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(
           &WebRtcMediaStreamTrackAdapterTest::CreateRemoteTrackAdapter,
@@ -242,7 +242,7 @@
 TEST_F(WebRtcMediaStreamTrackAdapterTest, LastReferenceOnSignalingThread) {
   scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
       blink::MockWebRtcAudioTrack::Create("remote_audio_track");
-  dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+  dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(
           &WebRtcMediaStreamTrackAdapterTest::CreateRemoteTrackAdapter,
@@ -255,7 +255,7 @@
   base::WaitableEvent waitable_event(
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
-  dependency_factory_->GetWebRtcSignalingThread()->PostTask(
+  dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(
           &WebRtcMediaStreamTrackAdapterTest::HoldOntoAdapterReference,
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_object.h b/third_party/blink/renderer/modules/webgpu/dawn_object.h
index 2615f2f..7b47862 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_object.h
+++ b/third_party/blink/renderer/modules/webgpu/dawn_object.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_OBJECT_H_
 
 #include <dawn/dawn.h>
+#include <dawn/dawn_proc_table.h>
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc
index d904b4b..72a09f5 100644
--- a/third_party/blink/renderer/modules/xr/xr.cc
+++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_view.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
 namespace blink {
 
 namespace {
@@ -466,8 +467,9 @@
   return frame_provider_;
 }
 
-const device::mojom::blink::XREnvironmentIntegrationProviderAssociatedPtr&
-XR::xrEnvironmentProviderPtr() {
+const mojo::AssociatedRemote<
+    device::mojom::blink::XREnvironmentIntegrationProvider>&
+XR::xrEnvironmentProviderRemote() {
   return environment_provider_;
 }
 
@@ -934,10 +936,11 @@
       // https://docs.google.com/spreadsheets/d/1b-dus1Ug3A8y0lX0blkmOjJILisUASdj8x9YN_XMwYc/view
       frameProvider()
           ->GetImmersiveDataProvider()
-          ->GetEnvironmentIntegrationProvider(mojo::MakeRequest(
-              &environment_provider_, GetExecutionContext()->GetTaskRunner(
-                                          TaskType::kMiscPlatformAPI)));
-      environment_provider_.set_connection_error_handler(WTF::Bind(
+          ->GetEnvironmentIntegrationProvider(
+              environment_provider_.BindNewEndpointAndPassReceiver(
+                  GetExecutionContext()->GetTaskRunner(
+                      TaskType::kMiscPlatformAPI)));
+      environment_provider_.set_disconnect_handler(WTF::Bind(
           &XR::OnEnvironmentProviderDisconnect, WrapWeakPersistent(this)));
 
       session->OnEnvironmentProviderCreated();
diff --git a/third_party/blink/renderer/modules/xr/xr.h b/third_party/blink/renderer/modules/xr/xr.h
index f37f30d..654c7199 100644
--- a/third_party/blink/renderer/modules/xr/xr.h
+++ b/third_party/blink/renderer/modules/xr/xr.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_H_
 
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -59,8 +60,9 @@
 
   XRFrameProvider* frameProvider();
 
-  const device::mojom::blink::XREnvironmentIntegrationProviderAssociatedPtr&
-  xrEnvironmentProviderPtr();
+  const mojo::AssociatedRemote<
+      device::mojom::blink::XREnvironmentIntegrationProvider>&
+  xrEnvironmentProviderRemote();
 
   // VRServiceClient overrides.
   void OnDeviceChanged() override;
@@ -297,7 +299,7 @@
   Member<XRFrameProvider> frame_provider_;
   HeapHashSet<WeakMember<XRSession>> sessions_;
   mojo::Remote<device::mojom::blink::VRService> service_;
-  device::mojom::blink::XREnvironmentIntegrationProviderAssociatedPtr
+  mojo::AssociatedRemote<device::mojom::blink::XREnvironmentIntegrationProvider>
       environment_provider_;
   mojo::Receiver<device::mojom::blink::VRServiceClient> receiver_{this};
 
diff --git a/third_party/blink/renderer/modules/xr/xr_anchor.cc b/third_party/blink/renderer/modules/xr/xr_anchor.cc
index d8fde7a6..322a48a6 100644
--- a/third_party/blink/renderer/modules/xr/xr_anchor.cc
+++ b/third_party/blink/renderer/modules/xr/xr_anchor.cc
@@ -72,7 +72,7 @@
 }
 
 void XRAnchor::detach() {
-  session_->xr()->xrEnvironmentProviderPtr()->DetachAnchor(id_);
+  session_->xr()->xrEnvironmentProviderRemote()->DetachAnchor(id_);
 }
 
 void XRAnchor::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index cdc4eb6..51498621 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -412,7 +412,7 @@
   }
 
   // Reject the promise if device doesn't support the anchors API.
-  if (!xr_->xrEnvironmentProviderPtr()) {
+  if (!xr_->xrEnvironmentProviderRemote()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       kAnchorsNotSupported);
     return ScriptPromise();
@@ -473,12 +473,12 @@
            << pose_ptr->position->y << ", " << pose_ptr->position->z << "]";
 
   if (plane) {
-    xr_->xrEnvironmentProviderPtr()->CreatePlaneAnchor(
+    xr_->xrEnvironmentProviderRemote()->CreatePlaneAnchor(
         std::move(pose_ptr), plane->id(),
         WTF::Bind(&XRSession::OnCreateAnchorResult, WrapPersistent(this),
                   WrapPersistent(resolver)));
   } else {
-    xr_->xrEnvironmentProviderPtr()->CreateAnchor(
+    xr_->xrEnvironmentProviderRemote()->CreateAnchor(
         std::move(pose_ptr),
         WTF::Bind(&XRSession::OnCreateAnchorResult, WrapPersistent(this),
                   WrapPersistent(resolver)));
@@ -541,7 +541,7 @@
   }
 
   // Reject the promise if device doesn't support the hit-test API.
-  if (!xr_->xrEnvironmentProviderPtr()) {
+  if (!xr_->xrEnvironmentProviderRemote()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
                                       kHitTestNotSupported);
     return ScriptPromise();
@@ -558,7 +558,7 @@
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
-  xr_->xrEnvironmentProviderPtr()->RequestHitTest(
+  xr_->xrEnvironmentProviderRemote()->RequestHitTest(
       std::move(ray_mojo),
       WTF::Bind(&XRSession::OnHitTestResults, WrapPersistent(this),
                 WrapPersistent(resolver)));
@@ -622,7 +622,7 @@
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
-  xr_->xrEnvironmentProviderPtr()->SubscribeToHitTest(
+  xr_->xrEnvironmentProviderRemote()->SubscribeToHitTest(
       maybe_native_origin->ToMojo(), std::move(ray_mojo),
       WTF::Bind(&XRSession::OnSubscribeToHitTestResult, WrapPersistent(this),
                 WrapPersistent(resolver), WrapPersistent(options)));
@@ -698,7 +698,7 @@
   // Install error handler on environment provider to ensure that we get
   // notified so that we can clean up all relevant pending promises.
   if (!environment_error_handler_subscribed_ &&
-      xr_->xrEnvironmentProviderPtr()) {
+      xr_->xrEnvironmentProviderRemote()) {
     environment_error_handler_subscribed_ = true;
     xr_->AddEnvironmentProviderErrorHandler(WTF::Bind(
         &XRSession::OnEnvironmentProviderError, WrapWeakPersistent(this)));
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation.cc b/third_party/blink/renderer/platform/animation/compositor_animation.cc
index 4ea0dac..d7f6df2 100644
--- a/third_party/blink/renderer/platform/animation/compositor_animation.cc
+++ b/third_party/blink/renderer/platform/animation/compositor_animation.cc
@@ -138,11 +138,4 @@
   }
 }
 
-void CompositorAnimation::NotifyLocalTimeUpdated(
-    base::Optional<base::TimeDelta> local_time) {
-  if (delegate_) {
-    delegate_->NotifyLocalTimeUpdated(local_time);
-  }
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation.h b/third_party/blink/renderer/platform/animation/compositor_animation.h
index e900e47..10cc086 100644
--- a/third_party/blink/renderer/platform/animation/compositor_animation.h
+++ b/third_party/blink/renderer/platform/animation/compositor_animation.h
@@ -82,8 +82,6 @@
                                int target_property,
                                base::TimeTicks animation_start_time,
                                std::unique_ptr<cc::AnimationCurve>) override;
-  void NotifyLocalTimeUpdated(
-      base::Optional<base::TimeDelta> local_time) override;
 
   scoped_refptr<cc::SingleKeyframeEffectAnimation> animation_;
   CompositorAnimationDelegate* delegate_;
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h b/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h
index 0650942..62a81b37 100644
--- a/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h
+++ b/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h
@@ -29,8 +29,6 @@
       double monotonic_time,
       double animation_start_time,
       std::unique_ptr<cc::AnimationCurve> curve) {}
-  virtual void NotifyLocalTimeUpdated(
-      base::Optional<base::TimeDelta> local_time) {}
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc b/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc
index ef3b966..4a47aa8a 100644
--- a/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc
+++ b/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc
@@ -121,36 +121,57 @@
       *destination++ = static_cast<float>(output);
     }
   } else {
-    double delay_time = this->DelayTime(sample_rate);
+    // This is basically the same as above, but optimized for the case where the
+    // delay time is constant for the current render.
+    //
+    // TODO(crbug.com/1012198): There are still some further optimizations that
+    // could be done.  interp_factor could be a float to eliminate several
+    // conversions between floats and doubles.  It might be possible to get rid
+    // of the wrapping if the buffer were longer.  This may aslo allow
+    // |write_index_| to be different from |read_index1| or |read_index2| which
+    // simplifies the loop a bit.
 
+    double delay_time = this->DelayTime(sample_rate);
     // Make sure the delay time is in a valid range.
     delay_time = clampTo(delay_time, 0.0, max_time);
+    double desired_delay_frames = delay_time * sample_rate;
+    double read_position = write_index_ + buffer_length - desired_delay_frames;
+    if (read_position >= buffer_length)
+      read_position -= buffer_length;
+
+    // Linearly interpolate in-between delay times.  |read_index1| and
+    // |read_index2| are the indices of the frames to be used for
+    // interpolation.
+    int read_index1 = static_cast<int>(read_position);
+    int read_index2 = (read_index1 + 1) % buffer_length;
+    double interp_factor = read_position - read_index1;
+
+    int w_index = write_index_;
 
     for (unsigned i = 0; i < frames_to_process; ++i) {
-      double desired_delay_frames = delay_time * sample_rate;
+      // Copy the latest sample into the buffer.  Needed because
+      // w_index could be the same as read_index1 or read_index2.
+      buffer[w_index] = *source++;
+      float sample1 = buffer[read_index1];
+      float sample2 = buffer[read_index2];
 
-      double read_position =
-          write_index_ + buffer_length - desired_delay_frames;
-      if (read_position >= buffer_length)
-        read_position -= buffer_length;
+      // Update the indices and wrap them to the beginning of the buffer if
+      // needed.
+      ++w_index;
+      ++read_index1;
+      ++read_index2;
+      if (w_index >= static_cast<int>(buffer_length))
+        w_index -= buffer_length;
+      if (read_index1 >= static_cast<int>(buffer_length))
+        read_index1 -= buffer_length;
+      if (read_index2 >= static_cast<int>(buffer_length))
+        read_index2 -= buffer_length;
 
-      // Linearly interpolate in-between delay times.
-      int read_index1 = static_cast<int>(read_position);
-      int read_index2 = (read_index1 + 1) % buffer_length;
-      double interpolation_factor = read_position - read_index1;
-
-      double input = static_cast<float>(*source++);
-      buffer[write_index_] = static_cast<float>(input);
-      write_index_ = (write_index_ + 1) % buffer_length;
-
-      double sample1 = buffer[read_index1];
-      double sample2 = buffer[read_index2];
-
-      double output = (1.0 - interpolation_factor) * sample1 +
-                      interpolation_factor * sample2;
-
-      *destination++ = static_cast<float>(output);
+      // Linearly interpolate between samples.
+      *destination++ = (1 - interp_factor) * sample1 + interp_factor * sample2;
     }
+
+    write_index_ = w_index;
   }
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
index b4002e6..af14383 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DAWN_CONTROL_CLIENT_HOLDER_H_
 
 #include <dawn/dawn.h>
+#include <dawn/dawn_proc_table.h>
 
 #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
diff --git a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
index fb824c4f..2dd398f4 100644
--- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
@@ -97,12 +97,14 @@
 
   int alloc_size_in_bytes = data_size.ValueOrDie();
   if (!src_image) {
-    auto data = WTF::ArrayBufferContents::CreateDataHandle(
-        alloc_size_in_bytes, WTF::ArrayBufferContents::kZeroInitialize);
-    if (!data)
+    WTF::ArrayBufferContents result(alloc_size_in_bytes, 1,
+                                    WTF::ArrayBufferContents::kNotShared,
+                                    WTF::ArrayBufferContents::kZeroInitialize);
+
+    // Check if the ArrayBuffer backing store was allocated correctly.
+    if (result.DataLength() != static_cast<size_t>(alloc_size_in_bytes)) {
       return false;
-    WTF::ArrayBufferContents result(std::move(data),
-                                    WTF::ArrayBufferContents::kNotShared);
+    }
     result.Transfer(dest_contents);
     return true;
   }
@@ -115,12 +117,14 @@
   WTF::ArrayBufferContents::InitializationPolicy initialization_policy =
       may_have_stray_area ? WTF::ArrayBufferContents::kZeroInitialize
                           : WTF::ArrayBufferContents::kDontInitialize;
-  auto data = WTF::ArrayBufferContents::CreateDataHandle(alloc_size_in_bytes,
-                                                         initialization_policy);
-  if (!data)
+
+  WTF::ArrayBufferContents result(alloc_size_in_bytes, 1,
+                                  WTF::ArrayBufferContents::kNotShared,
+                                  initialization_policy);
+  // Check if the ArrayBuffer backing store was allocated correctly.
+  if (result.DataLength() != static_cast<size_t>(alloc_size_in_bytes)) {
     return false;
-  WTF::ArrayBufferContents result(std::move(data),
-                                  WTF::ArrayBufferContents::kNotShared);
+  }
 
   SkColorType color_type =
       (color_params.GetSkColorType() == kRGBA_F16_SkColorType)
diff --git a/third_party/blink/renderer/platform/heap/heap_page.cc b/third_party/blink/renderer/platform/heap/heap_page.cc
index abb957b..cd09cb1c 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.cc
+++ b/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -1299,18 +1299,20 @@
             std::move(free_size)};
 }
 
-BasePage::BasePage(PageMemory* storage, BaseArena* arena)
+BasePage::BasePage(PageMemory* storage, BaseArena* arena, PageType page_type)
     : magic_(GetMagic()),
       storage_(storage),
       arena_(arena),
-      swept_(true) {
+      thread_state_(arena->GetThreadState()),
+      page_type_(page_type) {
 #if DCHECK_IS_ON()
   DCHECK(IsPageHeaderAddress(reinterpret_cast<Address>(this)));
 #endif
 }
 
 NormalPage::NormalPage(PageMemory* storage, BaseArena* arena)
-    : BasePage(storage, arena), object_start_bit_map_(Payload()) {
+    : BasePage(storage, arena, PageType::kNormalPage),
+      object_start_bit_map_(Payload()) {
 #if DCHECK_IS_ON()
   DCHECK(IsPageHeaderAddress(reinterpret_cast<Address>(this)));
 #endif  // DCHECK_IS_ON()
@@ -1710,7 +1712,8 @@
   return object_offset + offset_;
 }
 
-HeapObjectHeader* NormalPage::FindHeaderFromAddress(Address address) {
+HeapObjectHeader* NormalPage::ConservativelyFindHeaderFromAddress(
+    Address address) {
   if (!ContainedInObjectPayload(address))
     return nullptr;
   if (ArenaForNormalPage()->IsInCurrentAllocationPointRegion(address))
@@ -1725,6 +1728,17 @@
   return header;
 }
 
+HeapObjectHeader* NormalPage::FindHeaderFromAddress(Address address) {
+  DCHECK(ContainedInObjectPayload(address));
+  DCHECK(!ArenaForNormalPage()->IsInCurrentAllocationPointRegion(address));
+  HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(
+      object_start_bit_map()->FindHeader(address));
+  DCHECK(header->IsValid());
+  DCHECK_LT(0u, header->GcInfoIndex());
+  DCHECK_GT(header->PayloadEnd(), address);
+  return header;
+}
+
 void NormalPage::CollectStatistics(
     ThreadState::Statistics::ArenaStatistics* arena_stats) {
   HeapObjectHeader* header = nullptr;
@@ -1765,7 +1779,7 @@
 LargeObjectPage::LargeObjectPage(PageMemory* storage,
                                  BaseArena* arena,
                                  size_t object_size)
-    : BasePage(storage, arena),
+    : BasePage(storage, arena, PageType::kLargeObjectPage),
       object_size_(object_size)
 #ifdef ANNOTATE_CONTIGUOUS_CONTAINER
       ,
diff --git a/third_party/blink/renderer/platform/heap/heap_page.h b/third_party/blink/renderer/platform/heap/heap_page.h
index 51ae8806..733b97bd 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.h
+++ b/third_party/blink/renderer/platform/heap/heap_page.h
@@ -471,6 +471,10 @@
 // |SweepResult| indicates if page turned out to be empty after sweeping.
 enum class SweepResult : uint8_t { kPageEmpty, kPageNotEmpty };
 
+// |PageType| indicates whether a page is used for normal objects or whether it
+// holds a large object.
+enum class PageType : uint8_t { kNormalPage, kLargeObjectPage };
+
 // |BasePage| is a base class for |NormalPage| and |LargeObjectPage|.
 //
 // - |NormalPage| is a page whose size is |kBlinkPageSize|. A |NormalPage| can
@@ -487,7 +491,7 @@
   DISALLOW_NEW();
 
  public:
-  BasePage(PageMemory*, BaseArena*);
+  BasePage(PageMemory*, BaseArena*, PageType);
   virtual ~BasePage() = default;
 
   // Virtual methods are slow. So performance-sensitive methods should be
@@ -514,13 +518,14 @@
   virtual bool Contains(Address) = 0;
 #endif
   virtual size_t size() = 0;
-  virtual bool IsLargeObjectPage() { return false; }
 
   Address GetAddress() { return reinterpret_cast<Address>(this); }
   PageMemory* Storage() const { return storage_; }
   BaseArena* Arena() const { return arena_; }
+  ThreadState* thread_state() const { return thread_state_; }
 
-  // Returns true if this page has been swept by the ongoing lazy sweep.
+  // Returns true if this page has been swept by the ongoing sweep; false
+  // otherwise.
   bool HasBeenSwept() const { return swept_; }
 
   void MarkAsSwept() {
@@ -533,6 +538,11 @@
     swept_ = false;
   }
 
+  // Returns true  if this page is a large object page; false otherwise.
+  bool IsLargeObjectPage() const {
+    return page_type_ == PageType::kLargeObjectPage;
+  }
+
   // Returns true if magic number is valid.
   bool IsValid() const;
 
@@ -545,10 +555,13 @@
   uint32_t const magic_;
   PageMemory* const storage_;
   BaseArena* const arena_;
+  ThreadState* const thread_state_;
 
   // Track the sweeping state of a page. Set to false at the start of a sweep,
-  // true  upon completion of lazy sweeping.
-  bool swept_;
+  // true upon completion of sweeping that page.
+  bool swept_ = true;
+
+  PageType page_type_;
 
   friend class BaseArena;
 };
@@ -738,6 +751,11 @@
   // Uses the object_start_bit_map_ to find an object for a given address. The
   // returned header is either nullptr, indicating that no object could be
   // found, or it is pointing to valid object or free list entry.
+  HeapObjectHeader* ConservativelyFindHeaderFromAddress(Address);
+
+  // Uses the object_start_bit_map_ to find an object for a given address. It is
+  // assumed that the address points into a valid heap object. Use the
+  // conservative version if that assumption does not hold.
   HeapObjectHeader* FindHeaderFromAddress(Address);
 
   void VerifyMarking() override;
@@ -830,8 +848,6 @@
   void CollectStatistics(
       ThreadState::Statistics::ArenaStatistics* arena_stats) override;
 
-  bool IsLargeObjectPage() override { return true; }
-
   void VerifyMarking() override;
 
 #if defined(ADDRESS_SANITIZER)
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.cc b/third_party/blink/renderer/platform/heap/marking_visitor.cc
index 4ee13a3..f789e3a 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -80,28 +80,42 @@
   marked_bytes_ += header->size() - old_size;
 }
 
+// static
 bool MarkingVisitor::WriteBarrierSlow(void* value) {
   if (!value || IsHashTableDeleteValue(value))
     return false;
 
-  ThreadState* const thread_state = ThreadState::Current();
+  // It is guaranteed that managed references point to either GarbageCollected
+  // or GarbageCollectedMixin. Mixins are restricted to regular objects sizes.
+  // It is thus possible to get to the page header by aligning properly.
+  BasePage* base_page = PageFromObject(value);
+
+  ThreadState* const thread_state = base_page->thread_state();
   if (!thread_state->IsIncrementalMarking())
     return false;
 
-  HeapObjectHeader* const header = HeapObjectHeader::FromInnerAddress(
-      reinterpret_cast<Address>(const_cast<void*>(value)));
-  if (header->IsMarked<HeapObjectHeader::AccessMode::kAtomic>())
+  HeapObjectHeader* header;
+  if (LIKELY(!base_page->IsLargeObjectPage())) {
+    header = reinterpret_cast<HeapObjectHeader*>(
+        static_cast<NormalPage*>(base_page)->FindHeaderFromAddress(
+            reinterpret_cast<Address>(value)));
+  } else {
+    header = static_cast<LargeObjectPage*>(base_page)->ObjectHeader();
+  }
+  DCHECK(header->IsValid());
+
+  if (!header->TryMark<HeapObjectHeader::AccessMode::kAtomic>())
     return false;
 
-  if (header->IsInConstruction()) {
+  if (UNLIKELY(header->IsInConstruction())) {
+    // It is assumed that objects on not_fully_constructed_worklist_ are not
+    // marked.
+    header->Unmark();
     thread_state->CurrentVisitor()->not_fully_constructed_worklist_.Push(
         header->Payload());
     return true;
   }
 
-  // Mark and push trace callback.
-  if (!header->TryMark<HeapObjectHeader::AccessMode::kAtomic>())
-    return false;
   MarkingVisitor* visitor = thread_state->CurrentVisitor();
   visitor->AccountMarkedBytes(header);
   visitor->marking_worklist_.Push(
@@ -152,7 +166,8 @@
   HeapObjectHeader* const header =
       page->IsLargeObjectPage()
           ? static_cast<LargeObjectPage*>(page)->ObjectHeader()
-          : static_cast<NormalPage*>(page)->FindHeaderFromAddress(address);
+          : static_cast<NormalPage*>(page)->ConservativelyFindHeaderFromAddress(
+                address);
   if (!header || header->IsMarked())
     return;
 
diff --git a/third_party/blink/renderer/platform/instrumentation/BUILD.gn b/third_party/blink/renderer/platform/instrumentation/BUILD.gn
index 795b1a25..a53a2e66 100644
--- a/third_party/blink/renderer/platform/instrumentation/BUILD.gn
+++ b/third_party/blink/renderer/platform/instrumentation/BUILD.gn
@@ -6,6 +6,12 @@
 import("//third_party/blink/renderer/platform/platform.gni")
 
 blink_platform_sources("instrumentation") {
+  visibility = []
+  visibility = [
+    "//third_party/blink/renderer/platform",
+    "//third_party/blink/renderer/platform/scheduler",
+  ]
+
   sources = [
     "histogram.cc",
     "histogram.h",
diff --git a/third_party/blink/renderer/platform/loader/cors/cors.cc b/third_party/blink/renderer/platform/loader/cors/cors.cc
index e4ac62f..42c442f 100644
--- a/third_party/blink/renderer/platform/loader/cors/cors.cc
+++ b/third_party/blink/renderer/platform/loader/cors/cors.cc
@@ -360,7 +360,7 @@
 }
 
 bool IsForbiddenHeaderName(const String& name) {
-  return network::cors::IsForbiddenHeader(name.Latin1());
+  return !net::HttpUtil::IsSafeHeader(name.Latin1());
 }
 
 bool ContainsOnlyCorsSafelistedHeaders(const HTTPHeaderMap& header_map) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h b/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
index 4338460..3aea5dc 100644
--- a/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
+++ b/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
@@ -7,7 +7,7 @@
 
 #include <stdint.h>
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
-#include "third_party/blink/public/mojom/loader/code_cache.mojom-blink.h"
+#include "third_party/blink/public/mojom/loader/code_cache.mojom-blink-forward.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h b/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h
index 44d9491..8772922 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_CLIENT_SETTINGS_OBJECT_H_
 
 #include "base/optional.h"
-#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
+#include "services/network/public/mojom/ip_address_space.mojom-blink-forward.h"
 #include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc b/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
index fdb78e0..956550119 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
@@ -30,6 +30,7 @@
 
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
 
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
index 6187411..aa2b09db 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
@@ -36,8 +36,8 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/single_thread_task_runner.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
-#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink-forward.h"
 #include "third_party/blink/public/platform/resource_request_blocked_reason.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc b/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc
index 541caf3..cc25888 100644
--- a/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h"
 
+#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
 #include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/platform/loader/allowed_by_nosniff.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
index e627b88..9c3ad282 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -33,7 +33,7 @@
 #include "base/single_thread_task_runner.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
-#include "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom-blink.h"
+#include "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom-blink-forward.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
 #include "third_party/blink/renderer/platform/loader/fetch/preload_key.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties_test.cc
index dfa5788..b3ae16d 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties_test.cc
@@ -4,7 +4,9 @@
 
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
 
+#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom-blink.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
 #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
index a4b84bd..0d5bf38 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -33,6 +33,7 @@
 #include <memory>
 #include "base/optional.h"
 #include "build/build_config.h"
+#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
index 2b4ef5c..0046f32 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
@@ -35,7 +35,7 @@
 #include "base/single_thread_task_runner.h"
 #include "mojo/public/cpp/base/big_buffer.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
-#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_loader.h"
 #include "third_party/blink/public/platform/web_url_loader_client.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc
index a4ac914..1676929 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "mojo/public/cpp/base/big_buffer.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/public/platform/web_url_loader.h"
 #include "third_party/blink/public/platform/web_url_loader_factory.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
index 391301b..9a88c03 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
@@ -10,6 +10,7 @@
 #include "mojo/public/c/system/data_pipe.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_loader.h"
 #include "third_party/blink/public/platform/web_url_loader_factory.h"
 #include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h b/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
index 590c26e..24e2efe 100644
--- a/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
+++ b/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SCRIPT_FETCH_OPTIONS_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SCRIPT_FETCH_OPTIONS_H_
 
-#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/platform/loader/fetch/cross_origin_attribute_value.h"
diff --git a/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc b/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc
index 62a3c3a..3bef4165 100644
--- a/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc
+++ b/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
 
+#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
 #include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/platform/loader/allowed_by_nosniff.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
diff --git a/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc b/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc
index d45cce0..94d98b1 100644
--- a/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc
+++ b/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc
@@ -33,6 +33,7 @@
 #include <stddef.h>
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
+#include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-blink.h"
 #include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
 #include "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
 #include "third_party/blink/renderer/platform/mhtml/serialized_resource.h"
@@ -45,7 +46,6 @@
 #include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
 #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/vector.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/mhtml/mhtml_archive.h b/third_party/blink/renderer/platform/mhtml/mhtml_archive.h
index 0f19fd94..985d346 100644
--- a/third_party/blink/renderer/platform/mhtml/mhtml_archive.h
+++ b/third_party/blink/renderer/platform/mhtml/mhtml_archive.h
@@ -31,7 +31,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MHTML_MHTML_ARCHIVE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MHTML_MHTML_ARCHIVE_H_
 
-#include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-blink.h"
+#include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-blink-forward.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
diff --git a/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h b/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h
index d157f8e..dda183cb 100644
--- a/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h
+++ b/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_BIG_STRING_MOJOM_TRAITS_H_
 
 #include "mojo/public/cpp/bindings/struct_traits.h"
-#include "mojo/public/mojom/base/big_string.mojom-blink.h"
+#include "mojo/public/mojom/base/big_string.mojom-blink-forward.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
 
diff --git a/third_party/blink/renderer/platform/mojo/bluetooth_mojom_traits.h b/third_party/blink/renderer/platform/mojo/bluetooth_mojom_traits.h
index 10e78a1..cde9a60 100644
--- a/third_party/blink/renderer/platform/mojo/bluetooth_mojom_traits.h
+++ b/third_party/blink/renderer/platform/mojo/bluetooth_mojom_traits.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_BLUETOOTH_MOJOM_TRAITS_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_BLUETOOTH_MOJOM_TRAITS_H_
 
-#include "device/bluetooth/public/mojom/uuid.mojom-blink.h"
+#include "device/bluetooth/public/mojom/uuid.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-blink.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
diff --git a/third_party/blink/renderer/platform/mojo/canonical_cookie_mojom_traits.h b/third_party/blink/renderer/platform/mojo/canonical_cookie_mojom_traits.h
index cbb61ba..c6d6ec8 100644
--- a/third_party/blink/renderer/platform/mojo/canonical_cookie_mojom_traits.h
+++ b/third_party/blink/renderer/platform/mojo/canonical_cookie_mojom_traits.h
@@ -7,7 +7,7 @@
 
 #include "base/time/time.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
-#include "services/network/public/mojom/restricted_cookie_manager.mojom-blink.h"
+#include "services/network/public/mojom/restricted_cookie_manager.mojom-blink-forward.h"
 #include "third_party/blink/public/platform/web_canonical_cookie.h"
 #include "third_party/blink/public/platform/web_string.h"
 
diff --git a/third_party/blink/renderer/platform/mojo/fetch_api_request_headers_mojom_traits.h b/third_party/blink/renderer/platform/mojo/fetch_api_request_headers_mojom_traits.h
index 24e4e45..62f1464 100644
--- a/third_party/blink/renderer/platform/mojo/fetch_api_request_headers_mojom_traits.h
+++ b/third_party/blink/renderer/platform/mojo/fetch_api_request_headers_mojom_traits.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_FETCH_API_REQUEST_HEADERS_MOJOM_TRAITS_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_FETCH_API_REQUEST_HEADERS_MOJOM_TRAITS_H_
 
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
diff --git a/third_party/blink/renderer/platform/mojo/kurl_mojom_traits.h b/third_party/blink/renderer/platform/mojo/kurl_mojom_traits.h
index 9d065c52..bcd6a69 100644
--- a/third_party/blink/renderer/platform/mojo/kurl_mojom_traits.h
+++ b/third_party/blink/renderer/platform/mojo/kurl_mojom_traits.h
@@ -7,7 +7,7 @@
 
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#include "url/mojom/url.mojom-blink.h"
+#include "url/mojom/url.mojom-blink-forward.h"
 #include "url/url_constants.h"
 
 namespace mojo {
diff --git a/third_party/blink/renderer/platform/mojo/security_origin_mojom_traits.h b/third_party/blink/renderer/platform/mojo/security_origin_mojom_traits.h
index d483e3f..762af70 100644
--- a/third_party/blink/renderer/platform/mojo/security_origin_mojom_traits.h
+++ b/third_party/blink/renderer/platform/mojo/security_origin_mojom_traits.h
@@ -7,7 +7,7 @@
 
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#include "url/mojom/origin.mojom-blink.h"
+#include "url/mojom/origin.mojom-blink-forward.h"
 #include "url/scheme_host_port.h"
 
 namespace mojo {
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h b/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h
index e5e8cef..c81eef75 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h
+++ b/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_ELEMENT_MOJOM_TRAITS_H_
 
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/network/public/mojom/url_loader.mojom-blink.h"
+#include "services/network/public/mojom/url_loader.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
 
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h b/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h
index 0a403da6..07b98ae 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h
+++ b/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_MOJOM_TRAITS_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_MOJOM_TRAITS_H_
 
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h"
 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
 
 namespace mojo {
diff --git a/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.cc b/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.cc
index 13e028d..ae1a35b 100644
--- a/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.cc
+++ b/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.cc
@@ -11,6 +11,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
+#include "services/network/public/mojom/mdns_responder.mojom-blink.h"
 #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/renderer/platform/wtf/text/wtf_string.h"
diff --git a/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.h b/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.h
index 53f045aa..79a70b7 100644
--- a/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.h
+++ b/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_P2P_MDNS_RESPONDER_ADAPTER_H_
 
 #include "mojo/public/cpp/bindings/shared_remote.h"
-#include "services/network/public/mojom/mdns_responder.mojom-blink.h"
+#include "services/network/public/mojom/mdns_responder.mojom-blink-forward.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/webrtc/rtc_base/mdns_responder_interface.h"
 
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
index 7bfe79c..dfa44f5 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
@@ -372,8 +372,15 @@
   media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_I420;
   auto storage_type =
       media::VideoEncodeAccelerator::Config::StorageType::kShmem;
-  // TODO(crbug.com/1014209): Enable native input mode after fake video capture
-  // device supports delivering GpuMemoryBuffer frames.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kVideoCaptureUseGpuMemoryBuffer) &&
+      video_content_type_ != webrtc::VideoContentType::SCREENSHARE) {
+    // Use import mode for camera when GpuMemoryBuffer-based video capture is
+    // enabled.
+    pixel_format = media::PIXEL_FORMAT_NV12;
+    storage_type = media::VideoEncodeAccelerator::Config::StorageType::kDmabuf;
+    use_native_input_ = true;
+  }
   const media::VideoEncodeAccelerator::Config config(
       pixel_format, input_visible_size_, profile, bitrate * 1000, base::nullopt,
       base::nullopt, base::nullopt, storage_type,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 56317d6..2383941 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -146,7 +146,7 @@
       // from CSS.
       // https://github.com/WICG/intrinsicsize-attribute/issues/16
       name: "AspectRatioFromWidthAndHeight",
-      status: "test",
+      status: "stable",
     },
     {
       name: "AsyncClipboard",
@@ -395,6 +395,7 @@
       // Support for CSS content-size property.
       // http://tabatkins.github.io/specs/css-content-size/
       name: "CSSContentSize",
+      implied_by: ["DisplayLocking"],
       status: "experimental",
     },
     {
@@ -544,7 +545,6 @@
     {
       name: "DisplayLocking",
       origin_trial_feature_name: "DisplayLocking",
-      depends_on: ["CSSContentSize"],
       status: "experimental",
     },
     {
@@ -570,6 +570,10 @@
       implied_by: ["FormAssociatedCustomElements"],
     },
     {
+      name: "EmbeddedVTTStylesheets",
+      status: "experimental",
+    },
+    {
       name: "EncryptedMediaEncryptionSchemeQuery",
       status: "test",
     },
@@ -822,6 +826,10 @@
       status: "experimental",
     },
     {
+      name: "IntersectionObserverDocumentScrollingElementRoot",
+      status: "experimental",
+    },
+    {
       name: "IntersectionObserverV2",
       status: "stable",
     },
@@ -856,7 +864,7 @@
     },
     {
       name: "LayoutNG",
-      implied_by: ["LayoutNGBlockFragmentation", "LayoutNGFieldset", "LayoutNGFlexBox", "LayoutNGFragmentItem", "LayoutNGLineCache", "EditingNG", "BidiCaretAffinity", "LayoutNGTable"],
+      implied_by: ["LayoutNGBlockFragmentation", "LayoutNGFieldset", "LayoutNGFlexBox", "LayoutNGFragmentItem", "LayoutNGFragmentPaint", "LayoutNGLineCache", "EditingNG", "BidiCaretAffinity", "LayoutNGTable"],
       status: "stable",
     },
     {
@@ -876,6 +884,9 @@
       name: "LayoutNGFragmentItem",
     },
     {
+      name: "LayoutNGFragmentPaint",
+    },
+    {
       name: "LayoutNGLineCache",
     },
     {
@@ -912,9 +923,6 @@
       status: "stable",
     },
     {
-      name: "LongTaskV2",
-    },
-    {
       name:"ManualSlotting",
       status:"experimental",
     },
@@ -1805,7 +1813,7 @@
     {
       name: "WebXrGamepadModule",
       // depends_on: ["WebXR"],  // TODO(https://crbug.com/954679): uncomment once bug is fixed
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "WebXRHitTest",
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn
index 548af81b..6812a3a5f 100644
--- a/third_party/blink/renderer/platform/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -166,6 +166,7 @@
     "//services/metrics/public/cpp:ukm_builders",
     "//services/metrics/public/mojom",
     "//third_party/blink/renderer/platform:make_platform_generated",
+    "//third_party/blink/renderer/platform/instrumentation",
     "//third_party/blink/renderer/platform/wtf",
     "//v8",
   ]
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 157df65f..f7641b6 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -97,7 +97,7 @@
   if (is_backgrounded) {
     return "renderer_backgrounded";
   } else {
-    return "renderer_foregrounded";
+    return "renderer_visible";
   }
 }
 
@@ -371,12 +371,12 @@
                             &main_thread_scheduler_impl->tracing_controller_,
                             RAILModeToString),
       renderer_hidden(false,
-                      "RendererVisibility",
+                      "Scheduler.Hidden",
                       main_thread_scheduler_impl,
                       &main_thread_scheduler_impl->tracing_controller_,
                       HiddenStateToString),
       renderer_backgrounded(kLaunchingProcessIsBackgrounded,
-                            "RendererPriority",
+                            "RendererVisibility",
                             main_thread_scheduler_impl,
                             &main_thread_scheduler_impl->tracing_controller_,
                             BackgroundStateToString),
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index d874fd2..2748d22 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -856,7 +856,7 @@
         renderer_pause_count;  // Renderer is paused if non-zero.
     TraceableState<RAILMode, TracingCategoryName::kInfo>
         rail_mode_for_tracing;  // Don't use except for tracing.
-    TraceableState<bool, TracingCategoryName::kTopLevel> renderer_hidden;
+    TraceableState<bool, TracingCategoryName::kDebug> renderer_hidden;
     TraceableState<bool, TracingCategoryName::kTopLevel> renderer_backgrounded;
     TraceableState<bool, TracingCategoryName::kDefault>
         keep_active_fetch_or_worker;
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
index a47b8b7c..d58dedd 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
@@ -35,6 +35,7 @@
 #include "base/stl_util.h"
 #include "net/base/url_util.h"
 #include "services/network/public/mojom/cors.mojom-blink.h"
+#include "services/network/public/mojom/cors_origin_pattern.mojom-blink.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/blob/blob_url.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.h b/third_party/blink/renderer/platform/weborigin/security_policy.h
index cb4e4bd..20cd6eb 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.h
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.h
@@ -29,7 +29,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_POLICY_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_POLICY_H_
 
-#include "services/network/public/mojom/cors_origin_pattern.mojom-blink.h"
+#include "services/network/public/mojom/cors_origin_pattern.mojom-blink-forward.h"
 #include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/weborigin/referrer.h"
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy_test.cc b/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
index 32414fa..2c6e996 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
 
 #include "services/network/public/mojom/cors.mojom-blink.h"
+#include "services/network/public/mojom/cors_origin_pattern.mojom-blink.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
diff --git a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h
index 73d313d..4b398f93 100644
--- a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h
+++ b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h
@@ -64,7 +64,7 @@
                DataDeleter deleter,
                void* deleter_info)
         : data_(data),
-          data_length_(length),
+          data_length_(data ? length : 0),
           deleter_(deleter),
           deleter_info_(deleter_info) {}
     // Move constructor
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index 07f0dc83..3f28ee31 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -129,6 +129,9 @@
         ('mac10.11', 'x86'),
         ('mac10.12', 'x86'),
         ('mac10.13', 'x86'),
+        ('mac10.14', 'x86'),
+        ('mac10.15', 'x86'),
+
         ('win7', 'x86'),
         ('win10', 'x86'),
         ('trusty', 'x86_64'),
@@ -140,6 +143,10 @@
     )
 
     CONFIGURATION_SPECIFIER_MACROS = {
+        # NOTE: We don't support specifiers for mac10.14 or mac10.15 because
+        # we don't have separate baselines for them (they share the mac10.13
+        # results in the platform/mac directory). This list will need to be
+        # updated if/when we actually have separate baselines.
         'mac': ['retina', 'mac10.10', 'mac10.11', 'mac10.12', 'mac10.13'],
         'win': ['win7', 'win10'],
         'linux': ['trusty'],
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 93118db0..ac59955 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -260,9 +260,6 @@
 
 # Display locking failures
 
-# MSAN failure
-crbug.com/996625 inspector-protocol/accessibility/accessibility-getFullAXTree-display-locked.js [ Skip ]
-
 crbug.com/849459 fragmentation/repeating-thead-under-repeating-thead.html [ Failure ]
 
 # These tests are no longer applicable in BlinkGenPropertyTree mode.
@@ -2447,10 +2444,6 @@
 
 crbug.com/876485 fast/performance/performance-measure-null-exception.html [ Failure ]
 
-# These tests requires enabling the flag LongTaskV2 in a virtual test suite.
-crbug.com/738493 http/tests/performance-timing/longtask-v2/longtask-v8compile.html [ Skip ]
-crbug.com/738493 http/tests/performance-timing/longtask-v2/longtask-executescript.html [ Skip ]
-
 crbug.com/713587 external/wpt/css/css-ui/caret-color-006.html [ Skip ]
 
 # Automatically-added expectations for CSS tests which are temporarily
@@ -2537,7 +2530,17 @@
 crbug.com/953847 [ Mac ] virtual/threaded/fast/scrolling/scrollbars/scrollbar-occluded-by-div.html [ Failure ]
 crbug.com/953847 [ Mac ] virtual/threaded/fast/scrolling/scrollbars/scrollbar-button-gesture-target.html [ Failure ]
 crbug.com/953847 [ Mac ] virtual/threaded/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html [ Failure Timeout ]
-crbug.com/953847 [ Mac ] virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/scrollbar-button-gesture-target.html [ Failure ]
+
+# Compositor threaded scrollbar scrolls parent of subscroller.
+# Note: even when fixed will still fail on Mac until crbug.com/953847
+# is addressed. Uncomment below line when fixing crbug.com/965829.
+#crbug.com/953847 [ Mac ] virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/scrollbar-button-gesture-target.html [ Failure ]
+crbug.com/987115 [ Win ] virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/scrollbar-button-gesture-target.html [ Failure Crash ]
+crbug.com/965829 [ Mac ] virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/scrollbar-button-gesture-target.html [ Failure ]
+
+# 1px scroll offset difference for compositor threaded scrollbar scrolling on Linux.
+crbug.com/1009892 [ Win ] virtual/compositor_threaded_scrollbar_scrolling_hidpi_nozoomfordsf/fast/scrolling/scrollbars/mouse-scrolling-on-div-scrollbar.html [ Failure ]
+crbug.com/1009892 [ Win ] virtual/compositor_threaded_scrollbar_scrolling_hidpi_nozoomfordsf/fast/scrolling/scrollbars/scrollbar-thumb-snapping.html [ Failure ]
 
 # Some control characters still not visible
 crbug.com/893490 [ Mac ] external/wpt/css/css-text/white-space/control-chars-001.html [ Failure ]
@@ -2645,6 +2648,11 @@
 crbug.com/1012627 [ Win7 ] external/wpt/css/css-text/line-breaking/line-breaking-021.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Linux ] external/wpt/css/css-text/white-space/eol-spaces-bidi-001.html [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html [ Timeout ]
+crbug.com/626703 [ Mac ] external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html [ Timeout ]
+crbug.com/626703 [ Win ] external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html [ Timeout ]
+crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cues-enter-exit.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/preload/preload-with-type.html [ Pass Timeout ]
 crbug.com/626703 [ Win10 ] virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/threaded/external/wpt/css/css-animations/animation-opacity-pause-and-set-time.html [ Failure ]
@@ -2655,7 +2663,7 @@
 crbug.com/626703 [ Linux ] external/wpt/css/css-align/baseline-rules/grid-item-input-type-text.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-align/baseline-rules/grid-item-input-type-text.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-align/baseline-rules/grid-item-input-type-text.html [ Failure ]
-crbug.com/626703 external/wpt/webxr/events_referenceSpace_reset.https.html [ Timeout ]
+crbug.com/626703 external/wpt/webxr/events_referenceSpace_reset_inline.https.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/clear-site-data/storage.https.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/clear-site-data/storage.https.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/clear-site-data/storage.https.html [ Timeout ]
@@ -3715,7 +3723,6 @@
 crbug.com/626703 external/wpt/html/cross-origin-opener-policy/coop-sandbox.https.html [ Failure ]
 crbug.com/626703 [ Debug ] external/wpt/html/cross-origin-embedder-policy/require-corp.https.html [ Failure ]
 crbug.com/626703 [ Debug ] virtual/cross-origin-embedder-policy/external/wpt/html/cross-origin-embedder-policy/require-corp.https.html [ Pass Failure ]
-crbug.com/626703 [ Linux Mac10.10 ] virtual/cross-origin-embedder-policy/external/wpt/html/cross-origin-embedder-policy/none.https.html [ Pass Failure ]
 
 # Failure due to on-going off-thread paint worklet project.
 crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/invalid-image-pending-script.https.html [ Crash ]
@@ -3966,6 +3973,11 @@
 crbug.com/825798 external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-non-snap-to-lines.html [ Failure ]
 
 crbug.com/927477 external/wpt/import-maps/acquire-import-maps-flag/worker-request/ [ Skip ]
+crbug.com/927477 virtual/import-maps-without-builtin-modules/external/wpt/import-maps/acquire-import-maps-flag/worker-request/ [ Skip ]
+
+# Excludes tests that require built-in module supports.
+crbug.com/1010751 virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/ [ Skip ]
+crbug.com/1010751 external/wpt/import-maps/imported/ [ Skip ]
 
 # This test requires a special browser flag and seems not suitable for a wpt test, see bug.
 crbug.com/691944 external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
@@ -5619,6 +5631,9 @@
 crbug.com/1002527 [ Debug ] fast/text/large-text-composed-char.html [ Pass Crash ]
 crbug.com/1002686 [ Mac10.13 ] virtual/threaded/http/tests/devtools/tracing/timeline-style/timeline-recalculate-styles.js [ Failure ]
 
+# Sheriff 2019-09-11
+crbug.com/1002828 [ Linux ] virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/scrollbar-button-gesture-target.html [ Pass Failure Crash ]
+
 # Tests fail because of scrollbar-width property
 crbug.com/1003055 external/wpt/css/css-scroll-snap/scroll-target-align-001.html [ Failure ]
 crbug.com/1003055 external/wpt/css/css-scroll-snap/scroll-target-align-002.html [ Failure ]
@@ -5710,3 +5725,6 @@
 crbug.com/1014810 virtual/threaded/external/wpt/animation-worklet/stateful-animator.https.html [ Pass Timeout ]
 crbug.com/1014812 external/wpt/animation-worklet/playback-rate.https.html [ Pass Timeout Failure ]
 crbug.com/1014812 virtual/threaded/external/wpt/animation-worklet/playback-rate.https.html [ Pass Timeout Failure ]
+crbug.com/1014950 [ Mac ] http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end.js [ Pass Timeout ]
+crbug.com/1014950 [ Mac ] virtual/threaded/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end.js [ Pass Timeout ]
+crbug.com/1015187 [ Linux Mac ] virtual/cross-origin-embedder-policy/external/wpt/html/cross-origin-embedder-policy/none.https.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 5b4c0ab..efb5506 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -487,11 +487,6 @@
     "args": ["--force-presentation-receiver-for-testing"]
   },
   {
-    "prefix": "longtask-v2",
-    "base": "http/tests/performance-timing/longtask-v2",
-    "args": ["--enable-blink-features=LongTaskV2"]
-  },
-  {
     "prefix": "paint-timing",
     "base": "external/wpt/paint-timing",
     "args": ["--enable-threaded-compositing"]
@@ -1128,5 +1123,10 @@
     "prefix": "out-of-blink-frame-ancestors",
     "base": "external/wpt/content-security-policy/frame-ancestors",
     "args": ["--enable-features=OutOfBlinkFrameAncestors"]
+  },
+  {
+    "prefix": "import-maps-without-builtin-modules",
+    "base": "external/wpt/import-maps",
+    "args": ["--enable-blink-features=ImportMaps", "--disable-blink-features=ExperimentalProductivityFeatures"]
   }
 ]
diff --git a/third_party/blink/web_tests/WebGPUExpectations b/third_party/blink/web_tests/WebGPUExpectations
index 5172c18d..b396bc50 100644
--- a/third_party/blink/web_tests/WebGPUExpectations
+++ b/third_party/blink/web_tests/WebGPUExpectations
@@ -3,3 +3,46 @@
 #
 # Expectations in this file must apply to wpt_internal/webgpu/cts.html, NOT
 # external/wpt/webgpu/cts.html, as the latter is not run.
+
+#
+# Platform-independent failures
+#
+
+crbug.com/1014734 wpt_internal/webgpu/cts.html?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type~{"bindingType":"readonly-storage-buffer"} [ Failure ]
+
+crbug.com/1014735 wpt_internal/webgpu/cts.html?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type~{"bindingType":"storage-texture"} [ Failure ]
+
+crbug.com/1014735 wpt_internal/webgpu/cts.html?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"uniform-buffer","resourceType":"storage-texture"} [ Failure ]
+crbug.com/1014735 wpt_internal/webgpu/cts.html?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"storage-buffer","resourceType":"storage-texture"} [ Failure ]
+crbug.com/1014735 wpt_internal/webgpu/cts.html?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampler","resourceType":"storage-texture"} [ Failure ]
+crbug.com/1014735 wpt_internal/webgpu/cts.html?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampled-texture","resourceType":"storage-texture"} [ Failure ]
+
+crbug.com/1014735 wpt_internal/webgpu/cts.html?q=cts:validation/createBindGroup:texture+binding+must+have+correct+usage={"type":"storage-texture"} [ Failure ]
+
+crbug.com/1014734 wpt_internal/webgpu/cts.html?q=cts:validation/createBindGroupLayout:dynamic+set+to+true+is+allowed+only+for+buffers={"type":"readonly-storage-buffer","success":true} [ Failure ]
+
+crbug.com/dawn/243 wpt_internal/webgpu/cts.html?q=cts:validation/createTexture:validation+of+sampleCount={"sampleCount":4,"arrayLayerCount":2,"success":true} [ Failure ]
+
+crbug.com/1014750 wpt_internal/webgpu/cts.html?q=cts:validation/createView:it+is+invalid+to+use+a+texture+view+created+from+a+destroyed+texture= [ Failure ]
+
+#
+# Mac (Metal) specific
+#
+# TODO(crbug.com/1014785): Theses should be [ Mac ], but that doesn't work right now under the mac10.14 config that we run on dawn-mac-x64-deps-rel.
+#
+
+crbug.com/1014744 wpt_internal/webgpu/cts.html?q=cts:command_buffer/render/storeop:storeOp+controls+whether+1x1+drawn+quad+is+stored={"storeOp":"clear","expected":0} [ Failure ]
+
+#
+# Linux (Vulkan) specific
+#
+
+crbug.com/1014740 [ Linux ] wpt_internal/webgpu/cts.html?q=cts:buffers/map_oom:mapWriteAsync= [ Failure ]
+crbug.com/1014740 [ Linux ] wpt_internal/webgpu/cts.html?q=cts:buffers/map_oom:mapReadAsync= [ Failure ]
+
+#
+# Windows (D3D12) specific
+#
+
+crbug.com/1014738 [ Win ] wpt_internal/webgpu/cts.html?q=cts:buffers/map_oom:mapWriteAsync= [ Failure ]
+crbug.com/1014738 [ Win ] wpt_internal/webgpu/cts.html?q=cts:buffers/map_oom:mapReadAsync= [ Failure ]
diff --git a/third_party/blink/web_tests/editing/pasteboard/paste-xss-injection.html b/third_party/blink/web_tests/editing/pasteboard/paste-xss-injection.html
new file mode 100644
index 0000000..2d3cc84
--- /dev/null
+++ b/third_party/blink/web_tests/editing/pasteboard/paste-xss-injection.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
+<script>
+selection_test(
+  '<div contenteditable>te|st</div>',
+  selection => {
+    selection.setClipboardData('<math><xss style=display:block>t<style>X<a title="</style><img src onerror=alert(1)>">.<a>.');
+    selection.document.execCommand('paste');
+  },
+  '<div contenteditable>te<br>t<style>X<a title="</style><img src>">.<a>.|</a>st</div>',
+  'Paste blocks script injection');
+</script>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index 0939c49b..e2540fa 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -59921,6 +59921,30 @@
      {}
     ]
    ],
+   "css/css-overflow/overflow-body-propagation-005.html": [
+    [
+     "css/css-overflow/overflow-body-propagation-005.html",
+     [
+      [
+       "/css/css-overflow/reference/overflow-body-propagation-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-overflow/overflow-body-propagation-006.html": [
+    [
+     "css/css-overflow/overflow-body-propagation-006.html",
+     [
+      [
+       "/css/css-overflow/reference/overflow-body-propagation-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-overflow/webkit-line-clamp-001.html": [
     [
      "css/css-overflow/webkit-line-clamp-001.html",
@@ -65933,6 +65957,18 @@
      {}
     ]
    ],
+   "css/css-tables/box-shadow-001.html": [
+    [
+     "css/css-tables/box-shadow-001.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-tables/calc-percent-plus-0px-auto.html": [
     [
      "css/css-tables/calc-percent-plus-0px-auto.html",
@@ -74289,6 +74325,18 @@
      {}
     ]
    ],
+   "css/css-text/white-space/eol-spaces-bidi-001.html": [
+    [
+     "css/css-text/white-space/eol-spaces-bidi-001.html",
+     [
+      [
+       "/css/css-text/white-space/reference/eol-spaces-bidi-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/white-space/line-edge-white-space-collapse-001.html": [
     [
      "css/css-text/white-space/line-edge-white-space-collapse-001.html",
@@ -141151,9 +141199,6 @@
    "css/css-shapes/shape-outside/values/shape-margin-005-expected.txt": [
     []
    ],
-   "css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt": [
-    []
-   ],
    "css/css-shapes/shape-outside/values/shape-outside-inset-001-expected.txt": [
     []
    ],
@@ -143599,6 +143644,9 @@
    "css/css-text/white-space/reference/control-chars-000-ref.html": [
     []
    ],
+   "css/css-text/white-space/reference/eol-spaces-bidi-001-ref.html": [
+    []
+   ],
    "css/css-text/white-space/reference/line-edge-white-space-collapse-001-ref.html": [
     []
    ],
@@ -161815,9 +161863,6 @@
    "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/OWNERS": [
     []
    ],
-   "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window-expected.txt": [
-    []
-   ],
    "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-ignore-opens-during-unload.window-expected.txt": [
     []
    ],
@@ -162202,61 +162247,94 @@
    "images/yellow75.png": [
     []
    ],
-   "import-maps/@std/__dir__.headers": [
-    []
-   ],
-   "import-maps/@std/blank": [
-    []
-   ],
-   "import-maps/@std/none": [
-    []
-   ],
    "import-maps/README.md": [
     []
    ],
-   "import-maps/bare/__dir__.headers": [
+   "import-maps/builtin-support.tentative/@std/__dir__.headers": [
     []
    ],
-   "import-maps/bare/bare": [
+   "import-maps/builtin-support.tentative/@std/blank": [
     []
    ],
-   "import-maps/bare/blank": [
+   "import-maps/builtin-support.tentative/@std/none": [
     []
    ],
-   "import-maps/bare/cross-origin-bare": [
+   "import-maps/builtin-support.tentative/bare/__dir__.headers": [
     []
    ],
-   "import-maps/bare/none": [
+   "import-maps/builtin-support.tentative/bare/blank": [
     []
    ],
-   "import-maps/bare/std-blank": [
+   "import-maps/builtin-support.tentative/bare/none": [
     []
    ],
-   "import-maps/bare/std-none": [
+   "import-maps/builtin-support.tentative/bare/std-blank": [
     []
    ],
-   "import-maps/bare/to-bare": [
+   "import-maps/builtin-support.tentative/bare/std-none": [
     []
    ],
-   "import-maps/bare/to-data": [
+   "import-maps/builtin-support.tentative/imported/parsing-addresses.tentative-expected.txt": [
     []
    ],
-   "import-maps/imported/parsing-addresses.tentative-expected.txt": [
+   "import-maps/builtin-support.tentative/imported/parsing-scope-keys.tentative-expected.txt": [
     []
    ],
-   "import-maps/imported/parsing-schema.tentative-expected.txt": [
+   "import-maps/builtin-support.tentative/imported/parsing-specifier-keys.tentative-expected.txt": [
     []
    ],
-   "import-maps/imported/parsing-scope-keys.tentative-expected.txt": [
+   "import-maps/builtin-support.tentative/imported/resolving-builtins.tentative-expected.txt": [
     []
    ],
-   "import-maps/imported/parsing-specifier-keys.tentative-expected.txt": [
+   "import-maps/builtin-support.tentative/imported/resolving.tentative-expected.txt": [
     []
    ],
-   "import-maps/imported/resolving-builtins.tentative-expected.txt": [
+   "import-maps/builtin-support.tentative/imported/resources/helpers/parsing.js": [
     []
    ],
-   "import-maps/imported/resolving.tentative-expected.txt": [
+   "import-maps/builtin-support.tentative/imported/resources/parsing-addresses.js": [
+    []
+   ],
+   "import-maps/builtin-support.tentative/imported/resources/parsing-schema.js": [
+    []
+   ],
+   "import-maps/builtin-support.tentative/imported/resources/parsing-scope-keys.js": [
+    []
+   ],
+   "import-maps/builtin-support.tentative/imported/resources/parsing-specifier-keys.js": [
+    []
+   ],
+   "import-maps/builtin-support.tentative/imported/resources/resolving-builtins.js": [
+    []
+   ],
+   "import-maps/builtin-support.tentative/imported/resources/resolving-not-yet-implemented.js": [
+    []
+   ],
+   "import-maps/builtin-support.tentative/imported/resources/resolving-scopes.js": [
+    []
+   ],
+   "import-maps/builtin-support.tentative/imported/resources/resolving.js": [
+    []
+   ],
+   "import-maps/builtin-support.tentative/static-import.js": [
+    []
+   ],
+   "import-maps/core/bare/__dir__.headers": [
+    []
+   ],
+   "import-maps/core/bare/bare": [
+    []
+   ],
+   "import-maps/core/bare/cross-origin-bare": [
+    []
+   ],
+   "import-maps/core/bare/to-bare": [
+    []
+   ],
+   "import-maps/core/bare/to-data": [
+    []
+   ],
+   "import-maps/core/static-import.js": [
     []
    ],
    "import-maps/imported/resources/helpers/parsing.js": [
@@ -162274,12 +162352,6 @@
    "import-maps/imported/resources/parsing-specifier-keys.js": [
     []
    ],
-   "import-maps/imported/resources/resolving-builtins.js": [
-    []
-   ],
-   "import-maps/imported/resources/resolving-not-yet-implemented.js": [
-    []
-   ],
    "import-maps/imported/resources/resolving-scopes.js": [
     []
    ],
@@ -162301,9 +162373,6 @@
    "import-maps/resources/test-helper.js": [
     []
    ],
-   "import-maps/static-import.js": [
-    []
-   ],
    "inert/frame/button.html": [
     []
    ],
@@ -162634,6 +162703,9 @@
    "interfaces/css-regions.idl": [
     []
    ],
+   "interfaces/css-shadow-parts.idl": [
+    []
+   ],
    "interfaces/css-transitions.idl": [
     []
    ],
@@ -165292,6 +165364,21 @@
    "portals/csp/resources/frame-src.sub.html.sub.headers": [
     []
    ],
+   "portals/history/resources/inner-iframe.html": [
+    []
+   ],
+   "portals/history/resources/portal-harness.js": [
+    []
+   ],
+   "portals/history/resources/portal-manipulate-history-with-subframes.sub.html": [
+    []
+   ],
+   "portals/history/resources/portal-manipulate-history.html": [
+    []
+   ],
+   "portals/history/resources/run-test-in-portal.js": [
+    []
+   ],
    "portals/portals-referrer-inherit-header.html.headers": [
     []
    ],
@@ -170053,6 +170140,12 @@
    "shadow-dom/directionality-002-ref.html": [
     []
    ],
+   "shadow-dom/focus/click-focus-delegatesFocus-tabindex-varies-expected.txt": [
+    []
+   ],
+   "shadow-dom/focus/focus-method-delegatesFocus-expected.txt": [
+    []
+   ],
    "shadow-dom/focus/focus-selector-delegatesFocus-expected.txt": [
     []
    ],
@@ -170395,6 +170488,9 @@
    "storage-access-api/idl.window-expected.txt": [
     []
    ],
+   "storage-access-api/idlharness.window-expected.txt": [
+    []
+   ],
    "storage/META.yml": [
     []
    ],
@@ -174646,6 +174742,9 @@
    "tools/wpt/requirements.txt": [
     []
    ],
+   "tools/wpt/revlist.py": [
+    []
+   ],
    "tools/wpt/run.py": [
     []
    ],
@@ -176227,6 +176326,9 @@
    "web-nfc/NFCWriter-document-hidden-manual.https-expected.txt": [
     []
    ],
+   "web-nfc/NFCWriter_push.https-expected.txt": [
+    []
+   ],
    "web-nfc/OWNERS": [
     []
    ],
@@ -176773,12 +176875,18 @@
    "webgpu/framework/url_query.js": [
     []
    ],
+   "webgpu/framework/util/async_mutex.js": [
+    []
+   ],
    "webgpu/framework/util/index.js": [
     []
    ],
    "webgpu/framework/util/stack.js": [
     []
    ],
+   "webgpu/framework/util/timeout.js": [
+    []
+   ],
    "webgpu/framework/version.js": [
     []
    ],
@@ -176818,6 +176926,9 @@
    "webgpu/suites/cts/command_buffer/render/rendering.spec.js": [
     []
    ],
+   "webgpu/suites/cts/command_buffer/render/storeop.spec.js": [
+    []
+   ],
    "webgpu/suites/cts/examples.spec.js": [
     []
    ],
@@ -176830,6 +176941,63 @@
    "webgpu/suites/cts/index.js": [
     []
    ],
+   "webgpu/suites/cts/validation/createBindGroup.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/createBindGroupLayout.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/createPipelineLayout.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/createRenderPipeline.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/createTexture.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/createView.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/error_scope.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/fences.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/queue_submit.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/render_pass.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/render_pass_descriptor.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/setBindGroup.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/setBlendColor.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/setScissorRect.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/setStencilReference.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/setVertexBuffer.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/setViewport.spec.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/validation_test.js": [
+    []
+   ],
+   "webgpu/suites/cts/validation/vertex_input.spec.js": [
+    []
+   ],
    "webmessaging/META.yml": [
     []
    ],
@@ -211934,6 +212102,12 @@
      {}
     ]
    ],
+   "css/css-shadow-parts/idlharness.html": [
+    [
+     "css/css-shadow-parts/idlharness.html",
+     {}
+    ]
+   ],
    "css/css-shadow-parts/inner-host.html": [
     [
      "css/css-shadow-parts/inner-host.html",
@@ -212018,6 +212192,24 @@
      {}
     ]
    ],
+   "css/css-shadow-parts/simple-important-important.html": [
+    [
+     "css/css-shadow-parts/simple-important-important.html",
+     {}
+    ]
+   ],
+   "css/css-shadow-parts/simple-important-inline.html": [
+    [
+     "css/css-shadow-parts/simple-important-inline.html",
+     {}
+    ]
+   ],
+   "css/css-shadow-parts/simple-important.html": [
+    [
+     "css/css-shadow-parts/simple-important.html",
+     {}
+    ]
+   ],
    "css/css-shadow-parts/simple-inline.html": [
     [
      "css/css-shadow-parts/simple-inline.html",
@@ -248244,6 +248436,24 @@
      {}
     ]
    ],
+   "html/semantics/forms/form-submission-0/form-double-submit-2.html": [
+    [
+     "html/semantics/forms/form-submission-0/form-double-submit-2.html",
+     {}
+    ]
+   ],
+   "html/semantics/forms/form-submission-0/form-double-submit-3.html": [
+    [
+     "html/semantics/forms/form-submission-0/form-double-submit-3.html",
+     {}
+    ]
+   ],
+   "html/semantics/forms/form-submission-0/form-double-submit.html": [
+    [
+     "html/semantics/forms/form-submission-0/form-double-submit.html",
+     {}
+    ]
+   ],
    "html/semantics/forms/form-submission-0/form-submission-algorithm.html": [
     [
      "html/semantics/forms/form-submission-0/form-submission-algorithm.html",
@@ -254057,6 +254267,12 @@
      {}
     ]
    ],
+   "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html": [
+    [
+     "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html",
+     {}
+    ]
+   ],
    "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/mutation-events.window.js": [
     [
      "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/mutation-events.window.html",
@@ -255065,29 +255281,135 @@
      {}
     ]
    ],
-   "import-maps/bare.sub.tentative.html": [
+   "import-maps/builtin-support.tentative/bare.sub.tentative.html": [
     [
-     "import-maps/bare.sub.tentative.html",
+     "import-maps/builtin-support.tentative/bare.sub.tentative.html",
      {
       "timeout": "long"
      }
     ]
    ],
-   "import-maps/builtin-empty.tentative.html": [
+   "import-maps/builtin-support.tentative/builtin-empty.tentative.html": [
     [
-     "import-maps/builtin-empty.tentative.html",
+     "import-maps/builtin-support.tentative/builtin-empty.tentative.html",
      {}
     ]
    ],
-   "import-maps/builtin-import-scheme.tentative.html": [
+   "import-maps/builtin-support.tentative/builtin-import-scheme.tentative.html": [
     [
-     "import-maps/builtin-import-scheme.tentative.html",
+     "import-maps/builtin-support.tentative/builtin-import-scheme.tentative.html",
      {}
     ]
    ],
-   "import-maps/builtin.tentative.html": [
+   "import-maps/builtin-support.tentative/builtin.tentative.html": [
     [
-     "import-maps/builtin.tentative.html",
+     "import-maps/builtin-support.tentative/builtin.tentative.html",
+     {}
+    ]
+   ],
+   "import-maps/builtin-support.tentative/data.sub.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/data.sub.tentative.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
+   "import-maps/builtin-support.tentative/fallback-disallowed.sub.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/fallback-disallowed.sub.tentative.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
+   "import-maps/builtin-support.tentative/fallback.sub.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/fallback.sub.tentative.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
+   "import-maps/builtin-support.tentative/http.sub.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/http.sub.tentative.html",
+     {}
+    ]
+   ],
+   "import-maps/builtin-support.tentative/imported/parsing-addresses.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/imported/parsing-addresses.tentative.html",
+     {}
+    ]
+   ],
+   "import-maps/builtin-support.tentative/imported/parsing-schema.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/imported/parsing-schema.tentative.html",
+     {}
+    ]
+   ],
+   "import-maps/builtin-support.tentative/imported/parsing-scope-keys.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/imported/parsing-scope-keys.tentative.html",
+     {}
+    ]
+   ],
+   "import-maps/builtin-support.tentative/imported/parsing-specifier-keys.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/imported/parsing-specifier-keys.tentative.html",
+     {}
+    ]
+   ],
+   "import-maps/builtin-support.tentative/imported/resolving-builtins.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/imported/resolving-builtins.tentative.html",
+     {}
+    ]
+   ],
+   "import-maps/builtin-support.tentative/imported/resolving-not-yet-implemented.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/imported/resolving-not-yet-implemented.tentative.html",
+     {}
+    ]
+   ],
+   "import-maps/builtin-support.tentative/imported/resolving-scopes.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/imported/resolving-scopes.tentative.html",
+     {}
+    ]
+   ],
+   "import-maps/builtin-support.tentative/imported/resolving.tentative.html": [
+    [
+     "import-maps/builtin-support.tentative/imported/resolving.tentative.html",
+     {}
+    ]
+   ],
+   "import-maps/core/bare.sub.tentative.html": [
+    [
+     "import-maps/core/bare.sub.tentative.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
+   "import-maps/core/data.sub.tentative.html": [
+    [
+     "import-maps/core/data.sub.tentative.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
+   "import-maps/core/http.sub.tentative.html": [
+    [
+     "import-maps/core/http.sub.tentative.html",
+     {}
+    ]
+   ],
+   "import-maps/core/module-map-key.tentative.html": [
+    [
+     "import-maps/core/module-map-key.tentative.html",
      {}
     ]
    ],
@@ -255133,36 +255455,6 @@
      {}
     ]
    ],
-   "import-maps/data.sub.tentative.html": [
-    [
-     "import-maps/data.sub.tentative.html",
-     {
-      "timeout": "long"
-     }
-    ]
-   ],
-   "import-maps/fallback-disallowed.sub.tentative.html": [
-    [
-     "import-maps/fallback-disallowed.sub.tentative.html",
-     {
-      "timeout": "long"
-     }
-    ]
-   ],
-   "import-maps/fallback.sub.tentative.html": [
-    [
-     "import-maps/fallback.sub.tentative.html",
-     {
-      "timeout": "long"
-     }
-    ]
-   ],
-   "import-maps/http.sub.tentative.html": [
-    [
-     "import-maps/http.sub.tentative.html",
-     {}
-    ]
-   ],
    "import-maps/imported/parsing-addresses.tentative.html": [
     [
      "import-maps/imported/parsing-addresses.tentative.html",
@@ -255187,18 +255479,6 @@
      {}
     ]
    ],
-   "import-maps/imported/resolving-builtins.tentative.html": [
-    [
-     "import-maps/imported/resolving-builtins.tentative.html",
-     {}
-    ]
-   ],
-   "import-maps/imported/resolving-not-yet-implemented.tentative.html": [
-    [
-     "import-maps/imported/resolving-not-yet-implemented.tentative.html",
-     {}
-    ]
-   ],
    "import-maps/imported/resolving-scopes.tentative.html": [
     [
      "import-maps/imported/resolving-scopes.tentative.html",
@@ -255211,12 +255491,6 @@
      {}
     ]
    ],
-   "import-maps/module-map-key.tentative.html": [
-    [
-     "import-maps/module-map-key.tentative.html",
-     {}
-    ]
-   ],
    "inert/inert-does-not-match-disabled-selector.tentative.html": [
     [
      "inert/inert-does-not-match-disabled-selector.tentative.html",
@@ -255776,6 +256050,12 @@
      {}
     ]
    ],
+   "intersection-observer/document-scrolling-element-root.html": [
+    [
+     "intersection-observer/document-scrolling-element-root.html",
+     {}
+    ]
+   ],
    "intersection-observer/edge-inclusive-intersection.html": [
     [
      "intersection-observer/edge-inclusive-intersection.html",
@@ -273534,6 +273814,18 @@
      {}
     ]
    ],
+   "portals/history/history-manipulation-inside-portal-with-subframes.html": [
+    [
+     "portals/history/history-manipulation-inside-portal-with-subframes.html",
+     {}
+    ]
+   ],
+   "portals/history/history-manipulation-inside-portal.html": [
+    [
+     "portals/history/history-manipulation-inside-portal.html",
+     {}
+    ]
+   ],
    "portals/htmlportalelement-event-handler-content-attributes.html": [
     [
      "portals/htmlportalelement-event-handler-content-attributes.html",
@@ -291763,6 +292055,38 @@
      }
     ]
    ],
+   "shadow-dom/focus/click-focus-delegatesFocus-click-method.html": [
+    [
+     "shadow-dom/focus/click-focus-delegatesFocus-click-method.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
+   "shadow-dom/focus/click-focus-delegatesFocus-tabindex-varies.html": [
+    [
+     "shadow-dom/focus/click-focus-delegatesFocus-tabindex-varies.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
+   "shadow-dom/focus/click-focus-delegatesFocus-tabindex-zero.html": [
+    [
+     "shadow-dom/focus/click-focus-delegatesFocus-tabindex-zero.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
+   "shadow-dom/focus/focus-method-delegatesFocus.html": [
+    [
+     "shadow-dom/focus/focus-method-delegatesFocus.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
    "shadow-dom/focus/focus-selector-delegatesFocus.html": [
     [
      "shadow-dom/focus/focus-selector-delegatesFocus.html",
@@ -291771,6 +292095,14 @@
      }
     ]
    ],
+   "shadow-dom/focus/focus-tabindex-order-shadow-negative-delegatesFocus.html": [
+    [
+     "shadow-dom/focus/focus-tabindex-order-shadow-negative-delegatesFocus.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
    "shadow-dom/focus/focus-tabindex-order-shadow-negative.html": [
     [
      "shadow-dom/focus/focus-tabindex-order-shadow-negative.html",
@@ -291787,6 +292119,14 @@
      }
     ]
    ],
+   "shadow-dom/focus/focus-tabindex-order-shadow-varying-delegatesFocus.html": [
+    [
+     "shadow-dom/focus/focus-tabindex-order-shadow-varying-delegatesFocus.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
    "shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex.html": [
     [
      "shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex.html",
@@ -291795,6 +292135,14 @@
      }
     ]
    ],
+   "shadow-dom/focus/focus-tabindex-order-shadow-zero-delegatesFocus.html": [
+    [
+     "shadow-dom/focus/focus-tabindex-order-shadow-zero-delegatesFocus.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
    "shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative.html": [
     [
      "shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative.html",
@@ -292997,9 +293345,9 @@
      }
     ]
    ],
-   "storage-access-api/idl.window.js": [
+   "storage-access-api/idlharness.window.js": [
     [
-     "storage-access-api/idl.window.html",
+     "storage-access-api/idlharness.window.html",
      {
       "script_metadata": [
        [
@@ -311124,12 +311472,88 @@
      {}
     ],
     [
+     "webgpu/cts.html?q=cts:command_buffer/render/storeop:",
+     {}
+    ],
+    [
      "webgpu/cts.html?q=cts:examples:",
      {}
     ],
     [
      "webgpu/cts.html?q=cts:fences:",
      {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/createBindGroup:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/createBindGroupLayout:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/createPipelineLayout:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/createRenderPipeline:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/createTexture:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/createView:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/error_scope:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/fences:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/queue_submit:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/render_pass:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/render_pass_descriptor:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/setBindGroup:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/setBlendColor:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/setScissorRect:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/setStencilReference:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/setVertexBuffer:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/setViewport:",
+     {}
+    ],
+    [
+     "webgpu/cts.html?q=cts:validation/vertex_input:",
+     {}
     ]
    ],
    "webmessaging/Channel_postMessage_Blob.htm": [
@@ -317631,9 +318055,15 @@
      {}
     ]
    ],
-   "webxr/events_referenceSpace_reset.https.html": [
+   "webxr/events_referenceSpace_reset_immersive.https.html": [
     [
-     "webxr/events_referenceSpace_reset.https.html",
+     "webxr/events_referenceSpace_reset_immersive.https.html",
+     {}
+    ]
+   ],
+   "webxr/events_referenceSpace_reset_inline.https.html": [
+    [
+     "webxr/events_referenceSpace_reset_inline.https.html",
      {}
     ]
    ],
@@ -337590,7 +338020,7 @@
    "support"
   ],
   "animation-worklet/inactive-timeline.https.html": [
-   "bdd46f9abc0c8702931965f981b9beab2792380f",
+   "24dc98c2327e45dd263b63139510445e96141ce1",
    "testharness"
   ],
   "animation-worklet/multiple-effects-on-same-target-driven-by-individual-local-time.https.html": [
@@ -388353,6 +388783,14 @@
    "2ed8d2687a33608e0e70884ad2ea59330d2b09e3",
    "reftest"
   ],
+  "css/css-overflow/overflow-body-propagation-005.html": [
+   "e4de5769584611f61ec2fecd99c2324916a3d6ec",
+   "reftest"
+  ],
+  "css/css-overflow/overflow-body-propagation-006.html": [
+   "475815068357ee24cfe9f22f968b0289ca982383",
+   "reftest"
+  ],
   "css/css-overflow/overflow-codependent-scrollbars.html": [
    "eb0e4f5ea2ed9a97c0931c2c53de5e62e72ba1dd",
    "testharness"
@@ -391569,6 +392007,10 @@
    "2e65c4b1b57bd9959c68e85e11b442c67f2c5a42",
    "testharness"
   ],
+  "css/css-shadow-parts/idlharness.html": [
+   "182943ac69de04b409e91b4005fb378cac36f9e2",
+   "testharness"
+  ],
   "css/css-shadow-parts/inner-host.html": [
    "2dfd4b0510a758c73bf8ac8291088d39077578d7",
    "testharness"
@@ -391641,6 +392083,18 @@
    "a1a19aee4ede175f7e862318105f4625144b6e42",
    "testharness"
   ],
+  "css/css-shadow-parts/simple-important-important.html": [
+   "43ec1bbae346e03e2e28209409f24a91273c5bdd",
+   "testharness"
+  ],
+  "css/css-shadow-parts/simple-important-inline.html": [
+   "61b83dd4be7f4ff19236e5b6568424191382de80",
+   "testharness"
+  ],
+  "css/css-shadow-parts/simple-important.html": [
+   "e3240bd3905f86cfa37dbc72af14dc95eb40d84e",
+   "testharness"
+  ],
   "css/css-shadow-parts/simple-inline.html": [
    "f2c3638d1e9cc66dcc8051a00f778bc95cb33129",
    "testharness"
@@ -392553,12 +393007,8 @@
    "7f0571cdd6f3ad67391435f294ede7efaae7f47e",
    "testharness"
   ],
-  "css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt": [
-   "5e2a1630e2e73625f7230a5d319f7b10ce79ad85",
-   "support"
-  ],
   "css/css-shapes/shape-outside/values/shape-outside-computed-shape-000.html": [
-   "f79b21a723e6e253df0176e53a4567d8ec9567ab",
+   "def2d75fd93e472d2a40e4fd3f3418c6b96bafd5",
    "testharness"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-computed-shape-001.html": [
@@ -393997,6 +394447,10 @@
    "188257af443a235e8ad5d5804058cbf0bcc07a94",
    "testharness"
   ],
+  "css/css-tables/box-shadow-001.html": [
+   "cc699d999eaf9dd7a50c0111ef319816c5c2cb12",
+   "reftest"
+  ],
   "css/css-tables/calc-percent-plus-0px-auto.html": [
    "ec6e9ccfdd702963a8c1b35c8c6c64576189de77",
    "reftest"
@@ -401373,6 +401827,10 @@
    "44f6f83f2d110ac24d99de9eccaa6321903d931f",
    "reftest"
   ],
+  "css/css-text/white-space/eol-spaces-bidi-001.html": [
+   "976ce7c639f10d2137da1492f50ea74c769c1091",
+   "reftest"
+  ],
   "css/css-text/white-space/line-edge-white-space-collapse-001.html": [
    "4e144e6c330191435f183936c2ab2437ec4f7609",
    "reftest"
@@ -401565,6 +402023,10 @@
    "9d5fcb27147a8c53e410d08511cb5035b612f80c",
    "support"
   ],
+  "css/css-text/white-space/reference/eol-spaces-bidi-001-ref.html": [
+   "37da6649f9b776cb046a2bacdeb24968d27a2af8",
+   "support"
+  ],
   "css/css-text/white-space/reference/line-edge-white-space-collapse-001-ref.html": [
    "02fa594255f00396f66e86837409cd579282f7de",
    "support"
@@ -436014,7 +436476,7 @@
    "testharness"
   ],
   "event-timing/crossiframe.html": [
-   "bb19c82a2a1d64e5edcdc965812488aab1a73652",
+   "95e16afa0793e33d0839a6bad4a39a57a83ffec1",
    "testharness"
   ],
   "event-timing/idlharness.any-expected.txt": [
@@ -454801,6 +455263,18 @@
    "4ac26092eeea00cc84ccd04154c255280bc76cbb",
    "testharness"
   ],
+  "html/semantics/forms/form-submission-0/form-double-submit-2.html": [
+   "f0c9471a704d4c0c0742d7ed8e8f13a789514d69",
+   "testharness"
+  ],
+  "html/semantics/forms/form-submission-0/form-double-submit-3.html": [
+   "1bad23260d054b8f60e255de4d1a074803db4b2f",
+   "testharness"
+  ],
+  "html/semantics/forms/form-submission-0/form-double-submit.html": [
+   "1102e304174eeec18b65b54deec74a328d998be0",
+   "testharness"
+  ],
   "html/semantics/forms/form-submission-0/form-echo.py": [
    "a7f0dc87efd2d3bf8798919649873cb81fc25ade",
    "support"
@@ -460353,12 +460827,8 @@
    "b2f05cf056d54a0602a55a8dda7c67cb94883055",
    "testharness"
   ],
-  "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window-expected.txt": [
-   "b1c010efde04fa3f3dbf9847a80f3967dd061ec9",
-   "support"
-  ],
   "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js": [
-   "1d94de8a7c9f28dfab32111deb664d9921437e46",
+   "ba7278ef18adb42b3f527a9c27ff3a51bdb4dbf7",
    "testharness"
   ],
   "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/active.window.js": [
@@ -460441,6 +460911,10 @@
    "43506a22a46da53885a2b5a0888095bc52b460ca",
    "testharness"
   ],
+  "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html": [
+   "a3bdd86ee6ecfda5e09522df5e4c71373a362f5e",
+   "testharness"
+  ],
   "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/mutation-events.window.js": [
    "4efbb863c6372a3ee04d11f38d7ee56a44a2ac7d",
    "testharness"
@@ -460990,7 +461464,7 @@
    "testharness"
   ],
   "html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html": [
-   "627a2f3e5a7baca47cf40d8398e77a48d953b626",
+   "d61618a53ef8afd3cdd2527a0f7f7b094caf570b",
    "testharness"
   ],
   "html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-constructor.html": [
@@ -461026,7 +461500,7 @@
    "testharness"
   ],
   "html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py": [
-   "3e897bccdce1a3d1c340d03836c23bfabf59e90a",
+   "be74a36034eda86e1c6d77b84aef3fd631a8a9ef",
    "support"
   ],
   "html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-rejection-events.js": [
@@ -461517,18 +461991,6 @@
    "2bb82c9834790bfeb1a65cf2ef202dc63d2ebac4",
    "support"
   ],
-  "import-maps/@std/__dir__.headers": [
-   "e7ec0d6699d07e5b13d0cb6f24c3639258fccdaa",
-   "support"
-  ],
-  "import-maps/@std/blank": [
-   "38e1891bf7d5db9ad7063dc1f22bb17f8b1d4446",
-   "support"
-  ],
-  "import-maps/@std/none": [
-   "69e165cce5e223684c9d0f0f7848bc6fc02b14cd",
-   "support"
-  ],
   "import-maps/README.md": [
    "3803e350c844b4af77b871831817ac0d90aca97f",
    "support"
@@ -461545,58 +462007,202 @@
    "ceef4100aad01f5cd5959086aacf83665f60c805",
    "testharness"
   ],
-  "import-maps/bare.sub.tentative.html": [
-   "cf99589f9bd56730423880d6fd4f93df293e6ddf",
-   "testharness"
-  ],
-  "import-maps/bare/__dir__.headers": [
+  "import-maps/builtin-support.tentative/@std/__dir__.headers": [
    "e7ec0d6699d07e5b13d0cb6f24c3639258fccdaa",
    "support"
   ],
-  "import-maps/bare/bare": [
-   "1011e866b81dca7bf423fcb48266463c77591465",
+  "import-maps/builtin-support.tentative/@std/blank": [
+   "38e1891bf7d5db9ad7063dc1f22bb17f8b1d4446",
    "support"
   ],
-  "import-maps/bare/blank": [
+  "import-maps/builtin-support.tentative/@std/none": [
+   "69e165cce5e223684c9d0f0f7848bc6fc02b14cd",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/bare.sub.tentative.html": [
+   "e20424aed8a728b31774b71ae98ab7d882df2987",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/bare/__dir__.headers": [
+   "e7ec0d6699d07e5b13d0cb6f24c3639258fccdaa",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/bare/blank": [
    "841d433acf72f828c34faca23f26e9afd10fbab7",
    "support"
   ],
-  "import-maps/bare/cross-origin-bare": [
-   "64851cc8e2455a4cd150b10edf051425bb6dc5bf",
-   "support"
-  ],
-  "import-maps/bare/none": [
+  "import-maps/builtin-support.tentative/bare/none": [
    "2aec0d72826f203074dea5a5268b9f937bdb7183",
    "support"
   ],
-  "import-maps/bare/std-blank": [
+  "import-maps/builtin-support.tentative/bare/std-blank": [
    "5ded98fd382a52a11d52af57cc7e79f56b083843",
    "support"
   ],
-  "import-maps/bare/std-none": [
+  "import-maps/builtin-support.tentative/bare/std-none": [
    "1437d9ff1e0c0c60672a1f656a2ab91aa01601db",
    "support"
   ],
-  "import-maps/bare/to-bare": [
+  "import-maps/builtin-support.tentative/builtin-empty.tentative.html": [
+   "9ede75a4c42067a989ee6abaf4728bdaa2d56694",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/builtin-import-scheme.tentative.html": [
+   "d9977ae3535c48ebb33e6d3e97bd9d6bab409bc4",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/builtin.tentative.html": [
+   "497bb5c606b7648bbdf9a3cd8990b462d9ab7787",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/data.sub.tentative.html": [
+   "0377a788491cea37ee42ab4a00fba6a5d0df7d73",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/fallback-disallowed.sub.tentative.html": [
+   "7efe90eb751d981012c7d382842001ba176a3b24",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/fallback.sub.tentative.html": [
+   "45cf6dc1ced1dc34ef7df4ea477a4a85dbee45ee",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/http.sub.tentative.html": [
+   "7689ae13bb5373201890794e7a51e72167e3317d",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/imported/parsing-addresses.tentative-expected.txt": [
+   "35155a78eb534bc8f9cf6517c683309ff305e850",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/parsing-addresses.tentative.html": [
+   "0cc92ce3e5e4be7c44abaa41c5d26046feabc049",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/imported/parsing-schema.tentative.html": [
+   "9e3bca2935bcf3f52fdfcb76eeb8339674f0db47",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/imported/parsing-scope-keys.tentative-expected.txt": [
+   "501c084140fe9725349b0b68f368e35d2c607f7e",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/parsing-scope-keys.tentative.html": [
+   "be23c645d0bf071818f96c01b330a5efcab69594",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/imported/parsing-specifier-keys.tentative-expected.txt": [
+   "7f14731a4f3f7e291a515f05ccc5a418fcc283c7",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/parsing-specifier-keys.tentative.html": [
+   "7bc2d4799f1369750a8675f041c32f36f16c1e8f",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/imported/resolving-builtins.tentative-expected.txt": [
+   "a92b0600636bd8bb1bbb3586a122517f1bdc8d4f",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/resolving-builtins.tentative.html": [
+   "065cfa30964da0ec2958b4e95634804b8994056b",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/imported/resolving-not-yet-implemented.tentative.html": [
+   "3bdd591fd0cba9f83c1bfb6a7e3274532efd2961",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/imported/resolving-scopes.tentative.html": [
+   "33b49352ccdc3658022559907db7ac5a098ea698",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/imported/resolving.tentative-expected.txt": [
+   "39ebb09e8e672104425c908275bde416803a0c65",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/resolving.tentative.html": [
+   "1d24eb2031e4097551d21df57f0c595957c695ee",
+   "testharness"
+  ],
+  "import-maps/builtin-support.tentative/imported/resources/helpers/parsing.js": [
+   "daad6d26d220bb0241f8a413816bd100f3af580d",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/resources/parsing-addresses.js": [
+   "0f5fc73506b1222dd7b3ac422d8c6232ac202bd7",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/resources/parsing-schema.js": [
+   "695034533c7faa248a29436cfbe805f86506dc48",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/resources/parsing-scope-keys.js": [
+   "4993f3a9a8bac441b883be55b58d40423114beab",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/resources/parsing-specifier-keys.js": [
+   "9eb423a19eb1fb417526946c1701c7c9dde27c9c",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/resources/resolving-builtins.js": [
+   "a9383df843d44794351d5c6ffa5580d4d1254b9c",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/resources/resolving-not-yet-implemented.js": [
+   "93d782fdad83d58160061a4caa40659292a50866",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/resources/resolving-scopes.js": [
+   "ca19a66840601efe66fa2db24cffdcc0727681c5",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/imported/resources/resolving.js": [
+   "29ee31ccbc936787894a43de1f6625608d14a192",
+   "support"
+  ],
+  "import-maps/builtin-support.tentative/static-import.js": [
+   "1686fc123a798bddbf626f4d112516317739da8f",
+   "support"
+  ],
+  "import-maps/core/bare.sub.tentative.html": [
+   "7fb769e09a2af432517522f91a2a101efc484ad8",
+   "testharness"
+  ],
+  "import-maps/core/bare/__dir__.headers": [
+   "e7ec0d6699d07e5b13d0cb6f24c3639258fccdaa",
+   "support"
+  ],
+  "import-maps/core/bare/bare": [
+   "1011e866b81dca7bf423fcb48266463c77591465",
+   "support"
+  ],
+  "import-maps/core/bare/cross-origin-bare": [
+   "64851cc8e2455a4cd150b10edf051425bb6dc5bf",
+   "support"
+  ],
+  "import-maps/core/bare/to-bare": [
    "bdb3791bc9471533434067b826dc59fef07e1c0c",
    "support"
   ],
-  "import-maps/bare/to-data": [
+  "import-maps/core/bare/to-data": [
    "6f25c5af12412dc6dbb374d4bee0fdd1472f475e",
    "support"
   ],
-  "import-maps/builtin-empty.tentative.html": [
-   "aba28d18e043d53271888a382a7f7b4ca89a054a",
+  "import-maps/core/data.sub.tentative.html": [
+   "25c18c45b7dc3e7311247fc945eb08a75185085f",
    "testharness"
   ],
-  "import-maps/builtin-import-scheme.tentative.html": [
-   "6a27ef5aee2371b2114bc75b2b194c22a5fa85c2",
+  "import-maps/core/http.sub.tentative.html": [
+   "a33a0945e316711a4338045f53ab71d91121c8f6",
    "testharness"
   ],
-  "import-maps/builtin.tentative.html": [
-   "e85289e4d884d49bb4520154cecdd2826a07b333",
+  "import-maps/core/module-map-key.tentative.html": [
+   "6f2f18a02e12a92530b7a3ae6bbbd31eecff3816",
    "testharness"
   ],
+  "import-maps/core/static-import.js": [
+   "1686fc123a798bddbf626f4d112516317739da8f",
+   "support"
+  ],
   "import-maps/csp/applied-to-target-dynamic.sub.tentative.html": [
    "cef80bfcda72956fe7acc59788d984370f25a62f",
    "testharness"
@@ -461625,124 +462231,64 @@
    "101c33cf84e9fb5e68cf8d5910f4ffbe7949e9d7",
    "testharness"
   ],
-  "import-maps/data.sub.tentative.html": [
-   "634948942eea674ec923a4cb718cb92d5aab9021",
-   "testharness"
-  ],
-  "import-maps/fallback-disallowed.sub.tentative.html": [
-   "280d02d847314f97bb88e02618aa46256e055f04",
-   "testharness"
-  ],
-  "import-maps/fallback.sub.tentative.html": [
-   "3f4f2887da8208e8f65df41735e5b95e6bf59d4f",
-   "testharness"
-  ],
-  "import-maps/http.sub.tentative.html": [
-   "bd24f3535507334a69bb98b711106466c8a340a3",
-   "testharness"
-  ],
-  "import-maps/imported/parsing-addresses.tentative-expected.txt": [
-   "7d869b436fbf4187e87cdfa3d974c2da13cbbe4e",
-   "support"
-  ],
   "import-maps/imported/parsing-addresses.tentative.html": [
    "ddb3b724bb64e2edd60a620dc6ae1cc98f32a92a",
    "testharness"
   ],
-  "import-maps/imported/parsing-schema.tentative-expected.txt": [
-   "d2f63ce9f9bf128a5692a3f733e43d3b2874812e",
-   "support"
-  ],
   "import-maps/imported/parsing-schema.tentative.html": [
    "6b7c0e5b0eb40eee9c8a415af5b1af87a4af3492",
    "testharness"
   ],
-  "import-maps/imported/parsing-scope-keys.tentative-expected.txt": [
-   "22d627b0ea5ad2725452103b03f44791b79965aa",
-   "support"
-  ],
   "import-maps/imported/parsing-scope-keys.tentative.html": [
    "601ac37720d700d3f68e3b2f6aeae3b06c112132",
    "testharness"
   ],
-  "import-maps/imported/parsing-specifier-keys.tentative-expected.txt": [
-   "2c08065917f153cdc7f2db72add9b8acb96b4f32",
-   "support"
-  ],
   "import-maps/imported/parsing-specifier-keys.tentative.html": [
    "dd547f01d1dfb2379f6afa893385fadc8f1217d1",
    "testharness"
   ],
-  "import-maps/imported/resolving-builtins.tentative-expected.txt": [
-   "a92b0600636bd8bb1bbb3586a122517f1bdc8d4f",
-   "support"
-  ],
-  "import-maps/imported/resolving-builtins.tentative.html": [
-   "c1395c175c77455361e5ea51819ece56dd24737a",
-   "testharness"
-  ],
-  "import-maps/imported/resolving-not-yet-implemented.tentative.html": [
-   "7db5f29f892976720a145499d6e40ccb8959b006",
-   "testharness"
-  ],
   "import-maps/imported/resolving-scopes.tentative.html": [
    "4985249f4e2951965ad78321208ee08eca8617fa",
    "testharness"
   ],
-  "import-maps/imported/resolving.tentative-expected.txt": [
-   "39ebb09e8e672104425c908275bde416803a0c65",
-   "support"
-  ],
   "import-maps/imported/resolving.tentative.html": [
    "339026259b0f0b8286bc68ddf6976dac0009418b",
    "testharness"
   ],
   "import-maps/imported/resources/helpers/parsing.js": [
-   "5c22f6de710ea32753707273be5d63a285633860",
+   "daad6d26d220bb0241f8a413816bd100f3af580d",
    "support"
   ],
   "import-maps/imported/resources/parsing-addresses.js": [
-   "0f5fc73506b1222dd7b3ac422d8c6232ac202bd7",
+   "92d7714ade9b965325372d58775bd990d3a1ce85",
    "support"
   ],
   "import-maps/imported/resources/parsing-schema.js": [
-   "695034533c7faa248a29436cfbe805f86506dc48",
+   "f60422ae62bce21079cee8d6880cba0dc0dddaa5",
    "support"
   ],
   "import-maps/imported/resources/parsing-scope-keys.js": [
-   "cd1d9b34890971dbb21f7bde5d34ed9f2cc91f20",
+   "4993f3a9a8bac441b883be55b58d40423114beab",
    "support"
   ],
   "import-maps/imported/resources/parsing-specifier-keys.js": [
-   "9eb423a19eb1fb417526946c1701c7c9dde27c9c",
-   "support"
-  ],
-  "import-maps/imported/resources/resolving-builtins.js": [
-   "a9383df843d44794351d5c6ffa5580d4d1254b9c",
-   "support"
-  ],
-  "import-maps/imported/resources/resolving-not-yet-implemented.js": [
-   "93d782fdad83d58160061a4caa40659292a50866",
+   "7ac24bf867b06a6393fbd1ef8e9478d28b2d714d",
    "support"
   ],
   "import-maps/imported/resources/resolving-scopes.js": [
-   "ca19a66840601efe66fa2db24cffdcc0727681c5",
+   "d133b50bd2b8d80ae6b08531e9ff8607e8ed16f6",
    "support"
   ],
   "import-maps/imported/resources/resolving.js": [
-   "29ee31ccbc936787894a43de1f6625608d14a192",
+   "ef8a4f87d25e7fc6f11c3ee7ed6ef64497058d97",
    "support"
   ],
-  "import-maps/module-map-key.tentative.html": [
-   "13bd122c673144f001048eacbaf4fc7bb78c9b58",
-   "testharness"
-  ],
   "import-maps/resources/empty.js": [
    "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
    "support"
   ],
   "import-maps/resources/jest-test-helper.js": [
-   "8d9236a84d81f838a50acefffca105b44eb5025f",
+   "e6f12cd9e55ce5ef5c47d2f615c43b37d0fb1868",
    "support"
   ],
   "import-maps/resources/log.js": [
@@ -461754,11 +462300,7 @@
    "support"
   ],
   "import-maps/resources/test-helper.js": [
-   "2447bfb94353e1ec193857cbcda79f46556216a1",
-   "support"
-  ],
-  "import-maps/static-import.js": [
-   "1686fc123a798bddbf626f4d112516317739da8f",
+   "f21ad935baa5e1310e15232822902c86481bebc4",
    "support"
   ],
   "inert/frame/button.html": [
@@ -462382,11 +462924,11 @@
    "support"
   ],
   "interfaces/META.yml": [
-   "6895a5fc5674a099909a481a46726c990804fc61",
+   "c1dd8dddf9eec3ab3fb58df01c549c251f3a3fdf",
    "support"
   ],
   "interfaces/README.md": [
-   "f70ffd2e11e8ead7ccefed830331a1a5017a43e4",
+   "e0cdf0bc8fb16e32e3ea5465247756957ec1b73e",
    "support"
   ],
   "interfaces/SRI.idl": [
@@ -462517,6 +463059,10 @@
    "f7cf3d31a114a653734990ef081ac109478dfcd8",
    "support"
   ],
+  "interfaces/css-shadow-parts.idl": [
+   "66aaeba73a7a62280171ea1765915991b9da1ba3",
+   "support"
+  ],
   "interfaces/css-transitions.idl": [
    "15ce4b30f211012b5fd9e6a713d92c0ea1045a54",
    "support"
@@ -462870,7 +463416,7 @@
    "support"
   ],
   "interfaces/web-nfc.idl": [
-   "4e0dc0725163e42455fb3418ed9757ddc1d8f632",
+   "99e41f6adfa78629d33d9b3fd5b51b433d612491",
    "support"
   ],
   "interfaces/web-share.idl": [
@@ -462981,6 +463527,10 @@
    "cae35095e8ec25b4fb6aa2ec4a2b55076fb7f269",
    "testharness"
   ],
+  "intersection-observer/document-scrolling-element-root.html": [
+   "9996299312c996f3705af12e35c6eab088905047",
+   "testharness"
+  ],
   "intersection-observer/edge-inclusive-intersection.html": [
    "b73c407a274bde4f4361c6b03bb1d214d86095cf",
    "testharness"
@@ -463574,7 +464124,7 @@
    "testharness"
   ],
   "lint.whitelist": [
-   "d9ea5c17c7cf66a47199faba54d4793e8236f6cb",
+   "afdc20b67b544ddc7a8ddee518e76f748e147def",
    "support"
   ],
   "loading/lazyload/common.js": [
@@ -477518,7 +478068,7 @@
    "testharness"
   ],
   "pointerevents/pointerevent_setpointercapture_inactive_button_mouse.html": [
-   "fa9a5fb3c5b8ae7df867159aab8fc3ad7a6c77e2",
+   "b9172dddb4c576eedeeec696af9576cb9a47e6a0",
    "testharness"
   ],
   "pointerevents/pointerevent_setpointercapture_invalid_pointerid.html": [
@@ -477765,6 +478315,34 @@
    "d50520cd391a17bf175e9eb7a37ce7adc5be4c91",
    "support"
   ],
+  "portals/history/history-manipulation-inside-portal-with-subframes.html": [
+   "8f69182f6467cbf504a5c5cb28bf9cc10ea0b1ba",
+   "testharness"
+  ],
+  "portals/history/history-manipulation-inside-portal.html": [
+   "0fe88c4ea113d14f9800f69f49358ca4ff453ee3",
+   "testharness"
+  ],
+  "portals/history/resources/inner-iframe.html": [
+   "5c6daa22a55b5c453355eb9fe3abcd549a8f36ac",
+   "support"
+  ],
+  "portals/history/resources/portal-harness.js": [
+   "9761ac9268bce245cdf5335a613127874c0fd976",
+   "support"
+  ],
+  "portals/history/resources/portal-manipulate-history-with-subframes.sub.html": [
+   "4b3e8cf2d923327f6a39ee46676bf4f2163350a3",
+   "support"
+  ],
+  "portals/history/resources/portal-manipulate-history.html": [
+   "591fd2193c1ee7fc9080d0a775afa8870ab8f079",
+   "support"
+  ],
+  "portals/history/resources/run-test-in-portal.js": [
+   "c982a1fac8ea4f96b0c7697ec5ec8c45fae44933",
+   "support"
+  ],
   "portals/htmlportalelement-event-handler-content-attributes.html": [
    "8fc26386cfc9d30e89062440b9347c62b60aa592",
    "testharness"
@@ -496017,6 +496595,30 @@
    "20456b057e1e724cdac9bc656f3b3d6c7ac2f658",
    "testharness"
   ],
+  "shadow-dom/focus/click-focus-delegatesFocus-click-method.html": [
+   "92212cd85b86615db21192faa731799218a08d67",
+   "testharness"
+  ],
+  "shadow-dom/focus/click-focus-delegatesFocus-tabindex-varies-expected.txt": [
+   "b80d771c595de00e0ad7bc51bdafd71414ffb17e",
+   "support"
+  ],
+  "shadow-dom/focus/click-focus-delegatesFocus-tabindex-varies.html": [
+   "4051db128a915000bff162fefe4bed55eb338061",
+   "testharness"
+  ],
+  "shadow-dom/focus/click-focus-delegatesFocus-tabindex-zero.html": [
+   "5f7914f2a4e31e107caa817de1b29720c9a21862",
+   "testharness"
+  ],
+  "shadow-dom/focus/focus-method-delegatesFocus-expected.txt": [
+   "ae3eaa3ce88df34d132ca6b727e6af5cf0936e8e",
+   "support"
+  ],
+  "shadow-dom/focus/focus-method-delegatesFocus.html": [
+   "462542e3f7be18bc609b3e6d2c9d9e6ecde30a9f",
+   "testharness"
+  ],
   "shadow-dom/focus/focus-selector-delegatesFocus-expected.txt": [
    "362512535d5a56b7a54725207a512fc5ef10376b",
    "support"
@@ -496025,6 +496627,10 @@
    "3702f8e2ba63228cba5c483a76f2b77372e68c4d",
    "testharness"
   ],
+  "shadow-dom/focus/focus-tabindex-order-shadow-negative-delegatesFocus.html": [
+   "356b0bb329e27bf2706cbfeda66624adc277d43d",
+   "testharness"
+  ],
   "shadow-dom/focus/focus-tabindex-order-shadow-negative.html": [
    "ab25ea829bd10952ad6e96898fc95a1a1ae25d8f",
    "testharness"
@@ -496033,10 +496639,18 @@
    "3c9e70867c5883e96f6288b85a9bac60b9526f4e",
    "testharness"
   ],
+  "shadow-dom/focus/focus-tabindex-order-shadow-varying-delegatesFocus.html": [
+   "67899cff4a9d2e2acac622147c395720fb110f72",
+   "testharness"
+  ],
   "shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex.html": [
    "875e5b6814a95754f5c01d18c125c39c363ad3c6",
    "testharness"
   ],
+  "shadow-dom/focus/focus-tabindex-order-shadow-zero-delegatesFocus.html": [
+   "5e6ab3a90f8f12274c58620220e00e6bec09fc44",
+   "testharness"
+  ],
   "shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative.html": [
    "b491c7d237c20604039f2b6482d484635c9f50da",
    "testharness"
@@ -497241,7 +497855,11 @@
    "5872196a9e9c8d41a69c6f1b5e85169705f89096",
    "support"
   ],
-  "storage-access-api/idl.window.js": [
+  "storage-access-api/idlharness.window-expected.txt": [
+   "5872196a9e9c8d41a69c6f1b5e85169705f89096",
+   "support"
+  ],
+  "storage-access-api/idlharness.window.js": [
    "a0b4c37d916a8ef66b2c09ad6afee5ec9f21e12f",
    "testharness"
   ],
@@ -500962,7 +501580,7 @@
    "support"
   ],
   "tools/ci/run_tc.py": [
-   "956d1c353e54b2179316ad56ff85823bc5009ea6",
+   "60f175808715e2d8adf173c3145dc06b7bea64c2",
    "support"
   ],
   "tools/ci/taskcluster-run.py": [
@@ -505258,7 +505876,7 @@
    "support"
   ],
   "tools/wpt/commands.json": [
-   "178eda9c2656c393132792c2206489ee95a6a362",
+   "60fe1621af0decd0faa434f26dcba995154173e7",
    "support"
   ],
   "tools/wpt/create.py": [
@@ -505285,6 +505903,10 @@
    "566083cb6be89a03e48cc5c1a0a88fc5ce53a853",
    "support"
   ],
+  "tools/wpt/revlist.py": [
+   "f750311914ac13408cc082e58c36c2471e8f48be",
+   "support"
+  ],
   "tools/wpt/run.py": [
    "de20e2916ad30244ef3635d1fce562cebbd26052",
    "support"
@@ -505610,7 +506232,7 @@
    "support"
   ],
   "tools/wptrunner/wptrunner/formatters/wptreport.py": [
-   "d73911f6a94e922dc8dc6f1d7f746abbf1a04362",
+   "6d4401ae5aacf9db028f94e88bbfcc2be33726c3",
    "support"
   ],
   "tools/wptrunner/wptrunner/formatters/wptscreenshot.py": [
@@ -505630,7 +506252,7 @@
    "support"
   ],
   "tools/wptrunner/wptrunner/metadata.py": [
-   "15bbf94c33cea80ab4d0fdd958618f264fa57ab1",
+   "c328dcefa7253ddea952549c702723e98b4cadc4",
    "support"
   ],
   "tools/wptrunner/wptrunner/products.py": [
@@ -505730,7 +506352,7 @@
    "support"
   ],
   "tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py": [
-   "43225a1e72861ee4ee3b8b6e1fb0594a17a0891d",
+   "aacd7857072f420a209aeeff559ec2ae294170ba",
    "support"
   ],
   "tools/wptrunner/wptrunner/wptmanifest/backends/static.py": [
@@ -511457,8 +512079,12 @@
    "72d8c8143cd934ad3ff0f8e32968851a1769b0c3",
    "manual"
   ],
+  "web-nfc/NFCWriter_push.https-expected.txt": [
+   "72884b7e143029113a014f4653c8f857bc8d0c77",
+   "support"
+  ],
   "web-nfc/NFCWriter_push.https.html": [
-   "dda160f159a23223c0910d2393ecbc8c8e38aed1",
+   "6b4034fccc2987c0366ccdfd58dd394a5111709b",
    "testharness"
   ],
   "web-nfc/OWNERS": [
@@ -513638,7 +514264,7 @@
    "support"
   ],
   "webgpu/cts.html": [
-   "661060fcd7b32f7cecbbbc87499c7b70d1f39694",
+   "b60faec9623bb0adf7c1b9ad0e5d488ce14b5882",
    "testharness"
   ],
   "webgpu/framework/allowed_characters.js": [
@@ -513650,7 +514276,7 @@
    "support"
   ],
   "webgpu/framework/fixture.js": [
-   "d77ede708f0377feec5101d7272eaf27d07c5598",
+   "6e13385eef42d8af73b97a44a0945802483538c7",
    "support"
   ],
   "webgpu/framework/gpu/implementation.js": [
@@ -513674,7 +514300,7 @@
    "support"
   ],
   "webgpu/framework/logger.js": [
-   "8aa332c571a19f9db7a5486b9a8d28269101051e",
+   "d97be59091a1d9b5eadd93a105ba1023e730f8a1",
    "support"
   ],
   "webgpu/framework/params/combine.js": [
@@ -513698,11 +514324,11 @@
    "support"
   ],
   "webgpu/framework/test_filter/filter_by_group.js": [
-   "3036e21bd63c40b7d34f0f46dea432d505b131b1",
+   "e7cc5f78ba52a49eb021bf7cc9247448f2620f53",
    "support"
   ],
   "webgpu/framework/test_filter/filter_one_file.js": [
-   "6a998db0de9c96f796bd05f90e73e60e0d141aee",
+   "7616bee6afeaec3fb6836f21fd318a4185d78c5a",
    "support"
   ],
   "webgpu/framework/test_filter/index.js": [
@@ -513718,27 +514344,35 @@
    "support"
   ],
   "webgpu/framework/test_group.js": [
-   "c9cafb37b670dd1cf79a46fbcb6acd595b3b53b7",
+   "09697765b8c7c715bac92536418cceb1ce632ef9",
    "support"
   ],
   "webgpu/framework/url_query.js": [
-   "b265139043918b98e9937a6c216e3748a7c5cb87",
+   "f7c7cbc17fe5a908eb5b496a4a267274ebbc0030",
+   "support"
+  ],
+  "webgpu/framework/util/async_mutex.js": [
+   "cb900605bcfe77bae785c207419787e317bf1d73",
    "support"
   ],
   "webgpu/framework/util/index.js": [
-   "8d0fe0e5b48c6149bbf5c1f0aa8af2876fad00a3",
+   "32aea98dea7040379b5f2ccd74fdd57dd264f632",
    "support"
   ],
   "webgpu/framework/util/stack.js": [
    "ae99ac904a132d947f304ca0f03f50b25fd197ae",
    "support"
   ],
+  "webgpu/framework/util/timeout.js": [
+   "e565a518efc045cfc90e7dd7b23dc65604c6b805",
+   "support"
+  ],
   "webgpu/framework/version.js": [
-   "9be025609f36ae16b7395de98c355f6872a9d660",
+   "0bfbfad4f3995d27200b1601ef8411f5b16c12ae",
    "support"
   ],
   "webgpu/runtime/wpt.js": [
-   "d11499f1e8cd45553e2ae6832413e108790fe505",
+   "ec4ab0123924659e9cb23ba916c51d7ea6aa75cc",
    "support"
   ],
   "webgpu/suites/cts/buffers/create_mapped.spec.js": [
@@ -513766,39 +514400,119 @@
    "support"
   ],
   "webgpu/suites/cts/command_buffer/basic.spec.js": [
-   "137e428bd9db9311987662d6e5024c31f04742ca",
+   "c4333c794d2d75ca0b09991da9b40cd864b2bb91",
    "support"
   ],
   "webgpu/suites/cts/command_buffer/compute/basic.spec.js": [
-   "56754d5f39e21f483f9f21e931e35a7dfa1e1259",
+   "a001cbc0d82dc1388e875254d1eddd505ffc01f4",
    "support"
   ],
   "webgpu/suites/cts/command_buffer/copies.spec.js": [
-   "9bf78db8454bf848a6a1236db6503f893d33a225",
+   "0055a58d115303f0dfdd79422babfede04efd3c3",
    "support"
   ],
   "webgpu/suites/cts/command_buffer/render/basic.spec.js": [
-   "83bb026d963d105e3e73ae118664d9951d246710",
+   "8f4d8c879da7b16f3fc162c697025c2fab4c6ff7",
    "support"
   ],
   "webgpu/suites/cts/command_buffer/render/rendering.spec.js": [
-   "c8a7d7f5738b6efd199e96fadb937a64bd603d99",
+   "c065ed23be4f21d3368742e0760f07cea2c6f6ff",
+   "support"
+  ],
+  "webgpu/suites/cts/command_buffer/render/storeop.spec.js": [
+   "da58ad2d8a571685134180199369244339d4a4a1",
    "support"
   ],
   "webgpu/suites/cts/examples.spec.js": [
-   "ad86e73b191e74a8a23a81ccccf54cc2c95f3fa9",
+   "8a992a6239d78c1f45b2323d77690b8c5b4fab1f",
    "support"
   ],
   "webgpu/suites/cts/fences.spec.js": [
-   "d9f8ce57678e9907f20a5819b2a43686e27ba53c",
+   "ccf513633daf307119ffcfc0eb3e81d27da3f9d3",
    "support"
   ],
   "webgpu/suites/cts/gpu_test.js": [
-   "a67ce4ab0ba24f13c8a66dae551390c3d71d06ba",
+   "e437ee09449bae87f6b410c1643b8c23e628d703",
    "support"
   ],
   "webgpu/suites/cts/index.js": [
-   "cb2a592697f9b1041e5a2f71cf59939b5a04ed4a",
+   "b38ea11df28f63c6374d9b58df5c276d636b7b8d",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/createBindGroup.spec.js": [
+   "24d29d0e61e653c34ebde4a35bb01717ee23ab9e",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/createBindGroupLayout.spec.js": [
+   "8e3bfc22572c6e8bbeab51c98520f1cfbb3633b2",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/createPipelineLayout.spec.js": [
+   "dc759750a986630dc87ea5136afffb4b3b5bd396",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/createRenderPipeline.spec.js": [
+   "7cbe93dd9c958cc8331316a522846d126be89556",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/createTexture.spec.js": [
+   "c99204b9667fbfa53d743eff285a4090abe70b07",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/createView.spec.js": [
+   "2330c6ecdd91abedde02e88391921586cfd40dec",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/error_scope.spec.js": [
+   "2232db9b54e48426abf18f0d3c91f3c8901437de",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/fences.spec.js": [
+   "4aca3d7773afff6a801a705ca36fe675ee429588",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/queue_submit.spec.js": [
+   "50d912f4d6f2a3d53e072fae9b9930616e7ed1a7",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/render_pass.spec.js": [
+   "f71a4a2b97420ab76ead6cadf29c31e3d440ce1a",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/render_pass_descriptor.spec.js": [
+   "5a171879082762b039c1f21d18f7066b5c193078",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/setBindGroup.spec.js": [
+   "c669ea08d586b4911a74336314c72437aa2abec3",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/setBlendColor.spec.js": [
+   "a04bcd038ee5f0f957772bb52a63563b400fa502",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/setScissorRect.spec.js": [
+   "ee1588d2a885bd2d0fc386737b3e37a5fc91dd7d",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/setStencilReference.spec.js": [
+   "3db37a4908e719212043856b9bdf5e3b31a91065",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/setVertexBuffer.spec.js": [
+   "949dd91932d8d4e6b498f578e11d5c7c3fb019df",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/setViewport.spec.js": [
+   "5610930829e58862955ec7ba26e17d96ca5cb253",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/validation_test.js": [
+   "35ad5d895521f1207ab21be1de5af3a0d672cc4f",
+   "support"
+  ],
+  "webgpu/suites/cts/validation/vertex_input.spec.js": [
+   "432a04a4ceef0e895e0bc881bf6d4f2ec7f91034",
    "support"
   ],
   "webmessaging/Channel_postMessage_Blob.htm": [
@@ -519865,8 +520579,12 @@
    "107cc9b544e3a395c14655d0f4356485f05d7bf5",
    "testharness"
   ],
-  "webxr/events_referenceSpace_reset.https.html": [
-   "3a931168fd328b9fb62f5ecd65958f4365b7aa03",
+  "webxr/events_referenceSpace_reset_immersive.https.html": [
+   "2da3788a39a20db3396d542f7fb0dc5d486c2aa3",
+   "testharness"
+  ],
+  "webxr/events_referenceSpace_reset_inline.https.html": [
+   "b3abcc817bdf3e53c22284a14d03e31e1184e1a3",
    "testharness"
   ],
   "webxr/events_session_select.https.html": [
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/inactive-timeline.https.html b/third_party/blink/web_tests/external/wpt/animation-worklet/inactive-timeline.https.html
index bdd46f9..24dc98c 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/inactive-timeline.https.html
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/inactive-timeline.https.html
@@ -49,16 +49,6 @@
     promise_test(async t => {
       const animation = createScrollLinkedWorkletAnimation(t);
       const scroller = animation.timeline.scrollSource;
-      const target = animation.effect.target;
-
-      // There is no direct way to control when local times of composited
-      // animations are synced to the main thread. This test uses another
-      // composited worklet animation with an always active timeline as an
-      // indicator of when the sync is ready. The sync is done when animation
-      // effect's output has changed as a result of advancing the timeline.
-      const animationRef = createScrollLinkedWorkletAnimation(t);
-      const scrollerRef = animationRef.timeline.scrollSource;
-      const targetRef = animationRef.effect.target;
 
       const maxScroll = scroller.scrollHeight - scroller.clientHeight;
       const timeRange = animation.timeline.timeRange;
@@ -70,7 +60,6 @@
       scroller.scrollTop;
 
       animation.play();
-      animationRef.play();
       assert_equals(animation.currentTime, null,
         'Initial current time must be unresolved in idle state.');
       assert_equals(animation.startTime, null,
@@ -83,17 +72,6 @@
       assert_equals(animation.startTime, null,
         'Initial start time must be unresolved in playing state.');
 
-      scrollerRef.scrollTop = 0.2 * maxScroll;
-
-      // Wait until local times are synced back to the main thread.
-      await waitForAnimationFrameWithCondition(_ => {
-        return animationRef.effect.getComputedTiming().localTime == 200;
-      });
-
-      assert_equals(animation.effect.getComputedTiming().localTime, null,
-        'The underlying effect local time must be undefined while the ' +
-        'timeline is inactive.');
-
       // Make the timeline active.
       scroller.style.display = ""
       scroller.scrollTop;
@@ -103,15 +81,6 @@
       assert_times_equal(animation.startTime, 0,
         'Start time must be initialized.');
 
-      scrollerRef.scrollTop = 0.4 * maxScroll;
-      // Wait until local times are synced back to the main thread.
-      await waitForAnimationFrameWithCondition(_ => {
-        return animationRef.effect.getComputedTiming().localTime == 400;
-      });
-      assert_times_equal(animation.effect.getComputedTiming().localTime, 200,
-        'When the timeline becomes newly active, the underlying effect\'s ' +
-        'timing should be properly updated.');
-
       // Make the timeline inactive again.
       scroller.style.display = "none"
       scroller.scrollTop;
@@ -121,16 +90,6 @@
       assert_equals(animation.startTime, null,
         'Initial start time must be unresolved.');
 
-      scrollerRef.scrollTop = 0.6 * maxScroll;
-      scrollerRef.scrollTop;
-      // Wait until local times are synced back to the main thread.
-      await waitForAnimationFrameWithCondition(_ => {
-        return animationRef.effect.getComputedTiming().localTime == 600;
-      });
-
-      assert_times_equal(animation.effect.getComputedTiming().localTime, 200,
-        'When the timeline becomes newly inactive, the underlying effect\'s ' +
-        'timing should stay unchanged.');
     }, 'When timeline time becomes inactive previous current time must be ' +
        'the current time and start time unresolved');
     done();
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-005.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-005.html
new file mode 100644
index 0000000..e4de5769
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-005.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>CSS Overflow Test: overscroll-behavior doesn't stop overflow from being propagated from the body</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#overflow-propagation">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1586600">
+<link rel="match" href="reference/overflow-body-propagation-ref.html">
+<style>
+  :root {
+    overscroll-behavior-y: contain;
+  }
+  body {
+    overflow: scroll;
+    margin-top: 100px;
+  }
+</style>
+<body>The viewport should have scrollbars, not the body.</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-006.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-006.html
new file mode 100644
index 0000000..4758150
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-006.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>CSS Overflow Test: scroll-snap-type doesn't stop overflow from being propagated from the body</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#overflow-propagation">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1586600">
+<link rel="match" href="reference/overflow-body-propagation-ref.html">
+<style>
+  :root {
+    scroll-snap-type: both mandatory;
+  }
+  body {
+    overflow: scroll;
+    margin-top: 100px;
+  }
+</style>
+<body>The viewport should have scrollbars, not the body.</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/idlharness.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/idlharness.html
new file mode 100644
index 0000000..182943a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/idlharness.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>CSS Shadow Parts IDL tests</title>
+<link rel="help" href="https://drafts.csswg.org/css-shadow-parts/#idl">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="/resources/idlharness.js"></script>
+<script>
+idl_test(
+  ['css-shadow-parts'],
+  ['dom'],
+  idl_array => {
+    self.element = document.createElementNS('ns', 'e');
+    self.htmlElement = document.createElement('html');
+    self.svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+    idl_array.add_objects({
+      Element: ['element', 'htmlElement', 'svgElement'],
+    });
+  }
+);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple-important-important.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple-important-important.html
new file mode 100644
index 0000000..43ec1bba
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple-important-important.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Shadow Parts - Simple Important Important</title>
+    <meta href="mailto:fergal@chromium.org" rel="author" title="Fergal Daly">
+    <link href="http://www.google.com/" rel="author" title="Google">
+    <link href="https://drafts.csswg.org/css-shadow-parts/" rel="help">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="support/shadow-helper.js"></script>
+  </head>
+  <body>
+    <style>#c-e::part(partp) { color: green !important; }</style>
+    <script>installCustomElement("custom-element", "custom-element-template");</script>
+    <template id="custom-element-template">
+      <style>span { color: red !important; }</style>
+      <span id="part" part="partp">This text</span>
+    </template>
+    The following text should be green:
+    <custom-element id="c-e"></custom-element>
+    <script>
+      "use strict";
+      const colorRed = "rgb(255, 0, 0)";
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "part"]);
+        assert_equals(window.getComputedStyle(el).color, colorRed);
+      }, "Part in selected host is styled");
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple-important-inline.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple-important-inline.html
new file mode 100644
index 0000000..61b83dd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple-important-inline.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Shadow Parts - Simple Important Inline</title>
+    <meta href="mailto:fergal@chromium.org" rel="author" title="Fergal Daly">
+    <link href="http://www.google.com/" rel="author" title="Google">
+    <link href="https://drafts.csswg.org/css-shadow-parts/" rel="help">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="support/shadow-helper.js"></script>
+  </head>
+  <body>
+    <style>#c-e::part(partp) { color: red; }</style>
+    <script>installCustomElement("custom-element", "custom-element-template");</script>
+    <template id="custom-element-template">
+      <span id="part" part="partp" style="color: green !important">This text</span>
+    </template>
+    The following text should be red:
+    <custom-element id="c-e"></custom-element>
+    <script>
+      "use strict";
+      const colorGreen = "rgb(0, 128, 0)";
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "part"]);
+        assert_equals(window.getComputedStyle(el).color, colorGreen);
+      }, "Part in selected host is not styled");
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple-important.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple-important.html
new file mode 100644
index 0000000..e3240bd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple-important.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Shadow Parts - Simple Important</title>
+    <meta href="mailto:fergal@chromium.org" rel="author" title="Fergal Daly">
+    <link href="http://www.google.com/" rel="author" title="Google">
+    <link href="https://drafts.csswg.org/css-shadow-parts/" rel="help">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="support/shadow-helper.js"></script>
+  </head>
+  <body>
+    <style>#c-e::part(partp) { color: red; }</style>
+    <script>installCustomElement("custom-element", "custom-element-template");</script>
+    <template id="custom-element-template">
+      <style>span { color: green !important; }</style>
+      <span id="part" part="partp">This text</span>
+    </template>
+    The following text should be green:
+    <custom-element id="c-e"></custom-element>
+    <script>
+      "use strict";
+      const colorGreen = "rgb(0, 128, 0)";
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "part"]);
+        assert_equals(window.getComputedStyle(el).color, colorGreen);
+      }, "Part in selected host is styled");
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt
deleted file mode 100644
index 5e2a163..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shape Outside Basic Shape Computed Font Relative Lengths Uncaught TypeError: ParsingUtils.setupFonts(...) is not a function
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000.html b/third_party/blink/web_tests/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000.html
index f79b21a..def2d75f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000.html
@@ -19,16 +19,16 @@
             // font relative units: em, ex, ch, rem
             var units = ['em', 'ex', 'ch', 'rem'];
             var resolveds = {};
-            ParsingUtils.setupFonts(function () {
-                var div = document.createElement('div');
-                document.body.appendChild(div);
-                units.forEach(function(unit) {
-                    div.style.width = '10' + unit;
-                    var s = getComputedStyle(div);
-                    resolveds[unit] = parseFloat(s.width);
-                });
-                document.body.removeChild(div);
-            })();
+            ParsingUtils.setupFonts();
+
+            var div = document.createElement('div');
+            document.body.appendChild(div);
+            units.forEach(function(unit) {
+                div.style.width = '10' + unit;
+                var s = getComputedStyle(div);
+                resolveds[unit] = parseFloat(s.width);
+            });
+            document.body.removeChild(div);
 
             function fillArray(string, length) {
                 return Array.apply(null, new Array(length)).map(String.prototype.valueOf, string);
@@ -49,6 +49,8 @@
             });
 
             generate_tests(testUnit, tests);
+
+            ParsingUtils.restoreFonts();
         </script>
     </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/box-shadow-001.html b/third_party/blink/web_tests/external/wpt/css/css-tables/box-shadow-001.html
new file mode 100644
index 0000000..cc699d99
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/box-shadow-001.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>Collapsed borders and box-shadow</title>
+<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#in-collapsed-borders-mode">
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#box-shadow">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<link rel="bookmark" href="https://crbug.com/1006241" />
+<meta name="flags" content="" />
+<meta name="assert" content="box-shadow size and location on a cell with collapsed borders are calculated correctly" />
+
+<style>
+table {
+  border-collapse: collapse;
+}
+
+/* Make the green box-shadow start at the inner edge of the border to cover up all the red.
+   Chrome's bug made the box-shadow start further toward the center. */
+td {
+  border: 20px solid green;
+  box-shadow: inset 60px 0px green;
+  /* The properties after the blank line aren't the focus of the test. */
+
+  background: red;
+  line-height: 0px;
+  padding: 0px;
+}
+
+td > span {
+  display: inline-block; /* chrome only hits this bug when the td children are inline */
+
+  height: 60px;
+  width: 60px;
+}
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<table>
+  <tr>
+    <td><span></span></td>
+  </tr>
+</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/eol-spaces-bidi-001.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/eol-spaces-bidi-001.html
new file mode 100644
index 0000000..976ce7c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/eol-spaces-bidi-001.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text level 3 Test: collapsible white space at end-of-line and bidi</title>
+<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
+<link rel="match" href="reference/eol-spaces-bidi-001-ref.html">
+<meta name="assert" content="Collapsible space at the end of the line prior to bidi reordering are removed.">
+<style>
+div {
+  font-family: monospace;
+  border: solid blue;
+  font-size: 1.5em;
+  margin-bottom:1em;
+  display: inline-block;
+}
+.ref {
+  white-space: pre;
+  border-color: orange;
+}
+.w5 { width: 5ch; }
+.w6 { width: 6ch; }
+.w7 { width: 7ch; }
+.w8 { width: 8ch; }
+
+.blue {	background: #aaaaff; }
+.red {	background: #ffaaaa; }
+.green {	background: #aaffaa; }
+.pink {	background: #ffaaff; }
+.yellow {	background: #ffffaa; }
+
+</style>
+<p>Test passes if the content of each blue box (on the left) is laid out identically to the content of the orange box to its right.
+
+<div class=w5><span class=pink> </span>A<span class=blue> </span>B<span class=red> </span>ا<span class=green> </span>ب<span class=yellow> </span></div>
+<div class="ref w5">A<span class=blue> </span>B<span class=red> </span>ا<br>ب</div>
+<br>
+<div class=w6><span class=pink> </span>A<span class=blue> </span>B<span class=red> </span>ا<span class=green> </span>ب<span class=yellow> </span></div>
+<div class="ref w6">A<span class=blue> </span>B<span class=red> </span>ا<br>ب</div>
+<br>
+<div class=w7><span class=pink> </span>A<span class=blue> </span>B<span class=red> </span>ا<span class=green> </span>ب<span class=yellow> </span></div>
+<div class="ref w7">A<span class=blue> </span>B<span class=red> </span>ا<span class=green> </span>ب</div>
+<br>
+<div class=w8><span class=pink> </span>A<span class=blue> </span>B<span class=red> </span>ا<span class=green> </span>ب<span class=yellow> </span></div>
+<div class="ref w8">A<span class=blue> </span>B<span class=red> </span>ا<span class=green> </span>ب</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/eol-spaces-bidi-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/eol-spaces-bidi-001-ref.html
new file mode 100644
index 0000000..37da664
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/eol-spaces-bidi-001-ref.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS test reference</title>
+<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
+<style>
+div {
+  font-family: monospace;
+  border: solid blue;
+  font-size: 1.5em;
+  margin-bottom:1em;
+  display: inline-block;
+  white-space: pre;
+}
+.ref {
+  border-color: orange;
+}
+.w5 { width: 5ch; }
+.w6 { width: 6ch; }
+.w7 { width: 7ch; }
+.w8 { width: 8ch; }
+
+.blue {	background: #aaaaff; }
+.red {	background: #ffaaaa; }
+.green {	background: #aaffaa; }
+
+</style>
+<p>Test passes if the content of each blue box (on the left) is laid out identically to the content of the orange box to its right.
+
+<div class="w5">A<span class=blue> </span>B<span class=red> </span>ا<br>ب</div>
+<div class="ref w5">A<span class=blue> </span>B<span class=red> </span>ا<br>ب</div>
+<br>
+<div class="w6">A<span class=blue> </span>B<span class=red> </span>ا<br>ب</div>
+<div class="ref w6">A<span class=blue> </span>B<span class=red> </span>ا<br>ب</div>
+<br>
+<div class="w7">A<span class=blue> </span>B<span class=red> </span>ا<span class=green> </span>ب</div>
+<div class="ref w7">A<span class=blue> </span>B<span class=red> </span>ا<span class=green> </span>ب</div>
+<br>
+<div class="w8">A<span class=blue> </span>B<span class=red> </span>ا<span class=green> </span>ب</div>
+<div class="ref w8">A<span class=blue> </span>B<span class=red> </span>ا<span class=green> </span>ب</div>
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/crossiframe.html b/third_party/blink/web_tests/external/wpt/event-timing/crossiframe.html
index bb19c82..95e16afa 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/crossiframe.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/crossiframe.html
@@ -34,7 +34,7 @@
     assert_greater_than(entry.processingStart, processingStartMin,
         "The entry's processing start should be later than processingStartMin.");
     assert_greater_than(onloadStart, entry.processingStart,
-        "onload should occur later than the entry's procesisng start.");
+        "onload should occur later than the entry's processing start.");
     assert_greater_than(entry.startTime, clickTimeMin,
         "The entry's start time should be later than clickTimeMin.");
     assert_greater_than(onloadStart, entry.startTime,
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-2.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-2.html
new file mode 100644
index 0000000..f0c9471
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-2.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- The onclick submit() should *not* get superseded in this case by the
+     default action submit(), because onclick here calls preventDefault().
+  -->
+
+
+
+
+<label for=frame1 style="display:block">This frame should stay blank</label>
+<iframe name=frame1 id=frame1></iframe>
+<label for=frame2 style="display:block">This frame should navigate (to 404)</label>
+<iframe name=frame2 id=frame2></iframe>
+<form id="form1" target="frame2" action="nonexistent.html">
+  <input type=hidden name=navigated value=1>
+  <input id=submitbutton type=submit>
+</form>
+
+<script>
+let frame1 = document.getElementById('frame1');
+let frame2 = document.getElementById('frame2');
+
+async_test(t => {
+  window.addEventListener('load', () => {
+    frame1.addEventListener('load', t.step_func_done(() => {
+      assert_unreached("Frame1 should not get navigated by this test.");
+    }));
+    frame2.addEventListener('load', t.step_func_done(() => {
+      let params = (new URL(frame2.contentWindow.location)).searchParams;
+      let wasNavigated = !!params.get("navigated");
+      assert_true(wasNavigated);
+    }));
+    form1.addEventListener('click', t.step_func(() => {
+      form1.submit();
+      form1.target='frame1';
+      event.preventDefault();  // Prevent default here
+    }));
+    submitbutton.click();
+  });
+}, 'preventDefault should allow onclick submit() to succeed');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-3.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-3.html
new file mode 100644
index 0000000..1bad232
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-3.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- <button> should have the same double-submit protection that
+     <input type=submit> has.
+  -->
+
+
+
+
+<label for=frame1 style="display:block">This frame should stay blank</label>
+<iframe name=frame1 id=frame1></iframe>
+<label for=frame2 style="display:block">This frame should navigate (to 404)</label>
+<iframe name=frame2 id=frame2></iframe>
+<form id="form1" target="frame1" action="nonexistent.html">
+  <input type=hidden name=navigated value=1>
+  <button id=submitbutton>submit</button>
+</form>
+
+<script>
+let frame1 = document.getElementById('frame1');
+let frame2 = document.getElementById('frame2');
+
+async_test(t => {
+  window.addEventListener('load', () => {
+    frame1.addEventListener('load', t.step_func_done(() => {
+      assert_unreached("Frame1 should not get navigated by this test.");
+    }));
+    frame2.addEventListener('load', t.step_func_done(() => {
+      let params = (new URL(frame2.contentWindow.location)).searchParams;
+      let wasNavigated = !!params.get("navigated");
+      assert_true(wasNavigated)
+    }));
+    form1.addEventListener('click', t.step_func(() => {
+      form1.submit();
+      form1.target='frame2';
+
+    }));
+    submitbutton.click();
+  });
+}, '<button> should have the same double-submit protection as <input type=submit>');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit.html
new file mode 100644
index 0000000..1102e30
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- The onclick submit() should get superseded by the default
+     action submit(), which isn't preventDefaulted by onclick here.
+     This is per the Form Submission Algorithm [1], step 22.3, which
+     says that new planned navigations replace old planned navigations.
+    [1] https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm
+  -->
+
+<label for=frame1 style="display:block">This frame should stay blank</label>
+<iframe name=frame1 id=frame1></iframe>
+<label for=frame2 style="display:block">This frame should navigate (to 404)</label>
+<iframe name=frame2 id=frame2></iframe>
+<form id="form1" target="frame1" action="nonexistent.html">
+  <input type=hidden name=navigated value=1>
+  <input id=submitbutton type=submit>
+</form>
+
+<script>
+let frame1 = document.getElementById('frame1');
+let frame2 = document.getElementById('frame2');
+
+async_test(t => {
+  window.addEventListener('load', () => {
+    frame1.addEventListener('load', t.step_func_done(() => {
+      assert_unreached("Frame1 should not get navigated by this test.");
+    }));
+    frame2.addEventListener('load', t.step_func_done(() => {
+      let params = (new URL(frame2.contentWindow.location)).searchParams;
+      let wasNavigated = !!params.get("navigated");
+      assert_true(wasNavigated)
+    }));
+    form1.addEventListener('click', t.step_func(() => {
+      form1.submit();
+      form1.target='frame2';
+
+    }));
+    submitbutton.click();
+  });
+}, 'default submit action should supersede onclick submit()');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window-expected.txt b/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window-expected.txt
deleted file mode 100644
index b1c010e..0000000
--- a/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL document.open() after parser is aborted assert_true: child document should be empty expected true got false
-FAIL async document.open() after parser is aborted assert_true: child document should be empty expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js b/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js
index 1d94de8a..ba7278e 100644
--- a/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js
+++ b/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js
@@ -1,28 +1,31 @@
-// document.open() bails out early if there is an **active parser** with
-// non-zero script nesting level. window.stop() aborts the current parser and
-// makes it no longer active, and should allow document.open() to work.
-// For more details, see https://bugzilla.mozilla.org/show_bug.cgi?id=1475000.
+// document.open() bails out early if there is an active parser with non-zero
+// script nesting level or if a load was aborted while there was an active
+// parser. window.stop() aborts the current parser, so once it has been called
+// while a parser is active, document.open() will no longer do anything to that
+// document,
 
 window.handlers = {};
 
 async_test(t => {
   const frame = document.body.appendChild(document.createElement("iframe"));
+  t.add_cleanup(() => frame.remove());
   frame.src = "resources/aborted-parser-frame.html";
   window.handlers.afterOpen = t.step_func_done(() => {
     const openCalled = frame.contentDocument.childNodes.length === 0;
-    frame.remove();
-    assert_true(openCalled, "child document should be empty");
+    assert_false(openCalled, "child document should not be empty");
+    assert_equals(frame.contentDocument.querySelector("p").textContent,
+                  "Text", "Should still have our paragraph");
   });
 }, "document.open() after parser is aborted");
 
-// Note: This test should pass even if window.close() is not there, as
-// document.open() is not executed synchronously in an inline script.
 async_test(t => {
   const frame = document.body.appendChild(document.createElement("iframe"));
+  t.add_cleanup(() => frame.remove());
   frame.src = "resources/aborted-parser-async-frame.html";
   window.handlers.afterOpenAsync = t.step_func_done(() => {
     const openCalled = frame.contentDocument.childNodes.length === 0;
-    frame.remove();
-    assert_true(openCalled, "child document should be empty");
+    assert_false(openCalled, "child document should not be empty");
+    assert_equals(frame.contentDocument.querySelector("p").textContent,
+                  "Text", "Should still have our paragraph");
   });
 }, "async document.open() after parser is aborted");
diff --git a/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html b/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html
new file mode 100644
index 0000000..a3bdd86
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+  <script>
+    var t = async_test("Location sets should cancel current navigation and prevent later document.open() from doing anything");
+
+    var finishTest = t.step_func_done(function() {
+        assert_equals(frames[0].document.body.textContent, "PASS",
+                      "Should not have FAIL in our textContent");
+    });
+
+    t.step(function() {
+        var i = document.createElement("iframe");
+        i.srcdoc = `
+          <script>
+            var blob = new Blob(["PASS"], { type: "text/html" });
+            var url = URL.createObjectURL(blob);
+            location.href = url;
+            frameElement.onload = parent.finishTest;
+            document.open();
+            document.write("FAIL");
+            document.close();
+          <\/script>`;
+        document.body.appendChild(i);
+    });
+
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html b/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html
index 627a2f3..d61618a 100644
--- a/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html
+++ b/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html
@@ -10,21 +10,83 @@
 <script>
 'use strict';
 
+setup({
+  allow_uncaught_exception: true
+});
+
 (function() {
   var resolveLoaded;
   var loadedPromise = new Promise(function(resolve) { resolveLoaded = resolve; });
 
-  async_test(function(t) {
-    addEventListener('unhandledrejection', t.unreached_func('unhandledrejection event should never be triggered'));
-    addEventListener('rejectionhandled', t.unreached_func('rejectionhandled event should never be triggered'));
+  promise_test(function(t) {
+    var unreachedUnhandled = t.unreached_func('unhandledrejection event should never be triggered');
+    var unreachedHandled = t.unreached_func('rejectionhandled event should never be triggered');
 
-    loadedPromise.then(t.step_func(function() {
-      t.step_timeout(function() {
-        t.done();
-      }, 1000);
+    addEventListener('unhandledrejection', unreachedUnhandled);
+    addEventListener('rejectionhandled', unreachedHandled);
+    ensureCleanup(t, unreachedUnhandled, unreachedHandled);
+
+    return loadedPromise.then(t.step_func(function() {
+      return new Promise(function(resolve) {
+        t.step_timeout(function() {
+          resolve();
+        }, 1000);
+      });
     }));
   }, 'Promise rejection event should be muted for cross-origin non-CORS script');
 
+  promise_test(function(t) {
+    var unreachedUnhandled = t.unreached_func('unhandledrejection event should never be triggered');
+    var unreachedHandled = t.unreached_func('rejectionhandled event should never be triggered');
+
+    addEventListener('unhandledrejection', unreachedUnhandled);
+    addEventListener('rejectionhandled', unreachedHandled);
+    ensureCleanup(t, unreachedUnhandled, unreachedHandled);
+
+    return new Promise(function(resolve) {
+      handleRejectedPromise(new Promise(function(resolve, reject) { reject(42); }));
+      t.step_timeout(function() {
+        resolve();
+      }, 1000);
+    });
+  }, 'Promise rejection should be muted if the rejected promise is handled in cross-origin non-CORS script');
+
+  promise_test(function(t) {
+    var promise = new Promise(function(resolve, reject) { reject(42); });
+    var resolveReceived;
+    var eventPromise = new Promise(function(resolve) { resolveReceived = resolve; });
+    var unhandled = t.step_func(function(e) {
+      if (e.promise === promise) {
+        handleRejectedPromise(promise);
+        resolveReceived();
+      }
+    });
+    var unreachedHandled = t.unreached_func('rejectionhandled event should never be triggered');
+
+    addEventListener('unhandledrejection', unhandled);
+    addEventListener('rejectionhandled', unreachedHandled);
+    ensureCleanup(t, unhandled, unreachedHandled);
+
+    return eventPromise.then(t.step_func(function() {
+      return new Promise(function(resolve) {
+        t.step_timeout(function() {
+          resolve();
+        }, 1000);
+      });
+    }));
+  }, 'Promise rejection should be muted if the rejected promise is handled in unhandledrejection event handler in cross-origin non-CORS script');
+
+  function ensureCleanup(t, unhandled, handled) {
+    t.add_cleanup(function() {
+      if (unhandled) {
+        removeEventListener('unhandledrejection', unhandled);
+      }
+      if (handled) {
+        removeEventListener('rejectionhandled', handled);
+      }
+    });
+  }
+
   var scriptEl = document.createElement('script');
   scriptEl.src = CROSSDOMAIN + 'support/promise-access-control.py?allow=false';
   scriptEl.onload = resolveLoaded;
diff --git a/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py b/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py
index 3e897bc..be74a36 100644
--- a/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py
+++ b/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py
@@ -5,6 +5,14 @@
     if allow != "false":
         headers.append(("Access-Control-Allow-Origin", "*"))
 
-    body = "new Promise(function(resolve, reject) { reject(42); })"
+    body = """
+    	function handleRejectedPromise(promise) {
+    		promise.catch(() => {});
+    	}
+
+    	(function() {
+    		new Promise(function(resolve, reject) { reject(42); });
+    	})();
+    """
 
     return headers, body
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/@std/__dir__.headers b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/@std/__dir__.headers
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/@std/__dir__.headers
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/@std/__dir__.headers
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/@std/blank b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/@std/blank
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/@std/blank
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/@std/blank
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/@std/none b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/@std/none
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/@std/none
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/@std/none
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare.sub.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare.sub.tentative.html
similarity index 65%
rename from third_party/blink/web_tests/external/wpt/import-maps/bare.sub.tentative.html
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare.sub.tentative.html
index cf99589..e20424ae 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/bare.sub.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare.sub.tentative.html
@@ -2,7 +2,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/test-helper.js"></script>
+<script src="../resources/test-helper.js"></script>
 
 <script>
 // "bare/..." (i.e. without leading "./") are bare specifiers
@@ -15,16 +15,10 @@
 const importMap = `
 {
   "imports": {
-    "bare/bare": "./resources/log.js?pipe=sub&name=bare",
-    "bare/cross-origin-bare": "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-bare",
-    "bare/to-data": "data:text/javascript,log.push('dataURL')",
-
     "bare/std-blank": "std:blank",
     "bare/blank": "@std/blank",
     "bare/std-none": "std:none",
-    "bare/none": "@std/none",
-
-    "bare/to-bare": "bare/bare"
+    "bare/none": "@std/none"
   }
 }
 `;
@@ -41,16 +35,6 @@
   // a parse error, not fetch error. Therefore, we use Result.PARSE_ERROR
   // below. https://crbug.com/928435
 
-  // Bare to HTTP(S).
-  "bare/bare":
-    [Result.URL, Result.URL, "log:bare", "log:bare"],
-  "bare/cross-origin-bare":
-    [Result.URL, Result.URL, "log:cross-origin-bare", "log:cross-origin-bare"],
-
-  // Bare to data:
-  "bare/to-data":
-    [Result.URL, Result.URL, "dataURL", "dataURL"],
-
   // Bare to built-in.
   "bare/std-blank":
     [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
@@ -60,10 +44,6 @@
     [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
   "bare/none":
     [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
-
-  // Bare to bare mapping is disabled.
-  "bare/to-bare":
-    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
 };
 
 doTests(importMap, null, tests);
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare/__dir__.headers b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare/__dir__.headers
similarity index 100%
copy from third_party/blink/web_tests/external/wpt/import-maps/bare/__dir__.headers
copy to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare/__dir__.headers
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare/blank b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare/blank
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/bare/blank
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare/blank
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare/none b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare/none
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/bare/none
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare/none
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare/std-blank b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare/std-blank
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/bare/std-blank
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare/std-blank
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare/std-none b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare/std-none
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/bare/std-none
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/bare/std-none
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-empty.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/builtin-empty.tentative.html
similarity index 75%
rename from third_party/blink/web_tests/external/wpt/import-maps/builtin-empty.tentative.html
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/builtin-empty.tentative.html
index aba28d18e..9ede75a 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/builtin-empty.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/builtin-empty.tentative.html
@@ -1,16 +1,16 @@
 <!DOCTYPE html>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/test-helper.js"></script>
+<script src="../resources/test-helper.js"></script>
 
 <script>
 const importMap = `
 {
   "imports": {
-    "./resources/log.js?pipe=sub&name=empty": [ "@std/" ],
-    "./resources/log.js?pipe=sub&name=empty-fallback": [
+    "../resources/log.js?pipe=sub&name=empty": [ "@std/" ],
+    "../resources/log.js?pipe=sub&name=empty-fallback": [
       "@std/",
-      "./resources/log.js?pipe=sub&name=empty-fallback"
+      "../resources/log.js?pipe=sub&name=empty-fallback"
     ]
   }
 }
@@ -32,9 +32,9 @@
   "@std/":
     [Result.FETCH_ERROR, Result.PARSE_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR],
 
-  "./resources/log.js?pipe=sub&name=empty":
+  "../resources/log.js?pipe=sub&name=empty":
     [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
-  "./resources/log.js?pipe=sub&name=empty-fallback":
+  "../resources/log.js?pipe=sub&name=empty-fallback":
     [Result.URL, Result.URL, Result.URL, Result.URL],
 };
 
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-import-scheme.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/builtin-import-scheme.tentative.html
similarity index 93%
rename from third_party/blink/web_tests/external/wpt/import-maps/builtin-import-scheme.tentative.html
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/builtin-import-scheme.tentative.html
index 6a27ef5..d9977ae35 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/builtin-import-scheme.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/builtin-import-scheme.tentative.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/test-helper.js"></script>
+<script src="../resources/test-helper.js"></script>
 
 <script>
 const tests = {
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/builtin.tentative.html
similarity index 94%
rename from third_party/blink/web_tests/external/wpt/import-maps/builtin.tentative.html
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/builtin.tentative.html
index e85289e..497bb5c 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/builtin.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/builtin.tentative.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/test-helper.js"></script>
+<script src="../resources/test-helper.js"></script>
 
 <script>
 const tests = {
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/data.sub.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/data.sub.tentative.html
similarity index 60%
rename from third_party/blink/web_tests/external/wpt/import-maps/data.sub.tentative.html
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/data.sub.tentative.html
index 6349489..0377a78 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/data.sub.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/data.sub.tentative.html
@@ -2,7 +2,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/test-helper.js"></script>
+<script src="../resources/test-helper.js"></script>
 
 <script>
 // "bare/..." (i.e. without leading "./") are bare specifiers
@@ -15,18 +15,10 @@
 const importMap = `
 {
   "imports": {
-    "bare": "./resources/log.js?pipe=sub&name=bare",
-
-    "data:text/javascript,log.push('data:foo')": "./resources/log.js?pipe=sub&name=foo",
-    "data:text/javascript,log.push('data:cross-origin-foo')": "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-foo",
-    "data:text/javascript,log.push('data:to-data')": "data:text/javascript,log.push('dataURL')",
-
     "data:text/javascript,log.push('data:std-blank')": "std:blank",
     "data:text/javascript,log.push('data:blank')": "@std/blank",
     "data:text/javascript,log.push('data:std-none')": "std:none",
-    "data:text/javascript,log.push('data:none')": "@std/none",
-
-    "data:text/javascript,log.push('data:to-bare')": "bare"
+    "data:text/javascript,log.push('data:none')": "@std/none"
   }
 }
 `;
@@ -43,16 +35,6 @@
   // a parse error, not fetch error. Therefore, we use Result.PARSE_ERROR
   // below. https://crbug.com/928435
 
-  // data: to HTTP(S).
-  "data:text/javascript,log.push('data:foo')":
-    [Result.URL, Result.URL, "log:foo", "log:foo"],
-  "data:text/javascript,log.push('data:cross-origin-foo')":
-    [Result.URL, Result.URL, "log:cross-origin-foo", "log:cross-origin-foo"],
-
-  // data: to data:
-  "data:text/javascript,log.push('data:to-data')":
-    [Result.URL, Result.URL, "dataURL", "dataURL"],
-
   // data: to built-in.
   "data:text/javascript,log.push('data:std-blank')":
     [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
@@ -62,10 +44,6 @@
     [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
   "data:text/javascript,log.push('data:none')":
     [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
-
-  // data: to bare mapping is disabled.
-  "data:text/javascript,log.push('data:to-bare')":
-    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
 };
 
 doTests(importMap, null, tests);
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/fallback-disallowed.sub.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/fallback-disallowed.sub.tentative.html
new file mode 100644
index 0000000..7efe90eb75
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/fallback-disallowed.sub.tentative.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helper.js"></script>
+
+<script>
+// Fallbacks from external URLs (such as HTTPS URLs) are
+// blocked by ongoing spec discussions, for example
+// https://github.com/WICG/import-maps/issues/76.
+// https://crbug.com/928435
+//
+// This test, as well as Chromium's implementation, rejects broader range of
+// fallbacks (not only those from HTTPS), to avoid potential spec and
+// interoperability issues.
+// The only allowed fallback pattern is fallbacks from bare specifiers with
+// two elements, which are listed in fallback.sub.tentative.html.
+const importMap = `
+{
+  "imports": {
+    "bare": "../resources/log.js?pipe=sub&name=bare",
+
+    "../resources/log.js?pipe=sub&name=http-to-builtin": [
+      "../resources/log.js?pipe=sub&name=http-to-builtin",
+      "@std/blank"
+    ],
+
+    "../resources/log.js?pipe=sub&name=fallback-to-different-url-1": [
+      "@std/blank",
+      "../resources/log.js?pipe=sub&name=something-different"
+    ],
+    "../resources/log.js?pipe=sub&name=fallback-to-different-url-2": [
+      "@std/none",
+      "../resources/log.js?pipe=sub&name=something-different2"
+    ],
+    "../resources/log.js?pipe=sub&name=fallback-to-different-origin-1": [
+      "@std/blank",
+      "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=fallback-to-different-origin-1"
+    ],
+    "../resources/log.js?pipe=sub&name=fallback-to-different-origin-2": [
+      "@std/none",
+      "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=fallback-to-different-origin-2"
+    ],
+
+    "../resources/log.js?pipe=sub&name=more-than-two-values-1": [
+      "@std/none",
+      "@std/blank",
+      "../resources/log.js?pipe=sub&name=more-than-two-values-1"
+    ],
+    "../resources/log.js?pipe=sub&name=more-than-two-values-2": [
+      "@std/none",
+      "../resources/log.js?pipe=sub&name=more-than-two-values-2",
+      "@std/blank"
+    ],
+    "../resources/log.js?pipe=sub&name=fallback-from-http": [
+      "../resources/log.js?pipe=sub&name=non-built-in",
+      "../resources/log.js?pipe=sub&name=fallback-from-http"
+    ],
+    "../resources/log.js?pipe=sub&name=fallback-from-data-1": [
+      "data:text/plain,",
+      "../resources/log.js?pipe=sub&name=fallback-from-http"
+    ],
+    "../resources/log.js?pipe=sub&name=fallback-from-data-2": [
+      "data:text/javascript,log.push('dataURL')",
+      "../resources/log.js?pipe=sub&name=fallback-from-http"
+    ]
+  }
+}
+`;
+const tests = {};
+for (const key in JSON.parse(importMap).imports) {
+  if (key === "bare") {
+    continue;
+  }
+  tests[key] =
+    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR];
+}
+doTests(importMap, null, tests);
+</script>
+<body>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/fallback.sub.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/fallback.sub.tentative.html
similarity index 82%
rename from third_party/blink/web_tests/external/wpt/import-maps/fallback.sub.tentative.html
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/fallback.sub.tentative.html
index 3f4f2887..45cf6dc1 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/fallback.sub.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/fallback.sub.tentative.html
@@ -2,7 +2,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/test-helper.js"></script>
+<script src="../resources/test-helper.js"></script>
 
 <script>
 // This tests is for fallbacks with the pattern of
@@ -16,13 +16,13 @@
 const importMap = `
 {
   "imports": {
-    "./resources/log.js?pipe=sub&name=blank": [
+    "../resources/log.js?pipe=sub&name=blank": [
       "@std/blank",
-      "./resources/log.js?pipe=sub&name=blank"
+      "../resources/log.js?pipe=sub&name=blank"
     ],
-    "./resources/log.js?pipe=sub&name=none": [
+    "../resources/log.js?pipe=sub&name=none": [
       "@std/none",
-      "./resources/log.js?pipe=sub&name=none"
+      "../resources/log.js?pipe=sub&name=none"
     ],
     "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-blank": [
       "@std/blank",
@@ -33,13 +33,13 @@
       "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-none"
     ],
 
-    "./resources/log.js?pipe=sub&name=std-blank": [
+    "../resources/log.js?pipe=sub&name=std-blank": [
       "std:blank",
-      "./resources/log.js?pipe=sub&name=std-blank"
+      "../resources/log.js?pipe=sub&name=std-blank"
     ],
-    "./resources/log.js?pipe=sub&name=std-none": [
+    "../resources/log.js?pipe=sub&name=std-none": [
       "std:none",
-      "./resources/log.js?pipe=sub&name=std-none"
+      "../resources/log.js?pipe=sub&name=std-none"
     ],
     "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=std-cross-origin-blank": [
       "std:blank",
@@ -61,18 +61,18 @@
   // - dynamic import.
   // Result.URL indicates that the specifier was not re-mapped by import maps,
   // i.e. either considered as a relative path, or fallback occured.
-  "./resources/log.js?pipe=sub&name=blank":
+  "../resources/log.js?pipe=sub&name=blank":
     [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
-  "./resources/log.js?pipe=sub&name=none":
+  "../resources/log.js?pipe=sub&name=none":
     [Result.URL, Result.URL, Result.URL, Result.URL],
   "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-blank":
     [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
   "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-none":
     [Result.URL, Result.URL, Result.URL, Result.URL],
 
-  "./resources/log.js?pipe=sub&name=std-blank":
+  "../resources/log.js?pipe=sub&name=std-blank":
     [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
-  "./resources/log.js?pipe=sub&name=std-none":
+  "../resources/log.js?pipe=sub&name=std-none":
     [Result.URL, Result.URL, Result.URL, Result.URL],
   "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=std-cross-origin-blank":
     [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/http.sub.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/http.sub.tentative.html
new file mode 100644
index 0000000..7689ae13
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/http.sub.tentative.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helper.js"></script>
+
+<script>
+// "bare/..." (i.e. without leading "./") are bare specifiers
+// (not relative paths).
+//
+// Discussions about notations for builtin modules are ongoing, e.g.
+// https://github.com/tc39/proposal-javascript-standard-library/issues/12
+// Currently the tests expects two notations are accepted.
+// TODO: Once the discussions converge, update the tests.
+const importMap = `
+{
+  "imports": {
+    "../resources/log.js?pipe=sub&name=std-blank": "std:blank",
+    "../resources/log.js?pipe=sub&name=blank": "@std/blank",
+    "../resources/log.js?pipe=sub&name=std-none": "std:none",
+    "../resources/log.js?pipe=sub&name=none": "@std/none"
+  }
+}
+`;
+
+const tests = {
+  // Arrays of expected results for:
+  // - <script src type="module">,
+  // - <script src> (classic script),
+  // - static import, and
+  // - dynamic import.
+
+  // Currently, Chromium's implementation resolves import maps as a part of
+  // specifier resolution, and thus failure in import map resolution causes
+  // a parse error, not fetch error. Therefore, we use Result.PARSE_ERROR
+  // below. https://crbug.com/928435
+
+  // HTTP(S) to built-in.
+  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=std-blank":
+    [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
+  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=blank":
+    [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
+  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=std-none":
+    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
+  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=none":
+    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
+};
+
+doTests(importMap, null, tests);
+</script>
+<body>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-addresses.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-addresses.tentative-expected.txt
new file mode 100644
index 0000000..35155a7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-addresses.tentative-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+PASS Relative URL-like addresses / should accept strings prefixed with ./, ../, or /
+FAIL Relative URL-like addresses / should not accept strings prefixed with ./, ../, or / for data: base URLs assert_equals: expected "{\"imports\":{\"dotDotSlash\":[],\"dotSlash\":[],\"slash\":[]},\"scopes\":{}}" but got "{\"imports\":{\"dotDotSlash\":[\"http://web-platform.test:8001/import-maps/builtin-support.tentative/foo\"],\"dotSlash\":[\"http://web-platform.test:8001/import-maps/builtin-support.tentative/imported/foo\"],\"slash\":[\"http://web-platform.test:8001/foo\"]},\"scopes\":{}}"
+PASS Relative URL-like addresses / should accept the literal strings ./, ../, or / with no suffix
+PASS Relative URL-like addresses / should ignore percent-encoded variants of ./, ../, or /
+PASS Built-in module addresses / should accept URLs using the built-in module scheme
+PASS Built-in module addresses / should ignore percent-encoded variants of the built-in module scheme
+PASS Built-in module addresses / should allow built-in module URLs that contain "/" or "\"
+FAIL Absolute URL addresses / should only accept absolute URL addresses with fetch schemes assert_equals: expected "{\"imports\":{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[\"filesystem:good\"],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[],\"javascript\":[],\"mailto\":[],\"wss\":[]},\"scopes\":{}}" but got "{\"imports\":{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[\"import:bad\"],\"javascript\":[\"javascript:bad\"],\"mailto\":[\"mailto:bad\"],\"wss\":[\"wss://bad/\"]},\"scopes\":{}}"
+FAIL Absolute URL addresses / should only accept absolute URL addresses with fetch schemes inside arrays assert_equals: expected "{\"imports\":{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[\"filesystem:good\"],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[],\"javascript\":[],\"mailto\":[],\"wss\":[]},\"scopes\":{}}" but got "{\"imports\":{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[\"import:bad\"],\"javascript\":[\"javascript:bad\"],\"mailto\":[\"mailto:bad\"],\"wss\":[\"wss://bad/\"]},\"scopes\":{}}"
+FAIL Absolute URL addresses / should parse absolute URLs, ignoring unparseable ones assert_equals: expected "{\"imports\":{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/%41\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[],\"unparseable2\":[],\"unparseable3\":[]},\"scopes\":{}}" but got "{\"imports\":{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/A\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[\"https://ex%20ample.org/\"],\"unparseable2\":[],\"unparseable3\":[]},\"scopes\":{}}"
+FAIL Absolute URL addresses / should parse absolute URLs, ignoring unparseable ones inside arrays assert_equals: expected "{\"imports\":{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/%41\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[],\"unparseable2\":[],\"unparseable3\":[]},\"scopes\":{}}" but got "{\"imports\":{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/A\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[\"https://ex%20ample.org/\"],\"unparseable2\":[],\"unparseable3\":[]},\"scopes\":{}}"
+PASS Failing addresses: mismatched trailing slashes / should warn for the simple case
+PASS Failing addresses: mismatched trailing slashes / should warn for a mismatch alone in an array
+PASS Failing addresses: mismatched trailing slashes / should warn for a mismatch alongside non-mismatches in an array
+PASS Other invalid addresses / should ignore unprefixed strings that are not absolute URLs
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-addresses.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-addresses.tentative.html
new file mode 100644
index 0000000..0cc92ce
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-addresses.tentative.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/jest-test-helper.js"></script>
+<script type="module" src="resources/helpers/parsing.js"></script>
+
+<!--
+Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/parsing-addresses.js
+-->
+<script type="module" src="resources/parsing-addresses.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-schema.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-schema.tentative.html
new file mode 100644
index 0000000..9e3bca29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-schema.tentative.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/jest-test-helper.js"></script>
+<script type="module" src="resources/helpers/parsing.js"></script>
+
+<!--
+Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/parsing-schema.js
+-->
+<script type="module" src="resources/parsing-schema.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-scope-keys.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-scope-keys.tentative-expected.txt
new file mode 100644
index 0000000..501c084
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-scope-keys.tentative-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+PASS Relative URL scope keys / should work with no prefix
+PASS Relative URL scope keys / should work with ./, ../, and / prefixes
+PASS Relative URL scope keys / should work with /s, ?s, and #s
+PASS Relative URL scope keys / should work with an empty string scope key
+PASS Relative URL scope keys / should work with / suffixes
+PASS Relative URL scope keys / should deduplicate based on URL parsing rules
+PASS Absolute URL scope keys / should accept all absolute URL scope keys, with or without fetch schemes
+FAIL Absolute URL scope keys / should parse absolute URL scope keys, ignoring unparseable ones assert_equals: expected "{\"imports\":{},\"scopes\":{\"https://base.example/path1/path2/example.org\":{},\"https://example.com/%41\":{},\"https://example.com///\":{},\"https://example.com/foo/\":{},\"https://example.net/\":{}}}" but got "{\"imports\":{},\"scopes\":{\"https://base.example/path1/path2/example.org\":{},\"https://ex%20ample.org/\":{},\"https://example.com///\":{},\"https://example.com/A\":{},\"https://example.com/foo/\":{},\"https://example.net/\":{}}}"
+FAIL Absolute URL scope keys / should ignore relative URL scope keys when the base URL is a data: URL assert_equals: expected "{\"imports\":{},\"scopes\":{}}" but got "{\"imports\":{},\"scopes\":{\"http://web-platform.test:8001/foo\":{},\"http://web-platform.test:8001/import-maps/builtin-support.tentative/foo\":{},\"http://web-platform.test:8001/import-maps/builtin-support.tentative/imported/foo\":{}}}"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resolving-builtins.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-scope-keys.tentative.html
similarity index 60%
copy from third_party/blink/web_tests/external/wpt/import-maps/imported/resolving-builtins.tentative.html
copy to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-scope-keys.tentative.html
index c1395c1..be23c64 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/resolving-builtins.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-scope-keys.tentative.html
@@ -2,10 +2,10 @@
 <html>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../resources/jest-test-helper.js"></script>
+<script src="../../resources/jest-test-helper.js"></script>
 <script type="module" src="resources/helpers/parsing.js"></script>
 
 <!--
-Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/resolving-builtins.js
+Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/parsing-scope-keys.js
 -->
-<script type="module" src="resources/resolving-builtins.js"></script>
+<script type="module" src="resources/parsing-scope-keys.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-specifier-keys.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-specifier-keys.tentative-expected.txt
new file mode 100644
index 0000000..7f14731
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-specifier-keys.tentative-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+PASS Relative URL-like specifier keys / should absolutize strings prefixed with ./, ../, or / into the corresponding URLs
+FAIL Relative URL-like specifier keys / should not absolutize strings prefixed with ./, ../, or / with a data: URL base assert_equals: expected "{\"imports\":{\"../foo\":[\"https://example.com/dotdotslash\"],\"./foo\":[\"https://example.com/dotslash\"],\"/foo\":[\"https://example.com/slash\"]},\"scopes\":{}}" but got "{\"imports\":{\"http://web-platform.test:8001/foo\":[\"https://example.com/slash\"],\"http://web-platform.test:8001/import-maps/builtin-support.tentative/foo\":[\"https://example.com/dotdotslash\"],\"http://web-platform.test:8001/import-maps/builtin-support.tentative/imported/foo\":[\"https://example.com/dotslash\"]},\"scopes\":{}}"
+PASS Relative URL-like specifier keys / should absolutize the literal strings ./, ../, or / with no suffix
+PASS Relative URL-like specifier keys / should treat percent-encoded variants of ./, ../, or / as bare specifiers
+FAIL Absolute URL specifier keys / should only accept absolute URL specifier keys with fetch schemes, treating others as bare specifiers assert_equals: expected "{\"imports\":{\"about:good\":[\"https://base.example/about\"],\"blob:good\":[\"https://base.example/blob\"],\"data:good\":[\"https://base.example/data\"],\"file:///good\":[\"https://base.example/file\"],\"filesystem:good\":[\"https://base.example/filesystem\"],\"ftp://good/\":[\"https://base.example/ftp/\"],\"http://good/\":[\"https://base.example/http/\"],\"https://good/\":[\"https://base.example/https/\"],\"import:bad\":[\"https://base.example/import\"],\"javascript:bad\":[\"https://base.example/javascript\"],\"mailto:bad\":[\"https://base.example/mailto\"],\"wss:bad\":[\"https://base.example/wss\"]},\"scopes\":{}}" but got "{\"imports\":{\"about:good\":[\"https://base.example/about\"],\"blob:good\":[\"https://base.example/blob\"],\"data:good\":[\"https://base.example/data\"],\"file:///good\":[\"https://base.example/file\"],\"filesystem:good\":[\"https://base.example/filesystem\"],\"ftp://good/\":[\"https://base.example/ftp/\"],\"http://good/\":[\"https://base.example/http/\"],\"https://good/\":[\"https://base.example/https/\"],\"import:bad\":[\"https://base.example/import\"],\"javascript:bad\":[\"https://base.example/javascript\"],\"mailto:bad\":[\"https://base.example/mailto\"],\"wss://bad/\":[\"https://base.example/wss\"]},\"scopes\":{}}"
+FAIL Absolute URL specifier keys / should parse absolute URLs, treating unparseable ones as bare specifiers assert_equals: expected "{\"imports\":{\"http://[www.example.com]/\":[\"https://base.example/unparseable3/\"],\"https://ex ample.org/\":[\"https://base.example/unparseable1/\"],\"https://example.com/\":[\"https://base.example/percentDecoding/\"],\"https://example.com/%41\":[\"https://base.example/noPercentDecoding\"],\"https://example.com///\":[\"https://base.example/invalidButParseable2/\"],\"https://example.com:demo\":[\"https://base.example/unparseable2\"],\"https://example.net/\":[\"https://base.example/prettyNormal/\"],\"https://example.org/\":[\"https://base.example/invalidButParseable1/\"]},\"scopes\":{}}" but got "{\"imports\":{\"http://[www.example.com]/\":[\"https://base.example/unparseable3/\"],\"https://ex%20ample.org/\":[\"https://base.example/unparseable1/\"],\"https://example.com/\":[\"https://base.example/percentDecoding/\"],\"https://example.com///\":[\"https://base.example/invalidButParseable2/\"],\"https://example.com/A\":[\"https://base.example/noPercentDecoding\"],\"https://example.com:demo\":[\"https://base.example/unparseable2\"],\"https://example.net/\":[\"https://base.example/prettyNormal/\"],\"https://example.org/\":[\"https://base.example/invalidButParseable1/\"]},\"scopes\":{}}"
+PASS Absolute URL specifier keys / should parse built-in module specifier keys, including with a "/"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-specifier-keys.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-specifier-keys.tentative.html
new file mode 100644
index 0000000..7bc2d47
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/parsing-specifier-keys.tentative.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/jest-test-helper.js"></script>
+<script type="module" src="resources/helpers/parsing.js"></script>
+
+<!--
+Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/parsing-specifier-keys.js
+-->
+<script type="module" src="resources/parsing-specifier-keys.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resolving-builtins.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving-builtins.tentative-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/imported/resolving-builtins.tentative-expected.txt
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving-builtins.tentative-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resolving-builtins.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving-builtins.tentative.html
similarity index 86%
rename from third_party/blink/web_tests/external/wpt/import-maps/imported/resolving-builtins.tentative.html
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving-builtins.tentative.html
index c1395c1..065cfa3 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/resolving-builtins.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving-builtins.tentative.html
@@ -2,7 +2,7 @@
 <html>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../resources/jest-test-helper.js"></script>
+<script src="../../resources/jest-test-helper.js"></script>
 <script type="module" src="resources/helpers/parsing.js"></script>
 
 <!--
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resolving-not-yet-implemented.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving-not-yet-implemented.tentative.html
similarity index 87%
rename from third_party/blink/web_tests/external/wpt/import-maps/imported/resolving-not-yet-implemented.tentative.html
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving-not-yet-implemented.tentative.html
index 7db5f29f..3bdd591f 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/resolving-not-yet-implemented.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving-not-yet-implemented.tentative.html
@@ -2,7 +2,7 @@
 <html>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../resources/jest-test-helper.js"></script>
+<script src="../../resources/jest-test-helper.js"></script>
 <script type="module" src="resources/helpers/parsing.js"></script>
 
 <!--
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving-scopes.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving-scopes.tentative.html
new file mode 100644
index 0000000..33b4935
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving-scopes.tentative.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/jest-test-helper.js"></script>
+<script type="module" src="resources/helpers/parsing.js"></script>
+
+<!--
+Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/resolving-scopes.js
+-->
+<script type="module" src="resources/resolving-scopes.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resolving.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving.tentative-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/imported/resolving.tentative-expected.txt
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving.tentative-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving.tentative.html
new file mode 100644
index 0000000..1d24eb20
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resolving.tentative.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/jest-test-helper.js"></script>
+<script type="module" src="resources/helpers/parsing.js"></script>
+
+<!--
+Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/resolving.js
+-->
+<script type="module" src="resources/resolving.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/helpers/parsing.js b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/helpers/parsing.js
new file mode 100644
index 0000000..daad6d2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/helpers/parsing.js
@@ -0,0 +1,44 @@
+'use strict';
+const { parseFromString } = require('../../lib/parser.js');
+
+// Local modifications from upstream:
+// Currently warnings and scopes are not checked in expectSpecifierMap().
+exports.expectSpecifierMap = (input, baseURL, output, warnings = []) => {
+  expect(parseFromString(`{ "imports": ${input} }`, baseURL))
+    .toEqual({ imports: output, scopes: {} });
+};
+
+exports.expectScopes = (inputArray, baseURL, outputArray, warnings = []) => {
+  const checkWarnings = testWarningHandler(warnings);
+
+  const inputScopesAsStrings = inputArray.map(scopePrefix => `${JSON.stringify(scopePrefix)}: {}`);
+  const inputString = `{ "scopes": { ${inputScopesAsStrings.join(', ')} } }`;
+
+  const outputScopesObject = {};
+  for (const outputScopePrefix of outputArray) {
+    outputScopesObject[outputScopePrefix] = {};
+  }
+
+  expect(parseFromString(inputString, baseURL)).toEqual({ imports: {}, scopes: outputScopesObject });
+
+  checkWarnings();
+};
+
+exports.expectBad = (input, baseURL, warnings = []) => {
+  const checkWarnings = testWarningHandler(warnings);
+  expect(() => parseFromString(input, baseURL)).toThrow(TypeError);
+  checkWarnings();
+};
+
+exports.expectWarnings = (input, baseURL, output, warnings = []) => {
+  const checkWarnings = testWarningHandler(warnings);
+  expect(parseFromString(input, baseURL)).toEqual(output);
+
+  checkWarnings();
+};
+
+function testWarningHandler(expectedWarnings) {
+  // We don't check warnings on WPT tests, because there are no
+  // ways to catch console warnings from JavaScript.
+  return () => {};
+}
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-addresses.js b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-addresses.js
new file mode 100644
index 0000000..0f5fc73
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-addresses.js
@@ -0,0 +1,351 @@
+'use strict';
+const { expectSpecifierMap } = require('./helpers/parsing.js');
+const { BUILT_IN_MODULE_SCHEME } = require('../lib/utils.js');
+
+describe('Relative URL-like addresses', () => {
+  it('should accept strings prefixed with ./, ../, or /', () => {
+    expectSpecifierMap(
+      `{
+        "dotSlash": "./foo",
+        "dotDotSlash": "../foo",
+        "slash": "/foo"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        dotSlash: [expect.toMatchURL('https://base.example/path1/path2/foo')],
+        dotDotSlash: [expect.toMatchURL('https://base.example/path1/foo')],
+        slash: [expect.toMatchURL('https://base.example/foo')]
+      }
+    );
+  });
+
+  it('should not accept strings prefixed with ./, ../, or / for data: base URLs', () => {
+    expectSpecifierMap(
+      `{
+        "dotSlash": "./foo",
+        "dotDotSlash": "../foo",
+        "slash": "/foo"
+      }`,
+      'data:text/html,test',
+      {
+        dotSlash: [],
+        dotDotSlash: [],
+        slash: []
+      },
+      [
+        `Invalid address "./foo" for the specifier key "dotSlash".`,
+        `Invalid address "../foo" for the specifier key "dotDotSlash".`,
+        `Invalid address "/foo" for the specifier key "slash".`
+      ]
+    );
+  });
+
+  it('should accept the literal strings ./, ../, or / with no suffix', () => {
+    expectSpecifierMap(
+      `{
+        "dotSlash": "./",
+        "dotDotSlash": "../",
+        "slash": "/"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        dotSlash: [expect.toMatchURL('https://base.example/path1/path2/')],
+        dotDotSlash: [expect.toMatchURL('https://base.example/path1/')],
+        slash: [expect.toMatchURL('https://base.example/')]
+      }
+    );
+  });
+
+  it('should ignore percent-encoded variants of ./, ../, or /', () => {
+    expectSpecifierMap(
+      `{
+        "dotSlash1": "%2E/",
+        "dotDotSlash1": "%2E%2E/",
+        "dotSlash2": ".%2F",
+        "dotDotSlash2": "..%2F",
+        "slash2": "%2F",
+        "dotSlash3": "%2E%2F",
+        "dotDotSlash3": "%2E%2E%2F"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        dotSlash1: [],
+        dotDotSlash1: [],
+        dotSlash2: [],
+        dotDotSlash2: [],
+        slash2: [],
+        dotSlash3: [],
+        dotDotSlash3: []
+      },
+      [
+        `Invalid address "%2E/" for the specifier key "dotSlash1".`,
+        `Invalid address "%2E%2E/" for the specifier key "dotDotSlash1".`,
+        `Invalid address ".%2F" for the specifier key "dotSlash2".`,
+        `Invalid address "..%2F" for the specifier key "dotDotSlash2".`,
+        `Invalid address "%2F" for the specifier key "slash2".`,
+        `Invalid address "%2E%2F" for the specifier key "dotSlash3".`,
+        `Invalid address "%2E%2E%2F" for the specifier key "dotDotSlash3".`
+      ]
+    );
+  });
+});
+
+describe('Built-in module addresses', () => {
+  it('should accept URLs using the built-in module scheme', () => {
+    expectSpecifierMap(
+      `{
+        "foo": "${BUILT_IN_MODULE_SCHEME}:foo"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        foo: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo`)]
+      }
+    );
+  });
+
+  it('should ignore percent-encoded variants of the built-in module scheme', () => {
+    expectSpecifierMap(
+      `{
+        "foo": "${encodeURIComponent(BUILT_IN_MODULE_SCHEME + ':')}foo"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        foo: []
+      },
+      [`Invalid address "${encodeURIComponent(BUILT_IN_MODULE_SCHEME + ':')}foo" for the specifier key "foo".`]
+    );
+  });
+
+  it('should allow built-in module URLs that contain "/" or "\\"', () => {
+    expectSpecifierMap(
+      `{
+        "slashEnd": "${BUILT_IN_MODULE_SCHEME}:foo/",
+        "slashMiddle": "${BUILT_IN_MODULE_SCHEME}:foo/bar",
+        "backslash": "${BUILT_IN_MODULE_SCHEME}:foo\\\\baz"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        slashEnd: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo/`)],
+        slashMiddle: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo/bar`)],
+        backslash: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo\\baz`)]
+      }
+    );
+  });
+});
+
+describe('Absolute URL addresses', () => {
+  it('should only accept absolute URL addresses with fetch schemes', () => {
+    expectSpecifierMap(
+      `{
+        "about": "about:good",
+        "blob": "blob:good",
+        "data": "data:good",
+        "file": "file:///good",
+        "filesystem": "filesystem:good",
+        "http": "http://good/",
+        "https": "https://good/",
+        "ftp": "ftp://good/",
+        "import": "import:bad",
+        "mailto": "mailto:bad",
+        "javascript": "javascript:bad",
+        "wss": "wss:bad"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        about: [expect.toMatchURL('about:good')],
+        blob: [expect.toMatchURL('blob:good')],
+        data: [expect.toMatchURL('data:good')],
+        file: [expect.toMatchURL('file:///good')],
+        filesystem: [expect.toMatchURL('filesystem:good')],
+        http: [expect.toMatchURL('http://good/')],
+        https: [expect.toMatchURL('https://good/')],
+        ftp: [expect.toMatchURL('ftp://good/')],
+        import: [],
+        mailto: [],
+        javascript: [],
+        wss: []
+      },
+      [
+        `Invalid address "import:bad" for the specifier key "import".`,
+        `Invalid address "mailto:bad" for the specifier key "mailto".`,
+        `Invalid address "javascript:bad" for the specifier key "javascript".`,
+        `Invalid address "wss:bad" for the specifier key "wss".`
+      ]
+    );
+  });
+
+  it('should only accept absolute URL addresses with fetch schemes inside arrays', () => {
+    expectSpecifierMap(
+      `{
+        "about": ["about:good"],
+        "blob": ["blob:good"],
+        "data": ["data:good"],
+        "file": ["file:///good"],
+        "filesystem": ["filesystem:good"],
+        "http": ["http://good/"],
+        "https": ["https://good/"],
+        "ftp": ["ftp://good/"],
+        "import": ["import:bad"],
+        "mailto": ["mailto:bad"],
+        "javascript": ["javascript:bad"],
+        "wss": ["wss:bad"]
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        about: [expect.toMatchURL('about:good')],
+        blob: [expect.toMatchURL('blob:good')],
+        data: [expect.toMatchURL('data:good')],
+        file: [expect.toMatchURL('file:///good')],
+        filesystem: [expect.toMatchURL('filesystem:good')],
+        http: [expect.toMatchURL('http://good/')],
+        https: [expect.toMatchURL('https://good/')],
+        ftp: [expect.toMatchURL('ftp://good/')],
+        import: [],
+        mailto: [],
+        javascript: [],
+        wss: []
+      },
+      [
+        `Invalid address "import:bad" for the specifier key "import".`,
+        `Invalid address "mailto:bad" for the specifier key "mailto".`,
+        `Invalid address "javascript:bad" for the specifier key "javascript".`,
+        `Invalid address "wss:bad" for the specifier key "wss".`
+      ]
+    );
+  });
+
+  it('should parse absolute URLs, ignoring unparseable ones', () => {
+    expectSpecifierMap(
+      `{
+        "unparseable1": "https://ex ample.org/",
+        "unparseable2": "https://example.com:demo",
+        "unparseable3": "http://[www.example.com]/",
+        "invalidButParseable1": "https:example.org",
+        "invalidButParseable2": "https://///example.com///",
+        "prettyNormal": "https://example.net",
+        "percentDecoding": "https://ex%41mple.com/",
+        "noPercentDecoding": "https://example.com/%41"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        unparseable1: [],
+        unparseable2: [],
+        unparseable3: [],
+        invalidButParseable1: [expect.toMatchURL('https://example.org/')],
+        invalidButParseable2: [expect.toMatchURL('https://example.com///')],
+        prettyNormal: [expect.toMatchURL('https://example.net/')],
+        percentDecoding: [expect.toMatchURL('https://example.com/')],
+        noPercentDecoding: [expect.toMatchURL('https://example.com/%41')]
+      },
+      [
+        `Invalid address "https://ex ample.org/" for the specifier key "unparseable1".`,
+        `Invalid address "https://example.com:demo" for the specifier key "unparseable2".`,
+        `Invalid address "http://[www.example.com]/" for the specifier key "unparseable3".`
+      ]
+    );
+  });
+
+  it('should parse absolute URLs, ignoring unparseable ones inside arrays', () => {
+    expectSpecifierMap(
+      `{
+        "unparseable1": ["https://ex ample.org/"],
+        "unparseable2": ["https://example.com:demo"],
+        "unparseable3": ["http://[www.example.com]/"],
+        "invalidButParseable1": ["https:example.org"],
+        "invalidButParseable2": ["https://///example.com///"],
+        "prettyNormal": ["https://example.net"],
+        "percentDecoding": ["https://ex%41mple.com/"],
+        "noPercentDecoding": ["https://example.com/%41"]
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        unparseable1: [],
+        unparseable2: [],
+        unparseable3: [],
+        invalidButParseable1: [expect.toMatchURL('https://example.org/')],
+        invalidButParseable2: [expect.toMatchURL('https://example.com///')],
+        prettyNormal: [expect.toMatchURL('https://example.net/')],
+        percentDecoding: [expect.toMatchURL('https://example.com/')],
+        noPercentDecoding: [expect.toMatchURL('https://example.com/%41')]
+      },
+      [
+        `Invalid address "https://ex ample.org/" for the specifier key "unparseable1".`,
+        `Invalid address "https://example.com:demo" for the specifier key "unparseable2".`,
+        `Invalid address "http://[www.example.com]/" for the specifier key "unparseable3".`
+      ]
+    );
+  });
+});
+
+describe('Failing addresses: mismatched trailing slashes', () => {
+  it('should warn for the simple case', () => {
+    expectSpecifierMap(
+      `{
+        "trailer/": "/notrailer",
+        "${BUILT_IN_MODULE_SCHEME}:trailer/": "/bim-notrailer"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        'trailer/': [],
+        [`${BUILT_IN_MODULE_SCHEME}:trailer/`]: []
+      },
+      [
+        `Invalid address "https://base.example/notrailer" for package specifier key "trailer/". Package addresses must end with "/".`,
+        `Invalid address "https://base.example/bim-notrailer" for package specifier key "${BUILT_IN_MODULE_SCHEME}:trailer/". Package addresses must end with "/".`
+      ]
+    );
+  });
+
+  it('should warn for a mismatch alone in an array', () => {
+    expectSpecifierMap(
+      `{
+        "trailer/": ["/notrailer"],
+        "${BUILT_IN_MODULE_SCHEME}:trailer/": ["/bim-notrailer"]
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        'trailer/': [],
+        [`${BUILT_IN_MODULE_SCHEME}:trailer/`]: []
+      },
+      [
+        `Invalid address "https://base.example/notrailer" for package specifier key "trailer/". Package addresses must end with "/".`,
+        `Invalid address "https://base.example/bim-notrailer" for package specifier key "${BUILT_IN_MODULE_SCHEME}:trailer/". Package addresses must end with "/".`
+      ]
+    );
+  });
+
+  it('should warn for a mismatch alongside non-mismatches in an array', () => {
+    expectSpecifierMap(
+      `{
+        "trailer/": ["/atrailer/", "/notrailer"],
+        "${BUILT_IN_MODULE_SCHEME}:trailer/": ["/bim-atrailer/", "/bim-notrailer"]
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        'trailer/': [expect.toMatchURL('https://base.example/atrailer/')],
+        [`${BUILT_IN_MODULE_SCHEME}:trailer/`]: [expect.toMatchURL('https://base.example/bim-atrailer/')]
+      },
+      [
+        `Invalid address "https://base.example/notrailer" for package specifier key "trailer/". Package addresses must end with "/".`,
+        `Invalid address "https://base.example/bim-notrailer" for package specifier key "${BUILT_IN_MODULE_SCHEME}:trailer/". Package addresses must end with "/".`
+      ]
+    );
+  });
+});
+
+describe('Other invalid addresses', () => {
+  it('should ignore unprefixed strings that are not absolute URLs', () => {
+    for (const bad of ['bar', '\\bar', '~bar', '#bar', '?bar']) {
+      expectSpecifierMap(
+        `{
+          "foo": ${JSON.stringify(bad)}
+        }`,
+        'https://base.example/path1/path2/path3',
+        {
+          foo: []
+        },
+        [`Invalid address "${bad}" for the specifier key "foo".`]
+      );
+    }
+  });
+});
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-schema.js b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-schema.js
new file mode 100644
index 0000000..69503453
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-schema.js
@@ -0,0 +1,139 @@
+'use strict';
+const { parseFromString } = require('../lib/parser.js');
+const { expectBad, expectWarnings, expectSpecifierMap } = require('./helpers/parsing.js');
+
+const nonObjectStrings = ['null', 'true', '1', '"foo"', '[]'];
+
+test('Invalid JSON', () => {
+  expect(() => parseFromString('{ imports: {} }', 'https://base.example/')).toThrow(SyntaxError);
+});
+
+describe('Mismatching the top-level schema', () => {
+  it('should throw for top-level non-objects', () => {
+    for (const nonObject of nonObjectStrings) {
+      expectBad(nonObject, 'https://base.example/');
+    }
+  });
+
+  it('should throw if imports is a non-object', () => {
+    for (const nonObject of nonObjectStrings) {
+      expectBad(`{ "imports": ${nonObject} }`, 'https://base.example/');
+    }
+  });
+
+  it('should throw if scopes is a non-object', () => {
+    for (const nonObject of nonObjectStrings) {
+      expectBad(`{ "scopes": ${nonObject} }`, 'https://base.example/');
+    }
+  });
+
+  it('should ignore unspecified top-level entries', () => {
+    expectWarnings(
+      `{
+        "imports": {},
+        "new-feature": {},
+        "scops": {}
+      }`,
+      'https://base.example/',
+      { imports: {}, scopes: {} },
+      [
+        `Invalid top-level key "new-feature". Only "imports" and "scopes" can be present.`,
+        `Invalid top-level key "scops". Only "imports" and "scopes" can be present.`
+      ]
+    );
+  });
+});
+
+describe('Mismatching the specifier map schema', () => {
+  const invalidAddressStrings = ['true', '1', '{}'];
+  const invalidInsideArrayStrings = ['null', 'true', '1', '{}', '[]'];
+
+  it('should ignore entries where the address is not a string, array, or null', () => {
+    for (const invalid of invalidAddressStrings) {
+      expectSpecifierMap(
+        `{
+          "foo": ${invalid},
+          "bar": ["https://example.com/"]
+        }`,
+        'https://base.example/',
+        {
+          bar: [expect.toMatchURL('https://example.com/')]
+        },
+        [
+          `Invalid address ${invalid} for the specifier key "foo". ` +
+          `Addresses must be strings, arrays, or null.`
+        ]
+      );
+    }
+  });
+
+  it('should ignore entries where the specifier key is an empty string', () => {
+    expectSpecifierMap(
+      `{
+        "": ["https://example.com/"]
+      }`,
+      'https://base.example/',
+      {},
+      [`Invalid empty string specifier key.`]
+    );
+  });
+
+  it('should ignore members of an address array that are not strings', () => {
+    for (const invalid of invalidInsideArrayStrings) {
+      expectSpecifierMap(
+        `{
+          "foo": ["https://example.com/", ${invalid}],
+          "bar": ["https://example.com/"]
+        }`,
+        'https://base.example/',
+        {
+          foo: [expect.toMatchURL('https://example.com/')],
+          bar: [expect.toMatchURL('https://example.com/')]
+        },
+        [
+          `Invalid address ${invalid} inside the address array for the specifier key "foo". ` +
+          `Address arrays must only contain strings.`
+        ]
+      );
+    }
+  });
+
+  it('should throw if a scope\'s value is not an object', () => {
+    for (const invalid of nonObjectStrings) {
+      expectBad(`{ "scopes": { "https://scope.example/": ${invalid} } }`, 'https://base.example/');
+    }
+  });
+});
+
+describe('Normalization', () => {
+  it('should normalize empty import maps to have imports and scopes keys', () => {
+    expect(parseFromString(`{}`, 'https://base.example/'))
+      .toEqual({ imports: {}, scopes: {} });
+  });
+
+  it('should normalize an import map without imports to have imports', () => {
+    expect(parseFromString(`{ "scopes": {} }`, 'https://base.example/'))
+      .toEqual({ imports: {}, scopes: {} });
+  });
+
+  it('should normalize an import map without scopes to have scopes', () => {
+    expect(parseFromString(`{ "imports": {} }`, 'https://base.example/'))
+      .toEqual({ imports: {}, scopes: {} });
+  });
+
+  it('should normalize addresses to arrays', () => {
+    expectSpecifierMap(
+      `{
+        "foo": "https://example.com/1",
+        "bar": ["https://example.com/2"],
+        "baz": null
+      }`,
+      'https://base.example/',
+      {
+        foo: [expect.toMatchURL('https://example.com/1')],
+        bar: [expect.toMatchURL('https://example.com/2')],
+        baz: []
+      }
+    );
+  });
+});
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-scope-keys.js b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-scope-keys.js
new file mode 100644
index 0000000..4993f3a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-scope-keys.js
@@ -0,0 +1,144 @@
+'use strict';
+const { expectScopes } = require('./helpers/parsing.js');
+
+describe('Relative URL scope keys', () => {
+  it('should work with no prefix', () => {
+    expectScopes(
+      ['foo'],
+      'https://base.example/path1/path2/path3',
+      ['https://base.example/path1/path2/foo']
+    );
+  });
+
+  it('should work with ./, ../, and / prefixes', () => {
+    expectScopes(
+      ['./foo', '../foo', '/foo'],
+      'https://base.example/path1/path2/path3',
+      [
+        'https://base.example/path1/path2/foo',
+        'https://base.example/path1/foo',
+        'https://base.example/foo'
+      ]
+    );
+  });
+
+  it('should work with /s, ?s, and #s', () => {
+    expectScopes(
+      ['foo/bar?baz#qux'],
+      'https://base.example/path1/path2/path3',
+      ['https://base.example/path1/path2/foo/bar?baz#qux']
+    );
+  });
+
+  it('should work with an empty string scope key', () => {
+    expectScopes(
+      [''],
+      'https://base.example/path1/path2/path3',
+      ['https://base.example/path1/path2/path3']
+    );
+  });
+
+  it('should work with / suffixes', () => {
+    expectScopes(
+      ['foo/', './foo/', '../foo/', '/foo/', '/foo//'],
+      'https://base.example/path1/path2/path3',
+      [
+        'https://base.example/path1/path2/foo/',
+        'https://base.example/path1/path2/foo/',
+        'https://base.example/path1/foo/',
+        'https://base.example/foo/',
+        'https://base.example/foo//'
+      ]
+    );
+  });
+
+  it('should deduplicate based on URL parsing rules', () => {
+    expectScopes(
+      ['foo/\\', 'foo//', 'foo\\\\'],
+      'https://base.example/path1/path2/path3',
+      ['https://base.example/path1/path2/foo//']
+    );
+  });
+});
+
+describe('Absolute URL scope keys', () => {
+  it('should accept all absolute URL scope keys, with or without fetch schemes', () => {
+    expectScopes(
+      [
+        'about:good',
+        'blob:good',
+        'data:good',
+        'file:///good',
+        'filesystem:http://example.com/good/',
+        'http://good/',
+        'https://good/',
+        'ftp://good/',
+        'import:bad',
+        'mailto:bad',
+        'javascript:bad',
+        'wss:ba'
+      ],
+      'https://base.example/path1/path2/path3',
+      [
+        'about:good',
+        'blob:good',
+        'data:good',
+        'file:///good',
+        'filesystem:http://example.com/good/',
+        'http://good/',
+        'https://good/',
+        'ftp://good/',
+        'import:bad',
+        'mailto:bad',
+        'javascript:bad',
+        'wss://ba/'
+      ],
+      []
+    );
+  });
+
+  it('should parse absolute URL scope keys, ignoring unparseable ones', () => {
+    expectScopes(
+      [
+        'https://ex ample.org/',
+        'https://example.com:demo',
+        'http://[www.example.com]/',
+        'https:example.org',
+        'https://///example.com///',
+        'https://example.net',
+        'https://ex%41mple.com/foo/',
+        'https://example.com/%41'
+      ],
+      'https://base.example/path1/path2/path3',
+      [
+        'https://base.example/path1/path2/example.org', // tricky case! remember we have a base URL
+        'https://example.com///',
+        'https://example.net/',
+        'https://example.com/foo/',
+        'https://example.com/%41'
+      ],
+      [
+        'Invalid scope "https://ex ample.org/" (parsed against base URL "https://base.example/path1/path2/path3").',
+        'Invalid scope "https://example.com:demo" (parsed against base URL "https://base.example/path1/path2/path3").',
+        'Invalid scope "http://[www.example.com]/" (parsed against base URL "https://base.example/path1/path2/path3").'
+      ]
+    );
+  });
+
+  it('should ignore relative URL scope keys when the base URL is a data: URL', () => {
+    expectScopes(
+      [
+        './foo',
+        '../foo',
+        '/foo'
+      ],
+      'data:text/html,test',
+      [],
+      [
+        'Invalid scope "./foo" (parsed against base URL "data:text/html,test").',
+        'Invalid scope "../foo" (parsed against base URL "data:text/html,test").',
+        'Invalid scope "/foo" (parsed against base URL "data:text/html,test").'
+      ]
+    );
+  });
+});
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-specifier-keys.js b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-specifier-keys.js
new file mode 100644
index 0000000..9eb423a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/parsing-specifier-keys.js
@@ -0,0 +1,159 @@
+'use strict';
+const { expectSpecifierMap } = require('./helpers/parsing.js');
+const { BUILT_IN_MODULE_SCHEME } = require('../lib/utils.js');
+
+const BLANK = `${BUILT_IN_MODULE_SCHEME}:blank`;
+
+describe('Relative URL-like specifier keys', () => {
+  it('should absolutize strings prefixed with ./, ../, or / into the corresponding URLs', () => {
+    expectSpecifierMap(
+      `{
+        "./foo": "/dotslash",
+        "../foo": "/dotdotslash",
+        "/foo": "/slash"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        'https://base.example/path1/path2/foo': [expect.toMatchURL('https://base.example/dotslash')],
+        'https://base.example/path1/foo': [expect.toMatchURL('https://base.example/dotdotslash')],
+        'https://base.example/foo': [expect.toMatchURL('https://base.example/slash')]
+      }
+    );
+  });
+
+  it('should not absolutize strings prefixed with ./, ../, or / with a data: URL base', () => {
+    expectSpecifierMap(
+      `{
+        "./foo": "https://example.com/dotslash",
+        "../foo": "https://example.com/dotdotslash",
+        "/foo": "https://example.com/slash"
+      }`,
+      'data:text/html,test',
+      {
+        './foo': [expect.toMatchURL('https://example.com/dotslash')],
+        '../foo': [expect.toMatchURL('https://example.com/dotdotslash')],
+        '/foo': [expect.toMatchURL('https://example.com/slash')]
+      }
+    );
+  });
+
+  it('should absolutize the literal strings ./, ../, or / with no suffix', () => {
+    expectSpecifierMap(
+      `{
+        "./": "/dotslash/",
+        "../": "/dotdotslash/",
+        "/": "/slash/"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        'https://base.example/path1/path2/': [expect.toMatchURL('https://base.example/dotslash/')],
+        'https://base.example/path1/': [expect.toMatchURL('https://base.example/dotdotslash/')],
+        'https://base.example/': [expect.toMatchURL('https://base.example/slash/')]
+      }
+    );
+  });
+
+  it('should treat percent-encoded variants of ./, ../, or / as bare specifiers', () => {
+    expectSpecifierMap(
+      `{
+        "%2E/": "/dotSlash1/",
+        "%2E%2E/": "/dotDotSlash1/",
+        ".%2F": "/dotSlash2",
+        "..%2F": "/dotDotSlash2",
+        "%2F": "/slash2",
+        "%2E%2F": "/dotSlash3",
+        "%2E%2E%2F": "/dotDotSlash3"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        '%2E/': [expect.toMatchURL('https://base.example/dotSlash1/')],
+        '%2E%2E/': [expect.toMatchURL('https://base.example/dotDotSlash1/')],
+        '.%2F': [expect.toMatchURL('https://base.example/dotSlash2')],
+        '..%2F': [expect.toMatchURL('https://base.example/dotDotSlash2')],
+        '%2F': [expect.toMatchURL('https://base.example/slash2')],
+        '%2E%2F': [expect.toMatchURL('https://base.example/dotSlash3')],
+        '%2E%2E%2F': [expect.toMatchURL('https://base.example/dotDotSlash3')]
+      }
+    );
+  });
+});
+
+describe('Absolute URL specifier keys', () => {
+  it('should only accept absolute URL specifier keys with fetch schemes, treating others as bare specifiers', () => {
+    expectSpecifierMap(
+      `{
+        "about:good": "/about",
+        "blob:good": "/blob",
+        "data:good": "/data",
+        "file:///good": "/file",
+        "filesystem:good": "/filesystem",
+        "http://good/": "/http/",
+        "https://good/": "/https/",
+        "ftp://good/": "/ftp/",
+        "import:bad": "/import",
+        "mailto:bad": "/mailto",
+        "javascript:bad": "/javascript",
+        "wss:bad": "/wss"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        'about:good': [expect.toMatchURL('https://base.example/about')],
+        'blob:good': [expect.toMatchURL('https://base.example/blob')],
+        'data:good': [expect.toMatchURL('https://base.example/data')],
+        'file:///good': [expect.toMatchURL('https://base.example/file')],
+        'filesystem:good': [expect.toMatchURL('https://base.example/filesystem')],
+        'http://good/': [expect.toMatchURL('https://base.example/http/')],
+        'https://good/': [expect.toMatchURL('https://base.example/https/')],
+        'ftp://good/': [expect.toMatchURL('https://base.example/ftp/')],
+        'import:bad': [expect.toMatchURL('https://base.example/import')],
+        'mailto:bad': [expect.toMatchURL('https://base.example/mailto')],
+        'javascript:bad': [expect.toMatchURL('https://base.example/javascript')],
+        'wss:bad': [expect.toMatchURL('https://base.example/wss')]
+      }
+    );
+  });
+
+  it('should parse absolute URLs, treating unparseable ones as bare specifiers', () => {
+    expectSpecifierMap(
+      `{
+        "https://ex ample.org/": "/unparseable1/",
+        "https://example.com:demo": "/unparseable2",
+        "http://[www.example.com]/": "/unparseable3/",
+        "https:example.org": "/invalidButParseable1/",
+        "https://///example.com///": "/invalidButParseable2/",
+        "https://example.net": "/prettyNormal/",
+        "https://ex%41mple.com/": "/percentDecoding/",
+        "https://example.com/%41": "/noPercentDecoding"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        'https://ex ample.org/': [expect.toMatchURL('https://base.example/unparseable1/')],
+        'https://example.com:demo': [expect.toMatchURL('https://base.example/unparseable2')],
+        'http://[www.example.com]/': [expect.toMatchURL('https://base.example/unparseable3/')],
+        'https://example.org/': [expect.toMatchURL('https://base.example/invalidButParseable1/')],
+        'https://example.com///': [expect.toMatchURL('https://base.example/invalidButParseable2/')],
+        'https://example.net/': [expect.toMatchURL('https://base.example/prettyNormal/')],
+        'https://example.com/': [expect.toMatchURL('https://base.example/percentDecoding/')],
+        'https://example.com/%41': [expect.toMatchURL('https://base.example/noPercentDecoding')]
+      }
+    );
+  });
+
+  it('should parse built-in module specifier keys, including with a "/"', () => {
+    expectSpecifierMap(
+      `{
+        "${BLANK}": "/blank",
+        "${BLANK}/": "/blank/",
+        "${BLANK}/foo": "/blank/foo",
+        "${BLANK}\\\\foo": "/blank/backslashfoo"
+      }`,
+      'https://base.example/path1/path2/path3',
+      {
+        [BLANK]: [expect.toMatchURL('https://base.example/blank')],
+        [`${BLANK}/`]: [expect.toMatchURL('https://base.example/blank/')],
+        [`${BLANK}/foo`]: [expect.toMatchURL('https://base.example/blank/foo')],
+        [`${BLANK}\\foo`]: [expect.toMatchURL('https://base.example/blank/backslashfoo')]
+      }
+    );
+  });
+});
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving-builtins.js b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/resolving-builtins.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving-builtins.js
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/resolving-builtins.js
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving-not-yet-implemented.js b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/resolving-not-yet-implemented.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving-not-yet-implemented.js
rename to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/resolving-not-yet-implemented.js
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/resolving-scopes.js b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/resolving-scopes.js
new file mode 100644
index 0000000..ca19a66
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/resolving-scopes.js
@@ -0,0 +1,230 @@
+'use strict';
+const { URL } = require('url');
+const { parseFromString } = require('../lib/parser.js');
+const { resolve } = require('../lib/resolver.js');
+
+const mapBaseURL = new URL('https://example.com/app/index.html');
+
+function makeResolveUnderTest(mapString) {
+  const map = parseFromString(mapString, mapBaseURL);
+  return (specifier, baseURL) => resolve(specifier, map, baseURL);
+}
+
+describe('Mapped using scope instead of "imports"', () => {
+  const jsNonDirURL = new URL('https://example.com/js');
+  const jsPrefixedURL = new URL('https://example.com/jsiscool');
+  const inJSDirURL = new URL('https://example.com/js/app.mjs');
+  const topLevelURL = new URL('https://example.com/app.mjs');
+
+  it('should fail when the mapping is to an empty array', () => {
+    const resolveUnderTest = makeResolveUnderTest(`{
+      "scopes": {
+        "/js/": {
+          "moment": null,
+          "lodash": []
+        }
+      }
+    }`);
+
+    expect(() => resolveUnderTest('moment', inJSDirURL)).toThrow(TypeError);
+    expect(() => resolveUnderTest('lodash', inJSDirURL)).toThrow(TypeError);
+  });
+
+  describe('Exact vs. prefix based matching', () => {
+    it('should match correctly when both are in the map', () => {
+      const resolveUnderTest = makeResolveUnderTest(`{
+        "scopes": {
+          "/js": {
+            "moment": "/only-triggered-by-exact/moment",
+            "moment/": "/only-triggered-by-exact/moment/"
+          },
+          "/js/": {
+            "moment": "/triggered-by-any-subpath/moment",
+            "moment/": "/triggered-by-any-subpath/moment/"
+          }
+        }
+      }`);
+
+      expect(resolveUnderTest('moment', jsNonDirURL)).toMatchURL('https://example.com/only-triggered-by-exact/moment');
+      expect(resolveUnderTest('moment/foo', jsNonDirURL)).toMatchURL('https://example.com/only-triggered-by-exact/moment/foo');
+
+      expect(resolveUnderTest('moment', inJSDirURL)).toMatchURL('https://example.com/triggered-by-any-subpath/moment');
+      expect(resolveUnderTest('moment/foo', inJSDirURL)).toMatchURL('https://example.com/triggered-by-any-subpath/moment/foo');
+
+      expect(() => resolveUnderTest('moment', jsPrefixedURL)).toThrow(TypeError);
+      expect(() => resolveUnderTest('moment/foo', jsPrefixedURL)).toThrow(TypeError);
+    });
+
+    it('should match correctly when only an exact match is in the map', () => {
+      const resolveUnderTest = makeResolveUnderTest(`{
+        "scopes": {
+          "/js": {
+            "moment": "/only-triggered-by-exact/moment",
+            "moment/": "/only-triggered-by-exact/moment/"
+          }
+        }
+      }`);
+
+      expect(resolveUnderTest('moment', jsNonDirURL)).toMatchURL('https://example.com/only-triggered-by-exact/moment');
+      expect(resolveUnderTest('moment/foo', jsNonDirURL)).toMatchURL('https://example.com/only-triggered-by-exact/moment/foo');
+
+      expect(() => resolveUnderTest('moment', inJSDirURL)).toThrow(TypeError);
+      expect(() => resolveUnderTest('moment/foo', inJSDirURL)).toThrow(TypeError);
+
+      expect(() => resolveUnderTest('moment', jsPrefixedURL)).toThrow(TypeError);
+      expect(() => resolveUnderTest('moment/foo', jsPrefixedURL)).toThrow(TypeError);
+    });
+
+    it('should match correctly when only a prefix match is in the map', () => {
+      const resolveUnderTest = makeResolveUnderTest(`{
+        "scopes": {
+          "/js/": {
+            "moment": "/triggered-by-any-subpath/moment",
+            "moment/": "/triggered-by-any-subpath/moment/"
+          }
+        }
+      }`);
+
+      expect(() => resolveUnderTest('moment', jsNonDirURL)).toThrow(TypeError);
+      expect(() => resolveUnderTest('moment/foo', jsNonDirURL)).toThrow(TypeError);
+
+      expect(resolveUnderTest('moment', inJSDirURL)).toMatchURL('https://example.com/triggered-by-any-subpath/moment');
+      expect(resolveUnderTest('moment/foo', inJSDirURL)).toMatchURL('https://example.com/triggered-by-any-subpath/moment/foo');
+
+      expect(() => resolveUnderTest('moment', jsPrefixedURL)).toThrow(TypeError);
+      expect(() => resolveUnderTest('moment/foo', jsPrefixedURL)).toThrow(TypeError);
+    });
+  });
+
+  describe('Package-like scenarios', () => {
+    const resolveUnderTest = makeResolveUnderTest(`{
+      "imports": {
+        "moment": "/node_modules/moment/src/moment.js",
+        "moment/": "/node_modules/moment/src/",
+        "lodash-dot": "./node_modules/lodash-es/lodash.js",
+        "lodash-dot/": "./node_modules/lodash-es/",
+        "lodash-dotdot": "../node_modules/lodash-es/lodash.js",
+        "lodash-dotdot/": "../node_modules/lodash-es/"
+      },
+      "scopes": {
+        "/": {
+          "moment": "/node_modules_3/moment/src/moment.js",
+          "vue": "/node_modules_3/vue/dist/vue.runtime.esm.js"
+        },
+        "/js/": {
+          "lodash-dot": "./node_modules_2/lodash-es/lodash.js",
+          "lodash-dot/": "./node_modules_2/lodash-es/",
+          "lodash-dotdot": "../node_modules_2/lodash-es/lodash.js",
+          "lodash-dotdot/": "../node_modules_2/lodash-es/"
+        }
+      }
+    }`);
+
+    it('should resolve scoped', () => {
+      expect(resolveUnderTest('lodash-dot', inJSDirURL)).toMatchURL('https://example.com/app/node_modules_2/lodash-es/lodash.js');
+      expect(resolveUnderTest('lodash-dotdot', inJSDirURL)).toMatchURL('https://example.com/node_modules_2/lodash-es/lodash.js');
+      expect(resolveUnderTest('lodash-dot/foo', inJSDirURL)).toMatchURL('https://example.com/app/node_modules_2/lodash-es/foo');
+      expect(resolveUnderTest('lodash-dotdot/foo', inJSDirURL)).toMatchURL('https://example.com/node_modules_2/lodash-es/foo');
+    });
+
+    it('should apply best scope match', () => {
+      expect(resolveUnderTest('moment', topLevelURL)).toMatchURL('https://example.com/node_modules_3/moment/src/moment.js');
+      expect(resolveUnderTest('moment', inJSDirURL)).toMatchURL('https://example.com/node_modules_3/moment/src/moment.js');
+      expect(resolveUnderTest('vue', inJSDirURL)).toMatchURL('https://example.com/node_modules_3/vue/dist/vue.runtime.esm.js');
+    });
+
+    it('should fallback to "imports"', () => {
+      expect(resolveUnderTest('moment/foo', topLevelURL)).toMatchURL('https://example.com/node_modules/moment/src/foo');
+      expect(resolveUnderTest('moment/foo', inJSDirURL)).toMatchURL('https://example.com/node_modules/moment/src/foo');
+      expect(resolveUnderTest('lodash-dot', topLevelURL)).toMatchURL('https://example.com/app/node_modules/lodash-es/lodash.js');
+      expect(resolveUnderTest('lodash-dotdot', topLevelURL)).toMatchURL('https://example.com/node_modules/lodash-es/lodash.js');
+      expect(resolveUnderTest('lodash-dot/foo', topLevelURL)).toMatchURL('https://example.com/app/node_modules/lodash-es/foo');
+      expect(resolveUnderTest('lodash-dotdot/foo', topLevelURL)).toMatchURL('https://example.com/node_modules/lodash-es/foo');
+    });
+
+    it('should still fail for package-like specifiers that are not declared', () => {
+      expect(() => resolveUnderTest('underscore/', inJSDirURL)).toThrow(TypeError);
+      expect(() => resolveUnderTest('underscore/foo', inJSDirURL)).toThrow(TypeError);
+    });
+  });
+
+  describe('The scope inheritance example from the README', () => {
+    const resolveUnderTest = makeResolveUnderTest(`{
+      "imports": {
+        "a": "/a-1.mjs",
+        "b": "/b-1.mjs",
+        "c": "/c-1.mjs"
+      },
+      "scopes": {
+        "/scope2/": {
+          "a": "/a-2.mjs"
+        },
+        "/scope2/scope3/": {
+          "b": "/b-3.mjs"
+        }
+      }
+    }`);
+
+    const scope1URL = new URL('https://example.com/scope1/foo.mjs');
+    const scope2URL = new URL('https://example.com/scope2/foo.mjs');
+    const scope3URL = new URL('https://example.com/scope2/scope3/foo.mjs');
+
+    it('should fall back to "imports" when none match', () => {
+      expect(resolveUnderTest('a', scope1URL)).toMatchURL('https://example.com/a-1.mjs');
+      expect(resolveUnderTest('b', scope1URL)).toMatchURL('https://example.com/b-1.mjs');
+      expect(resolveUnderTest('c', scope1URL)).toMatchURL('https://example.com/c-1.mjs');
+    });
+
+    it('should use a direct scope override', () => {
+      expect(resolveUnderTest('a', scope2URL)).toMatchURL('https://example.com/a-2.mjs');
+      expect(resolveUnderTest('b', scope2URL)).toMatchURL('https://example.com/b-1.mjs');
+      expect(resolveUnderTest('c', scope2URL)).toMatchURL('https://example.com/c-1.mjs');
+    });
+
+    it('should use an indirect scope override', () => {
+      expect(resolveUnderTest('a', scope3URL)).toMatchURL('https://example.com/a-2.mjs');
+      expect(resolveUnderTest('b', scope3URL)).toMatchURL('https://example.com/b-3.mjs');
+      expect(resolveUnderTest('c', scope3URL)).toMatchURL('https://example.com/c-1.mjs');
+    });
+  });
+
+  describe('Relative URL scope keys', () => {
+    const resolveUnderTest = makeResolveUnderTest(`{
+      "imports": {
+        "a": "/a-1.mjs",
+        "b": "/b-1.mjs",
+        "c": "/c-1.mjs"
+      },
+      "scopes": {
+        "": {
+          "a": "/a-empty-string.mjs"
+        },
+        "./": {
+          "b": "/b-dot-slash.mjs"
+        },
+        "../": {
+          "c": "/c-dot-dot-slash.mjs"
+        }
+      }
+    }`);
+    const inSameDirAsMap = new URL('./foo.mjs', mapBaseURL);
+    const inDirAboveMap = new URL('../foo.mjs', mapBaseURL);
+
+    it('should resolve an empty string scope using the import map URL', () => {
+      expect(resolveUnderTest('a', mapBaseURL)).toMatchURL('https://example.com/a-empty-string.mjs');
+      expect(resolveUnderTest('a', inSameDirAsMap)).toMatchURL('https://example.com/a-1.mjs');
+    });
+
+    it('should resolve a ./ scope using the import map URL\'s directory', () => {
+      expect(resolveUnderTest('b', mapBaseURL)).toMatchURL('https://example.com/b-dot-slash.mjs');
+      expect(resolveUnderTest('b', inSameDirAsMap)).toMatchURL('https://example.com/b-dot-slash.mjs');
+    });
+
+    it('should resolve a ../ scope using the import map URL\'s directory', () => {
+      expect(resolveUnderTest('c', mapBaseURL)).toMatchURL('https://example.com/c-dot-dot-slash.mjs');
+      expect(resolveUnderTest('c', inSameDirAsMap)).toMatchURL('https://example.com/c-dot-dot-slash.mjs');
+      expect(resolveUnderTest('c', inDirAboveMap)).toMatchURL('https://example.com/c-dot-dot-slash.mjs');
+    });
+  });
+});
+
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/resolving.js b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/resolving.js
new file mode 100644
index 0000000..29ee31cc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/imported/resources/resolving.js
@@ -0,0 +1,270 @@
+'use strict';
+const { URL } = require('url');
+const { parseFromString } = require('../lib/parser.js');
+const { resolve } = require('../lib/resolver.js');
+
+const mapBaseURL = new URL('https://example.com/app/index.html');
+const scriptURL = new URL('https://example.com/js/app.mjs');
+
+function makeResolveUnderTest(mapString) {
+  const map = parseFromString(mapString, mapBaseURL);
+  return specifier => resolve(specifier, map, scriptURL);
+}
+
+describe('Unmapped', () => {
+  const resolveUnderTest = makeResolveUnderTest(`{}`);
+
+  it('should resolve ./ specifiers as URLs', () => {
+    expect(resolveUnderTest('./foo')).toMatchURL('https://example.com/js/foo');
+    expect(resolveUnderTest('./foo/bar')).toMatchURL('https://example.com/js/foo/bar');
+    expect(resolveUnderTest('./foo/../bar')).toMatchURL('https://example.com/js/bar');
+    expect(resolveUnderTest('./foo/../../bar')).toMatchURL('https://example.com/bar');
+  });
+
+  it('should resolve ../ specifiers as URLs', () => {
+    expect(resolveUnderTest('../foo')).toMatchURL('https://example.com/foo');
+    expect(resolveUnderTest('../foo/bar')).toMatchURL('https://example.com/foo/bar');
+    expect(resolveUnderTest('../../../foo/bar')).toMatchURL('https://example.com/foo/bar');
+  });
+
+  it('should resolve / specifiers as URLs', () => {
+    expect(resolveUnderTest('/foo')).toMatchURL('https://example.com/foo');
+    expect(resolveUnderTest('/foo/bar')).toMatchURL('https://example.com/foo/bar');
+    expect(resolveUnderTest('/../../foo/bar')).toMatchURL('https://example.com/foo/bar');
+    expect(resolveUnderTest('/../foo/../bar')).toMatchURL('https://example.com/bar');
+  });
+
+  it('should parse absolute fetch-scheme URLs', () => {
+    expect(resolveUnderTest('about:good')).toMatchURL('about:good');
+    expect(resolveUnderTest('https://example.net')).toMatchURL('https://example.net/');
+    expect(resolveUnderTest('https://ex%41mple.com/')).toMatchURL('https://example.com/');
+    expect(resolveUnderTest('https:example.org')).toMatchURL('https://example.org/');
+    expect(resolveUnderTest('https://///example.com///')).toMatchURL('https://example.com///');
+  });
+
+  it('should fail for absolute non-fetch-scheme URLs', () => {
+    expect(() => resolveUnderTest('mailto:bad')).toThrow(TypeError);
+    expect(() => resolveUnderTest('import:bad')).toThrow(TypeError);
+    expect(() => resolveUnderTest('javascript:bad')).toThrow(TypeError);
+    expect(() => resolveUnderTest('wss:bad')).toThrow(TypeError);
+  });
+
+  it('should fail for strings not parseable as absolute URLs and not starting with ./ ../ or /', () => {
+    expect(() => resolveUnderTest('foo')).toThrow(TypeError);
+    expect(() => resolveUnderTest('\\foo')).toThrow(TypeError);
+    expect(() => resolveUnderTest(':foo')).toThrow(TypeError);
+    expect(() => resolveUnderTest('@foo')).toThrow(TypeError);
+    expect(() => resolveUnderTest('%2E/foo')).toThrow(TypeError);
+    expect(() => resolveUnderTest('%2E%2E/foo')).toThrow(TypeError);
+    expect(() => resolveUnderTest('.%2Ffoo')).toThrow(TypeError);
+    expect(() => resolveUnderTest('https://ex ample.org/')).toThrow(TypeError);
+    expect(() => resolveUnderTest('https://example.com:demo')).toThrow(TypeError);
+    expect(() => resolveUnderTest('http://[www.example.com]/')).toThrow(TypeError);
+  });
+});
+
+describe('Mapped using the "imports" key only (no scopes)', () => {
+  it('should fail when the mapping is to an empty array', () => {
+    const resolveUnderTest = makeResolveUnderTest(`{
+      "imports": {
+        "moment": null,
+        "lodash": []
+      }
+    }`);
+
+    expect(() => resolveUnderTest('moment')).toThrow(TypeError);
+    expect(() => resolveUnderTest('lodash')).toThrow(TypeError);
+  });
+
+  describe('Package-like scenarios', () => {
+    const resolveUnderTest = makeResolveUnderTest(`{
+      "imports": {
+        "moment": "/node_modules/moment/src/moment.js",
+        "moment/": "/node_modules/moment/src/",
+        "lodash-dot": "./node_modules/lodash-es/lodash.js",
+        "lodash-dot/": "./node_modules/lodash-es/",
+        "lodash-dotdot": "../node_modules/lodash-es/lodash.js",
+        "lodash-dotdot/": "../node_modules/lodash-es/",
+        "nowhere/": []
+      }
+    }`);
+
+    it('should work for package main modules', () => {
+      expect(resolveUnderTest('moment')).toMatchURL('https://example.com/node_modules/moment/src/moment.js');
+      expect(resolveUnderTest('lodash-dot')).toMatchURL('https://example.com/app/node_modules/lodash-es/lodash.js');
+      expect(resolveUnderTest('lodash-dotdot')).toMatchURL('https://example.com/node_modules/lodash-es/lodash.js');
+    });
+
+    it('should work for package submodules', () => {
+      expect(resolveUnderTest('moment/foo')).toMatchURL('https://example.com/node_modules/moment/src/foo');
+      expect(resolveUnderTest('lodash-dot/foo')).toMatchURL('https://example.com/app/node_modules/lodash-es/foo');
+      expect(resolveUnderTest('lodash-dotdot/foo')).toMatchURL('https://example.com/node_modules/lodash-es/foo');
+    });
+
+    it('should work for package names that end in a slash by just passing through', () => {
+      // TODO: is this the right behavior, or should we throw?
+      expect(resolveUnderTest('moment/')).toMatchURL('https://example.com/node_modules/moment/src/');
+    });
+
+    it('should still fail for package modules that are not declared', () => {
+      expect(() => resolveUnderTest('underscore/')).toThrow(TypeError);
+      expect(() => resolveUnderTest('underscore/foo')).toThrow(TypeError);
+    });
+
+    it('should fail for package submodules that map to nowhere', () => {
+      expect(() => resolveUnderTest('nowhere/foo')).toThrow(TypeError);
+    });
+  });
+
+  describe('Tricky specifiers', () => {
+    const resolveUnderTest = makeResolveUnderTest(`{
+      "imports": {
+        "package/withslash": "/node_modules/package-with-slash/index.mjs",
+        "not-a-package": "/lib/not-a-package.mjs",
+        ".": "/lib/dot.mjs",
+        "..": "/lib/dotdot.mjs",
+        "..\\\\": "/lib/dotdotbackslash.mjs",
+        "%2E": "/lib/percent2e.mjs",
+        "%2F": "/lib/percent2f.mjs"
+      }
+    }`);
+
+    it('should work for explicitly-mapped specifiers that happen to have a slash', () => {
+      expect(resolveUnderTest('package/withslash')).toMatchURL('https://example.com/node_modules/package-with-slash/index.mjs');
+    });
+
+    it('should work when the specifier has punctuation', () => {
+      expect(resolveUnderTest('.')).toMatchURL('https://example.com/lib/dot.mjs');
+      expect(resolveUnderTest('..')).toMatchURL('https://example.com/lib/dotdot.mjs');
+      expect(resolveUnderTest('..\\')).toMatchURL('https://example.com/lib/dotdotbackslash.mjs');
+      expect(resolveUnderTest('%2E')).toMatchURL('https://example.com/lib/percent2e.mjs');
+      expect(resolveUnderTest('%2F')).toMatchURL('https://example.com/lib/percent2f.mjs');
+    });
+
+    it('should fail for attempting to get a submodule of something not declared with a trailing slash', () => {
+      expect(() => resolveUnderTest('not-a-package/foo')).toThrow(TypeError);
+    });
+  });
+
+  describe('URL-like specifiers', () => {
+    const resolveUnderTest = makeResolveUnderTest(`{
+      "imports": {
+        "/node_modules/als-polyfill/index.mjs": "std:kv-storage",
+
+        "/lib/foo.mjs": "./more/bar.mjs",
+        "./dotrelative/foo.mjs": "/lib/dot.mjs",
+        "../dotdotrelative/foo.mjs": "/lib/dotdot.mjs",
+
+        "/lib/no.mjs": null,
+        "./dotrelative/no.mjs": [],
+
+        "/": "/lib/slash-only/",
+        "./": "/lib/dotslash-only/",
+
+        "/test/": "/lib/url-trailing-slash/",
+        "./test/": "/lib/url-trailing-slash-dot/",
+
+        "/test": "/lib/test1.mjs",
+        "../test": "/lib/test2.mjs"
+      }
+    }`);
+
+    it('should remap to other URLs', () => {
+      expect(resolveUnderTest('https://example.com/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs');
+      expect(resolveUnderTest('https://///example.com/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs');
+      expect(resolveUnderTest('/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs');
+
+      expect(resolveUnderTest('https://example.com/app/dotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dot.mjs');
+      expect(resolveUnderTest('../app/dotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dot.mjs');
+
+      expect(resolveUnderTest('https://example.com/dotdotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dotdot.mjs');
+      expect(resolveUnderTest('../dotdotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dotdot.mjs');
+    });
+
+    it('should fail for URLs that remap to empty arrays', () => {
+      expect(() => resolveUnderTest('https://example.com/lib/no.mjs')).toThrow(TypeError);
+      expect(() => resolveUnderTest('/lib/no.mjs')).toThrow(TypeError);
+      expect(() => resolveUnderTest('../lib/no.mjs')).toThrow(TypeError);
+
+      expect(() => resolveUnderTest('https://example.com/app/dotrelative/no.mjs')).toThrow(TypeError);
+      expect(() => resolveUnderTest('/app/dotrelative/no.mjs')).toThrow(TypeError);
+      expect(() => resolveUnderTest('../app/dotrelative/no.mjs')).toThrow(TypeError);
+    });
+
+    it('should remap URLs that are just composed from / and .', () => {
+      expect(resolveUnderTest('https://example.com/')).toMatchURL('https://example.com/lib/slash-only/');
+      expect(resolveUnderTest('/')).toMatchURL('https://example.com/lib/slash-only/');
+      expect(resolveUnderTest('../')).toMatchURL('https://example.com/lib/slash-only/');
+
+      expect(resolveUnderTest('https://example.com/app/')).toMatchURL('https://example.com/lib/dotslash-only/');
+      expect(resolveUnderTest('/app/')).toMatchURL('https://example.com/lib/dotslash-only/');
+      expect(resolveUnderTest('../app/')).toMatchURL('https://example.com/lib/dotslash-only/');
+    });
+
+    it('should remap URLs that are prefix-matched by keys with trailing slashes', () => {
+      expect(resolveUnderTest('/test/foo.mjs')).toMatchURL('https://example.com/lib/url-trailing-slash/foo.mjs');
+      expect(resolveUnderTest('https://example.com/app/test/foo.mjs')).toMatchURL('https://example.com/lib/url-trailing-slash-dot/foo.mjs');
+    });
+
+    it('should use the last entry\'s address when URL-like specifiers parse to the same absolute URL', () => {
+      expect(resolveUnderTest('/test')).toMatchURL('https://example.com/lib/test2.mjs');
+    });
+  });
+
+  describe('Overlapping entries with trailing slashes', () => {
+    it('should favor the most-specific key (no empty arrays)', () => {
+      const resolveUnderTest = makeResolveUnderTest(`{
+        "imports": {
+          "a": "/1",
+          "a/": "/2/",
+          "a/b": "/3",
+          "a/b/": "/4/"
+        }
+      }`);
+
+      expect(resolveUnderTest('a')).toMatchURL('https://example.com/1');
+      expect(resolveUnderTest('a/')).toMatchURL('https://example.com/2/');
+      expect(resolveUnderTest('a/b')).toMatchURL('https://example.com/3');
+      expect(resolveUnderTest('a/b/')).toMatchURL('https://example.com/4/');
+      expect(resolveUnderTest('a/b/c')).toMatchURL('https://example.com/4/c');
+    });
+
+    it('should favor the most-specific key when empty arrays are involved for less-specific keys', () => {
+      const resolveUnderTest = makeResolveUnderTest(`{
+        "imports": {
+          "a": [],
+          "a/": [],
+          "a/b": "/3",
+          "a/b/": "/4/"
+        }
+      }`);
+
+      expect(() => resolveUnderTest('a')).toThrow(TypeError);
+      expect(() => resolveUnderTest('a/')).toThrow(TypeError);
+      expect(() => resolveUnderTest('a/x')).toThrow(TypeError);
+      expect(resolveUnderTest('a/b')).toMatchURL('https://example.com/3');
+      expect(resolveUnderTest('a/b/')).toMatchURL('https://example.com/4/');
+      expect(resolveUnderTest('a/b/c')).toMatchURL('https://example.com/4/c');
+      expect(() => resolveUnderTest('a/x/c')).toThrow(TypeError);
+    });
+
+    it('should favor the most-specific key when empty arrays are involved for more-specific keys', () => {
+      const resolveUnderTest = makeResolveUnderTest(`{
+        "imports": {
+          "a": "/1",
+          "a/": "/2/",
+          "a/b": [],
+          "a/b/": []
+        }
+      }`);
+
+      expect(resolveUnderTest('a')).toMatchURL('https://example.com/1');
+      expect(resolveUnderTest('a/')).toMatchURL('https://example.com/2/');
+      expect(resolveUnderTest('a/x')).toMatchURL('https://example.com/2/x');
+      expect(() => resolveUnderTest('a/b')).toThrow(TypeError);
+      expect(() => resolveUnderTest('a/b/')).toThrow(TypeError);
+      expect(() => resolveUnderTest('a/b/c')).toThrow(TypeError);
+      expect(resolveUnderTest('a/x/c')).toMatchURL('https://example.com/2/x/c');
+    });
+  });
+});
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/static-import.js b/third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/static-import.js
similarity index 100%
copy from third_party/blink/web_tests/external/wpt/import-maps/static-import.js
copy to third_party/blink/web_tests/external/wpt/import-maps/builtin-support.tentative/static-import.js
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare.sub.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/core/bare.sub.tentative.html
similarity index 62%
copy from third_party/blink/web_tests/external/wpt/import-maps/bare.sub.tentative.html
copy to third_party/blink/web_tests/external/wpt/import-maps/core/bare.sub.tentative.html
index cf99589..7fb769e 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/bare.sub.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/core/bare.sub.tentative.html
@@ -2,28 +2,18 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/test-helper.js"></script>
+<script src="../resources/test-helper.js"></script>
 
 <script>
 // "bare/..." (i.e. without leading "./") are bare specifiers
 // (not relative paths).
-//
-// Discussions about notations for builtin modules are ongoing, e.g.
-// https://github.com/tc39/proposal-javascript-standard-library/issues/12
-// Currently the tests expects two notations are accepted.
-// TODO: Once the discussions converge, update the tests.
 const importMap = `
 {
   "imports": {
-    "bare/bare": "./resources/log.js?pipe=sub&name=bare",
+    "bare/bare": "../resources/log.js?pipe=sub&name=bare",
     "bare/cross-origin-bare": "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-bare",
     "bare/to-data": "data:text/javascript,log.push('dataURL')",
 
-    "bare/std-blank": "std:blank",
-    "bare/blank": "@std/blank",
-    "bare/std-none": "std:none",
-    "bare/none": "@std/none",
-
     "bare/to-bare": "bare/bare"
   }
 }
@@ -51,16 +41,6 @@
   "bare/to-data":
     [Result.URL, Result.URL, "dataURL", "dataURL"],
 
-  // Bare to built-in.
-  "bare/std-blank":
-    [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
-  "bare/blank":
-    [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
-  "bare/std-none":
-    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
-  "bare/none":
-    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
-
   // Bare to bare mapping is disabled.
   "bare/to-bare":
     [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare/__dir__.headers b/third_party/blink/web_tests/external/wpt/import-maps/core/bare/__dir__.headers
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/bare/__dir__.headers
rename to third_party/blink/web_tests/external/wpt/import-maps/core/bare/__dir__.headers
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare/bare b/third_party/blink/web_tests/external/wpt/import-maps/core/bare/bare
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/bare/bare
rename to third_party/blink/web_tests/external/wpt/import-maps/core/bare/bare
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare/cross-origin-bare b/third_party/blink/web_tests/external/wpt/import-maps/core/bare/cross-origin-bare
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/bare/cross-origin-bare
rename to third_party/blink/web_tests/external/wpt/import-maps/core/bare/cross-origin-bare
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare/to-bare b/third_party/blink/web_tests/external/wpt/import-maps/core/bare/to-bare
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/bare/to-bare
rename to third_party/blink/web_tests/external/wpt/import-maps/core/bare/to-bare
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/bare/to-data b/third_party/blink/web_tests/external/wpt/import-maps/core/bare/to-data
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/bare/to-data
rename to third_party/blink/web_tests/external/wpt/import-maps/core/bare/to-data
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/core/data.sub.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/core/data.sub.tentative.html
new file mode 100644
index 0000000..25c18c45
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/core/data.sub.tentative.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helper.js"></script>
+
+<script>
+// "bare/..." (i.e. without leading "./") are bare specifiers
+// (not relative paths).
+const importMap = `
+{
+  "imports": {
+    "bare": "../resources/log.js?pipe=sub&name=bare",
+
+    "data:text/javascript,log.push('data:foo')": "../resources/log.js?pipe=sub&name=foo",
+    "data:text/javascript,log.push('data:cross-origin-foo')": "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-foo",
+    "data:text/javascript,log.push('data:to-data')": "data:text/javascript,log.push('dataURL')",
+
+    "data:text/javascript,log.push('data:to-bare')": "bare"
+  }
+}
+`;
+
+const tests = {
+  // Arrays of expected results for:
+  // - <script src type="module">,
+  // - <script src> (classic script),
+  // - static import, and
+  // - dynamic import.
+
+  // Currently, Chromium's implementation resolves import maps as a part of
+  // specifier resolution, and thus failure in import map resolution causes
+  // a parse error, not fetch error. Therefore, we use Result.PARSE_ERROR
+  // below. https://crbug.com/928435
+
+  // data: to HTTP(S).
+  "data:text/javascript,log.push('data:foo')":
+    [Result.URL, Result.URL, "log:foo", "log:foo"],
+  "data:text/javascript,log.push('data:cross-origin-foo')":
+    [Result.URL, Result.URL, "log:cross-origin-foo", "log:cross-origin-foo"],
+
+  // data: to data:
+  "data:text/javascript,log.push('data:to-data')":
+    [Result.URL, Result.URL, "dataURL", "dataURL"],
+
+  // data: to bare mapping is disabled.
+  "data:text/javascript,log.push('data:to-bare')":
+    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
+};
+
+doTests(importMap, null, tests);
+</script>
+<body>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/core/http.sub.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/core/http.sub.tentative.html
new file mode 100644
index 0000000..a33a094
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/core/http.sub.tentative.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helper.js"></script>
+
+<script>
+// "bare/..." (i.e. without leading "./") are bare specifiers
+// (not relative paths).
+const importMap = `
+{
+  "imports": {
+    "bare": "../resources/log.js?pipe=sub&name=bare",
+
+    "../resources/log.js?pipe=sub&name=foo": "../resources/log.js?pipe=sub&name=bar",
+    "../resources/log.js?pipe=sub&name=cross-origin-foo": "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-bar",
+    "../resources/log.js?pipe=sub&name=to-data": "data:text/javascript,log.push('dataURL')",
+
+    "../resources/log.js?pipe=sub&name=to-bare": "bare"
+  }
+}
+`;
+
+const tests = {
+  // Arrays of expected results for:
+  // - <script src type="module">,
+  // - <script src> (classic script),
+  // - static import, and
+  // - dynamic import.
+
+  // Currently, Chromium's implementation resolves import maps as a part of
+  // specifier resolution, and thus failure in import map resolution causes
+  // a parse error, not fetch error. Therefore, we use Result.PARSE_ERROR
+  // below. https://crbug.com/928435
+
+  // HTTP(S) to HTTP(S).
+  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=foo":
+    [Result.URL, Result.URL, "log:bar", "log:bar"],
+  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-foo":
+    [Result.URL, Result.URL, "log:cross-origin-bar", "log:cross-origin-bar"],
+
+  // HTTP(S) to data:
+  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=to-data":
+    [Result.URL, Result.URL, "dataURL", "dataURL"],
+
+  // HTTP(S) to bare mapping is disabled.
+  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=to-bare":
+    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
+};
+
+doTests(importMap, null, tests);
+</script>
+<body>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/module-map-key.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/core/module-map-key.tentative.html
similarity index 75%
rename from third_party/blink/web_tests/external/wpt/import-maps/module-map-key.tentative.html
rename to third_party/blink/web_tests/external/wpt/import-maps/core/module-map-key.tentative.html
index 13bd122..6f2f18a 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/module-map-key.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/core/module-map-key.tentative.html
@@ -5,7 +5,7 @@
 <script type="importmap">
 {
   "imports": {
-    "./resources/log.js?pipe=sub&name=A": "./resources/log.js?pipe=sub&name=B"
+    "../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
   }
 }
 </script>
@@ -17,8 +17,8 @@
 // key will become the URL/specifier BEFORE import map resolution.
 // https://crbug.com/928435
 promise_test(() => {
-  return import("./resources/log.js?pipe=sub&name=A")
-    .then(() => import("./resources/log.js?pipe=sub&name=B"))
+  return import("../resources/log.js?pipe=sub&name=A")
+    .then(() => import("../resources/log.js?pipe=sub&name=B"))
     .then(() => assert_array_equals(log, ["log:B"]))
   },
   "Module map's key is the URL after import map resolution");
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/static-import.js b/third_party/blink/web_tests/external/wpt/import-maps/core/static-import.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/import-maps/static-import.js
rename to third_party/blink/web_tests/external/wpt/import-maps/core/static-import.js
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/fallback-disallowed.sub.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/fallback-disallowed.sub.tentative.html
deleted file mode 100644
index 280d02d..0000000
--- a/third_party/blink/web_tests/external/wpt/import-maps/fallback-disallowed.sub.tentative.html
+++ /dev/null
@@ -1,80 +0,0 @@
-<!DOCTYPE html>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/test-helper.js"></script>
-
-<script>
-// Fallbacks from external URLs (such as HTTPS URLs) are
-// blocked by ongoing spec discussions, for example
-// https://github.com/WICG/import-maps/issues/76.
-// https://crbug.com/928435
-//
-// This test, as well as Chromium's implementation, rejects broader range of
-// fallbacks (not only those from HTTPS), to avoid potential spec and
-// interoperability issues.
-// The only allowed fallback pattern is fallbacks from bare specifiers with
-// two elements, which are listed in fallback.sub.tentative.html.
-const importMap = `
-{
-  "imports": {
-    "bare": "./resources/log.js?pipe=sub&name=bare",
-
-    "./resources/log.js?pipe=sub&name=http-to-builtin": [
-      "./resources/log.js?pipe=sub&name=http-to-builtin",
-      "@std/blank"
-    ],
-
-    "./resources/log.js?pipe=sub&name=fallback-to-different-url-1": [
-      "@std/blank",
-      "./resources/log.js?pipe=sub&name=something-different"
-    ],
-    "./resources/log.js?pipe=sub&name=fallback-to-different-url-2": [
-      "@std/none",
-      "./resources/log.js?pipe=sub&name=something-different2"
-    ],
-    "./resources/log.js?pipe=sub&name=fallback-to-different-origin-1": [
-      "@std/blank",
-      "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=fallback-to-different-origin-1"
-    ],
-    "./resources/log.js?pipe=sub&name=fallback-to-different-origin-2": [
-      "@std/none",
-      "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=fallback-to-different-origin-2"
-    ],
-
-    "./resources/log.js?pipe=sub&name=more-than-two-values-1": [
-      "@std/none",
-      "@std/blank",
-      "./resources/log.js?pipe=sub&name=more-than-two-values-1"
-    ],
-    "./resources/log.js?pipe=sub&name=more-than-two-values-2": [
-      "@std/none",
-      "./resources/log.js?pipe=sub&name=more-than-two-values-2",
-      "@std/blank"
-    ],
-    "./resources/log.js?pipe=sub&name=fallback-from-http": [
-      "./resources/log.js?pipe=sub&name=non-built-in",
-      "./resources/log.js?pipe=sub&name=fallback-from-http"
-    ],
-    "./resources/log.js?pipe=sub&name=fallback-from-data-1": [
-      "data:text/plain,",
-      "./resources/log.js?pipe=sub&name=fallback-from-http"
-    ],
-    "./resources/log.js?pipe=sub&name=fallback-from-data-2": [
-      "data:text/javascript,log.push('dataURL')",
-      "./resources/log.js?pipe=sub&name=fallback-from-http"
-    ]
-  }
-}
-`;
-const tests = {};
-for (const key in JSON.parse(importMap).imports) {
-  if (key === "bare") {
-    continue;
-  }
-  tests[key] =
-    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR];
-}
-doTests(importMap, null, tests);
-</script>
-<body>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/http.sub.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/http.sub.tentative.html
deleted file mode 100644
index bd24f353..0000000
--- a/third_party/blink/web_tests/external/wpt/import-maps/http.sub.tentative.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/test-helper.js"></script>
-
-<script>
-// "bare/..." (i.e. without leading "./") are bare specifiers
-// (not relative paths).
-//
-// Discussions about notations for builtin modules are ongoing, e.g.
-// https://github.com/tc39/proposal-javascript-standard-library/issues/12
-// Currently the tests expects two notations are accepted.
-// TODO: Once the discussions converge, update the tests.
-const importMap = `
-{
-  "imports": {
-    "bare": "./resources/log.js?pipe=sub&name=bare",
-
-    "./resources/log.js?pipe=sub&name=foo": "./resources/log.js?pipe=sub&name=bar",
-    "./resources/log.js?pipe=sub&name=cross-origin-foo": "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-bar",
-    "./resources/log.js?pipe=sub&name=to-data": "data:text/javascript,log.push('dataURL')",
-
-    "./resources/log.js?pipe=sub&name=std-blank": "std:blank",
-    "./resources/log.js?pipe=sub&name=blank": "@std/blank",
-    "./resources/log.js?pipe=sub&name=std-none": "std:none",
-    "./resources/log.js?pipe=sub&name=none": "@std/none",
-
-    "./resources/log.js?pipe=sub&name=to-bare": "bare"
-  }
-}
-`;
-
-const tests = {
-  // Arrays of expected results for:
-  // - <script src type="module">,
-  // - <script src> (classic script),
-  // - static import, and
-  // - dynamic import.
-
-  // Currently, Chromium's implementation resolves import maps as a part of
-  // specifier resolution, and thus failure in import map resolution causes
-  // a parse error, not fetch error. Therefore, we use Result.PARSE_ERROR
-  // below. https://crbug.com/928435
-
-  // HTTP(S) to HTTP(S).
-  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=foo":
-    [Result.URL, Result.URL, "log:bar", "log:bar"],
-  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-foo":
-    [Result.URL, Result.URL, "log:cross-origin-bar", "log:cross-origin-bar"],
-
-  // HTTP(S) to data:
-  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=to-data":
-    [Result.URL, Result.URL, "dataURL", "dataURL"],
-
-  // HTTP(S) to built-in.
-  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=std-blank":
-    [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
-  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=blank":
-    [Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
-  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=std-none":
-    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
-  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=none":
-    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
-
-  // HTTP(S) to bare mapping is disabled.
-  "{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=to-bare":
-    [Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
-};
-
-doTests(importMap, null, tests);
-</script>
-<body>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-schema.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-schema.tentative-expected.txt
deleted file mode 100644
index d2f63ce..0000000
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-schema.tentative-expected.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-This is a testharness.js-based test.
-PASS Invalid JSON
-PASS Mismatching the top-level schema / should throw for top-level non-objects
-PASS Mismatching the top-level schema / should throw if imports is a non-object
-PASS Mismatching the top-level schema / should throw if scopes is a non-object
-FAIL Mismatching the top-level schema / should ignore unspecified top-level entries assert_object_equals: expected property "0" missing
-PASS Mismatching the specifier map schema / should ignore entries where the address is not a string, array, or null
-PASS Mismatching the specifier map schema / should ignore entries where the specifier key is an empty string
-PASS Mismatching the specifier map schema / should ignore members of an address array that are not strings
-PASS Mismatching the specifier map schema / should throw if a scope's value is not an object
-PASS Normalization / should normalize empty import maps to have imports and scopes keys
-PASS Normalization / should normalize an import map without imports to have imports
-PASS Normalization / should normalize an import map without scopes to have scopes
-PASS Normalization / should normalize addresses to arrays
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-scope-keys.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-scope-keys.tentative-expected.txt
deleted file mode 100644
index 22d627b..0000000
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-scope-keys.tentative-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This is a testharness.js-based test.
-PASS Relative URL scope keys / should work with no prefix
-PASS Relative URL scope keys / should work with ./, ../, and / prefixes
-PASS Relative URL scope keys / should work with /s, ?s, and #s
-PASS Relative URL scope keys / should work with an empty string scope key
-PASS Relative URL scope keys / should work with / suffixes
-PASS Relative URL scope keys / should deduplicate based on URL parsing rules
-FAIL Absolute URL scope keys / should only accept absolute URL scope keys with fetch schemes assert_object_equals: expected property "0" missing
-FAIL Absolute URL scope keys / should parse absolute URL scope keys, ignoring unparseable ones assert_object_equals: expected property "0" missing
-FAIL Absolute URL scope keys / should ignore relative URL scope keys when the base URL is a data: URL assert_object_equals: expected property "0" missing
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-specifier-keys.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-specifier-keys.tentative-expected.txt
deleted file mode 100644
index 2c08065..0000000
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-specifier-keys.tentative-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-PASS Relative URL-like specifier keys / should absolutize strings prefixed with ./, ../, or / into the corresponding URLs
-FAIL Relative URL-like specifier keys / should not absolutize strings prefixed with ./, ../, or / with a data: URL base assert_equals: expected "{\"../foo\":[\"https://example.com/dotdotslash\"],\"./foo\":[\"https://example.com/dotslash\"],\"/foo\":[\"https://example.com/slash\"]}" but got "{\"http://web-platform.test:8001/foo\":[\"https://example.com/slash\"],\"http://web-platform.test:8001/import-maps/foo\":[\"https://example.com/dotdotslash\"],\"http://web-platform.test:8001/import-maps/imported/foo\":[\"https://example.com/dotslash\"]}"
-PASS Relative URL-like specifier keys / should absolutize the literal strings ./, ../, or / with no suffix
-PASS Relative URL-like specifier keys / should treat percent-encoded variants of ./, ../, or / as bare specifiers
-FAIL Absolute URL specifier keys / should only accept absolute URL specifier keys with fetch schemes, treating others as bare specifiers assert_equals: expected "{\"about:good\":[\"https://base.example/about\"],\"blob:good\":[\"https://base.example/blob\"],\"data:good\":[\"https://base.example/data\"],\"file:///good\":[\"https://base.example/file\"],\"filesystem:good\":[\"https://base.example/filesystem\"],\"ftp://good/\":[\"https://base.example/ftp/\"],\"http://good/\":[\"https://base.example/http/\"],\"https://good/\":[\"https://base.example/https/\"],\"import:bad\":[\"https://base.example/import\"],\"javascript:bad\":[\"https://base.example/javascript\"],\"mailto:bad\":[\"https://base.example/mailto\"],\"wss:bad\":[\"https://base.example/wss\"]}" but got "{\"about:good\":[\"https://base.example/about\"],\"blob:good\":[\"https://base.example/blob\"],\"data:good\":[\"https://base.example/data\"],\"file:///good\":[\"https://base.example/file\"],\"filesystem:good\":[\"https://base.example/filesystem\"],\"ftp://good/\":[\"https://base.example/ftp/\"],\"http://good/\":[\"https://base.example/http/\"],\"https://good/\":[\"https://base.example/https/\"],\"import:bad\":[\"https://base.example/import\"],\"javascript:bad\":[\"https://base.example/javascript\"],\"mailto:bad\":[\"https://base.example/mailto\"],\"wss://bad/\":[\"https://base.example/wss\"]}"
-FAIL Absolute URL specifier keys / should parse absolute URLs, treating unparseable ones as bare specifiers assert_equals: expected "{\"http://[www.example.com]/\":[\"https://base.example/unparseable3/\"],\"https://ex ample.org/\":[\"https://base.example/unparseable1/\"],\"https://example.com/\":[\"https://base.example/percentDecoding/\"],\"https://example.com/%41\":[\"https://base.example/noPercentDecoding\"],\"https://example.com///\":[\"https://base.example/invalidButParseable2/\"],\"https://example.com:demo\":[\"https://base.example/unparseable2\"],\"https://example.net/\":[\"https://base.example/prettyNormal/\"],\"https://example.org/\":[\"https://base.example/invalidButParseable1/\"]}" but got "{\"http://[www.example.com]/\":[\"https://base.example/unparseable3/\"],\"https://ex%20ample.org/\":[\"https://base.example/unparseable1/\"],\"https://example.com/\":[\"https://base.example/percentDecoding/\"],\"https://example.com///\":[\"https://base.example/invalidButParseable2/\"],\"https://example.com/A\":[\"https://base.example/noPercentDecoding\"],\"https://example.com:demo\":[\"https://base.example/unparseable2\"],\"https://example.net/\":[\"https://base.example/prettyNormal/\"],\"https://example.org/\":[\"https://base.example/invalidButParseable1/\"]}"
-PASS Absolute URL specifier keys / should parse built-in module specifier keys, including with a "/"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/helpers/parsing.js b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/helpers/parsing.js
index 5c22f6d..daad6d2 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/helpers/parsing.js
+++ b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/helpers/parsing.js
@@ -38,13 +38,7 @@
 };
 
 function testWarningHandler(expectedWarnings) {
-  const warnings = [];
-  const { warn } = console;
-  console.warn = warning => {
-    warnings.push(warning);
-  };
-  return () => {
-    console.warn = warn;
-    expect(warnings).toEqual(expectedWarnings);
-  };
+  // We don't check warnings on WPT tests, because there are no
+  // ways to catch console warnings from JavaScript.
+  return () => {};
 }
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-addresses.js b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-addresses.js
index 0f5fc73..92d7714 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-addresses.js
+++ b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-addresses.js
@@ -1,6 +1,5 @@
 'use strict';
 const { expectSpecifierMap } = require('./helpers/parsing.js');
-const { BUILT_IN_MODULE_SCHEME } = require('../lib/utils.js');
 
 describe('Relative URL-like addresses', () => {
   it('should accept strings prefixed with ./, ../, or /', () => {
@@ -12,9 +11,9 @@
       }`,
       'https://base.example/path1/path2/path3',
       {
-        dotSlash: [expect.toMatchURL('https://base.example/path1/path2/foo')],
-        dotDotSlash: [expect.toMatchURL('https://base.example/path1/foo')],
-        slash: [expect.toMatchURL('https://base.example/foo')]
+        dotSlash: expect.toMatchURL('https://base.example/path1/path2/foo'),
+        dotDotSlash: expect.toMatchURL('https://base.example/path1/foo'),
+        slash: expect.toMatchURL('https://base.example/foo')
       }
     );
   });
@@ -28,9 +27,6 @@
       }`,
       'data:text/html,test',
       {
-        dotSlash: [],
-        dotDotSlash: [],
-        slash: []
       },
       [
         `Invalid address "./foo" for the specifier key "dotSlash".`,
@@ -49,9 +45,9 @@
       }`,
       'https://base.example/path1/path2/path3',
       {
-        dotSlash: [expect.toMatchURL('https://base.example/path1/path2/')],
-        dotDotSlash: [expect.toMatchURL('https://base.example/path1/')],
-        slash: [expect.toMatchURL('https://base.example/')]
+        dotSlash: expect.toMatchURL('https://base.example/path1/path2/'),
+        dotDotSlash: expect.toMatchURL('https://base.example/path1/'),
+        slash: expect.toMatchURL('https://base.example/')
       }
     );
   });
@@ -69,13 +65,6 @@
       }`,
       'https://base.example/path1/path2/path3',
       {
-        dotSlash1: [],
-        dotDotSlash1: [],
-        dotSlash2: [],
-        dotDotSlash2: [],
-        slash2: [],
-        dotSlash3: [],
-        dotDotSlash3: []
       },
       [
         `Invalid address "%2E/" for the specifier key "dotSlash1".`,
@@ -90,49 +79,6 @@
   });
 });
 
-describe('Built-in module addresses', () => {
-  it('should accept URLs using the built-in module scheme', () => {
-    expectSpecifierMap(
-      `{
-        "foo": "${BUILT_IN_MODULE_SCHEME}:foo"
-      }`,
-      'https://base.example/path1/path2/path3',
-      {
-        foo: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo`)]
-      }
-    );
-  });
-
-  it('should ignore percent-encoded variants of the built-in module scheme', () => {
-    expectSpecifierMap(
-      `{
-        "foo": "${encodeURIComponent(BUILT_IN_MODULE_SCHEME + ':')}foo"
-      }`,
-      'https://base.example/path1/path2/path3',
-      {
-        foo: []
-      },
-      [`Invalid address "${encodeURIComponent(BUILT_IN_MODULE_SCHEME + ':')}foo" for the specifier key "foo".`]
-    );
-  });
-
-  it('should allow built-in module URLs that contain "/" or "\\"', () => {
-    expectSpecifierMap(
-      `{
-        "slashEnd": "${BUILT_IN_MODULE_SCHEME}:foo/",
-        "slashMiddle": "${BUILT_IN_MODULE_SCHEME}:foo/bar",
-        "backslash": "${BUILT_IN_MODULE_SCHEME}:foo\\\\baz"
-      }`,
-      'https://base.example/path1/path2/path3',
-      {
-        slashEnd: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo/`)],
-        slashMiddle: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo/bar`)],
-        backslash: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo\\baz`)]
-      }
-    );
-  });
-});
-
 describe('Absolute URL addresses', () => {
   it('should only accept absolute URL addresses with fetch schemes', () => {
     expectSpecifierMap(
@@ -141,7 +87,7 @@
         "blob": "blob:good",
         "data": "data:good",
         "file": "file:///good",
-        "filesystem": "filesystem:good",
+        "filesystem": "filesystem:http://example.com/good/",
         "http": "http://good/",
         "https": "https://good/",
         "ftp": "ftp://good/",
@@ -152,65 +98,20 @@
       }`,
       'https://base.example/path1/path2/path3',
       {
-        about: [expect.toMatchURL('about:good')],
-        blob: [expect.toMatchURL('blob:good')],
-        data: [expect.toMatchURL('data:good')],
-        file: [expect.toMatchURL('file:///good')],
-        filesystem: [expect.toMatchURL('filesystem:good')],
-        http: [expect.toMatchURL('http://good/')],
-        https: [expect.toMatchURL('https://good/')],
-        ftp: [expect.toMatchURL('ftp://good/')],
-        import: [],
-        mailto: [],
-        javascript: [],
-        wss: []
+        about: expect.toMatchURL('about:good'),
+        blob: expect.toMatchURL('blob:good'),
+        data: expect.toMatchURL('data:good'),
+        file: expect.toMatchURL('file:///good'),
+        filesystem: expect.toMatchURL('filesystem:http://example.com/good/'),
+        http: expect.toMatchURL('http://good/'),
+        https: expect.toMatchURL('https://good/'),
+        ftp: expect.toMatchURL('ftp://good/'),
+        import: expect.toMatchURL('import:bad'),
+        javascript: expect.toMatchURL('javascript:bad'),
+        mailto: expect.toMatchURL('mailto:bad'),
+        wss: expect.toMatchURL('wss://bad/')
       },
-      [
-        `Invalid address "import:bad" for the specifier key "import".`,
-        `Invalid address "mailto:bad" for the specifier key "mailto".`,
-        `Invalid address "javascript:bad" for the specifier key "javascript".`,
-        `Invalid address "wss:bad" for the specifier key "wss".`
-      ]
-    );
-  });
-
-  it('should only accept absolute URL addresses with fetch schemes inside arrays', () => {
-    expectSpecifierMap(
-      `{
-        "about": ["about:good"],
-        "blob": ["blob:good"],
-        "data": ["data:good"],
-        "file": ["file:///good"],
-        "filesystem": ["filesystem:good"],
-        "http": ["http://good/"],
-        "https": ["https://good/"],
-        "ftp": ["ftp://good/"],
-        "import": ["import:bad"],
-        "mailto": ["mailto:bad"],
-        "javascript": ["javascript:bad"],
-        "wss": ["wss:bad"]
-      }`,
-      'https://base.example/path1/path2/path3',
-      {
-        about: [expect.toMatchURL('about:good')],
-        blob: [expect.toMatchURL('blob:good')],
-        data: [expect.toMatchURL('data:good')],
-        file: [expect.toMatchURL('file:///good')],
-        filesystem: [expect.toMatchURL('filesystem:good')],
-        http: [expect.toMatchURL('http://good/')],
-        https: [expect.toMatchURL('https://good/')],
-        ftp: [expect.toMatchURL('ftp://good/')],
-        import: [],
-        mailto: [],
-        javascript: [],
-        wss: []
-      },
-      [
-        `Invalid address "import:bad" for the specifier key "import".`,
-        `Invalid address "mailto:bad" for the specifier key "mailto".`,
-        `Invalid address "javascript:bad" for the specifier key "javascript".`,
-        `Invalid address "wss:bad" for the specifier key "wss".`
-      ]
+      []
     );
   });
 
@@ -228,45 +129,11 @@
       }`,
       'https://base.example/path1/path2/path3',
       {
-        unparseable1: [],
-        unparseable2: [],
-        unparseable3: [],
-        invalidButParseable1: [expect.toMatchURL('https://example.org/')],
-        invalidButParseable2: [expect.toMatchURL('https://example.com///')],
-        prettyNormal: [expect.toMatchURL('https://example.net/')],
-        percentDecoding: [expect.toMatchURL('https://example.com/')],
-        noPercentDecoding: [expect.toMatchURL('https://example.com/%41')]
-      },
-      [
-        `Invalid address "https://ex ample.org/" for the specifier key "unparseable1".`,
-        `Invalid address "https://example.com:demo" for the specifier key "unparseable2".`,
-        `Invalid address "http://[www.example.com]/" for the specifier key "unparseable3".`
-      ]
-    );
-  });
-
-  it('should parse absolute URLs, ignoring unparseable ones inside arrays', () => {
-    expectSpecifierMap(
-      `{
-        "unparseable1": ["https://ex ample.org/"],
-        "unparseable2": ["https://example.com:demo"],
-        "unparseable3": ["http://[www.example.com]/"],
-        "invalidButParseable1": ["https:example.org"],
-        "invalidButParseable2": ["https://///example.com///"],
-        "prettyNormal": ["https://example.net"],
-        "percentDecoding": ["https://ex%41mple.com/"],
-        "noPercentDecoding": ["https://example.com/%41"]
-      }`,
-      'https://base.example/path1/path2/path3',
-      {
-        unparseable1: [],
-        unparseable2: [],
-        unparseable3: [],
-        invalidButParseable1: [expect.toMatchURL('https://example.org/')],
-        invalidButParseable2: [expect.toMatchURL('https://example.com///')],
-        prettyNormal: [expect.toMatchURL('https://example.net/')],
-        percentDecoding: [expect.toMatchURL('https://example.com/')],
-        noPercentDecoding: [expect.toMatchURL('https://example.com/%41')]
+        invalidButParseable1: expect.toMatchURL('https://example.org/'),
+        invalidButParseable2: expect.toMatchURL('https://example.com///'),
+        prettyNormal: expect.toMatchURL('https://example.net/'),
+        percentDecoding: expect.toMatchURL('https://example.com/'),
+        noPercentDecoding: expect.toMatchURL('https://example.com/%41')
       },
       [
         `Invalid address "https://ex ample.org/" for the specifier key "unparseable1".`,
@@ -281,54 +148,12 @@
   it('should warn for the simple case', () => {
     expectSpecifierMap(
       `{
-        "trailer/": "/notrailer",
-        "${BUILT_IN_MODULE_SCHEME}:trailer/": "/bim-notrailer"
+        "trailer/": "/notrailer"
       }`,
       'https://base.example/path1/path2/path3',
       {
-        'trailer/': [],
-        [`${BUILT_IN_MODULE_SCHEME}:trailer/`]: []
       },
-      [
-        `Invalid address "https://base.example/notrailer" for package specifier key "trailer/". Package addresses must end with "/".`,
-        `Invalid address "https://base.example/bim-notrailer" for package specifier key "${BUILT_IN_MODULE_SCHEME}:trailer/". Package addresses must end with "/".`
-      ]
-    );
-  });
-
-  it('should warn for a mismatch alone in an array', () => {
-    expectSpecifierMap(
-      `{
-        "trailer/": ["/notrailer"],
-        "${BUILT_IN_MODULE_SCHEME}:trailer/": ["/bim-notrailer"]
-      }`,
-      'https://base.example/path1/path2/path3',
-      {
-        'trailer/': [],
-        [`${BUILT_IN_MODULE_SCHEME}:trailer/`]: []
-      },
-      [
-        `Invalid address "https://base.example/notrailer" for package specifier key "trailer/". Package addresses must end with "/".`,
-        `Invalid address "https://base.example/bim-notrailer" for package specifier key "${BUILT_IN_MODULE_SCHEME}:trailer/". Package addresses must end with "/".`
-      ]
-    );
-  });
-
-  it('should warn for a mismatch alongside non-mismatches in an array', () => {
-    expectSpecifierMap(
-      `{
-        "trailer/": ["/atrailer/", "/notrailer"],
-        "${BUILT_IN_MODULE_SCHEME}:trailer/": ["/bim-atrailer/", "/bim-notrailer"]
-      }`,
-      'https://base.example/path1/path2/path3',
-      {
-        'trailer/': [expect.toMatchURL('https://base.example/atrailer/')],
-        [`${BUILT_IN_MODULE_SCHEME}:trailer/`]: [expect.toMatchURL('https://base.example/bim-atrailer/')]
-      },
-      [
-        `Invalid address "https://base.example/notrailer" for package specifier key "trailer/". Package addresses must end with "/".`,
-        `Invalid address "https://base.example/bim-notrailer" for package specifier key "${BUILT_IN_MODULE_SCHEME}:trailer/". Package addresses must end with "/".`
-      ]
+      [`Invalid address "https://base.example/notrailer" for package specifier key "trailer/". Package addresses must end with "/".`]
     );
   });
 });
@@ -342,7 +167,6 @@
         }`,
         'https://base.example/path1/path2/path3',
         {
-          foo: []
         },
         [`Invalid address "${bad}" for the specifier key "foo".`]
       );
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-schema.js b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-schema.js
index 69503453..f60422ae 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-schema.js
+++ b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-schema.js
@@ -45,24 +45,20 @@
 });
 
 describe('Mismatching the specifier map schema', () => {
-  const invalidAddressStrings = ['true', '1', '{}'];
-  const invalidInsideArrayStrings = ['null', 'true', '1', '{}', '[]'];
+  const invalidAddressStrings = ['null', 'true', '1', '{}', '[]', '["https://example.com/"]'];
 
-  it('should ignore entries where the address is not a string, array, or null', () => {
+  it('should ignore entries where the address is not a string', () => {
     for (const invalid of invalidAddressStrings) {
       expectSpecifierMap(
         `{
           "foo": ${invalid},
-          "bar": ["https://example.com/"]
+          "bar": "https://example.com/"
         }`,
         'https://base.example/',
         {
-          bar: [expect.toMatchURL('https://example.com/')]
+          bar: expect.toMatchURL('https://example.com/')
         },
-        [
-          `Invalid address ${invalid} for the specifier key "foo". ` +
-          `Addresses must be strings, arrays, or null.`
-        ]
+        [`Invalid address ${invalid} for the specifier key "foo". Addresses must be strings.`]
       );
     }
   });
@@ -70,7 +66,7 @@
   it('should ignore entries where the specifier key is an empty string', () => {
     expectSpecifierMap(
       `{
-        "": ["https://example.com/"]
+        "": "https://example.com/"
       }`,
       'https://base.example/',
       {},
@@ -78,26 +74,6 @@
     );
   });
 
-  it('should ignore members of an address array that are not strings', () => {
-    for (const invalid of invalidInsideArrayStrings) {
-      expectSpecifierMap(
-        `{
-          "foo": ["https://example.com/", ${invalid}],
-          "bar": ["https://example.com/"]
-        }`,
-        'https://base.example/',
-        {
-          foo: [expect.toMatchURL('https://example.com/')],
-          bar: [expect.toMatchURL('https://example.com/')]
-        },
-        [
-          `Invalid address ${invalid} inside the address array for the specifier key "foo". ` +
-          `Address arrays must only contain strings.`
-        ]
-      );
-    }
-  });
-
   it('should throw if a scope\'s value is not an object', () => {
     for (const invalid of nonObjectStrings) {
       expectBad(`{ "scopes": { "https://scope.example/": ${invalid} } }`, 'https://base.example/');
@@ -120,20 +96,4 @@
     expect(parseFromString(`{ "imports": {} }`, 'https://base.example/'))
       .toEqual({ imports: {}, scopes: {} });
   });
-
-  it('should normalize addresses to arrays', () => {
-    expectSpecifierMap(
-      `{
-        "foo": "https://example.com/1",
-        "bar": ["https://example.com/2"],
-        "baz": null
-      }`,
-      'https://base.example/',
-      {
-        foo: [expect.toMatchURL('https://example.com/1')],
-        bar: [expect.toMatchURL('https://example.com/2')],
-        baz: []
-      }
-    );
-  });
 });
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-scope-keys.js b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-scope-keys.js
index cd1d9b3..4993f3a 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-scope-keys.js
+++ b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-scope-keys.js
@@ -62,14 +62,14 @@
 });
 
 describe('Absolute URL scope keys', () => {
-  it('should only accept absolute URL scope keys with fetch schemes', () => {
+  it('should accept all absolute URL scope keys, with or without fetch schemes', () => {
     expectScopes(
       [
         'about:good',
         'blob:good',
         'data:good',
         'file:///good',
-        'filesystem:good',
+        'filesystem:http://example.com/good/',
         'http://good/',
         'https://good/',
         'ftp://good/',
@@ -84,17 +84,16 @@
         'blob:good',
         'data:good',
         'file:///good',
-        'filesystem:good',
+        'filesystem:http://example.com/good/',
         'http://good/',
         'https://good/',
-        'ftp://good/'
+        'ftp://good/',
+        'import:bad',
+        'mailto:bad',
+        'javascript:bad',
+        'wss://ba/'
       ],
-      [
-        'Invalid scope "import:bad". Scope URLs must have a fetch scheme.',
-        'Invalid scope "mailto:bad". Scope URLs must have a fetch scheme.',
-        'Invalid scope "javascript:bad". Scope URLs must have a fetch scheme.',
-        'Invalid scope "wss://ba/". Scope URLs must have a fetch scheme.'
-      ]
+      []
     );
   });
 
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-specifier-keys.js b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-specifier-keys.js
index 9eb423a..7ac24bf8 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-specifier-keys.js
+++ b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/parsing-specifier-keys.js
@@ -1,8 +1,5 @@
 'use strict';
 const { expectSpecifierMap } = require('./helpers/parsing.js');
-const { BUILT_IN_MODULE_SCHEME } = require('../lib/utils.js');
-
-const BLANK = `${BUILT_IN_MODULE_SCHEME}:blank`;
 
 describe('Relative URL-like specifier keys', () => {
   it('should absolutize strings prefixed with ./, ../, or / into the corresponding URLs', () => {
@@ -14,9 +11,9 @@
       }`,
       'https://base.example/path1/path2/path3',
       {
-        'https://base.example/path1/path2/foo': [expect.toMatchURL('https://base.example/dotslash')],
-        'https://base.example/path1/foo': [expect.toMatchURL('https://base.example/dotdotslash')],
-        'https://base.example/foo': [expect.toMatchURL('https://base.example/slash')]
+        'https://base.example/path1/path2/foo': expect.toMatchURL('https://base.example/dotslash'),
+        'https://base.example/path1/foo': expect.toMatchURL('https://base.example/dotdotslash'),
+        'https://base.example/foo': expect.toMatchURL('https://base.example/slash')
       }
     );
   });
@@ -30,9 +27,9 @@
       }`,
       'data:text/html,test',
       {
-        './foo': [expect.toMatchURL('https://example.com/dotslash')],
-        '../foo': [expect.toMatchURL('https://example.com/dotdotslash')],
-        '/foo': [expect.toMatchURL('https://example.com/slash')]
+        './foo': expect.toMatchURL('https://example.com/dotslash'),
+        '../foo': expect.toMatchURL('https://example.com/dotdotslash'),
+        '/foo': expect.toMatchURL('https://example.com/slash')
       }
     );
   });
@@ -46,9 +43,9 @@
       }`,
       'https://base.example/path1/path2/path3',
       {
-        'https://base.example/path1/path2/': [expect.toMatchURL('https://base.example/dotslash/')],
-        'https://base.example/path1/': [expect.toMatchURL('https://base.example/dotdotslash/')],
-        'https://base.example/': [expect.toMatchURL('https://base.example/slash/')]
+        'https://base.example/path1/path2/': expect.toMatchURL('https://base.example/dotslash/'),
+        'https://base.example/path1/': expect.toMatchURL('https://base.example/dotdotslash/'),
+        'https://base.example/': expect.toMatchURL('https://base.example/slash/')
       }
     );
   });
@@ -66,27 +63,27 @@
       }`,
       'https://base.example/path1/path2/path3',
       {
-        '%2E/': [expect.toMatchURL('https://base.example/dotSlash1/')],
-        '%2E%2E/': [expect.toMatchURL('https://base.example/dotDotSlash1/')],
-        '.%2F': [expect.toMatchURL('https://base.example/dotSlash2')],
-        '..%2F': [expect.toMatchURL('https://base.example/dotDotSlash2')],
-        '%2F': [expect.toMatchURL('https://base.example/slash2')],
-        '%2E%2F': [expect.toMatchURL('https://base.example/dotSlash3')],
-        '%2E%2E%2F': [expect.toMatchURL('https://base.example/dotDotSlash3')]
+        '%2E/': expect.toMatchURL('https://base.example/dotSlash1/'),
+        '%2E%2E/': expect.toMatchURL('https://base.example/dotDotSlash1/'),
+        '.%2F': expect.toMatchURL('https://base.example/dotSlash2'),
+        '..%2F': expect.toMatchURL('https://base.example/dotDotSlash2'),
+        '%2F': expect.toMatchURL('https://base.example/slash2'),
+        '%2E%2F': expect.toMatchURL('https://base.example/dotSlash3'),
+        '%2E%2E%2F': expect.toMatchURL('https://base.example/dotDotSlash3')
       }
     );
   });
 });
 
 describe('Absolute URL specifier keys', () => {
-  it('should only accept absolute URL specifier keys with fetch schemes, treating others as bare specifiers', () => {
+  it('Accept all absolute URL specifier keys even with fetch schemes as URLs', () => {
     expectSpecifierMap(
       `{
         "about:good": "/about",
         "blob:good": "/blob",
         "data:good": "/data",
         "file:///good": "/file",
-        "filesystem:good": "/filesystem",
+        "filesystem:http://example.com/good/": "/filesystem/",
         "http://good/": "/http/",
         "https://good/": "/https/",
         "ftp://good/": "/ftp/",
@@ -97,18 +94,18 @@
       }`,
       'https://base.example/path1/path2/path3',
       {
-        'about:good': [expect.toMatchURL('https://base.example/about')],
-        'blob:good': [expect.toMatchURL('https://base.example/blob')],
-        'data:good': [expect.toMatchURL('https://base.example/data')],
-        'file:///good': [expect.toMatchURL('https://base.example/file')],
-        'filesystem:good': [expect.toMatchURL('https://base.example/filesystem')],
-        'http://good/': [expect.toMatchURL('https://base.example/http/')],
-        'https://good/': [expect.toMatchURL('https://base.example/https/')],
-        'ftp://good/': [expect.toMatchURL('https://base.example/ftp/')],
-        'import:bad': [expect.toMatchURL('https://base.example/import')],
-        'mailto:bad': [expect.toMatchURL('https://base.example/mailto')],
-        'javascript:bad': [expect.toMatchURL('https://base.example/javascript')],
-        'wss:bad': [expect.toMatchURL('https://base.example/wss')]
+        'about:good': expect.toMatchURL('https://base.example/about'),
+        'blob:good': expect.toMatchURL('https://base.example/blob'),
+        'data:good': expect.toMatchURL('https://base.example/data'),
+        'file:///good': expect.toMatchURL('https://base.example/file'),
+        'filesystem:http://example.com/good/': expect.toMatchURL('https://base.example/filesystem/'),
+        'http://good/': expect.toMatchURL('https://base.example/http/'),
+        'https://good/': expect.toMatchURL('https://base.example/https/'),
+        'ftp://good/': expect.toMatchURL('https://base.example/ftp/'),
+        'import:bad': expect.toMatchURL('https://base.example/import'),
+        'mailto:bad': expect.toMatchURL('https://base.example/mailto'),
+        'javascript:bad': expect.toMatchURL('https://base.example/javascript'),
+        'wss://bad/': expect.toMatchURL('https://base.example/wss')
       }
     );
   });
@@ -127,32 +124,40 @@
       }`,
       'https://base.example/path1/path2/path3',
       {
-        'https://ex ample.org/': [expect.toMatchURL('https://base.example/unparseable1/')],
-        'https://example.com:demo': [expect.toMatchURL('https://base.example/unparseable2')],
-        'http://[www.example.com]/': [expect.toMatchURL('https://base.example/unparseable3/')],
-        'https://example.org/': [expect.toMatchURL('https://base.example/invalidButParseable1/')],
-        'https://example.com///': [expect.toMatchURL('https://base.example/invalidButParseable2/')],
-        'https://example.net/': [expect.toMatchURL('https://base.example/prettyNormal/')],
-        'https://example.com/': [expect.toMatchURL('https://base.example/percentDecoding/')],
-        'https://example.com/%41': [expect.toMatchURL('https://base.example/noPercentDecoding')]
+        'https://ex ample.org/': expect.toMatchURL('https://base.example/unparseable1/'),
+        'https://example.com:demo': expect.toMatchURL('https://base.example/unparseable2'),
+        'http://[www.example.com]/': expect.toMatchURL('https://base.example/unparseable3/'),
+        'https://example.org/': expect.toMatchURL('https://base.example/invalidButParseable1/'),
+        'https://example.com///': expect.toMatchURL('https://base.example/invalidButParseable2/'),
+        'https://example.net/': expect.toMatchURL('https://base.example/prettyNormal/'),
+        'https://example.com/': expect.toMatchURL('https://base.example/percentDecoding/'),
+        'https://example.com/%41': expect.toMatchURL('https://base.example/noPercentDecoding')
       }
     );
   });
 
-  it('should parse built-in module specifier keys, including with a "/"', () => {
+  it('should sort correctly (issue #181)', () => {
     expectSpecifierMap(
       `{
-        "${BLANK}": "/blank",
-        "${BLANK}/": "/blank/",
-        "${BLANK}/foo": "/blank/foo",
-        "${BLANK}\\\\foo": "/blank/backslashfoo"
+        "https://example.com/aaa": "https://example.com/aaa",
+        "https://example.com/a": "https://example.com/a"
       }`,
-      'https://base.example/path1/path2/path3',
+      'https://base.example/',
       {
-        [BLANK]: [expect.toMatchURL('https://base.example/blank')],
-        [`${BLANK}/`]: [expect.toMatchURL('https://base.example/blank/')],
-        [`${BLANK}/foo`]: [expect.toMatchURL('https://base.example/blank/foo')],
-        [`${BLANK}\\foo`]: [expect.toMatchURL('https://base.example/blank/backslashfoo')]
+        'https://example.com/aaa': expect.toMatchURL('https://example.com/aaa'),
+        'https://example.com/a': expect.toMatchURL('https://example.com/a')
+      }
+    );
+
+    expectSpecifierMap(
+      `{
+        "https://example.com/a": "https://example.com/a",
+        "https://example.com/aaa": "https://example.com/aaa"
+      }`,
+      'https://base.example/',
+      {
+        'https://example.com/aaa': expect.toMatchURL('https://example.com/aaa'),
+        'https://example.com/a': expect.toMatchURL('https://example.com/a')
       }
     );
   });
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving-scopes.js b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving-scopes.js
index ca19a66..d133b50b 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving-scopes.js
+++ b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving-scopes.js
@@ -16,20 +16,6 @@
   const inJSDirURL = new URL('https://example.com/js/app.mjs');
   const topLevelURL = new URL('https://example.com/app.mjs');
 
-  it('should fail when the mapping is to an empty array', () => {
-    const resolveUnderTest = makeResolveUnderTest(`{
-      "scopes": {
-        "/js/": {
-          "moment": null,
-          "lodash": []
-        }
-      }
-    }`);
-
-    expect(() => resolveUnderTest('moment', inJSDirURL)).toThrow(TypeError);
-    expect(() => resolveUnderTest('lodash', inJSDirURL)).toThrow(TypeError);
-  });
-
   describe('Exact vs. prefix based matching', () => {
     it('should match correctly when both are in the map', () => {
       const resolveUnderTest = makeResolveUnderTest(`{
@@ -153,14 +139,17 @@
       "imports": {
         "a": "/a-1.mjs",
         "b": "/b-1.mjs",
-        "c": "/c-1.mjs"
+        "c": "/c-1.mjs",
+        "d": "/d-1.mjs"
       },
       "scopes": {
         "/scope2/": {
-          "a": "/a-2.mjs"
+          "a": "/a-2.mjs",
+          "d": "/d-2.mjs"
         },
         "/scope2/scope3/": {
-          "b": "/b-3.mjs"
+          "b": "/b-3.mjs",
+          "d": "/d-3.mjs"
         }
       }
     }`);
@@ -173,18 +162,21 @@
       expect(resolveUnderTest('a', scope1URL)).toMatchURL('https://example.com/a-1.mjs');
       expect(resolveUnderTest('b', scope1URL)).toMatchURL('https://example.com/b-1.mjs');
       expect(resolveUnderTest('c', scope1URL)).toMatchURL('https://example.com/c-1.mjs');
+      expect(resolveUnderTest('d', scope1URL)).toMatchURL('https://example.com/d-1.mjs');
     });
 
     it('should use a direct scope override', () => {
       expect(resolveUnderTest('a', scope2URL)).toMatchURL('https://example.com/a-2.mjs');
       expect(resolveUnderTest('b', scope2URL)).toMatchURL('https://example.com/b-1.mjs');
       expect(resolveUnderTest('c', scope2URL)).toMatchURL('https://example.com/c-1.mjs');
+      expect(resolveUnderTest('d', scope2URL)).toMatchURL('https://example.com/d-2.mjs');
     });
 
     it('should use an indirect scope override', () => {
       expect(resolveUnderTest('a', scope3URL)).toMatchURL('https://example.com/a-2.mjs');
       expect(resolveUnderTest('b', scope3URL)).toMatchURL('https://example.com/b-3.mjs');
       expect(resolveUnderTest('c', scope3URL)).toMatchURL('https://example.com/c-1.mjs');
+      expect(resolveUnderTest('d', scope3URL)).toMatchURL('https://example.com/d-3.mjs');
     });
   });
 
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving.js b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving.js
index 29ee31cc..ef8a4f87 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving.js
+++ b/third_party/blink/web_tests/external/wpt/import-maps/imported/resources/resolving.js
@@ -42,11 +42,11 @@
     expect(resolveUnderTest('https://///example.com///')).toMatchURL('https://example.com///');
   });
 
-  it('should fail for absolute non-fetch-scheme URLs', () => {
-    expect(() => resolveUnderTest('mailto:bad')).toThrow(TypeError);
-    expect(() => resolveUnderTest('import:bad')).toThrow(TypeError);
-    expect(() => resolveUnderTest('javascript:bad')).toThrow(TypeError);
-    expect(() => resolveUnderTest('wss:bad')).toThrow(TypeError);
+  it('should parse absolute non-fetch-scheme URLs', () => {
+    expect(resolveUnderTest('mailto:bad')).toMatchURL('mailto:bad');
+    expect(resolveUnderTest('import:bad')).toMatchURL('import:bad');
+    expect(resolveUnderTest('javascript:bad')).toMatchURL('javascript:bad');
+    expect(resolveUnderTest('wss:bad')).toMatchURL('wss://bad/');
   });
 
   it('should fail for strings not parseable as absolute URLs and not starting with ./ ../ or /', () => {
@@ -64,18 +64,6 @@
 });
 
 describe('Mapped using the "imports" key only (no scopes)', () => {
-  it('should fail when the mapping is to an empty array', () => {
-    const resolveUnderTest = makeResolveUnderTest(`{
-      "imports": {
-        "moment": null,
-        "lodash": []
-      }
-    }`);
-
-    expect(() => resolveUnderTest('moment')).toThrow(TypeError);
-    expect(() => resolveUnderTest('lodash')).toThrow(TypeError);
-  });
-
   describe('Package-like scenarios', () => {
     const resolveUnderTest = makeResolveUnderTest(`{
       "imports": {
@@ -84,8 +72,7 @@
         "lodash-dot": "./node_modules/lodash-es/lodash.js",
         "lodash-dot/": "./node_modules/lodash-es/",
         "lodash-dotdot": "../node_modules/lodash-es/lodash.js",
-        "lodash-dotdot/": "../node_modules/lodash-es/",
-        "nowhere/": []
+        "lodash-dotdot/": "../node_modules/lodash-es/"
       }
     }`);
 
@@ -110,10 +97,6 @@
       expect(() => resolveUnderTest('underscore/')).toThrow(TypeError);
       expect(() => resolveUnderTest('underscore/foo')).toThrow(TypeError);
     });
-
-    it('should fail for package submodules that map to nowhere', () => {
-      expect(() => resolveUnderTest('nowhere/foo')).toThrow(TypeError);
-    });
   });
 
   describe('Tricky specifiers', () => {
@@ -121,6 +104,7 @@
       "imports": {
         "package/withslash": "/node_modules/package-with-slash/index.mjs",
         "not-a-package": "/lib/not-a-package.mjs",
+        "only-slash/": "/lib/only-slash/",
         ".": "/lib/dot.mjs",
         "..": "/lib/dotdot.mjs",
         "..\\\\": "/lib/dotdotbackslash.mjs",
@@ -144,20 +128,19 @@
     it('should fail for attempting to get a submodule of something not declared with a trailing slash', () => {
       expect(() => resolveUnderTest('not-a-package/foo')).toThrow(TypeError);
     });
+
+    it('should fail for attempting to get a module if only a trailing-slash version is present', () => {
+      expect(() => resolveUnderTest('only-slash')).toThrow(TypeError);
+    });
   });
 
   describe('URL-like specifiers', () => {
     const resolveUnderTest = makeResolveUnderTest(`{
       "imports": {
-        "/node_modules/als-polyfill/index.mjs": "std:kv-storage",
-
         "/lib/foo.mjs": "./more/bar.mjs",
         "./dotrelative/foo.mjs": "/lib/dot.mjs",
         "../dotdotrelative/foo.mjs": "/lib/dotdot.mjs",
 
-        "/lib/no.mjs": null,
-        "./dotrelative/no.mjs": [],
-
         "/": "/lib/slash-only/",
         "./": "/lib/dotslash-only/",
 
@@ -181,16 +164,6 @@
       expect(resolveUnderTest('../dotdotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dotdot.mjs');
     });
 
-    it('should fail for URLs that remap to empty arrays', () => {
-      expect(() => resolveUnderTest('https://example.com/lib/no.mjs')).toThrow(TypeError);
-      expect(() => resolveUnderTest('/lib/no.mjs')).toThrow(TypeError);
-      expect(() => resolveUnderTest('../lib/no.mjs')).toThrow(TypeError);
-
-      expect(() => resolveUnderTest('https://example.com/app/dotrelative/no.mjs')).toThrow(TypeError);
-      expect(() => resolveUnderTest('/app/dotrelative/no.mjs')).toThrow(TypeError);
-      expect(() => resolveUnderTest('../app/dotrelative/no.mjs')).toThrow(TypeError);
-    });
-
     it('should remap URLs that are just composed from / and .', () => {
       expect(resolveUnderTest('https://example.com/')).toMatchURL('https://example.com/lib/slash-only/');
       expect(resolveUnderTest('/')).toMatchURL('https://example.com/lib/slash-only/');
@@ -212,7 +185,7 @@
   });
 
   describe('Overlapping entries with trailing slashes', () => {
-    it('should favor the most-specific key (no empty arrays)', () => {
+    it('should favor the most-specific key', () => {
       const resolveUnderTest = makeResolveUnderTest(`{
         "imports": {
           "a": "/1",
@@ -229,11 +202,9 @@
       expect(resolveUnderTest('a/b/c')).toMatchURL('https://example.com/4/c');
     });
 
-    it('should favor the most-specific key when empty arrays are involved for less-specific keys', () => {
+    it('should favor the most-specific key when there are no mappings for less-specific keys', () => {
       const resolveUnderTest = makeResolveUnderTest(`{
         "imports": {
-          "a": [],
-          "a/": [],
           "a/b": "/3",
           "a/b/": "/4/"
         }
@@ -247,24 +218,15 @@
       expect(resolveUnderTest('a/b/c')).toMatchURL('https://example.com/4/c');
       expect(() => resolveUnderTest('a/x/c')).toThrow(TypeError);
     });
+  });
 
-    it('should favor the most-specific key when empty arrays are involved for more-specific keys', () => {
-      const resolveUnderTest = makeResolveUnderTest(`{
-        "imports": {
-          "a": "/1",
-          "a/": "/2/",
-          "a/b": [],
-          "a/b/": []
-        }
-      }`);
+  it('should deal with data: URL bases', () => {
+    const resolveUnderTest = makeResolveUnderTest(`{
+      "imports": {
+        "foo/": "data:text/javascript,foo/"
+      }
+    }`);
 
-      expect(resolveUnderTest('a')).toMatchURL('https://example.com/1');
-      expect(resolveUnderTest('a/')).toMatchURL('https://example.com/2/');
-      expect(resolveUnderTest('a/x')).toMatchURL('https://example.com/2/x');
-      expect(() => resolveUnderTest('a/b')).toThrow(TypeError);
-      expect(() => resolveUnderTest('a/b/')).toThrow(TypeError);
-      expect(() => resolveUnderTest('a/b/c')).toThrow(TypeError);
-      expect(resolveUnderTest('a/x/c')).toMatchURL('https://example.com/2/x/c');
-    });
+    expect(() => resolveUnderTest('foo/bar')).toThrow(TypeError);
   });
 });
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/resources/jest-test-helper.js b/third_party/blink/web_tests/external/wpt/import-maps/resources/jest-test-helper.js
index 8d9236a..e6f12cd 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/resources/jest-test-helper.js
+++ b/third_party/blink/web_tests/external/wpt/import-maps/resources/jest-test-helper.js
@@ -15,6 +15,22 @@
   }, exports);
 }
 
+// Sort keys and then stringify for comparison.
+function stringifyImportMap(importMap) {
+  function getKeys(m) {
+    if (typeof m !== 'object')
+      return [];
+
+    let keys = [];
+    for (const key in m) {
+      keys.push(key);
+      keys = keys.concat(getKeys(m[key]));
+    }
+    return keys;
+  }
+  return JSON.stringify(importMap, getKeys(importMap).sort());
+}
+
 function expect(v) {
   return {
     toMatchURL: expected => assert_equals(v, expected),
@@ -34,10 +50,8 @@
         const actualParsedImportMap = JSON.parse(
             internals.getParsedImportMap(v.contentDocument));
         assert_equals(
-          JSON.stringify(actualParsedImportMap,
-                         Object.keys(actualParsedImportMap).sort()),
-          JSON.stringify(expected.imports,
-                         Object.keys(expected.imports).sort())
+          stringifyImportMap(actualParsedImportMap),
+          stringifyImportMap(expected)
         );
       } else {
         assert_object_equals(v, expected);
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/resources/test-helper.js b/third_party/blink/web_tests/external/wpt/import-maps/resources/test-helper.js
index 2447bfb..f21ad93 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/resources/test-helper.js
+++ b/third_party/blink/web_tests/external/wpt/import-maps/resources/test-helper.js
@@ -153,7 +153,7 @@
     const script = document.createElement("script");
     script.setAttribute("type", "module");
     script.setAttribute("src",
-        "/import-maps/static-import.js?pipe=sub(none)&url=" +
+        "static-import.js?pipe=sub(none)&url=" +
         encodeURIComponent("${specifier}"));
     script.addEventListener("load", handlers[Handler.ScriptLoadEvent]);
     script.addEventListener("error", handlers[Handler.ScriptErrorEvent]);
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/META.yml b/third_party/blink/web_tests/external/wpt/interfaces/META.yml
index 6895a5fc..c1dd8ddd 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/META.yml
+++ b/third_party/blink/web_tests/external/wpt/interfaces/META.yml
@@ -1,3 +1,2 @@
 suggested_reviewers:
   - foolip
-  - lukebjerring
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/README.md b/third_party/blink/web_tests/external/wpt/interfaces/README.md
index f70ffd2e..e0cdf0b 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/README.md
+++ b/third_party/blink/web_tests/external/wpt/interfaces/README.md
@@ -2,4 +2,4 @@
 
 The `.idl` files are extracted from specs by [Reffy](https://github.com/tidoust/reffy) into [reffy-reports](https://github.com/tidoust/reffy-reports), and a [sync script](https://github.com/tidoust/reffy-reports/blob/master/wpt-sync/sync.js) sends [automatic PRs](https://github.com/web-platform-tests/wpt/pulls/autofoolip). The PRs require manual review but can be approved/merged by anyone with write access.
 
-If some IDL in this directory is not up to date with the spec, first look for an [open PR](https://github.com/web-platform-tests/wpt/pulls/autofoolip) and if there is none [file an issue on reffy-reports](https://github.com/tidoust/reffy-reports/issues) and notify @foolip and @lukebjerring.
+If some IDL in this directory is not up to date with the spec, first look for an [open PR](https://github.com/web-platform-tests/wpt/pulls/autofoolip) and if there is none [file an issue on reffy-reports](https://github.com/tidoust/reffy-reports/issues) and notify @foolip.
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/css-shadow-parts.idl b/third_party/blink/web_tests/external/wpt/interfaces/css-shadow-parts.idl
new file mode 100644
index 0000000..66aaeba
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/interfaces/css-shadow-parts.idl
@@ -0,0 +1,8 @@
+// GENERATED CONTENT - DO NOT EDIT
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: CSS Shadow Parts (http://drafts.csswg.org/css-shadow-parts/)
+
+partial interface Element {
+  [SameObject, PutForwards=value] readonly attribute DOMTokenList part;
+};
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/web-nfc.idl b/third_party/blink/web_tests/external/wpt/interfaces/web-nfc.idl
index 4e0dc072..99e41f6 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/web-nfc.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/web-nfc.idl
@@ -10,7 +10,7 @@
 };
 
 dictionary NDEFMessageInit {
-  sequence<NDEFRecordInit> records;
+  required sequence<NDEFRecordInit> records;
 };
 
 [Exposed=Window]
@@ -28,7 +28,7 @@
 };
 
 dictionary NDEFRecordInit {
-  NDEFRecordType recordType;
+  required NDEFRecordType recordType;
   USVString mediaType;
   USVString id;
 
@@ -84,6 +84,7 @@
   NFCPushTarget target = "any";
   unrestricted double timeout = Infinity;
   boolean ignoreRead = true;
+  boolean overwrite = true;
   AbortSignal? signal;
 };
 
diff --git a/third_party/blink/web_tests/external/wpt/intersection-observer/document-scrolling-element-root.html b/third_party/blink/web_tests/external/wpt/intersection-observer/document-scrolling-element-root.html
new file mode 100644
index 0000000..9996299
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/intersection-observer/document-scrolling-element-root.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/intersection-observer-test-utils.js"></script>
+
+<style>
+pre, #log {
+  position: absolute;
+  top: 0;
+  left: 200px;
+}
+iframe {
+  height: 250px;
+  width: 150px;
+  border: 0;
+}
+</style>
+<iframe id="target-iframe" src="resources/iframe-no-root-subframe.html"></iframe>
+
+<script>
+var iframe = document.getElementById("target-iframe");
+var target;
+var root;
+var entries = [];
+
+iframe.onload = function() {
+  runTestCycle(function() {
+    assert_true(!!iframe, "iframe exists");
+
+    root = iframe.contentDocument.scrollingElement;
+    assert_true(!!root, "Root element exists.");
+    target = iframe.contentDocument.getElementById("target");
+    assert_true(!!target, "Target element exists.");
+    var observer = new IntersectionObserver(function(changes) {
+      entries = entries.concat(changes)
+    }, { root: root });
+    observer.observe(target);
+    entries = entries.concat(observer.takeRecords());
+    assert_equals(entries.length, 0, "No initial notifications.");
+    runTestCycle(step0, "First rAF.");
+  }, "Observer with explicit root which is the document's scrolling element.");
+};
+
+function step0() {
+  let vw = iframe.contentDocument.documentElement.clientWidth;
+  let vh = iframe.contentDocument.documentElement.clientHeight;
+  // The target element is partially clipped by the iframe's root scroller, so
+  // height of the intersection rect is (250 - 208) == 42.
+  checkLastEntry(entries, 0, [8, 108, 208, 308, 8, 108, 208, 250, 0, vw, 0, vh, true]);
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/lint.whitelist b/third_party/blink/web_tests/external/wpt/lint.whitelist
index d9ea5c1..afdc20b6 100644
--- a/third_party/blink/web_tests/external/wpt/lint.whitelist
+++ b/third_party/blink/web_tests/external/wpt/lint.whitelist
@@ -313,6 +313,7 @@
 SET TIMEOUT: html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html
 SET TIMEOUT: html/dom/documents/dom-tree-accessors/Document.currentScript.html
 SET TIMEOUT: html/webappapis/timers/*
+SET TIMEOUT: portals/history/resources/portal-harness.js
 SET TIMEOUT: resources/chromium/*
 SET TIMEOUT: resources/test/tests/functional/add_cleanup.html
 SET TIMEOUT: resources/test/tests/functional/add_cleanup_async.html
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_setpointercapture_inactive_button_mouse.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_setpointercapture_inactive_button_mouse.html
index fa9a5fb..b9172dd 100644
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_setpointercapture_inactive_button_mouse.html
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_setpointercapture_inactive_button_mouse.html
@@ -27,7 +27,7 @@
 
             var captureGot = false;
 
-            setup({ explicit_done: true });
+            setup({ single_test: true });
             add_completion_callback(showPointerTypes);
 
             function run() {
@@ -49,9 +49,7 @@
                  // When the setPointerCapture method is invoked, if the specified pointer is not in active button state, then the method must have no effect on subsequent pointer events.
                  // TA: 13.2
                 on_event(target0, "pointerout", function (event) {
-                    test(function() {
-                        assert_false(captureGot, "pointer capture is not set while button state is inactive")
-                    }, "pointer capture is not set while button state is inactive");
+                    assert_false(captureGot, "pointer capture is not set while button state is inactive")
                     // Make sure the test finishes after all the input actions are completed.
                     actions_promise.then( () => {
                         done();
diff --git a/third_party/blink/web_tests/external/wpt/portals/history/history-manipulation-inside-portal-with-subframes.html b/third_party/blink/web_tests/external/wpt/portals/history/history-manipulation-inside-portal-with-subframes.html
new file mode 100644
index 0000000..8f69182f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/portals/history/history-manipulation-inside-portal-with-subframes.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/run-test-in-portal.js"></script>
+<body>
+<script>
+  var portalSrc =
+      'resources/portal-manipulate-history-with-subframes.sub.html';
+
+  // Runs before and after the history manipulation in the portal to confirm
+  // that the session history of the portal host is not affected by any history
+  // changes in the portal.
+  function assertInitialHistoryState() {
+    assert_equals(history.length, 1);
+    assert_false(!!history.state);
+  }
+
+  promise_test(async () => {
+    assertInitialHistoryState();
+    await runTestInPortal(portalSrc, 'testIFrameSrcInPortal');
+    assertInitialHistoryState();
+  }, 'Setting iframe src navigates independently in a portal');
+
+  promise_test(async () => {
+    assertInitialHistoryState();
+    await runTestInPortal(portalSrc, 'testCrossSiteIFrameSrcInPortal');
+    assertInitialHistoryState();
+  }, 'Setting cross site iframe src navigates independently in a portal');
+
+  promise_test(async () => {
+    assertInitialHistoryState();
+    await runTestInPortal(portalSrc, 'testIFrameNavInPortal');
+    assertInitialHistoryState();
+  }, 'iframe navigates itself independently in a portal');
+
+  promise_test(async () => {
+    assertInitialHistoryState();
+    await runTestInPortal(portalSrc, 'testCrossSiteIFrameNavInPortal');
+    assertInitialHistoryState();
+  }, 'Cross site iframe navigates itself independently in a portal');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/portals/history/history-manipulation-inside-portal.html b/third_party/blink/web_tests/external/wpt/portals/history/history-manipulation-inside-portal.html
new file mode 100644
index 0000000..0fe88c4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/portals/history/history-manipulation-inside-portal.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/run-test-in-portal.js"></script>
+<body>
+<script>
+  var portalSrc =
+      'resources/portal-manipulate-history.html';
+
+  // Runs before and after the history manipulation in the portal to confirm
+  // that the session history of the portal host is not affected by any history
+  // changes in the portal.
+  function assertInitialHistoryState() {
+    assert_equals(history.length, 1);
+    assert_false(!!history.state);
+  }
+
+  promise_test(async () => {
+    assertInitialHistoryState();
+    await runTestInPortal(portalSrc, 'testHistoryPushStateInPortal');
+    assertInitialHistoryState();
+  }, 'history.pushState navigates independently in a portal');
+
+  promise_test(async () => {
+    assertInitialHistoryState();
+    await runTestInPortal(portalSrc, 'testHistoryReplaceStateInPortal');
+    assertInitialHistoryState();
+  }, 'history.replaceState navigates independently in a portal');
+
+  promise_test(async () => {
+    assertInitialHistoryState();
+    await runTestInPortal(portalSrc, 'testLocationAssignInPortal');
+    assertInitialHistoryState();
+  }, 'location.assign navigates independently in a portal');
+
+  promise_test(async () => {
+    assertInitialHistoryState();
+    await runTestInPortal(portalSrc, 'testLocationReplaceInPortal');
+    assertInitialHistoryState();
+  }, 'location.replace navigates independently in a portal');
+
+  promise_test(async () => {
+    assertInitialHistoryState();
+    await runTestInPortal(portalSrc, 'testSetLocationHrefInPortal');
+    assertInitialHistoryState();
+  }, 'Setting location.href navigates independently in a portal');
+
+  promise_test(async () => {
+    assertInitialHistoryState();
+    await runTestInPortal(portalSrc, 'testSyntheticAnchorClickInPortal');
+    assertInitialHistoryState();
+  }, 'Synthetic anchor click navigates independently in a portal');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/portals/history/resources/inner-iframe.html b/third_party/blink/web_tests/external/wpt/portals/history/resources/inner-iframe.html
new file mode 100644
index 0000000..5c6daa2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/portals/history/resources/inner-iframe.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<body>
+  <script>
+    window.onmessage = (e) => {
+      if (e.data == 'reportHistoryLength') {
+        e.source.postMessage(history.length, '*');
+      } else if (e.data == 'navigate') {
+        location.href = '#test';
+        e.source.postMessage('Done', '*');
+      }
+    };
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/portals/history/resources/portal-harness.js b/third_party/blink/web_tests/external/wpt/portals/history/resources/portal-harness.js
new file mode 100644
index 0000000..9761ac9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/portals/history/resources/portal-harness.js
@@ -0,0 +1,30 @@
+// We don't have the test harness in this context, so we roll our own
+// which communicates with our host which is actually running the tests.
+
+window.onload = async () => {
+  let urlParams = new URLSearchParams(window.location.search);
+  let testName = urlParams.get('testName');
+  let testFn = window[testName];
+  if (!testFn) {
+    window.portalHost.postMessage('Missing test: ' + testName, '*');
+    return;
+  }
+
+  // The document load event is not finished at this point, so navigations
+  // would be done with replacement. This interferes with our tests. We wait
+  // for the next task before navigating to avoid this.
+  await new Promise((resolve) => { window.setTimeout(resolve); });
+
+  try {
+    await testFn();
+    window.portalHost.postMessage('Passed', '*');
+  } catch (e) {
+    window.portalHost.postMessage(
+        'Failed: ' + e.name + ': ' + e.message, '*');
+  }
+};
+
+function assert(condition, message) {
+  if (!condition)
+    throw new Error('Assertion failed: ' + message);
+}
diff --git a/third_party/blink/web_tests/external/wpt/portals/history/resources/portal-manipulate-history-with-subframes.sub.html b/third_party/blink/web_tests/external/wpt/portals/history/resources/portal-manipulate-history-with-subframes.sub.html
new file mode 100644
index 0000000..4b3e8cf2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/portals/history/resources/portal-manipulate-history-with-subframes.sub.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<script src="portal-harness.js"></script>
+<body>
+<script>
+  function messageFrameAndAwaitResponse(frame, message) {
+    return new Promise((resolve) => {
+      window.onmessage = (e) => {
+        resolve(e.data);
+      };
+      frame.contentWindow.postMessage(message, '*');
+    });
+  }
+
+  function innerFrameUrl(crossSite) {
+    return (crossSite ?
+                'https://{{hosts[alt][www]}}:{{ports[https][0]}}' : '') +
+            '/portals/history/resources/inner-iframe.html'
+  }
+
+  async function runTestIFrameSrcInPortal(crossSite) {
+    assert(history.length == 1, 'Initial history length');
+
+    let iframe = document.createElement('iframe');
+    iframe.src = innerFrameUrl(crossSite);
+    await new Promise((resolve) => {
+      iframe.onload = resolve;
+      document.body.appendChild(iframe);
+    });
+
+    let frameHistoryLength =
+        await messageFrameAndAwaitResponse(iframe, 'reportHistoryLength');
+    assert(history.length == 1, 'History length unchanged when iframe added');
+    assert(frameHistoryLength == 1, 'History length in iframe when added');
+
+    iframe.src = iframe.src + '#test';
+
+    frameHistoryLength =
+        await messageFrameAndAwaitResponse(iframe, 'reportHistoryLength');
+    assert(
+        history.length == 2, 'History length changed when iframe src set');
+    assert(
+        frameHistoryLength == 2,
+        'History length in iframe changed when iframe src set');
+  }
+
+  function testIFrameSrcInPortal() {
+    return runTestIFrameSrcInPortal(false);
+  }
+
+  function testCrossSiteIFrameSrcInPortal() {
+    return runTestIFrameSrcInPortal(true);
+  }
+
+  async function runTestIFrameNavInPortal(crossSite) {
+    assert(history.length == 1, 'Initial history length');
+
+    let iframe = document.createElement('iframe');
+    iframe.src = innerFrameUrl(crossSite);
+    await new Promise((resolve) => {
+      iframe.onload = resolve;
+      document.body.appendChild(iframe);
+    });
+
+    await messageFrameAndAwaitResponse(iframe, 'navigate');
+
+    let frameHistoryLength =
+        await messageFrameAndAwaitResponse(iframe, 'reportHistoryLength');
+    assert(
+        history.length == 2, 'History length changed when iframe navigates');
+    assert(
+        frameHistoryLength == 2,
+        'History length in iframe changed when iframe navigates');
+  }
+
+  function testIFrameNavInPortal() {
+    return runTestIFrameNavInPortal(false);
+  }
+
+  function testCrossSiteIFrameNavInPortal() {
+    return runTestIFrameNavInPortal(true);
+  }
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/portals/history/resources/portal-manipulate-history.html b/third_party/blink/web_tests/external/wpt/portals/history/resources/portal-manipulate-history.html
new file mode 100644
index 0000000..591fd21
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/portals/history/resources/portal-manipulate-history.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<script src="portal-harness.js"></script>
+<body>
+<script>
+  function testHistoryPushStateInPortal() {
+    assert(history.length == 1, 'Initial history length');
+    assert(!history.state, 'Initial history state');
+
+    history.pushState('teststate', null, null);
+
+    assert(history.length == 2, 'History length changed');
+    assert(history.state == 'teststate', 'Update state');
+  }
+
+  function testHistoryReplaceStateInPortal() {
+    assert(history.length == 1, 'Initial history length');
+    assert(!history.state, 'Initial history state');
+
+    history.replaceState('teststate', null, null);
+
+    assert(history.length == 1, 'History length unchanged');
+    assert(history.state == 'teststate', 'Update state');
+  }
+
+  function testLocationAssignInPortal() {
+    assert(history.length == 1, 'Initial history length');
+    let initialLocation = location.href;
+    location.assign('#test');
+
+    assert(history.length == 2, 'History length changed');
+    assert(location.href != initialLocation, 'Update location');
+  }
+
+  function testLocationReplaceInPortal() {
+    assert(history.length == 1, 'Initial history length');
+    let initialLocation = location.href;
+    location.replace('#test');
+
+    assert(history.length == 1, 'History length unchanged');
+    assert(location.href != initialLocation, 'Update location');
+  }
+
+  function testSetLocationHrefInPortal() {
+    assert(history.length == 1, 'Initial history length');
+    let initialLocation = location.href;
+    location.href = '#test';
+
+    assert(history.length == 2, 'History length changed');
+    assert(location.href != initialLocation, 'Update location');
+  }
+
+  function testSyntheticAnchorClickInPortal() {
+    assert(history.length == 1, 'Initial history length');
+    let initialLocation = location.href;
+
+    var anchor = document.createElement('a');
+    anchor.href = '#test';
+    document.body.appendChild(anchor);
+
+    anchor.click();
+
+    assert(history.length == 2, 'History length changed');
+    assert(location.href != initialLocation, 'Update location');
+  }
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/portals/history/resources/run-test-in-portal.js b/third_party/blink/web_tests/external/wpt/portals/history/resources/run-test-in-portal.js
new file mode 100644
index 0000000..c982a1fa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/portals/history/resources/run-test-in-portal.js
@@ -0,0 +1,16 @@
+// This is called from the portal host which is running with the test harness.
+// This creates a portal and communicates with our ad hoc test harness in the
+// portal context which performs the history manipulation in the portal. We
+// confirm that the history manipulation works as expected in the portal.
+async function runTestInPortal(portalSrc, testName) {
+  let portal = document.createElement('portal');
+  portal.src = portalSrc + '?testName=' + testName;
+  let result = await new Promise((resolve) => {
+    portal.onmessage = (e) => {
+      resolve(e.data);
+    };
+    document.body.appendChild(portal);
+  });
+
+  assert_equals(result, 'Passed');
+}
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-click-method.html b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-click-method.html
new file mode 100644
index 0000000..92212cd85
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-click-method.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: click on shadow host with delegatesFocus</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+
+<body>
+<div id="host">
+  <div id="slotted">slotted</div>
+</div>
+<div id="outside">outside</div>
+</body>
+
+<script>
+const host = document.getElementById("host");
+const slotted = document.getElementById("slotted");
+
+const shadowRoot = host.attachShadow({ mode: "open", delegatesFocus: true });
+const aboveSlot = document.createElement("div");
+aboveSlot.innerText = "aboveSlot";
+const slot = document.createElement("slot");
+shadowRoot.appendChild(aboveSlot);
+shadowRoot.appendChild(slot);
+
+const elementsInFlatTreeOrder = [host, aboveSlot, slot, slotted, outside];
+
+// Final structure:
+// <div #host> (delegatesFocus=true)
+//    #shadowRoot
+//      <div #aboveSlot>
+//      <slot #slot>
+//        (slotted) <div #slotted>
+// <div #outside>
+
+function setAllTabIndex(value) {
+  setTabIndex(elementsInFlatTreeOrder, value);
+}
+
+function removeAllTabIndex() {
+  removeTabIndex(elementsInFlatTreeOrder);
+}
+
+function resetTabIndexAndFocus() {
+  removeAllTabIndex();
+  resetFocus(document);
+  resetFocus(shadowRoot);
+}
+
+test(() => {
+  resetTabIndexAndFocus();
+  setAllTabIndex(0);
+  host.click();
+  assert_equals(shadowRoot.activeElement, null);
+  assert_equals(document.activeElement, document.body);
+}, "call click() on host with delegatesFocus, all tabindex=0");
+
+test(() => {
+  resetTabIndexAndFocus();
+  setAllTabIndex(0);
+  slotted.click();
+  assert_equals(shadowRoot.activeElement, null);
+  assert_equals(document.activeElement, document.body);
+}, "call click() on slotted element in delegatesFocus shadow tree, all tabindex=0");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-tabindex-varies-expected.txt b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-tabindex-varies-expected.txt
new file mode 100644
index 0000000..b80d771
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-tabindex-varies-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL click on host with delegatesFocus, #aboveSlot tabindex = 2, #slot and #slotted tabindex = 1 assert_equals: expected Element node <div tabindex="2">aboveSlot</div> but got null
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-tabindex-varies.html b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-tabindex-varies.html
new file mode 100644
index 0000000..4051db12
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-tabindex-varies.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: click on shadow host with delegatesFocus</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+
+<body>
+<div id="host">
+  <div id="slotted">slotted</div>
+</div>
+<div id="outside">outside</div>
+</body>
+
+<script>
+const host = document.getElementById("host");
+const slotted = document.getElementById("slotted");
+
+const shadowRoot = host.attachShadow({ mode: "open", delegatesFocus: true });
+const aboveSlot = document.createElement("div");
+aboveSlot.innerText = "aboveSlot";
+const slot = document.createElement("slot");
+// Add an unfocusable spacer, because test_driver.click will click on the
+// center point of #host, and we don't want the click to land on #aboveSlot
+// or #slot.
+const spacer = document.createElement("div");
+spacer.style = "height: 1000px;";
+shadowRoot.appendChild(spacer);
+shadowRoot.appendChild(aboveSlot);
+shadowRoot.appendChild(slot);
+
+const elementsInFlatTreeOrder = [host, aboveSlot, spacer, slot, slotted, outside];
+
+// Final structure:
+// <div #host> (delegatesFocus=true)
+//    #shadowRoot
+//      <div #spacer>
+//      <div #aboveSlot>
+//      <slot #slot>
+//        (slotted) <div #slotted>
+// <div #outside>
+
+function setAllTabIndex(value) {
+  setTabIndex(elementsInFlatTreeOrder, value);
+}
+
+function removeAllTabIndex() {
+  removeTabIndex(elementsInFlatTreeOrder);
+}
+
+function resetTabIndexAndFocus() {
+  removeAllTabIndex();
+  resetFocus(document);
+  resetFocus(shadowRoot);
+}
+
+promise_test(async () => {
+  resetTabIndexAndFocus();
+  setTabIndex([aboveSlot], 2);
+  setTabIndex([slot, slotted], 1);
+  await test_driver.click(host);
+  assert_equals(shadowRoot.activeElement, aboveSlot);
+  assert_equals(document.activeElement, host);
+}, "click on host with delegatesFocus, #aboveSlot tabindex = 2, #slot and #slotted tabindex = 1");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-tabindex-zero.html b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-tabindex-zero.html
new file mode 100644
index 0000000..5f7914f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/click-focus-delegatesFocus-tabindex-zero.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: click on shadow host with delegatesFocus</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+
+<body>
+<div id="host">
+  <div id="slotted">slotted</div>
+</div>
+<div id="outside">outside</div>
+</body>
+
+<script>
+const host = document.getElementById("host");
+const slotted = document.getElementById("slotted");
+
+const shadowRoot = host.attachShadow({ mode: "open", delegatesFocus: true });
+const aboveSlot = document.createElement("div");
+aboveSlot.innerText = "aboveSlot";
+const slot = document.createElement("slot");
+// Add an unfocusable spacer, because test_driver.click will click on the
+// center point of #host, and we don't want the click to land on #aboveSlot
+// or #slot.
+const spacer = document.createElement("div");
+spacer.style = "height: 1000px;";
+shadowRoot.appendChild(spacer);
+shadowRoot.appendChild(aboveSlot);
+shadowRoot.appendChild(slot);
+
+const elementsInFlatTreeOrder = [host, aboveSlot, spacer, slot, slotted, outside];
+
+// Final structure:
+// <div #host> (delegatesFocus=true)
+//    #shadowRoot
+//      <div #spacer>
+//      <div #aboveSlot>
+//      <slot #slot>
+//        (slotted) <div #slotted>
+// <div #outside>
+
+function setAllTabIndex(value) {
+  setTabIndex(elementsInFlatTreeOrder, value);
+}
+
+function removeAllTabIndex() {
+  removeTabIndex(elementsInFlatTreeOrder);
+}
+
+function resetTabIndexAndFocus() {
+  removeAllTabIndex();
+  resetFocus(document);
+  resetFocus(shadowRoot);
+}
+
+promise_test(async () => {
+  resetTabIndexAndFocus();
+  setAllTabIndex(0);
+  removeTabIndex([spacer]);
+  await test_driver.click(host);
+  assert_equals(shadowRoot.activeElement, aboveSlot);
+  assert_equals(document.activeElement, host);
+}, "click on host with delegatesFocus, all tabindex=0 except spacer");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-method-delegatesFocus-expected.txt b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-method-delegatesFocus-expected.txt
new file mode 100644
index 0000000..ae3eaa3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-method-delegatesFocus-expected.txt
@@ -0,0 +1,20 @@
+This is a testharness.js-based test.
+PASS focus() on host with delegatesFocus, all tabindex=0
+PASS focus() on host with delegatesFocus & tabindex =-1, all other tabindex=0
+PASS focus() on host with delegatesFocus & no tabindex, all other tabindex=0
+FAIL focus() on host with delegatesFocus & tabindex = 0, all other tabindex=-1 assert_equals: expected Element node <div tabindex="-1">aboveSlots</div> but got null
+FAIL focus() on host with delegatesFocus, all without tabindex assert_equals: expected Element node <body>
+<div id="host">
+  <div id="slottedToSecondSlot" sl... but got Element node <div id="host">
+  <div id="slottedToSecondSlot" slot="sec...
+FAIL focus() on host with delegatesFocus, all tabindex=-1 assert_equals: expected Element node <div tabindex="-1">aboveSlots</div> but got null
+PASS focus() on host with delegatesFocus & tabindex=0, #belowSlots with tabindex=0
+FAIL focus() on host with delegatesFocus & tabindex=0, #outside with tabindex=0 assert_equals: expected Element node <body>
+<div id="host" tabindex="0">
+  <div id="slottedToS... but got Element node <div id="host" tabindex="0">
+  <div id="slottedToSecondSl...
+PASS focus() on host with delegatesFocus & tabindex=0, #aboveSlots and #belowSlots with tabindex=0
+FAIL focus() on host with delegatesFocus & tabindex=0, #aboveSlots with tabindex=0 and #belowSlots with tabindex=1 assert_equals: expected Element node <div tabindex="0">aboveSlots</div> but got Element node <div tabindex="1">belowSlots</div>
+PASS focus() on host with delegatesFocus & tabindex=0, #slottedToFirstSlot, #slottedToSecondSlot, #belowSlots  with tabindex=0
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-method-delegatesFocus.html b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-method-delegatesFocus.html
new file mode 100644
index 0000000..462542e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-method-delegatesFocus.html
@@ -0,0 +1,232 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus() on shadow host with delegatesFocus</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+
+<body>
+<div id="host">
+  <div id="slottedToSecondSlot" slot="secondSlot">slottedToSecondSlot</div>
+  <div id="slottedToFirstSlot" slot="firstSlot">slottedToFirstSlot</div>
+</div>
+<div id="outside">outside</div>
+</body>
+
+<script>
+const host = document.getElementById("host");
+const slottedToSecondSlot = document.getElementById("slottedToSecondSlot");
+const slottedToFirstSlot = document.getElementById("slottedToFirstSlot");
+const outside = document.getElementById("outside");
+
+const shadowRoot = host.attachShadow({ mode: "open", delegatesFocus: true });
+const aboveSlots = document.createElement("div");
+aboveSlots.innerText = "aboveSlots";
+const firstSlot = document.createElement("slot");
+firstSlot.name = "firstSlot";
+const secondSlot = document.createElement("slot");
+secondSlot.name = "secondSlot";
+const belowSlots = document.createElement("div");
+belowSlots.innerText = "belowSlots";
+shadowRoot.appendChild(aboveSlots);
+shadowRoot.appendChild(firstSlot);
+shadowRoot.appendChild(secondSlot);
+shadowRoot.appendChild(belowSlots);
+
+const elementsInFlatTreeOrder = [host, aboveSlots, firstSlot,
+  slottedToFirstSlot, secondSlot, slottedToSecondSlot, belowSlots, outside];
+
+// Final structure:
+// <div #host> (delegatesFocus=true)
+//    #shadowRoot
+//      <div #aboveSlots>
+//      <slot #firstSlot>
+//        (slotted) <div #slottedToFirstSlot>
+//      <slot #secondSlot>
+//        (slotted) <div #slottedToSecondSlot>
+//      <div #belowSlots>
+// <div #outside>
+
+
+function setAllTabIndex(value) {
+  setTabIndex(elementsInFlatTreeOrder, value);
+}
+
+function removeAllTabIndex() {
+  removeTabIndex(elementsInFlatTreeOrder);
+}
+
+function resetTabIndexAndFocus() {
+  removeAllTabIndex();
+  resetFocus(document);
+  resetFocus(shadowRoot);
+}
+
+test(() => {
+  resetTabIndexAndFocus();
+  setAllTabIndex(0);
+  // Structure:
+  // <div #host> (delegatesFocus=true) tabindex=0
+  //    #shadowRoot
+  //      <div #aboveSlots> tabindex=0
+  //      <slot #firstSlot> tabindex=0
+  //        (slotted) <div #slottedToFirstSlot> tabindex=0
+  //      <slot #secondSlot> tabindex=0
+  //        (slotted) <div #slottedToSecondSlot> tabindex=0
+  //      <div #belowSlots> tabindex=0
+  // <div #outside> tabindex=0
+  // First focusable = #aboveSlots
+  host.focus();
+  assert_equals(shadowRoot.activeElement, aboveSlots);
+  assert_equals(document.activeElement, host);
+}, "focus() on host with delegatesFocus, all tabindex=0");
+
+test(() => {
+  resetTabIndexAndFocus();
+  setAllTabIndex(0);
+  setTabIndex([host], -1);
+  // First focusable = #aboveSlots
+  host.focus();
+  assert_equals(shadowRoot.activeElement, aboveSlots);
+  assert_equals(document.activeElement, host);
+}, "focus() on host with delegatesFocus & tabindex =-1, all other tabindex=0");
+
+test(() => {
+  resetTabIndexAndFocus();
+  setTabIndex([aboveSlots, slottedToFirstSlot, slottedToSecondSlot, belowSlots], 0);
+  // First focusable = #aboveSlots
+  host.focus();
+  assert_equals(shadowRoot.activeElement, aboveSlots);
+  assert_equals(document.activeElement, host);
+}, "focus() on host with delegatesFocus & no tabindex, all other tabindex=0");
+
+test(() => {
+  resetTabIndexAndFocus();
+  setAllTabIndex(-1);
+  setTabIndex([host], 0);
+  // First focusable = #aboveSlots
+  host.focus();
+  assert_equals(shadowRoot.activeElement, aboveSlots);
+  assert_equals(document.activeElement, host);
+}, "focus() on host with delegatesFocus & tabindex = 0, all other tabindex=-1");
+
+test(() => {
+  resetTabIndexAndFocus();
+  removeAllTabIndex();
+  // No focusable element under #host in the flat tree.
+  host.focus();
+  assert_equals(shadowRoot.activeElement, null);
+  assert_equals(document.activeElement, document.body);
+}, "focus() on host with delegatesFocus, all without tabindex");
+
+test(() => {
+  resetTabIndexAndFocus();
+  // First focusable = #aboveSlots
+  setAllTabIndex(-1);
+  host.focus();
+  assert_equals(shadowRoot.activeElement, aboveSlots);
+  assert_equals(document.activeElement, host);
+}, "focus() on host with delegatesFocus, all tabindex=-1");
+
+test(() => {
+  resetTabIndexAndFocus();
+  removeAllTabIndex();
+  setTabIndex([host, belowSlots], 0);
+  // Structure:
+  // <div #host> (delegatesFocus=true) tabindex=0
+  //    #shadowRoot
+  //      <div #aboveSlots>
+  //      <slot #firstSlot>
+  //        (slotted) <div #slottedToFirstSlot>
+  //      <slot #secondSlot>
+  //        (slotted) <div #slottedToSecondSlot>
+  //      <div #belowSlots> tabindex=0
+  // <div #outside>
+  // First focusable = #belowSlots
+  host.focus();
+  assert_equals(shadowRoot.activeElement, belowSlots);
+  assert_equals(document.activeElement, host);
+}, "focus() on host with delegatesFocus & tabindex=0, #belowSlots with tabindex=0");
+
+test(() => {
+  resetTabIndexAndFocus();
+  removeAllTabIndex();
+  setTabIndex([host, outside], 0);
+  // Structure:
+  // <div #host> (delegatesFocus=true) tabindex=0
+  //    #shadowRoot
+  //      <div #aboveSlots>
+  //      <slot #firstSlot>
+  //        (slotted) <div #slottedToFirstSlot>
+  //      <slot #secondSlot>
+  //        (slotted) <div #slottedToSecondSlot>
+  //      <div #belowSlots>
+  // <div #outside> tabindex=0
+  // No focusable element under #host in the flat tree.
+  host.focus();
+  assert_equals(shadowRoot.activeElement, null);
+  assert_equals(document.activeElement, document.body);
+}, "focus() on host with delegatesFocus & tabindex=0, #outside with tabindex=0");
+
+test(() => {
+  resetTabIndexAndFocus();
+  setTabIndex([host, aboveSlots, belowSlots], 0);
+  // Structure:
+  // <div #host> (delegatesFocus=true) tabindex=0
+  //    #shadowRoot
+  //      <div #aboveSlots> tabindex=0
+  //      <slot #firstSlot>
+  //        (slotted) <div #slottedToFirstSlot>
+  //      <slot #secondSlot>
+  //        (slotted) <div #slottedToSecondSlot>
+  //      <div #belowSlots> tabindex=0
+  // <div #outside>
+  // First focusable = #aboveSlots
+  host.focus();
+  assert_equals(shadowRoot.activeElement, aboveSlots);
+  assert_equals(document.activeElement, host);
+}, "focus() on host with delegatesFocus & tabindex=0, #aboveSlots and #belowSlots with tabindex=0");
+
+test(() => {
+  resetTabIndexAndFocus();
+  setTabIndex([host, aboveSlots], 0);
+  setTabIndex([belowSlots], 1);
+  // Structure:
+  // <div #host> (delegatesFocus=true) tabindex=0
+  //    #shadowRoot
+  //      <div #aboveSlots> tabindex=0
+  //      <slot #firstSlot>
+  //        (slotted) <div #slottedToFirstSlot>
+  //      <slot #secondSlot>
+  //        (slotted) <div #slottedToSecondSlot>
+  //      <div #belowSlots> tabindex=1
+  // <div #outside>
+  // First focusable = #aboveSlots
+  host.focus();
+  assert_equals(shadowRoot.activeElement, aboveSlots);
+  assert_equals(document.activeElement, host);
+}, "focus() on host with delegatesFocus & tabindex=0, #aboveSlots with tabindex=0 and #belowSlots with tabindex=1");
+  
+test(() => {
+  resetTabIndexAndFocus();
+  setTabIndex([host, slottedToFirstSlot, slottedToSecondSlot, belowSlots], 0);
+  // Structure:
+  // <div #host> (delegatesFocus=true) tabindex=0
+  //    #shadowRoot
+  //      <div #aboveSlots>
+  //      <slot #firstSlot>
+  //        (slotted) <div #slottedToFirstSlot> tabindex=0
+  //      <slot #secondSlot>
+  //        (slotted) <div #slottedToSecondSlot> tabindex=0
+  //      <div #belowSlots> tabindex=0
+  // <div #outside>
+  // First focusable = #slottedToFirstSlot
+  host.focus();
+  assert_equals(shadowRoot.activeElement, null);
+  assert_equals(document.activeElement, slottedToFirstSlot);
+}, "focus() on host with delegatesFocus & tabindex=0, #slottedToFirstSlot, #slottedToSecondSlot, #belowSlots  with tabindex=0");
+
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tabindex-order-shadow-negative-delegatesFocus.html b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tabindex-order-shadow-negative-delegatesFocus.html
new file mode 100644
index 0000000..356b0bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tabindex-order-shadow-negative-delegatesFocus.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom that delegates focus and all tabindex=-1 in shadow tree</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=0>
+//    #shadowRoot (delegatesFocus)
+//      <div #aboveSlot tabindex=-1>
+//      <slot #slotAbove tabindex=-1>
+//        (slotted) <div #slottedAbove tabindex=-1>
+//      <slot #slotBelow tabindex=-1>
+//        (slotted) <div #slottedBelow tabindex=-1>
+//      <div #belowSlot tabindex=-1>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] = elementsInFlatTreeOrder = prepareDOM(document.body, true);
+  setTabIndex(elementsInFlatTreeOrder, -1);
+  setTabIndex([aboveHost, host, belowHost], 0);
+  resetFocus();
+  // Focus should only land on #aboveHost and #belowHost (all others are non-sequentially focusable).
+  return assertFocusOrder([aboveHost, belowHost]);
+}, "Order when all tabindex=-1 is and delegatesFocus = true");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tabindex-order-shadow-varying-delegatesFocus.html b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tabindex-order-shadow-varying-delegatesFocus.html
new file mode 100644
index 0000000..67899cf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tabindex-order-shadow-varying-delegatesFocus.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom that delegates focus and tabindex in shadow tree varies</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=0>
+//    #shadowRoot (delegatesFocus)
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=1>
+//        (slotted) <div #slottedAbove tabindex=3>
+//      <slot #slotBelow tabindex=2>
+//        (slotted) <div #slottedBelow tabindex=1>
+//      <div #belowSlot tabindex=-1>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] = elementsInFlatTreeOrder = prepareDOM(document.body, true);
+  setTabIndex(elementsInFlatTreeOrder, -1);
+  setTabIndex([aboveHost, host, aboveSlot, belowHost], 0);
+  setTabIndex([slotAbove, slottedBelow], 1);
+  setTabIndex([slotBelow], 2);
+  setTabIndex([slottedAbove], 3);
+  resetFocus();
+  return assertFocusOrder([aboveHost, slottedAbove, slottedBelow, aboveSlot, belowHost]);
+}, "Order when tabindex varies and delegatesFocus = true");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tabindex-order-shadow-zero-delegatesFocus.html b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tabindex-order-shadow-zero-delegatesFocus.html
new file mode 100644
index 0000000..5e6ab3a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tabindex-order-shadow-zero-delegatesFocus.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom that delegates focus and all tabindex=0</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=0>
+//    #shadowRoot (delegatesFocus)
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=0>
+//        (slotted) <div #slottedAbove tabindex=0>
+//      <slot #slotBelow tabindex=0>
+//        (slotted) <div #slottedBelow tabindex=0>
+//      <div #belowSlot tabindex=0>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] = elementsInFlatTreeOrder = prepareDOM(document.body, true);
+  setTabIndex(elementsInFlatTreeOrder, 0);
+  resetFocus();
+  // Focus should move in flat tree order since every one of them has tabindex ==0,
+  // but doesn't include slots and #host.
+  return assertFocusOrder([aboveHost, aboveSlot, slottedAbove, slottedBelow, belowSlot, belowHost]);
+}, "Order when all tabindex=0 is and delegatesFocus = true");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/storage-access-api/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/storage-access-api/idlharness.window-expected.txt
new file mode 100644
index 0000000..5872196
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/storage-access-api/idlharness.window-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS idl_test validation
+FAIL Partial interface Document: original interface defined assert_true: Original interface should be defined expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/storage-access-api/idl.window.js b/third_party/blink/web_tests/external/wpt/storage-access-api/idlharness.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/storage-access-api/idl.window.js
rename to third_party/blink/web_tests/external/wpt/storage-access-api/idlharness.window.js
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/run_tc.py b/third_party/blink/web_tests/external/wpt/tools/ci/run_tc.py
index 956d1c3..60f1758 100755
--- a/third_party/blink/web_tests/external/wpt/tools/ci/run_tc.py
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/run_tc.py
@@ -42,6 +42,8 @@
 import subprocess
 import sys
 import tempfile
+from socket import error as SocketError  # NOQA: N812
+import errno
 try:
     from urllib2 import urlopen
 except ImportError:
@@ -161,15 +163,41 @@
     run(["sudo", "apt-get", "-qqy", "-t", "bionic-wpt-webkit-updates", "install", "webkit2gtk-driver"])
 
 
+# Download an URL in chunks and saves it to a file descriptor (truncating it)
+# It doesn't close the descriptor, but flushes it on success.
+# It retries the download in case of ECONNRESET up to max_retries.
+def download_url_to_descriptor(fd, url, max_retries=3):
+    download_succeed = False
+    if max_retries < 0:
+        max_retries = 0
+    for current_retry in range(max_retries+1):
+        try:
+            resp = urlopen(url)
+            # We may come here in a retry, ensure to truncate fd before start writing.
+            fd.seek(0)
+            fd.truncate(0)
+            while True:
+                chunk = resp.read(16*1024)
+                if not chunk:
+                    break  # Download finished
+                fd.write(chunk)
+            fd.flush()
+            download_succeed = True
+            break  # Sucess
+        except SocketError as e:
+            if e.errno != errno.ECONNRESET:
+                raise  # Unknown error
+            if current_retry < max_retries:
+                print("ERROR: Connection reset by peer. Retrying ...")
+                continue  # Retry
+    return download_succeed
+
+
 def install_webkitgtk_from_tarball_bundle(channel):
     with tempfile.NamedTemporaryFile(suffix=".tar.xz") as temp_tarball:
-        resp = urlopen("https://webkitgtk.org/built-products/nightly/webkitgtk-nightly-build-last.tar.xz")
-        while True:
-            chunk = resp.read(16*1024)
-            if not chunk:
-                break
-            temp_tarball.write(chunk)
-        temp_tarball.flush()
+        download_url = "https://webkitgtk.org/built-products/nightly/webkitgtk-nightly-build-last.tar.xz"
+        if not download_url_to_descriptor(temp_tarball, download_url):
+            raise RuntimeError("Can't download %s. Aborting" % download_url)
         run(["sudo", "tar", "xfa", temp_tarball.name, "-C", "/"])
     # Install dependencies
     run(["sudo", "apt-get", "-qqy", "update"])
diff --git a/third_party/blink/web_tests/external/wpt/tools/wpt/commands.json b/third_party/blink/web_tests/external/wpt/tools/wpt/commands.json
index 178eda9..60fe162 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wpt/commands.json
+++ b/third_party/blink/web_tests/external/wpt/tools/wpt/commands.json
@@ -63,6 +63,13 @@
     "help": "Print branch point from master",
     "virtualenv": false
   },
+  "rev-list": {
+    "path": "revlist.py",
+    "script": "run_rev_list",
+    "parser": "get_parser",
+    "help": "List tagged revisions at regular intervals",
+    "virtualenv": false
+  },
   "install-android-emulator": {
     "path": "android.py",
     "script": "run_install",
diff --git a/third_party/blink/web_tests/external/wpt/tools/wpt/revlist.py b/third_party/blink/web_tests/external/wpt/tools/wpt/revlist.py
new file mode 100644
index 0000000..f750311
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/wpt/revlist.py
@@ -0,0 +1,118 @@
+import argparse
+import logging
+import os
+import time
+from tools.wpt.testfiles import get_git_cmd
+
+here = os.path.dirname(__file__)
+wpt_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir))
+
+logger = logging.getLogger()
+
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Any
+    from typing import Dict
+    from typing import List
+    from typing import Text
+
+
+def calculate_cutoff_date(until, epoch, offset):
+    return ((((until - offset) // epoch)) * epoch) + offset
+
+
+def parse_epoch(string):
+    # type: (str) -> int
+    UNIT_DICT = {"h": 3600, "d": 86400, "w": 604800}
+    base = string[:-1]
+    unit = string[-1:]
+    if base.isdigit() and unit in UNIT_DICT:
+        return int(base) * UNIT_DICT[unit]
+    raise argparse.ArgumentTypeError('must be digits followed by h/d/w')
+
+
+def get_tagged_revisions(pattern):
+    # type: (bytes) -> List[..., Dict]
+    '''
+    Returns the tagged revisions indexed by the committer date.
+    '''
+    git = get_git_cmd(wpt_root)
+    args = [
+        pattern,
+        '--sort=-committerdate',
+        '--format=%(refname:lstrip=2) %(objectname) %(committerdate:raw)',
+        '--count=100000'
+    ]
+    for line in git("for-each-ref", *args).splitlines():
+        tag, commit, date, _ = line.split(" ")
+        date = int(date)
+        yield tag, commit, date
+
+
+def list_tagged_revisons(epoch, max_count):
+    # type: (**Any) -> List[Text]
+    logger.debug("list_tagged_revisons(%s, %s)" % (epoch, max_count))
+    # Set an offset to start to count the the weekly epoch from
+    # Monday 00:00:00. This is particularly important for the weekly epoch
+    # because fix the start of the epoch to Monday. This offset is calculated
+    # from Thursday, 1 January 1970 0:00:00 to Monday, 5 January 1970 0:00:00
+    epoch_offset = 345600
+    # "epoch_threshold" set a safety margin after this time it is fine to
+    # consider that any tags are created and pushed.
+    epoch_threshold = 600
+    epoch_until = int(time.time()) - epoch_threshold
+    count = 0
+
+    # Iterates the tagged revisions in descending order finding the more
+    # recent commit still older than a "cutoff_date" value.
+    # When a commit is found "cutoff_date" is set to a new value multiplier of
+    # "epoch" but still below of the date of the current commit found.
+    # This needed to deal with intervals where no candidates were found
+    # for the current "epoch" and the next candidate found is yet below
+    # the lower values of the interval (it is the case of J and I for the
+    # interval between Wed and Tue, in the example). The algorithm fix
+    # the next "cutoff_date" value based on the date value of the current one
+    # skipping the intermediate values.
+    # The loop ends once we reached the required number of revisions to return
+    # or the are no more tagged revisions or the cutoff_date reach zero.
+    #
+    #   Fri   Sat   Sun   Mon   Tue   Wed   Thu   Fri   Sat
+    #    |     |     |     |     |     |     |     |     |
+    # -A---B-C---DEF---G---H--IJ----------K-----L-M----N--O--
+    #                                                       ^
+    #                                                      now
+    # Expected result: N,M,K,J,H,G,F,C,A
+
+    cutoff_date = calculate_cutoff_date(epoch_until, epoch, epoch_offset)
+    for _, commit, date in get_tagged_revisions("refs/tags/merge_pr_*"):
+        if count >= max_count:
+            return
+        if date < cutoff_date:
+            print(commit)
+            count += 1
+            cutoff_date = calculate_cutoff_date(date, epoch, epoch_offset)
+
+
+def get_parser():
+    # type: () -> argparse.ArgumentParser
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--epoch",
+                        default="1d",
+                        type=parse_epoch,
+                        help="regular interval of time selected to get the "
+                             "tagged revisions. Valid values are digits "
+                             "followed by h/d/w (e.x. 9h, 9d, 9w ...) where "
+                             "the mimimun selectable interval is one hour "
+                             "(1h)")
+    parser.add_argument("--max-count",
+                        default=1,
+                        type=int,
+                        help="maximum number of revisions to be returned by "
+                             "the command")
+    return parser
+
+
+def run_rev_list(**kwargs):
+    # type: (**Any) -> None
+    list_tagged_revisons(kwargs["epoch"], kwargs["max_count"])
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/wptreport.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/wptreport.py
index d73911f..6d4401a 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/wptreport.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/wptreport.py
@@ -62,15 +62,15 @@
         if 'run_info' in data:
             self.results['run_info'] = data['run_info']
         self.results['time_start'] = data['time']
+        self.results["results"] = []
 
     def suite_end(self, data):
         self.results['time_end'] = data['time']
-        self.results["results"] = []
         for test_name in self.raw_results:
             result = {"test": test_name}
             result.update(self.raw_results[test_name])
             self.results["results"].append(result)
-        return json.dumps(self.results)
+        return json.dumps(self.results) + "\n"
 
     def find_or_create_test(self, data):
         test_name = data["test"]
@@ -126,6 +126,11 @@
                 for item in data["extra"]["reftest_screenshots"]
                 if type(item) == dict
             }
+        test_name = data["test"]
+        result = {"test": data["test"]}
+        result.update(self.raw_results[test_name])
+        self.results["results"].append(result)
+        self.raw_results.pop(test_name)
 
     def assertion_count(self, data):
         test = self.find_or_create_test(data)
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/metadata.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/metadata.py
index 15bbf94..c328dce 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/metadata.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/metadata.py
@@ -353,18 +353,41 @@
         self.tests_visited = {}
 
     def update_from_log(self, log_file):
+        # We support three possible formats:
+        # * wptreport format; one json object in the file, possibly pretty-printed
+        # * wptreport format; one run per line
+        # * raw log format
+
+        # Try reading a single json object in wptreport format
         self.run_info = None
+        success = self.get_wptreport_data(log_file.read())
+
+        if success:
+            return
+
+        # Try line-separated json objects in wptreport format
+        log_file.seek(0)
+        for line in log_file:
+            success = self.get_wptreport_data(line)
+            if not success:
+                break
+        else:
+            return
+
+        # Assume the file is a raw log
+        log_file.seek(0)
+        self.update_from_raw_log(log_file)
+
+    def get_wptreport_data(self, input_str):
         try:
-            data = json.load(log_file)
+            data = json.loads(input_str)
         except Exception:
             pass
         else:
             if "action" not in data and "results" in data:
                 self.update_from_wptreport_log(data)
-                return
-
-        log_file.seek(0)
-        self.update_from_raw_log(log_file)
+                return True
+        return False
 
     def update_from_raw_log(self, log_file):
         action_map = self.action_map
@@ -642,6 +665,11 @@
                 expected_subtest = test.get_subtest(item)
                 if not self.is_disabled(expected_subtest):
                     rv.append(expected_subtest)
+            for name in seen_subtests:
+                subtest = test.get_subtest(name)
+                # If any of the items have children (ie subsubtests) we want to prune thes
+                if subtest.children:
+                    rv.extend(subtest.children)
 
         return rv
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py
index 43225a1..aacd7857 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py
@@ -359,6 +359,7 @@
     def _remove_child(self, child):
         self.children.remove(child)
         child.parent = None
+        child.node.remove()
 
     def iterchildren(self, name=None):
         for item in self.children:
diff --git a/third_party/blink/web_tests/external/wpt/web-nfc/NFCWriter_push.https-expected.txt b/third_party/blink/web_tests/external/wpt/web-nfc/NFCWriter_push.https-expected.txt
new file mode 100644
index 0000000..72884b7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-nfc/NFCWriter_push.https-expected.txt
@@ -0,0 +1,37 @@
+This is a testharness.js-based test.
+PASS Test that promise is rejected with TypeError if NDEFMessageSource is invalid.
+PASS Test that promise is rejected with SyntaxError if NDEFMessageSource contains invalid records.
+PASS NFCWriter.push should fail if abort push request before push happends.
+PASS NFCWriter.push should fail if signal's aborted flag is set.
+PASS NFCWriter.push should fail if signal is not an AbortSignal.
+PASS Synchronously signaled abort.
+PASS NFCWriter.push should fail when NFC HW is disabled.
+PASS NFCWriter.push should fail when NFC HW is not supported.
+PASS NFCWriter.push should fail with TypeError when invalid timeout is provided.
+PASS NFCWriter.push should fail with TypeError when invalid negative timeout value is provided.
+PASS NFCWriter.push should fail with TimeoutError when timer expires.
+PASS Reject promise with NotSupportedError if NFC message size exceeds 32KB.
+PASS Reject promise with SyntaxError if WebNFC Id cannot be created from provided URL.
+PASS Reject promise with exceptions thrown from serializing the 'json' record data.
+PASS NFCWriter.push should fail with TypeError when invalid target value is provided.
+PASS Test that WebNFC API is not accessible from iframe context.
+PASS NFCWriter.push should succeed when NFC HW is enabled
+PASS NFCWriter.push NDEFMessage containing text, json, opaque, url and external records with default NFCPushOptions.
+PASS Test that NFCWriter.push succeeds when message is DOMString.
+PASS Test that NFCWriter.push succeeds when message is ArrayBuffer.
+PASS NFCWriter.push with 'empty' record should succeed.
+PASS Check that default NFCPushOptions values are correctly set.
+PASS Check that provided NFCPushOptions values are correctly converted.
+PASS NFCWriter.push should read data when ignoreRead is false.
+PASS NFCWriter.push should ignore reading data when ignoreRead is true.
+PASS NFCWriter.push should replace all previously configured push operations.
+PASS Test that recordType should be set to 'text' if NDEFRecordInit.record's recordType is undefined and NDEFRecordInit.record's data is DOMString.
+PASS Test that recordType should be set to 'opaque' if NDEFRecordInit.record's recordType is undefined and NDEFRecordInit.record's data is ArrayBuffer.
+PASS Test that recordType should be set to 'json' if NDEFRecordInit.record's recordType is undefined and NDEFRecordInit.record's data is not DOMString or ArrayBuffer.
+PASS Test that mediaType should be set to 'text/plain' if NDEFRecordInit.record's recordType is 'text' and NDEFRecordInit.record's mediaType is undefined.
+PASS Test that mediaType should be set to 'application/octet-stream' if NDEFRecordInit.record's recordType is 'opaque' and NDEFRecordInit.record's mediaType is undefined.
+PASS Test that mediaType should be set to 'application/json' if NDEFRecordInit.record's recordType is 'json' and NDEFRecordInit.record's mediaType is undefined.
+FAIL Test that mediaType should be set to 'text/plain' if NDEFRecordInit.record's recordType is 'url' and NDEFRecordInit.record's mediaType is undefined. promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'substring' of null"
+PASS Test that mediaType should be set to 'application/octet-stream' if NDEFRecordInit.record's recordType is external type and NDEFRecordInit.record's mediaType is undefined.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/web-nfc/NFCWriter_push.https.html b/third_party/blink/web_tests/external/wpt/web-nfc/NFCWriter_push.https.html
index dda160f1..6b4034f 100644
--- a/third_party/blink/web_tests/external/wpt/web-nfc/NFCWriter_push.https.html
+++ b/third_party/blink/web_tests/external/wpt/web-nfc/NFCWriter_push.https.html
@@ -377,4 +377,69 @@
     });
   });
 }, "NFCWriter.push should replace all previously configured push operations.");
-</script>
\ No newline at end of file
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  await writer.push({ records: [{ data: test_text_data}] });
+  assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
+}, "Test that recordType should be set to 'text' if NDEFRecordInit.record's \
+recordType is undefined and NDEFRecordInit.record's data is DOMString.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  await writer.push({ records: [{ data: test_buffer_data}] });
+  assertNDEFMessagesEqual(test_buffer_data, mockNFC.pushedMessage());
+}, "Test that recordType should be set to 'opaque' if NDEFRecordInit.record's \
+recordType is undefined and NDEFRecordInit.record's data is ArrayBuffer.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  await writer.push({ records: [{ data: test_json_data }] });
+  const message = createMessage([createJsonRecord(test_json_data)]);
+  assertNDEFMessagesEqual(message, mockNFC.pushedMessage());
+}, "Test that recordType should be set to 'json' if NDEFRecordInit.record's \
+recordType is undefined and NDEFRecordInit.record's data is not DOMString or \
+ArrayBuffer.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  await writer.push({ records: [{ recordType: "text", data: test_text_data }] });
+  assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
+}, "Test that mediaType should be set to 'text/plain' if NDEFRecordInit.record's \
+recordType is 'text' and NDEFRecordInit.record's mediaType is undefined.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  await writer.push({ records: [{ recordType: "opaque", data: test_buffer_data }] });
+  assertNDEFMessagesEqual(test_buffer_data, mockNFC.pushedMessage());
+}, "Test that mediaType should be set to 'application/octet-stream' if \
+NDEFRecordInit.record's recordType is 'opaque' and NDEFRecordInit.record's \
+mediaType is undefined.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  await writer.push({ records: [{ recordType: "json", data: test_json_data }] });
+  const message = createMessage([createJsonRecord(test_json_data)]);
+  assertNDEFMessagesEqual(message, mockNFC.pushedMessage());
+}, "Test that mediaType should be set to 'application/json' if \
+NDEFRecordInit.record's recordType is 'json' and NDEFRecordInit.record's \
+mediaType is undefined.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  await writer.push({ records: [{ recordType: "url", data: test_url_data }] });
+  const message = createMessage([createUrlRecord(test_url_data)]);
+  assertNDEFMessagesEqual(message, mockNFC.pushedMessage());
+}, "Test that mediaType should be set to 'text/plain' if NDEFRecordInit.record's \
+recordType is 'url' and NDEFRecordInit.record's mediaType is undefined.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  await writer.push({ records: [{ recordType: "w3.org:xyz", data: test_buffer_data }] });
+  const message = createMessage([createRecord('w3.org:xyz', 'application/octet-stream',
+      test_buffer_data)]);
+  assertNDEFMessagesEqual(message, mockNFC.pushedMessage());
+}, "Test that mediaType should be set to 'application/octet-stream' if \
+NDEFRecordInit.record's recordType is external type and NDEFRecordInit.record's \
+mediaType is undefined.");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/cts.html b/third_party/blink/web_tests/external/wpt/webgpu/cts.html
index 661060fc..b60faec9 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/cts.html
+++ b/third_party/blink/web_tests/external/wpt/webgpu/cts.html
@@ -1,11 +1,30 @@
+<!-- AUTO-GENERATED - DO NOT EDIT. See gen_wpt_cts_html.ts. -->
 <!--
     This test suite is built from the TypeScript sources at:
     https://github.com/gpuweb/cts
+
+    NOTE:
+    The WPT version of this file is generated with *one test spec per variant*.
+    If your harness needs more fine-grained suppressions, you'll need to
+    generate your own variants list from your suppression list. For example, if
+    test file cts:a/b: has 3 tests and you need to suppress a single case, you
+    might break it out into the following variants:
+
+    - cts:a/b:test1~
+    - cts:a/b:test2={"x":1}
+    - cts:a/b:test2={"x":2}  // <- suppress this one
+    - cts:a/b:test2={"x":3}
+    - cts:a/b:test3~
+
+    When run under browser CI, the original cts.html should be skipped, and
+    this alternate version should be run instead, under a non-exported WPT test
+    directory (e.g. Chromium's wpt_internal).
 -->
+
 <!doctype html>
 <title>WebGPU CTS</title>
 <meta charset=utf-8>
-<link rel="help" href="https://gpuweb.github.io/gpuweb/">
+<link rel=help href='https://gpuweb.github.io/gpuweb/'>
 
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
@@ -18,17 +37,36 @@
 </style>
 
 <textarea id=results></textarea>
-<script type=module src="runtime/wpt.js"></script>
+<script type=module src=/webgpu/runtime/wpt.js></script>
 
-<meta name="variant" content="?q=cts:buffers/create_mapped:">
-<meta name="variant" content="?q=cts:buffers/map:">
-<meta name="variant" content="?q=cts:buffers/map_detach:">
-<meta name="variant" content="?q=cts:buffers/map_oom:">
-<meta name="variant" content="?q=cts:canvas/context_creation:">
-<meta name="variant" content="?q=cts:command_buffer/basic:">
-<meta name="variant" content="?q=cts:command_buffer/compute/basic:">
-<meta name="variant" content="?q=cts:command_buffer/copies:">
-<meta name="variant" content="?q=cts:command_buffer/render/basic:">
-<meta name="variant" content="?q=cts:command_buffer/render/rendering:">
-<meta name="variant" content="?q=cts:examples:">
-<meta name="variant" content="?q=cts:fences:">
+<meta name=variant content='?q=cts:buffers/create_mapped:'>
+<meta name=variant content='?q=cts:buffers/map:'>
+<meta name=variant content='?q=cts:buffers/map_detach:'>
+<meta name=variant content='?q=cts:buffers/map_oom:'>
+<meta name=variant content='?q=cts:canvas/context_creation:'>
+<meta name=variant content='?q=cts:command_buffer/basic:'>
+<meta name=variant content='?q=cts:command_buffer/compute/basic:'>
+<meta name=variant content='?q=cts:command_buffer/copies:'>
+<meta name=variant content='?q=cts:command_buffer/render/basic:'>
+<meta name=variant content='?q=cts:command_buffer/render/rendering:'>
+<meta name=variant content='?q=cts:command_buffer/render/storeop:'>
+<meta name=variant content='?q=cts:examples:'>
+<meta name=variant content='?q=cts:fences:'>
+<meta name=variant content='?q=cts:validation/createBindGroup:'>
+<meta name=variant content='?q=cts:validation/createBindGroupLayout:'>
+<meta name=variant content='?q=cts:validation/createPipelineLayout:'>
+<meta name=variant content='?q=cts:validation/createRenderPipeline:'>
+<meta name=variant content='?q=cts:validation/createTexture:'>
+<meta name=variant content='?q=cts:validation/createView:'>
+<meta name=variant content='?q=cts:validation/error_scope:'>
+<meta name=variant content='?q=cts:validation/fences:'>
+<meta name=variant content='?q=cts:validation/queue_submit:'>
+<meta name=variant content='?q=cts:validation/render_pass:'>
+<meta name=variant content='?q=cts:validation/render_pass_descriptor:'>
+<meta name=variant content='?q=cts:validation/setBindGroup:'>
+<meta name=variant content='?q=cts:validation/setBlendColor:'>
+<meta name=variant content='?q=cts:validation/setScissorRect:'>
+<meta name=variant content='?q=cts:validation/setStencilReference:'>
+<meta name=variant content='?q=cts:validation/setVertexBuffer:'>
+<meta name=variant content='?q=cts:validation/setViewport:'>
+<meta name=variant content='?q=cts:validation/vertex_input:'>
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/framework/fixture.js b/third_party/blink/web_tests/external/wpt/webgpu/framework/fixture.js
index d77ede70..6e13385 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/framework/fixture.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/framework/fixture.js
@@ -23,11 +23,15 @@
 
   async init() {}
 
+  debug(msg) {
+    this.rec.debug(msg);
+  }
+
   log(msg) {
     this.rec.log(msg);
   }
 
-  finalize() {
+  async finalize() {
     if (this.numOutstandingAsyncExpectations !== 0) {
       throw new Error('there were outstanding asynchronous expectations (e.g. shouldReject) at the end of the test');
     }
@@ -48,8 +52,9 @@
 
   async asyncExpectation(fn) {
     this.numOutstandingAsyncExpectations++;
-    await fn();
+    const ret = await fn();
     this.numOutstandingAsyncExpectations--;
+    return ret;
   }
 
   expectErrorValue(expectedName, ex, m) {
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/framework/logger.js b/third_party/blink/web_tests/external/wpt/webgpu/framework/logger.js
index 8aa332c5..d97be59 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/framework/logger.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/framework/logger.js
@@ -60,14 +60,17 @@
 
     _defineProperty(this, "logs", []);
 
+    _defineProperty(this, "debugging", false);
+
     this.result = result;
   }
 
-  start() {
+  start(debug = false) {
     this.startTime = now();
     this.logs = [];
     this.failed = false;
     this.warned = false;
+    this.debugging = debug;
   }
 
   finish() {
@@ -80,6 +83,15 @@
     this.result.timems = Math.ceil((endTime - this.startTime) * 1000) / 1000;
     this.result.status = this.failed ? 'fail' : this.warned ? 'warn' : 'pass';
     this.result.logs = this.logs;
+    this.debugging = false;
+  }
+
+  debug(msg) {
+    if (!this.debugging) {
+      return;
+    }
+
+    this.log('DEBUG: ' + msg);
   }
 
   log(msg) {
@@ -112,9 +124,7 @@
 
   threw(e) {
     this.failed = true;
-    let m = 'EXCEPTION';
-    m += ' ' + getStackTrace(e);
-    this.log(m);
+    this.log('EXCEPTION: ' + e.name + ': ' + e.message + '\n' + getStackTrace(e));
   }
 
 }
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/framework/test_filter/filter_by_group.js b/third_party/blink/web_tests/external/wpt/webgpu/framework/test_filter/filter_by_group.js
index 3036e21..e7cc5f78b 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/framework/test_filter/filter_by_group.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/framework/test_filter/filter_by_group.js
@@ -14,10 +14,6 @@
     this.groupPrefix = groupPrefix;
   }
 
-  matches(spec, testcase) {
-    throw new Error('unimplemented');
-  }
-
   async iterate(loader) {
     const specs = await loader.listing(this.suite);
     const entries = [];
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/framework/test_filter/filter_one_file.js b/third_party/blink/web_tests/external/wpt/webgpu/framework/test_filter/filter_one_file.js
index 6a998db0..7616bee 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/framework/test_filter/filter_one_file.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/framework/test_filter/filter_one_file.js
@@ -52,10 +52,6 @@
     return filterTestGroup(spec.g, testcase => testcase.test.startsWith(this.testPrefix));
   }
 
-  matches(spec, testcase) {
-    throw new Error('unimplemented');
-  }
-
 }
 export class FilterByParamsMatch extends FilterOneFile {
   constructor(specId, test, params) {
@@ -73,10 +69,6 @@
     return filterTestGroup(spec.g, testcase => testcase.test === this.test && paramsSupersets(testcase.params, this.params));
   }
 
-  matches(spec, testcase) {
-    throw new Error('unimplemented');
-  }
-
 }
 export class FilterByParamsExact extends FilterOneFile {
   constructor(specId, test, params) {
@@ -94,9 +86,5 @@
     return filterTestGroup(spec.g, testcase => testcase.test === this.test && paramsEquals(testcase.params, this.params));
   }
 
-  matches(spec, testcase) {
-    throw new Error('unimplemented');
-  }
-
 }
 //# sourceMappingURL=filter_one_file.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/framework/test_group.js b/third_party/blink/web_tests/external/wpt/webgpu/framework/test_group.js
index c9cafb3..09697765 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/framework/test_group.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/framework/test_group.js
@@ -36,7 +36,7 @@
     }
 
     if (this.seen.has(name)) {
-      throw new Error('Duplicate test name');
+      throw new Error(`Duplicate test name: ${name}`);
     }
 
     this.seen.add(name);
@@ -113,15 +113,15 @@
     this.fn = fn;
   }
 
-  async run() {
+  async run(debug) {
     const [rec, res] = this.recorder.record(this.id.test, this.id.params);
-    rec.start();
+    rec.start(debug);
 
     try {
       const inst = new this.fixture(rec, this.id.params || {});
       await inst.init();
       await this.fn(inst);
-      inst.finalize();
+      await inst.finalize();
     } catch (e) {
       rec.threw(e);
     }
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/framework/url_query.js b/third_party/blink/web_tests/external/wpt/webgpu/framework/url_query.js
index b265139..f7c7cbc 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/framework/url_query.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/framework/url_query.js
@@ -11,6 +11,8 @@
   ret = ret.replace(/%2F/g, '/');
   ret = ret.replace(/%3A/g, ':');
   ret = ret.replace(/%3D/g, '=');
+  ret = ret.replace(/%5B/g, '[');
+  ret = ret.replace(/%5D/g, ']');
   ret = ret.replace(/%7B/g, '{');
   ret = ret.replace(/%7D/g, '}');
   return ret;
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/framework/util/async_mutex.js b/third_party/blink/web_tests/external/wpt/webgpu/framework/util/async_mutex.js
new file mode 100644
index 0000000..cb900605b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/framework/util/async_mutex.js
@@ -0,0 +1,32 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+export class AsyncMutex {
+  constructor() {
+    _defineProperty(this, "newestQueueItem", void 0);
+  }
+
+  // Run an async function with a lock on this mutex.
+  // Waits until the mutex is available, locks it, runs the function, then releases it.
+  async with(fn) {
+    const p = (async () => {
+      // If the mutex is locked, wait for the last thing in the queue before running.
+      // (Everything in the queue runs in order, so this is after everything currently enqueued.)
+      if (this.newestQueueItem) {
+        await this.newestQueueItem;
+      }
+
+      return fn();
+    })(); // Push the newly-created Promise onto the queue by replacing the old "newest" item.
+
+
+    this.newestQueueItem = p; // And return so the caller can wait on the result.
+
+    return p;
+  }
+
+}
+//# sourceMappingURL=async_mutex.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/framework/util/index.js b/third_party/blink/web_tests/external/wpt/webgpu/framework/util/index.js
index 8d0fe0e..32aea98 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/framework/util/index.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/framework/util/index.js
@@ -2,12 +2,20 @@
 * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
 **/
 
+import { timeout } from './timeout.js';
 export * from './stack.js'; // performance.now() is available in all browsers, but not in scope by default in Node.
 
 const perf = typeof performance !== 'undefined' ? performance : require('perf_hooks').performance;
 export function now() {
   return perf.now();
 }
+export function rejectOnTimeout(ms, msg) {
+  return new Promise((resolve, reject) => {
+    timeout(() => {
+      reject(new Error(msg));
+    }, ms);
+  });
+}
 export function objectEquals(x, y) {
   if (typeof x !== 'object' || typeof y !== 'object') return x === y;
   if (x === null || y === null) return x === y;
@@ -24,4 +32,7 @@
   const p = Object.keys(x);
   return Object.keys(y).every(i => p.indexOf(i) !== -1) && p.every(i => objectEquals(x1[i], y1[i]));
 }
+export function range(n, fn) {
+  return [...new Array(n)].map((_, i) => fn(i));
+}
 //# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/framework/util/timeout.js b/third_party/blink/web_tests/external/wpt/webgpu/framework/util/timeout.js
new file mode 100644
index 0000000..e565a51
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/framework/util/timeout.js
@@ -0,0 +1,6 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const timeout = typeof step_timeout !== 'undefined' ? step_timeout : setTimeout;
+//# sourceMappingURL=timeout.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/framework/version.js b/third_party/blink/web_tests/external/wpt/webgpu/framework/version.js
index 9be02560..0bfbfad4 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/framework/version.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/framework/version.js
@@ -1,3 +1,3 @@
 // AUTO-GENERATED - DO NOT EDIT. See tools/gen_version.
 
-export const version = 'f690ac56a3291801e817433f43877132bb531d5f';
+export const version = 'afbbce5a6a4e9093d01ed454fdc7f257f29d2977';
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/runtime/wpt.js b/third_party/blink/web_tests/external/wpt/webgpu/runtime/wpt.js
index d11499f1..ec4ab012 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/runtime/wpt.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/runtime/wpt.js
@@ -5,11 +5,13 @@
 import { TestLoader } from '../framework/loader.js';
 import { Logger } from '../framework/logger.js';
 import { makeQueryString } from '../framework/url_query.js';
+import { AsyncMutex } from '../framework/util/async_mutex.js';
 
 (async () => {
   const loader = new TestLoader();
   const files = await loader.loadTestsFromQuery(window.location.search);
   const log = new Logger();
+  const mutex = new AsyncMutex();
   const running = [];
 
   for (const f of files) {
@@ -17,20 +19,22 @@
       continue;
     }
 
-    const [rec] = log.record(f.id); // TODO: don't run all tests all at once
+    const [rec] = log.record(f.id);
 
     for (const t of f.spec.g.iterate(rec)) {
-      const run = t.run();
-      running.push(run); // Note: apparently, async_tests must ALL be added within the same task.
-
-      async_test(async function () {
-        const r = await run;
-        this.step(() => {
-          if (r.status === 'fail') {
-            throw (r.logs || []).join('\n');
-          }
+      // Note: apparently, async_tests must ALL be added within the same task.
+      async_test(function () {
+        const p = mutex.with(async () => {
+          const r = await t.run();
+          this.step(() => {
+            if (r.status === 'fail') {
+              throw (r.logs || []).join('\n');
+            }
+          });
+          this.done();
         });
-        this.done();
+        running.push(p);
+        return p;
       }, makeQueryString(f.id, t.id));
     }
   }
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/basic.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/basic.spec.js
index 137e428..c4333c7 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/basic.spec.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/basic.spec.js
@@ -9,7 +9,7 @@
 import { GPUTest } from '../gpu_test.js';
 export const g = new TestGroup(GPUTest);
 g.test('empty', async t => {
-  const encoder = t.device.createCommandEncoder({});
+  const encoder = t.device.createCommandEncoder();
   const cmd = encoder.finish();
   t.device.getQueue().submit([cmd]); // TODO: test that submit() succeeded.
 });
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/compute/basic.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/compute/basic.spec.js
index 56754d5..a001cbc 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/compute/basic.spec.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/compute/basic.spec.js
@@ -78,7 +78,7 @@
     },
     layout: pl
   });
-  const encoder = t.device.createCommandEncoder({});
+  const encoder = t.device.createCommandEncoder();
   const pass = encoder.beginComputePass();
   pass.setPipeline(pipeline);
   pass.setBindGroup(0, bg);
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/copies.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/copies.spec.js
index 9bf78db8..0055a58 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/copies.spec.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/copies.spec.js
@@ -20,7 +20,7 @@
     size: 4,
     usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
   });
-  const encoder = t.device.createCommandEncoder({});
+  const encoder = t.device.createCommandEncoder();
   encoder.copyBufferToBuffer(src, 0, dst, 0, 4);
   t.device.getQueue().submit([encoder.finish()]);
   await t.expectContents(dst, data);
@@ -46,7 +46,7 @@
     format: 'rgba8uint',
     usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST
   });
-  const encoder = t.device.createCommandEncoder({});
+  const encoder = t.device.createCommandEncoder();
   encoder.copyBufferToTexture({
     buffer: src,
     rowPitch: 256,
@@ -107,7 +107,7 @@
   };
   const mid1 = t.device.createTexture(midDesc);
   const mid2 = t.device.createTexture(midDesc);
-  const encoder = t.device.createCommandEncoder({});
+  const encoder = t.device.createCommandEncoder();
   encoder.copyBufferToTexture({
     buffer: src,
     rowPitch: 256,
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/render/basic.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/render/basic.spec.js
index 83bb026..8f4d8c8 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/render/basic.spec.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/render/basic.spec.js
@@ -23,7 +23,7 @@
     usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.OUTPUT_ATTACHMENT
   });
   const colorAttachmentView = colorAttachment.createView();
-  const encoder = t.device.createCommandEncoder({});
+  const encoder = t.device.createCommandEncoder();
   const pass = encoder.beginRenderPass({
     colorAttachments: [{
       attachment: colorAttachmentView,
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/render/rendering.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/render/rendering.spec.js
index c8a7d7f..c065ed23 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/render/rendering.spec.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/render/rendering.spec.js
@@ -78,7 +78,7 @@
       vertexBuffers: []
     }
   });
-  const encoder = t.device.createCommandEncoder({});
+  const encoder = t.device.createCommandEncoder();
   const pass = encoder.beginRenderPass({
     colorAttachments: [{
       attachment: colorAttachmentView,
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/render/storeop.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/render/storeop.spec.js
new file mode 100644
index 0000000..da58ad2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/command_buffer/render/storeop.spec.js
@@ -0,0 +1,113 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+renderPass store op test that drawn quad is either stored or cleared based on storeop`;
+import { TestGroup } from '../../../../framework/index.js';
+import { GPUTest } from '../../gpu_test.js';
+export const g = new TestGroup(GPUTest);
+g.test('storeOp controls whether 1x1 drawn quad is stored', async t => {
+  const renderTexture = t.device.createTexture({
+    size: {
+      width: 1,
+      height: 1,
+      depth: 1
+    },
+    format: 'r8unorm',
+    usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.OUTPUT_ATTACHMENT
+  }); // create render pipeline
+
+  const vertexModule = t.device.createShaderModule({
+    code:
+    /* GLSL(
+     *       'vertex',
+     *       `#version 450
+     *       const vec2 pos[3] = vec2[3](
+     *                               vec2( 1.0f, -1.0f),
+     *                               vec2( 1.0f,  1.0f),
+     *                               vec2(-1.0f,  1.0f)
+     *                               );
+     *
+     *       void main() {
+     *           gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
+     *       }`
+     *     )
+     */
+    new Uint32Array([119734787, 66304, 524295, 39, 0, 131089, 1, 393227, 1, 1280527431, 1685353262, 808793134, 0, 196622, 0, 1, 458767, 0, 4, 1852399981, 0, 13, 26, 196611, 2, 450, 262149, 4, 1852399981, 0, 393221, 11, 1348430951, 1700164197, 2019914866, 0, 393222, 11, 0, 1348430951, 1953067887, 7237481, 458758, 11, 1, 1348430951, 1953393007, 1702521171, 0, 458758, 11, 2, 1130327143, 1148217708, 1635021673, 6644590, 458758, 11, 3, 1130327143, 1147956341, 1635021673, 6644590, 196613, 13, 0, 393221, 26, 1449094247, 1702130277, 1684949368, 30821, 327685, 29, 1701080681, 1818386808, 101, 327752, 11, 0, 11, 0, 327752, 11, 1, 11, 1, 327752, 11, 2, 11, 3, 327752, 11, 3, 11, 4, 196679, 11, 2, 262215, 26, 11, 42, 131091, 2, 196641, 3, 2, 196630, 6, 32, 262167, 7, 6, 4, 262165, 8, 32, 0, 262187, 8, 9, 1, 262172, 10, 6, 9, 393246, 11, 7, 6, 10, 10, 262176, 12, 3, 11, 262203, 12, 13, 3, 262165, 14, 32, 1, 262187, 14, 15, 0, 262167, 16, 6, 2, 262187, 8, 17, 3, 262172, 18, 16, 17, 262187, 6, 19, 1065353216, 262187, 6, 20, 3212836864, 327724, 16, 21, 19, 20, 327724, 16, 22, 19, 19, 327724, 16, 23, 20, 19, 393260, 18, 24, 21, 22, 23, 262176, 25, 1, 14, 262203, 25, 26, 1, 262176, 28, 7, 18, 262176, 30, 7, 16, 262187, 6, 33, 0, 262176, 37, 3, 7, 327734, 2, 4, 0, 3, 131320, 5, 262203, 28, 29, 7, 262205, 14, 27, 26, 196670, 29, 24, 327745, 30, 31, 29, 27, 262205, 16, 32, 31, 327761, 6, 34, 32, 0, 327761, 6, 35, 32, 1, 458832, 7, 36, 34, 35, 33, 19, 327745, 37, 38, 13, 15, 196670, 38, 36, 65789, 65592])
+  });
+  const fragmentModule = t.device.createShaderModule({
+    code:
+    /* GLSL(
+     *       'fragment',
+     *       `#version 450
+     *       layout(location = 0) out vec4 fragColor;
+     *       void main() {
+     *           fragColor = vec4(1.0, 0.0, 0.0, 1.0);
+     *       }`
+     *     )
+     */
+    new Uint32Array([119734787, 66304, 524295, 13, 0, 131089, 1, 393227, 1, 1280527431, 1685353262, 808793134, 0, 196622, 0, 1, 393231, 4, 4, 1852399981, 0, 9, 196624, 4, 7, 196611, 2, 450, 262149, 4, 1852399981, 0, 327685, 9, 1734439526, 1869377347, 114, 262215, 9, 30, 0, 131091, 2, 196641, 3, 2, 196630, 6, 32, 262167, 7, 6, 4, 262176, 8, 3, 7, 262203, 8, 9, 3, 262187, 6, 10, 1065353216, 262187, 6, 11, 0, 458796, 7, 12, 10, 11, 11, 10, 327734, 2, 4, 0, 3, 131320, 5, 196670, 9, 12, 65789, 65592])
+  });
+  const renderPipeline = t.device.createRenderPipeline({
+    vertexStage: {
+      module: vertexModule,
+      entryPoint: 'main'
+    },
+    fragmentStage: {
+      module: fragmentModule,
+      entryPoint: 'main'
+    },
+    layout: t.device.createPipelineLayout({
+      bindGroupLayouts: []
+    }),
+    primitiveTopology: 'triangle-list',
+    colorStates: [{
+      format: 'r8unorm'
+    }]
+  }); // encode pass and submit
+
+  const encoder = t.device.createCommandEncoder();
+  const pass = encoder.beginRenderPass({
+    colorAttachments: [{
+      attachment: renderTexture.createView(),
+      storeOp: t.params.storeOp,
+      loadValue: {
+        r: 0.0,
+        g: 0.0,
+        b: 0.0,
+        a: 0.0
+      }
+    }]
+  });
+  pass.setPipeline(renderPipeline);
+  pass.draw(3, 1, 0, 0);
+  pass.endPass();
+  const dstBuffer = t.device.createBuffer({
+    size: 4,
+    usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC
+  });
+  encoder.copyTextureToBuffer({
+    texture: renderTexture
+  }, {
+    buffer: dstBuffer,
+    rowPitch: 256,
+    imageHeight: 1
+  }, {
+    width: 1,
+    height: 1,
+    depth: 1
+  });
+  t.device.getQueue().submit([encoder.finish()]); // expect the buffer to be clear
+
+  const expectedContent = new Uint32Array([t.params.expected]);
+  await t.expectContents(dstBuffer, expectedContent);
+}).params([{
+  storeOp: 'store',
+  expected: 255
+}, //
+{
+  storeOp: 'clear',
+  expected: 0
+}]);
+//# sourceMappingURL=storeop.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/examples.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/examples.spec.js
index ad86e73b..8a992a6 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/examples.spec.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/examples.spec.js
@@ -44,12 +44,13 @@
   x: 2,
   y: 4,
   result: 6
-}, // (blank comment to enforce newlines on autoformat)
+}, //
 {
   x: -10,
   y: 18,
   result: 8
-}]);
+}]); // (note blank comment above to enforce newlines on autoformat)
+
 g.test('gpu/async', async t => {
   const fence = t.queue.createFence();
   t.queue.signal(fence, 2);
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/fences.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/fences.spec.js
index d9f8ce5..ccf5136 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/fences.spec.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/fences.spec.js
@@ -34,13 +34,20 @@
   t.queue.signal(fence, 2);
   await fence.onCompletion(2);
   t.expect(fence.getCompletedValue() === 2);
-}); // Test it is illegal to wait on a value greater than the signaled value.
+}); // All promises resolve when signal is called once.
 
-g.test('wait/greater than signaled', async t => {
+g.test('wait/signaled once', async t => {
   const fence = t.queue.createFence();
-  t.queue.signal(fence, 2);
-  const promise = fence.onCompletion(3);
-  await t.shouldReject('OperationError', promise);
+  t.queue.signal(fence, 20);
+  const promises = [];
+
+  for (let i = 0; i <= 20; ++i) {
+    promises.push(fence.onCompletion(i).then(() => {
+      t.expect(fence.getCompletedValue() >= i);
+    }));
+  }
+
+  await Promise.all(promises);
 }); // Promise resolves when signal is called multiple times.
 
 g.test('wait/signaled multiple times', async t => {
@@ -64,19 +71,6 @@
   t.expect(fence.getCompletedValue() === 2);
   await fence.onCompletion(2);
   t.expect(fence.getCompletedValue() === 2);
-}); // Test it is illegal to wait on a fence without signaling the value.
-
-g.test('wait/without signal', async t => {
-  const fence = t.queue.createFence();
-  const promise = fence.onCompletion(2);
-  await t.shouldReject('OperationError', promise);
-}); // Test it is illegal to wait on a fence before it is signaled.
-
-g.test('wait/before signaled', async t => {
-  const fence = t.queue.createFence();
-  const promise = fence.onCompletion(2);
-  t.queue.signal(fence, 2);
-  await t.shouldReject('OperationError', promise);
 }); // Test many calls to signal and wait on fence values one at a time.
 
 g.test('wait/many/serially', async t => {
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/gpu_test.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/gpu_test.js
index a67ce4a..e437ee09 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/gpu_test.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/gpu_test.js
@@ -5,7 +5,8 @@
 function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
 
 import { getGPU } from '../../framework/gpu/implementation.js';
-import { Fixture } from '../../framework/index.js'; // TODO: Should this gain some functionality currently only in UnitTest?
+import { Fixture } from '../../framework/index.js';
+let glslangInstance; // TODO: Should this gain some functionality currently only in UnitTest?
 
 export class GPUTest extends Fixture {
   constructor(...args) {
@@ -20,8 +21,51 @@
     super.init();
     const gpu = getGPU();
     const adapter = await gpu.requestAdapter();
-    this.device = await adapter.requestDevice({});
+    this.device = await adapter.requestDevice();
     this.queue = this.device.getQueue();
+    this.device.pushErrorScope('out-of-memory');
+    this.device.pushErrorScope('validation');
+  }
+
+  async finalize() {
+    super.finalize();
+    const gpuValidationError = await this.device.popErrorScope();
+
+    if (gpuValidationError !== null) {
+      if (!(gpuValidationError instanceof GPUValidationError)) throw new Error();
+      this.fail(`Unexpected validation error occurred: ${gpuValidationError.message}`);
+    }
+
+    const gpuOutOfMemoryError = await this.device.popErrorScope();
+
+    if (gpuOutOfMemoryError !== null) {
+      if (!(gpuOutOfMemoryError instanceof GPUOutOfMemoryError)) throw new Error();
+      this.fail('Unexpected out-of-memory error occurred');
+    }
+  }
+
+  async initGLSL() {
+    if (!glslangInstance) {
+      const glslangPath = '../../glslang.js';
+      const glslangModule = (await import(glslangPath)).default;
+      await new Promise(resolve => {
+        glslangModule().then(glslang => {
+          glslangInstance = glslang;
+          resolve();
+        });
+      });
+    }
+  }
+
+  makeShaderModule(stage, source) {
+    if (!glslangInstance) {
+      throw new Error('GLSL is not instantiated. Run `await t.initGLSL()` first');
+    }
+
+    const code = glslangInstance.compileGLSL(source, stage, false);
+    return this.device.createShaderModule({
+      code
+    });
   } // TODO: add an expectContents for textures, which logs data: uris on failure
 
 
@@ -33,7 +77,7 @@
         size: expected.buffer.byteLength,
         usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
       });
-      const c = this.device.createCommandEncoder({});
+      const c = this.device.createCommandEncoder();
       c.copyBufferToBuffer(src, 0, dst, 0, size);
       this.queue.submit([c.finish()]);
       const actual = new Uint8Array((await dst.mapReadAsync()));
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/index.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/index.js
index cb2a592..b38ea11 100644
--- a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/index.js
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/index.js
@@ -1,4 +1,4 @@
-// AUTO-GENERATED - DO NOT EDIT. See src/tools/gen.ts.
+// AUTO-GENERATED - DO NOT EDIT. See src/tools/gen_listings.ts.
 
 export const listing = [
   {
@@ -42,11 +42,87 @@
     "description": ""
   },
   {
+    "path": "command_buffer/render/storeop",
+    "description": "renderPass store op test that drawn quad is either stored or cleared based on storeop"
+  },
+  {
     "path": "examples",
     "description": "Examples of writing CTS tests with various features."
   },
   {
     "path": "fences",
     "description": ""
+  },
+  {
+    "path": "validation/createBindGroup",
+    "description": "createBindGroup validation tests."
+  },
+  {
+    "path": "validation/createBindGroupLayout",
+    "description": "createBindGroupLayout validation tests."
+  },
+  {
+    "path": "validation/createPipelineLayout",
+    "description": "createPipelineLayout validation tests."
+  },
+  {
+    "path": "validation/createRenderPipeline",
+    "description": "createRenderPipeline validation tests."
+  },
+  {
+    "path": "validation/createTexture",
+    "description": "createTexture validation tests."
+  },
+  {
+    "path": "validation/createView",
+    "description": "createView validation tests."
+  },
+  {
+    "path": "validation/error_scope",
+    "description": "error scope validation tests."
+  },
+  {
+    "path": "validation/fences",
+    "description": "fences validation tests."
+  },
+  {
+    "path": "validation/queue_submit",
+    "description": "queue submit validation tests."
+  },
+  {
+    "path": "validation/render_pass",
+    "description": "render pass validation tests."
+  },
+  {
+    "path": "validation/render_pass_descriptor",
+    "description": "render pass descriptor validation tests."
+  },
+  {
+    "path": "validation/setBindGroup",
+    "description": "setBindGroup validation tests."
+  },
+  {
+    "path": "validation/setBlendColor",
+    "description": "setBlendColor validation tests."
+  },
+  {
+    "path": "validation/setScissorRect",
+    "description": "setScissorRect validation tests."
+  },
+  {
+    "path": "validation/setStencilReference",
+    "description": "setStencilReference validation tests."
+  },
+  {
+    "path": "validation/setVertexBuffer",
+    "description": "setVertexBuffer validation tests."
+  },
+  {
+    "path": "validation/setViewport",
+    "description": "setViewport validation tests."
+  },
+  {
+    "path": "validation/vertex_input",
+    "description": "vertexInput validation tests."
   }
 ];
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createBindGroup.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createBindGroup.spec.js
new file mode 100644
index 0000000..24d29d0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createBindGroup.spec.js
@@ -0,0 +1,477 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+createBindGroup validation tests.
+`;
+import { TestGroup, pcombine, poptions } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+
+function clone(descriptor) {
+  return JSON.parse(JSON.stringify(descriptor));
+}
+
+class F extends ValidationTest {
+  getStorageBuffer() {
+    return this.device.createBuffer({
+      size: 1024,
+      usage: GPUBufferUsage.STORAGE
+    });
+  }
+
+  getUniformBuffer() {
+    return this.device.createBuffer({
+      size: 1024,
+      usage: GPUBufferUsage.UNIFORM
+    });
+  }
+
+  getSampler() {
+    return this.device.createSampler();
+  }
+
+  getSampledTexture() {
+    return this.device.createTexture({
+      size: {
+        width: 16,
+        height: 16,
+        depth: 1
+      },
+      format: 'rgba8unorm',
+      usage: GPUTextureUsage.SAMPLED
+    });
+  }
+
+  getStorageTexture() {
+    return this.device.createTexture({
+      size: {
+        width: 16,
+        height: 16,
+        depth: 1
+      },
+      format: 'rgba8unorm',
+      usage: GPUTextureUsage.STORAGE
+    });
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('binding count mismatch', async t => {
+  const bindGroupLayout = t.device.createBindGroupLayout({
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.COMPUTE,
+      type: 'storage-buffer'
+    }]
+  });
+  const goodDescriptor = {
+    bindings: [{
+      binding: 0,
+      resource: {
+        buffer: t.getStorageBuffer()
+      }
+    }],
+    layout: bindGroupLayout
+  }; // Control case
+
+  t.device.createBindGroup(goodDescriptor); // Another binding is not expected.
+
+  const badDescriptor = {
+    bindings: [{
+      binding: 0,
+      resource: {
+        buffer: t.getStorageBuffer()
+      }
+    }, // Another binding is added.
+    {
+      binding: 1,
+      resource: {
+        buffer: t.getStorageBuffer()
+      }
+    }],
+    layout: bindGroupLayout
+  };
+  await t.expectValidationError(() => {
+    t.device.createBindGroup(badDescriptor);
+  });
+});
+g.test('binding must be present in layout', async t => {
+  const bindGroupLayout = t.device.createBindGroupLayout({
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.COMPUTE,
+      type: 'storage-buffer'
+    }]
+  });
+  const goodDescriptor = {
+    bindings: [{
+      binding: 0,
+      resource: {
+        buffer: t.getStorageBuffer()
+      }
+    }],
+    layout: bindGroupLayout
+  }; // Control case
+
+  t.device.createBindGroup(goodDescriptor); // Binding index 0 must be present.
+
+  const badDescriptor = {
+    bindings: [{
+      binding: 1,
+      // binding index becomes 1.
+      resource: {
+        buffer: t.getStorageBuffer()
+      }
+    }],
+    layout: bindGroupLayout
+  };
+  await t.expectValidationError(() => {
+    t.device.createBindGroup(badDescriptor);
+  });
+});
+g.test('buffer binding must contain exactly one buffer of its type', async t => {
+  const {
+    bindingType,
+    resourceType
+  } = t.params;
+  const bindGroupLayout = t.device.createBindGroupLayout({
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.COMPUTE,
+      type: bindingType
+    }]
+  });
+  let resource;
+
+  if (resourceType === 'error') {
+    resource = {
+      buffer: await t.getErrorBuffer()
+    };
+  } else if (resourceType === 'uniform-buffer') {
+    resource = {
+      buffer: t.getUniformBuffer()
+    };
+  } else if (resourceType === 'storage-buffer') {
+    resource = {
+      buffer: t.getStorageBuffer()
+    };
+  } else if (resourceType === 'sampler') {
+    resource = t.getSampler();
+  } else if (resourceType === 'sampled-texture') {
+    resource = t.getSampledTexture().createView();
+  } else if (resourceType === 'storage-texture') {
+    resource = t.getStorageTexture().createView();
+  } else throw new Error();
+
+  let shouldError = bindingType !== resourceType;
+
+  if (bindingType === 'readonly-storage-buffer' && resourceType === 'storage-buffer') {
+    shouldError = false;
+  }
+
+  await t.expectValidationError(() => {
+    t.device.createBindGroup({
+      bindings: [{
+        binding: 0,
+        resource
+      }],
+      layout: bindGroupLayout
+    });
+  }, shouldError);
+}).params(pcombine([poptions('bindingType', ['uniform-buffer', 'storage-buffer', 'readonly-storage-buffer', 'sampler', 'sampled-texture', 'storage-texture']), poptions('resourceType', ['error', 'uniform-buffer', 'storage-buffer', 'sampler', 'sampled-texture', 'storage-texture'])]));
+g.test('texture binding must have correct usage', async t => {
+  const {
+    type
+  } = t.params;
+  const bindGroupLayout = t.device.createBindGroupLayout({
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.FRAGMENT,
+      type
+    }]
+  });
+  let usage;
+
+  if (type === 'sampled-texture') {
+    usage = GPUTextureUsage.SAMPLED;
+  } else if (type === 'storage-texture') {
+    usage = GPUTextureUsage.STORAGE;
+  } else {
+    throw new Error('Unexpected binding type');
+  }
+
+  const goodDescriptor = {
+    size: {
+      width: 16,
+      height: 16,
+      depth: 1
+    },
+    format: 'r8unorm',
+    usage
+  }; // Control case
+
+  t.device.createBindGroup({
+    bindings: [{
+      binding: 0,
+      resource: t.device.createTexture(goodDescriptor).createView()
+    }],
+    layout: bindGroupLayout
+  });
+
+  function* mismatchedTextureUsages() {
+    yield GPUTextureUsage.COPY_SRC;
+    yield GPUTextureUsage.COPY_DST;
+
+    if (type !== 'sampled-texture') {
+      yield GPUTextureUsage.SAMPLED;
+    }
+
+    if (type !== 'storage-texture') {
+      yield GPUTextureUsage.STORAGE;
+    }
+
+    yield GPUTextureUsage.OUTPUT_ATTACHMENT;
+  } // Mismatched texture binding usages are not valid.
+
+
+  for (const mismatchedTextureUsage of mismatchedTextureUsages()) {
+    const badDescriptor = clone(goodDescriptor);
+    badDescriptor.usage = mismatchedTextureUsage;
+    await t.expectValidationError(() => {
+      t.device.createBindGroup({
+        bindings: [{
+          binding: 0,
+          resource: t.device.createTexture(badDescriptor).createView()
+        }],
+        layout: bindGroupLayout
+      });
+    });
+  }
+}).params(poptions('type', ['sampled-texture', 'storage-texture']));
+g.test('texture must have correct component type', async t => {
+  const {
+    textureComponentType
+  } = t.params;
+  const bindGroupLayout = t.device.createBindGroupLayout({
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.FRAGMENT,
+      type: 'sampled-texture',
+      textureComponentType
+    }]
+  }); // TODO: Test more texture component types.
+
+  let format;
+
+  if (textureComponentType === 'float') {
+    format = 'r8unorm';
+  } else if (textureComponentType === 'sint') {
+    format = 'r8sint';
+  } else if (textureComponentType === 'uint') {
+    format = 'r8uint';
+  } else {
+    throw new Error('Unexpected texture component type');
+  }
+
+  const goodDescriptor = {
+    size: {
+      width: 16,
+      height: 16,
+      depth: 1
+    },
+    format,
+    usage: GPUTextureUsage.SAMPLED
+  }; // Control case
+
+  t.device.createBindGroup({
+    bindings: [{
+      binding: 0,
+      resource: t.device.createTexture(goodDescriptor).createView()
+    }],
+    layout: bindGroupLayout
+  });
+
+  function* mismatchedTextureFormats() {
+    if (textureComponentType !== 'float') {
+      yield 'r8unorm';
+    }
+
+    if (textureComponentType !== 'sint') {
+      yield 'r8sint';
+    }
+
+    if (textureComponentType !== 'uint') {
+      yield 'r8uint';
+    }
+  } // Mismatched texture binding formats are not valid.
+
+
+  for (const mismatchedTextureFormat of mismatchedTextureFormats()) {
+    const badDescriptor = clone(goodDescriptor);
+    badDescriptor.format = mismatchedTextureFormat;
+    await t.expectValidationError(() => {
+      t.device.createBindGroup({
+        bindings: [{
+          binding: 0,
+          resource: t.device.createTexture(badDescriptor).createView()
+        }],
+        layout: bindGroupLayout
+      });
+    });
+  }
+}).params(poptions('textureComponentType', ['float', 'sint', 'uint'])); // TODO: Write test for all dimensions.
+
+g.test('texture must have correct dimension', async t => {
+  const bindGroupLayout = t.device.createBindGroupLayout({
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.FRAGMENT,
+      type: 'sampled-texture',
+      textureDimension: '2d'
+    }]
+  });
+  const goodDescriptor = {
+    size: {
+      width: 16,
+      height: 16,
+      depth: 1
+    },
+    arrayLayerCount: 1,
+    format: 'rgba8unorm',
+    usage: GPUTextureUsage.SAMPLED
+  }; // Control case
+
+  t.device.createBindGroup({
+    bindings: [{
+      binding: 0,
+      resource: t.device.createTexture(goodDescriptor).createView()
+    }],
+    layout: bindGroupLayout
+  }); // Mismatched texture binding formats are not valid.
+
+  const badDescriptor = clone(goodDescriptor);
+  badDescriptor.arrayLayerCount = 2;
+  await t.expectValidationError(() => {
+    t.device.createBindGroup({
+      bindings: [{
+        binding: 0,
+        resource: t.device.createTexture(badDescriptor).createView()
+      }],
+      layout: bindGroupLayout
+    });
+  });
+});
+g.test('buffer offset and size for bind groups match', async t => {
+  const {
+    offset,
+    size,
+    success
+  } = t.params;
+  const bindGroupLayout = t.device.createBindGroupLayout({
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.COMPUTE,
+      type: 'storage-buffer'
+    }]
+  });
+  const buffer = t.device.createBuffer({
+    size: 1024,
+    usage: GPUBufferUsage.STORAGE
+  });
+  const descriptor = {
+    bindings: [{
+      binding: 0,
+      resource: {
+        buffer,
+        offset,
+        size
+      }
+    }],
+    layout: bindGroupLayout
+  };
+
+  if (success) {
+    // Control case
+    t.device.createBindGroup(descriptor);
+  } else {
+    // Buffer offset and/or size don't match in bind groups.
+    await t.expectValidationError(() => {
+      t.device.createBindGroup(descriptor);
+    });
+  }
+}).params([{
+  offset: 0,
+  size: 512,
+  success: true
+}, // offset 0 is valid
+{
+  offset: 256,
+  size: 256,
+  success: true
+}, // offset 256 (aligned) is valid
+// unaligned buffer offset is invalid
+{
+  offset: 1,
+  size: 256,
+  success: false
+}, {
+  offset: 1,
+  size: undefined,
+  success: false
+}, {
+  offset: 128,
+  size: 256,
+  success: false
+}, {
+  offset: 255,
+  size: 256,
+  success: false
+}, {
+  offset: 0,
+  size: 256,
+  success: true
+}, // touching the start of the buffer works
+{
+  offset: 256 * 3,
+  size: 256,
+  success: true
+}, // touching the end of the buffer works
+{
+  offset: 1024,
+  size: 0,
+  success: true
+}, // touching the end of the buffer works
+{
+  offset: 0,
+  size: 1024,
+  success: true
+}, // touching the full buffer works
+{
+  offset: 0,
+  size: undefined,
+  success: true
+}, // touching the full buffer works
+{
+  offset: 256 * 5,
+  size: 0,
+  success: false
+}, // offset is OOB
+{
+  offset: 0,
+  size: 256 * 5,
+  success: false
+}, // size is OOB
+{
+  offset: 1024,
+  size: 1,
+  success: false
+}, // offset+size is OOB
+{
+  offset: 256,
+  size: -256,
+  success: false
+} // offset+size overflows to be 0
+]);
+//# sourceMappingURL=createBindGroup.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createBindGroupLayout.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createBindGroupLayout.spec.js
new file mode 100644
index 0000000..8e3bfc2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createBindGroupLayout.spec.js
@@ -0,0 +1,145 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+createBindGroupLayout validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+
+function clone(descriptor) {
+  return JSON.parse(JSON.stringify(descriptor));
+}
+
+export const g = new TestGroup(ValidationTest);
+g.test('some binding index was specified more than once', async t => {
+  const goodDescriptor = {
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.COMPUTE,
+      type: 'storage-buffer'
+    }, {
+      binding: 1,
+      visibility: GPUShaderStage.COMPUTE,
+      type: 'storage-buffer'
+    }]
+  }; // Control case
+
+  t.device.createBindGroupLayout(goodDescriptor);
+  const badDescriptor = clone(goodDescriptor);
+  badDescriptor.bindings[1].binding = 0; // Binding index 0 can't be specified twice.
+
+  await t.expectValidationError(() => {
+    t.device.createBindGroupLayout(badDescriptor);
+  });
+});
+g.test('negative binding index', async t => {
+  const goodDescriptor = {
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.COMPUTE,
+      type: 'storage-buffer'
+    }]
+  }; // Control case
+
+  t.device.createBindGroupLayout(goodDescriptor); // Negative binding index can't be specified.
+
+  const badDescriptor = clone(goodDescriptor);
+  badDescriptor.bindings[0].binding = -1;
+  await t.expectValidationError(() => {
+    t.device.createBindGroupLayout(badDescriptor);
+  });
+});
+g.test('Visibility of bindings can be 0', async t => {
+  const descriptor = {
+    bindings: [{
+      binding: 0,
+      visibility: 0,
+      type: 'storage-buffer'
+    }]
+  };
+  t.device.createBindGroupLayout(descriptor);
+});
+g.test('number of dynamic buffers exceeds the maximum value', async t => {
+  const {
+    type,
+    maxDynamicBufferCount
+  } = t.params;
+  const maxDynamicBufferBindings = [];
+
+  for (let i = 0; i < maxDynamicBufferCount; i++) {
+    maxDynamicBufferBindings.push({
+      binding: i,
+      visibility: GPUShaderStage.COMPUTE,
+      type,
+      hasDynamicOffset: true
+    });
+  }
+
+  const goodDescriptor = {
+    bindings: [...maxDynamicBufferBindings, {
+      binding: maxDynamicBufferBindings.length,
+      visibility: GPUShaderStage.COMPUTE,
+      type,
+      hasDynamicOffset: false
+    }]
+  }; // Control case
+
+  t.device.createBindGroupLayout(goodDescriptor); // Dynamic buffers exceed maximum in a bind group layout.
+
+  const badDescriptor = clone(goodDescriptor);
+  badDescriptor.bindings[maxDynamicBufferCount].hasDynamicOffset = true;
+  await t.expectValidationError(() => {
+    t.device.createBindGroupLayout(badDescriptor);
+  });
+}).params([{
+  type: 'storage-buffer',
+  maxDynamicBufferCount: 4
+}, {
+  type: 'uniform-buffer',
+  maxDynamicBufferCount: 8
+}]);
+g.test('dynamic set to true is allowed only for buffers', async t => {
+  const {
+    type,
+    success
+  } = t.params;
+  const descriptor = {
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.FRAGMENT,
+      type,
+      hasDynamicOffset: true
+    }]
+  };
+
+  if (success) {
+    // Control case
+    t.device.createBindGroupLayout(descriptor);
+  } else {
+    // Dynamic set to true is not allowed in some cases.
+    await t.expectValidationError(() => {
+      t.device.createBindGroupLayout(descriptor);
+    });
+  }
+}).params([{
+  type: 'uniform-buffer',
+  success: true
+}, {
+  type: 'storage-buffer',
+  success: true
+}, {
+  type: 'readonly-storage-buffer',
+  success: true
+}, {
+  type: 'sampler',
+  success: false
+}, {
+  type: 'sampled-texture',
+  success: false
+}, {
+  type: 'storage-texture',
+  success: false
+}]);
+//# sourceMappingURL=createBindGroupLayout.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createPipelineLayout.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createPipelineLayout.spec.js
new file mode 100644
index 0000000..dc759750
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createPipelineLayout.spec.js
@@ -0,0 +1,90 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+createPipelineLayout validation tests.
+`;
+import { TestGroup, poptions } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+
+function clone(descriptor) {
+  return JSON.parse(JSON.stringify(descriptor));
+}
+
+export const g = new TestGroup(ValidationTest);
+g.test('number of dynamic buffers exceeds the maximum value', async t => {
+  const {
+    type,
+    maxDynamicBufferCount
+  } = t.params;
+  const maxDynamicBufferBindings = [];
+
+  for (let i = 0; i < maxDynamicBufferCount; i++) {
+    maxDynamicBufferBindings.push({
+      binding: i,
+      visibility: GPUShaderStage.COMPUTE,
+      type,
+      hasDynamicOffset: true
+    });
+  }
+
+  const maxDynamicBufferBindGroupLayout = t.device.createBindGroupLayout({
+    bindings: maxDynamicBufferBindings
+  });
+  const goodDescriptor = {
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.COMPUTE,
+      type,
+      hasDynamicOffset: false
+    }]
+  };
+  const goodPipelineLayoutDescriptor = {
+    bindGroupLayouts: [maxDynamicBufferBindGroupLayout, t.device.createBindGroupLayout(goodDescriptor)]
+  }; // Control case
+
+  t.device.createPipelineLayout(goodPipelineLayoutDescriptor); // Check dynamic buffers exceed maximum in pipeline layout.
+
+  const badDescriptor = clone(goodDescriptor);
+  badDescriptor.bindings[0].hasDynamicOffset = true;
+  const badPipelineLayoutDescriptor = {
+    bindGroupLayouts: [maxDynamicBufferBindGroupLayout, t.device.createBindGroupLayout(badDescriptor)]
+  };
+  await t.expectValidationError(() => {
+    t.device.createPipelineLayout(badPipelineLayoutDescriptor);
+  });
+}).params([{
+  type: 'storage-buffer',
+  maxDynamicBufferCount: 4
+}, {
+  type: 'uniform-buffer',
+  maxDynamicBufferCount: 8
+}]);
+g.test('number of bind group layouts exceeds the maximum value', async t => {
+  const {
+    type
+  } = t.params;
+  const bindGroupLayoutDescriptor = {
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.COMPUTE,
+      type
+    }]
+  }; // 4 is the maximum number of bind group layouts.
+
+  const maxBindGroupLayouts = [1, 2, 3, 4].map(() => t.device.createBindGroupLayout(bindGroupLayoutDescriptor));
+  const goodPipelineLayoutDescriptor = {
+    bindGroupLayouts: maxBindGroupLayouts
+  }; // Control case
+
+  t.device.createPipelineLayout(goodPipelineLayoutDescriptor); // Check bind group layouts exceed maximum in pipeline layout.
+
+  const badPipelineLayoutDescriptor = {
+    bindGroupLayouts: [...maxBindGroupLayouts, t.device.createBindGroupLayout(bindGroupLayoutDescriptor)]
+  };
+  await t.expectValidationError(() => {
+    t.device.createPipelineLayout(badPipelineLayoutDescriptor);
+  });
+}).params(poptions('type', ['storage-buffer', 'uniform-buffer']));
+//# sourceMappingURL=createPipelineLayout.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createRenderPipeline.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createRenderPipeline.spec.js
new file mode 100644
index 0000000..7cbe93dd9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createRenderPipeline.spec.js
@@ -0,0 +1,379 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+createRenderPipeline validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+
+class F extends ValidationTest {
+  async init() {
+    await Promise.all([super.init(), this.initGLSL()]);
+  }
+
+  getDescriptor(options = {}) {
+    const defaultColorStates = [{
+      format: 'rgba8unorm'
+    }];
+    const {
+      primitiveTopology = 'triangle-list',
+      colorStates = defaultColorStates,
+      sampleCount = 1,
+      depthStencilState
+    } = options;
+    const format = colorStates.length ? colorStates[0].format : 'rgba8unorm';
+    return {
+      vertexStage: this.getVertexStage(),
+      fragmentStage: this.getFragmentStage(format),
+      layout: this.getPipelineLayout(),
+      primitiveTopology,
+      colorStates,
+      sampleCount,
+      depthStencilState
+    };
+  }
+
+  getVertexStage() {
+    return {
+      module: this.device.createShaderModule({
+        code:
+        /* GLSL(
+         *           'vertex',
+         *           `#version 450
+         *             void main() {
+         *               gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
+         *             }
+         *           `
+         *         )
+         */
+        new Uint32Array([119734787, 66304, 524295, 21, 0, 131089, 1, 393227, 1, 1280527431, 1685353262, 808793134, 0, 196622, 0, 1, 393231, 0, 4, 1852399981, 0, 13, 196611, 2, 450, 262149, 4, 1852399981, 0, 393221, 11, 1348430951, 1700164197, 2019914866, 0, 393222, 11, 0, 1348430951, 1953067887, 7237481, 458758, 11, 1, 1348430951, 1953393007, 1702521171, 0, 458758, 11, 2, 1130327143, 1148217708, 1635021673, 6644590, 458758, 11, 3, 1130327143, 1147956341, 1635021673, 6644590, 196613, 13, 0, 327752, 11, 0, 11, 0, 327752, 11, 1, 11, 1, 327752, 11, 2, 11, 3, 327752, 11, 3, 11, 4, 196679, 11, 2, 131091, 2, 196641, 3, 2, 196630, 6, 32, 262167, 7, 6, 4, 262165, 8, 32, 0, 262187, 8, 9, 1, 262172, 10, 6, 9, 393246, 11, 7, 6, 10, 10, 262176, 12, 3, 11, 262203, 12, 13, 3, 262165, 14, 32, 1, 262187, 14, 15, 0, 262187, 6, 16, 0, 262187, 6, 17, 1065353216, 458796, 7, 18, 16, 16, 16, 17, 262176, 19, 3, 7, 327734, 2, 4, 0, 3, 131320, 5, 327745, 19, 20, 13, 15, 196670, 20, 18, 65789, 65592])
+      }),
+      entryPoint: 'main'
+    };
+  }
+
+  getFragmentStage(format) {
+    let fragColorType;
+
+    if (format.endsWith('sint')) {
+      fragColorType = 'ivec4';
+    } else if (format.endsWith('uint')) {
+      fragColorType = 'uvec4';
+    } else {
+      fragColorType = 'vec4';
+    }
+
+    const code = `
+      #version 450
+      layout(location = 0) out ${fragColorType} fragColor;
+      void main() {
+        fragColor = ${fragColorType}(0.0, 1.0, 0.0, 1.0);
+      }
+    `;
+    return {
+      module: this.makeShaderModule('fragment', code),
+      entryPoint: 'main'
+    };
+  }
+
+  getPipelineLayout() {
+    return this.device.createPipelineLayout({
+      bindGroupLayouts: []
+    });
+  }
+
+  createTexture(params) {
+    const {
+      format,
+      sampleCount
+    } = params;
+    return this.device.createTexture({
+      size: {
+        width: 4,
+        height: 4,
+        depth: 1
+      },
+      usage: GPUTextureUsage.OUTPUT_ATTACHMENT,
+      format,
+      sampleCount
+    });
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('basic use of createRenderPipeline', t => {
+  const descriptor = t.getDescriptor();
+  t.device.createRenderPipeline(descriptor);
+});
+g.test('at least one color state is required', async t => {
+  const goodDescriptor = t.getDescriptor({
+    colorStates: [{
+      format: 'rgba8unorm'
+    }]
+  }); // Control case
+
+  t.device.createRenderPipeline(goodDescriptor); // Fail because lack of color states
+
+  const badDescriptor = t.getDescriptor({
+    colorStates: []
+  });
+  await t.expectValidationError(() => {
+    t.device.createRenderPipeline(badDescriptor);
+  });
+});
+g.test('color formats must be renderable', async t => {
+  const {
+    format,
+    success
+  } = t.params;
+  const descriptor = t.getDescriptor({
+    colorStates: [{
+      format
+    }]
+  });
+
+  if (success) {
+    // Succeeds when format is renderable
+    t.device.createRenderPipeline(descriptor);
+  } else {
+    // Fails because when format is non-renderable
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+}).params([// 8-bit formats
+{
+  format: 'r8unorm',
+  success: true
+}, {
+  format: 'r8snorm',
+  success: false
+}, {
+  format: 'r8uint',
+  success: true
+}, {
+  format: 'r8sint',
+  success: true
+}, // 16-bit formats
+{
+  format: 'r16uint',
+  success: true
+}, {
+  format: 'r16sint',
+  success: true
+}, {
+  format: 'r16float',
+  success: true
+}, {
+  format: 'rg8unorm',
+  success: true
+}, {
+  format: 'rg8snorm',
+  success: false
+}, {
+  format: 'rg8uint',
+  success: true
+}, {
+  format: 'rg8sint',
+  success: true
+}, // 32-bit formats
+{
+  format: 'r32uint',
+  success: true
+}, {
+  format: 'r32sint',
+  success: true
+}, {
+  format: 'r32float',
+  success: true
+}, {
+  format: 'rg16uint',
+  success: true
+}, {
+  format: 'rg16sint',
+  success: true
+}, {
+  format: 'rg16float',
+  success: true
+}, {
+  format: 'rgba8unorm',
+  success: true
+}, {
+  format: 'rgba8unorm-srgb',
+  success: true
+}, {
+  format: 'rgba8snorm',
+  success: false
+}, {
+  format: 'rgba8uint',
+  success: true
+}, {
+  format: 'rgba8sint',
+  success: true
+}, {
+  format: 'bgra8unorm',
+  success: true
+}, {
+  format: 'bgra8unorm-srgb',
+  success: true
+}, // Packed 32-bit formats
+{
+  format: 'rgb10a2unorm',
+  success: true
+}, {
+  format: 'rg11b10float',
+  success: false
+}, // 64-bit formats
+{
+  format: 'rg32uint',
+  success: true
+}, {
+  format: 'rg32sint',
+  success: true
+}, {
+  format: 'rg32float',
+  success: true
+}, {
+  format: 'rgba16uint',
+  success: true
+}, {
+  format: 'rgba16sint',
+  success: true
+}, {
+  format: 'rgba16float',
+  success: true
+}, // 128-bit formats
+{
+  format: 'rgba32uint',
+  success: true
+}, {
+  format: 'rgba32sint',
+  success: true
+}, {
+  format: 'rgba32float',
+  success: true
+}]);
+g.test('sample count must be valid', async t => {
+  const {
+    sampleCount,
+    success
+  } = t.params;
+  const descriptor = t.getDescriptor({
+    sampleCount
+  });
+
+  if (success) {
+    // Succeeds when sample count is valid
+    t.device.createRenderPipeline(descriptor);
+  } else {
+    // Fails when sample count is not 4 or 1
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+}).params([{
+  sampleCount: 0,
+  success: false
+}, {
+  sampleCount: 1,
+  success: true
+}, {
+  sampleCount: 2,
+  success: false
+}, {
+  sampleCount: 3,
+  success: false
+}, {
+  sampleCount: 4,
+  success: true
+}, {
+  sampleCount: 8,
+  success: false
+}, {
+  sampleCount: 16,
+  success: false
+}]);
+g.test('sample count must be equal to the one of every attachment in the render pass', async t => {
+  const {
+    attachmentSamples,
+    pipelineSamples,
+    success
+  } = t.params;
+  const colorTexture = t.createTexture({
+    format: 'rgba8unorm',
+    sampleCount: attachmentSamples
+  });
+  const depthStencilTexture = t.createTexture({
+    format: 'depth24plus-stencil8',
+    sampleCount: attachmentSamples
+  });
+  const renderPassDescriptorWithoutDepthStencil = {
+    colorAttachments: [{
+      attachment: colorTexture.createView(),
+      loadValue: {
+        r: 1.0,
+        g: 0.0,
+        b: 0.0,
+        a: 1.0
+      }
+    }]
+  };
+  const renderPassDescriptorWithDepthStencilOnly = {
+    colorAttachments: [],
+    depthStencilAttachment: {
+      attachment: depthStencilTexture.createView(),
+      depthLoadValue: 1.0,
+      depthStoreOp: 'store',
+      stencilLoadValue: 0,
+      stencilStoreOp: 'store'
+    }
+  };
+  const pipelineWithoutDepthStencil = t.device.createRenderPipeline(t.getDescriptor({
+    sampleCount: pipelineSamples
+  }));
+  const pipelineWithDepthStencilOnly = t.device.createRenderPipeline(t.getDescriptor({
+    colorStates: [],
+    depthStencilState: {
+      format: 'depth24plus-stencil8'
+    },
+    sampleCount: pipelineSamples
+  }));
+
+  for (const {
+    renderPassDescriptor,
+    pipeline
+  } of [{
+    renderPassDescriptor: renderPassDescriptorWithoutDepthStencil,
+    pipeline: pipelineWithoutDepthStencil
+  }, {
+    renderPassDescriptor: renderPassDescriptorWithDepthStencilOnly,
+    pipeline: pipelineWithDepthStencilOnly
+  }]) {
+    const commandEncoder = t.device.createCommandEncoder();
+    const renderPass = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPass.setPipeline(pipeline);
+    renderPass.endPass();
+    await t.expectValidationError(() => {
+      commandEncoder.finish();
+    }, !success);
+  }
+}).params([{
+  attachmentSamples: 4,
+  pipelineSamples: 4,
+  success: true
+}, // It is allowed to use multisampled render pass and multisampled render pipeline.
+{
+  attachmentSamples: 4,
+  pipelineSamples: 1,
+  success: false
+}, // It is not allowed to use multisampled render pass and non-multisampled render pipeline.
+{
+  attachmentSamples: 1,
+  pipelineSamples: 4,
+  success: false
+} // It is not allowed to use non-multisampled render pass and multisampled render pipeline.
+]);
+//# sourceMappingURL=createRenderPipeline.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createTexture.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createTexture.spec.js
new file mode 100644
index 0000000..c99204b96
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createTexture.spec.js
@@ -0,0 +1,336 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+createTexture validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+
+class F extends ValidationTest {
+  getDescriptor(options = {}) {
+    const {
+      width = 32,
+      height = 32,
+      arrayLayerCount = 1,
+      mipLevelCount = 1,
+      sampleCount = 1,
+      format = 'rgba8unorm'
+    } = options;
+    return {
+      size: {
+        width,
+        height,
+        depth: 1
+      },
+      arrayLayerCount,
+      mipLevelCount,
+      sampleCount,
+      dimension: '2d',
+      format,
+      usage: GPUTextureUsage.OUTPUT_ATTACHMENT | GPUTextureUsage.SAMPLED
+    };
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('validation of sampleCount', async t => {
+  const {
+    sampleCount,
+    mipLevelCount,
+    arrayLayerCount,
+    success
+  } = t.params;
+  const descriptor = t.getDescriptor({
+    sampleCount,
+    mipLevelCount,
+    arrayLayerCount
+  });
+  await t.expectValidationError(() => {
+    t.device.createTexture(descriptor);
+  }, !success);
+}).params([{
+  sampleCount: 0,
+  success: false
+}, // sampleCount of 0 is not allowed
+{
+  sampleCount: 1,
+  success: true
+}, // sampleCount of 1 is allowed
+{
+  sampleCount: 2,
+  success: false
+}, // sampleCount of 2 is not allowed
+{
+  sampleCount: 3,
+  success: false
+}, // sampleCount of 3 is not allowed
+{
+  sampleCount: 4,
+  success: true
+}, // sampleCount of 4 is allowed
+{
+  sampleCount: 8,
+  success: false
+}, // sampleCount of 8 is not allowed
+{
+  sampleCount: 16,
+  success: false
+}, // sampleCount of 16 is not allowed
+{
+  sampleCount: 4,
+  mipLevelCount: 2,
+  success: false
+}, // it is an error to create a multisampled texture with mipLevelCount > 1
+{
+  sampleCount: 4,
+  arrayLayerCount: 2,
+  success: true
+} // multisampled 2D array texture is supported
+]);
+g.test('validation of mipLevelCount', async t => {
+  const {
+    width,
+    height,
+    mipLevelCount,
+    success
+  } = t.params;
+  const descriptor = t.getDescriptor({
+    width,
+    height,
+    mipLevelCount
+  });
+  await t.expectValidationError(() => {
+    t.device.createTexture(descriptor);
+  }, !success);
+}).params([{
+  width: 32,
+  height: 32,
+  mipLevelCount: 1,
+  success: true
+}, // mipLevelCount of 1 is allowed
+{
+  width: 32,
+  height: 32,
+  mipLevelCount: 0,
+  success: false
+}, // mipLevelCount of 0 is not allowed
+{
+  width: 32,
+  height: 32,
+  mipLevelCount: 6,
+  success: true
+}, // full mip chains are allowed (Mip level sizes: 32, 16, 8, 4, 2, 1)
+{
+  width: 31,
+  height: 32,
+  mipLevelCount: 7,
+  success: false
+}, // too big mip chains on width are disallowed (Mip level width: 31, 15, 7, 3, 1, 1)
+{
+  width: 32,
+  height: 31,
+  mipLevelCount: 7,
+  success: false
+}, // too big mip chains on height are disallowed (Mip level width: 31, 15, 7, 3, 1, 1)
+{
+  width: 32,
+  height: 32,
+  mipLevelCount: 100,
+  success: false
+}, // undefined shift check if miplevel is bigger than the integer bit width
+{
+  width: 32,
+  height: 8,
+  mipLevelCount: 6,
+  success: true
+} // non square mip map halves the resolution until a 1x1 dimension. (Mip maps: 32 * 8, 16 * 4, 8 * 2, 4 * 1, 2 * 1, 1 * 1)
+]);
+g.test('it is valid to destroy a texture', t => {
+  const descriptor = t.getDescriptor();
+  const texture = t.device.createTexture(descriptor);
+  texture.destroy();
+});
+g.test('it is valid to destroy a destroyed texture', t => {
+  const descriptor = t.getDescriptor();
+  const texture = t.device.createTexture(descriptor);
+  texture.destroy();
+  texture.destroy();
+});
+g.test('it is invalid to submit a destroyed texture before and after encode', async t => {
+  const {
+    destroyBeforeEncode,
+    destroyAfterEncode,
+    success
+  } = t.params;
+  const descriptor = t.getDescriptor();
+  const texture = t.device.createTexture(descriptor);
+  const textureView = texture.createView();
+
+  if (destroyBeforeEncode) {
+    texture.destroy();
+  }
+
+  const commandEncoder = t.device.createCommandEncoder();
+  const renderPass = commandEncoder.beginRenderPass({
+    colorAttachments: [{
+      attachment: textureView,
+      loadValue: {
+        r: 1.0,
+        g: 0.0,
+        b: 0.0,
+        a: 1.0
+      }
+    }]
+  });
+  renderPass.endPass();
+  const commandBuffer = commandEncoder.finish();
+
+  if (destroyAfterEncode) {
+    texture.destroy();
+  }
+
+  await t.expectValidationError(() => {
+    t.queue.submit([commandBuffer]);
+  }, !success);
+}).params([{
+  destroyBeforeEncode: false,
+  destroyAfterEncode: false,
+  success: true
+}, {
+  destroyBeforeEncode: true,
+  destroyAfterEncode: false,
+  success: false
+}, {
+  destroyBeforeEncode: false,
+  destroyAfterEncode: true,
+  success: false
+}]);
+g.test('it is invalid to have an output attachment texture with non renderable format', async t => {
+  const {
+    format,
+    success
+  } = t.params;
+  const descriptor = t.getDescriptor({
+    width: 1,
+    height: 1,
+    format
+  });
+  await t.expectValidationError(() => {
+    t.device.createTexture(descriptor);
+  }, !success);
+}).params([// 8-bit formats
+{
+  format: 'r8unorm',
+  success: true
+}, {
+  format: 'r8snorm',
+  success: false
+}, {
+  format: 'r8uint',
+  success: true
+}, {
+  format: 'r8sint',
+  success: true
+}, // 16-bit formats
+{
+  format: 'r16uint',
+  success: true
+}, {
+  format: 'r16sint',
+  success: true
+}, {
+  format: 'r16float',
+  success: true
+}, {
+  format: 'rg8unorm',
+  success: true
+}, {
+  format: 'rg8snorm',
+  success: false
+}, {
+  format: 'rg8uint',
+  success: true
+}, {
+  format: 'rg8sint',
+  success: true
+}, // 32-bit formats
+{
+  format: 'r32uint',
+  success: true
+}, {
+  format: 'r32sint',
+  success: true
+}, {
+  format: 'r32float',
+  success: true
+}, {
+  format: 'rg16uint',
+  success: true
+}, {
+  format: 'rg16sint',
+  success: true
+}, {
+  format: 'rg16float',
+  success: true
+}, {
+  format: 'rgba8unorm',
+  success: true
+}, {
+  format: 'rgba8unorm-srgb',
+  success: true
+}, {
+  format: 'rgba8snorm',
+  success: false
+}, {
+  format: 'rgba8uint',
+  success: true
+}, {
+  format: 'rgba8sint',
+  success: true
+}, {
+  format: 'bgra8unorm',
+  success: true
+}, {
+  format: 'bgra8unorm-srgb',
+  success: true
+}, // Packed 32-bit formats
+{
+  format: 'rgb10a2unorm',
+  success: true
+}, {
+  format: 'rg11b10float',
+  success: false
+}, // 64-bit formats
+{
+  format: 'rg32uint',
+  success: true
+}, {
+  format: 'rg32sint',
+  success: true
+}, {
+  format: 'rg32float',
+  success: true
+}, {
+  format: 'rgba16uint',
+  success: true
+}, {
+  format: 'rgba16sint',
+  success: true
+}, {
+  format: 'rgba16float',
+  success: true
+}, // 128-bit formats
+{
+  format: 'rgba32uint',
+  success: true
+}, {
+  format: 'rgba32sint',
+  success: true
+}, {
+  format: 'rgba32float',
+  success: true
+}]); // TODO: Add tests for compressed texture formats
+//# sourceMappingURL=createTexture.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createView.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createView.spec.js
new file mode 100644
index 0000000..2330c6e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/createView.spec.js
@@ -0,0 +1,417 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+createView validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+const ARRAY_LAYER_COUNT_2D = 6;
+const MIP_LEVEL_COUNT = 6;
+const FORMAT = 'rgba8unorm';
+
+class F extends ValidationTest {
+  createTexture(options = {}) {
+    const {
+      width = 32,
+      height = 32,
+      arrayLayerCount = 1,
+      mipLevelCount = MIP_LEVEL_COUNT,
+      sampleCount = 1
+    } = options;
+    return this.device.createTexture({
+      size: {
+        width,
+        height,
+        depth: 1
+      },
+      arrayLayerCount,
+      mipLevelCount,
+      sampleCount,
+      dimension: '2d',
+      format: FORMAT,
+      usage: GPUTextureUsage.SAMPLED
+    });
+  }
+
+  getDescriptor(options = {}) {
+    const {
+      format = FORMAT,
+      dimension = '2d',
+      baseMipLevel = 0,
+      mipLevelCount = MIP_LEVEL_COUNT,
+      baseArrayLayer = 0,
+      arrayLayerCount = 1
+    } = options;
+    return {
+      format,
+      dimension,
+      baseMipLevel,
+      mipLevelCount,
+      baseArrayLayer,
+      arrayLayerCount
+    };
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('creating texture view on a 2D non array texture', async t => {
+  const {
+    dimension = '2d',
+    arrayLayerCount,
+    mipLevelCount,
+    baseMipLevel,
+    success
+  } = t.params;
+  const texture = t.createTexture({
+    arrayLayerCount: 1
+  });
+  const descriptor = t.getDescriptor({
+    dimension,
+    arrayLayerCount,
+    mipLevelCount,
+    baseMipLevel
+  });
+  await t.expectValidationError(() => {
+    texture.createView(descriptor);
+  }, !success);
+}).params([{
+  success: true
+}, // default view works
+{
+  arrayLayerCount: 1,
+  success: true
+}, // it is OK to create a 2D texture view on a 2D texture
+{
+  arrayLayerCount: 2,
+  success: false
+}, // it is an error to view a layer past the end of the texture
+{
+  dimension: '2d-array',
+  arrayLayerCount: 1,
+  success: true
+}, // it is OK to create a 1-layer 2D array texture view on a 2D texture
+// mip level is in range
+{
+  mipLevelCount: 1,
+  baseMipLevel: MIP_LEVEL_COUNT - 1,
+  success: true
+}, {
+  mipLevelCount: 2,
+  baseMipLevel: MIP_LEVEL_COUNT - 2,
+  success: true
+}, // baseMipLevel == k && mipLevelCount == 0 means to use levels k..end
+{
+  mipLevelCount: 0,
+  baseMipLevel: 0,
+  success: true
+}, {
+  mipLevelCount: 0,
+  baseMipLevel: 1,
+  success: true
+}, {
+  mipLevelCount: 0,
+  baseMipLevel: MIP_LEVEL_COUNT - 1,
+  success: true
+}, {
+  mipLevelCount: 0,
+  baseMipLevel: MIP_LEVEL_COUNT,
+  success: false
+}, // it is an error to make the mip level out of range
+{
+  mipLevelCount: MIP_LEVEL_COUNT + 1,
+  baseMipLevel: 0,
+  success: false
+}, {
+  mipLevelCount: MIP_LEVEL_COUNT,
+  baseMipLevel: 1,
+  success: false
+}, {
+  mipLevelCount: 2,
+  baseMipLevel: MIP_LEVEL_COUNT - 1,
+  success: false
+}, {
+  mipLevelCount: 1,
+  baseMipLevel: MIP_LEVEL_COUNT,
+  success: false
+}]);
+g.test('creating texture view on a 2D array texture', async t => {
+  const {
+    dimension = '2d-array',
+    arrayLayerCount,
+    baseArrayLayer,
+    success
+  } = t.params;
+  const texture = t.createTexture({
+    arrayLayerCount: ARRAY_LAYER_COUNT_2D
+  });
+  const descriptor = t.getDescriptor({
+    dimension,
+    arrayLayerCount,
+    baseArrayLayer
+  });
+  await t.expectValidationError(() => {
+    texture.createView(descriptor);
+  }, !success);
+}).params([{
+  success: true
+}, // default view works
+{
+  dimension: '2d',
+  arrayLayerCount: 1,
+  success: true
+}, // it is OK to create a 2D texture view on a 2D array texture
+{
+  arrayLayerCount: ARRAY_LAYER_COUNT_2D,
+  success: true
+}, // it is OK to create a 2D array texture view on a 2D array texture
+// baseArrayLayer == k && arrayLayerCount == 0 means to use layers k..end.
+{
+  arrayLayerCount: 0,
+  baseArrayLayer: 0,
+  success: true
+}, {
+  arrayLayerCount: 0,
+  baseArrayLayer: 1,
+  success: true
+}, {
+  arrayLayerCount: 0,
+  baseArrayLayer: ARRAY_LAYER_COUNT_2D - 1,
+  success: true
+}, {
+  arrayLayerCount: 0,
+  baseArrayLayer: ARRAY_LAYER_COUNT_2D,
+  success: false
+}, // It is an error for the array layer range of the view to exceed that of the texture
+{
+  arrayLayerCount: ARRAY_LAYER_COUNT_2D + 1,
+  baseArrayLayer: 0,
+  success: false
+}, {
+  arrayLayerCount: ARRAY_LAYER_COUNT_2D,
+  baseArrayLayer: 1,
+  success: false
+}, {
+  arrayLayerCount: 2,
+  baseArrayLayer: ARRAY_LAYER_COUNT_2D - 1,
+  success: false
+}, {
+  arrayLayerCount: 1,
+  baseArrayLayer: ARRAY_LAYER_COUNT_2D,
+  success: false
+}]);
+g.test('Using defaults validates the same as setting values for more than 1 array layer', async t => {
+  const {
+    format,
+    dimension,
+    arrayLayerCount,
+    mipLevelCount,
+    success
+  } = t.params;
+  const texture = t.createTexture({
+    arrayLayerCount: ARRAY_LAYER_COUNT_2D
+  });
+  const descriptor = {
+    format,
+    dimension,
+    arrayLayerCount,
+    mipLevelCount
+  };
+  await t.expectValidationError(() => {
+    texture.createView(descriptor);
+  }, !success);
+}).params([{
+  success: true
+}, {
+  format: 'rgba8unorm',
+  success: true
+}, {
+  format: 'r8unorm',
+  success: false
+}, {
+  dimension: '2d-array',
+  success: true
+}, {
+  dimension: '2d',
+  success: false
+}, {
+  arrayLayerCount: ARRAY_LAYER_COUNT_2D,
+  success: false
+}, // setting array layers to non-0 means the dimensionality will default to 2D so by itself it causes an error.
+{
+  arrayLayerCount: ARRAY_LAYER_COUNT_2D,
+  dimension: '2d-array',
+  success: true
+}, {
+  arrayLayerCount: ARRAY_LAYER_COUNT_2D,
+  dimension: '2d-array',
+  mipLevelCount: MIP_LEVEL_COUNT,
+  success: true
+}]);
+g.test('Using defaults validates the same as setting values for only 1 array layer', async t => {
+  const {
+    format,
+    dimension,
+    arrayLayerCount,
+    mipLevelCount,
+    success
+  } = t.params;
+  const texture = t.createTexture({
+    arrayLayerCount: 1
+  });
+  const descriptor = {
+    format,
+    dimension,
+    arrayLayerCount,
+    mipLevelCount
+  };
+  await t.expectValidationError(() => {
+    texture.createView(descriptor);
+  }, !success);
+}).params([{
+  success: true
+}, {
+  format: 'rgba8unorm',
+  success: true
+}, {
+  format: 'r8unorm',
+  success: false
+}, {
+  dimension: '2d-array',
+  success: true
+}, {
+  dimension: '2d',
+  success: true
+}, {
+  arrayLayerCount: 0,
+  success: true
+}, {
+  arrayLayerCount: 1,
+  success: true
+}, {
+  arrayLayerCount: 2,
+  success: false
+}, {
+  mipLevelCount: MIP_LEVEL_COUNT,
+  success: true
+}, {
+  mipLevelCount: 1,
+  success: true
+}]);
+g.test('creating cube map texture view', async t => {
+  const {
+    dimension = '2d-array',
+    arrayLayerCount,
+    success
+  } = t.params;
+  const texture = t.createTexture({
+    arrayLayerCount: 16
+  });
+  const descriptor = t.getDescriptor({
+    dimension,
+    arrayLayerCount
+  });
+  await t.expectValidationError(() => {
+    texture.createView(descriptor);
+  }, !success);
+}).params([{
+  dimension: 'cube',
+  arrayLayerCount: 6,
+  success: true
+}, // it is OK to create a cube map texture view with arrayLayerCount == 6
+// it is an error to create a cube map texture view with arrayLayerCount != 6
+{
+  dimension: 'cube',
+  arrayLayerCount: 3,
+  success: false
+}, {
+  dimension: 'cube',
+  arrayLayerCount: 7,
+  success: false
+}, {
+  dimension: 'cube',
+  arrayLayerCount: 12,
+  success: false
+}, {
+  dimension: 'cube',
+  success: false
+}, {
+  dimension: 'cube-array',
+  arrayLayerCount: 12,
+  success: true
+}, // it is OK to create a cube map array texture view with arrayLayerCount % 6 == 0
+// it is an error to create a cube map array texture view with arrayLayerCount % 6 != 0
+{
+  dimension: 'cube-array',
+  arrayLayerCount: 11,
+  success: false
+}, {
+  dimension: 'cube-array',
+  arrayLayerCount: 13,
+  success: false
+}]);
+g.test('creating cube map texture view with a non square texture', async t => {
+  const {
+    dimension,
+    arrayLayerCount
+  } = t.params;
+  const nonSquareTexture = t.createTexture({
+    arrayLayerCount: 18,
+    width: 32,
+    height: 16,
+    mipLevelCount: 5
+  });
+  const descriptor = t.getDescriptor({
+    dimension,
+    arrayLayerCount
+  });
+  await t.expectValidationError(() => {
+    nonSquareTexture.createView(descriptor);
+  });
+}).params([{
+  dimension: 'cube',
+  arrayLayerCount: 6
+}, // it is an error to create a cube map texture view with width != height.
+{
+  dimension: 'cube-array',
+  arrayLayerCount: 12
+} // it is an error to create a cube map array texture view with width != height.
+]); // TODO: add more tests when rules are fully implemented.
+
+g.test('test the format compatibility rules when creating a texture view', async t => {
+  const texture = t.createTexture({
+    arrayLayerCount: 1
+  });
+  const descriptor = t.getDescriptor({
+    format: 'depth24plus-stencil8'
+  }); // it is invalid to create a view in depth-stencil format on a RGBA texture
+
+  await t.expectValidationError(() => {
+    texture.createView(descriptor);
+  });
+});
+g.test('it is invalid to use a texture view created from a destroyed texture', async t => {
+  const texture = t.createTexture({
+    arrayLayerCount: 1
+  });
+  texture.destroy();
+  const commandEncoder = t.device.createCommandEncoder();
+  const renderPass = commandEncoder.beginRenderPass({
+    colorAttachments: [{
+      attachment: texture.createView(),
+      loadValue: {
+        r: 1.0,
+        g: 0.0,
+        b: 0.0,
+        a: 1.0
+      }
+    }]
+  });
+  renderPass.endPass();
+  await t.expectValidationError(() => {
+    commandEncoder.finish();
+  });
+}); // TODO: Add tests for TextureAspect
+//# sourceMappingURL=createView.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/error_scope.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/error_scope.spec.js
new file mode 100644
index 0000000..2232db9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/error_scope.spec.js
@@ -0,0 +1,141 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+export const description = `
+error scope validation tests.
+`;
+import { getGPU } from '../../../framework/gpu/implementation.js';
+import { Fixture, TestGroup, rejectOnTimeout } from '../../../framework/index.js';
+
+class F extends Fixture {
+  constructor(...args) {
+    super(...args);
+
+    _defineProperty(this, "device", undefined);
+  }
+
+  async init() {
+    super.init();
+    const gpu = getGPU();
+    const adapter = await gpu.requestAdapter();
+    this.device = await adapter.requestDevice();
+  }
+
+  createErrorBuffer() {
+    this.device.createBuffer({
+      size: 1024,
+      usage: 0xffff // Invalid GPUBufferUsage
+
+    }); // TODO: Remove when chrome does it automatically.
+
+    this.device.getQueue().submit([]);
+  }
+
+  async expectUncapturedError(fn) {
+    return this.asyncExpectation(() => {
+      // TODO: Make arbitrary timeout value a test runner variable
+      const TIMEOUT_IN_MS = 1000;
+      const promise = new Promise(resolve => {
+        const eventListener = event => {
+          this.debug(`Got uncaptured error event with ${event.error}`);
+          resolve(event);
+        };
+
+        this.device.addEventListener('uncapturederror', eventListener, {
+          once: true
+        });
+      });
+      fn();
+      return Promise.race([promise, rejectOnTimeout(TIMEOUT_IN_MS, 'Timeout occurred waiting for uncaptured error')]);
+    });
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('simple case where the error scope catches an error', async t => {
+  t.device.pushErrorScope('validation');
+  t.createErrorBuffer();
+  const error = await t.device.popErrorScope();
+  t.expect(error instanceof GPUValidationError);
+});
+g.test('errors bubble to the parent scope if not handled by the current scope', async t => {
+  t.device.pushErrorScope('validation');
+  t.device.pushErrorScope('out-of-memory');
+  t.createErrorBuffer();
+  {
+    const error = await t.device.popErrorScope();
+    t.expect(error === null);
+  }
+  {
+    const error = await t.device.popErrorScope();
+    t.expect(error instanceof GPUValidationError);
+  }
+});
+g.test('if an error scope matches an error it does not bubble to the parent scope', async t => {
+  t.device.pushErrorScope('validation');
+  t.device.pushErrorScope('validation');
+  t.createErrorBuffer();
+  {
+    const error = await t.device.popErrorScope();
+    t.expect(error instanceof GPUValidationError);
+  }
+  {
+    const error = await t.device.popErrorScope();
+    t.expect(error === null);
+  }
+});
+g.test('if no error scope handles an error it fires an uncapturederror event', async t => {
+  t.device.pushErrorScope('out-of-memory');
+  const uncapturedErrorEvent = await t.expectUncapturedError(() => {
+    t.createErrorBuffer();
+  });
+  t.expect(uncapturedErrorEvent.error instanceof GPUValidationError);
+  const error = await t.device.popErrorScope();
+  t.expect(error === null);
+});
+g.test('push/popping sibling error scopes must be balanced', async t => {
+  {
+    const promise = t.device.popErrorScope();
+    await t.shouldReject('OperationError', promise);
+  }
+  const promises = [];
+
+  for (let i = 0; i < 1000; i++) {
+    t.device.pushErrorScope('validation');
+    promises.push(t.device.popErrorScope());
+  }
+
+  const errors = await Promise.all(promises);
+  t.expect(errors.every(e => e === null));
+  {
+    const promise = t.device.popErrorScope();
+    await t.shouldReject('OperationError', promise);
+  }
+});
+g.test('push/popping nested error scopes must be balanced', async t => {
+  {
+    const promise = t.device.popErrorScope();
+    await t.shouldReject('OperationError', promise);
+  }
+  const promises = [];
+
+  for (let i = 0; i < 1000; i++) {
+    t.device.pushErrorScope('validation');
+  }
+
+  for (let i = 0; i < 1000; i++) {
+    promises.push(t.device.popErrorScope());
+  }
+
+  const errors = await Promise.all(promises);
+  t.expect(errors.every(e => e === null));
+  {
+    const promise = t.device.popErrorScope();
+    await t.shouldReject('OperationError', promise);
+  }
+});
+//# sourceMappingURL=error_scope.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/fences.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/fences.spec.js
new file mode 100644
index 0000000..4aca3d7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/fences.spec.js
@@ -0,0 +1,70 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+fences validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+export const g = new TestGroup(ValidationTest); // TODO: Remove if https://github.com/gpuweb/gpuweb/issues/377 is decided
+
+g.test('wait on a fence without signaling the value is invalid', async t => {
+  const fence = t.queue.createFence();
+  await t.expectValidationError(() => {
+    t.shouldReject('OperationError', fence.onCompletion(2));
+  });
+}); // TODO: Remove if https://github.com/gpuweb/gpuweb/issues/377 is decided
+
+g.test('wait on a fence with a value greater than signaled value is invalid', async t => {
+  const fence = t.queue.createFence();
+  t.queue.signal(fence, 2);
+  await t.expectValidationError(() => {
+    t.shouldReject('OperationError', fence.onCompletion(3));
+  });
+});
+g.test('signal a value lower than signaled value is invalid', async t => {
+  const fence = t.queue.createFence({
+    initialValue: 1
+  });
+  await t.expectValidationError(() => {
+    t.queue.signal(fence, 0);
+  });
+});
+g.test('signal a value equal to signaled value is invalid', async t => {
+  const fence = t.queue.createFence({
+    initialValue: 1
+  });
+  await t.expectValidationError(() => {
+    t.queue.signal(fence, 1);
+  });
+});
+g.test('increasing fence value by more than 1 succeeds', async t => {
+  const fence = t.queue.createFence();
+  t.queue.signal(fence, 2);
+  await fence.onCompletion(2);
+  t.queue.signal(fence, 6);
+  await fence.onCompletion(6);
+});
+g.test('signal a fence on a different device than it was created on is invalid', async t => {
+  const fence = t.queue.createFence();
+  const anotherDevice = await t.device.adapter.requestDevice();
+  const anotherQueue = anotherDevice.getQueue();
+  await t.expectValidationError(() => {
+    anotherQueue.signal(fence, 2);
+  });
+});
+g.test('signal a fence on a different device does not update fence signaled value', async t => {
+  const fence = t.queue.createFence({
+    initialValue: 1
+  });
+  const anotherDevice = await t.device.adapter.requestDevice();
+  const anotherQueue = anotherDevice.getQueue();
+  await t.expectValidationError(() => {
+    anotherQueue.signal(fence, 2);
+  });
+  t.expect(fence.getCompletedValue() === 1);
+  t.queue.signal(fence, 2);
+  await fence.onCompletion(2);
+});
+//# sourceMappingURL=fences.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/queue_submit.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/queue_submit.spec.js
new file mode 100644
index 0000000..50d912f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/queue_submit.spec.js
@@ -0,0 +1,39 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+queue submit validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+export const g = new TestGroup(ValidationTest);
+g.test('submitting with a mapped buffer is disallowed', async t => {
+  const buffer = t.device.createBuffer({
+    size: 4,
+    usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC
+  });
+  const targetBuffer = t.device.createBuffer({
+    size: 4,
+    usage: GPUBufferUsage.COPY_DST
+  });
+
+  const getCommandBuffer = () => {
+    const commandEncoder = t.device.createCommandEncoder();
+    commandEncoder.copyBufferToBuffer(buffer, 0, targetBuffer, 0, 4);
+    return commandEncoder.finish();
+  }; // Submitting when the buffer has never been mapped should succeed
+
+
+  t.queue.submit([getCommandBuffer()]); // Map the buffer, submitting when the buffer is mapped should fail
+
+  await buffer.mapWriteAsync();
+  t.queue.submit([]);
+  await t.expectValidationError(() => {
+    t.queue.submit([getCommandBuffer()]);
+  }); // Unmap the buffer, queue submit should succeed
+
+  buffer.unmap();
+  t.queue.submit([getCommandBuffer()]);
+});
+//# sourceMappingURL=queue_submit.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/render_pass.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/render_pass.spec.js
new file mode 100644
index 0000000..f71a4a2b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/render_pass.spec.js
@@ -0,0 +1,174 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+render pass validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+
+class F extends ValidationTest {
+  getUniformBuffer() {
+    return this.device.createBuffer({
+      size: 4 * Float32Array.BYTES_PER_ELEMENT,
+      usage: GPUBufferUsage.UNIFORM
+    });
+  }
+
+  createRenderPipeline(pipelineLayout) {
+    const vertexModule = this.device.createShaderModule({
+      code:
+      /* GLSL(
+       *         'vertex',
+       *         `#version 450
+       *           layout (set = 0, binding = 0) uniform vertexUniformBuffer {
+       *               mat2 transform;
+       *           };
+       *           void main() {
+       *               const vec2 pos[3] = vec2[3](vec2(-1.f, -1.f), vec2(1.f, -1.f), vec2(-1.f, 1.f));
+       *               gl_Position = vec4(transform * pos[gl_VertexIndex], 0.f, 1.f);
+       *           }
+       *         `
+       *       )
+       */
+      new Uint32Array([119734787, 66304, 524295, 47, 0, 131089, 1, 393227, 1, 1280527431, 1685353262, 808793134, 0, 196622, 0, 1, 458767, 0, 4, 1852399981, 0, 13, 33, 196611, 2, 450, 262149, 4, 1852399981, 0, 393221, 11, 1348430951, 1700164197, 2019914866, 0, 393222, 11, 0, 1348430951, 1953067887, 7237481, 458758, 11, 1, 1348430951, 1953393007, 1702521171, 0, 458758, 11, 2, 1130327143, 1148217708, 1635021673, 6644590, 458758, 11, 3, 1130327143, 1147956341, 1635021673, 6644590, 196613, 13, 0, 458757, 18, 1953654134, 1851095141, 1919903337, 1718960749, 7497062, 393222, 18, 0, 1851880052, 1919903347, 109, 196613, 20, 0, 393221, 33, 1449094247, 1702130277, 1684949368, 30821, 327685, 36, 1701080681, 1818386808, 101, 327752, 11, 0, 11, 0, 327752, 11, 1, 11, 1, 327752, 11, 2, 11, 3, 327752, 11, 3, 11, 4, 196679, 11, 2, 262216, 18, 0, 5, 327752, 18, 0, 35, 0, 327752, 18, 0, 7, 16, 196679, 18, 2, 262215, 20, 34, 0, 262215, 20, 33, 0, 262215, 33, 11, 42, 131091, 2, 196641, 3, 2, 196630, 6, 32, 262167, 7, 6, 4, 262165, 8, 32, 0, 262187, 8, 9, 1, 262172, 10, 6, 9, 393246, 11, 7, 6, 10, 10, 262176, 12, 3, 11, 262203, 12, 13, 3, 262165, 14, 32, 1, 262187, 14, 15, 0, 262167, 16, 6, 2, 262168, 17, 16, 2, 196638, 18, 17, 262176, 19, 2, 18, 262203, 19, 20, 2, 262176, 21, 2, 17, 262187, 8, 24, 3, 262172, 25, 16, 24, 262187, 6, 26, 3212836864, 327724, 16, 27, 26, 26, 262187, 6, 28, 1065353216, 327724, 16, 29, 28, 26, 327724, 16, 30, 26, 28, 393260, 25, 31, 27, 29, 30, 262176, 32, 1, 14, 262203, 32, 33, 1, 262176, 35, 7, 25, 262176, 37, 7, 16, 262187, 6, 41, 0, 262176, 45, 3, 7, 327734, 2, 4, 0, 3, 131320, 5, 262203, 35, 36, 7, 327745, 21, 22, 20, 15, 262205, 17, 23, 22, 262205, 14, 34, 33, 196670, 36, 31, 327745, 37, 38, 36, 34, 262205, 16, 39, 38, 327825, 16, 40, 23, 39, 327761, 6, 42, 40, 0, 327761, 6, 43, 40, 1, 458832, 7, 44, 42, 43, 41, 28, 327745, 45, 46, 13, 15, 196670, 46, 44, 65789, 65592])
+    });
+    const fragmentModule = this.device.createShaderModule({
+      code:
+      /* GLSL(
+       *         'fragment',
+       *         `#version 450
+       *           layout (set = 1, binding = 0) uniform fragmentUniformBuffer {
+       *             vec4 color;
+       *           };
+       *           layout(location = 0) out vec4 fragColor;
+       *           void main() {
+       *           }
+       *         `
+       *       )
+       */
+      new Uint32Array([119734787, 66304, 524295, 13, 0, 131089, 1, 393227, 1, 1280527431, 1685353262, 808793134, 0, 196622, 0, 1, 393231, 4, 4, 1852399981, 0, 12, 196624, 4, 7, 196611, 2, 450, 262149, 4, 1852399981, 0, 524293, 8, 1734439526, 1953391981, 1718185557, 1114468975, 1701209717, 114, 327686, 8, 0, 1869377379, 114, 196613, 10, 0, 327685, 12, 1734439526, 1869377347, 114, 327752, 8, 0, 35, 0, 196679, 8, 2, 262215, 10, 34, 1, 262215, 10, 33, 0, 262215, 12, 30, 0, 131091, 2, 196641, 3, 2, 196630, 6, 32, 262167, 7, 6, 4, 196638, 8, 7, 262176, 9, 2, 8, 262203, 9, 10, 2, 262176, 11, 3, 7, 262203, 11, 12, 3, 327734, 2, 4, 0, 3, 131320, 5, 65789, 65592])
+    });
+    const pipeline = this.device.createRenderPipeline({
+      vertexStage: {
+        module: vertexModule,
+        entryPoint: 'main'
+      },
+      fragmentStage: {
+        module: fragmentModule,
+        entryPoint: 'main'
+      },
+      layout: pipelineLayout,
+      primitiveTopology: 'triangle-list',
+      colorStates: [{
+        format: 'rgba8unorm'
+      }]
+    });
+    return pipeline;
+  }
+
+  beginRenderPass(commandEncoder) {
+    const attachmentTexture = this.device.createTexture({
+      format: 'rgba8unorm',
+      size: {
+        width: 16,
+        height: 16,
+        depth: 1
+      },
+      usage: GPUTextureUsage.OUTPUT_ATTACHMENT
+    });
+    return commandEncoder.beginRenderPass({
+      colorAttachments: [{
+        attachment: attachmentTexture.createView(),
+        loadValue: {
+          r: 1.0,
+          g: 0.0,
+          b: 0.0,
+          a: 1.0
+        }
+      }]
+    });
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('it is invalid to draw in a render pass with missing bind groups', async t => {
+  const {
+    setBindGroup1,
+    setBindGroup2,
+    success
+  } = t.params;
+  const uniformBuffer = t.getUniformBuffer();
+  const bindGroupLayout1 = t.device.createBindGroupLayout({
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.VERTEX,
+      type: 'uniform-buffer'
+    }]
+  });
+  const bindGroup1 = t.device.createBindGroup({
+    bindings: [{
+      binding: 0,
+      resource: {
+        buffer: uniformBuffer
+      }
+    }],
+    layout: bindGroupLayout1
+  });
+  const bindGroupLayout2 = t.device.createBindGroupLayout({
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.FRAGMENT,
+      type: 'uniform-buffer'
+    }]
+  });
+  const bindGroup2 = t.device.createBindGroup({
+    bindings: [{
+      binding: 0,
+      resource: {
+        buffer: uniformBuffer
+      }
+    }],
+    layout: bindGroupLayout2
+  });
+  const pipelineLayout = t.device.createPipelineLayout({
+    bindGroupLayouts: [bindGroupLayout1, bindGroupLayout2]
+  });
+  const pipeline = t.createRenderPipeline(pipelineLayout);
+  const commandEncoder = t.device.createCommandEncoder();
+  const renderPass = t.beginRenderPass(commandEncoder);
+  renderPass.setPipeline(pipeline);
+
+  if (setBindGroup1) {
+    renderPass.setBindGroup(0, bindGroup1);
+  }
+
+  if (setBindGroup2) {
+    renderPass.setBindGroup(1, bindGroup2);
+  }
+
+  renderPass.draw(3, 1, 0, 0);
+  renderPass.endPass();
+  await t.expectValidationError(() => {
+    commandEncoder.finish();
+  }, !success);
+}).params([{
+  setBindGroup1: true,
+  setBindGroup2: true,
+  success: true
+}, {
+  setBindGroup1: true,
+  setBindGroup2: false,
+  success: false
+}, {
+  setBindGroup1: false,
+  setBindGroup2: true,
+  success: false
+}, {
+  setBindGroup1: false,
+  setBindGroup2: false,
+  success: false
+}]);
+//# sourceMappingURL=render_pass.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/render_pass_descriptor.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/render_pass_descriptor.spec.js
new file mode 100644
index 0000000..5a171879
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/render_pass_descriptor.spec.js
@@ -0,0 +1,560 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+render pass descriptor validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+
+class F extends ValidationTest {
+  createTexture(options = {}) {
+    const {
+      format = 'rgba8unorm',
+      width = 16,
+      height = 16,
+      arrayLayerCount = 1,
+      mipLevelCount = 1,
+      sampleCount = 1,
+      usage = GPUTextureUsage.OUTPUT_ATTACHMENT
+    } = options;
+    return this.device.createTexture({
+      size: {
+        width,
+        height,
+        depth: 1
+      },
+      format,
+      arrayLayerCount,
+      mipLevelCount,
+      sampleCount,
+      usage
+    });
+  }
+
+  getColorAttachment(texture, textureViewDescriptor) {
+    const attachment = texture.createView(textureViewDescriptor);
+    return {
+      attachment,
+      loadValue: {
+        r: 1.0,
+        g: 0.0,
+        b: 0.0,
+        a: 1.0
+      }
+    };
+  }
+
+  getDepthStencilAttachment(texture, textureViewDescriptor) {
+    const attachment = texture.createView(textureViewDescriptor);
+    return {
+      attachment,
+      depthLoadValue: 1.0,
+      depthStoreOp: 'store',
+      stencilLoadValue: 0,
+      stencilStoreOp: 'store'
+    };
+  }
+
+  async tryRenderPass(success, descriptor) {
+    const commandEncoder = this.device.createCommandEncoder();
+    const renderPass = commandEncoder.beginRenderPass(descriptor);
+    renderPass.endPass();
+    await this.expectValidationError(() => {
+      commandEncoder.finish();
+    }, !success);
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('a render pass with only one color is ok', t => {
+  const colorTexture = t.createTexture({
+    format: 'rgba8unorm'
+  });
+  const descriptor = {
+    colorAttachments: [t.getColorAttachment(colorTexture)]
+  };
+  t.tryRenderPass(true, descriptor);
+});
+g.test('a render pass with only one depth attachment is ok', t => {
+  const depthStencilTexture = t.createTexture({
+    format: 'depth24plus-stencil8'
+  });
+  const descriptor = {
+    colorAttachments: [],
+    depthStencilAttachment: t.getDepthStencilAttachment(depthStencilTexture)
+  };
+  t.tryRenderPass(true, descriptor);
+});
+g.test('OOB color attachment indices are handled', async t => {
+  const {
+    colorAttachmentsCount,
+    success
+  } = t.params;
+  const colorAttachments = [];
+
+  for (let i = 0; i < colorAttachmentsCount; i++) {
+    const colorTexture = t.createTexture();
+    colorAttachments.push(t.getColorAttachment(colorTexture));
+  }
+
+  await t.tryRenderPass(success, {
+    colorAttachments
+  });
+}).params([{
+  colorAttachmentsCount: 4,
+  success: true
+}, // Control case
+{
+  colorAttachmentsCount: 5,
+  success: false
+} // Out of bounds
+]);
+g.test('attachments must have the same size', async t => {
+  const colorTexture1x1A = t.createTexture({
+    width: 1,
+    height: 1,
+    format: 'rgba8unorm'
+  });
+  const colorTexture1x1B = t.createTexture({
+    width: 1,
+    height: 1,
+    format: 'rgba8unorm'
+  });
+  const colorTexture2x2 = t.createTexture({
+    width: 2,
+    height: 2,
+    format: 'rgba8unorm'
+  });
+  const depthStencilTexture1x1 = t.createTexture({
+    width: 1,
+    height: 1,
+    format: 'depth24plus-stencil8'
+  });
+  const depthStencilTexture2x2 = t.createTexture({
+    width: 2,
+    height: 2,
+    format: 'depth24plus-stencil8'
+  });
+  {
+    // Control case: all the same size (1x1)
+    const descriptor = {
+      colorAttachments: [t.getColorAttachment(colorTexture1x1A), t.getColorAttachment(colorTexture1x1B)],
+      depthStencilAttachment: t.getDepthStencilAttachment(depthStencilTexture1x1)
+    };
+    t.tryRenderPass(true, descriptor);
+  }
+  {
+    // One of the color attachments has a different size
+    const descriptor = {
+      colorAttachments: [t.getColorAttachment(colorTexture1x1A), t.getColorAttachment(colorTexture2x2)]
+    };
+    await t.tryRenderPass(false, descriptor);
+  }
+  {
+    // The depth stencil attachment has a different size
+    const descriptor = {
+      colorAttachments: [t.getColorAttachment(colorTexture1x1A), t.getColorAttachment(colorTexture1x1B)],
+      depthStencilAttachment: t.getDepthStencilAttachment(depthStencilTexture2x2)
+    };
+    await t.tryRenderPass(false, descriptor);
+  }
+});
+g.test('attachments must match whether they are used for color or depth stencil', async t => {
+  const colorTexture = t.createTexture({
+    format: 'rgba8unorm'
+  });
+  const depthStencilTexture = t.createTexture({
+    format: 'depth24plus-stencil8'
+  });
+  {
+    // Using depth-stencil for color
+    const descriptor = {
+      colorAttachments: [t.getColorAttachment(depthStencilTexture)]
+    };
+    await t.tryRenderPass(false, descriptor);
+  }
+  {
+    // Using color for depth-stencil
+    const descriptor = {
+      colorAttachments: [],
+      depthStencilAttachment: t.getDepthStencilAttachment(colorTexture)
+    };
+    await t.tryRenderPass(false, descriptor);
+  }
+});
+g.test('check layer count for color or depth stencil', async t => {
+  const {
+    arrayLayerCount,
+    baseArrayLayer,
+    success
+  } = t.params;
+  const ARRAY_LAYER_COUNT = 10;
+  const MIP_LEVEL_COUNT = 1;
+  const COLOR_FORMAT = 'rgba8unorm';
+  const DEPTH_STENCIL_FORMAT = 'depth24plus-stencil8';
+  const colorTexture = t.createTexture({
+    format: COLOR_FORMAT,
+    width: 32,
+    height: 32,
+    mipLevelCount: MIP_LEVEL_COUNT,
+    arrayLayerCount: ARRAY_LAYER_COUNT
+  });
+  const depthStencilTexture = t.createTexture({
+    format: DEPTH_STENCIL_FORMAT,
+    width: 32,
+    height: 32,
+    mipLevelCount: MIP_LEVEL_COUNT,
+    arrayLayerCount: ARRAY_LAYER_COUNT
+  });
+  const baseTextureViewDescriptor = {
+    dimension: '2d-array',
+    baseArrayLayer,
+    arrayLayerCount,
+    baseMipLevel: 0,
+    mipLevelCount: MIP_LEVEL_COUNT
+  };
+  {
+    // Check 2D array texture view for color
+    const textureViewDescriptor = { ...baseTextureViewDescriptor,
+      format: COLOR_FORMAT
+    };
+    const descriptor = {
+      colorAttachments: [t.getColorAttachment(colorTexture, textureViewDescriptor)]
+    };
+    await t.tryRenderPass(success, descriptor);
+  }
+  {
+    // Check 2D array texture view for depth stencil
+    const textureViewDescriptor = { ...baseTextureViewDescriptor,
+      format: DEPTH_STENCIL_FORMAT
+    };
+    const descriptor = {
+      colorAttachments: [],
+      depthStencilAttachment: t.getDepthStencilAttachment(depthStencilTexture, textureViewDescriptor)
+    };
+    await t.tryRenderPass(success, descriptor);
+  }
+}).params([{
+  arrayLayerCount: 5,
+  baseArrayLayer: 0,
+  success: false
+}, // using 2D array texture view with arrayLayerCount > 1 is not allowed
+{
+  arrayLayerCount: 1,
+  baseArrayLayer: 0,
+  success: true
+}, // using 2D array texture view that covers the first layer of the texture is OK
+{
+  arrayLayerCount: 1,
+  baseArrayLayer: 9,
+  success: true
+} // using 2D array texture view that covers the last layer is OK for depth stencil
+]);
+g.test('check mip level count for color or depth stencil', async t => {
+  const {
+    mipLevelCount,
+    baseMipLevel,
+    success
+  } = t.params;
+  const ARRAY_LAYER_COUNT = 1;
+  const MIP_LEVEL_COUNT = 4;
+  const COLOR_FORMAT = 'rgba8unorm';
+  const DEPTH_STENCIL_FORMAT = 'depth24plus-stencil8';
+  const colorTexture = t.createTexture({
+    format: COLOR_FORMAT,
+    width: 32,
+    height: 32,
+    mipLevelCount: MIP_LEVEL_COUNT,
+    arrayLayerCount: ARRAY_LAYER_COUNT
+  });
+  const depthStencilTexture = t.createTexture({
+    format: DEPTH_STENCIL_FORMAT,
+    width: 32,
+    height: 32,
+    mipLevelCount: MIP_LEVEL_COUNT,
+    arrayLayerCount: ARRAY_LAYER_COUNT
+  });
+  const baseTextureViewDescriptor = {
+    dimension: '2d',
+    baseArrayLayer: 0,
+    arrayLayerCount: ARRAY_LAYER_COUNT,
+    baseMipLevel,
+    mipLevelCount
+  };
+  {
+    // Check 2D texture view for color
+    const textureViewDescriptor = { ...baseTextureViewDescriptor,
+      format: COLOR_FORMAT
+    };
+    const descriptor = {
+      colorAttachments: [t.getColorAttachment(colorTexture, textureViewDescriptor)]
+    };
+    await t.tryRenderPass(success, descriptor);
+  }
+  {
+    // Check 2D texture view for depth stencil
+    const textureViewDescriptor = { ...baseTextureViewDescriptor,
+      format: DEPTH_STENCIL_FORMAT
+    };
+    const descriptor = {
+      colorAttachments: [],
+      depthStencilAttachment: t.getDepthStencilAttachment(depthStencilTexture, textureViewDescriptor)
+    };
+    await t.tryRenderPass(success, descriptor);
+  }
+}).params([{
+  mipLevelCount: 2,
+  baseMipLevel: 0,
+  success: false
+}, // using 2D texture view with mipLevelCount > 1 is not allowed
+{
+  mipLevelCount: 1,
+  baseMipLevel: 0,
+  success: true
+}, // using 2D texture view that covers the first level of the texture is OK
+{
+  mipLevelCount: 1,
+  baseMipLevel: 3,
+  success: true
+} // using 2D texture view that covers the last level of the texture is OK
+]);
+g.test('it is invalid to set resolve target if color attachment is non multisampled', async t => {
+  const colorTexture = t.createTexture({
+    sampleCount: 1
+  });
+  const resolveTargetTexture = t.createTexture({
+    sampleCount: 1
+  });
+  const descriptor = {
+    colorAttachments: [{
+      attachment: colorTexture.createView(),
+      resolveTarget: resolveTargetTexture.createView(),
+      loadValue: {
+        r: 1.0,
+        g: 0.0,
+        b: 0.0,
+        a: 1.0
+      }
+    }]
+  };
+  await t.tryRenderPass(false, descriptor);
+});
+g.test('check the use of multisampled textures as color attachments', async t => {
+  const colorTexture = t.createTexture({
+    sampleCount: 1
+  });
+  const multisampledColorTexture = t.createTexture({
+    sampleCount: 4
+  });
+  {
+    // It is allowed to use a multisampled color attachment without setting resolve target
+    const descriptor = {
+      colorAttachments: [t.getColorAttachment(multisampledColorTexture)]
+    };
+    t.tryRenderPass(true, descriptor);
+  }
+  {
+    // It is not allowed to use multiple color attachments with different sample counts
+    const descriptor = {
+      colorAttachments: [t.getColorAttachment(colorTexture), t.getColorAttachment(multisampledColorTexture)]
+    };
+    await t.tryRenderPass(false, descriptor);
+  }
+});
+g.test('it is invalid to use a multisampled resolve target', async t => {
+  const multisampledColorTexture = t.createTexture({
+    sampleCount: 4
+  });
+  const multisampledResolveTargetTexture = t.createTexture({
+    sampleCount: 4
+  });
+  const colorAttachment = t.getColorAttachment(multisampledColorTexture);
+  colorAttachment.resolveTarget = multisampledResolveTargetTexture.createView();
+  const descriptor = {
+    colorAttachments: [colorAttachment]
+  };
+  await t.tryRenderPass(false, descriptor);
+});
+g.test('it is invalid to use a resolve target with array layer count greater than 1', async t => {
+  const multisampledColorTexture = t.createTexture({
+    sampleCount: 4
+  });
+  const resolveTargetTexture = t.createTexture({
+    arrayLayerCount: 2
+  });
+  const colorAttachment = t.getColorAttachment(multisampledColorTexture);
+  colorAttachment.resolveTarget = resolveTargetTexture.createView();
+  const descriptor = {
+    colorAttachments: [colorAttachment]
+  };
+  await t.tryRenderPass(false, descriptor);
+});
+g.test('it is invalid to use a resolve target with mipmap level count greater than 1', async t => {
+  const multisampledColorTexture = t.createTexture({
+    sampleCount: 4
+  });
+  const resolveTargetTexture = t.createTexture({
+    mipLevelCount: 2
+  });
+  const colorAttachment = t.getColorAttachment(multisampledColorTexture);
+  colorAttachment.resolveTarget = resolveTargetTexture.createView();
+  const descriptor = {
+    colorAttachments: [colorAttachment]
+  };
+  await t.tryRenderPass(false, descriptor);
+});
+g.test('it is invalid to use a resolve target whose usage is not output attachment', async t => {
+  const multisampledColorTexture = t.createTexture({
+    sampleCount: 4
+  });
+  const resolveTargetTexture = t.createTexture({
+    usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST
+  });
+  const colorAttachment = t.getColorAttachment(multisampledColorTexture);
+  colorAttachment.resolveTarget = resolveTargetTexture.createView();
+  const descriptor = {
+    colorAttachments: [colorAttachment]
+  };
+  await t.tryRenderPass(false, descriptor);
+});
+g.test('it is invalid to use a resolve target in error state', async t => {
+  const ARRAY_LAYER_COUNT = 1;
+  const multisampledColorTexture = t.createTexture({
+    sampleCount: 4
+  });
+  const resolveTargetTexture = t.createTexture({
+    arrayLayerCount: ARRAY_LAYER_COUNT
+  });
+  const colorAttachment = t.getColorAttachment(multisampledColorTexture);
+  await t.expectValidationError(() => {
+    colorAttachment.resolveTarget = resolveTargetTexture.createView({
+      dimension: '2d',
+      format: 'rgba8unorm',
+      baseArrayLayer: ARRAY_LAYER_COUNT + 1
+    });
+  });
+  const descriptor = {
+    colorAttachments: [colorAttachment]
+  };
+  await t.tryRenderPass(false, descriptor);
+});
+g.test('use of multisampled attachment and non multisampled resolve target is allowed', async t => {
+  const multisampledColorTexture = t.createTexture({
+    sampleCount: 4
+  });
+  const resolveTargetTexture = t.createTexture({
+    sampleCount: 1
+  });
+  const colorAttachment = t.getColorAttachment(multisampledColorTexture);
+  colorAttachment.resolveTarget = resolveTargetTexture.createView();
+  const descriptor = {
+    colorAttachments: [colorAttachment]
+  };
+  t.tryRenderPass(true, descriptor);
+});
+g.test('use a resolve target in a format different than the attachment is not allowed', async t => {
+  const multisampledColorTexture = t.createTexture({
+    sampleCount: 4
+  });
+  const resolveTargetTexture = t.createTexture({
+    format: 'bgra8unorm'
+  });
+  const colorAttachment = t.getColorAttachment(multisampledColorTexture);
+  colorAttachment.resolveTarget = resolveTargetTexture.createView();
+  const descriptor = {
+    colorAttachments: [colorAttachment]
+  };
+  await t.tryRenderPass(false, descriptor);
+});
+g.test('size of the resolve target must be the same as the color attachment', async t => {
+  const size = 16;
+  const multisampledColorTexture = t.createTexture({
+    width: size,
+    height: size,
+    sampleCount: 4
+  });
+  const resolveTargetTexture = t.createTexture({
+    width: size * 2,
+    height: size * 2,
+    mipLevelCount: 2
+  });
+  {
+    const resolveTargetTextureView = resolveTargetTexture.createView({
+      baseMipLevel: 0,
+      mipLevelCount: 1
+    });
+    const colorAttachment = t.getColorAttachment(multisampledColorTexture);
+    colorAttachment.resolveTarget = resolveTargetTextureView;
+    const descriptor = {
+      colorAttachments: [colorAttachment]
+    };
+    await t.tryRenderPass(false, descriptor);
+  }
+  {
+    const resolveTargetTextureView = resolveTargetTexture.createView({
+      baseMipLevel: 1
+    });
+    const colorAttachment = t.getColorAttachment(multisampledColorTexture);
+    colorAttachment.resolveTarget = resolveTargetTextureView;
+    const descriptor = {
+      colorAttachments: [colorAttachment]
+    };
+    t.tryRenderPass(true, descriptor);
+  }
+});
+g.test('check depth stencil attachment sample counts mismatch', async t => {
+  const multisampledDepthStencilTexture = t.createTexture({
+    sampleCount: 4,
+    format: 'depth24plus-stencil8'
+  });
+  {
+    // It is not allowed to use a depth stencil attachment whose sample count is different from the
+    // one of the color attachment
+    const depthStencilTexture = t.createTexture({
+      sampleCount: 1,
+      format: 'depth24plus-stencil8'
+    });
+    const multisampledColorTexture = t.createTexture({
+      sampleCount: 4
+    });
+    const descriptor = {
+      colorAttachments: [t.getColorAttachment(multisampledColorTexture)],
+      depthStencilAttachment: t.getDepthStencilAttachment(depthStencilTexture)
+    };
+    await t.tryRenderPass(false, descriptor);
+  }
+  {
+    const colorTexture = t.createTexture({
+      sampleCount: 1
+    });
+    const descriptor = {
+      colorAttachments: [t.getColorAttachment(colorTexture)],
+      depthStencilAttachment: t.getDepthStencilAttachment(multisampledDepthStencilTexture)
+    };
+    await t.tryRenderPass(false, descriptor);
+  }
+  {
+    // It is allowed to use a multisampled depth stencil attachment whose sample count is equal to
+    // the one of the color attachment.
+    const multisampledColorTexture = t.createTexture({
+      sampleCount: 4
+    });
+    const descriptor = {
+      colorAttachments: [t.getColorAttachment(multisampledColorTexture)],
+      depthStencilAttachment: t.getDepthStencilAttachment(multisampledDepthStencilTexture)
+    };
+    t.tryRenderPass(true, descriptor);
+  }
+  {
+    // It is allowed to use a multisampled depth stencil attachment with no color attachment
+    const descriptor = {
+      colorAttachments: [],
+      depthStencilAttachment: t.getDepthStencilAttachment(multisampledDepthStencilTexture)
+    };
+    t.tryRenderPass(true, descriptor);
+  }
+});
+//# sourceMappingURL=render_pass_descriptor.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setBindGroup.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setBindGroup.spec.js
new file mode 100644
index 0000000..c669ea0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setBindGroup.spec.js
@@ -0,0 +1,205 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+setBindGroup validation tests.
+`;
+import { TestGroup, pcombine, poptions } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+
+class F extends ValidationTest {
+  makeAttachmentTexture() {
+    return this.device.createTexture({
+      format: 'rgba8unorm',
+      size: {
+        width: 16,
+        height: 16,
+        depth: 1
+      },
+      usage: GPUTextureUsage.OUTPUT_ATTACHMENT
+    });
+  }
+
+  testComputePass(bindGroup, dynamicOffsets) {
+    const encoder = this.device.createCommandEncoder();
+    const computePass = encoder.beginComputePass();
+    computePass.setBindGroup(0, bindGroup, dynamicOffsets);
+    computePass.endPass();
+    encoder.finish();
+  }
+
+  testRenderPass(bindGroup, dynamicOffsets) {
+    const encoder = this.device.createCommandEncoder();
+    const renderPass = encoder.beginRenderPass({
+      colorAttachments: [{
+        attachment: this.makeAttachmentTexture().createView(),
+        loadValue: {
+          r: 1.0,
+          g: 0.0,
+          b: 0.0,
+          a: 1.0
+        }
+      }]
+    });
+    renderPass.setBindGroup(0, bindGroup, dynamicOffsets);
+    renderPass.endPass();
+    encoder.finish();
+  }
+
+  testRenderBundle(bindGroup, dynamicOffsets) {
+    const encoder = this.device.createRenderBundleEncoder({
+      colorFormats: ['rgba8unorm']
+    });
+    encoder.setBindGroup(0, bindGroup, dynamicOffsets);
+    encoder.finish();
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('dynamic offsets passed but not expected/compute pass', async t => {
+  const bindGroupLayout = t.device.createBindGroupLayout({
+    bindings: []
+  });
+  const bindGroup = t.device.createBindGroup({
+    layout: bindGroupLayout,
+    bindings: []
+  });
+  const {
+    type
+  } = t.params;
+  const dynamicOffsets = [0];
+  await t.expectValidationError(() => {
+    if (type === 'compute') {
+      const encoder = t.device.createCommandEncoder();
+      const computePass = encoder.beginComputePass();
+      computePass.setBindGroup(0, bindGroup, dynamicOffsets);
+      computePass.endPass();
+      encoder.finish();
+    } else if (type === 'renderpass') {
+      const encoder = t.device.createCommandEncoder();
+      const renderPass = encoder.beginRenderPass({
+        colorAttachments: [{
+          attachment: t.makeAttachmentTexture().createView(),
+          loadValue: {
+            r: 1.0,
+            g: 0.0,
+            b: 0.0,
+            a: 1.0
+          }
+        }]
+      });
+      renderPass.setBindGroup(0, bindGroup, dynamicOffsets);
+      renderPass.endPass();
+      encoder.finish();
+    } else if (type === 'renderbundle') {
+      const encoder = t.device.createRenderBundleEncoder({
+        colorFormats: ['rgba8unorm']
+      });
+      encoder.setBindGroup(0, bindGroup, dynamicOffsets);
+      encoder.finish();
+    } else {
+      t.fail();
+    }
+  });
+}).params(poptions('type', ['compute', 'renderpass', 'renderbundle']));
+g.test('dynamic offsets match expectations in pass encoder', async t => {
+  // Dynamic buffer offsets require offset to be divisible by 256
+  const MIN_DYNAMIC_BUFFER_OFFSET_ALIGNMENT = 256;
+  const BINDING_SIZE = 9;
+  const bindGroupLayout = t.device.createBindGroupLayout({
+    bindings: [{
+      binding: 0,
+      visibility: GPUShaderStage.COMPUTE | GPUShaderStage.FRAGMENT,
+      type: 'uniform-buffer',
+      hasDynamicOffset: true
+    }, {
+      binding: 1,
+      visibility: GPUShaderStage.COMPUTE | GPUShaderStage.FRAGMENT,
+      type: 'storage-buffer',
+      hasDynamicOffset: true
+    }]
+  });
+  const uniformBuffer = t.device.createBuffer({
+    size: 2 * MIN_DYNAMIC_BUFFER_OFFSET_ALIGNMENT + 8,
+    usage: GPUBufferUsage.UNIFORM
+  });
+  const storageBuffer = t.device.createBuffer({
+    size: 2 * MIN_DYNAMIC_BUFFER_OFFSET_ALIGNMENT + 8,
+    usage: GPUBufferUsage.STORAGE
+  });
+  const bindGroup = t.device.createBindGroup({
+    layout: bindGroupLayout,
+    bindings: [{
+      binding: 0,
+      resource: {
+        buffer: uniformBuffer,
+        size: BINDING_SIZE
+      }
+    }, {
+      binding: 1,
+      resource: {
+        buffer: storageBuffer,
+        size: BINDING_SIZE
+      }
+    }]
+  });
+  const {
+    type,
+    dynamicOffsets,
+    success
+  } = t.params;
+  await t.expectValidationError(() => {
+    if (type === 'compute') {
+      t.testComputePass(bindGroup, dynamicOffsets);
+    } else if (type === 'renderpass') {
+      t.testRenderPass(bindGroup, dynamicOffsets);
+    } else if (type === 'renderbundle') {
+      t.testRenderBundle(bindGroup, dynamicOffsets);
+    } else {
+      t.fail();
+    }
+
+    t.testComputePass(bindGroup, dynamicOffsets);
+  }, !success);
+}).params(pcombine([poptions('type', ['compute', 'renderpass', 'renderbundle']), [{
+  dynamicOffsets: [256, 0],
+  success: true
+}, // Dynamic offsets aligned
+{
+  dynamicOffsets: [1, 2],
+  success: false
+}, // Dynamic offsets not aligned
+// Wrong number of dynamic offsets
+{
+  dynamicOffsets: [256, 0, 0],
+  success: false
+}, {
+  dynamicOffsets: [256],
+  success: false
+}, {
+  dynamicOffsets: [],
+  success: false
+}, // Dynamic uniform buffer out of bounds because of binding size
+{
+  dynamicOffsets: [512, 0],
+  success: false
+}, {
+  dynamicOffsets: [1024, 0],
+  success: false
+}, {
+  dynamicOffsets: [Number.MAX_SAFE_INTEGER, 0],
+  success: false
+}, // Dynamic storage buffer out of bounds because of binding size
+{
+  dynamicOffsets: [0, 512],
+  success: false
+}, {
+  dynamicOffsets: [0, 1024],
+  success: false
+}, {
+  dynamicOffsets: [0, Number.MAX_SAFE_INTEGER],
+  success: false
+}]])); // TODO: test error bind group
+//# sourceMappingURL=setBindGroup.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setBlendColor.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setBlendColor.spec.js
new file mode 100644
index 0000000..a04bcd0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setBlendColor.spec.js
@@ -0,0 +1,66 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+setBlendColor validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js'; // TODO: Move beginRenderPass to a Fixture class.
+
+class F extends ValidationTest {
+  beginRenderPass(commandEncoder) {
+    const attachmentTexture = this.device.createTexture({
+      format: 'rgba8unorm',
+      size: {
+        width: 16,
+        height: 16,
+        depth: 1
+      },
+      usage: GPUTextureUsage.OUTPUT_ATTACHMENT
+    });
+    return commandEncoder.beginRenderPass({
+      colorAttachments: [{
+        attachment: attachmentTexture.createView(),
+        loadValue: {
+          r: 1.0,
+          g: 0.0,
+          b: 0.0,
+          a: 1.0
+        }
+      }]
+    });
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('basic use of setBlendColor', t => {
+  const commandEncoder = t.device.createCommandEncoder();
+  const renderPass = t.beginRenderPass(commandEncoder);
+  renderPass.setBlendColor({
+    r: 0,
+    g: 0,
+    b: 0,
+    a: 0
+  });
+  renderPass.endPass();
+  commandEncoder.finish();
+});
+g.test('setBlendColor allows any number value', t => {
+  const values = [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER];
+
+  for (const value of values) {
+    const commandEncoder = t.device.createCommandEncoder();
+    const renderPass = t.beginRenderPass(commandEncoder);
+    renderPass.setBlendColor({
+      r: value,
+      g: value,
+      b: value,
+      a: value
+    });
+    renderPass.endPass();
+    commandEncoder.finish();
+  }
+});
+//# sourceMappingURL=setBlendColor.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setScissorRect.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setScissorRect.spec.js
new file mode 100644
index 0000000..ee1588d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setScissorRect.spec.js
@@ -0,0 +1,91 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+setScissorRect validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+const TEXTURE_WIDTH = 16;
+const TEXTURE_HEIGHT = 16; // TODO: Move this fixture class to a common file.
+
+class F extends ValidationTest {
+  beginRenderPass(commandEncoder) {
+    const attachmentTexture = this.device.createTexture({
+      format: 'rgba8unorm',
+      size: {
+        width: TEXTURE_WIDTH,
+        height: TEXTURE_HEIGHT,
+        depth: 1
+      },
+      usage: GPUTextureUsage.OUTPUT_ATTACHMENT
+    });
+    return commandEncoder.beginRenderPass({
+      colorAttachments: [{
+        attachment: attachmentTexture.createView(),
+        loadValue: {
+          r: 1.0,
+          g: 0.0,
+          b: 0.0,
+          a: 1.0
+        }
+      }]
+    });
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('use of setScissorRect', async t => {
+  const {
+    x,
+    y,
+    width,
+    height,
+    success
+  } = t.params;
+  const commandEncoder = t.device.createCommandEncoder();
+  const renderPass = t.beginRenderPass(commandEncoder);
+  renderPass.setScissorRect(x, y, width, height);
+  renderPass.endPass();
+  await t.expectValidationError(() => {
+    commandEncoder.finish();
+  }, !success);
+}).params([{
+  x: 0,
+  y: 0,
+  width: 1,
+  height: 1,
+  success: true
+}, // Basic use
+{
+  x: 0,
+  y: 0,
+  width: 0,
+  height: 1,
+  success: false
+}, // Width of zero is not allowed
+{
+  x: 0,
+  y: 0,
+  width: 1,
+  height: 0,
+  success: false
+}, // Height of zero is not allowed
+{
+  x: 0,
+  y: 0,
+  width: 0,
+  height: 0,
+  success: false
+}, // Both width and height of zero are not allowed
+{
+  x: 0,
+  y: 0,
+  width: TEXTURE_WIDTH + 1,
+  height: TEXTURE_HEIGHT + 1,
+  success: true
+} // Scissor larger than the framebuffer is allowed
+]);
+//# sourceMappingURL=setScissorRect.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setStencilReference.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setStencilReference.spec.js
new file mode 100644
index 0000000..3db37a4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setStencilReference.spec.js
@@ -0,0 +1,48 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+setStencilReference validation tests.
+`;
+import { TestGroup, poptions } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js'; // TODO: Move this fixture class to a common file.
+
+class F extends ValidationTest {
+  beginRenderPass(commandEncoder) {
+    const attachmentTexture = this.device.createTexture({
+      format: 'rgba8unorm',
+      size: {
+        width: 16,
+        height: 16,
+        depth: 1
+      },
+      usage: GPUTextureUsage.OUTPUT_ATTACHMENT
+    });
+    return commandEncoder.beginRenderPass({
+      colorAttachments: [{
+        attachment: attachmentTexture.createView(),
+        loadValue: {
+          r: 1.0,
+          g: 0.0,
+          b: 0.0,
+          a: 1.0
+        }
+      }]
+    });
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('use of setStencilReference', t => {
+  const {
+    reference
+  } = t.params;
+  const commandEncoder = t.device.createCommandEncoder();
+  const renderPass = t.beginRenderPass(commandEncoder);
+  renderPass.setStencilReference(reference);
+  renderPass.endPass();
+  commandEncoder.finish();
+}).params(poptions('reference', [0, 0xffffffff]));
+//# sourceMappingURL=setStencilReference.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setVertexBuffer.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setVertexBuffer.spec.js
new file mode 100644
index 0000000..949dd91
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setVertexBuffer.spec.js
@@ -0,0 +1,182 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+setVertexBuffer validation tests.
+`;
+import { TestGroup, range } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+
+class F extends ValidationTest {
+  async init() {
+    await Promise.all([super.init(), this.initGLSL()]);
+  }
+
+  getVertexBuffer() {
+    return this.device.createBuffer({
+      size: 256,
+      usage: GPUBufferUsage.VERTEX
+    });
+  }
+
+  createRenderPipeline(bufferCount) {
+    const descriptor = {
+      vertexStage: this.getVertexStage(bufferCount),
+      fragmentStage: this.getFragmentStage(),
+      layout: this.getPipelineLayout(),
+      primitiveTopology: 'triangle-list',
+      colorStates: [{
+        format: 'rgba8unorm'
+      }],
+      vertexInput: {
+        vertexBuffers: [{
+          stride: 3 * 4,
+          attributeSet: range(bufferCount, i => ({
+            format: 'float3',
+            shaderLocation: i
+          }))
+        }]
+      }
+    };
+    return this.device.createRenderPipeline(descriptor);
+  }
+
+  getVertexStage(bufferCount) {
+    const code = `
+      #version 450
+      ${range(bufferCount, i => `\nlayout(location = ${i}) in vec3 a_position${i};`).join('')}
+      void main() {
+        gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
+       }
+    `;
+    return {
+      module: this.makeShaderModule('vertex', code),
+      entryPoint: 'main'
+    };
+  }
+
+  getFragmentStage() {
+    const code = `
+      #version 450
+      layout(location = 0) out vec4 fragColor;
+      void main() {
+        fragColor = vec4(0.0, 1.0, 0.0, 1.0);
+      }
+    `;
+    return {
+      module: this.makeShaderModule('fragment', code),
+      entryPoint: 'main'
+    };
+  }
+
+  getPipelineLayout() {
+    return this.device.createPipelineLayout({
+      bindGroupLayouts: []
+    });
+  }
+
+  beginRenderPass(commandEncoder) {
+    const attachmentTexture = this.device.createTexture({
+      format: 'rgba8unorm',
+      size: {
+        width: 16,
+        height: 16,
+        depth: 1
+      },
+      usage: GPUTextureUsage.OUTPUT_ATTACHMENT
+    });
+    return commandEncoder.beginRenderPass({
+      colorAttachments: [{
+        attachment: attachmentTexture.createView(),
+        loadValue: {
+          r: 1.0,
+          g: 0.0,
+          b: 0.0,
+          a: 1.0
+        }
+      }]
+    });
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('vertex buffers inherit from previous pipeline', async t => {
+  const pipeline1 = t.createRenderPipeline(1);
+  const pipeline2 = t.createRenderPipeline(2);
+  const vertexBuffer1 = t.getVertexBuffer();
+  const vertexBuffer2 = t.getVertexBuffer();
+  {
+    // Check failure when vertex buffer is not set
+    const commandEncoder = t.device.createCommandEncoder();
+    const renderPass = t.beginRenderPass(commandEncoder);
+    renderPass.setPipeline(pipeline1);
+    renderPass.draw(3, 1, 0, 0);
+    renderPass.endPass();
+    await t.expectValidationError(() => {
+      commandEncoder.finish();
+    });
+  }
+  {
+    // Check success when vertex buffer is inherited from previous pipeline
+    const commandEncoder = t.device.createCommandEncoder();
+    const renderPass = t.beginRenderPass(commandEncoder);
+    renderPass.setPipeline(pipeline2);
+    renderPass.setVertexBuffer(0, vertexBuffer1);
+    renderPass.setVertexBuffer(1, vertexBuffer2);
+    renderPass.draw(3, 1, 0, 0);
+    renderPass.setPipeline(pipeline1);
+    renderPass.draw(3, 1, 0, 0);
+    renderPass.endPass();
+    commandEncoder.finish();
+  }
+});
+g.test('vertex buffers do not inherit between render passes', async t => {
+  const pipeline1 = t.createRenderPipeline(1);
+  const pipeline2 = t.createRenderPipeline(2);
+  const vertexBuffer1 = t.getVertexBuffer();
+  const vertexBuffer2 = t.getVertexBuffer();
+  {
+    // Check success when vertex buffer is set for each render pass
+    const commandEncoder = t.device.createCommandEncoder();
+    {
+      const renderPass = t.beginRenderPass(commandEncoder);
+      renderPass.setPipeline(pipeline2);
+      renderPass.setVertexBuffer(0, vertexBuffer1);
+      renderPass.setVertexBuffer(1, vertexBuffer2);
+      renderPass.draw(3, 1, 0, 0);
+      renderPass.endPass();
+    }
+    {
+      const renderPass = t.beginRenderPass(commandEncoder);
+      renderPass.setPipeline(pipeline1);
+      renderPass.setVertexBuffer(0, vertexBuffer1);
+      renderPass.draw(3, 1, 0, 0);
+      renderPass.endPass();
+    }
+    commandEncoder.finish();
+  }
+  {
+    // Check failure because vertex buffer is not inherited in second subpass
+    const commandEncoder = t.device.createCommandEncoder();
+    {
+      const renderPass = t.beginRenderPass(commandEncoder);
+      renderPass.setPipeline(pipeline2);
+      renderPass.setVertexBuffer(0, vertexBuffer1);
+      renderPass.setVertexBuffer(1, vertexBuffer2);
+      renderPass.draw(3, 1, 0, 0);
+      renderPass.endPass();
+    }
+    {
+      const renderPass = t.beginRenderPass(commandEncoder);
+      renderPass.setPipeline(pipeline1);
+      renderPass.draw(3, 1, 0, 0);
+      renderPass.endPass();
+    }
+    await t.expectValidationError(() => {
+      commandEncoder.finish();
+    });
+  }
+});
+//# sourceMappingURL=setVertexBuffer.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setViewport.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setViewport.spec.js
new file mode 100644
index 0000000..56109308
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/setViewport.spec.js
@@ -0,0 +1,193 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+setViewport validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+const TEXTURE_WIDTH = 16;
+const TEXTURE_HEIGHT = 16; // TODO: Move this fixture class to a common file.
+
+class F extends ValidationTest {
+  beginRenderPass(commandEncoder) {
+    const attachmentTexture = this.device.createTexture({
+      format: 'rgba8unorm',
+      size: {
+        width: TEXTURE_WIDTH,
+        height: TEXTURE_HEIGHT,
+        depth: 1
+      },
+      usage: GPUTextureUsage.OUTPUT_ATTACHMENT
+    });
+    return commandEncoder.beginRenderPass({
+      colorAttachments: [{
+        attachment: attachmentTexture.createView(),
+        loadValue: {
+          r: 1.0,
+          g: 0.0,
+          b: 0.0,
+          a: 1.0
+        }
+      }]
+    });
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('use of setViewport', async t => {
+  const {
+    x,
+    y,
+    width,
+    height,
+    minDepth,
+    maxDepth,
+    success
+  } = t.params;
+  const commandEncoder = t.device.createCommandEncoder();
+  const renderPass = t.beginRenderPass(commandEncoder);
+  renderPass.setViewport(x, y, width, height, minDepth, maxDepth);
+  renderPass.endPass();
+  await t.expectValidationError(() => {
+    commandEncoder.finish();
+  }, !success);
+}).params([{
+  x: 0,
+  y: 0,
+  width: 1,
+  height: 1,
+  minDepth: 0,
+  maxDepth: 1,
+  success: true
+}, // Basic use
+{
+  x: 0,
+  y: 0,
+  width: 0,
+  height: 1,
+  minDepth: 0,
+  maxDepth: 1,
+  success: false
+}, // Width of zero is not allowed
+{
+  x: 0,
+  y: 0,
+  width: 1,
+  height: 0,
+  minDepth: 0,
+  maxDepth: 1,
+  success: false
+}, // Height of zero is not allowed
+{
+  x: 0,
+  y: 0,
+  width: 0,
+  height: 0,
+  minDepth: 0,
+  maxDepth: 1,
+  success: false
+}, // Both width and height of zero are not allowed
+{
+  x: -1,
+  y: 0,
+  width: 1,
+  height: 1,
+  minDepth: 0,
+  maxDepth: 1,
+  success: true
+}, // Negative x is allowed
+{
+  x: 0,
+  y: -1,
+  width: 1,
+  height: 1,
+  minDepth: 0,
+  maxDepth: 1,
+  success: true
+}, // Negative y is allowed
+{
+  x: 0,
+  y: 0,
+  width: -1,
+  height: 1,
+  minDepth: 0,
+  maxDepth: 1,
+  success: false
+}, // Negative width is not allowed
+{
+  x: 0,
+  y: 0,
+  width: 1,
+  height: -1,
+  minDepth: 0,
+  maxDepth: 1,
+  success: false
+}, // Negative height is not allowed
+{
+  x: 0,
+  y: 0,
+  width: 1,
+  height: 1,
+  minDepth: -1,
+  maxDepth: 1,
+  success: false
+}, // Negative minDepth is not allowed
+{
+  x: 0,
+  y: 0,
+  width: 1,
+  height: 1,
+  minDepth: 0,
+  maxDepth: -1,
+  success: false
+}, // Negative maxDepth is not allowed
+{
+  x: 0,
+  y: 0,
+  width: 1,
+  height: 1,
+  minDepth: 10,
+  maxDepth: 1,
+  success: false
+}, // minDepth greater than 1 is not allowed
+{
+  x: 0,
+  y: 0,
+  width: 1,
+  height: 1,
+  minDepth: 0,
+  maxDepth: 10,
+  success: false
+}, // maxDepth greater than 1 is not allowed
+{
+  x: 0,
+  y: 0,
+  width: 1,
+  height: 1,
+  minDepth: 0.5,
+  maxDepth: 0.5,
+  success: true
+}, // minDepth equal to maxDepth is allowed
+{
+  x: 0,
+  y: 0,
+  width: 1,
+  height: 1,
+  minDepth: 0.8,
+  maxDepth: 0.5,
+  success: true
+}, // minDepth greater than maxDepth is allowed
+{
+  x: 0,
+  y: 0,
+  width: TEXTURE_WIDTH + 1,
+  height: TEXTURE_HEIGHT + 1,
+  minDepth: 0,
+  maxDepth: 1,
+  success: true
+} // Viewport larger than the framebuffer is allowed
+]);
+//# sourceMappingURL=setViewport.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/validation_test.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/validation_test.js
new file mode 100644
index 0000000..35ad5d8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/validation_test.js
@@ -0,0 +1,39 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+import { GPUTest } from '../gpu_test.js';
+export class ValidationTest extends GPUTest {
+  async getErrorBuffer() {
+    this.device.pushErrorScope('validation');
+    const errorBuffer = this.device.createBuffer({
+      size: 1024,
+      usage: 0xffff // Invalid GPUBufferUsage
+
+    });
+    await this.device.popErrorScope();
+    return errorBuffer;
+  }
+
+  async expectValidationError(fn, shouldError = true) {
+    // If no error is expected, we let the scope surrounding the test catch it.
+    if (shouldError === false) {
+      fn();
+      return;
+    }
+
+    return this.asyncExpectation(async () => {
+      this.device.pushErrorScope('validation');
+      fn();
+      const gpuValidationError = await this.device.popErrorScope();
+
+      if (!gpuValidationError) {
+        this.fail('Validation error was expected.');
+      } else if (gpuValidationError instanceof GPUValidationError) {
+        this.debug(`Captured validation error - ${gpuValidationError.message}`);
+      }
+    });
+  }
+
+}
+//# sourceMappingURL=validation_test.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/vertex_input.spec.js b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/vertex_input.spec.js
new file mode 100644
index 0000000..432a04a4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webgpu/suites/cts/validation/vertex_input.spec.js
@@ -0,0 +1,623 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+vertexInput validation tests.
+`;
+import { TestGroup } from '../../../framework/index.js';
+import { ValidationTest } from './validation_test.js';
+const MAX_VERTEX_ATTRIBUTES = 16;
+const MAX_VERTEX_BUFFER_END = 2048;
+const MAX_VERTEX_BUFFER_STRIDE = 2048;
+const MAX_VERTEX_BUFFERS = 16;
+const VERTEX_SHADER_CODE_WITH_NO_INPUT = `
+  #version 450
+  void main() {
+    gl_Position = vec4(0.0);
+  }
+`;
+
+function clone(descriptor) {
+  return JSON.parse(JSON.stringify(descriptor));
+}
+
+class F extends ValidationTest {
+  async init() {
+    await Promise.all([super.init(), this.initGLSL()]);
+  }
+
+  getDescriptor(vertexInput, vertexShaderCode) {
+    const descriptor = {
+      vertexStage: this.getVertexStage(vertexShaderCode),
+      fragmentStage: this.getFragmentStage(),
+      layout: this.getPipelineLayout(),
+      primitiveTopology: 'triangle-list',
+      colorStates: [{
+        format: 'rgba8unorm'
+      }],
+      vertexInput
+    };
+    return descriptor;
+  }
+
+  getVertexStage(code) {
+    return {
+      module: this.makeShaderModule('vertex', code),
+      entryPoint: 'main'
+    };
+  }
+
+  getFragmentStage() {
+    const code = `
+      #version 450
+      layout(location = 0) out vec4 fragColor;
+      void main() {
+        fragColor = vec4(0.0, 1.0, 0.0, 1.0);
+      }
+    `;
+    return {
+      module: this.makeShaderModule('fragment', code),
+      entryPoint: 'main'
+    };
+  }
+
+  getPipelineLayout() {
+    return this.device.createPipelineLayout({
+      bindGroupLayouts: []
+    });
+  }
+
+}
+
+export const g = new TestGroup(F);
+g.test('an empty vertex input is valid', t => {
+  const vertexInput = {};
+  const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+  t.device.createRenderPipeline(descriptor);
+});
+g.test('a null buffer is valid', t => {
+  {
+    // One null buffer is OK
+    const vertexInput = {
+      vertexBuffers: [{
+        stride: 0,
+        attributeSet: []
+      }]
+    };
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    //  One null buffer followed by a buffer is OK
+    const vertexInput = {
+      vertexBuffers: [{
+        stride: 0,
+        attributeSet: []
+      }, {
+        stride: 0,
+        attributeSet: [{
+          shaderLocation: 0,
+          format: 'float'
+        }]
+      }]
+    };
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    //  One null buffer sitting between buffers is OK
+    const vertexInput = {
+      vertexBuffers: [{
+        stride: 0,
+        attributeSet: [{
+          shaderLocation: 0,
+          format: 'float'
+        }]
+      }, {
+        stride: 0,
+        attributeSet: []
+      }, {
+        stride: 0,
+        attributeSet: [{
+          shaderLocation: 1,
+          format: 'float'
+        }]
+      }]
+    };
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+});
+g.test('pipeline vertex buffers are backed by attributes in vertex input', async t => {
+  const vertexInput = {
+    vertexBuffers: [{
+      stride: 2 * Float32Array.BYTES_PER_ELEMENT,
+      attributeSet: [{
+        shaderLocation: 0,
+        format: 'float'
+      }, {
+        shaderLocation: 1,
+        format: 'float'
+      }]
+    }]
+  };
+  {
+    // Control case: pipeline with one input per attribute
+    const code = `
+      #version 450
+      layout(location = 0) in vec4 a;
+      layout(location = 1) in vec4 b;
+      void main() {
+          gl_Position = vec4(0.0);
+      }
+    `;
+    const descriptor = t.getDescriptor(vertexInput, code);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Check it is valid for the pipeline to use a subset of the VertexInput
+    const code = `
+      #version 450
+      layout(location = 0) in vec4 a;
+      void main() {
+          gl_Position = vec4(0.0);
+      }
+    `;
+    const descriptor = t.getDescriptor(vertexInput, code);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Check for an error when the pipeline uses an attribute not in the vertex input
+    const code = `
+      #version 450
+      layout(location = 2) in vec4 a;
+      void main() {
+          gl_Position = vec4(0.0);
+      }
+    `;
+    const descriptor = t.getDescriptor(vertexInput, code);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('a stride of 0 is valid', t => {
+  const vertexInput = {
+    vertexBuffers: [{
+      stride: 0,
+      attributeSet: [{
+        shaderLocation: 0,
+        format: 'float'
+      }]
+    }]
+  };
+  {
+    // Works ok without attributes
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Works ok with attributes at a large-ish offset
+    vertexInput.vertexBuffers[0].attributeSet[0].offset = 128;
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+});
+g.test('offset should be within vertex buffer stride if stride is not zero', async t => {
+  const vertexInput = {
+    vertexBuffers: [{
+      stride: 2 * Float32Array.BYTES_PER_ELEMENT,
+      attributeSet: [{
+        shaderLocation: 0,
+        format: 'float'
+      }, {
+        offset: Float32Array.BYTES_PER_ELEMENT,
+        shaderLocation: 1,
+        format: 'float'
+      }]
+    }]
+  };
+  {
+    // Control case, setting correct stride and offset
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Test vertex attribute offset exceed vertex buffer stride range
+    const badVertexInput = clone(vertexInput);
+    badVertexInput.vertexBuffers[0].attributeSet[1].format = 'float2';
+    const descriptor = t.getDescriptor(badVertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+  {
+    // Test vertex attribute offset exceed vertex buffer stride range
+    const badVertexInput = clone(vertexInput);
+    badVertexInput.vertexBuffers[0].stride = Float32Array.BYTES_PER_ELEMENT;
+    const descriptor = t.getDescriptor(badVertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+  {
+    // It's OK if stride is zero
+    const goodVertexInput = clone(vertexInput);
+    goodVertexInput.vertexBuffers[0].stride = 0;
+    const descriptor = t.getDescriptor(goodVertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+});
+g.test('check two attributes overlapping', async t => {
+  const vertexInput = {
+    vertexBuffers: [{
+      stride: 2 * Float32Array.BYTES_PER_ELEMENT,
+      attributeSet: [{
+        shaderLocation: 0,
+        format: 'float'
+      }, {
+        offset: Float32Array.BYTES_PER_ELEMENT,
+        shaderLocation: 1,
+        format: 'float'
+      }]
+    }]
+  };
+  {
+    // Control case, setting correct stride and offset
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Test two attributes overlapping
+    const badVertexInput = clone(vertexInput);
+    badVertexInput.vertexBuffers[0].attributeSet[0].format = 'int2';
+    const descriptor = t.getDescriptor(badVertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('check out of bounds condition on total number of vertex buffers', async t => {
+  const vertexBuffers = [];
+
+  for (let i = 0; i < MAX_VERTEX_BUFFERS; i++) {
+    vertexBuffers.push({
+      stride: 0,
+      attributeSet: [{
+        shaderLocation: i,
+        format: 'float'
+      }]
+    });
+  }
+
+  {
+    // Control case, setting max vertex buffer number
+    const vertexInput = {
+      vertexBuffers
+    };
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Test vertex buffer number exceed the limit
+    const vertexInput = {
+      vertexBuffers: [...vertexBuffers, {
+        stride: 0,
+        attributeSet: [{
+          shaderLocation: MAX_VERTEX_BUFFERS,
+          format: 'float'
+        }]
+      }]
+    };
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('check out of bounds on number of vertex attributes on a single vertex buffer', async t => {
+  const vertexAttributes = [];
+
+  for (let i = 0; i < MAX_VERTEX_ATTRIBUTES; i++) {
+    vertexAttributes.push({
+      shaderLocation: i,
+      format: 'float'
+    });
+  }
+
+  {
+    // Control case, setting max vertex buffer number
+    const vertexInput = {
+      vertexBuffers: [{
+        stride: 0,
+        attributeSet: vertexAttributes
+      }]
+    };
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Test vertex attribute number exceed the limit
+    const vertexInput = {
+      vertexBuffers: [{
+        stride: 0,
+        attributeSet: [...vertexAttributes, {
+          shaderLocation: MAX_VERTEX_ATTRIBUTES,
+          format: 'float'
+        }]
+      }]
+    };
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('check out of bounds on number of vertex attributes across vertex buffers', async t => {
+  const vertexBuffers = [];
+
+  for (let i = 0; i < MAX_VERTEX_ATTRIBUTES; i++) {
+    vertexBuffers.push({
+      stride: 0,
+      attributeSet: [{
+        shaderLocation: i,
+        format: 'float'
+      }]
+    });
+  }
+
+  {
+    // Control case, setting max vertex buffer number
+    const vertexInput = {
+      vertexBuffers
+    };
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Test vertex attribute number exceed the limit
+    vertexBuffers[MAX_VERTEX_ATTRIBUTES - 1].attributeSet.push({
+      shaderLocation: MAX_VERTEX_ATTRIBUTES,
+      format: 'float'
+    });
+    const vertexInput = {
+      vertexBuffers
+    };
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('check out of bounds condition on input strides', async t => {
+  const vertexInput = {
+    vertexBuffers: [{
+      stride: MAX_VERTEX_BUFFER_STRIDE,
+      attributeSet: []
+    }]
+  };
+  {
+    // Control case, setting max input stride
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Test input stride OOB
+    vertexInput.vertexBuffers[0].stride = MAX_VERTEX_BUFFER_STRIDE + 4;
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('check multiple of 4 bytes constraint on input stride', async t => {
+  const vertexInput = {
+    vertexBuffers: [{
+      stride: 4,
+      attributeSet: [{
+        shaderLocation: 0,
+        format: 'uchar2'
+      }]
+    }]
+  };
+  {
+    // Control case, setting input stride 4 bytes
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Test input stride not multiple of 4 bytes
+    vertexInput.vertexBuffers[0].stride = 2;
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('identical duplicate attributes are invalid', async t => {
+  const vertexInput = {
+    vertexBuffers: [{
+      stride: 0,
+      attributeSet: [{
+        shaderLocation: 0,
+        format: 'float'
+      }]
+    }]
+  };
+  {
+    // Control case, setting attribute 0
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Oh no, attribute 0 is set twice
+    vertexInput.vertexBuffers[0].attributeSet.push({
+      shaderLocation: 0,
+      format: 'float'
+    });
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('we cannot set same shader location', async t => {
+  {
+    const vertexInput = {
+      vertexBuffers: [{
+        stride: 0,
+        attributeSet: [{
+          shaderLocation: 0,
+          format: 'float'
+        }, {
+          offset: Float32Array.BYTES_PER_ELEMENT,
+          shaderLocation: 1,
+          format: 'float'
+        }]
+      }]
+    };
+    {
+      // Control case, setting different shader locations in two attributes
+      const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+      t.device.createRenderPipeline(descriptor);
+    }
+    {
+      // Test same shader location in two attributes in the same buffer
+      vertexInput.vertexBuffers[0].attributeSet[1].shaderLocation = 0;
+      const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+      await t.expectValidationError(() => {
+        t.device.createRenderPipeline(descriptor);
+      });
+    }
+  }
+  {
+    const vertexInput = {
+      vertexBuffers: [{
+        stride: 0,
+        attributeSet: [{
+          shaderLocation: 0,
+          format: 'float'
+        }]
+      }, {
+        stride: 0,
+        attributeSet: [{
+          shaderLocation: 0,
+          format: 'float'
+        }]
+      }]
+    }; // Test same shader location in two attributes in different buffers
+
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('check out of bounds condition on attribute shader location', async t => {
+  const vertexInput = {
+    vertexBuffers: [{
+      stride: 0,
+      attributeSet: [{
+        shaderLocation: MAX_VERTEX_ATTRIBUTES - 1,
+        format: 'float'
+      }]
+    }]
+  };
+  {
+    // Control case, setting last attribute shader location
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Test attribute location OOB
+    vertexInput.vertexBuffers[0].attributeSet[0].shaderLocation = MAX_VERTEX_ATTRIBUTES;
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('check attribute offset out of bounds', async t => {
+  const vertexInput = {
+    vertexBuffers: [{
+      stride: 0,
+      attributeSet: [{
+        offset: MAX_VERTEX_BUFFER_END - 2 * Float32Array.BYTES_PER_ELEMENT,
+        shaderLocation: 0,
+        format: 'float2'
+      }]
+    }]
+  };
+  {
+    // Control case, setting max attribute offset to MAX_VERTEX_BUFFER_END - 8
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Control case, setting attribute offset to 8
+    vertexInput.vertexBuffers[0].attributeSet[0].offset = 8;
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Test attribute offset out of bounds
+    vertexInput.vertexBuffers[0].attributeSet[0].offset = MAX_VERTEX_BUFFER_END - 4;
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('check multiple of 4 bytes constraint on offset', async t => {
+  const vertexInput = {
+    vertexBuffers: [{
+      stride: 0,
+      attributeSet: [{
+        offset: Float32Array.BYTES_PER_ELEMENT,
+        shaderLocation: 0,
+        format: 'float'
+      }]
+    }]
+  };
+  {
+    // Control case, setting offset 4 bytes
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    t.device.createRenderPipeline(descriptor);
+  }
+  {
+    // Test offset of 2 bytes with uchar2 format
+    vertexInput.vertexBuffers[0].attributeSet[0].offset = 2;
+    vertexInput.vertexBuffers[0].attributeSet[0].format = 'uchar2';
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+  {
+    // Test offset of 2 bytes with float format
+    vertexInput.vertexBuffers[0].attributeSet[0].offset = 2;
+    vertexInput.vertexBuffers[0].attributeSet[0].format = 'float';
+    const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+    await t.expectValidationError(() => {
+      t.device.createRenderPipeline(descriptor);
+    });
+  }
+});
+g.test('check attribute offset overflow', async t => {
+  const vertexInput = {
+    vertexBuffers: [{
+      stride: 0,
+      attributeSet: [{
+        offset: Number.MAX_SAFE_INTEGER,
+        shaderLocation: 0,
+        format: 'float'
+      }]
+    }]
+  };
+  const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
+  await t.expectValidationError(() => {
+    t.device.createRenderPipeline(descriptor);
+  });
+});
+//# sourceMappingURL=vertex_input.spec.js.map
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset.https.html b/third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset_immersive.https.html
similarity index 86%
copy from third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset.https.html
copy to third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset_immersive.https.html
index 3a93116..2da3788 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset.https.html
+++ b/third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset_immersive.https.html
@@ -8,8 +8,6 @@
 <script>
 let immersiveTestName = "XRSession resetpose from a device properly fires off " +
   "the right events for immersive sessions";
-let nonImmersiveTestName = "XRSession resetpose from a device properly fires off " +
-  "the right events for non-immersive sessions";
 
 let watcherDone = new Event("watcherdone");
 
@@ -49,9 +47,5 @@
 
 xr_session_promise_test(
   immersiveTestName, testFunction, fakeDeviceInitParams, 'immersive-vr');
-xr_session_promise_test(
-  nonImmersiveTestName, testFunction, fakeDeviceInitParams, 'inline', {
-    requiredFeatures: ['local'],
-  });
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset.https.html b/third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset_inline.https.html
similarity index 88%
rename from third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset.https.html
rename to third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset_inline.https.html
index 3a93116..b3abcc8 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset.https.html
+++ b/third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset_inline.https.html
@@ -6,8 +6,6 @@
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let immersiveTestName = "XRSession resetpose from a device properly fires off " +
-  "the right events for immersive sessions";
 let nonImmersiveTestName = "XRSession resetpose from a device properly fires off " +
   "the right events for non-immersive sessions";
 
@@ -48,8 +46,6 @@
 };
 
 xr_session_promise_test(
-  immersiveTestName, testFunction, fakeDeviceInitParams, 'immersive-vr');
-xr_session_promise_test(
   nonImmersiveTestName, testFunction, fakeDeviceInitParams, 'inline', {
     requiredFeatures: ['local'],
   });
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-zoom150-bottom-edge-no-nan.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-zoom150-bottom-edge-no-nan.html
new file mode 100644
index 0000000..a75a5e81
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-zoom150-bottom-edge-no-nan.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../../../../resources/testharness.js'></script>
+<script src='../../../../resources/testharnessreport.js'></script>
+<script src='../../../forms/resources/picker-common.js'></script>
+</head>
+<body style='zoom: 1.5;'>
+<input type='color' id='color' value='#80d9ff'>
+<script>
+'use strict';
+
+let t = async_test('Color picker: Color values at the bottom edge of the color picker at zoom 150% should be valid numbers.');
+t.step(() => {
+  let colorControl = document.getElementById('color');
+  openPicker(colorControl, t.step_func_done(() => {
+    popupWindow.focus();
+    const popupDocument = popupWindow.document;
+    const hexValueContainer = popupDocument.getElementById('hexValueContainer');
+    assert_equals(hexValueContainer.value, '#80d9ff');
+    eventSender.keyDown('Tab');
+    const colorWellSelectionRing = popupDocument.querySelector('color-well > color-selection-ring');
+    assert_equals(popupDocument.activeElement, colorWellSelectionRing);
+    for (let i = 0; i < 10; i++) {
+      eventSender.keyDown('ArrowDown', ['ctrlKey']);
+    }
+    assert_equals(hexValueContainer.value, '#000000', 'Selected color value should be \'#000000\'');
+  }), t.step_func_done(() => {
+    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
+  }));
+});
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png b/third_party/blink/web_tests/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png
new file mode 100644
index 0000000..2c2fad6
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/meter/meter-extreme-values.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/meter/meter-extreme-values.html
new file mode 100644
index 0000000..a1b79a3
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/meter/meter-extreme-values.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meter min="0" max="100" low="30" high="60" optimum="100" value="100" style="zoom:300%"></meter><br><br>
+<meter min="0" max="100" low="30" high="60" optimum="100" value="99" style="zoom:300%"></meter><br><br>
+<meter min="0" max="100" low="30" high="60" optimum="100" value="98" style="zoom:300%"></meter><br><br>
+<meter min="0" max="100" low="30" high="60" optimum="100" value="50" style="zoom:300%"></meter><br><br>
+<meter min="0" max="100" low="30" high="60" optimum="100" value="2" style="zoom:300%"></meter><br><br>
+<meter min="0" max="100" low="30" high="60" optimum="100" value="1" style="zoom:300%"></meter><br><br>
+<meter min="0" max="100" low="30" high="60" optimum="100" value="0" style="zoom:300%"></meter><br><br>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/mediastream/MediaDevices-enumerateDevices-iframe.html b/third_party/blink/web_tests/fast/mediastream/MediaDevices-enumerateDevices-iframe.html
index 78e318d..308dc9e 100644
--- a/third_party/blink/web_tests/fast/mediastream/MediaDevices-enumerateDevices-iframe.html
+++ b/third_party/blink/web_tests/fast/mediastream/MediaDevices-enumerateDevices-iframe.html
@@ -25,8 +25,7 @@
   navigator.mediaDevices.enumerateDevices().then(main_devices => {
     assert_equals(main_devices.length, iframe_devices.length);
     for (var i = 0; i < main_devices.length; i++) {
-      assert_equals(main_devices[i].deviceId, iframe_devices[i].deviceId);
-      assert_equals(main_devices[i].groupId, iframe_devices[i].groupId);
+      assert_not_equals(main_devices[i].groupId, iframe_devices[i].groupId);
       assert_equals(main_devices[i].kind, iframe_devices[i].kind);
       assert_equals(main_devices[i].label, iframe_devices[i].label);
     }
diff --git a/third_party/blink/web_tests/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html b/third_party/blink/web_tests/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html
index 6f79c8e5..b2c5bfc9 100644
--- a/third_party/blink/web_tests/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html
+++ b/third_party/blink/web_tests/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html
@@ -81,7 +81,7 @@
     // test will ensure that track autoscroll happens successfully and that the
     // autoscroll aborts when thumb reaches the pointer.
     const trackscroll_x = scrollerRect.right - BUTTON_WIDTH / 2;
-    const trackscroll_y = scrollerRect.top + 45;
+    const trackscroll_y = scrollerRect.bottom - SCROLL_CORNER - BUTTON_WIDTH;
     await mousePressOn(trackscroll_x, trackscroll_y, PRESS_DURATION);
     var err = `Autoscroll down failed (scroller.scrollTop = ${scroller.scrollTop})`;
 
diff --git a/third_party/blink/web_tests/fast/scrolling/scrollbars/mouse-scrolling-on-div-scrollbar.html b/third_party/blink/web_tests/fast/scrolling/scrollbars/mouse-scrolling-on-div-scrollbar.html
index 0f0c2ed..67451aade 100644
--- a/third_party/blink/web_tests/fast/scrolling/scrollbars/mouse-scrolling-on-div-scrollbar.html
+++ b/third_party/blink/web_tests/fast/scrolling/scrollbars/mouse-scrolling-on-div-scrollbar.html
@@ -61,6 +61,11 @@
 if (window.internals)
     internals.settings.setScrollAnimatorEnabled(false);
 
+// TODO(crbug.com/953847): Support other platforms or use calculated expectations.
+let platform = navigator.userAgent.includes("Linux") ? "linux" :
+               navigator.userAgent.includes("Windows") ? "win" :
+               (() => { throw "Platform unsupported. See crbug.com/953847"; })();
+
 window.onload = () => {
   const standardDivFast = document.getElementById("standard");
   const standardRectFast = standardDivFast.getBoundingClientRect();
@@ -134,27 +139,29 @@
 
     // Testing forward scroll on vertical scrollbar.
     let x = standardRectFast.right - BUTTON_WIDTH / 2;
-    let y = standardRectFast.top + 50;
+    let y = standardRectFast.bottom - SCROLL_CORNER - BUTTON_WIDTH - 15;
     await mouseClickOn(x, y, /*left_click*/0, /*modifier*/ "Shift");
-    assert_equals(standardDivFast.scrollTop, 617, "Shift + click forward didn't scroll.");
+    let forward_offset = {linux: 915, win: 794}[platform];
+    assert_equals(standardDivFast.scrollTop, forward_offset, "Shift + click forward didn't scroll.");
 
     // Testing backward scroll on vertical scrollbar.
     x = standardRectFast.right - BUTTON_WIDTH / 2;
-    y = standardRectFast.top + 30;
+    y = standardRectFast.top + BUTTON_WIDTH + 15;
+    let backward_offset = {linux: 0, win: 144}[platform];
     await mouseClickOn(x, y, /*left_click*/0, /*modifier*/ "Shift");
-    assert_equals(standardDivFast.scrollTop, 160, "Shift + click backward didn't scroll.");
+    assert_equals(standardDivFast.scrollTop, backward_offset, "Shift + click backward didn't scroll.");
 
     // Testing forward scroll on horizontal scrollbar.
-    x = standardRectFast.left + 50;
+    x = standardRectFast.right - SCROLL_CORNER - BUTTON_WIDTH - 15;
     y = standardRectFast.bottom - BUTTON_WIDTH / 2;
     await mouseClickOn(x, y, /*left_click*/0, /*modifier*/ "Shift");
-    assert_equals(standardDivFast.scrollLeft, 617, "Shift + click forward didn't scroll.");
+    assert_equals(standardDivFast.scrollLeft, forward_offset, "Shift + click forward didn't scroll.");
 
     // Testing backward scroll on horizontal scrollbar.
-    x = standardRectFast.left + 30;
+    x = standardRectFast.left + BUTTON_WIDTH + 15;
     y = standardRectFast.bottom - BUTTON_WIDTH / 2;
     await mouseClickOn(x, y, /*left_click*/0, /*modifier*/ "Shift");
-    assert_equals(standardDivFast.scrollLeft, 160, "Shift + click backward didn't scroll.");
+    assert_equals(standardDivFast.scrollLeft, backward_offset, "Shift + click backward didn't scroll.");
   }, "Test shift + click on non-custom composited scrollbars.");
 
   promise_test (async () => {
@@ -220,9 +227,9 @@
     let x = standardRectFast.right - TRACK_WIDTH / 2;
     let y = standardRectFast.top + BUTTON_WIDTH + 5;
     let asc_increments = [15, 10, 7, 6, 2];
-    let asc_offsets = [343, 571, 732, 869, 915];
+    let asc_offsets = {linux: [549, 915, 915, 915, 915], win: [361, 601, 770, 915, 915]}[platform];
     let desc_increments = [3, 2, 5, 9, 21];
-    let desc_offsets = [846, 800, 686, 480, 0];
+    let desc_offsets = {linux: [915, 915, 915, 768, 0], win: [890, 842, 722, 505, 0]}[platform];
 
     await mouseMoveTo(x, y);
     await mouseDownAt(x, y);
@@ -231,14 +238,17 @@
     for (var i = 0; i < 5; i++){
       y += asc_increments[i];
       await mouseMoveTo(x, y);
-      assert_equals(standardDivFast.scrollTop, asc_offsets[i], "Vertical thumb drag downwards did not scroll as expected at "+asc_increments[i]+" - ");
+      // TODO(crbug.com/1009892): Sometimes there is 1px difference in threaded scrollbar scrolling mode.
+      // Change assert_approx_equals(..., 1, ...) to assert_equals(...) when the bug is fixed.
+      assert_approx_equals(standardDivFast.scrollTop, asc_offsets[i], 1, "Vertical thumb drag downwards did not scroll as expected at "+asc_increments[i]+" - ");
     };
 
     // Scroll up
     for (var i = 0; i < 5; i++){
       y -= desc_increments[i];
       await mouseMoveTo(x, y);
-      assert_equals(standardDivFast.scrollTop, desc_offsets[i], "Vertical thumb drag upwards did not scroll as expected at "+desc_increments[i]+" - ");
+      // TODO(crbug.com/1009892): Ditto.
+      assert_approx_equals(standardDivFast.scrollTop, desc_offsets[i], 1, "Vertical thumb drag upwards did not scroll as expected at "+desc_increments[i]+" - ");
     };
 
     await mouseUpAt(x, y);
@@ -255,14 +265,16 @@
     for (var i = 0; i < 5; i++){
       x += asc_increments[i];
       await mouseMoveTo(x, y);
-      assert_equals(standardDivFast.scrollLeft, asc_offsets[i], "Horizontal thumb drag to the right did not scroll as expected at "+asc_increments[i]+" - ");
+      // TODO(crbug.com/1009892): Ditto.
+      assert_approx_equals(standardDivFast.scrollLeft, asc_offsets[i], 1, "Horizontal thumb drag to the right did not scroll as expected at "+asc_increments[i]+" - ");
     };
 
     // Scroll left
     for (var i = 0; i < 5; i++){
       x -= desc_increments[i];
       await mouseMoveTo(x, y);
-      assert_equals(standardDivFast.scrollLeft, desc_offsets[i], "Horizontal thumb drag to the left did not scroll as expected at "+desc_increments[i]+" - ");
+      // TODO(crbug.com/1009892): Ditto.
+      assert_approx_equals(standardDivFast.scrollLeft, desc_offsets[i], 1, "Horizontal thumb drag to the left did not scroll as expected at "+desc_increments[i]+" - ");
     };
 
     await mouseUpAt(x, y);
diff --git a/third_party/blink/web_tests/fast/scrolling/scrollbars/scrollbar-thumb-snapping.html b/third_party/blink/web_tests/fast/scrolling/scrollbars/scrollbar-thumb-snapping.html
index 4618a40a..f2698ae 100644
--- a/third_party/blink/web_tests/fast/scrolling/scrollbars/scrollbar-thumb-snapping.html
+++ b/third_party/blink/web_tests/fast/scrolling/scrollbars/scrollbar-thumb-snapping.html
@@ -34,7 +34,10 @@
   var testThumbSnapBack = async_test('Tests that the thumb snaps back.');
   function pointerActionsCallback() {
     const scroller = document.getElementById("standard");
-    assert_true(scroll_threshold_crossed && scroller.scrollTop == 20);
+    testThumbSnapBack.step(() => {
+      assert_true(scroll_threshold_crossed, 'scroll threashold should be crossed');
+      assert_equals(scroller.scrollTop, 20, 'scroll offset should snap back');
+    });
     testThumbSnapBack.done();
   }
 
@@ -51,7 +54,7 @@
 
     // Verify that scrolling occured.
     standardDivFast.addEventListener("scroll", function() {
-      if(standardDivFast.scrollTop == 480)
+      if (standardDivFast.scrollTop == 505)
         scroll_threshold_crossed = true;
     });
 
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-check-target-element.html b/third_party/blink/web_tests/fast/spatial-navigation/snav-check-target-element.html
new file mode 100644
index 0000000..a68f85d13
--- /dev/null
+++ b/third_party/blink/web_tests/fast/spatial-navigation/snav-check-target-element.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- TODO(crbug.com/1015307): should adopt title value. -->
+<title>SpatNav isn't performed if the webpage directly handles the arrow keys for moving focus.</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+
+<button id="a">A</button>
+<button id="b">B</button>
+<button id="c">C</button>
+<button id="d">D</button>
+<button id="e">E</button>
+<button id="f">F</button>
+<button id="g">G</button>
+
+<script>
+  const buttons = document.getElementsByTagName("button");
+
+  // Keydown on D button.
+  buttons[3].addEventListener('keydown', (e) => {
+    // Custom SpatNav on web page.
+    if (e.key == 'ArrowLeft') {
+      // Move focus to B button.
+      buttons[1].focus();
+    } else if (e.key == 'ArrowRight') {
+      // Move focus to F button.
+      buttons[5].focus();
+    }
+  });
+</script>
+<script>
+  var resultMap = [
+    ["Right", "a"],
+    ["Right", "b"],
+    ["Right", "c"],
+    ["Right", "d"],
+    ["Right", "f"],
+    ["Right", "g"],
+    ["Left", "f"],
+    ["Left", "e"],
+    ["Left", "d"],
+    ["Left", "b"],
+    ["Left", "a"]
+  ];
+
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-prevent-default.html b/third_party/blink/web_tests/fast/spatial-navigation/snav-prevent-default.html
new file mode 100644
index 0000000..910e14f
--- /dev/null
+++ b/third_party/blink/web_tests/fast/spatial-navigation/snav-prevent-default.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+
+<div>
+  <button id="a">A</button>
+</div>
+<div>
+  <button id="b">B</button>
+</div>
+<div>
+  <button id="c">C</button>
+</div>
+<div tabindex=-1>
+  <button id="d">D</button>
+</div>
+<div tabindex=-1>
+  <button id="e">E</button>
+</div>
+<div tabindex=-1>
+  <button id="f">F</button>
+</div>
+<div>
+  <button id="g">G</button>
+</div>
+<script>
+  const buttons = document.getElementsByTagName("button");
+  const parentContainers = document.getElementsByTagName("div");
+  const preventDefault = (e) => e.preventDefault();
+
+  // Add event handler.
+  buttons[0].addEventListener('keydown', preventDefault);
+  buttons[1].addEventListener('keydown', preventDefault, { capture: true });
+  buttons[2].addEventListener('keydown', preventDefault, { passive: true });
+  parentContainers[3].addEventListener('keydown', preventDefault);
+  parentContainers[4].addEventListener('keydown', preventDefault, { capture: true });
+  parentContainers[5].addEventListener('keydown', preventDefault, { passive: true });
+  buttons[6].addEventListener('keydown', (e) => { });
+
+  // SpatNav Test
+  snav.assertSnavEnabledAndTestable();
+
+  test(() => {
+    // Move focus to A button.
+    snav.triggerMove('Down');
+    assert_equals(window.internals.interestedElement,
+      buttons[0], "A button should start off interested.");
+
+    snav.triggerMove('Down');
+    assert_equals(window.internals.interestedElement,
+      buttons[0], "The focus should remain on the A button.");
+
+    // Move focus to B button.
+    buttons[1].focus();
+    assert_equals(window.internals.interestedElement,
+      buttons[1], "A button should start off interested.");
+
+    snav.triggerMove('Down');
+    assert_equals(window.internals.interestedElement,
+      buttons[1], "The focus should remain on the B button.");
+
+    // Move focus to C button.
+    buttons[2].focus();
+    assert_equals(window.internals.interestedElement,
+      buttons[2], "C button should start off interested.");
+
+    // Move focus to D button.
+    snav.triggerMove('Down');
+    assert_equals(window.internals.interestedElement,
+      buttons[3], "The focus should move to the D button.");
+
+    snav.triggerMove('Down');
+    assert_equals(window.internals.interestedElement,
+      buttons[3], "The focus should remain on the D button.");
+
+    // Move focus to E button.
+    buttons[4].focus();
+    assert_equals(window.internals.interestedElement,
+      buttons[4], "E button should start off interested.");
+
+    snav.triggerMove('Down');
+    assert_equals(window.internals.interestedElement,
+      buttons[4], "The focus should remain on the E button.");
+
+    // Move focus to F button.
+    buttons[5].focus();
+    assert_equals(window.internals.interestedElement,
+      buttons[5], "E button should start off interested.");
+
+    // Move focus to G button.
+    snav.triggerMove('Down');
+    assert_equals(window.internals.interestedElement,
+      buttons[6], "The focus should move to the G button.");
+
+    // Move focus to F button again.
+    snav.triggerMove('Up');
+    assert_equals(window.internals.interestedElement,
+      buttons[5], "The focus should move to the F button.");
+
+  }, "Event.preventDefault() prevent the spatial-navigation operation.");
+</script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/reveal-objects-expected.txt b/third_party/blink/web_tests/http/tests/devtools/reveal-objects-expected.txt
index 0302376..42fdcb62 100644
--- a/third_party/blink/web_tests/http/tests/devtools/reveal-objects-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/reveal-objects-expected.txt
@@ -6,6 +6,12 @@
 Running: revealNode
 Node revealed in the Elements panel
 
+Running: revealRemovedNode
+Removed node not revealed
+
+Running: revealNodeWithRemovedParent
+Node with removed parent not revealed
+
 Running: revealUILocation
 UILocation bar.js:2:1 revealed in the Sources panel
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/reveal-objects.js b/third_party/blink/web_tests/http/tests/devtools/reveal-objects.js
index 72f95bb..0f58da07 100644
--- a/third_party/blink/web_tests/http/tests/devtools/reveal-objects.js
+++ b/third_party/blink/web_tests/http/tests/devtools/reveal-objects.js
@@ -11,8 +11,12 @@
   await TestRunner.showPanel('resources');
   await TestRunner.showPanel('network');
   await TestRunner.loadHTML(`
-      <div id="div">
+      <div id="targetnode">
       </div>
+      <span id="toremove"></span>
+      <span id="containertoremove">
+        <span id="child"></span>
+      </span>
     `);
   await TestRunner.evaluateInPagePromise(`
       function loadResource(url)
@@ -24,14 +28,17 @@
   `);
   await TestRunner.addScriptTag('resources/bar.js');
 
-  var node;
+  var divNode;
+  var spanNode;
+  var childNode;
+  var container;
   var resource;
   var uiLocation;
   var requestWithResource;
   var requestWithoutResource;
 
   TestRunner.runTestSuite([
-    function init(next) {
+    async function init(next) {
       installHooks();
 
       TestRunner.resourceTreeModel.forAllResources(function(r) {
@@ -42,14 +49,14 @@
       });
       uiLocation = Workspace.workspace.uiSourceCodeForURL(resource.url).uiLocation(2, 1);
 
-      ElementsTestRunner.nodeWithId('div', nodeCallback);
+      divNode = await ElementsTestRunner.nodeWithIdPromise('targetnode');
+      spanNode = await ElementsTestRunner.nodeWithIdPromise('toremove');
+      childNode = await ElementsTestRunner.nodeWithIdPromise('child');
+      container = await ElementsTestRunner.nodeWithIdPromise('containertoremove');
 
-      function nodeCallback(foundNode) {
-        node = foundNode;
-        NetworkTestRunner.recordNetwork();
-        var url = TestRunner.url('bar.js');
-        TestRunner.evaluateInPage(`loadResource('${url}')`, firstXhrCallback);
-      }
+      NetworkTestRunner.recordNetwork();
+      var url = TestRunner.url('bar.js');
+      TestRunner.evaluateInPage(`loadResource('${url}')`, firstXhrCallback);
 
       function firstXhrCallback() {
         requestWithResource = SDK.networkLog.requestForURL(resource.url);
@@ -69,7 +76,29 @@
     },
 
     function revealNode(next) {
-      Common.Revealer.reveal(node).then(next);
+      Common.Revealer.reveal(divNode).then(next);
+    },
+
+    function revealRemovedNode(next) {
+      spanNode.removeNode(function() {
+        Common.Revealer.reveal(spanNode).then(() => {
+          TestRunner.addResult('Removed node revealed');
+        }, () => {
+          TestRunner.addResult('Removed node not revealed');
+        }).finally(next);
+      });
+    },
+
+    function revealNodeWithRemovedParent(next) {
+      // Note: we remove the container, and then check that the child also can
+      // not be revealed since it is also detached.
+      container.parentNode.removeNode(function() {
+        Common.Revealer.reveal(childNode).then(() => {
+          TestRunner.addResult('Node with removed parent revealed');
+        }, () => {
+          TestRunner.addResult('Node with removed parent not revealed');
+        }).finally(next);
+      });
     },
 
     function revealUILocation(next) {
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-message-order-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-message-order-expected.txt
new file mode 100644
index 0000000..b0a9b322
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-message-order-expected.txt
@@ -0,0 +1,32 @@
+Tests the order in which not finished asynchronous Runtime.evaluate calls are temintated on navigation.
+promise created (100021)
+promise created (100013)
+promise created (100017)
+receiving result 100021:
+{
+    error : {
+        code : -32000
+        message : Inspected target navigated or closed
+    }
+    id : 100021
+    sessionId : <string>
+}
+receiving result 100013:
+{
+    error : {
+        code : -32000
+        message : Inspected target navigated or closed
+    }
+    id : 100013
+    sessionId : <string>
+}
+receiving result 100017:
+{
+    error : {
+        code : -32000
+        message : Inspected target navigated or closed
+    }
+    id : 100017
+    sessionId : <string>
+}
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-message-order.js b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-message-order.js
new file mode 100644
index 0000000..7d96cd4
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-message-order.js
@@ -0,0 +1,48 @@
+(async function(testRunner) {
+  var {page, session, dp} = await testRunner.startURL(
+      'http://first.test:8000/inspector-protocol/resources/test-page.html',
+      `Tests the order in which not finished asynchronous Runtime.evaluate calls are temintated on navigation.`);
+
+  // Runtime.enable so we can capture console.log below. This command completes.
+  await dp.Runtime.enable();
+  dp.Runtime.onConsoleAPICalled(e => { testRunner.log(e.params.args[0].value); });
+
+  const callIdsToWatch = new Set();
+
+  // Creates a promise which doesn't resolve, so therefore, we won't get a
+  // response - only once we've navigated below, we'll get an error response
+  // due to command termination. Request ID will be call_id.
+  async function createNonResolvingPromise(call_id) {
+    const saveRequestId = DevToolsAPI._requestId;
+    DevToolsAPI._requestId = call_id - 1;
+    dp.Runtime.evaluate({
+      expression: `new Promise(() => console.log('promise created (` + call_id + `)'))`,
+      awaitPromise: true
+    });
+    DevToolsAPI._requestId = saveRequestId;
+    await dp.Runtime.onceConsoleAPICalled();  // Be sure to capture the log.
+    callIdsToWatch.add(call_id);
+  }
+
+  // Override the dispatch routine so we can intercept and log the responses in
+  // the order in which they arrive.
+  const originalDispatch = DevToolsAPI.dispatchMessage;
+  DevToolsAPI.dispatchMessage = function(message) {
+    var obj = JSON.parse(message);
+    if (callIdsToWatch.has(obj.id)) {
+      testRunner.log(obj, 'receiving result ' + obj.id + ':\n', ['sessionId']);
+    }
+    originalDispatch(message);
+  }
+
+  // Now create three promises with out-of-order call ids.
+  await createNonResolvingPromise(100021);
+  await createNonResolvingPromise(100013);
+  await createNonResolvingPromise(100017);
+
+  // Navigate, this causes the error notifications to arrive - in the same order
+  // in which the original requests were sent.
+  await page.navigate('http://second.test:8000/inspector-protocol/resources/test-page.html');
+
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-paused-message-order-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-paused-message-order-expected.txt
new file mode 100644
index 0000000..ee8b892
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-paused-message-order-expected.txt
@@ -0,0 +1,65 @@
+Tests the order in which unfinished Runtime.{enable,evaluate} calls are handled around paused navigation.
+promise created (100021)
+promise created (100013)
+promise created (100017)
+Unpausing navigation ...
+receiving result 100021:
+{
+    error : {
+        code : -32000
+        message : Inspected target navigated or closed
+    }
+    id : 100021
+    sessionId : <string>
+}
+receiving result 100013:
+{
+    error : {
+        code : -32000
+        message : Inspected target navigated or closed
+    }
+    id : 100013
+    sessionId : <string>
+}
+receiving result 100017:
+{
+    error : {
+        code : -32000
+        message : Inspected target navigated or closed
+    }
+    id : 100017
+    sessionId : <string>
+}
+receiving result 100091:
+{
+    id : 100091
+    result : {
+    }
+    sessionId : <string>
+}
+receiving result 100081:
+{
+    id : 100081
+    result : {
+    }
+    sessionId : <string>
+}
+receiving result 100086:
+{
+    id : 100086
+    result : {
+    }
+    sessionId : <string>
+}
+Hi from request 10099!
+receiving result 100099:
+{
+    id : 100099
+    result : {
+        result : {
+            type : undefined
+        }
+    }
+    sessionId : <string>
+}
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-paused-message-order.js b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-paused-message-order.js
new file mode 100644
index 0000000..8a6ebb10
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-navigation-paused-message-order.js
@@ -0,0 +1,91 @@
+
+(async function(testRunner) {
+  var {page, session, dp} = await testRunner.startURL(
+      'http://first.test:8000/inspector-protocol/resources/test-page.html',
+      `Tests the order in which unfinished Runtime.{enable,evaluate} calls are handled around paused navigation.`);
+
+  // Unlike eval-avait-navigation-message-order.js, this test uses
+  // the Fetch domain to pause navigation; in this way we can send additional
+  // commands during the pause and observe how they're handled when navigation
+  // completes.
+
+  // Runtime.enable so we can capture console.log below. This command completes.
+  await dp.Runtime.enable();
+  dp.Runtime.onConsoleAPICalled(e => { testRunner.log(e.params.args[0].value); });
+
+  const callIdsToWatch = new Set();
+
+  // Creates a promise which doesn't resolve, so therefore, we won't get a
+  // response - only once we've navigated below, we'll get an error response
+  // due to command termination. Request ID will be call_id.
+  async function createNonResolvingPromise(call_id) {
+    const saveRequestId = DevToolsAPI._requestId;
+    DevToolsAPI._requestId = call_id - 1;
+    dp.Runtime.evaluate({
+      expression: `new Promise(() => console.log('promise created (` + call_id + `)'))`,
+      awaitPromise: true
+    });
+    DevToolsAPI._requestId = saveRequestId;
+    await dp.Runtime.onceConsoleAPICalled();  // Be sure to capture the log.
+    callIdsToWatch.add(call_id);
+  }
+
+  // Override the dispatch routine so we can intercept and log the responses in
+  // the order in which they arrive.
+  const originalDispatch = DevToolsAPI.dispatchMessage;
+  DevToolsAPI.dispatchMessage = function(message) {
+    var obj = JSON.parse(message);
+    if (callIdsToWatch.has(obj.id)) {
+      testRunner.log(obj, 'receiving result ' + obj.id + ':\n', ['sessionId']);
+    }
+    originalDispatch(message);
+  }
+
+  // Now create three promises with out-of-order call ids.
+  await createNonResolvingPromise(100021);
+  await createNonResolvingPromise(100013);
+  await createNonResolvingPromise(100017);
+
+  // This makes it so that we'll get a Fetch.requestPaused event after
+  // issuing page.navigate below.
+  await dp.Fetch.enable();
+
+  // Navigate, this causes the error notifications to arrive - in the same order
+  // in which the original requests were sent.
+  const navigatePromise = page.navigate('http://second.test:8000/inspector-protocol/resources/test-page.html');
+
+  const requestId = (await dp.Fetch.onceRequestPaused()).params.requestId;
+  // The DevToolsSession is now in suspended state. Messages will be queued
+  // but not executed until after the navigation is unpaused and completes.
+
+  // Send three Runtime.enable commands with specific command ids.
+  // We'll observe that these commands complete in the order in which they
+  // were issued, after the navigation is unpaused.
+  const saveRequestId = DevToolsAPI._requestId;
+  DevToolsAPI._requestId = 100090;
+  callIdsToWatch.add(100091);
+  dp.Runtime.enable();
+  DevToolsAPI._requestId = 100080;
+  callIdsToWatch.add(100081);
+  dp.Runtime.enable();
+  DevToolsAPI._requestId = 100085;
+  callIdsToWatch.add(100086);
+  dp.Runtime.enable();
+
+  // If we issue commands that are not idempotent, that is, commands
+  // that would get terminated on cross process navigation, while a navigation
+  // is paused, they'll get queued and issued after the navigation (just
+  // like Runtime.enable above).
+  DevToolsAPI._requestId = 100098;
+  callIdsToWatch.add(100099);
+  dp.Runtime.evaluate({
+    expression: `console.log('Hi from request 10099!')`, awaitPromise: true
+  });
+  DevToolsAPI._requestId = saveRequestId;
+
+  testRunner.log('Unpausing navigation ...');
+  await dp.Fetch.continueRequest({requestId});
+  await navigatePromise;
+
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-tertmiated-on-navigation-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-terminated-on-navigation-expected.txt
similarity index 93%
rename from third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-tertmiated-on-navigation-expected.txt
rename to third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-terminated-on-navigation-expected.txt
index 082d637..48a8f232 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-tertmiated-on-navigation-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-terminated-on-navigation-expected.txt
@@ -1,4 +1,4 @@
-Tests that not finished asynchronous Runtime.evaluate calls are temintated on navigation.
+Tests that not finished asynchronous Runtime.evaluate calls are terminated on navigation.
 Evaluated promise in page
 Navigated to another domain
 Evaluation response received:
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-tertmiated-on-navigation.js b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-terminated-on-navigation.js
similarity index 91%
rename from third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-tertmiated-on-navigation.js
rename to third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-terminated-on-navigation.js
index 1fed7de..dcb5840 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-tertmiated-on-navigation.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/runtime/eval-await-terminated-on-navigation.js
@@ -1,13 +1,11 @@
 (async function(testRunner) {
   var {page, session, dp} = await testRunner.startURL(
     'http://first.test:8000/inspector-protocol/resources/test-page.html',
-    `Tests that not finished asynchronous Runtime.evaluate calls are temintated on navigation.`);
-  let error;
+    `Tests that not finished asynchronous Runtime.evaluate calls are terminated on navigation.`);
   const evalPromise = dp.Runtime.evaluate({
     expression: `new Promise(() => console.log('Never resolving promise created'))`,
     awaitPromise: true
-  }).catch(e => error = e);
-  await Promise.resolve();
+  });
   testRunner.log('Evaluated promise in page');
   await page.navigate('http://second.test:8000/inspector-protocol/resources/test-page.html');
   testRunner.log('Navigated to another domain');
diff --git a/third_party/blink/web_tests/http/tests/mixed-autoupgrade/optionally/image-upgrade-console-message.https-expected.txt b/third_party/blink/web_tests/http/tests/mixed-autoupgrade/optionally/image-upgrade-console-message.https-expected.txt
index 91d8cc0..b070597 100644
--- a/third_party/blink/web_tests/http/tests/mixed-autoupgrade/optionally/image-upgrade-console-message.https-expected.txt
+++ b/third_party/blink/web_tests/http/tests/mixed-autoupgrade/optionally/image-upgrade-console-message.https-expected.txt
@@ -1,2 +1,2 @@
-CONSOLE WARNING: line 2: Mixed Content: The page at 'https://127.0.0.1:8443/mixed-autoupgrade/optionally/image-upgrade-console-message.https.html' was loaded over HTTPS, but requested an insecure element 'http://web-platform.test:8443/mixed-autoupgrade/resources/pass.png'. As part of an experiment this request was automatically upgraded to HTTPS, For more information see https://chromium.googlesource.com/chromium/src/+/master/docs/security/autoupgrade-mixed.md
-CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/mixed-autoupgrade/optionally/image-upgrade-console-message.https.html' was loaded over HTTPS, but requested an insecure element 'http://web-platform.test:8443/mixed-autoupgrade/resources/pass.png'. As part of an experiment this request was automatically upgraded to HTTPS, For more information see https://chromium.googlesource.com/chromium/src/+/master/docs/security/autoupgrade-mixed.md
+CONSOLE WARNING: line 2: Mixed Content: The page at 'https://127.0.0.1:8443/mixed-autoupgrade/optionally/image-upgrade-console-message.https.html' was loaded over HTTPS, but requested an insecure element 'http://web-platform.test:8443/mixed-autoupgrade/resources/pass.png'. This request was automatically upgraded to HTTPS, For more information see https://chromium.googlesource.com/chromium/src/+/master/docs/security/autoupgrade-mixed.md
+CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/mixed-autoupgrade/optionally/image-upgrade-console-message.https.html' was loaded over HTTPS, but requested an insecure element 'http://web-platform.test:8443/mixed-autoupgrade/resources/pass.png'. This request was automatically upgraded to HTTPS, For more information see https://chromium.googlesource.com/chromium/src/+/master/docs/security/autoupgrade-mixed.md
diff --git a/third_party/blink/web_tests/http/tests/performance-timing/longtask-v2/longtask-executescript.html b/third_party/blink/web_tests/http/tests/performance-timing/longtask-v2/longtask-executescript.html
deleted file mode 100644
index efb92d7..0000000
--- a/third_party/blink/web_tests/http/tests/performance-timing/longtask-v2/longtask-executescript.html
+++ /dev/null
@@ -1,64 +0,0 @@
-\<!DOCTYPE HTML>
-<meta charset=utf-8>
-<title>LongTask Timing: validate the long task attributes for execute-script</title>
-<body>
-
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<h1>Long Task Attributes for Execute Script</h1>
-<div id="log"></div>
-<script>
-  async_test(function (t) {
-    if (typeof PerformanceLongTaskTiming === 'undefined') {
-        assert_unreached("Longtasks are not supported.");
-        t.done();
-    }
-    const scriptURL = 'longtask-executescript.html';
-    const observer = new PerformanceObserver(
-      t.step_func(function (entryList) {
-        const longtaskEntries = entryList.getEntries().filter((e) => {
-          return e.attribution.find((a) => {
-            return a.scriptURL.includes(scriptURL);
-          });
-        });
-        if (longtaskEntries.length === 0) {
-          return;
-        }
-        assert_equals(longtaskEntries.length, 1);
-        const longtask = longtaskEntries[0];
-        assert_equals(longtask.entryType, 'longtask');
-        assert_equals(longtask.name, 'self');
-        assert_greater_than(longtask.duration, 50);
-        assert_greater_than_equal(longtask.startTime, 0);
-        const currentTime = performance.now();
-        assert_less_than_equal(longtask.startTime, currentTime);
-
-        const runAttribution = longtask.attribution.filter(function(it) {
-          return it.name === 'script-run' &&
-              it.scriptURL.includes(scriptURL);
-        });
-        assert_equals(runAttribution.length, 1);
-        const it = runAttribution[0];
-        assert_greater_than(it.duration, 51);
-        assert_greater_than(it.startTime, 0);
-        assert_equals(it.entryType, 'taskattribution');
-        assert_equals(it.containerType, 'iframe');
-        assert_equals(it.containerId, '');
-        assert_equals(it.containerName, '');
-        assert_equals(it.containerSrc, '');
-        assert_true(it.scriptURL.includes(scriptURL));
-        observer.disconnect();
-        t.done();
-      })
-    );
-    observer.observe({entryTypes: ['longtask']});
-
-    /* Generate a slow task. */
-    const begin = window.performance.now();
-    while (window.performance.now() < begin + 51);
-
-}, 'Performance longtask entries with execute-script attribute are observable.');
-</script>
-
-</body>
diff --git a/third_party/blink/web_tests/http/tests/performance-timing/longtask-v2/longtask-v8compile.html b/third_party/blink/web_tests/http/tests/performance-timing/longtask-v2/longtask-v8compile.html
deleted file mode 100644
index 7aa129f4..0000000
--- a/third_party/blink/web_tests/http/tests/performance-timing/longtask-v2/longtask-v8compile.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!DOCTYPE HTML>
-<meta charset=utf-8>
-<title>LongTask Timing: validate the long task attributes for script-compile</title>
-<body>
-
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<h1>Long Task Attributes for Script-compile</h1>
-<div id="log"></div>
-<script>
-  async_test(function (t) {
-    if (typeof PerformanceLongTaskTiming === 'undefined') {
-        assert_unreached("Longtasks are not supported.");
-        t.done();
-    }
-    const scriptURL = 'resources/makelongtask.js';
-    const lineColumn = '(0, 0)';
-    const observer = new PerformanceObserver(
-      t.step_func(function(entryList) {
-        const longtaskEntries = entryList.getEntries().filter((e) => {
-          return e.attribution.find((a) => {
-            return a.scriptURL.includes(scriptURL);
-          });
-        });
-        if (longtaskEntries.length === 0) {
-          return;
-        }
-        assert_equals(longtaskEntries.length, 1);
-        const longtask = longtaskEntries[0];
-        assert_equals(longtask.entryType, 'longtask');
-        assert_equals(longtask.name, 'self');
-        assert_greater_than(longtask.duration, 50);
-        assert_greater_than_equal(longtask.startTime, 0);
-        const currentTime = performance.now();
-        assert_less_than_equal(longtask.startTime, currentTime);
-
-        const compileAttribution = longtask.attribution.filter((it) => {
-          return it.name === 'script-compile' &&
-              it.scriptURL.includes(scriptURL);
-        });
-        assert_equals(compileAttribution.length, 1);
-        const it = compileAttribution[0];
-        assert_greater_than(it.startTime, 0);
-        assert_equals(it.entryType, 'taskattribution');
-        assert_equals(it.containerType, 'iframe');
-        assert_equals(it.containerId, '');
-        assert_equals(it.containerName, '');
-        assert_equals(it.containerSrc, '');
-        assert_true(it.scriptURL.includes(scriptURL + lineColumn));
-        observer.disconnect();
-        t.done();
-      })
-    );
-    observer.observe({entryTypes: ['longtask']});
-
-    internals.BypassLongCompileThresholdOnce();
-
-    var iframe=document.createElement('Script');
-    iframe.src = "resources/makelongtask.js";
-    document.body.appendChild(iframe);
-
-}, 'Performance longtask entries with script-compile attribute are observable.');
-</script>
-
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/performance-timing/longtask-v2/resources/makelongtask.js b/third_party/blink/web_tests/http/tests/performance-timing/longtask-v2/resources/makelongtask.js
deleted file mode 100644
index cb5e7be..0000000
--- a/third_party/blink/web_tests/http/tests/performance-timing/longtask-v2/resources/makelongtask.js
+++ /dev/null
@@ -1,3 +0,0 @@
-/* Generate a slow task. */
-const begin = window.performance.now();
-while (window.performance.now() < begin + 51);
\ No newline at end of file
diff --git a/third_party/blink/web_tests/inspector-protocol/accessibility/accessibility-getFullAXTree-display-locked.js b/third_party/blink/web_tests/inspector-protocol/accessibility/accessibility-getFullAXTree-display-locked.js
index df05bd0..4177283 100644
--- a/third_party/blink/web_tests/inspector-protocol/accessibility/accessibility-getFullAXTree-display-locked.js
+++ b/third_party/blink/web_tests/inspector-protocol/accessibility/accessibility-getFullAXTree-display-locked.js
@@ -1,20 +1,17 @@
 (async function(testRunner) {
   var {page, session, dp} = await testRunner.startHTML(`
-    <div id='activatable' rendersubtree='invisible-activatable'>
+    <div id='activatable' rendersubtree='invisible skip-viewport-activation'>
       locked
       <div id='child'>
         child
         <div id='grandChild'>grandChild</div>
       </div>
       <div id='invisible' style='display:none'>invisible</div>
-      <div id='nested' rendersubtree='invisible-activatable'>nested</div>
+      <div id='nested' rendersubtree='invisible skip-viewport-activation'>nested</div>
       text
     </div>
-    <div id='nonActivatable' rendersubtree='invisible'>nonActivatable text</div>
+    <div id='nonActivatable' rendersubtree='invisible skip-activation'>nonActivatable text</div>
     <div id='normal'>normal text</div>
-    <script>
-      activatable.getBoundingClientRect(); // Force layout
-    </script>
   `, 'Tests accessibility values of display locked nodes');
   const dumpAccessibilityNodesFromList =
       (await testRunner.loadScript('../resources/accessibility-dumpAccessibilityNodesFromList.js'))(testRunner, session);
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt
new file mode 100644
index 0000000..b54d554
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+PASS em units
+PASS ex units
+PASS ch units
+PASS rem units
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
index 6fc5e74..02ced7f1 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
index c7c55ed..4e30cbf 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
index 2ea3030..a546fe80 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
index c50a979c..38484562 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
index 000f4d5..b6e7846 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
index 6fcedf7..e6f2ec5 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
index 2e6919a..4b94875 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
index 54a1a84..61ea59e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
index 3dd196e..035f07c7 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
index e0432bd..eefd8f4 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
index 73687cc9..321b7ab 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
index 6fcedf7..e6f2ec5 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
index 2e6919a..4b94875 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
index 54a1a84..61ea59e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
index 3dd196e..035f07c7 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
index e0432bd..eefd8f4 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
index 73687cc9..321b7ab 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
index 6fcedf7..e6f2ec5 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
index 2e6919a..4b94875 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
index 54a1a84..61ea59e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
index 3dd196e..035f07c7 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
index e0432bd..eefd8f4 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
index 73687cc9..321b7ab 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
index 18442dc..951a48e 100644
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
index 1124605..4ea5fcd 100644
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
index 04c33af..3f1a166 100644
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
index b2f94b2..28b310b 100644
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
index e0432bd..eefd8f4 100644
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
index 73687cc9..321b7ab 100644
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt
new file mode 100644
index 0000000..b54d554
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+PASS em units
+PASS ex units
+PASS ch units
+PASS rem units
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png
new file mode 100644
index 0000000..1f3a327
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
index 18442dc..951a48e 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
index 1124605..4ea5fcd 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
index 04c33af..3f1a166 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
index b2f94b2..28b310b 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
index e0432bd..eefd8f4 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
index 73687cc9..321b7ab 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png
new file mode 100644
index 0000000..fc9597e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt
new file mode 100644
index 0000000..f94be57
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/external/wpt/css/css-shapes/shape-outside/values/shape-outside-computed-shape-000-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+PASS em units
+FAIL ex units assert_equals: expected "polygon(130.078px 130.078px)" but got "polygon(130.08px 130.08px)"
+PASS ch units
+PASS rem units
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
deleted file mode 100644
index 2d8883e..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
index fcf7406..fbc6311 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
+++ b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-picker-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
index abb8185..cf7e2d8 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
+++ b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-empty-list-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
index 8b280cf..64beaac 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
index 835044f63..8ff82cb 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
+++ b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/color-suggestion-picker-appearance-value-attribute-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
index 918e395..8066e35f 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
+++ b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/range_meter_progress-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
index ab85159c..35e9401 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png
new file mode 100644
index 0000000..3adb18be
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/meter/meter-extreme-values-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/README.md b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/README.md
new file mode 100644
index 0000000..57203c7
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/README.md
@@ -0,0 +1,4 @@
+This virtual test is for testing import maps without built-in module support.
+
+In the non-virtual tests, import maps with built-in module support is tested
+because built-in modules are enabled via ExperimentalProductivityFeatures.
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-addresses.tentative-expected.txt b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/parsing-addresses.tentative-expected.txt
similarity index 75%
rename from third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-addresses.tentative-expected.txt
rename to third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/parsing-addresses.tentative-expected.txt
index 7d869b4..9140933 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/imported/parsing-addresses.tentative-expected.txt
+++ b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/parsing-addresses.tentative-expected.txt
@@ -5,14 +5,14 @@
 PASS Relative URL-like addresses / should ignore percent-encoded variants of ./, ../, or /
 PASS Built-in module addresses / should accept URLs using the built-in module scheme
 PASS Built-in module addresses / should ignore percent-encoded variants of the built-in module scheme
-PASS Built-in module addresses / should allow built-in module URLs that contain "/" or "\"
+FAIL Built-in module addresses / should allow built-in module URLs that contain "/" or "\" assert_equals: expected "{\"backslash\":[\"std:foo\\\baz\"],\"slashEnd\":[\"std:foo/\"],\"slashMiddle\":[\"std:foo/bar\"]}" but got "{\"backslash\":[\"std:foo\\baz\"],\"slashEnd\":[\"std:foo/\"],\"slashMiddle\":[\"std:foo/bar\"]}"
 FAIL Absolute URL addresses / should only accept absolute URL addresses with fetch schemes assert_equals: expected "{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[\"filesystem:good\"],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[],\"javascript\":[],\"mailto\":[],\"wss\":[]}" but got "{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[\"import:bad\"],\"javascript\":[\"javascript:bad\"],\"mailto\":[\"mailto:bad\"],\"wss\":[\"wss://bad/\"]}"
-FAIL Absolute URL addresses / should only accept absolute URL addresses with fetch schemes inside arrays assert_equals: expected "{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[\"filesystem:good\"],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[],\"javascript\":[],\"mailto\":[],\"wss\":[]}" but got "{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[\"import:bad\"],\"javascript\":[\"javascript:bad\"],\"mailto\":[\"mailto:bad\"],\"wss\":[\"wss://bad/\"]}"
+FAIL Absolute URL addresses / should only accept absolute URL addresses with fetch schemes inside arrays assert_equals: expected "{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[\"filesystem:good\"],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[],\"javascript\":[],\"mailto\":[],\"wss\":[]}" but got "{}"
 FAIL Absolute URL addresses / should parse absolute URLs, ignoring unparseable ones assert_equals: expected "{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/%41\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[],\"unparseable2\":[],\"unparseable3\":[]}" but got "{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/A\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[\"https://ex%20ample.org/\"],\"unparseable2\":[],\"unparseable3\":[]}"
-FAIL Absolute URL addresses / should parse absolute URLs, ignoring unparseable ones inside arrays assert_equals: expected "{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/%41\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[],\"unparseable2\":[],\"unparseable3\":[]}" but got "{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/A\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[\"https://ex%20ample.org/\"],\"unparseable2\":[],\"unparseable3\":[]}"
+FAIL Absolute URL addresses / should parse absolute URLs, ignoring unparseable ones inside arrays assert_equals: expected "{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/%41\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[],\"unparseable2\":[],\"unparseable3\":[]}" but got "{}"
 PASS Failing addresses: mismatched trailing slashes / should warn for the simple case
-PASS Failing addresses: mismatched trailing slashes / should warn for a mismatch alone in an array
-PASS Failing addresses: mismatched trailing slashes / should warn for a mismatch alongside non-mismatches in an array
+FAIL Failing addresses: mismatched trailing slashes / should warn for a mismatch alone in an array assert_equals: expected "{\"std:trailer/\":[],\"trailer/\":[]}" but got "{}"
+FAIL Failing addresses: mismatched trailing slashes / should warn for a mismatch alongside non-mismatches in an array assert_equals: expected "{\"std:trailer/\":[\"https://base.example/bim-atrailer/\"],\"trailer/\":[\"https://base.example/atrailer/\"]}" but got "{}"
 PASS Other invalid addresses / should ignore unprefixed strings that are not absolute URLs
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/parsing-schema.tentative-expected.txt b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/parsing-schema.tentative-expected.txt
new file mode 100644
index 0000000..f767dd2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/parsing-schema.tentative-expected.txt
@@ -0,0 +1,16 @@
+This is a testharness.js-based test.
+PASS Invalid JSON
+PASS Mismatching the top-level schema / should throw for top-level non-objects
+PASS Mismatching the top-level schema / should throw if imports is a non-object
+PASS Mismatching the top-level schema / should throw if scopes is a non-object
+PASS Mismatching the top-level schema / should ignore unspecified top-level entries
+FAIL Mismatching the specifier map schema / should ignore entries where the address is not a string, array, or null assert_equals: expected "{\"bar\":[\"https://example.com/\"]}" but got "{}"
+PASS Mismatching the specifier map schema / should ignore entries where the specifier key is an empty string
+FAIL Mismatching the specifier map schema / should ignore members of an address array that are not strings assert_equals: expected "{\"bar\":[\"https://example.com/\"],\"foo\":[\"https://example.com/\"]}" but got "{}"
+PASS Mismatching the specifier map schema / should throw if a scope's value is not an object
+PASS Normalization / should normalize empty import maps to have imports and scopes keys
+PASS Normalization / should normalize an import map without imports to have imports
+PASS Normalization / should normalize an import map without scopes to have scopes
+FAIL Normalization / should normalize addresses to arrays assert_equals: expected "{\"bar\":[\"https://example.com/2\"],\"baz\":[],\"foo\":[\"https://example.com/1\"]}" but got "{\"foo\":[\"https://example.com/1\"]}"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/resolving-builtins.tentative-expected.txt b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/resolving-builtins.tentative-expected.txt
new file mode 100644
index 0000000..cfea38c
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/resolving-builtins.tentative-expected.txt
@@ -0,0 +1,16 @@
+This is a testharness.js-based test.
+PASS Unmapped built-in module specifiers / should resolve "std:blank" to "std:blank"
+FAIL Unmapped built-in module specifiers / should error resolving "std:none" assert_throws: function "() => resolveUnderTest(NONE)" did not throw
+PASS Remapping built-in module specifiers / should remap built-in modules
+FAIL Remapping built-in module specifiers / should remap built-in modules with slashes assert_equals: expected "https://example.com/app/blank-slash/bar" but got "std:blank/bar"
+FAIL Remapping built-in module specifiers / should remap built-in modules with fallbacks assert_equals: expected "https://example.com/app/none.mjs" but got "std:none"
+FAIL Remapping built-in module specifiers / should remap built-in modules with slashes and fallbacks assert_equals: expected "https://example.com/app/blank/" but got "std:blank/"
+PASS Remapping to built-in modules / should remap to "std:blank"
+FAIL Remapping to built-in modules / should fail when remapping to "std:blank/" assert_throws: function "() => resolveUnderTest('/blank/')" did not throw
+FAIL Remapping to built-in modules / should remap to "std:blank/for-testing" assert_equals: expected "std:blank/for-testing" but got "https://example.com/blank/for-testing"
+PASS Remapping to built-in modules / should remap to "std:blank" for URL-like specifiers
+FAIL Remapping to built-in modules / should fail when remapping to "std:none" assert_throws: function "() => resolveUnderTest('none')" did not throw
+FAIL Fallbacks with built-in module addresses / should resolve to "std:blank" Failed to resolve module specifier blank: Relative references must start with either "/", "./", or "../".
+FAIL Fallbacks with built-in module addresses / should fall back past "std:none" Failed to resolve module specifier none: Relative references must start with either "/", "./", or "../".
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/resolving.tentative-expected.txt b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/resolving.tentative-expected.txt
new file mode 100644
index 0000000..3532634
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/builtin-support.tentative/imported/resolving.tentative-expected.txt
@@ -0,0 +1,26 @@
+This is a testharness.js-based test.
+PASS Unmapped / should resolve ./ specifiers as URLs
+PASS Unmapped / should resolve ../ specifiers as URLs
+PASS Unmapped / should resolve / specifiers as URLs
+PASS Unmapped / should parse absolute fetch-scheme URLs
+FAIL Unmapped / should fail for absolute non-fetch-scheme URLs assert_throws: function "() => resolveUnderTest('mailto:bad')" did not throw
+FAIL Unmapped / should fail for strings not parseable as absolute URLs and not starting with ./ ../ or / assert_throws: function "() => resolveUnderTest('https://ex ample.org/')" did not throw
+PASS Mapped using the "imports" key only (no scopes) / should fail when the mapping is to an empty array
+PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should work for package main modules
+PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should work for package submodules
+PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should work for package names that end in a slash by just passing through
+PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should still fail for package modules that are not declared
+PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should fail for package submodules that map to nowhere
+PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should work for explicitly-mapped specifiers that happen to have a slash
+PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should work when the specifier has punctuation
+PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should fail for attempting to get a submodule of something not declared with a trailing slash
+PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap to other URLs
+FAIL Mapped using the "imports" key only (no scopes) / URL-like specifiers / should fail for URLs that remap to empty arrays assert_throws: function "() => resolveUnderTest('https://example.com/lib/no.mjs')" did not throw
+PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap URLs that are just composed from / and .
+FAIL Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap URLs that are prefix-matched by keys with trailing slashes assert_equals: expected "https://example.com/lib/url-trailing-slash/foo.mjs" but got "https://example.com/test/foo.mjs"
+PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should use the last entry's address when URL-like specifiers parse to the same absolute URL
+PASS Mapped using the "imports" key only (no scopes) / Overlapping entries with trailing slashes / should favor the most-specific key (no empty arrays)
+PASS Mapped using the "imports" key only (no scopes) / Overlapping entries with trailing slashes / should favor the most-specific key when empty arrays are involved for less-specific keys
+FAIL Mapped using the "imports" key only (no scopes) / Overlapping entries with trailing slashes / should favor the most-specific key when empty arrays are involved for more-specific keys assert_throws: function "() => resolveUnderTest('a/b')" did not throw
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/parsing-addresses.tentative-expected.txt b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/parsing-addresses.tentative-expected.txt
new file mode 100644
index 0000000..8d7447d
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/parsing-addresses.tentative-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+PASS Relative URL-like addresses / should accept strings prefixed with ./, ../, or /
+FAIL Relative URL-like addresses / should not accept strings prefixed with ./, ../, or / for data: base URLs assert_equals: expected "{\"imports\":{},\"scopes\":{}}" but got "{\"imports\":{\"dotDotSlash\":\"http://web-platform.test:8001/import-maps/foo\",\"dotSlash\":\"http://web-platform.test:8001/import-maps/imported/foo\",\"slash\":\"http://web-platform.test:8001/foo\"},\"scopes\":{}}"
+PASS Relative URL-like addresses / should accept the literal strings ./, ../, or / with no suffix
+FAIL Relative URL-like addresses / should ignore percent-encoded variants of ./, ../, or / assert_equals: expected "{\"imports\":{},\"scopes\":{}}" but got "{\"imports\":{\"dotDotSlash1\":[],\"dotDotSlash2\":[],\"dotDotSlash3\":[],\"dotSlash1\":[],\"dotSlash2\":[],\"dotSlash3\":[],\"slash2\":[]},\"scopes\":{}}"
+PASS Absolute URL addresses / should only accept absolute URL addresses with fetch schemes
+FAIL Absolute URL addresses / should parse absolute URLs, ignoring unparseable ones assert_equals: expected "{\"imports\":{\"invalidButParseable1\":\"https://example.org/\",\"invalidButParseable2\":\"https://example.com///\",\"noPercentDecoding\":\"https://example.com/%41\",\"percentDecoding\":\"https://example.com/\",\"prettyNormal\":\"https://example.net/\"},\"scopes\":{}}" but got "{\"imports\":{\"invalidButParseable1\":\"https://example.org/\",\"invalidButParseable2\":\"https://example.com///\",\"noPercentDecoding\":\"https://example.com/A\",\"percentDecoding\":\"https://example.com/\",\"prettyNormal\":\"https://example.net/\",\"unparseable1\":\"https://ex%20ample.org/\",\"unparseable2\":[],\"unparseable3\":[]},\"scopes\":{}}"
+FAIL Failing addresses: mismatched trailing slashes / should warn for the simple case assert_equals: expected "{\"imports\":{},\"scopes\":{}}" but got "{\"imports\":{\"trailer/\":[]},\"scopes\":{}}"
+FAIL Other invalid addresses / should ignore unprefixed strings that are not absolute URLs assert_equals: expected "{\"imports\":{},\"scopes\":{}}" but got "{\"imports\":{\"foo\":[]},\"scopes\":{}}"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/parsing-scope-keys.tentative-expected.txt b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/parsing-scope-keys.tentative-expected.txt
new file mode 100644
index 0000000..f2c8f28
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/parsing-scope-keys.tentative-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+PASS Relative URL scope keys / should work with no prefix
+PASS Relative URL scope keys / should work with ./, ../, and / prefixes
+PASS Relative URL scope keys / should work with /s, ?s, and #s
+PASS Relative URL scope keys / should work with an empty string scope key
+PASS Relative URL scope keys / should work with / suffixes
+PASS Relative URL scope keys / should deduplicate based on URL parsing rules
+PASS Absolute URL scope keys / should accept all absolute URL scope keys, with or without fetch schemes
+FAIL Absolute URL scope keys / should parse absolute URL scope keys, ignoring unparseable ones assert_equals: expected "{\"imports\":{},\"scopes\":{\"https://base.example/path1/path2/example.org\":{},\"https://example.com/%41\":{},\"https://example.com///\":{},\"https://example.com/foo/\":{},\"https://example.net/\":{}}}" but got "{\"imports\":{},\"scopes\":{\"https://base.example/path1/path2/example.org\":{},\"https://ex%20ample.org/\":{},\"https://example.com///\":{},\"https://example.com/A\":{},\"https://example.com/foo/\":{},\"https://example.net/\":{}}}"
+FAIL Absolute URL scope keys / should ignore relative URL scope keys when the base URL is a data: URL assert_equals: expected "{\"imports\":{},\"scopes\":{}}" but got "{\"imports\":{},\"scopes\":{\"http://web-platform.test:8001/foo\":{},\"http://web-platform.test:8001/import-maps/foo\":{},\"http://web-platform.test:8001/import-maps/imported/foo\":{}}}"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/parsing-specifier-keys.tentative-expected.txt b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/parsing-specifier-keys.tentative-expected.txt
new file mode 100644
index 0000000..d27449d
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/parsing-specifier-keys.tentative-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+PASS Relative URL-like specifier keys / should absolutize strings prefixed with ./, ../, or / into the corresponding URLs
+FAIL Relative URL-like specifier keys / should not absolutize strings prefixed with ./, ../, or / with a data: URL base assert_equals: expected "{\"imports\":{\"../foo\":\"https://example.com/dotdotslash\",\"./foo\":\"https://example.com/dotslash\",\"/foo\":\"https://example.com/slash\"},\"scopes\":{}}" but got "{\"imports\":{\"http://web-platform.test:8001/foo\":\"https://example.com/slash\",\"http://web-platform.test:8001/import-maps/foo\":\"https://example.com/dotdotslash\",\"http://web-platform.test:8001/import-maps/imported/foo\":\"https://example.com/dotslash\"},\"scopes\":{}}"
+PASS Relative URL-like specifier keys / should absolutize the literal strings ./, ../, or / with no suffix
+PASS Relative URL-like specifier keys / should treat percent-encoded variants of ./, ../, or / as bare specifiers
+PASS Absolute URL specifier keys / Accept all absolute URL specifier keys even with fetch schemes as URLs
+FAIL Absolute URL specifier keys / should parse absolute URLs, treating unparseable ones as bare specifiers assert_equals: expected "{\"imports\":{\"http://[www.example.com]/\":\"https://base.example/unparseable3/\",\"https://ex ample.org/\":\"https://base.example/unparseable1/\",\"https://example.com/\":\"https://base.example/percentDecoding/\",\"https://example.com/%41\":\"https://base.example/noPercentDecoding\",\"https://example.com///\":\"https://base.example/invalidButParseable2/\",\"https://example.com:demo\":\"https://base.example/unparseable2\",\"https://example.net/\":\"https://base.example/prettyNormal/\",\"https://example.org/\":\"https://base.example/invalidButParseable1/\"},\"scopes\":{}}" but got "{\"imports\":{\"http://[www.example.com]/\":\"https://base.example/unparseable3/\",\"https://ex%20ample.org/\":\"https://base.example/unparseable1/\",\"https://example.com/\":\"https://base.example/percentDecoding/\",\"https://example.com///\":\"https://base.example/invalidButParseable2/\",\"https://example.com/A\":\"https://base.example/noPercentDecoding\",\"https://example.com:demo\":\"https://base.example/unparseable2\",\"https://example.net/\":\"https://base.example/prettyNormal/\",\"https://example.org/\":\"https://base.example/invalidButParseable1/\"},\"scopes\":{}}"
+PASS Absolute URL specifier keys / should sort correctly (issue #181)
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/resolving.tentative-expected.txt b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/resolving.tentative-expected.txt
new file mode 100644
index 0000000..9fca2dbc
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/import-maps-without-builtin-modules/external/wpt/import-maps/imported/resolving.tentative-expected.txt
@@ -0,0 +1,24 @@
+This is a testharness.js-based test.
+PASS Unmapped / should resolve ./ specifiers as URLs
+PASS Unmapped / should resolve ../ specifiers as URLs
+PASS Unmapped / should resolve / specifiers as URLs
+PASS Unmapped / should parse absolute fetch-scheme URLs
+PASS Unmapped / should parse absolute non-fetch-scheme URLs
+FAIL Unmapped / should fail for strings not parseable as absolute URLs and not starting with ./ ../ or / assert_throws: function "() => resolveUnderTest('https://ex ample.org/')" did not throw
+PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should work for package main modules
+PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should work for package submodules
+PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should work for package names that end in a slash by just passing through
+PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should still fail for package modules that are not declared
+PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should work for explicitly-mapped specifiers that happen to have a slash
+PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should work when the specifier has punctuation
+PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should fail for attempting to get a submodule of something not declared with a trailing slash
+PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should fail for attempting to get a module if only a trailing-slash version is present
+PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap to other URLs
+PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap URLs that are just composed from / and .
+FAIL Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap URLs that are prefix-matched by keys with trailing slashes assert_equals: expected "https://example.com/lib/url-trailing-slash/foo.mjs" but got "https://example.com/test/foo.mjs"
+PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should use the last entry's address when URL-like specifiers parse to the same absolute URL
+PASS Mapped using the "imports" key only (no scopes) / Overlapping entries with trailing slashes / should favor the most-specific key
+PASS Mapped using the "imports" key only (no scopes) / Overlapping entries with trailing slashes / should favor the most-specific key when there are no mappings for less-specific keys
+PASS Mapped using the "imports" key only (no scopes) / should deal with data: URL bases
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/longtask-v2/http/tests/performance-timing/longtask-v2/README.txt b/third_party/blink/web_tests/virtual/longtask-v2/http/tests/performance-timing/longtask-v2/README.txt
deleted file mode 100644
index 4e5a3130..0000000
--- a/third_party/blink/web_tests/virtual/longtask-v2/http/tests/performance-timing/longtask-v2/README.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This directory contains the test cases for longtask API v2. For now, the
-longtask API V2 are still working in progress and not ready to be web-exposed
- yet. Some of them, if appropriate to be web-platform-tests, will be upstreamed
-to web-platform-tests after longtask v2 are fully implemented.
-
-The test cases in this directory requires enabling of runtime feature flag
-LongTaskV2.
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 5f8b634a..804112e8 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -8773,6 +8773,7 @@
     method getViewerPose
 interface XRInputSource
     attribute @@toStringTag
+    getter gamepad
     getter gripSpace
     getter handedness
     getter profiles
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/rendersubtree/resize-observer-skipped.html b/third_party/blink/web_tests/wpt_internal/display-lock/rendersubtree/resize-observer-skipped.html
new file mode 100644
index 0000000..e44d7f3
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/rendersubtree/resize-observer-skipped.html
@@ -0,0 +1,72 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Display Locking: locked attribute</title>
+<link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+
+<div id="container">
+  <div id="resize" style="width: 50px; height: 50px">
+  </div>
+</div>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<script>
+async_test((t) => {
+  let didCallback = false;
+
+  async function runTest() {
+    let resizeCallback = function (entries) {
+      didCallback = true;
+    }
+    let resizeObserver = new ResizeObserver(resizeCallback);
+
+    resizeObserver.observe(resize);
+
+    requestAnimationFrame(step2);
+  }
+
+  async function step2() {
+    assert_equals(true, didCallback, 'Resize observation should happen in first frame after registering');
+    didCallback = false;
+
+    const container = document.getElementById("container");
+    await setInvisible(container);
+
+    // Change the size of #resize. This should cause a resize observation, but
+    // only when the element becomes unlocked.
+    resize.style.width = '100px';
+
+    requestAnimationFrame(step3);
+  }
+
+  async function step3() {
+    assert_equals(false, didCallback,
+        'ResizeObsever should not run during updateRendering while also invisible');
+
+    await container.updateRendering();
+    requestAnimationFrame(step4);
+  }
+
+  async function step4() {
+    assert_equals(false, didCallback,
+        'ResizeObsever should not run while invisible');
+    await setVisible(container);
+    requestAnimationFrame(step5);
+  }
+
+  async function step5() {
+    assert_equals(true, didCallback,
+        'ResizeObsevers should now run once becoming visible');
+    t.done();
+  }
+
+  window.onload = function() {
+    requestAnimationFrame(() => requestAnimationFrame(runTest));
+  };
+}, "ResizeObserver skipped while locked");
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/webgpu/cts.html b/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
index cbd974a..343572c 100644
--- a/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
+++ b/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
@@ -9,7 +9,7 @@
 <!doctype html>
 <title>WebGPU CTS</title>
 <meta charset=utf-8>
-<link rel="help" href="https://gpuweb.github.io/gpuweb/">
+<link rel=help href='https://gpuweb.github.io/gpuweb/'>
 
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
@@ -22,17 +22,197 @@
 </style>
 
 <textarea id=results></textarea>
-<script type=module src="/webgpu/runtime/wpt.js"></script>
+<script type=module src=/webgpu/runtime/wpt.js></script>
 
-<meta name='variant' content='?q=cts:buffers/create_mapped:'>
-<meta name='variant' content='?q=cts:buffers/map:'>
-<meta name='variant' content='?q=cts:buffers/map_detach:'>
-<meta name='variant' content='?q=cts:buffers/map_oom:'>
-<meta name='variant' content='?q=cts:canvas/context_creation:'>
-<meta name='variant' content='?q=cts:command_buffer/basic:'>
-<meta name='variant' content='?q=cts:command_buffer/compute/basic:'>
-<meta name='variant' content='?q=cts:command_buffer/copies:'>
-<meta name='variant' content='?q=cts:command_buffer/render/basic:'>
-<meta name='variant' content='?q=cts:command_buffer/render/rendering:'>
-<meta name='variant' content='?q=cts:examples:'>
-<meta name='variant' content='?q=cts:fences:'>
+<meta name=variant content='?q=cts:buffers/create_mapped:'>
+<meta name=variant content='?q=cts:buffers/map:'>
+<meta name=variant content='?q=cts:buffers/map_detach:'>
+
+<meta name=variant content='?q=cts:buffers/map_oom:mapWriteAsync='>
+<meta name=variant content='?q=cts:buffers/map_oom:mapReadAsync='>
+<meta name=variant content='?q=cts:buffers/map_oom:createBufferMapped='>
+<meta name=variant content='?q=cts:buffers/map_oom:createBufferAsync='>
+
+<meta name=variant content='?q=cts:canvas/context_creation:'>
+<meta name=variant content='?q=cts:command_buffer/basic:'>
+<meta name=variant content='?q=cts:command_buffer/compute/basic:'>
+<meta name=variant content='?q=cts:command_buffer/copies:'>
+<meta name=variant content='?q=cts:command_buffer/render/basic:'>
+<meta name=variant content='?q=cts:command_buffer/render/rendering:'>
+
+<meta name=variant content='?q=cts:command_buffer/render/storeop:storeOp+controls+whether+1x1+drawn+quad+is+stored={"storeOp":"clear","expected":0}'>
+<meta name=variant content='?q=cts:command_buffer/render/storeop:storeOp+controls+whether+1x1+drawn+quad+is+stored={"storeOp":"store","expected":255}'>
+
+<meta name=variant content='?q=cts:examples:'>
+<meta name=variant content='?q=cts:fences:'>
+
+<meta name=variant content='?q=cts:validation/createBindGroup:binding+count+mismatch='>
+<meta name=variant content='?q=cts:validation/createBindGroup:binding+must+be+present+in+layout='>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"uniform-buffer","resourceType":"error"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"uniform-buffer","resourceType":"uniform-buffer"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"uniform-buffer","resourceType":"storage-buffer"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"uniform-buffer","resourceType":"sampler"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"uniform-buffer","resourceType":"sampled-texture"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"uniform-buffer","resourceType":"storage-texture"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"storage-buffer","resourceType":"error"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"storage-buffer","resourceType":"uniform-buffer"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"storage-buffer","resourceType":"storage-buffer"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"storage-buffer","resourceType":"sampler"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"storage-buffer","resourceType":"sampled-texture"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"storage-buffer","resourceType":"storage-texture"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type~{"bindingType":"readonly-storage-buffer"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampler","resourceType":"error"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampler","resourceType":"uniform-buffer"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampler","resourceType":"storage-buffer"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampler","resourceType":"sampler"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampler","resourceType":"sampled-texture"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampler","resourceType":"storage-texture"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampled-texture","resourceType":"error"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampled-texture","resourceType":"uniform-buffer"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampled-texture","resourceType":"storage-buffer"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampled-texture","resourceType":"sampler"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampled-texture","resourceType":"sampled-texture"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type={"bindingType":"sampled-texture","resourceType":"storage-texture"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+binding+must+contain+exactly+one+buffer+of+its+type~{"bindingType":"storage-texture"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:texture+binding+must+have+correct+usage={"type":"sampled-texture"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:texture+binding+must+have+correct+usage={"type":"storage-texture"}'>
+<meta name=variant content='?q=cts:validation/createBindGroup:texture+must+have+correct+component+type~'>
+<meta name=variant content='?q=cts:validation/createBindGroup:texture+must+have+correct+dimension='>
+<meta name=variant content='?q=cts:validation/createBindGroup:buffer+offset+and+size+for+bind+groups+match~'>
+
+<meta name=variant content='?q=cts:validation/createBindGroupLayout:some+binding+index+was+specified+more+than+once='>
+<meta name=variant content='?q=cts:validation/createBindGroupLayout:negative+binding+index='>
+<meta name=variant content='?q=cts:validation/createBindGroupLayout:Visibility+of+bindings+can+be+0='>
+<meta name=variant content='?q=cts:validation/createBindGroupLayout:number+of+dynamic+buffers+exceeds+the+maximum+value~'>
+<meta name=variant content='?q=cts:validation/createBindGroupLayout:dynamic+set+to+true+is+allowed+only+for+buffers={"type":"uniform-buffer","success":true}'>
+<meta name=variant content='?q=cts:validation/createBindGroupLayout:dynamic+set+to+true+is+allowed+only+for+buffers={"type":"storage-buffer","success":true}'>
+<meta name=variant content='?q=cts:validation/createBindGroupLayout:dynamic+set+to+true+is+allowed+only+for+buffers={"type":"readonly-storage-buffer","success":true}'>
+<meta name=variant content='?q=cts:validation/createBindGroupLayout:dynamic+set+to+true+is+allowed+only+for+buffers={"type":"sampler","success":false}'>
+<meta name=variant content='?q=cts:validation/createBindGroupLayout:dynamic+set+to+true+is+allowed+only+for+buffers={"type":"sampled-texture","success":false}'>
+<meta name=variant content='?q=cts:validation/createBindGroupLayout:dynamic+set+to+true+is+allowed+only+for+buffers={"type":"storage-texture","success":false}'>
+
+<meta name=variant content='?q=cts:validation/createPipelineLayout:'>
+
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+sampleCount={"sampleCount":0,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+sampleCount={"sampleCount":1,"success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+sampleCount={"sampleCount":2,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+sampleCount={"sampleCount":3,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+sampleCount={"sampleCount":4,"success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+sampleCount={"sampleCount":8,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+sampleCount={"sampleCount":16,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+sampleCount={"sampleCount":4,"mipLevelCount":2,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+sampleCount={"sampleCount":4,"arrayLayerCount":2,"success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+mipLevelCount={"width":32,"height":32,"mipLevelCount":1,"success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+mipLevelCount={"width":32,"height":32,"mipLevelCount":0,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+mipLevelCount={"width":32,"height":32,"mipLevelCount":6,"success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+mipLevelCount={"width":31,"height":32,"mipLevelCount":7,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+mipLevelCount={"width":32,"height":31,"mipLevelCount":7,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+mipLevelCount={"width":32,"height":32,"mipLevelCount":100,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:validation+of+mipLevelCount={"width":32,"height":8,"mipLevelCount":6,"success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+valid+to+destroy+a+texture='>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+valid+to+destroy+a+destroyed+texture='>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+submit+a+destroyed+texture+before+and+after+encode={"destroyBeforeEncode":false,"destroyAfterEncode":false,"success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+submit+a+destroyed+texture+before+and+after+encode={"destroyBeforeEncode":true,"destroyAfterEncode":false,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+submit+a+destroyed+texture+before+and+after+encode={"destroyBeforeEncode":false,"destroyAfterEncode":true,"success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"r8unorm","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"r8snorm","success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"r8uint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"r8sint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"r16uint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"r16sint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"r16float","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rg8unorm","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rg8snorm","success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rg8uint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rg8sint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"r32uint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"r32sint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"r32float","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rg16uint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rg16sint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rg16float","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgba8unorm","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgba8unorm-srgb","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgba8snorm","success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgba8uint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgba8sint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"bgra8unorm","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"bgra8unorm-srgb","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgb10a2unorm","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rg11b10float","success":false}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rg32uint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rg32sint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rg32float","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgba16uint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgba16sint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgba16float","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgba32uint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgba32sint","success":true}'>
+<meta name=variant content='?q=cts:validation/createTexture:it+is+invalid+to+have+an+output+attachment+texture+with+non+renderable+format={"format":"rgba32float","success":true}'>
+
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"arrayLayerCount":1,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"arrayLayerCount":2,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"dimension":"2d-array","arrayLayerCount":1,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"mipLevelCount":1,"baseMipLevel":5,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"mipLevelCount":2,"baseMipLevel":4,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"mipLevelCount":0,"baseMipLevel":0,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"mipLevelCount":0,"baseMipLevel":1,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"mipLevelCount":0,"baseMipLevel":5,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"mipLevelCount":0,"baseMipLevel":6,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"mipLevelCount":7,"baseMipLevel":0,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"mipLevelCount":6,"baseMipLevel":1,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"mipLevelCount":2,"baseMipLevel":5,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+non+array+texture={"mipLevelCount":1,"baseMipLevel":6,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+array+texture={"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+array+texture={"dimension":"2d","arrayLayerCount":1,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+array+texture={"arrayLayerCount":6,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+array+texture={"arrayLayerCount":0,"baseArrayLayer":0,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+array+texture={"arrayLayerCount":0,"baseArrayLayer":1,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+array+texture={"arrayLayerCount":0,"baseArrayLayer":5,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+array+texture={"arrayLayerCount":0,"baseArrayLayer":6,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+array+texture={"arrayLayerCount":7,"baseArrayLayer":0,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+array+texture={"arrayLayerCount":6,"baseArrayLayer":1,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+array+texture={"arrayLayerCount":2,"baseArrayLayer":5,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+texture+view+on+a+2D+array+texture={"arrayLayerCount":1,"baseArrayLayer":6,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+more+than+1+array+layer={"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+more+than+1+array+layer={"format":"rgba8unorm","success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+more+than+1+array+layer={"format":"r8unorm","success":false}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+more+than+1+array+layer={"dimension":"2d-array","success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+more+than+1+array+layer={"dimension":"2d","success":false}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+more+than+1+array+layer={"arrayLayerCount":6,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+more+than+1+array+layer={"arrayLayerCount":6,"dimension":"2d-array","success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+more+than+1+array+layer={"arrayLayerCount":6,"dimension":"2d-array","mipLevelCount":6,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+only+1+array+layer={"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+only+1+array+layer={"format":"rgba8unorm","success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+only+1+array+layer={"format":"r8unorm","success":false}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+only+1+array+layer={"dimension":"2d-array","success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+only+1+array+layer={"dimension":"2d","success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+only+1+array+layer={"arrayLayerCount":0,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+only+1+array+layer={"arrayLayerCount":1,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+only+1+array+layer={"arrayLayerCount":2,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+only+1+array+layer={"mipLevelCount":6,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:Using+defaults+validates+the+same+as+setting+values+for+only+1+array+layer={"mipLevelCount":1,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+cube+map+texture+view={"dimension":"cube","arrayLayerCount":6,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+cube+map+texture+view={"dimension":"cube","arrayLayerCount":3,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+cube+map+texture+view={"dimension":"cube","arrayLayerCount":7,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+cube+map+texture+view={"dimension":"cube","arrayLayerCount":12,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+cube+map+texture+view={"dimension":"cube","success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+cube+map+texture+view={"dimension":"cube-array","arrayLayerCount":12,"success":true}'>
+<meta name=variant content='?q=cts:validation/createView:creating+cube+map+texture+view={"dimension":"cube-array","arrayLayerCount":11,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+cube+map+texture+view={"dimension":"cube-array","arrayLayerCount":13,"success":false}'>
+<meta name=variant content='?q=cts:validation/createView:creating+cube+map+texture+view+with+a+non+square+texture={"dimension":"cube","arrayLayerCount":6}'>
+<meta name=variant content='?q=cts:validation/createView:creating+cube+map+texture+view+with+a+non+square+texture={"dimension":"cube-array","arrayLayerCount":12}'>
+<meta name=variant content='?q=cts:validation/createView:test+the+format+compatibility+rules+when+creating+a+texture+view='>
+<meta name=variant content='?q=cts:validation/createView:it+is+invalid+to+use+a+texture+view+created+from+a+destroyed+texture='>
+
+<meta name=variant content='?q=cts:validation/error_scope:'>
+<meta name=variant content='?q=cts:validation/fences:'>
+<meta name=variant content='?q=cts:validation/queue_submit:'>
+<meta name=variant content='?q=cts:validation/render_pass:'>
+<meta name=variant content='?q=cts:validation/render_pass_descriptor:'>
+<meta name=variant content='?q=cts:validation/setBindGroup:'>
+<meta name=variant content='?q=cts:validation/setBlendColor:'>
+<meta name=variant content='?q=cts:validation/setScissorRect:'>
+<meta name=variant content='?q=cts:validation/setStencilReference:'>
+<meta name=variant content='?q=cts:validation/setViewport:'>
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index bd1d2b0..a8cf754 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -595,6 +595,13 @@
 chrome.automation.AutomationNode.prototype.description;
 
 /**
+ * The tooltip of the node, if any.
+ * @type {(string|undefined)}
+ * @see https://developer.chrome.com/extensions/automation#type-tooltip
+ */
+chrome.automation.AutomationNode.prototype.tooltip;
+
+/**
  * The placeholder for this text field, if any.
  * @type {(string|undefined)}
  * @see https://developer.chrome.com/extensions/automation#type-placeholder
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
index bbf107a..b636820 100644
--- a/third_party/harfbuzz-ng/README.chromium
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -1,9 +1,9 @@
 Name: harfbuzz-ng
 Short Name: harfbuzz-ng
 URL: http://harfbuzz.org
-Version: 2.6.1-125
-Date: 20190923
-Revision: c1a585fab0c17fe47b0728cf67920791c2173019
+Version: 2.6.2-45
+Date: 20191016
+Revision: e637a4b3de2fb8bdbc1b82e822f4a6cc579e794b
 Security Critical: yes
 License: MIT
 License File: src/COPYING
diff --git a/third_party/hunspell/google.patch b/third_party/hunspell/google.patch
index 1a773929..dcc0af9 100644
--- a/third_party/hunspell/google.patch
+++ b/third_party/hunspell/google.patch
@@ -1042,7 +1042,7 @@
 index 43af66b..a35df83 100644
 --- a/src/hunspell/hunspell.hxx
 +++ b/src/hunspell/hunspell.hxx
-@@ -79,6 +79,10 @@
+@@ -79,9 +79,13 @@
  #include <string>
  #include <vector>
  
@@ -1052,7 +1052,12 @@
 +
  #define SPELL_XML "<?xml?>"
  
- #define MAXSUGGESTION 15
+-#define MAXSUGGESTION 15
++#define MAXSUGGESTION 5
+ #define MAXSHARPS 5
+ 
+ #ifndef MAXWORDLEN
+
 @@ -115,11 +119,17 @@ class LIBHUNSPELL_DLL_EXPORTED Hunspell {
     * long path names (without the long path prefix Hunspell will use fopen()
     * with system-dependent character encoding instead of _wfopen()).
diff --git a/third_party/hunspell/src/hunspell/hunspell.hxx b/third_party/hunspell/src/hunspell/hunspell.hxx
index a35df83..13cc2bd 100644
--- a/third_party/hunspell/src/hunspell/hunspell.hxx
+++ b/third_party/hunspell/src/hunspell/hunspell.hxx
@@ -85,7 +85,7 @@
 
 #define SPELL_XML "<?xml?>"
 
-#define MAXSUGGESTION 15
+#define MAXSUGGESTION 5
 #define MAXSHARPS 5
 
 #ifndef MAXWORDLEN
diff --git a/third_party/khronos/GLES2/gl2.h b/third_party/khronos/GLES2/gl2.h
index 38fc7e1..f447378 100644
--- a/third_party/khronos/GLES2/gl2.h
+++ b/third_party/khronos/GLES2/gl2.h
@@ -47,7 +47,7 @@
 #define GL_GLES_PROTOTYPES 1
 #endif
 
-/* Generated on date 20180725 */
+/* Generated on date 20190911 */
 
 /* Generated C header for:
  * API: gles2
@@ -65,8 +65,8 @@
 typedef khronos_int8_t GLbyte;
 typedef khronos_float_t GLclampf;
 typedef khronos_int32_t GLfixed;
-typedef short GLshort;
-typedef unsigned short GLushort;
+typedef khronos_int16_t GLshort;
+typedef khronos_uint16_t GLushort;
 typedef void GLvoid;
 typedef struct __GLsync *GLsync;
 typedef khronos_int64_t GLint64;
diff --git a/third_party/khronos/GLES2/gl2ext.h b/third_party/khronos/GLES2/gl2ext.h
index da10c65..3472b00 100644
--- a/third_party/khronos/GLES2/gl2ext.h
+++ b/third_party/khronos/GLES2/gl2ext.h
@@ -38,7 +38,7 @@
 #define GL_APIENTRYP GL_APIENTRY*
 #endif
 
-/* Generated on date 20180725 */
+/* Generated on date 20190911 */
 
 /* Generated C header for:
  * API: gles2
@@ -198,6 +198,22 @@
 #endif
 #endif /* GL_KHR_robustness */
 
+#ifndef GL_KHR_shader_subgroup
+#define GL_KHR_shader_subgroup 1
+#define GL_SUBGROUP_SIZE_KHR 0x9532
+#define GL_SUBGROUP_SUPPORTED_STAGES_KHR 0x9533
+#define GL_SUBGROUP_SUPPORTED_FEATURES_KHR 0x9534
+#define GL_SUBGROUP_QUAD_ALL_STAGES_KHR 0x9535
+#define GL_SUBGROUP_FEATURE_BASIC_BIT_KHR 0x00000001
+#define GL_SUBGROUP_FEATURE_VOTE_BIT_KHR 0x00000002
+#define GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR 0x00000004
+#define GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR 0x00000008
+#define GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR 0x00000010
+#define GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR 0x00000020
+#define GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR 0x00000040
+#define GL_SUBGROUP_FEATURE_QUAD_BIT_KHR 0x00000080
+#endif /* GL_KHR_shader_subgroup */
+
 #ifndef GL_KHR_texture_compression_astc_hdr
 #define GL_KHR_texture_compression_astc_hdr 1
 #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR   0x93B0
@@ -1300,6 +1316,11 @@
 #endif
 #endif /* GL_EXT_debug_marker */
 
+#ifndef GL_EXT_depth_clamp
+#define GL_EXT_depth_clamp 1
+#define GL_DEPTH_CLAMP_EXT 0x864F
+#endif /* GL_EXT_depth_clamp */
+
 #ifndef GL_EXT_discard_framebuffer
 #define GL_EXT_discard_framebuffer 1
 #define GL_COLOR_EXT                      0x1800
@@ -1861,6 +1882,18 @@
 #endif
 #endif /* GL_EXT_multiview_draw_buffers */
 
+#ifndef GL_EXT_multiview_tessellation_geometry_shader
+#define GL_EXT_multiview_tessellation_geometry_shader 1
+#endif /* GL_EXT_multiview_tessellation_geometry_shader */
+
+#ifndef GL_EXT_multiview_texture_multisample
+#define GL_EXT_multiview_texture_multisample 1
+#endif /* GL_EXT_multiview_texture_multisample */
+
+#ifndef GL_EXT_multiview_timer_query
+#define GL_EXT_multiview_timer_query 1
+#endif /* GL_EXT_multiview_timer_query */
+
 #ifndef GL_EXT_occlusion_query_boolean
 #define GL_EXT_occlusion_query_boolean 1
 #define GL_ANY_SAMPLES_PASSED_EXT         0x8C2F
@@ -2452,6 +2485,10 @@
 #define GL_RGB16_SNORM_EXT                0x8F9A
 #endif /* GL_EXT_texture_norm16 */
 
+#ifndef GL_EXT_texture_query_lod
+#define GL_EXT_texture_query_lod 1
+#endif /* GL_EXT_texture_query_lod */
+
 #ifndef GL_EXT_texture_rg
 #define GL_EXT_texture_rg 1
 #define GL_RED_EXT                        0x1903
@@ -2477,6 +2514,10 @@
 #define GL_SKIP_DECODE_EXT                0x8A4A
 #endif /* GL_EXT_texture_sRGB_decode */
 
+#ifndef GL_EXT_texture_shadow_lod
+#define GL_EXT_texture_shadow_lod 1
+#endif /* GL_EXT_texture_shadow_lod */
+
 #ifndef GL_EXT_texture_storage
 #define GL_EXT_texture_storage 1
 #define GL_TEXTURE_IMMUTABLE_FORMAT_EXT   0x912F
@@ -2728,6 +2769,21 @@
 #ifndef GL_MESA_framebuffer_flip_y
 #define GL_MESA_framebuffer_flip_y 1
 #define GL_FRAMEBUFFER_FLIP_Y_MESA 0x8BBB
+typedef void(GL_APIENTRYP PFNGLFRAMEBUFFERPARAMETERIMESAPROC)(GLenum target,
+                                                              GLenum pname,
+                                                              GLint param);
+typedef void(GL_APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC)(
+    GLenum target,
+    GLenum pname,
+    GLint* params);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glFramebufferParameteriMESA(GLenum target,
+                                                        GLenum pname,
+                                                        GLint param);
+GL_APICALL void GL_APIENTRY glGetFramebufferParameterivMESA(GLenum target,
+                                                            GLenum pname,
+                                                            GLint* params);
+#endif
 #endif /* GL_MESA_framebuffer_flip_y */
 
 #ifndef GL_MESA_program_binary_formats
@@ -2860,6 +2916,10 @@
 #endif
 #endif /* GL_NV_clip_space_w_scaling */
 
+#ifndef GL_NV_compute_shader_derivatives
+#define GL_NV_compute_shader_derivatives 1
+#endif /* GL_NV_compute_shader_derivatives */
+
 #ifndef GL_NV_conditional_render
 #define GL_NV_conditional_render 1
 #define GL_QUERY_WAIT_NV                  0x8E13
@@ -3051,6 +3111,10 @@
 #endif
 #endif /* GL_NV_fragment_coverage_to_color */
 
+#ifndef GL_NV_fragment_shader_barycentric
+#define GL_NV_fragment_shader_barycentric 1
+#endif /* GL_NV_fragment_shader_barycentric */
+
 #ifndef GL_NV_fragment_shader_interlock
 #define GL_NV_fragment_shader_interlock 1
 #endif /* GL_NV_fragment_shader_interlock */
@@ -3236,6 +3300,137 @@
 #endif
 #endif /* GL_NV_internalformat_sample_query */
 
+#ifndef GL_NV_memory_attachment
+#define GL_NV_memory_attachment 1
+#define GL_ATTACHED_MEMORY_OBJECT_NV 0x95A4
+#define GL_ATTACHED_MEMORY_OFFSET_NV 0x95A5
+#define GL_MEMORY_ATTACHABLE_ALIGNMENT_NV 0x95A6
+#define GL_MEMORY_ATTACHABLE_SIZE_NV 0x95A7
+#define GL_MEMORY_ATTACHABLE_NV 0x95A8
+#define GL_DETACHED_MEMORY_INCARNATION_NV 0x95A9
+#define GL_DETACHED_TEXTURES_NV 0x95AA
+#define GL_DETACHED_BUFFERS_NV 0x95AB
+#define GL_MAX_DETACHED_TEXTURES_NV 0x95AC
+#define GL_MAX_DETACHED_BUFFERS_NV 0x95AD
+typedef void(GL_APIENTRYP PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC)(
+    GLuint memory,
+    GLenum pname,
+    GLint first,
+    GLsizei count,
+    GLuint* params);
+typedef void(GL_APIENTRYP PFNGLRESETMEMORYOBJECTPARAMETERNVPROC)(GLuint memory,
+                                                                 GLenum pname);
+typedef void(GL_APIENTRYP PFNGLTEXATTACHMEMORYNVPROC)(GLenum target,
+                                                      GLuint memory,
+                                                      GLuint64 offset);
+typedef void(GL_APIENTRYP PFNGLBUFFERATTACHMEMORYNVPROC)(GLenum target,
+                                                         GLuint memory,
+                                                         GLuint64 offset);
+typedef void(GL_APIENTRYP PFNGLTEXTUREATTACHMEMORYNVPROC)(GLuint texture,
+                                                          GLuint memory,
+                                                          GLuint64 offset);
+typedef void(GL_APIENTRYP PFNGLNAMEDBUFFERATTACHMEMORYNVPROC)(GLuint buffer,
+                                                              GLuint memory,
+                                                              GLuint64 offset);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY
+glGetMemoryObjectDetachedResourcesuivNV(GLuint memory,
+                                        GLenum pname,
+                                        GLint first,
+                                        GLsizei count,
+                                        GLuint* params);
+GL_APICALL void GL_APIENTRY glResetMemoryObjectParameterNV(GLuint memory,
+                                                           GLenum pname);
+GL_APICALL void GL_APIENTRY glTexAttachMemoryNV(GLenum target,
+                                                GLuint memory,
+                                                GLuint64 offset);
+GL_APICALL void GL_APIENTRY glBufferAttachMemoryNV(GLenum target,
+                                                   GLuint memory,
+                                                   GLuint64 offset);
+GL_APICALL void GL_APIENTRY glTextureAttachMemoryNV(GLuint texture,
+                                                    GLuint memory,
+                                                    GLuint64 offset);
+GL_APICALL void GL_APIENTRY glNamedBufferAttachMemoryNV(GLuint buffer,
+                                                        GLuint memory,
+                                                        GLuint64 offset);
+#endif
+#endif /* GL_NV_memory_attachment */
+
+#ifndef GL_NV_mesh_shader
+#define GL_NV_mesh_shader 1
+#define GL_MESH_SHADER_NV 0x9559
+#define GL_TASK_SHADER_NV 0x955A
+#define GL_MAX_MESH_UNIFORM_BLOCKS_NV 0x8E60
+#define GL_MAX_MESH_TEXTURE_IMAGE_UNITS_NV 0x8E61
+#define GL_MAX_MESH_IMAGE_UNIFORMS_NV 0x8E62
+#define GL_MAX_MESH_UNIFORM_COMPONENTS_NV 0x8E63
+#define GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_NV 0x8E64
+#define GL_MAX_MESH_ATOMIC_COUNTERS_NV 0x8E65
+#define GL_MAX_MESH_SHADER_STORAGE_BLOCKS_NV 0x8E66
+#define GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_NV 0x8E67
+#define GL_MAX_TASK_UNIFORM_BLOCKS_NV 0x8E68
+#define GL_MAX_TASK_TEXTURE_IMAGE_UNITS_NV 0x8E69
+#define GL_MAX_TASK_IMAGE_UNIFORMS_NV 0x8E6A
+#define GL_MAX_TASK_UNIFORM_COMPONENTS_NV 0x8E6B
+#define GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_NV 0x8E6C
+#define GL_MAX_TASK_ATOMIC_COUNTERS_NV 0x8E6D
+#define GL_MAX_TASK_SHADER_STORAGE_BLOCKS_NV 0x8E6E
+#define GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_NV 0x8E6F
+#define GL_MAX_MESH_WORK_GROUP_INVOCATIONS_NV 0x95A2
+#define GL_MAX_TASK_WORK_GROUP_INVOCATIONS_NV 0x95A3
+#define GL_MAX_MESH_TOTAL_MEMORY_SIZE_NV 0x9536
+#define GL_MAX_TASK_TOTAL_MEMORY_SIZE_NV 0x9537
+#define GL_MAX_MESH_OUTPUT_VERTICES_NV 0x9538
+#define GL_MAX_MESH_OUTPUT_PRIMITIVES_NV 0x9539
+#define GL_MAX_TASK_OUTPUT_COUNT_NV 0x953A
+#define GL_MAX_DRAW_MESH_TASKS_COUNT_NV 0x953D
+#define GL_MAX_MESH_VIEWS_NV 0x9557
+#define GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_NV 0x92DF
+#define GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_NV 0x9543
+#define GL_MAX_MESH_WORK_GROUP_SIZE_NV 0x953B
+#define GL_MAX_TASK_WORK_GROUP_SIZE_NV 0x953C
+#define GL_MESH_WORK_GROUP_SIZE_NV 0x953E
+#define GL_TASK_WORK_GROUP_SIZE_NV 0x953F
+#define GL_MESH_VERTICES_OUT_NV 0x9579
+#define GL_MESH_PRIMITIVES_OUT_NV 0x957A
+#define GL_MESH_OUTPUT_TYPE_NV 0x957B
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_NV 0x959C
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_NV 0x959D
+#define GL_REFERENCED_BY_MESH_SHADER_NV 0x95A0
+#define GL_REFERENCED_BY_TASK_SHADER_NV 0x95A1
+#define GL_MESH_SHADER_BIT_NV 0x00000040
+#define GL_TASK_SHADER_BIT_NV 0x00000080
+#define GL_MESH_SUBROUTINE_NV 0x957C
+#define GL_TASK_SUBROUTINE_NV 0x957D
+#define GL_MESH_SUBROUTINE_UNIFORM_NV 0x957E
+#define GL_TASK_SUBROUTINE_UNIFORM_NV 0x957F
+#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_NV 0x959E
+#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_NV 0x959F
+typedef void(GL_APIENTRYP PFNGLDRAWMESHTASKSNVPROC)(GLuint first, GLuint count);
+typedef void(GL_APIENTRYP PFNGLDRAWMESHTASKSINDIRECTNVPROC)(GLintptr indirect);
+typedef void(GL_APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC)(
+    GLintptr indirect,
+    GLsizei drawcount,
+    GLsizei stride);
+typedef void(GL_APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC)(
+    GLintptr indirect,
+    GLintptr drawcount,
+    GLsizei maxdrawcount,
+    GLsizei stride);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glDrawMeshTasksNV(GLuint first, GLuint count);
+GL_APICALL void GL_APIENTRY glDrawMeshTasksIndirectNV(GLintptr indirect);
+GL_APICALL void GL_APIENTRY glMultiDrawMeshTasksIndirectNV(GLintptr indirect,
+                                                           GLsizei drawcount,
+                                                           GLsizei stride);
+GL_APICALL void GL_APIENTRY
+glMultiDrawMeshTasksIndirectCountNV(GLintptr indirect,
+                                    GLintptr drawcount,
+                                    GLsizei maxdrawcount,
+                                    GLsizei stride);
+#endif
+#endif /* GL_NV_mesh_shader */
+
 #ifndef GL_NV_non_square_matrices
 #define GL_NV_non_square_matrices 1
 #define GL_FLOAT_MAT2x3_NV                0x8B65
@@ -3697,6 +3892,11 @@
 #define GL_NV_read_stencil 1
 #endif /* GL_NV_read_stencil */
 
+#ifndef GL_NV_representative_fragment_test
+#define GL_NV_representative_fragment_test 1
+#define GL_REPRESENTATIVE_FRAGMENT_TEST_NV 0x937F
+#endif /* GL_NV_representative_fragment_test */
+
 #ifndef GL_NV_sRGB_formats
 #define GL_NV_sRGB_formats 1
 #define GL_SLUMINANCE_NV                  0x8C46
@@ -3735,6 +3935,28 @@
 #define GL_NV_sample_mask_override_coverage 1
 #endif /* GL_NV_sample_mask_override_coverage */
 
+#ifndef GL_NV_scissor_exclusive
+#define GL_NV_scissor_exclusive 1
+#define GL_SCISSOR_TEST_EXCLUSIVE_NV 0x9555
+#define GL_SCISSOR_BOX_EXCLUSIVE_NV 0x9556
+typedef void(GL_APIENTRYP PFNGLSCISSOREXCLUSIVENVPROC)(GLint x,
+                                                       GLint y,
+                                                       GLsizei width,
+                                                       GLsizei height);
+typedef void(GL_APIENTRYP PFNGLSCISSOREXCLUSIVEARRAYVNVPROC)(GLuint first,
+                                                             GLsizei count,
+                                                             const GLint* v);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glScissorExclusiveNV(GLint x,
+                                                 GLint y,
+                                                 GLsizei width,
+                                                 GLsizei height);
+GL_APICALL void GL_APIENTRY glScissorExclusiveArrayvNV(GLuint first,
+                                                       GLsizei count,
+                                                       const GLint* v);
+#endif
+#endif /* GL_NV_scissor_exclusive */
+
 #ifndef GL_NV_shader_atomic_fp16_vector
 #define GL_NV_shader_atomic_fp16_vector 1
 #endif /* GL_NV_shader_atomic_fp16_vector */
@@ -3743,6 +3965,82 @@
 #define GL_NV_shader_noperspective_interpolation 1
 #endif /* GL_NV_shader_noperspective_interpolation */
 
+#ifndef GL_NV_shader_subgroup_partitioned
+#define GL_NV_shader_subgroup_partitioned 1
+#define GL_SUBGROUP_FEATURE_PARTITIONED_BIT_NV 0x00000100
+#endif /* GL_NV_shader_subgroup_partitioned */
+
+#ifndef GL_NV_shader_texture_footprint
+#define GL_NV_shader_texture_footprint 1
+#endif /* GL_NV_shader_texture_footprint */
+
+#ifndef GL_NV_shading_rate_image
+#define GL_NV_shading_rate_image 1
+#define GL_SHADING_RATE_IMAGE_NV 0x9563
+#define GL_SHADING_RATE_NO_INVOCATIONS_NV 0x9564
+#define GL_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV 0x9565
+#define GL_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV 0x9566
+#define GL_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV 0x9567
+#define GL_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV 0x9568
+#define GL_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV 0x9569
+#define GL_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV 0x956A
+#define GL_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV 0x956B
+#define GL_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV 0x956C
+#define GL_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV 0x956D
+#define GL_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV 0x956E
+#define GL_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV 0x956F
+#define GL_SHADING_RATE_IMAGE_BINDING_NV 0x955B
+#define GL_SHADING_RATE_IMAGE_TEXEL_WIDTH_NV 0x955C
+#define GL_SHADING_RATE_IMAGE_TEXEL_HEIGHT_NV 0x955D
+#define GL_SHADING_RATE_IMAGE_PALETTE_SIZE_NV 0x955E
+#define GL_MAX_COARSE_FRAGMENT_SAMPLES_NV 0x955F
+#define GL_SHADING_RATE_SAMPLE_ORDER_DEFAULT_NV 0x95AE
+#define GL_SHADING_RATE_SAMPLE_ORDER_PIXEL_MAJOR_NV 0x95AF
+#define GL_SHADING_RATE_SAMPLE_ORDER_SAMPLE_MAJOR_NV 0x95B0
+typedef void(GL_APIENTRYP PFNGLBINDSHADINGRATEIMAGENVPROC)(GLuint texture);
+typedef void(GL_APIENTRYP PFNGLGETSHADINGRATEIMAGEPALETTENVPROC)(
+    GLuint viewport,
+    GLuint entry,
+    GLenum* rate);
+typedef void(GL_APIENTRYP PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC)(
+    GLenum rate,
+    GLuint samples,
+    GLuint index,
+    GLint* location);
+typedef void(GL_APIENTRYP PFNGLSHADINGRATEIMAGEBARRIERNVPROC)(
+    GLboolean synchronize);
+typedef void(GL_APIENTRYP PFNGLSHADINGRATEIMAGEPALETTENVPROC)(
+    GLuint viewport,
+    GLuint first,
+    GLsizei count,
+    const GLenum* rates);
+typedef void(GL_APIENTRYP PFNGLSHADINGRATESAMPLEORDERNVPROC)(GLenum order);
+typedef void(GL_APIENTRYP PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC)(
+    GLenum rate,
+    GLuint samples,
+    const GLint* locations);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glBindShadingRateImageNV(GLuint texture);
+GL_APICALL void GL_APIENTRY glGetShadingRateImagePaletteNV(GLuint viewport,
+                                                           GLuint entry,
+                                                           GLenum* rate);
+GL_APICALL void GL_APIENTRY glGetShadingRateSampleLocationivNV(GLenum rate,
+                                                               GLuint samples,
+                                                               GLuint index,
+                                                               GLint* location);
+GL_APICALL void GL_APIENTRY glShadingRateImageBarrierNV(GLboolean synchronize);
+GL_APICALL void GL_APIENTRY glShadingRateImagePaletteNV(GLuint viewport,
+                                                        GLuint first,
+                                                        GLsizei count,
+                                                        const GLenum* rates);
+GL_APICALL void GL_APIENTRY glShadingRateSampleOrderNV(GLenum order);
+GL_APICALL void GL_APIENTRY
+glShadingRateSampleOrderCustomNV(GLenum rate,
+                                 GLuint samples,
+                                 const GLint* locations);
+#endif
+#endif /* GL_NV_shading_rate_image */
+
 #ifndef GL_NV_shadow_samplers_array
 #define GL_NV_shadow_samplers_array 1
 #define GL_SAMPLER_2D_ARRAY_SHADOW_NV     0x8DC4
@@ -3853,6 +4151,10 @@
 #endif
 #endif /* GL_OVR_multiview_multisampled_render_to_texture */
 
+#ifndef GL_QCOM_YUV_texture_gather
+#define GL_QCOM_YUV_texture_gather 1
+#endif /* GL_QCOM_YUV_texture_gather */
+
 #ifndef GL_QCOM_alpha_test
 #define GL_QCOM_alpha_test 1
 #define GL_ALPHA_TEST_QCOM                0x0BC0
@@ -3959,6 +4261,10 @@
 #endif
 #endif /* GL_QCOM_shader_framebuffer_fetch_noncoherent */
 
+#ifndef GL_QCOM_shader_framebuffer_fetch_rate
+#define GL_QCOM_shader_framebuffer_fetch_rate 1
+#endif /* GL_QCOM_shader_framebuffer_fetch_rate */
+
 #ifndef GL_QCOM_texture_foveated
 #define GL_QCOM_texture_foveated 1
 #define GL_TEXTURE_FOVEATED_FEATURE_BITS_QCOM 0x8BFB
@@ -3987,6 +4293,12 @@
 #endif
 #endif /* GL_QCOM_texture_foveated */
 
+#ifndef GL_QCOM_texture_foveated_subsampled_layout
+#define GL_QCOM_texture_foveated_subsampled_layout 1
+#define GL_FOVEATION_SUBSAMPLED_LAYOUT_METHOD_BIT_QCOM 0x00000004
+#define GL_MAX_SHADER_SUBSAMPLED_IMAGE_UNITS_QCOM 0x8FA1
+#endif /* GL_QCOM_texture_foveated_subsampled_layout */
+
 #ifndef GL_QCOM_tiled_rendering
 #define GL_QCOM_tiled_rendering 1
 #define GL_COLOR_BUFFER_BIT0_QCOM         0x00000001
diff --git a/third_party/leveldatabase/env_chromium.cc b/third_party/leveldatabase/env_chromium.cc
index ce44be2..c9db48d3 100644
--- a/third_party/leveldatabase/env_chromium.cc
+++ b/third_party/leveldatabase/env_chromium.cc
@@ -1699,6 +1699,11 @@
   return leveldb::Slice(s.begin(), s.size());
 }
 
+leveldb::Slice MakeSlice(base::span<const uint8_t> s) {
+  return MakeSlice(
+      base::StringPiece(reinterpret_cast<const char*>(s.data()), s.size()));
+}
+
 }  // namespace leveldb_env
 
 namespace leveldb {
diff --git a/third_party/leveldatabase/env_chromium.h b/third_party/leveldatabase/env_chromium.h
index a7027578..988393a 100644
--- a/third_party/leveldatabase/env_chromium.h
+++ b/third_party/leveldatabase/env_chromium.h
@@ -372,6 +372,7 @@
 
 LEVELDB_EXPORT base::StringPiece MakeStringPiece(const leveldb::Slice& s);
 LEVELDB_EXPORT leveldb::Slice MakeSlice(const base::StringPiece& s);
+LEVELDB_EXPORT leveldb::Slice MakeSlice(base::span<const uint8_t> s);
 
 }  // namespace leveldb_env
 
diff --git a/third_party/re2/BUILD.gn b/third_party/re2/BUILD.gn
index b96f754..faf546f 100644
--- a/third_party/re2/BUILD.gn
+++ b/third_party/re2/BUILD.gn
@@ -34,6 +34,8 @@
     "src/re2/set.cc",
     "src/re2/set.h",
     "src/re2/simplify.cc",
+    "src/re2/sparse_array.h",
+    "src/re2/sparse_set.h",
     "src/re2/stringpiece.cc",
     "src/re2/stringpiece.h",
     "src/re2/tostring.cc",
@@ -47,8 +49,6 @@
     "src/util/mix.h",
     "src/util/mutex.h",
     "src/util/rune.cc",
-    "src/util/sparse_array.h",
-    "src/util/sparse_set.h",
     "src/util/strutil.cc",
     "src/util/strutil.h",
     "src/util/utf.h",
diff --git a/third_party/re2/README.chromium b/third_party/re2/README.chromium
index 730859a..1b70458 100644
--- a/third_party/re2/README.chromium
+++ b/third_party/re2/README.chromium
@@ -1,8 +1,8 @@
 Name: re2 - an efficient, principled regular expression library
 Short Name: re2
 URL: https://github.com/google/re2
-Version: db20d46c05900a12539225fe800dd860b14e0061
-Date: 2017-02-22
+Version: ab12219ba56a47200385673446b5d371548c25db
+Date: 2019-10-15
 License: BSD 3-Clause
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/webrtc_overrides/BUILD.gn b/third_party/webrtc_overrides/BUILD.gn
index 4483d30..1189cea 100644
--- a/third_party/webrtc_overrides/BUILD.gn
+++ b/third_party/webrtc_overrides/BUILD.gn
@@ -40,7 +40,7 @@
   public_configs = [ "//third_party/webrtc:common_inherited_config" ]
 }
 
-static_library("init_webrtc") {
+source_set("init_webrtc") {
   sources = [
     "init_webrtc.cc",
     "init_webrtc.h",
@@ -67,7 +67,7 @@
   ]
 }
 
-static_library("metrics") {
+source_set("metrics") {
   sources = [
     "metrics.cc",
   ]
@@ -76,7 +76,7 @@
   ]
 }
 
-static_library("field_trial") {
+source_set("field_trial") {
   sources = [
     "field_trial.cc",
   ]
diff --git a/tools/android/checkstyle/chromium-style-5.0.xml b/tools/android/checkstyle/chromium-style-5.0.xml
index 2f2a6d31..5962538 100644
--- a/tools/android/checkstyle/chromium-style-5.0.xml
+++ b/tools/android/checkstyle/chromium-style-5.0.xml
@@ -145,11 +145,12 @@
       <property name="message" value="Please name the StrictModeContext variable &quot;ignored&quot; (crbug.com/767058#c4)."/>
     </module>
     <module name="RegexpSinglelineJava">
+      <!-- Tests are also allowed to use System.exit if need be, please add dirs to tools/android/checkstyle/suppressions.xml -->
       <property name="id" value="SystemExitCheck"/>
       <property name="severity" value="error"/>
-      <property name="format" value="System\.exit\([^0]"/>
+      <property name="format" value="System\.exit\("/>
       <property name="ignoreComments" value="true"/>
-      <property name="message" value="Throw an exception instead of calling System#exit with a non-zero exit status (crbug.com/1000651)."/>
+      <property name="message" value="Throw an exception instead of calling System#exit (crbug.com/1000651)."/>
     </module>
   </module>
 </module>
diff --git a/tools/android/checkstyle/suppressions.xml b/tools/android/checkstyle/suppressions.xml
index b42993b..d0abc67ef 100644
--- a/tools/android/checkstyle/suppressions.xml
+++ b/tools/android/checkstyle/suppressions.xml
@@ -15,6 +15,6 @@
   <suppress id="MethodNameCheck" files="FirstRunIntegrationUnitTest.java"/>
   <suppress id="MethodNameCheck" files="SplashActivityTest.java"/>
   <!-- Third-party libraries, test infrastructure, build, and tooling can use
-       System#exit freely. -->
-  <suppress id="SystemExitCheck" files="src/(build|testing|third_party|tools)/"/>
+       System#exit freely. Feel free to add other test directories here. -->
+  <suppress id="SystemExitCheck" files="src/(build|testing|third_party|tools|base/test)/"/>
 </suppressions>
diff --git a/tools/android/pagecontroller/OWNERS b/tools/android/pagecontroller/OWNERS
new file mode 100644
index 0000000..d992c646
--- /dev/null
+++ b/tools/android/pagecontroller/OWNERS
@@ -0,0 +1 @@
+file://chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/OWNERS
diff --git a/tools/android/pagecontroller/search_strings.py b/tools/android/pagecontroller/search_strings.py
new file mode 100755
index 0000000..8739ee9d
--- /dev/null
+++ b/tools/android/pagecontroller/search_strings.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Finds R.string.* ids in android_chrome_sstrings.grd file."""
+
+import argparse
+import os
+import re
+import sys
+import xml.etree.cElementTree as ElementTree
+
+_SRC_DIR = os.path.abspath(os.path.join(
+      os.path.dirname(__file__), '..', '..', '..'))
+
+_DEFAULT_GRD = os.path.join(_SRC_DIR,
+    'chrome/android/java/strings/android_chrome_strings.grd')
+_MATCH_MSG = u'  R.string.{}:\n    Text = "{}"\n    Desc = "{}"'
+
+class GrdSearch(object):
+  """ Search strings.grd file. """
+  def __init__(self, grd_path):
+    grd_dom = ElementTree.parse(grd_path)
+    messages = grd_dom.findall('.//message')
+    self.message_lookup = {}
+    for message in messages:
+      self.message_lookup[message.get('name')] = {
+          'text': message.itertext().next().strip(),
+          'desc': message.get('desc')}
+
+  def find_term_in_grd(self, term, is_regex):
+    """ Returns matches for term in the form (string id, text, desc) """
+    results = []
+    for name,value in self.message_lookup.iteritems():
+      if ((not is_regex and value['text'] == term) or
+          (is_regex and re.match(term, value['text']))):
+        results.append((name[4:].lower(), value['text'], value['desc']))
+    return results
+
+def main():
+  parser = argparse.ArgumentParser(description='Find clank string resource ids'
+                                   ' based on string content.')
+  parser.add_argument('-r', '--regex', action='store_true',
+                      help='perform regex search instead of literal')
+  parser.add_argument('-g', '--grd-file',
+                      default=_DEFAULT_GRD,
+                      help='strings.grd file, default: {}'.format(_DEFAULT_GRD))
+  parser.add_argument('terms', nargs='+',
+                      help='Search terms.')
+
+  args = parser.parse_args(sys.argv[1:])
+
+  searcher = GrdSearch(args.grd_file)
+
+  for t in args.terms:
+    print('{} search term: "{}", R.string.* matches:'.format(
+        ('Regex' if args.regex else 'Literal'), t))
+    for name, text, desc, in searcher.find_term_in_grd(t.decode('utf-8'),
+                                                       args.regex):
+      print(_MATCH_MSG.format(name, text, desc))
+    print('')
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/binary_size/libsupersize/caspian/caspian_web.cc b/tools/binary_size/libsupersize/caspian/caspian_web.cc
new file mode 100644
index 0000000..3bccad1
--- /dev/null
+++ b/tools/binary_size/libsupersize/caspian/caspian_web.cc
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Command-line interface for checking the integrity of .size files.
+// Intended to be called from WebAssembly code.
+
+#include <stdlib.h>
+
+#include "tools/binary_size/libsupersize/caspian/file_format.h"
+#include "tools/binary_size/libsupersize/caspian/model.h"
+
+caspian::SizeInfo info;
+
+extern "C" {
+bool LoadSizeFile(const char* compressed, size_t size) {
+  caspian::ParseSizeInfo(compressed, size, &info);
+  return true;
+}
+}
diff --git a/tools/binary_size/libsupersize/start_server.py b/tools/binary_size/libsupersize/start_server.py
index a253d4e..88ffefa9 100644
--- a/tools/binary_size/libsupersize/start_server.py
+++ b/tools/binary_size/libsupersize/start_server.py
@@ -44,6 +44,7 @@
 
   SupersizeHTTPRequestHandler.serve_from = static_files
   SupersizeHTTPRequestHandler.data_file_path = args.report_file
+  SupersizeHTTPRequestHandler.extensions_map['.wasm'] = 'application/wasm'
   httpd = BaseHTTPServer.HTTPServer(server_addr, SupersizeHTTPRequestHandler)
 
   sa = httpd.socket.getsockname()
diff --git a/tools/binary_size/libsupersize/static/start-worker.js b/tools/binary_size/libsupersize/static/start-worker.js
index 1b98f42..6c8680a 100644
--- a/tools/binary_size/libsupersize/static/start-worker.js
+++ b/tools/binary_size/libsupersize/static/start-worker.js
@@ -5,7 +5,14 @@
 // @ts-check
 'use strict';
 
-const _innerWorker = new Worker('tree-worker.js');
+let _innerWorker = null;
+const urlParams = new URLSearchParams(window.location.search);
+if (urlParams.get('wasm')) {
+  console.log("wasm=1; Using WebAssembly web worker");
+  _innerWorker = new Worker('tree-worker-wasm.js');
+} else {
+  _innerWorker = new Worker('tree-worker.js');
+}
 
 /**
  * We use a worker to keep large tree creation logic off the UI thread.
diff --git a/tools/binary_size/libsupersize/static/tree-worker-wasm.js b/tools/binary_size/libsupersize/static/tree-worker-wasm.js
new file mode 100644
index 0000000..3baf830
--- /dev/null
+++ b/tools/binary_size/libsupersize/static/tree-worker-wasm.js
@@ -0,0 +1,237 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+importScripts('./shared.js');
+importScripts('./caspian_web.js');
+
+const _PATH_SEP = '/';
+const _NAMES_TO_FLAGS = Object.freeze({
+  hot: _FLAGS.HOT,
+  generated: _FLAGS.GENERATED_SOURCE,
+  coverage: _FLAGS.COVERAGE,
+  uncompressed: _FLAGS.UNCOMPRESSED,
+});
+
+
+/**
+ * Wrapper around fetch for requesting the same resource multiple times.
+ */
+class DataFetcher {
+  constructor(input) {
+    /** @type {AbortController | null} */
+    this._controller = null;
+    this.setInput(input);
+  }
+
+  /**
+   * Sets the input that describes what will be fetched. Also clears the cache.
+   * @param {string | Request} input URL to the resource you want to fetch.
+   */
+  setInput(input) {
+    if (typeof this._input === 'string' && this._input.startsWith('blob:')) {
+      // Revoke the previous Blob url to prevent memory leaks
+      URL.revokeObjectURL(this._input);
+    }
+
+    /** @type {Uint8Array | null} */
+    this._cache = null;
+    this._input = input;
+  }
+
+  /**
+   * Starts a new request and aborts the previous one.
+   * @param {string | Request} url
+   */
+  async fetch(url) {
+    if (this._controller) this._controller.abort();
+    this._controller = new AbortController();
+    const headers = new Headers();
+    headers.append('cache-control', 'no-cache');
+    return fetch(url, {
+      headers,
+      credentials: 'same-origin',
+      signal: this._controller.signal,
+    });
+  }
+
+  /**
+   * Yields binary chunks as Uint8Arrays. After a complete run, the bytes are
+   * cached and future calls will yield the cached Uint8Array instead.
+   */
+  async *arrayBufferStream() {
+    if (this._cache) {
+      yield this._cache;
+      return;
+    }
+
+    const response = await this.fetch(this._input);
+    let result;
+    // Use streams, if supported, so that we can show in-progress data instead
+    // of waiting for the entire data file to download. The file can be >100 MB,
+    // so streams ensure slow connections still see some data.
+    if (response.body) {
+      const reader = response.body.getReader();
+
+      /** @type {Uint8Array[]} Store received bytes to merge later */
+      let buffer = [];
+      /** Total size of received bytes */
+      let byteSize = 0;
+      while (true) {
+        // Read values from the stream
+        const {done, value} = await reader.read();
+        if (done) break;
+
+        const chunk = new Uint8Array(value, 0, value.length);
+        yield chunk;
+        buffer.push(chunk);
+        byteSize += chunk.length;
+      }
+
+      // We just cache a single typed array to save some memory and make future
+      // runs consistent with the no streams mode.
+      result = new Uint8Array(byteSize);
+      let i = 0;
+      for (const chunk of buffer) {
+        result.set(chunk, i);
+        i += chunk.length;
+      }
+    } else {
+      // In-memory version for browsers without stream support
+      yield result;
+    }
+
+    this._cache = result;
+  }
+
+  /**
+   * Outputs a single UInt8Array containing the entire input .size file.
+   */
+  async loadSizeBuffer() {
+    // Flush cache.
+    for await (const bytes of this.arrayBufferStream()) {
+      if (this._controller.signal.aborted) {
+        throw new DOMException('Request was aborted', 'AbortError');
+      }
+    }
+    return this._cache;
+  }
+}
+
+function mallocBuffer(buf) {
+  var dataPtr = Module._malloc(buf.byteLength);
+  var dataHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, buf.byteLength);
+  dataHeap.set(new Uint8Array(buf));
+  return dataHeap;
+}
+
+function freeBuffer(buf) {
+  Module._free(buf.byteOffset);
+}
+
+// Placeholder input name until supplied via setInput()
+const fetcher = new DataFetcher('data.size');
+
+async function buildTree(
+    groupBy, filterTest, highlightTest, methodCountMode, onProgress) {
+
+  let sizeBuffer = await fetcher.loadSizeBuffer();
+  let heapBuffer = mallocBuffer(sizeBuffer);
+  console.log('Passing ' + sizeBuffer.byteLength + ' bytes to WebAssembly');
+  let LoadSizeFile = Module.cwrap('LoadSizeFile', 'bool', ['number', 'number']);
+  let start_time = Date.now();
+  console.log(LoadSizeFile(heapBuffer.byteOffset, sizeBuffer.byteLength));
+  console.log('Loaded size file in ' + (Date.now() - start_time)/1000.0 + ' seconds');
+  freeBuffer(heapBuffer);
+}
+
+/**
+ * Parse the options represented as a query string, into an object.
+ * Includes checks for valid values.
+ * @param {string} options Query string
+ */
+function parseOptions(options) {
+  const params = new URLSearchParams(options);
+
+  const url = params.get('load_url');
+  const groupBy = params.get('group_by') || 'source_path';
+  const methodCountMode = params.has('method_count');
+  const filterGeneratedFiles = params.has('generated_filter');
+  const flagToHighlight = _NAMES_TO_FLAGS[params.get('highlight')];
+
+  function filterTest(symbolNode) {
+    return true;
+  }
+  function highlightTest(symbolNode) {
+    return false;
+  }
+  return {groupBy, filterTest, highlightTest, url, methodCountMode};
+}
+
+const actions = {
+  /** @param {{input:string|null,options:string}} param0 */
+  load({input, options}) {
+    const {groupBy, filterTest, highlightTest, url, methodCountMode} =
+        parseOptions(options);
+    if (input === 'from-url://' && url) {
+      // Display the data from the `load_url` query parameter
+      console.info('Displaying data from', url);
+      fetcher.setInput(url);
+    } else if (input != null) {
+      console.info('Displaying uploaded data');
+      fetcher.setInput(input);
+    }
+
+    return buildTree(
+        groupBy, filterTest, highlightTest, methodCountMode, progress => {
+          // @ts-ignore
+          self.postMessage(progress);
+        });
+  },
+  /** @param {string} path */
+  async open(path) {
+    if (!builder) throw new Error('Called open before load');
+    const node = builder.find(path);
+    return builder.formatNode(node);
+  },
+};
+
+/**
+ * Call the requested action function with the given data. If an error is thrown
+ * or rejected, post the error message to the UI thread.
+ * @param {number} id Unique message ID.
+ * @param {string} action Action type, corresponding to a key in `actions.`
+ * @param {any} data Data to supply to the action function.
+ */
+async function runAction(id, action, data) {
+  try {
+    const result = await actions[action](data);
+    // @ts-ignore
+    self.postMessage({id, result});
+  } catch (err) {
+    // @ts-ignore
+    self.postMessage({id, error: err.message});
+    throw err;
+  }
+}
+
+const runActionDebounced = debounce(runAction, 0);
+
+/**
+ * @param {MessageEvent} event Event for when this worker receives a message.
+ */
+self.onmessage = async event => {
+  const {id, action, data} = event.data;
+  if (action === 'load') {
+    // Loading large files will block the worker thread until complete or when
+    // an await statement is reached. During this time, multiple load messages
+    // can pile up due to filters being adjusted. We debounce the load call
+    // so that only the last message is read (the current set of filters).
+    runActionDebounced(id, action, data);
+  } else {
+    runAction(id, action, data);
+  }
+};
+
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index 79c3e27..60aa21e9 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -172,34 +172,37 @@
   "chrome/browser/resources/usb_internals/resources.grd": {
     "includes": [14030],
   },
+  "components/sync/driver/resources.grd": {
+    "includes": [14050],
+  },
   # END chrome/ WebUI resources section
 
   # START chrome/ miscellaneous section.
   "chrome/android/features/test_dummy/internal/resources/resources.grd": {
-    "includes": [14070],
+    "includes": [14090],
   },
   "chrome/common/common_resources.grd": {
-    "includes": [14160],
+    "includes": [14180],
   },
   "chrome/credential_provider/gaiacp/gaia_resources.grd": {
-    "includes": [14180],
-    "messages": [14190],
+    "includes": [14200],
+    "messages": [14210],
   },
   "chrome/renderer/resources/renderer_resources.grd": {
-    "includes": [14230],
-    "structures": [14340],
+    "includes": [14250],
+    "structures": [14360],
   },
   "chrome/test/data/webui_test_resources.grd": {
-    "includes": [14380],
+    "includes": [14400],
   },
   # END chrome/ miscellaneous section.
 
   # START chromeos/ section.
   "chromeos/chromeos_strings.grd": {
-    "messages": [14510],
+    "messages": [14530],
   },
   "chromeos/resources/chromeos_resources.grd": {
-    "includes": [14560],
+    "includes": [14580],
   },
   # END chromeos/ section.
 
@@ -208,27 +211,27 @@
   # We only use one file depending on whether we're building Chromium or
   # Google Chrome.
   "components/components_chromium_strings.grd": {
-    "messages": [15040],
+    "messages": [15060],
   },
   "components/components_google_chrome_strings.grd": {
-    "messages": [15040],
+    "messages": [15060],
   },
 
   "components/components_locale_settings.grd": {
-    "includes": [15060],
-    "messages": [15070],
+    "includes": [15080],
+    "messages": [15090],
   },
   "components/components_strings.grd": {
-    "messages": [15100],
+    "messages": [15120],
   },
   "components/omnibox/resources/omnibox_resources.grd": {
-    "includes": [17190],
+    "includes": [17210],
   },
   "components/policy/resources/policy_templates.grd": {
-    "structures": [17200],
+    "structures": [17220],
   },
   "components/resources/components_resources.grd": {
-    "includes": [17210],
+    "includes": [17230],
   },
   "components/resources/components_scaled_resources.grd": {
     "structures": [17400],
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 30c46ba..fb662fe2 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -651,6 +651,7 @@
     },
 
     'tryserver.chromium.android': {
+      'android-opus-kitkat-arm-rel': 'android_release_trybot',
       # TODO(crbug/597596): Switch this back to debug_trybot when cronet's
       # shared library loading is fixed.
       'android-cronet-arm-dbg': 'android_cronet_debug_static_bot_arm_no_neon',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 28ae171..721a1fe 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -6739,6 +6739,11 @@
   <int value="1" label="Registration is a duplicate"/>
 </enum>
 
+<enum name="BooleanReloaded">
+  <int value="0" label="Didn't reload"/>
+  <int value="1" label="Reloaded"/>
+</enum>
+
 <enum name="BooleanRemoved">
   <int value="0" label="Not removed"/>
   <int value="1" label="Removed"/>
@@ -7029,6 +7034,16 @@
   </details>
 </enum>
 
+<enum name="BottomSheet.StateChangeReason">
+  <int value="0" label="None"/>
+  <int value="1" label="Swipe"/>
+  <int value="2" label="Back press"/>
+  <int value="3" label="Tap scrim"/>
+  <int value="4" label="Navigation"/>
+  <int value="5" label="Composited UI"/>
+  <int value="6" label="VR"/>
+</enum>
+
 <enum name="BrokenAlternateProtocolLocation">
   <int value="0" label="HTTP_STREAM_FACTORY_JOB"/>
   <int value="1" label="QUIC_STREAM_FACTORY"/>
@@ -18319,6 +18334,7 @@
   <int value="628" label="DeviceLoginScreenScreenMagnifierType"/>
   <int value="629" label="CorsMitigationList"/>
   <int value="630" label="CorsLegacyModeEnabled"/>
+  <int value="631" label="ExternalPrintServersWhitelist"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">
@@ -35429,6 +35445,7 @@
   <int value="-1633586675" label="TabModalJsDialog:enabled"/>
   <int value="-1631329950" label="ssl-version-max"/>
   <int value="-1630419335" label="enable-download-notification"/>
+  <int value="-1626110605" label="WebBundles:disabled"/>
   <int value="-1625881240" label="FullscreenExitUI:disabled"/>
   <int value="-1625777277"
       label="AutofillUpstreamEditableCardholderName:disabled"/>
@@ -36090,7 +36107,6 @@
   <int value="-835331907" label="TabOutlinesInLowContrastThemes:disabled"/>
   <int value="-834661509" label="ModalPermissionPrompts:disabled"/>
   <int value="-832561975" label="enable-picture-in-picture"/>
-  <int value="-828612322" label="BundledHTTPExchanges:enabled"/>
   <int value="-825942229" label="tab-management-experiment-type-elderberry"/>
   <int value="-824199802" label="ContextualSearchSimplifiedServer:enabled"/>
   <int value="-823165021" label="MaterialDesignUserMenu:enabled"/>
@@ -36106,6 +36122,7 @@
   <int value="-810684526"
       label="AutofillToolkitViewsCreditCardDialogsMac:disabled"/>
   <int value="-808486493" label="NewWallpaperPicker:disabled"/>
+  <int value="-806480547" label="SyncDeviceInfoInTransportMode:enabled"/>
   <int value="-803233334" label="AutofillRefreshStyleAndroid:disabled"/>
   <int value="-802348444" label="disable-site-engagement-service"/>
   <int value="-799931058" label="UseMultiloginEndpoint:disabled"/>
@@ -36155,6 +36172,7 @@
       label="AssistantEnableMediaSessionIntegration:enabled"/>
   <int value="-747463111" label="ContentSuggestionsNotifications:disabled"/>
   <int value="-746328467" label="ExpensiveBackgroundTimerThrottling:disabled"/>
+  <int value="-745082968" label="SyncDeviceInfoInTransportMode:disabled"/>
   <int value="-744159181" label="disable-spdy-proxy-dev-auth-origin"/>
   <int value="-743590125" label="TabEngagementReportingAndroid:enabled"/>
   <int value="-743103250" label="enable-linkable-ephemeral-apps"/>
@@ -36777,9 +36795,11 @@
   <int value="79094339" label="VrLaunchIntents:enabled"/>
   <int value="79503461" label="disable-account-consistency"/>
   <int value="79595680" label="OmniboxTabSwitchSuggestions:enabled"/>
+  <int value="79729295" label="WebBundles:enabled"/>
   <int value="83422372"
       label="ChromeHomePersonalizedOmniboxSuggestions:enabled"/>
   <int value="84911198" label="ScanCardsInWebPayments:disabled"/>
+  <int value="88412508" label="EnableCrOSActionRecorder:enabled"/>
   <int value="88437020" label="FeaturePolicy:enabled"/>
   <int value="89785725"
       label="DataReductionProxyEnabledWithNetworkService:disabled"/>
@@ -36867,6 +36887,7 @@
   <int value="217455219" label="SyncStandaloneTransport:enabled"/>
   <int value="218890378" label="ManualSaving:disabled"/>
   <int value="219117936" label="AllowReaderForAccessibility:enabled"/>
+  <int value="220418174" label="EnableCrOSActionRecorder:disabled"/>
   <int value="221212638" label="StartSurfaceAndroid:enabled"/>
   <int value="222184258"
       label="AutofillEnforceMinRequiredFieldsForHeuristics:disabled"/>
@@ -38239,7 +38260,6 @@
   <int value="2049432745" label="VaapiWebPImageDecodeAcceleration:enabled"/>
   <int value="2050985807" label="AllowPopupsDuringPageUnload:enabled"/>
   <int value="2051403297" label="ShowBluetoothDeviceBattery:disabled"/>
-  <int value="2053273256" label="BundledHTTPExchanges:disabled"/>
   <int value="2056572020" label="EnableUsernameCorrection:disabled"/>
   <int value="2058148069" label="UseMessagesStagingUrl:enabled"/>
   <int value="2058283872" label="CCTModuleCache:disabled"/>
@@ -50280,6 +50300,7 @@
   <int value="22" label="DEFAULT_DPI"/>
   <int value="23" label="NON_DEFAULT_DPI"/>
   <int value="24" label="PIN"/>
+  <int value="25" label="FIT_TO_PAPER"/>
 </enum>
 
 <enum name="PrivetNotificationsEvent">
@@ -57448,21 +57469,26 @@
   <int value="65546" label="Custom | UndisplayableExtension"/>
 <!-- Common errors for ReadVersion operation: (2 shiftleft 16) + Windows error -->
 
+  <int value="131078" label="ReadVersion | ERROR_INVALID_HANDLE"/>
   <int value="131181" label="ReadVersion | ERROR_BROKEN_PIPE"/>
   <int value="131306" label="ReadVersion | ERROR_MORE_DATA"/>
 <!-- Common errors for ReadRequestLength operation: (3 shiftleft 16) + Windows error -->
 
+  <int value="196614" label="ReadRequestLength | ERROR_INVALID_HANDLE"/>
   <int value="196717" label="ReadRequestLength | ERROR_BROKEN_PIPE"/>
   <int value="196842" label="ReadRequestLength | ERROR_MORE_DATA"/>
 <!-- Common errors for ReadRequest operation: (4 shiftleft 16) + Windows error -->
 
+  <int value="262150" label="ReadRequest | ERROR_INVALID_HANDLE"/>
   <int value="262253" label="ReadRequest | ERROR_BROKEN_PIPE"/>
   <int value="262378" label="ReadRequest | ERROR_MORE_DATA"/>
 <!-- Common error for WriteResponse operation: (5 shiftleft 16) + Windows error -->
 
+  <int value="327686" label="WriteResponse | ERROR_INVALID_HANDLE"/>
   <int value="327789" label="WriteResponse | ERROR_BROKEN_PIPE"/>
 <!-- Common error for WriteResponseLength operation: (6 shiftleft 16) + Windows error -->
 
+  <int value="393222" label="WriteResponseLength | ERROR_INVALID_HANDLE"/>
   <int value="393325" label="WriteResponseLength | ERROR_BROKEN_PIPE"/>
 </enum>
 
@@ -63469,6 +63495,7 @@
   <int value="0" label="Omnibox"/>
   <int value="1" label="NTP"/>
   <int value="2" label="Search widget"/>
+  <int value="3" label="Tasks surface"/>
 </enum>
 
 <enum name="VPNDriver">
@@ -63989,6 +64016,18 @@
   <int value="15" label="Install icon in the Omnibox"/>
 </enum>
 
+<enum name="WebAppManifestUpdateResult">
+  <int value="0" label="No app in scope"/>
+  <int value="1" label="Updating throttled"/>
+  <int value="2" label="WebContents destroyed"/>
+  <int value="3" label="App uninstalled during update"/>
+  <int value="4" label="App is placeholder"/>
+  <int value="5" label="App is up to date"/>
+  <int value="6" label="App manifest invalid"/>
+  <int value="7" label="App update failed"/>
+  <int value="8" label="App updated"/>
+</enum>
+
 <enum name="WebappUninstallDialogAction">
   <int value="0" label="Uninstall only"/>
   <int value="1" label="Uninstall and remove data"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 583ab920..9fe791b9 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -4050,6 +4050,31 @@
   <summary>Records ClearProxyOverride calls.</summary>
 </histogram>
 
+<histogram name="Android.WebView.CookieManager.SameSiteAttributeValue"
+    enum="CookieSameSiteString" expires_after="M90">
+  <owner>chlily@chromium.org</owner>
+  <owner>torne@chromium.org</owner>
+  <summary>
+    The value of the cookie's SameSite attribute, if any. This is logged once
+    per creation of a cookie via the CookieManager API. These cookies are double
+    counted (once from CookieManager, logged in this histogram, and once from
+    CanonicalCookie::Create, logged in Cookie.SameSiteAttributeValue).
+  </summary>
+</histogram>
+
+<histogram name="Android.WebView.CookieManager.SameSiteNoneIsSecure"
+    enum="Boolean" expires_after="M90">
+  <owner>chlily@chromium.org</owner>
+  <owner>torne@chromium.org</owner>
+  <summary>
+    If a cookie was SameSite=None, this records whether or not it was Secure.
+    Logged once per attempt to set a SameSite=None cookie via CookieManager API.
+    These cookies are double counted (once from CookieManager, logged in this
+    histogram, and once from CookieMonster::SetCanonicalCookie, logged in
+    Cookie.SameSiteNoneIsSecure).
+  </summary>
+</histogram>
+
 <histogram name="Android.WebView.Gfx.FunctorStencilEnabled"
     enum="BooleanEnabled" expires_after="M78">
   <owner>boliu@chromium.org</owner>
@@ -4082,6 +4107,10 @@
 </histogram>
 
 <histogram name="Android.WebView.LoadUrl.DataUriHasOctothorpe" enum="Boolean">
+  <obsolete>
+    Removed 2019/10/11. This histogram was never well understood and the
+    underlying feature has been shipped for some time anyway.
+  </obsolete>
   <owner>smcgruer@chromium.org</owner>
   <summary>
     Records if a data url passed to loadUrl had a '#' character. This is to be
@@ -48268,7 +48297,7 @@
 </histogram>
 
 <histogram name="FileBrowser.TeamDrivesCount" units="Team Drives"
-    expires_after="M79">
+    expires_after="M89">
   <owner>slangley@chromium.org</owner>
   <owner>weifangsun@chromium.org</owner>
   <summary>
@@ -67763,6 +67792,19 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Memory.GPU.PeakMemoryUsage" units="KB">
+<!-- Name completed by histogram_suffixes name="GPU.PeakMemoryUsage" -->
+
+  <owner>graphics-dev@chromium.org</owner>
+  <owner>jonross@chromium.org</owner>
+  <owner>sadrul@chromium.org</owner>
+  <summary>
+    The maximum amount of memory of the GPU process, when one of a tab switch,
+    page load or scroll occurs. Currently only tab switching has been
+    implemented.
+  </summary>
+</histogram>
+
 <histogram name="Memory.Gpu.PrivateMemoryFootprint" units="MB"
     expires_after="never">
 <!-- expires-never: guiding metric (internal: go/chrome-browser-guiding-metrics) -->
@@ -78548,6 +78590,34 @@
   </summary>
 </histogram>
 
+<histogram name="Net.KeepaliveRequest.HeadersSize" units="bytes"
+    expires_after="2020-06-30">
+  <owner>yhirano@chromium.org</owner>
+  <owner>panicker@chromium.org</owner>
+  <summary>
+    The size of the request headers for each request with keepalive specified.
+  </summary>
+</histogram>
+
+<histogram name="Net.KeepaliveRequest.UrlPlusHeadersSize" units="bytes"
+    expires_after="2020-06-30">
+  <owner>yhirano@chromium.org</owner>
+  <owner>panicker@chromium.org</owner>
+  <summary>
+    The sum of the size of the request headers and destination URL for each
+    request with keepalive specified.
+  </summary>
+</histogram>
+
+<histogram name="Net.KeepaliveRequest.UrlSize" units="bytes"
+    expires_after="2020-06-30">
+  <owner>yhirano@chromium.org</owner>
+  <owner>panicker@chromium.org</owner>
+  <summary>
+    The size of the destination URL for each request with keepalive specified.
+  </summary>
+</histogram>
+
 <histogram name="Net.KeepaliveStatisticsRecorder.PeakInflightRequests"
     units="requests" expires_after="2018-03-07">
   <obsolete>
@@ -95838,8 +95908,8 @@
   </summary>
 </histogram>
 
-<histogram name="OptimizationGuide.HintCache.HasHint.AtCommit" units="counts"
-    expires_after="M85">
+<histogram name="OptimizationGuide.HintCache.HasHint.AtCommit"
+    units="BooleanAvailable" expires_after="M85">
   <owner>dougarnett@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -95849,7 +95919,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.HintCache.HasHint.BeforeCommit"
-    units="counts" expires_after="M85">
+    units="BooleanAvailable" expires_after="M85">
   <owner>dougarnett@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -95869,8 +95939,8 @@
   </summary>
 </histogram>
 
-<histogram name="OptimizationGuide.HintCache.HostMatch.AtCommit" units="counts"
-    expires_after="M85">
+<histogram name="OptimizationGuide.HintCache.HostMatch.AtCommit"
+    units="BooleanMatched" expires_after="M85">
   <owner>dougarnett@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -95882,8 +95952,8 @@
   </summary>
 </histogram>
 
-<histogram name="OptimizationGuide.HintCache.PageMatch.AtCommit" units="counts"
-    expires_after="M85">
+<histogram name="OptimizationGuide.HintCache.PageMatch.AtCommit"
+    units="BooleanMatched" expires_after="M85">
   <owner>dougarnett@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -97429,6 +97499,31 @@
   </summary>
 </histogram>
 
+<histogram name="PageLoad.Clients.Ads.HeavyAds.FrameRemovedPriorToPageEnd"
+    enum="BooleanRemoved" expires_after="2020-10-08">
+  <owner>johnidel@chromium.org</owner>
+  <owner>jkarlin@chromium.org</owner>
+  <summary>
+    Records whether a heavy ad frame was deleted prior to the page being
+    destroyed/navigated. Recorded per-frame, only if the frame is considered a
+    heavy ad, when the frame is destroyed. For example, this is true if the
+    iframe element is deleted by javascript. This is recorded whether or not the
+    ads are actually unloaded by the intervention.
+  </summary>
+</histogram>
+
+<histogram name="PageLoad.Clients.Ads.HeavyAds.UserDidReload"
+    enum="BooleanReloaded" expires_after="2020-10-08">
+  <owner>johnidel@chromium.org</owner>
+  <owner>jkarlin@chromium.org</owner>
+  <summary>
+    Records whether a page with a heavy ad is reloaded or navigated/destroyed
+    for any other reason. Only recorded for pages that had at least one heavy ad
+    prior to being navigated/reloaded/destroyed. This is recorded whether or not
+    the ads are actually unloaded by the intervention.
+  </summary>
+</histogram>
+
 <histogram name="PageLoad.Clients.Ads.Resources.Bytes.Ads" units="KB"
     expires_after="2019-09-05">
   <obsolete>
@@ -101369,7 +101464,10 @@
 </histogram>
 
 <histogram name="PasswordManager.AccountsPerSite" units="units"
-    expires_after="2020-03-22">
+    expires_after="M79">
+  <obsolete>
+    Deprecated in M79 in favor of PasswordManager.AccountsPerSiteHiRes.
+  </obsolete>
   <owner>dvadym@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -101378,6 +101476,17 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="PasswordManager.AccountsPerSiteHiRes"
+    units="units" expires_after="2020-03-22">
+  <owner>dvadym@chromium.org</owner>
+  <owner>vasilii@chromium.org</owner>
+  <summary>
+    The number of accounts stored per site in the password manager (one event
+    per site), split by whether created by the user or generated by Chrome, and
+    further by whether the user used sync with custom passphrase or not.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.AccountsReusingPassword" units="accounts"
     expires_after="M81">
   <owner>dvadym@chromium.org</owner>
@@ -103659,14 +103768,15 @@
   </summary>
 </histogram>
 
-<histogram name="PasswordManager.TimesPasswordUsed" units="units"
+<histogram base="true" name="PasswordManager.TimesPasswordUsed" units="units"
     expires_after="M81">
   <owner>battre@chromium.org</owner>
   <owner>dvadym@chromium.org</owner>
   <summary>
-    The number of times each saved password has been used to log in. Does not
-    include generated passwords. Recorded by iterating over stored passwords
-    once per run. This information is persisted and synced.
+    The number of times each saved password has been used to log in. Split by
+    whether created by the user or generated by Chrome, and further by whether
+    the user used sync with custom passphrase or not. Recorded by iterating over
+    stored passwords once per run. This information is persisted and synced.
   </summary>
 </histogram>
 
@@ -103708,6 +103818,26 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.TouchToFill.CredentialIndex" units="index"
+    expires_after="2020-09-30">
+  <owner>jdoerrie@chromium.org</owner>
+  <owner>fhorschig@chromium.org</owner>
+  <summary>
+    The index of a selected credential in the Touch To Fill sheet. Only recorded
+    when the sheet showed at least two entries.
+  </summary>
+</histogram>
+
+<histogram name="PasswordManager.TouchToFill.DismissalReason"
+    enum="BottomSheet.StateChangeReason" expires_after="2020-09-30">
+  <owner>jdoerrie@chromium.org</owner>
+  <owner>fhorschig@chromium.org</owner>
+  <summary>
+    The reason why a user dismissed the Touch To Fill sheet. Recorded once for
+    each dismissal.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.UIDismissalReason"
     enum="PasswordManagerUIDismissalReason" expires_after="2020-03-22">
   <owner>vasilii@chromium.org</owner>
@@ -126134,6 +126264,24 @@
   </summary>
 </histogram>
 
+<histogram name="Scheduling.Renderer.DrawIntervalWithCustomPropertyAnimations2"
+    units="microseconds" expires_after="2020-10-01">
+  <owner>animations-dev@chromium.org</owner>
+  <summary>
+    The time delta between the draw times of back-to-back BeginImplFrames,
+    regardless of whether or not they result in a swap, when there is at least
+    one custom property animation.
+
+    The interval is only recorded when every BeginImplFrame wants to draw.
+
+    Warning: This metric may include reports from clients with low-resolution
+    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
+    will cause this metric to have an abnormal distribution. When considering
+    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
+    solution.
+  </summary>
+</histogram>
+
 <histogram name="Scheduling.Renderer.DrawIntervalWithMainThreadAnimations2"
     units="microseconds" expires_after="2020-02-16">
   <owner>paint-dev@chromium.org</owner>
@@ -131635,6 +131783,16 @@
   </summary>
 </histogram>
 
+<histogram name="Session.WebStates.ReadFromFileTime" units="ms"
+    expires_after="2020-06-30">
+  <owner>justincohen@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <summary>
+    The time needed to read iOS session webState data from a file. It's recorded
+    every time a webState is restored, typically on app startup.
+  </summary>
+</histogram>
+
 <histogram name="Session.WebStates.SerializedSize" units="KB"
     expires_after="2020-06-30">
   <owner>justincohen@chromium.org</owner>
@@ -131646,6 +131804,17 @@
   </summary>
 </histogram>
 
+<histogram name="Session.WebStates.WriteToFileTime" units="ms"
+    expires_after="2020-06-30">
+  <owner>justincohen@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <summary>
+    The time needed to write iOS session webState data to a file. It's recorded
+    every time the file is written to storage, which happens on page loads, tab
+    changes and app backgrounding.
+  </summary>
+</histogram>
+
 <histogram name="SessionCrashed.Bubble" enum="SessionCrashedBubbleUserAction"
     expires_after="M77">
   <owner>yiyaoliu@chromium.org</owner>
@@ -133558,6 +133727,8 @@
 </histogram>
 
 <histogram name="Sharing.MessageAckTime" units="ms" expires_after="2020-02-02">
+<!-- Name completed by histogram_suffixes name="SharingMessage" -->
+
   <owner>mvanouwerkerk@chromium.org</owner>
   <owner>knollr@chromium.org</owner>
   <summary>
@@ -133568,6 +133739,8 @@
 
 <histogram name="Sharing.MessageReceivedType" enum="SharingMessageType"
     expires_after="2020-02-02">
+<!-- Name completed by histogram_suffixes name="SharingMessage" -->
+
   <owner>mvanouwerkerk@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -133579,6 +133752,8 @@
 
 <histogram name="Sharing.SendAckMessageResult" enum="SharingSendMessageResult"
     expires_after="M80">
+<!-- Name completed by histogram_suffixes name="SharingMessage" -->
+
   <owner>alexchau@chromium.org</owner>
   <owner>knollr@chromium.org</owner>
   <summary>
@@ -133602,6 +133777,8 @@
 
 <histogram name="Sharing.SendMessageResult" enum="SharingSendMessageResult"
     expires_after="M80">
+<!-- Name completed by histogram_suffixes name="SharingMessage" -->
+
   <owner>alexchau@chromium.org</owner>
   <owner>knollr@chromium.org</owner>
   <summary>
@@ -153943,7 +154120,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Liftoff (V8's baseline compiler for WebAssembly) bails out if it hits the
     first unsupported feature in a function. The function is recompiled with
@@ -154483,7 +154660,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Time to execute a single WebAssembly code GC, measured from when it is
     triggered until all isolates reported live code and all dead code was freed.
@@ -154496,7 +154673,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Time to compile a WebAssembly function. Recorded on each compilation of a
     single function, either synchronous, asynchronous, or lazily.
@@ -154513,7 +154690,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Peak memory used to compile a WebAssembly function. Recorded for each
     TurboFan compilation of a WebAssembly function.
@@ -154524,7 +154701,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Time to asynchronously compile a WebAssembly module (using the
     'WebAssembly.compile' API). Recorded on each asynchronous WebAssembly
@@ -154536,7 +154713,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Time to compile a WebAssembly module. Recorded on each synchronous
     WebAssembly compilation.
@@ -154553,7 +154730,7 @@
     units="microseconds" expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Time to asynchronously compile a WebAssembly module using streaming
     compilation (via the 'WebAssembly.compileStreaming' API). Recorded on each
@@ -154567,7 +154744,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Time to decode a WebAssembly function. Recorded on each validation of a
     WebAssembly function.
@@ -154584,7 +154761,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Time to decode a WebAssembly module. Recorded for each WebAssembly module
     which is decoded for validation, compilation, or deserialization.
@@ -154601,7 +154778,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Peak memory used to decode a WebAssembly module. Recorded for each
     WebAssembly module which is decoded for validation, compilation, or
@@ -154612,7 +154789,7 @@
 <histogram name="V8.WasmDeserializeModuleStreamingMicroSeconds"
     units="microseconds" expires_after="2020-03-31">
   <owner>bbudge@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <summary>
     Time to deserialize a WebAssembly module during streaming compilation (via
@@ -154645,7 +154822,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Size of a WebAssembly function in bytes. Recorded on each compilation of a
     single function, either synchronous, asynchronous, or lazily.
@@ -154656,7 +154833,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Function count per WebAssembly module. Recorded for each WebAssembly module
     which is decoded for validation, compilation, or deserialization.
@@ -154667,7 +154844,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Time to instantiate a WebAssembly module. Recorded on each instantiation of
     a WebAssembly module.
@@ -154684,7 +154861,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <owner>aseemgarg@chromium.org</owner>
   <summary>
     Time for lazy compilation of WebAssembly functions. This is recorded per
@@ -154702,7 +154879,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <owner>aseemgarg@chromium.org</owner>
   <summary>
     Throughput of compilation of lazily compiled WebAssembly functions in KB/s
@@ -154748,7 +154925,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     The amount of generated code in MiB for one WebAssembly module. Recorded
     when baseline compilation finished.
@@ -154759,7 +154936,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     The amount of WebAssembly code freed by garbage collection, in MiB. Recorded
     for each live module after each full GC.
@@ -154770,7 +154947,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     The amount of committed code space in MiB used by individual WebAssembly
     modules. Recorded for each live module after each full GC.
@@ -154781,7 +154958,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     The percentage of WebAssembly code freed by garbage collection (ratio of
     total freed code to total generated code so far). Recorded for each live
@@ -154793,7 +154970,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     The amount of generated code in MiB for one WebAssembly module. Recorded
     when top-tier compilation finished.
@@ -154804,7 +154981,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Number of code GCs triggered per WebAssembly module. Recorded after each
     code GC.
@@ -154815,7 +154992,7 @@
     expires_after="2020-03-31">
   <owner>mstarzinger@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     The number of code spaces (i.e. individual reservations) of a WebAssembly
     module. Recorded on every code space allocation.
@@ -154838,7 +155015,7 @@
     expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
-  <owner>clemensh@chromium.org</owner>
+  <owner>clemensb@chromium.org</owner>
   <summary>
     Time to tier-up a WebAssembly module, i.e. the time between baseline
     compilation finishes and top-tier compilation finishes. Recorded whenever an
@@ -156895,6 +157072,17 @@
   </summary>
 </histogram>
 
+<histogram name="WebApp.Icon.HttpStatusCodeClassOnUpdate"
+    enum="HttpStatusCodeClass" expires_after="2021-01-01">
+  <owner>alancutter@chromium.org</owner>
+  <owner>raymes@chromium.org</owner>
+  <owner>loyso@chromium.org</owner>
+  <summary>
+    The HTTP status code class returned for each icon loaded when updating a
+    WebApp. Recorded when WebAppDataRetriever starts downloading icons.
+  </summary>
+</histogram>
+
 <histogram name="Webapp.Install.DisplayMode" enum="WebAppDisplayMode"
     expires_after="M87">
   <owner>peter@chromium.org</owner>
@@ -157072,6 +157260,19 @@
   </summary>
 </histogram>
 
+<histogram name="Webapp.Update.ManifestUpdateResult"
+    enum="WebAppManifestUpdateResult" expires_after="M89">
+  <owner>alancutter@chromium.org</owner>
+  <owner>loyso@chromium.org</owner>
+  <owner>raymes@chromium.org</owner>
+  <summary>
+    Records the result of web app manifest update checks during page
+    navigations. Note that &quot;No app in scope&quot; results are ignored due
+    to the expected several orders of magnitude higher volume than all other
+    update results.
+  </summary>
+</histogram>
+
 <histogram name="WebAudio.AudioBuffer.Length" units="frames"
     expires_after="M81">
   <owner>rtoy@chromium.org</owner>
@@ -169493,6 +169694,13 @@
   <affected-histogram name="GPU.ContextMemory.WebGL"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="GPU.PeakMemoryUsage" separator=".">
+  <suffix name="ChangeTab" label="Changing Tabs."/>
+  <suffix name="PageLoad" label="Page Load."/>
+  <suffix name="Scroll" label="Scroll."/>
+  <affected-histogram name="Memory.GPU.PeakMemoryUsage"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="GPU.ProtectedVideoType" separator=".">
   <suffix name="Clear" label="Clear"/>
   <suffix name="HardwareProtected" label="HardwareProtected"/>
@@ -171398,9 +171606,13 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="Mobile.Messages.Type" separator=".">
-  <suffix name="InfobarTypeConfirm" label=""/>
-  <suffix name="InfobarTypePasswordSave" label=""/>
-  <suffix name="InfobarTypePasswordUpdate" label=""/>
+  <suffix name="InfobarTypeConfirm" label="Recorded for Confirm Messages."/>
+  <suffix name="InfobarTypePasswordSave"
+      label="Recorded for Save Password Messages."/>
+  <suffix name="InfobarTypePasswordUpdate"
+      label="Recorded for Update Password Messages."/>
+  <suffix name="InfobarTypeSaveCard" label="Recorded for Save Card Messages."/>
+  <affected-histogram name="Mobile.Messages.Badge.Tapped"/>
   <affected-histogram name="Mobile.Messages.Banner.Dismiss"/>
   <affected-histogram name="Mobile.Messages.Banner.Event"/>
   <affected-histogram name="Mobile.Messages.Modal.Event"/>
@@ -175140,10 +175352,15 @@
   <affected-histogram name="PasswordManager.AccountsPerSite"/>
   <affected-histogram name="PasswordManager.AccountsPerSite.AutoGenerated"/>
   <affected-histogram name="PasswordManager.AccountsPerSite.UserCreated"/>
+  <affected-histogram
+      name="PasswordManager.AccountsPerSiteHiRes.AutoGenerated"/>
+  <affected-histogram name="PasswordManager.AccountsPerSiteHiRes.Overall"/>
+  <affected-histogram name="PasswordManager.AccountsPerSiteHiRes.UserCreated"/>
   <affected-histogram name="PasswordManager.BlacklistedSites"/>
   <affected-histogram name="PasswordManager.BlacklistedSitesHiRes"/>
   <affected-histogram name="PasswordManager.TimesGeneratedPasswordUsed"/>
   <affected-histogram name="PasswordManager.TimesPasswordUsed.AutoGenerated"/>
+  <affected-histogram name="PasswordManager.TimesPasswordUsed.Overall"/>
   <affected-histogram name="PasswordManager.TimesPasswordUsed.UserCreated"/>
   <affected-histogram name="PasswordManager.TotalAccounts.AutoGenerated"/>
   <affected-histogram name="PasswordManager.TotalAccounts.UserCreated"/>
@@ -175159,6 +175376,7 @@
   <suffix base="true" name="Overall" label=""/>
   <suffix base="true" name="UserCreated" label=""/>
   <affected-histogram name="PasswordManager.AccountsPerSite"/>
+  <affected-histogram name="PasswordManager.AccountsPerSiteHiRes"/>
   <affected-histogram name="PasswordManager.TimesPasswordUsed"/>
   <affected-histogram name="PasswordManager.TotalAccounts"/>
   <affected-histogram name="PasswordManager.TotalAccountsHiRes.ByType"/>
@@ -178397,6 +178615,18 @@
   <affected-histogram name="Sharing.ClickToCallSelectedDeviceIndex"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="SharingMessage" separator=".">
+  <suffix name="ACK_MESSAGE" label="Ack Message"/>
+  <suffix name="CLICK_TO_CALL_MESSAGE" label="Click To Call Message"/>
+  <suffix name="PING_MESSAGE" label="Ping Message"/>
+  <suffix name="SHARED_CLIPBOARD_MESSAGE" label="Shared Clipboard Message"/>
+  <suffix name="UNKNOWN_MESSAGE" label="Unknown Message"/>
+  <affected-histogram name="Sharing.MessageAckTime"/>
+  <affected-histogram name="Sharing.MessageReceivedType"/>
+  <affected-histogram name="Sharing.SendAckMessageResult"/>
+  <affected-histogram name="Sharing.SendMessageResult"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="ShillCumulativeTimeOnline" separator=".">
   <suffix name="Any" label="Any connection type"/>
   <suffix name="Cellular" label="Cellular connection"/>
diff --git a/tools/perf/benchmarks/blink_perf_unittest.py b/tools/perf/benchmarks/blink_perf_unittest.py
index d3ca94d..3128d33 100644
--- a/tools/perf/benchmarks/blink_perf_unittest.py
+++ b/tools/perf/benchmarks/blink_perf_unittest.py
@@ -1,176 +1,135 @@
 # 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.
+from __future__ import division
+
 import os
-import shutil
-import tempfile
 import unittest
 
 from telemetry import decorators
 from telemetry import story
-from telemetry.testing import options_for_unittests
-from telemetry.testing import page_test_test_case
+from telemetry.testing import legacy_page_test_case
 from telemetry.timeline import async_slice
 from telemetry.timeline import model as model_module
 
-
 from benchmarks import blink_perf
 
 
-class BlinkPerfTest(page_test_test_case.PageTestTestCase):
-  _BLINK_PERF_TEST_DATA_DIR = os.path.join(os.path.dirname(__file__),
-      '..', '..', '..', 'third_party', 'blink', 'perf_tests',
-      'test_data')
+_BLINK_PERF_TEST_DATA_DIR = os.path.join(os.path.dirname(__file__),
+    '..', '..', '..', 'third_party', 'blink', 'perf_tests', 'test_data')
+_BLINK_PERF_RESOURCES_DIR = os.path.join(os.path.dirname(__file__),
+    '..', '..', '..', 'third_party', 'blink', 'perf_tests', 'resources')
 
-  _BLINK_PERF_RESOURCES_DIR = os.path.join(os.path.dirname(__file__),
-      '..', '..', '..', 'third_party', 'blink', 'perf_tests',
-      'resources')
+
+def _Mean(values):
+  return sum(values) / len(values)
+
+
+class BlinkPerfTest(legacy_page_test_case.LegacyPageTestCase):
   def setUp(self):
-    self._options = options_for_unittests.GetRunOptions(
-        output_dir=tempfile.mkdtemp())
+    super(BlinkPerfTest, self).setUp()
     # pylint: disable=protected-access
-    self._measurement = blink_perf._BlinkPerfMeasurement()
+    self.blink_page_test = blink_perf._BlinkPerfMeasurement()
     # pylint: enable=protected-access
 
-  def tearDown(self):
-    shutil.rmtree(self._options.output_dir)
-
-  def _CreateStorySetForTestFile(self, test_file_name):
-    story_set = story.StorySet(base_dir=self._BLINK_PERF_TEST_DATA_DIR,
-        serving_dirs={self._BLINK_PERF_TEST_DATA_DIR,
-                      self._BLINK_PERF_RESOURCES_DIR})
-    # pylint: disable=protected-access
-    page = blink_perf._BlinkPerfPage('file://' + test_file_name, story_set,
-        base_dir=story_set.base_dir, name=test_file_name)
-    # pylint: enable=protected-access
-    story_set.AddStory(page)
+  @staticmethod
+  def CreateStorySetForTest(url):
+    story_set = story.StorySet(
+        base_dir=_BLINK_PERF_TEST_DATA_DIR,
+        serving_dirs=[_BLINK_PERF_TEST_DATA_DIR, _BLINK_PERF_RESOURCES_DIR])
+    assert url.startswith('file://'), 'Expected local URI, got %s' % url
+    blink_page = blink_perf._BlinkPerfPage(  # pylint: disable=protected-access
+        url, story_set, base_dir=story_set.base_dir, name=url[len('file://'):])
+    story_set.AddStory(blink_page)
     return story_set
 
   def testBlinkPerfTracingMetricsForMeasureTime(self):
-    results = self.RunMeasurement(
-        self._measurement,
-        self._CreateStorySetForTestFile('append-child-measure-time.html'),
-        run_options=self._options)
-    self.assertFalse(results.had_failures)
-    self.assertEquals(len(list(results.IterRunsWithTraces())), 1)
+    measurements = self.RunPageTest(
+        self.blink_page_test, 'file://append-child-measure-time.html')
+    self.assertIn('trace.html', self.test_result['outputArtifacts'])
 
-    frame_view_layouts = results.FindAllPageSpecificValuesNamed(
-        'LocalFrameView::layout')
-    self.assertEquals(len(frame_view_layouts), 1)
+    frame_view_layouts = measurements['LocalFrameView::layout']['samples']
     # append-child-measure-time.html specifies 5 iterationCount.
-    self.assertEquals(len(frame_view_layouts[0].values), 5)
-    self.assertGreater(frame_view_layouts[0].mean, 0.001)
+    self.assertEquals(len(frame_view_layouts), 5)
+    self.assertGreater(_Mean(frame_view_layouts), 0.001)
 
-    update_layout_trees = results.FindAllPageSpecificValuesNamed(
-        'UpdateLayoutTree')
-    self.assertEquals(len(update_layout_trees), 1)
+    update_layout_trees = measurements['UpdateLayoutTree']['samples']
     # append-child-measure-time.html specifies 5 iterationCount.
-    self.assertEquals(len(update_layout_trees[0].values), 5)
-    self.assertGreater(update_layout_trees[0].mean, 0.001)
+    self.assertEquals(len(update_layout_trees), 5)
+    self.assertGreater(_Mean(update_layout_trees), 0.001)
 
   def testBlinkPerfTracingMetricsForMeasureFrameTime(self):
-    results = self.RunMeasurement(
-        self._measurement,
-        self._CreateStorySetForTestFile(
-            'color-changes-measure-frame-time.html'),
-        run_options=self._options)
-    self.assertFalse(results.had_failures)
-    self.assertEquals(len(list(results.IterRunsWithTraces())), 1)
+    measurements = self.RunPageTest(
+        self.blink_page_test, 'file://color-changes-measure-frame-time.html')
+    self.assertIn('trace.html', self.test_result['outputArtifacts'])
 
-    frame_view_prepaints = results.FindAllPageSpecificValuesNamed(
-        'LocalFrameView::RunPrePaintLifecyclePhase')
+    frame_view_prepaints = measurements[
+        'LocalFrameView::RunPrePaintLifecyclePhase']['samples']
 
-    self.assertEquals(len(frame_view_prepaints), 1)
     # color-changes-measure-frame-time.html specifies 9 iterationCount.
-    self.assertEquals(len(frame_view_prepaints[0].values), 9)
-    self.assertGreater(frame_view_prepaints[0].mean, 0.001)
+    self.assertEquals(len(frame_view_prepaints), 9)
+    self.assertGreater(_Mean(frame_view_prepaints), 0.001)
 
-    frame_view_painttrees = results.FindAllPageSpecificValuesNamed(
-        'LocalFrameView::RunPaintLifecyclePhase')
-    self.assertEquals(len(frame_view_painttrees), 1)
+    frame_view_painttrees = measurements[
+        'LocalFrameView::RunPaintLifecyclePhase']['samples']
     # color-changes-measure-frame-time.html specifies 9 iterationCount.
-    self.assertEquals(len(frame_view_painttrees[0].values), 9)
-    self.assertGreater(frame_view_painttrees[0].mean, 0.001)
+    self.assertEquals(len(frame_view_painttrees), 9)
+    self.assertGreater(_Mean(frame_view_painttrees), 0.001)
 
   def testBlinkPerfTracingMetricsForMeasurePageLoadTime(self):
-    results = self.RunMeasurement(
-        self._measurement,
-        self._CreateStorySetForTestFile(
-            'simple-html-measure-page-load-time.html'),
-        run_options=self._options)
-    self.assertFalse(results.had_failures)
-    self.assertEquals(len(list(results.IterRunsWithTraces())), 1)
+    measurements = self.RunPageTest(
+        self.blink_page_test, 'file://simple-html-measure-page-load-time.html')
+    self.assertIn('trace.html', self.test_result['outputArtifacts'])
 
-    create_child_frame = results.FindAllPageSpecificValuesNamed(
-        'WebLocalFrameImpl::createChildframe')
-    self.assertEquals(len(create_child_frame), 1)
+    create_child_frame = measurements[
+        'WebLocalFrameImpl::createChildframe']['samples']
     # color-changes-measure-frame-time.html specifies 7 iterationCount.
-    self.assertEquals(len(create_child_frame[0].values), 7)
-    self.assertGreater(create_child_frame[0].mean, 0.001)
+    self.assertEquals(len(create_child_frame), 7)
+    self.assertGreater(_Mean(create_child_frame), 0.001)
 
-    post_layout_task = results.FindAllPageSpecificValuesNamed(
-        'LocalFrameView::performPostLayoutTasks')
-    self.assertEquals(len(post_layout_task), 1)
+    post_layout_task = measurements[
+        'LocalFrameView::performPostLayoutTasks']['samples']
     # color-changes-measure-frame-time.html specifies 7 iterationCount.
-    self.assertEquals(len(post_layout_task[0].values), 7)
-    self.assertGreater(post_layout_task[0].mean, 0.001)
-
+    self.assertEquals(len(post_layout_task), 7)
+    self.assertGreater(_Mean(post_layout_task), 0.001)
 
   @decorators.Disabled('mac')  # Flaky on mac: crbug.com/960554
   def testBlinkPerfTracingMetricsForMeasureAsync(self):
-    results = self.RunMeasurement(
-        self._measurement,
-        self._CreateStorySetForTestFile('simple-blob-measure-async.html'),
-        run_options=self._options)
-    self.assertFalse(results.had_failures)
-    self.assertEquals(len(list(results.IterRunsWithTraces())), 1)
+    measurements = self.RunPageTest(
+        self.blink_page_test, 'file://simple-blob-measure-async.html')
+    self.assertIn('trace.html', self.test_result['outputArtifacts'])
 
-    blob_requests = results.FindAllPageSpecificValuesNamed(
-        'BlobRequest')
-    blob_readers = results.FindAllPageSpecificValuesNamed(
-        'BlobReader')
-    self.assertEquals(len(blob_requests), 1)
-    self.assertEquals(len(blob_readers), 1)
+    blob_requests = measurements['BlobRequest']['samples']
+    blob_readers = measurements['BlobReader']['samples']
     # simple-blob-measure-async.html specifies 6 iterationCount.
-    self.assertEquals(len(blob_requests[0].values), 6)
-    self.assertEquals(len(blob_readers[0].values), 6)
+    self.assertEquals(len(blob_requests), 6)
+    self.assertEquals(len(blob_readers), 6)
 
     # TODO(mek): Delete non-mojo code paths when blobs are always using mojo.
-    using_mojo = blob_readers[0].mean > 0.001
+    using_mojo = _Mean(blob_readers) > 0.001
     if using_mojo:
-      self.assertEquals(blob_requests[0].mean, 0)
-      self.assertGreater(blob_readers[0].mean, 0.001)
+      self.assertEquals(_Mean(blob_requests), 0)
+      self.assertGreater(_Mean(blob_readers), 0.001)
     else:
-      self.assertGreater(blob_requests[0].mean, 0.001)
-      self.assertEquals(blob_readers[0].mean, 0)
+      self.assertGreater(_Mean(blob_requests), 0.001)
+      self.assertEquals(_Mean(blob_readers), 0)
 
     if using_mojo:
-      read_data = results.FindAllPageSpecificValuesNamed(
-          'BlobReader::ReadMore')
+      read_data = measurements['BlobReader::ReadMore']['samples']
     else:
-      read_data = results.FindAllPageSpecificValuesNamed(
-          'BlobRequest::ReadRawData')
-    self.assertEquals(len(read_data), 1)
+      read_data = measurements['BlobRequest::ReadRawData']['samples']
     # simple-blob-measure-async.html specifies 6 iterationCount.
-    self.assertEquals(len(read_data[0].values), 6)
-    self.assertGreater(read_data[0].mean, 0.001)
+    self.assertEquals(len(read_data), 6)
+    self.assertGreater(_Mean(read_data), 0.001)
 
   def testBlinkPerfLifecycleMethods(self):
-    results = self.RunMeasurement(
-        self._measurement,
-        self._CreateStorySetForTestFile('lifecycle-methods.html'),
-        run_options=self._options)
-    self.assertFalse(results.had_failures)
-    self.assertEquals(len(list(results.IterRunsWithTraces())), 0)
+    self.RunPageTest(self.blink_page_test, 'file://lifecycle-methods.html')
+    self.assertNotIn('trace.html', self.test_result['outputArtifacts'])
 
   def testExtraChromeCategories(self):
-    self._options.extra_chrome_categories = 'cc,blink'
-    results = self.RunMeasurement(
-        self._measurement,
-        self._CreateStorySetForTestFile('lifecycle-methods.html'),
-        run_options=self._options)
-    self.assertFalse(results.had_failures)
-    self.assertEquals(len(list(results.IterRunsWithTraces())), 1)
+    self.options.extra_chrome_categories = 'cc,blink'
+    self.RunPageTest(self.blink_page_test, 'file://lifecycle-methods.html')
+    self.assertIn('trace.html', self.test_result['outputArtifacts'])
 
 
 # pylint: disable=protected-access
diff --git a/tools/perf/benchmarks/startup_mobile.py b/tools/perf/benchmarks/startup_mobile.py
index e372479..acb11b97 100644
--- a/tools/perf/benchmarks/startup_mobile.py
+++ b/tools/perf/benchmarks/startup_mobile.py
@@ -127,7 +127,8 @@
     self.platform.StartActivity(
         intent.Intent(package=self._possible_browser.settings.package,
                       activity=self._possible_browser.settings.activity,
-                      action=None, data=url),
+                      data=url,
+                      action='android.intent.action.VIEW'),
         blocking=True)
 
   def LaunchCCT(self, url):
@@ -140,20 +141,21 @@
     self.platform.StartActivity(
         intent.Intent(package=self._possible_browser.settings.package,
                       activity=self._possible_browser.settings.activity,
-                      action=None, data=url, extras=cct_extras),
+                      data=url, extras=cct_extras,
+                      action='android.intent.action.VIEW'),
         blocking=True)
 
   def LaunchMapsPwa(self):
     # Launches a bound webapk. The APK should be installed by the shared state
     # constructor. Upon launch, Chrome extracts the icon and the URL from the
     # APK.
+    self.platform.WaitForBatteryTemperature(_MAX_BATTERY_TEMP)
     self.platform.StartActivity(
         intent.Intent(package='org.chromium.maps_go_webapk',
                       activity='org.chromium.webapk.shell_apk.MainActivity',
                       category='android.intent.category.LAUNCHER',
                       action='android.intent.action.MAIN'),
         blocking=True)
-    self.platform.WaitForBatteryTemperature(_MAX_BATTERY_TEMP)
 
   @contextlib.contextmanager
   def FindBrowser(self):
diff --git a/tools/perf/benchmarks/system_health_smoke_test.py b/tools/perf/benchmarks/system_health_smoke_test.py
index ab8a0099..e025326f 100644
--- a/tools/perf/benchmarks/system_health_smoke_test.py
+++ b/tools/perf/benchmarks/system_health_smoke_test.py
@@ -43,6 +43,7 @@
   # crbug.com/878390 - These stories are already covered by their 2018 versions
   # and will later be removed.
   'system_health.memory_mobile/browse:tech:discourse_infinite_scroll',
+  'system_health.memory_mobile/browse:shopping:amazon',
   'system_health.memory_mobile/browse:social:facebook_infinite_scroll',
   'system_health.memory_mobile/browse:social:instagram',
   'system_health.memory_mobile/browse:news:reddit',
@@ -170,6 +171,10 @@
   'system_health.memory_desktop/browse:tools:sheets:2019',
   'system_health.memory_desktop/browse:tools:maps:2019',
 
+  # crbug.com/1014661
+  'system_health.memory_desktop/browse:social:tumblr_infinite_scroll:2018'
+  'system_health.memory_desktop/browse:search:google_india:2018'
+
   # The following tests are disabled because they are disabled on the perf
   # waterfall (using tools/perf/expectations.config) on one platform or another.
   # They may run fine on the CQ, but it isn't worth the bot time to run them.
diff --git a/tools/perf/contrib/cluster_telemetry/screenshot_unittest.py b/tools/perf/contrib/cluster_telemetry/screenshot_unittest.py
index eb01ca8..0be5157 100644
--- a/tools/perf/contrib/cluster_telemetry/screenshot_unittest.py
+++ b/tools/perf/contrib/cluster_telemetry/screenshot_unittest.py
@@ -3,41 +3,27 @@
 # found in the LICENSE file.
 
 import os
-import shutil
-import tempfile
 
 from telemetry import decorators
-from telemetry.testing import options_for_unittests
-from telemetry.testing import page_test_test_case
+from telemetry.testing import legacy_page_test_case
 from telemetry.util import image_util
 from contrib.cluster_telemetry import screenshot
 
-class ScreenshotUnitTest(page_test_test_case.PageTestTestCase):
 
-  def setUp(self):
-    self._png_outdir = tempfile.mkdtemp('_png_test')
-    self._options = options_for_unittests.GetRunOptions(
-        output_dir=self._png_outdir)
-
-  def tearDown(self):
-    shutil.rmtree(self._png_outdir)
-
+class ScreenshotUnitTest(legacy_page_test_case.LegacyPageTestCase):
   @decorators.Enabled('linux')
   def testScreenshot(self):
     # Screenshots for Cluster Telemetry purposes currently only supported on
     # Linux platform.
-    story_set = self.CreateStorySetFromFileInUnittestDataDir(
-      'screenshot_test.html')
-    measurement = screenshot.Screenshot(self._png_outdir)
-    self.RunMeasurement(measurement, story_set, run_options=self._options)
+    screenshot_test = screenshot.Screenshot(self.options.output_dir)
+    self.RunPageTest(screenshot_test, 'file://screenshot_test.html')
 
-    path = os.path.join(
-        self._png_outdir, story_set.stories[0].file_safe_name + '.png')
-    self.assertTrue(os.path.exists(path))
-    self.assertTrue(os.path.isfile(path))
-    self.assertTrue(os.access(path, os.R_OK))
+    filepath = os.path.join(self.options.output_dir, 'screenshot_test.png')
+    self.assertTrue(os.path.exists(filepath))
+    self.assertTrue(os.path.isfile(filepath))
+    self.assertTrue(os.access(filepath, os.R_OK))
 
-    image = image_util.FromPngFile(path)
+    image = image_util.FromPngFile(filepath)
     screenshot_pixels = image_util.Pixels(image)
     special_colored_pixel = bytearray([217, 115, 43])
     self.assertTrue(special_colored_pixel in screenshot_pixels)
diff --git a/tools/perf/core/minidump_unittest.py b/tools/perf/core/minidump_unittest.py
index 52dfbac..045245c 100644
--- a/tools/perf/core/minidump_unittest.py
+++ b/tools/perf/core/minidump_unittest.py
@@ -17,9 +17,9 @@
 
 class BrowserMinidumpTest(tab_test_case.TabTestCase):
   @decorators.Isolated
-  # ChromeOS and Android are currently hard coded to return None for minidump
-  # paths, so disable on those platforms.
-  @decorators.Disabled('android')
+  # Android is currently hard coded to return None for minidump paths.
+  # Flakes on chromeos: crbug.com/1014754
+  @decorators.Disabled('android', 'chromeos')
   def testSymbolizeMinidump(self):
     # Wait for the browser to restart fully before crashing
     self._LoadPageThenWait('var sam = "car";', 'sam')
@@ -92,7 +92,8 @@
     self.assertTrue(crash_function in sections[4] or match is not None)
 
   @decorators.Isolated
-  @decorators.Disabled('android')
+  # Flakes on chromeos: crbug.com/1014754
+  @decorators.Disabled('android', 'chromeos')
   def testMultipleCrashMinidumps(self):
     # Wait for the browser to restart fully before crashing
     self._LoadPageThenWait('var cat = "dog";', 'cat')
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 10680db..9abc10c 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -297,6 +297,7 @@
 crbug.com/958422 [ win7 ] system_health.common_desktop/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
 crbug.com/1008028 [ desktop ] system_health.common_desktop/browse:news:hackernews:2018 [ Skip ]
 crbug.com/1009838 [ mac ] system_health.common_desktop/browse:tools:maps:2019 [ Skip ]
+crbug.com/1008001 [ win ] system_health.common_desktop/browse:tools:sheets:2019 [ Skip ]
 
 # Benchmark: system_health.common_mobile
 crbug.com/1013317 [ android ] system_health.common_mobile/load:news:nytimes [ Skip ]
@@ -348,6 +349,7 @@
 crbug.com/959418 [ chromeos ] system_health.memory_desktop/long_running:tools:gmail-foreground [ Skip ]
 crbug.com/1008028 [ desktop ] system_health.memory_desktop/browse:news:hackernews:2018 [ Skip ]
 crbug.com/1009838 [ mac ] system_health.memory_desktop/browse:tools:maps:2019 [ Skip ]
+crbug.com/1008001 [ win ] system_health.memory_desktop/browse:tools:sheets:2019 [ Skip ]
 
 # Benchmark: system_health.memory_mobile
 crbug.com/1013317 [ android ] system_health.memory_mobile/load:news:nytimes [ Skip ]
@@ -405,16 +407,16 @@
 crbug.com/958507 [ desktop ] v8.browsing_desktop/browse:media:imgur [ Skip ]
 crbug.com/1008028 [ desktop ] v8.browsing_desktop/browse:news:hackernews:2018 [ Skip ]
 crbug.com/1009838 [ mac ] v8.browsing_desktop/browse:tools:maps:2019 [ Skip ]
+crbug.com/1008001 [ win ] v8.browsing_desktop/browse:tools:sheets:2019 [ Skip ]
 
 # Benchmark v8.browsing_desktop-future
 crbug.com/788796 [ linux ] v8.browsing_desktop-future/browse:media:imgur [ Skip ]
-crbug.com/953371 [ desktop ] v8.browsing_desktop-future/browse:social:twitter_infinite_scroll:2018 [ Skip ]
 crbug.com/773084 [ mac ] v8.browsing_desktop-future/browse:tools:maps [ Skip ]
 crbug.com/906654 v8.browsing_desktop-future/browse:search:google [ Skip ]
-crbug.com/953371 [ win ] v8.browsing_desktop-future/browse:social:twitter_infinite_scroll:2018 [ Skip ]
 crbug.com/958422 v8.browsing_desktop-future/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
 crbug.com/958507 [ desktop ] v8.browsing_desktop-future/browse:media:imgur [ Skip ]
 crbug.com/1008028 [ desktop ] v8.browsing_desktop-future/browse:news:hackernews:2018 [ Skip ]
+crbug.com/1008001 [ win ] v8.browsing_desktop-future/browse:tools:sheets:2019 [ Skip ]
 
 # Benchmark: v8.browsing_mobile
 crbug.com/958034 [ android-go android-webview ] v8.browsing_mobile/* [ Skip ]
diff --git a/tools/perf/measurements/multipage_skpicture_printer_unittest.py b/tools/perf/measurements/multipage_skpicture_printer_unittest.py
index bf20882..f2e89b0 100644
--- a/tools/perf/measurements/multipage_skpicture_printer_unittest.py
+++ b/tools/perf/measurements/multipage_skpicture_printer_unittest.py
@@ -2,30 +2,17 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import shutil
-import tempfile
-
 from telemetry import decorators
-from telemetry.testing import options_for_unittests
-from telemetry.testing import page_test_test_case
+from telemetry.testing import legacy_page_test_case
 
 from measurements import multipage_skpicture_printer
 
 
-class MultipageSkpicturePrinterUnitTest(page_test_test_case.PageTestTestCase):
-
-  def setUp(self):
-    self._mskp_outdir = tempfile.mkdtemp('_mskp_test')
-    self._options = options_for_unittests.GetRunOptions(
-        output_dir=self._mskp_outdir)
-
-  def tearDown(self):
-    shutil.rmtree(self._mskp_outdir)
-
+class MultipageSkpicturePrinterUnitTest(
+    legacy_page_test_case.LegacyPageTestCase):
   # Picture printing is not supported on all platforms.
   @decorators.Disabled('android', 'chromeos')
   def testSkpicturePrinter(self):
-    story_set = self.CreateStorySetFromFileInUnittestDataDir('blank.html')
-    measurement = multipage_skpicture_printer.MultipageSkpicturePrinter(
-        self._mskp_outdir)
-    self.RunMeasurement(measurement, story_set, run_options=self._options)
+    page_test = multipage_skpicture_printer.MultipageSkpicturePrinter(
+        self.options.output_dir)
+    self.RunPageTest(page_test, 'file://blank.html')
diff --git a/tools/perf/measurements/rasterize_and_record_micro_unittest.py b/tools/perf/measurements/rasterize_and_record_micro_unittest.py
index 330d2cf3b..c9ffa1c 100644
--- a/tools/perf/measurements/rasterize_and_record_micro_unittest.py
+++ b/tools/perf/measurements/rasterize_and_record_micro_unittest.py
@@ -2,20 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import logging
-import shutil
-import tempfile
-
 from telemetry import decorators
-from telemetry.page import legacy_page_test
-from telemetry.testing import options_for_unittests
-from telemetry.testing import page_test_test_case
-from telemetry.util import wpr_modes
+from telemetry.testing import legacy_page_test_case
 
 from measurements import rasterize_and_record_micro
 
 
-class RasterizeAndRecordMicroUnitTest(page_test_test_case.PageTestTestCase):
+class RasterizeAndRecordMicroUnitTest(legacy_page_test_case.LegacyPageTestCase):
   """Smoke test for rasterize_and_record_micro measurement
 
      Runs rasterize_and_record_micro measurement on a simple page and verifies
@@ -23,92 +16,33 @@
      i.e. it only checks if the metrics are present and non-zero.
   """
 
-  def setUp(self):
-    self._options = options_for_unittests.GetRunOptions(
-        output_dir=tempfile.mkdtemp())
-    self._options.browser_options.wpr_mode = wpr_modes.WPR_OFF
-
-  def tearDown(self):
-    shutil.rmtree(self._options.output_dir)
-
   @decorators.Disabled('win', 'chromeos', 'linux')
   def testRasterizeAndRecordMicro(self):
-    story_set = self.CreateStorySetFromFileInUnittestDataDir('blank.html')
-    measurement = rasterize_and_record_micro.RasterizeAndRecordMicro(
+    pate_test = rasterize_and_record_micro.RasterizeAndRecordMicro(
         rasterize_repeat=1, record_repeat=1, start_wait_time=0.0,
         report_detailed_results=True)
-    try:
-      results = self.RunMeasurement(
-          measurement, story_set, run_options=self._options)
-    except legacy_page_test.TestNotSupportedOnPlatformError as failure:
-      logging.warning(str(failure))
-      return
-    self.assertFalse(results.had_failures)
+    measurements = self.RunPageTest(pate_test, 'file://blank.html')
 
-    rasterize_time = results.FindAllPageSpecificValuesNamed('rasterize_time')
-    self.assertEquals(len(rasterize_time), 1)
-    self.assertGreater(rasterize_time[0].value, 0)
+    # For these measurements, a single positive scalar value is expected.
+    expected_positve_scalar = [
+        'rasterize_time',
+        'record_time',
+        'pixels_rasterized',
+        'pixels_recorded',
+        'pixels_rasterized_with_non_solid_color',
+        'pixels_rasterized_as_opaque',
+        'total_layers',
+        'total_picture_layers',
+        'total_picture_layers_with_no_content',
+        'painter_memory_usage',
+        'paint_op_memory_usage',
+        'paint_op_count',
+    ]
+    for name in expected_positve_scalar:
+      samples = measurements[name]['samples']
+      self.assertEqual(len(samples), 1)
+      self.assertGreater(samples[0], 0)
 
-    record_time = results.FindAllPageSpecificValuesNamed('record_time')
-    self.assertEquals(len(record_time), 1)
-    self.assertGreater(record_time[0].value, 0)
-
-    rasterized_pixels = results.FindAllPageSpecificValuesNamed(
-        'pixels_rasterized')
-    self.assertEquals(len(rasterized_pixels), 1)
-    self.assertGreater(rasterized_pixels[0].value, 0)
-
-    recorded_pixels = results.FindAllPageSpecificValuesNamed('pixels_recorded')
-    self.assertEquals(len(recorded_pixels), 1)
-    self.assertGreater(recorded_pixels[0].value, 0)
-
-    pixels_rasterized_with_non_solid_color = \
-        results.FindAllPageSpecificValuesNamed(
-            'pixels_rasterized_with_non_solid_color')
-    self.assertEquals(len(pixels_rasterized_with_non_solid_color), 1)
-    self.assertGreater(
-        pixels_rasterized_with_non_solid_color[0].value, 0)
-
-    pixels_rasterized_as_opaque = \
-        results.FindAllPageSpecificValuesNamed('pixels_rasterized_as_opaque')
-    self.assertEquals(len(pixels_rasterized_as_opaque), 1)
-    self.assertGreater(
-        pixels_rasterized_as_opaque[0].value, 0)
-
-    total_layers = results.FindAllPageSpecificValuesNamed('total_layers')
-    self.assertEquals(len(total_layers), 1)
-    self.assertGreater(total_layers[0].value, 0)
-
-    total_picture_layers = \
-        results.FindAllPageSpecificValuesNamed('total_picture_layers')
-    self.assertEquals(len(total_picture_layers), 1)
-    self.assertGreater(total_picture_layers[0].value, 0)
-
-    total_picture_layers_with_no_content = \
-        results.FindAllPageSpecificValuesNamed(
-            'total_picture_layers_with_no_content')
-    self.assertEquals(len(total_picture_layers_with_no_content), 1)
-    self.assertGreater(
-        total_picture_layers_with_no_content[0].value, 0)
-
-    total_picture_layers_off_screen = \
-        results.FindAllPageSpecificValuesNamed(
-            'total_picture_layers_off_screen')
-    self.assertEquals(len(total_picture_layers_off_screen), 1)
-    self.assertEqual(
-        total_picture_layers_off_screen[0].value, 0)
-
-    painter_memory_usage = results.FindAllPageSpecificValuesNamed(
-        'painter_memory_usage')
-    self.assertEquals(len(painter_memory_usage), 1)
-    self.assertGreater(painter_memory_usage[0].value, 0)
-
-    paint_op_memory_usage = results.FindAllPageSpecificValuesNamed(
-        'paint_op_memory_usage')
-    self.assertEquals(len(paint_op_memory_usage), 1)
-    self.assertGreater(paint_op_memory_usage[0].value, 0)
-
-    paint_op_count = results.FindAllPageSpecificValuesNamed(
-        'paint_op_count')
-    self.assertEquals(len(paint_op_count), 1)
-    self.assertGreater(paint_op_count[0].value, 0)
+    samples = measurements['total_picture_layers_off_screen']['samples']
+    self.assertEqual(len(samples), 1)
+    self.assertEqual(samples[0], 0)
diff --git a/tools/perf/measurements/skpicture_printer_unittest.py b/tools/perf/measurements/skpicture_printer_unittest.py
index 4203731..8f01445e 100644
--- a/tools/perf/measurements/skpicture_printer_unittest.py
+++ b/tools/perf/measurements/skpicture_printer_unittest.py
@@ -2,35 +2,18 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import shutil
-import tempfile
-
 from telemetry import decorators
-from telemetry.testing import options_for_unittests
-from telemetry.testing import page_test_test_case
+from telemetry.testing import legacy_page_test_case
 
 from measurements import skpicture_printer
 
 
-class SkpicturePrinterUnitTest(page_test_test_case.PageTestTestCase):
-
-  def setUp(self):
-    self._skp_outdir = tempfile.mkdtemp('_skp_test')
-    self._options = options_for_unittests.GetRunOptions(
-        output_dir=self._skp_outdir)
-
-  def tearDown(self):
-    shutil.rmtree(self._skp_outdir)
-
+class SkpicturePrinterUnitTest(legacy_page_test_case.LegacyPageTestCase):
   # Picture printing is not supported on all platforms.
   @decorators.Disabled('android', 'chromeos')
   def testSkpicturePrinter(self):
-    story_set = self.CreateStorySetFromFileInUnittestDataDir('blank.html')
-    measurement = skpicture_printer.SkpicturePrinter(self._skp_outdir)
-    results = self.RunMeasurement(
-        measurement, story_set, run_options=self._options)
-
-    saved_picture_count = results.FindAllPageSpecificValuesNamed(
-        'saved_picture_count')
+    page_test = skpicture_printer.SkpicturePrinter(self.options.output_dir)
+    measurements = self.RunPageTest(page_test, 'file://blank.html')
+    saved_picture_count = measurements['saved_picture_count']['samples']
     self.assertEquals(len(saved_picture_count), 1)
-    self.assertGreater(saved_picture_count[0].value, 0)
+    self.assertGreater(saved_picture_count[0], 0)
diff --git a/tools/perf/page_sets/data/system_health_mobile.json b/tools/perf/page_sets/data/system_health_mobile.json
index f09b4d1..4ab41b35 100644
--- a/tools/perf/page_sets/data/system_health_mobile.json
+++ b/tools/perf/page_sets/data/system_health_mobile.json
@@ -84,6 +84,9 @@
         "browse:shopping:amazon": {
             "DEFAULT": "system_health_mobile_053.wprgo"
         },
+        "browse:shopping:amazon:2019": {
+            "DEFAULT": "system_health_mobile_5f3fefb816.wprgo"
+        },
         "browse:shopping:avito": {
             "DEFAULT": "system_health_mobile_053.wprgo"
         },
diff --git a/tools/perf/page_sets/data/system_health_mobile_5f3fefb816.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_5f3fefb816.wprgo.sha1
new file mode 100644
index 0000000..c2238dd
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_5f3fefb816.wprgo.sha1
@@ -0,0 +1 @@
+5f3fefb81618081a3e6c01e291e8fab68cee1232
\ No newline at end of file
diff --git a/tools/perf/page_sets/jetstream2_pages.py b/tools/perf/page_sets/jetstream2_pages.py
index fe1c7aa4..661a6a0e7 100644
--- a/tools/perf/page_sets/jetstream2_pages.py
+++ b/tools/perf/page_sets/jetstream2_pages.py
@@ -66,7 +66,7 @@
           benchmark, 'score', v['Score'],
           description='Geometric mean of the iterations')
       self.AddMeasurement(
-          '%s.Iterations' % benchmark, 'number', v['Iterations'],
+          '%s.Iterations' % benchmark, 'count', v['Iterations'],
           description='Total number of iterations')
       for sub_k, sub_v in v['SubResults'].iteritems():
         self.AddMeasurement('%s.%s' % (benchmark, sub_k), 'score', sub_v)
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index bc67ad7..1ba20a0 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -885,6 +885,16 @@
   ITEMS_TO_VISIT = 4
 
 
+class BrowseAmazonMobileStory2019(_ArticleBrowsingStory):
+  NAME = 'browse:shopping:amazon:2019'
+  URL = 'https://www.amazon.com.br/s/?k=telefone+celular'
+  SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
+  TAGS = [story_tags.EMERGING_MARKET, story_tags.YEAR_2019]
+
+  ITEM_SELECTOR = '[class="a-size-base a-color-base a-text-normal"]'
+  ITEMS_TO_VISIT = 4
+
+
 class BrowseLazadaMobileStory(_ArticleBrowsingStory):
   NAME = 'browse:shopping:lazada'
   URL = 'https://www.lazada.co.id/catalog/?q=Wrist+watch'
diff --git a/tools/perf/process_perf_results.py b/tools/perf/process_perf_results.py
index f77fd1a..38f238d 100755
--- a/tools/perf/process_perf_results.py
+++ b/tools/perf/process_perf_results.py
@@ -56,6 +56,7 @@
 # are okay with potentially encountering issues.
 GTEST_CONVERSION_WHITELIST = [
   'angle_perftests',
+  'browser_tests',
   'cc_perftests',
   'components_perftests',
   'gpu_perftests',
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index 6811b398..3566ccb 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -586,17 +586,7 @@
 }
 
 void AXNodeData::SetDescription(const std::string& description) {
-  auto iter = std::find_if(string_attributes.begin(), string_attributes.end(),
-                           [](const auto& string_attribute) {
-                             return string_attribute.first ==
-                                    ax::mojom::StringAttribute::kDescription;
-                           });
-  if (iter == string_attributes.end()) {
-    string_attributes.push_back(
-        std::make_pair(ax::mojom::StringAttribute::kDescription, description));
-  } else {
-    iter->second = description;
-  }
+  AddStringAttribute(ax::mojom::StringAttribute::kDescription, description);
 }
 
 void AXNodeData::SetDescription(const base::string16& description) {
@@ -604,17 +594,7 @@
 }
 
 void AXNodeData::SetValue(const std::string& value) {
-  auto iter = std::find_if(string_attributes.begin(), string_attributes.end(),
-                           [](const auto& string_attribute) {
-                             return string_attribute.first ==
-                                    ax::mojom::StringAttribute::kValue;
-                           });
-  if (iter == string_attributes.end()) {
-    string_attributes.push_back(
-        std::make_pair(ax::mojom::StringAttribute::kValue, value));
-  } else {
-    iter->second = value;
-  }
+  AddStringAttribute(ax::mojom::StringAttribute::kValue, value);
 }
 
 void AXNodeData::SetValue(const base::string16& value) {
diff --git a/ui/accessibility/ax_node_data_unittest.cc b/ui/accessibility/ax_node_data_unittest.cc
index 3097f563..5313f03 100644
--- a/ui/accessibility/ax_node_data_unittest.cc
+++ b/ui/accessibility/ax_node_data_unittest.cc
@@ -66,6 +66,13 @@
                             "different font");
   EXPECT_TRUE(node_1.GetTextStyles() != node_2.GetTextStyles());
 
+  std::string tooltip;
+  node_2.AddStringAttribute(ax::mojom::StringAttribute::kTooltip,
+                            "test tooltip");
+  EXPECT_TRUE(node_2.GetStringAttribute(ax::mojom::StringAttribute::kTooltip,
+                                        &tooltip));
+  EXPECT_EQ(tooltip, "test tooltip");
+
   AXNodeTextStyles node1_styles = node_1.GetTextStyles();
   AXNodeTextStyles moved_styles = std::move(node1_styles);
   EXPECT_TRUE(node1_styles != moved_styles);
diff --git a/ui/accessibility/ax_range.h b/ui/accessibility/ax_range.h
index 80f0c0df..c58a5f2 100644
--- a/ui/accessibility/ax_range.h
+++ b/ui/accessibility/ax_range.h
@@ -6,6 +6,8 @@
 #define UI_ACCESSIBILITY_AX_RANGE_H_
 
 #include <memory>
+#include <ostream>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -125,6 +127,11 @@
     return anchor_->IsNullPosition() || focus_->IsNullPosition();
   }
 
+  std::string ToString() const {
+    return "Range\nAnchor:" + anchor_->ToString() +
+           "\nFocus:" + focus_->ToString();
+  }
+
   // We can decompose any given AXRange into multiple "leaf text ranges".
   // As an example, consider the following HTML code:
   //
@@ -349,6 +356,12 @@
   AXPositionInstance focus_;
 };
 
+template <class AXPositionType>
+std::ostream& operator<<(std::ostream& stream,
+                         const AXRange<AXPositionType>& range) {
+  return stream << range.ToString();
+}
+
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_AX_RANGE_H_
diff --git a/ui/accessibility/ax_table_info.cc b/ui/accessibility/ax_table_info.cc
index 89453afb5..63c45ef 100644
--- a/ui/accessibility/ax_table_info.cc
+++ b/ui/accessibility/ax_table_info.cc
@@ -342,11 +342,15 @@
   // The table header container is just a node with all of the headers in the
   // table as indirect children.
 
-  // Delete old extra nodes.
-  ClearExtraMacNodes();
+  if (!extra_mac_nodes.empty()) {
+    // Delete old extra nodes.
+    ClearExtraMacNodes();
+  }
 
+  // One node for each column, and one more for the table header container.
+  size_t extra_node_count = col_count + 1;
   // Resize.
-  extra_mac_nodes.resize(col_count + 1);
+  extra_mac_nodes.resize(extra_node_count);
 
   // Create column nodes.
   for (size_t i = 0; i < col_count; i++)
diff --git a/ui/accessibility/ax_table_info_unittest.cc b/ui/accessibility/ax_table_info_unittest.cc
index de310a2a..af717dc 100644
--- a/ui/accessibility/ax_table_info_unittest.cc
+++ b/ui/accessibility/ax_table_info_unittest.cc
@@ -472,6 +472,8 @@
   EXPECT_EQ(-1, table_info->extra_mac_nodes[0]->id());
   EXPECT_EQ(1, table_info->extra_mac_nodes[0]->parent()->id());
   EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_0.role);
+  EXPECT_EQ(2U, table_info->extra_mac_nodes[0]->GetIndexInParent());
+  EXPECT_EQ(2U, table_info->extra_mac_nodes[0]->GetUnignoredIndexInParent());
   EXPECT_EQ(0, extra_node_0.GetIntAttribute(
                    ax::mojom::IntAttribute::kTableColumnIndex));
   std::vector<int32_t> indirect_child_ids;
@@ -487,6 +489,8 @@
   EXPECT_EQ(-2, table_info->extra_mac_nodes[1]->id());
   EXPECT_EQ(1, table_info->extra_mac_nodes[1]->parent()->id());
   EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_1.role);
+  EXPECT_EQ(3U, table_info->extra_mac_nodes[1]->GetIndexInParent());
+  EXPECT_EQ(3U, table_info->extra_mac_nodes[1]->GetUnignoredIndexInParent());
   EXPECT_EQ(1, extra_node_1.GetIntAttribute(
                    ax::mojom::IntAttribute::kTableColumnIndex));
   indirect_child_ids.clear();
@@ -502,6 +506,8 @@
   EXPECT_EQ(-3, table_info->extra_mac_nodes[2]->id());
   EXPECT_EQ(1, table_info->extra_mac_nodes[2]->parent()->id());
   EXPECT_EQ(ax::mojom::Role::kTableHeaderContainer, extra_node_2.role);
+  EXPECT_EQ(4U, table_info->extra_mac_nodes[2]->GetIndexInParent());
+  EXPECT_EQ(4U, table_info->extra_mac_nodes[2]->GetUnignoredIndexInParent());
   indirect_child_ids.clear();
   EXPECT_EQ(true, extra_node_2.GetIntListAttribute(
                       ax::mojom::IntListAttribute::kIndirectChildIds,
@@ -734,7 +740,7 @@
   EXPECT_EQ(3, cell_id_7->GetTableCellColIndex());
 }
 
-TEST_F(AXTableInfoTest, AriaIndicesinferred) {
+TEST_F(AXTableInfoTest, AriaIndicesInferred) {
   // Note that ARIA indices are 1-based, whereas the rest of
   // the table indices are zero-based.
   AXTreeUpdate initial_state;
@@ -857,4 +863,145 @@
   EXPECT_FALSE(table_info);
 }
 
+TEST_F(AXTableInfoTest, ExtraMacNodesChanges) {
+  // Simple 2 x 2 table with 2 column headers in first row, 2 cells in second
+  // row.
+  AXTreeUpdate initial_state;
+  initial_state.root_id = 1;
+  initial_state.nodes.resize(7);
+  MakeTable(&initial_state.nodes[0], 1, 0, 0);
+  initial_state.nodes[0].child_ids = {2, 3};
+  MakeRow(&initial_state.nodes[1], 2, 0);
+  initial_state.nodes[1].child_ids = {4, 5};
+  MakeRow(&initial_state.nodes[2], 3, 1);
+  initial_state.nodes[2].child_ids = {6, 7};
+  MakeColumnHeader(&initial_state.nodes[3], 4, 0, 0);
+  MakeColumnHeader(&initial_state.nodes[4], 5, 0, 1);
+  MakeCell(&initial_state.nodes[5], 6, 1, 0);
+  MakeCell(&initial_state.nodes[6], 7, 1, 1);
+  AXTree tree(initial_state);
+
+  tree.SetEnableExtraMacNodes(true);
+  AXTableInfo* table_info = GetTableInfo(&tree, tree.root());
+  ASSERT_NE(nullptr, table_info);
+  // We expect 3 extra Mac nodes: two column nodes, and one header node.
+  ASSERT_EQ(3U, table_info->extra_mac_nodes.size());
+
+  // Hide the first row. The number of extra Mac nodes should remain the same,
+  // but their data should change.
+  AXTreeUpdate update1;
+  update1.nodes.resize(1);
+  MakeRow(&update1.nodes[0], 2, 0);
+  update1.nodes[0].AddState(ax::mojom::State::kIgnored);
+  update1.nodes[0].child_ids = {4, 5};
+  ASSERT_TRUE(tree.Unserialize(update1));
+  table_info = GetTableInfo(&tree, tree.root());
+  ASSERT_EQ(3U, table_info->extra_mac_nodes.size());
+
+  {
+    // The first column.
+    AXNodeData extra_node_0 = table_info->extra_mac_nodes[0]->data();
+    EXPECT_EQ(-4, table_info->extra_mac_nodes[0]->id());
+    EXPECT_EQ(1, table_info->extra_mac_nodes[0]->parent()->id());
+    EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_0.role);
+    EXPECT_EQ(2U, table_info->extra_mac_nodes[0]->GetIndexInParent());
+    EXPECT_EQ(3U, table_info->extra_mac_nodes[0]->GetUnignoredIndexInParent());
+    EXPECT_EQ(0, extra_node_0.GetIntAttribute(
+                     ax::mojom::IntAttribute::kTableColumnIndex));
+    std::vector<int32_t> indirect_child_ids;
+    EXPECT_EQ(true, extra_node_0.GetIntListAttribute(
+                        ax::mojom::IntListAttribute::kIndirectChildIds,
+                        &indirect_child_ids));
+    EXPECT_EQ(1U, indirect_child_ids.size());
+    EXPECT_EQ(6, indirect_child_ids[0]);
+
+    // The second column.
+    AXNodeData extra_node_1 = table_info->extra_mac_nodes[1]->data();
+    EXPECT_EQ(-5, table_info->extra_mac_nodes[1]->id());
+    EXPECT_EQ(1, table_info->extra_mac_nodes[1]->parent()->id());
+    EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_1.role);
+    EXPECT_EQ(3U, table_info->extra_mac_nodes[1]->GetIndexInParent());
+    EXPECT_EQ(4U, table_info->extra_mac_nodes[1]->GetUnignoredIndexInParent());
+    EXPECT_EQ(1, extra_node_1.GetIntAttribute(
+                     ax::mojom::IntAttribute::kTableColumnIndex));
+    indirect_child_ids.clear();
+    EXPECT_EQ(true, extra_node_1.GetIntListAttribute(
+                        ax::mojom::IntListAttribute::kIndirectChildIds,
+                        &indirect_child_ids));
+    EXPECT_EQ(1U, indirect_child_ids.size());
+    EXPECT_EQ(7, indirect_child_ids[0]);
+
+    // The table header container.
+    AXNodeData extra_node_2 = table_info->extra_mac_nodes[2]->data();
+    EXPECT_EQ(-6, table_info->extra_mac_nodes[2]->id());
+    EXPECT_EQ(1, table_info->extra_mac_nodes[2]->parent()->id());
+    EXPECT_EQ(ax::mojom::Role::kTableHeaderContainer, extra_node_2.role);
+    EXPECT_EQ(4U, table_info->extra_mac_nodes[2]->GetIndexInParent());
+    EXPECT_EQ(5U, table_info->extra_mac_nodes[2]->GetUnignoredIndexInParent());
+    indirect_child_ids.clear();
+    EXPECT_EQ(true, extra_node_2.GetIntListAttribute(
+                        ax::mojom::IntListAttribute::kIndirectChildIds,
+                        &indirect_child_ids));
+    EXPECT_EQ(0U, indirect_child_ids.size());
+  }
+
+  // Delete the first row. Again, the number of extra Mac nodes should remain
+  // the same, but their data should change.
+  AXTreeUpdate update2;
+  update2.node_id_to_clear = 2;
+  update2.nodes.resize(1);
+  MakeTable(&update2.nodes[0], 1, 0, 0);
+  update2.nodes[0].child_ids = {3};
+  ASSERT_TRUE(tree.Unserialize(update2));
+  table_info = GetTableInfo(&tree, tree.root());
+  ASSERT_EQ(3U, table_info->extra_mac_nodes.size());
+
+  {
+    // The first column.
+    AXNodeData extra_node_0 = table_info->extra_mac_nodes[0]->data();
+    EXPECT_EQ(-7, table_info->extra_mac_nodes[0]->id());
+    EXPECT_EQ(1, table_info->extra_mac_nodes[0]->parent()->id());
+    EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_0.role);
+    EXPECT_EQ(1U, table_info->extra_mac_nodes[0]->GetIndexInParent());
+    EXPECT_EQ(1U, table_info->extra_mac_nodes[0]->GetUnignoredIndexInParent());
+    EXPECT_EQ(0, extra_node_0.GetIntAttribute(
+                     ax::mojom::IntAttribute::kTableColumnIndex));
+    std::vector<int32_t> indirect_child_ids;
+    EXPECT_EQ(true, extra_node_0.GetIntListAttribute(
+                        ax::mojom::IntListAttribute::kIndirectChildIds,
+                        &indirect_child_ids));
+    EXPECT_EQ(1U, indirect_child_ids.size());
+    EXPECT_EQ(6, indirect_child_ids[0]);
+
+    // The second column.
+    AXNodeData extra_node_1 = table_info->extra_mac_nodes[1]->data();
+    EXPECT_EQ(-8, table_info->extra_mac_nodes[1]->id());
+    EXPECT_EQ(1, table_info->extra_mac_nodes[1]->parent()->id());
+    EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_1.role);
+    EXPECT_EQ(2U, table_info->extra_mac_nodes[1]->GetIndexInParent());
+    EXPECT_EQ(2U, table_info->extra_mac_nodes[1]->GetUnignoredIndexInParent());
+    EXPECT_EQ(1, extra_node_1.GetIntAttribute(
+                     ax::mojom::IntAttribute::kTableColumnIndex));
+    indirect_child_ids.clear();
+    EXPECT_EQ(true, extra_node_1.GetIntListAttribute(
+                        ax::mojom::IntListAttribute::kIndirectChildIds,
+                        &indirect_child_ids));
+    EXPECT_EQ(1U, indirect_child_ids.size());
+    EXPECT_EQ(7, indirect_child_ids[0]);
+
+    // The table header container.
+    AXNodeData extra_node_2 = table_info->extra_mac_nodes[2]->data();
+    EXPECT_EQ(-9, table_info->extra_mac_nodes[2]->id());
+    EXPECT_EQ(1, table_info->extra_mac_nodes[2]->parent()->id());
+    EXPECT_EQ(ax::mojom::Role::kTableHeaderContainer, extra_node_2.role);
+    EXPECT_EQ(3U, table_info->extra_mac_nodes[2]->GetIndexInParent());
+    EXPECT_EQ(3U, table_info->extra_mac_nodes[2]->GetUnignoredIndexInParent());
+    indirect_child_ids.clear();
+    EXPECT_EQ(true, extra_node_2.GetIntListAttribute(
+                        ax::mojom::IntListAttribute::kIndirectChildIds,
+                        &indirect_child_ids));
+    EXPECT_EQ(0U, indirect_child_ids.size());
+  }
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index 35b3251d..1e46510 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -1770,7 +1770,14 @@
 }
 
 void AXTree::SetEnableExtraMacNodes(bool enabled) {
-  DCHECK(enable_extra_mac_nodes_ != enabled);
+  if (enable_extra_mac_nodes_ == enabled)
+    return;  // No change.
+  if (enable_extra_mac_nodes_ && !enabled) {
+    NOTREACHED()
+        << "We don't support disabling the extra Mac nodes once enabled.";
+    return;
+  }
+
   DCHECK_EQ(0U, table_info_map_.size());
   enable_extra_mac_nodes_ = enabled;
 }
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index f273a388..53b0513 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -61,6 +61,10 @@
 #define ATK_230
 #endif
 
+#if defined(ATK_CHECK_VERSION) && ATK_CHECK_VERSION(2, 34, 0)
+#define ATK_234
+#endif
+
 namespace ui {
 
 namespace {
@@ -145,6 +149,14 @@
 constexpr AtkRole kAtkFootnoteRole = ATK_ROLE_LIST_ITEM;
 #endif
 
+#if defined(ATK_234)
+constexpr AtkRole kAtkRoleContentDeletion = ATK_ROLE_CONTENT_DELETION;
+constexpr AtkRole kAtkRoleContentInsertion = ATK_ROLE_CONTENT_INSERTION;
+#else
+constexpr AtkRole kAtkRoleContentDeletion = ATK_ROLE_SECTION;
+constexpr AtkRole kAtkRoleContentInsertion = ATK_ROLE_SECTION;
+#endif
+
 AXPlatformNodeAuraLinux* AtkObjectToAXPlatformNodeAuraLinux(
     AtkObject* atk_object) {
   if (!atk_object)
@@ -2341,9 +2353,9 @@
     case ax::mojom::Role::kComplementary:
       return ATK_ROLE_LANDMARK;
     case ax::mojom::Role::kContentDeletion:
+      return kAtkRoleContentDeletion;
     case ax::mojom::Role::kContentInsertion:
-      // TODO(accessibility) https://github.com/w3c/html-aam/issues/141
-      return ATK_ROLE_SECTION;
+      return kAtkRoleContentInsertion;
     case ax::mojom::Role::kContentInfo:
     case ax::mojom::Role::kFooter:
       return ATK_ROLE_LANDMARK;
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 5fd5aaa..6f73e22 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -5,6 +5,7 @@
 #include "ui/accessibility/platform/ax_platform_node_win.h"
 
 #include <wrl/client.h>
+#include <wrl/implements.h>
 
 #include <algorithm>
 #include <map>
@@ -1181,16 +1182,14 @@
 
   // Multiple items are selected.
   LONG selected_count = static_cast<LONG>(selected_nodes.size());
-  auto* enum_variant = new base::win::EnumVariant(selected_count);
-  enum_variant->AddRef();
+  Microsoft::WRL::ComPtr<base::win::EnumVariant> enum_variant =
+      Microsoft::WRL::Make<base::win::EnumVariant>(selected_count);
   for (LONG i = 0; i < selected_count; ++i) {
     enum_variant->ItemAt(i)->vt = VT_DISPATCH;
     enum_variant->ItemAt(i)->pdispVal = selected_nodes[i].Detach();
   }
   selected->vt = VT_UNKNOWN;
-  HRESULT hr = enum_variant->QueryInterface(IID_PPV_ARGS(&V_UNKNOWN(selected)));
-  enum_variant->Release();
-  return hr;
+  return enum_variant.CopyTo(IID_PPV_ARGS(&V_UNKNOWN(selected)));
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::accSelect(LONG flagsSelect, VARIANT var_id) {
diff --git a/ui/compositor/layer_animator.h b/ui/compositor/layer_animator.h
index c400361..72bd365 100644
--- a/ui/compositor/layer_animator.h
+++ b/ui/compositor/layer_animator.h
@@ -376,8 +376,6 @@
       int target_property,
       base::TimeTicks animation_start_time,
       std::unique_ptr<cc::AnimationCurve> curve) override {}
-  void NotifyLocalTimeUpdated(
-      base::Optional<base::TimeDelta> local_time) override {}
 
   // Implementation of LayerThreadedAnimationDelegate.
   void AddThreadedAnimation(
diff --git a/ui/events/ozone/BUILD.gn b/ui/events/ozone/BUILD.gn
index 6b53a18..f0e7a46d 100644
--- a/ui/events/ozone/BUILD.gn
+++ b/ui/events/ozone/BUILD.gn
@@ -122,6 +122,8 @@
         "evdev/touch_filter/horizontally_aligned_touch_noise_filter.h",
         "evdev/touch_filter/low_pressure_filter.cc",
         "evdev/touch_filter/low_pressure_filter.h",
+        "evdev/touch_filter/neural_stylus_palm_detection_filter_model.cc",
+        "evdev/touch_filter/neural_stylus_palm_detection_filter_model.h",
         "evdev/touch_filter/neural_stylus_palm_detection_filter_util.cc",
         "evdev/touch_filter/neural_stylus_palm_detection_filter_util.h",
         "evdev/touch_filter/open_palm_detection_filter.cc",
diff --git a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.cc b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.cc
new file mode 100644
index 0000000..354489a
--- /dev/null
+++ b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.cc
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.h"
+
+namespace ui {
+
+NeuralStylusPalmDetectionFilterModelConfig::
+    NeuralStylusPalmDetectionFilterModelConfig() = default;
+
+NeuralStylusPalmDetectionFilterModelConfig::
+    NeuralStylusPalmDetectionFilterModelConfig(
+        const NeuralStylusPalmDetectionFilterModelConfig& other) = default;
+
+}  // namespace ui
diff --git a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.h b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.h
new file mode 100644
index 0000000..f5f1e9d1
--- /dev/null
+++ b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.h
@@ -0,0 +1,70 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_NEURAL_STYLUS_PALM_DETECTION_FILTER_MODEL_H_
+#define UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_NEURAL_STYLUS_PALM_DETECTION_FILTER_MODEL_H_
+
+#include <cstdint>
+#include <vector>
+
+#include "base/time/time.h"
+#include "ui/events/ozone/evdev/events_ozone_evdev_export.h"
+
+namespace ui {
+
+struct EVENTS_OZONE_EVDEV_EXPORT NeuralStylusPalmDetectionFilterModelConfig {
+  // Explicit constructor to make chromium style happy.
+  NeuralStylusPalmDetectionFilterModelConfig();
+  NeuralStylusPalmDetectionFilterModelConfig(
+      const NeuralStylusPalmDetectionFilterModelConfig& other);
+
+  // Number of nearest neighbors to use in vector construction.
+  uint32_t nearest_neighbor_count = 0;
+
+  // Number of biggest nearby neighbors to use in vector construction.
+  uint32_t biggest_near_neighbor_count = 0;
+
+  // Maximum distance of neighbor centroid, in millimeters.
+  float max_neighbor_distance_in_mm = 0.0f;
+
+  base::TimeDelta max_dead_neighbor_time =
+      base::TimeDelta::FromMilliseconds(0.0);
+
+  // Minimum count of samples in a stroke for neural comparison.
+  uint32_t min_sample_count = 0;
+
+  // Maximum sample count.
+  uint32_t max_sample_count = 0;
+
+  uint32_t max_sequence_start_count_for_inference = 0;
+
+  bool include_sequence_count_in_strokes = false;
+
+  // If this number is positive, short strokes with a touch major greater than
+  // or equal to this should be marked as a palm. If 0 or less, has no effect.
+  float heuristic_palm_touch_limit = 0.0f;
+
+  // If this number is positive, short strokes with any touch having an area
+  // greater than or equal to this should be marked as a palm. If <= 0, has no
+  // effect
+  float heuristic_palm_area_limit = 0.0f;
+};
+
+// An abstract model utilized by NueralStylusPalmDetectionFilter.
+class EVENTS_OZONE_EVDEV_EXPORT NeuralStylusPalmDetectionFilterModel {
+ public:
+  virtual ~NeuralStylusPalmDetectionFilterModel() {}
+
+  // Actually execute inference on floating point input. If the length of
+  // features is not correct, return Nan. The return value is assumed to be the
+  // input of a sigmoid. i.e. any value greater than 0 implies a positive
+  // result.
+  virtual float Inference(const std::vector<float>& features) const = 0;
+
+  virtual NeuralStylusPalmDetectionFilterModelConfig& config() const = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_NEURAL_STYLUS_PALM_DETECTION_FILTER_MODEL_H_
diff --git a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.cc b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.cc
index 8ddc230..e63a4c1 100644
--- a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.cc
+++ b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.cc
@@ -116,6 +116,14 @@
   tracking_id_ = tracking_id;
 }
 
+float Stroke::MaxMajorRadius() const {
+  float maximum = 0.0;
+  for (const auto& sample : samples_) {
+    maximum = std::max(maximum, sample.major_radius);
+  }
+  return maximum;
+}
+
 float Stroke::BiggestSize() const {
   float biggest = 0;
   for (const auto& sample : samples_) {
diff --git a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h
index ddd9919..fe4962d 100644
--- a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h
+++ b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h
@@ -59,10 +59,13 @@
   void AddSample(const Sample& sample);
   gfx::PointF GetCentroid() const;
   float BiggestSize() const;
+  // If no elements in stroke, returns 0.0;
+  float MaxMajorRadius() const;
   void SetTrackingId(int tracking_id);
   const std::deque<Sample>& samples() const;
   uint64_t samples_seen() const;
   int tracking_id() const;
+
  private:
   std::deque<Sample> samples_;
   int tracking_id_ = 0;
diff --git a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util_unittest.cc b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util_unittest.cc
index 25bfb93..fde33e6 100644
--- a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util_unittest.cc
+++ b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util_unittest.cc
@@ -176,4 +176,22 @@
   }
 }
 
+TEST_F(NeuralStylusPalmDetectionFilterUtilTest, StrokeGetMaxMajorTest) {
+  Stroke stroke(3);
+  EXPECT_FLOAT_EQ(0, stroke.MaxMajorRadius());
+  base::TimeTicks t =
+      base::TimeTicks::UnixEpoch() + base::TimeDelta::FromSeconds(30);
+  const DistilledDevInfo nocturne_distilled =
+      DistilledDevInfo::Create(nocturne_touchscreen_);
+  for (int i = 1; i < 50; ++i) {
+    touch_.major = i;
+    touch_.minor = i - 1;
+    Sample s(touch_, t, nocturne_distilled);
+    t += base::TimeDelta::FromMilliseconds(8);
+    EXPECT_EQ(static_cast<uint64_t>(i - 1), stroke.samples_seen());
+    stroke.AddSample(s);
+    EXPECT_FLOAT_EQ(i, stroke.MaxMajorRadius());
+  }
+}
+
 }  // namespace ui
diff --git a/ui/file_manager/file_manager/foreground/css/file_types.css b/ui/file_manager/file_manager/foreground/css/file_types.css
index be5a979..b48eda4 100644
--- a/ui/file_manager/file_manager/foreground/css/file_types.css
+++ b/ui/file_manager/file_manager/foreground/css/file_types.css
@@ -3,8 +3,9 @@
  * found in the LICENSE file. */
 
 /* TODO(crbug.com/992819): unify root-type-icon and volume-type-icon into one
- type, called file-type-icon or entry_type-icon say, since there is no real
- need for the root/volume icon distinction. */
+ type if possible, called file-type-icon or entry-type-icon say, since there
+ is no real need for the root/volume icon distinction for the directory tree
+ noting that file-type-icon is also used in lists and menus ;) */
 
 /* Small icons for file types, used in lists and menus. */
 [file-type-icon] {
@@ -40,6 +41,18 @@
       url(../images/filetype/2x/filetype_folder.png) 2x);
 }
 
+.computers-root[file-type-icon='folder'] {
+  background-image: -webkit-image-set(
+      url(../images/volumes/computer.png) 1x,
+      url(../images/volumes/2x/computer.png) 2x);
+}
+
+.external-media-root[file-type-icon='folder'] {
+  background-image: -webkit-image-set(
+      url(../images/volumes/usb.png) 1x,
+      url(../images/volumes/2x/usb.png) 2x);
+}
+
 .shared[file-type-icon='folder'] {
   background-image: -webkit-image-set(
       url(../images/filetype/filetype_folder_shared.png) 1x,
@@ -52,20 +65,19 @@
       url(../images/volumes/2x/hard_drive.png) 2x);
 }
 
-tree .tree-item[selected] > .tree-row > [file-type-icon='folder'] {
+.tree-row[selected] > [file-type-icon='folder'] {
   background-image: -webkit-image-set(
       url(../images/filetype/filetype_folder_active.png) 1x,
       url(../images/filetype/2x/filetype_folder_active.png) 2x);
 }
 
-tree .tree-item[selected] > .tree-row > .shared[file-type-icon='folder'] {
+.tree-row[selected] > .shared[file-type-icon='folder'] {
   background-image: -webkit-image-set(
       url(../images/filetype/filetype_folder_shared_active.png) 1x,
       url(../images/filetype/2x/filetype_folder_shared_active.png) 2x);
 }
 
-tree .tree-item[selected] > .tree-row >
-.team-drive-root[file-type-icon='folder'] {
+.tree-row[selected] > .team-drive-root[file-type-icon='folder'] {
   background-image: -webkit-image-set(
       url(../images/filetype/filetype_team_drive_active.png) 1x,
       url(../images/filetype/2x/filetype_team_drive_active.png) 2x);
@@ -233,8 +245,8 @@
       url(../images/volumes/2x/my_files_active.png) 2x);
 }
 
-[file-type-icon='downloads'],
-[volume-type-icon='downloads'] {
+[volume-type-icon='downloads'],
+[file-type-icon='downloads'] {
   background-image: -webkit-image-set(
       url(../images/volumes/downloads.png) 1x,
       url(../images/volumes/2x/downloads.png) 2x);
@@ -258,6 +270,7 @@
       url(../images/volumes/drive_active.png) 1x,
       url(../images/volumes/2x/drive_active.png) 2x);
 }
+
 [volume-type-icon='shortcut'] {
   background-image: -webkit-image-set(
       url(../images/volumes/shortcut.png) 1x,
@@ -271,7 +284,7 @@
 }
 
 .drive-volume > .tree-row > [volume-type-icon='drive'],
-[root-type-icon='drive'] {
+.tree-row [root-type-icon='drive'] {
   background-image: -webkit-image-set(
       url(../images/volumes/service_drive.png) 1x,
       url(../images/volumes/2x/service_drive.png) 2x);
@@ -320,16 +333,13 @@
       url(../images/volumes/2x/devices_active.png) 2x);
 }
 
-[volume-type-icon='computer'],
-.computers-root[file-type-icon='folder'] {
+[volume-type-icon='computer'] {
   background-image: -webkit-image-set(
       url(../images/volumes/computer.png) 1x,
       url(../images/volumes/2x/computer.png) 2x);
 }
 
-.tree-row[selected] [volume-type-icon='computer'],
-tree .tree-item[selected] > .tree-row >
-.computers-root[file-type-icon='folder'] {
+.tree-row[selected] [volume-type-icon='computer'] {
   background-image: -webkit-image-set(
       url(../images/volumes/computer_active.png) 1x,
       url(../images/volumes/2x/computer_active.png) 2x);
@@ -371,7 +381,6 @@
       url(../images/volumes/2x/recent_active.png) 2x);
 }
 
-.external-media-root[file-type-icon='folder'],
 [volume-type-icon='external_media'],
 [volume-type-icon='removable'],
 [root-type-icon='removable'] {
@@ -380,8 +389,6 @@
       url(../images/volumes/2x/usb.png) 2x);
 }
 
-tree .tree-item[selected] > .tree-row >
-.external-media-root[file-type-icon='folder'],
 .tree-row[selected] [volume-type-icon='external_media'],
 .tree-row[selected] [volume-type-icon='removable'],
 .tree-row[selected] [root-type-icon='removable'] {
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index 5ee50ae5..cb62465 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -6,11 +6,11 @@
 const directorytree = {};
 
 ////////////////////////////////////////////////////////////////////////////////
-// DirectoryTreeBase
+// DirectoryTreeBase methods
 
 /**
  * Implementation of methods for DirectoryTree and DirectoryItem. These classes
- * inherits cr.ui.Tree/TreeItem so we can't make them inherit this class.
+ * inherits cr.ui.Tree/cr.ui.TreeItem so we can't make them inherit this class.
  * Instead, we separate their implementations to this separate object and call
  * it with setting 'this' from DirectoryTree/Item.
  */
@@ -138,6 +138,17 @@
 // DirectoryItem
 
 /**
+ * An optional rowElement depth (indent) style handler where undefined uses the
+ * default cr.ui.Tree/cr.ui.TreeItem indent styling.
+ *
+ * TODO(crbug.com/992819): add an implementation for the FILES_NG_ENABLED case,
+ * where a rowElement child needs the indent, not the rowElement itself.
+ *
+ * @type {function(!cr.ui.TreeItem,number)|undefined}
+ */
+directorytree.styleRowElementDepth = undefined;
+
+/**
  * An expandable directory in the tree. Each element represents one folder (sub
  * directory) or one volume (root directory).
  */
@@ -2241,6 +2252,10 @@
     (el, directoryModel, volumeManager, metadataModel, fileOperationManager,
      fakeEntriesVisible) => {
       el.__proto__ = DirectoryTree.prototype;
+
+      // TODO(crbug.com/992819): add overrides for the FILES_NG_ENABLED case.
+      Object.freeze(directorytree);
+
       /** @type {DirectoryTree} */ (el).decorateDirectoryTree(
           directoryModel, volumeManager, metadataModel, fileOperationManager,
           fakeEntriesVisible);
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index bcb4fbc..b85c8ee 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -685,6 +685,7 @@
   if (!is_ios) {
     sources += [
       "animation/animation_container_unittest.cc",
+      "animation/animation_runner_unittest.cc",
       "animation/animation_unittest.cc",
       "animation/multi_animation_unittest.cc",
       "animation/slide_animation_unittest.cc",
diff --git a/ui/gfx/animation/animation_container.cc b/ui/gfx/animation/animation_container.cc
index 19e1a8c..5cd28af 100644
--- a/ui/gfx/animation/animation_container.cc
+++ b/ui/gfx/animation/animation_container.cc
@@ -19,16 +19,16 @@
   if (observer_)
     observer_->AnimationContainerShuttingDown(this);
 
-  // The animations own us and stop themselves before being deleted. If
-  // elements_ is not empty, something is wrong.
-  DCHECK(elements_.empty());
+  // The animations own us and stop themselves before being deleted. If they're
+  // still running, something is wrong.
+  DCHECK(!is_running());
 }
 
 void AnimationContainer::Start(AnimationContainerElement* element) {
   DCHECK(elements_.count(element) == 0);  // Start should only be invoked if the
                                           // element isn't running.
 
-  if (elements_.empty()) {
+  if (!is_running()) {
     last_tick_time_ = base::TimeTicks::Now();
     SetMinTimerInterval(element->GetTimerInterval());
     min_timer_interval_count_ = 1;
@@ -49,7 +49,7 @@
   base::TimeDelta interval = element->GetTimerInterval();
   elements_.erase(element);
 
-  if (elements_.empty()) {
+  if (!is_running()) {
     runner_->Stop();
     min_timer_interval_count_ = 0;
     if (observer_)
@@ -76,6 +76,8 @@
   runner_ = has_custom_animation_runner_
                 ? std::move(runner)
                 : AnimationRunner::CreateDefaultAnimationRunner();
+  if (is_running())
+    RestartTimer(base::TimeTicks::Now() - last_tick_time_);
 }
 
 void AnimationContainer::Run(base::TimeTicks current_time) {
@@ -107,14 +109,18 @@
   // that shouldn't be a problem for uses of Animation/AnimationContainer.
   runner_->Stop();
   min_timer_interval_ = delta;
+  RestartTimer(base::TimeDelta());
+}
+
+void AnimationContainer::RestartTimer(base::TimeDelta elapsed) {
   runner_->Start(
-      min_timer_interval_,
+      min_timer_interval_, elapsed,
       base::BindRepeating(&AnimationContainer::Run, base::Unretained(this)));
 }
 
 std::pair<TimeDelta, size_t> AnimationContainer::GetMinIntervalAndCount()
     const {
-  DCHECK(!elements_.empty());
+  DCHECK(is_running());
 
   // Find the minimum interval and the number of elements sharing that same
   // interval. It is tempting to create a map of intervals -> counts in order to
diff --git a/ui/gfx/animation/animation_container.h b/ui/gfx/animation/animation_container.h
index 9a084944..1bbf40a 100644
--- a/ui/gfx/animation/animation_container.h
+++ b/ui/gfx/animation/animation_container.h
@@ -56,6 +56,7 @@
   bool is_running() const { return !elements_.empty(); }
 
   void SetAnimationRunner(std::unique_ptr<AnimationRunner> runner);
+  AnimationRunner* animation_runner_for_testing() { return runner_.get(); }
   bool has_custom_animation_runner() const {
     return has_custom_animation_runner_;
   }
@@ -80,6 +81,10 @@
   // Sets min_timer_interval_ and restarts the timer.
   void SetMinTimerInterval(base::TimeDelta delta);
 
+  // Restarts the timer, assuming |elapsed| has already elapsed out of the timer
+  // interval.
+  void RestartTimer(base::TimeDelta elapsed);
+
   // Returns the min timer interval of all the timers, and the count of timers
   // at that interval.
   std::pair<base::TimeDelta, size_t> GetMinIntervalAndCount() const;
diff --git a/ui/gfx/animation/animation_container_unittest.cc b/ui/gfx/animation/animation_container_unittest.cc
index 483eb7b0d..d01fc3e 100644
--- a/ui/gfx/animation/animation_container_unittest.cc
+++ b/ui/gfx/animation/animation_container_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/animation/animation_container_observer.h"
+#include "ui/gfx/animation/animation_test_api.h"
 #include "ui/gfx/animation/linear_animation.h"
 #include "ui/gfx/animation/test_animation_delegate.h"
 
@@ -53,6 +54,8 @@
 
   void AnimateToState(double state) override {}
 
+  using LinearAnimation::duration;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(TestAnimation);
 };
@@ -143,4 +146,27 @@
   container->set_observer(NULL);
 }
 
+// Tests that calling SetAnimationRunner() keeps running animations at their
+// current point.
+TEST_F(AnimationContainerTest, AnimationsRunAcrossRunnerChange) {
+  TestAnimationDelegate delegate;
+  auto container = base::MakeRefCounted<AnimationContainer>();
+  AnimationContainerTestApi test_api(container.get());
+  TestAnimation animation(&delegate);
+  animation.SetContainer(container.get());
+
+  animation.Start();
+  test_api.IncrementTime(animation.duration() / 2);
+  EXPECT_FALSE(delegate.finished());
+
+  container->SetAnimationRunner(nullptr);
+  AnimationRunner* runner = container->animation_runner_for_testing();
+  ASSERT_TRUE(runner);
+  ASSERT_FALSE(runner->step_is_null_for_testing());
+  EXPECT_FALSE(delegate.finished());
+
+  test_api.IncrementTime(animation.duration() / 2);
+  EXPECT_TRUE(delegate.finished());
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/animation/animation_runner.cc b/ui/gfx/animation/animation_runner.cc
index 3928800..eebf9e4 100644
--- a/ui/gfx/animation/animation_runner.cc
+++ b/ui/gfx/animation/animation_runner.cc
@@ -18,21 +18,40 @@
   ~DefaultAnimationRunner() override = default;
 
   // gfx::AnimationRunner:
-  void Stop() override { timer_.Stop(); }
+  void Stop() override;
 
  protected:
   // gfx::AnimationRunner:
-  void OnStart(base::TimeDelta min_interval) override {
-    timer_.Start(FROM_HERE, min_interval, this,
-                 &DefaultAnimationRunner::OnTimerTick);
-  }
+  void OnStart(base::TimeDelta min_interval, base::TimeDelta elapsed) override;
 
  private:
-  void OnTimerTick() { Step(base::TimeTicks::Now()); }
+  void OnTimerTick();
 
-  base::RepeatingTimer timer_;
+  base::OneShotTimer timer_;
+  base::TimeDelta min_interval_;
 };
 
+void DefaultAnimationRunner::Stop() {
+  timer_.Stop();
+}
+
+void DefaultAnimationRunner::OnStart(base::TimeDelta min_interval,
+                                     base::TimeDelta elapsed) {
+  min_interval_ = min_interval;
+  timer_.Start(FROM_HERE, min_interval - elapsed, this,
+               &DefaultAnimationRunner::OnTimerTick);
+}
+
+void DefaultAnimationRunner::OnTimerTick() {
+  // This is effectively a RepeatingTimer.  It's possible to use a true
+  // RepeatingTimer for this, but since OnStart() may need to use a OneShotTimer
+  // anyway (when |elapsed| is nonzero), it's just more complicated.
+  timer_.Start(FROM_HERE, min_interval_, this,
+               &DefaultAnimationRunner::OnTimerTick);
+  // Call Step() after timer_.Start() in case Step() calls Stop().
+  Step(base::TimeTicks::Now());
+}
+
 }  // namespace
 
 namespace gfx {
@@ -47,9 +66,10 @@
 
 void AnimationRunner::Start(
     base::TimeDelta min_interval,
+    base::TimeDelta elapsed,
     base::RepeatingCallback<void(base::TimeTicks)> step) {
   step_ = std::move(step);
-  OnStart(min_interval);
+  OnStart(min_interval, elapsed);
 }
 
 AnimationRunner::AnimationRunner() = default;
diff --git a/ui/gfx/animation/animation_runner.h b/ui/gfx/animation/animation_runner.h
index 7e15647..988bc6cc 100644
--- a/ui/gfx/animation/animation_runner.h
+++ b/ui/gfx/animation/animation_runner.h
@@ -27,20 +27,25 @@
   virtual ~AnimationRunner();
 
   // Sets the provided |step| callback, then calls OnStart() with the provided
-  // |min_interval| to allow the subclass to actually begin animating.
-  // Subclasses are expected to call Step() periodically to drive the animation.
+  // |min_interval| and |elapsed| time to allow the subclass to actually begin
+  // animating. Subclasses are expected to call Step() periodically to drive the
+  // animation.
   void Start(base::TimeDelta min_interval,
+             base::TimeDelta elapsed,
              base::RepeatingCallback<void(base::TimeTicks)> step);
 
   // Called when subclasses don't need to call Step() anymore.
   virtual void Stop() = 0;
 
+  bool step_is_null_for_testing() const { return step_.is_null(); }
+
  protected:
   AnimationRunner();
 
   // Called when subclasses should start calling Step() periodically to
   // drive the animation.
-  virtual void OnStart(base::TimeDelta min_interval) = 0;
+  virtual void OnStart(base::TimeDelta min_interval,
+                       base::TimeDelta elapsed) = 0;
 
   // Advances the animation based on |tick|.
   void Step(base::TimeTicks tick);
diff --git a/ui/gfx/animation/animation_runner_unittest.cc b/ui/gfx/animation/animation_runner_unittest.cc
new file mode 100644
index 0000000..85038aaf
--- /dev/null
+++ b/ui/gfx/animation/animation_runner_unittest.cc
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/animation/animation_runner.h"
+
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gfx {
+namespace {
+
+using AnimationRunnerTest = testing::Test;
+
+// Verifies that calling Stop() during Step() actually stops the timer.
+TEST(AnimationRunnerTest, StopDuringStep) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+
+  auto runner = AnimationRunner::CreateDefaultAnimationRunner();
+  constexpr auto kDelay = base::TimeDelta::FromMilliseconds(20);
+  int call_count = 0;
+  runner->Start(kDelay, base::TimeDelta(),
+                base::BindLambdaForTesting([&](base::TimeTicks ticks) {
+                  ++call_count;
+                  runner->Stop();
+                }));
+  EXPECT_EQ(0, call_count);
+  task_environment.FastForwardBy(kDelay);
+  EXPECT_EQ(1, call_count);
+  task_environment.FastForwardBy(kDelay);
+  EXPECT_EQ(1, call_count);
+}
+
+}  // namespace
+}  // namespace gfx
diff --git a/ui/gfx/animation/animation_unittest.cc b/ui/gfx/animation/animation_unittest.cc
index 35f653b..d1b2848 100644
--- a/ui/gfx/animation/animation_unittest.cc
+++ b/ui/gfx/animation/animation_unittest.cc
@@ -16,7 +16,7 @@
 
 namespace gfx {
 
-class AnimationTest: public testing::Test {
+class AnimationTest : public testing::Test {
  protected:
   AnimationTest()
       : task_environment_(
diff --git a/ui/gfx/animation/test_animation_delegate.h b/ui/gfx/animation/test_animation_delegate.h
index 3c40a899..32af452 100644
--- a/ui/gfx/animation/test_animation_delegate.h
+++ b/ui/gfx/animation/test_animation_delegate.h
@@ -15,31 +15,25 @@
 // message loop.
 class TestAnimationDelegate : public AnimationDelegate {
  public:
-  TestAnimationDelegate() : canceled_(false), finished_(false) {
-  }
+  TestAnimationDelegate() = default;
 
   virtual void AnimationEnded(const Animation* animation) {
     finished_ = true;
-    base::RunLoop::QuitCurrentWhenIdleDeprecated();
+    if (base::RunLoop::IsRunningOnCurrentThread())
+      base::RunLoop::QuitCurrentWhenIdleDeprecated();
   }
 
   virtual void AnimationCanceled(const Animation* animation) {
-    finished_ = true;
     canceled_ = true;
-    base::RunLoop::QuitCurrentWhenIdleDeprecated();
+    AnimationEnded(animation);
   }
 
-  bool finished() const {
-    return finished_;
-  }
-
-  bool canceled() const {
-    return canceled_;
-  }
+  bool finished() const { return finished_; }
+  bool canceled() const { return canceled_; }
 
  private:
-  bool canceled_;
-  bool finished_;
+  bool canceled_ = false;
+  bool finished_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TestAnimationDelegate);
 };
diff --git a/ui/gfx/transform.cc b/ui/gfx/transform.cc
index 873388e..8dcaea0a 100644
--- a/ui/gfx/transform.cc
+++ b/ui/gfx/transform.cc
@@ -14,6 +14,7 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/vector3d_f.h"
+#include "ui/gfx/rrect_f.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/gfx/transform_util.h"
 
@@ -485,6 +486,14 @@
   return true;
 }
 
+bool Transform::TransformRRectF(RRectF* rrect) const {
+  SkRRect result;
+  if (!SkRRect(*rrect).transform(matrix_, &result))
+    return false;
+  *rrect = gfx::RRectF(result);
+  return true;
+}
+
 void Transform::TransformBox(BoxF* box) const {
   BoxF bounds;
   bool first_point = true;
diff --git a/ui/gfx/transform.h b/ui/gfx/transform.h
index 62930a6..2889f3e 100644
--- a/ui/gfx/transform.h
+++ b/ui/gfx/transform.h
@@ -17,6 +17,7 @@
 
 class BoxF;
 class RectF;
+class RRectF;
 class Point;
 class PointF;
 class Point3F;
@@ -242,6 +243,10 @@
   // inverted.
   bool TransformRectReverse(RectF* rect) const;
 
+  // Applies transformation on the given |rrect|. Returns false if the transform
+  // matrix cannot be applied to rrect.
+  bool TransformRRectF(RRectF* rrect) const;
+
   // Applies transformation on the given box. After the function completes,
   // |box| will be the smallest axis aligned bounding box containing the
   // transformed box.
diff --git a/ui/gfx/transform_unittest.cc b/ui/gfx/transform_unittest.cc
index ee410a3..b6357dc 100644
--- a/ui/gfx/transform_unittest.cc
+++ b/ui/gfx/transform_unittest.cc
@@ -19,6 +19,7 @@
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/quad_f.h"
 #include "ui/gfx/geometry/vector3d_f.h"
+#include "ui/gfx/rrect_f.h"
 #include "ui/gfx/transform_util.h"
 
 namespace gfx {
@@ -2638,6 +2639,35 @@
   EXPECT_FALSE(singular.TransformRectReverse(&rect));
 }
 
+TEST(XFormTest, TransformRRectF) {
+  Transform translation;
+  translation.Translate(-3.f, -7.f);
+  RRectF rrect(1.f, 2.f, 20.f, 25.f, 5.f);
+  RRectF expected(-2.f, -5.f, 20.f, 25.f, 5.f);
+  EXPECT_TRUE(translation.TransformRRectF(&rrect));
+  EXPECT_EQ(expected.ToString(), rrect.ToString());
+
+  SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
+  rot.set3x3(0, 1, 0, -1, 0, 0, 0, 0, 1);
+  Transform rotation_90_Clock(rot);
+
+  rrect = RRectF(gfx::RectF(0, 0, 20.f, 25.f),
+                 gfx::RoundedCornersF(1.f, 2.f, 3.f, 4.f));
+  expected = RRectF(gfx::RectF(-25.f, 0, 25.f, 20.f),
+                    gfx::RoundedCornersF(4.f, 1.f, 2.f, 3.f));
+  EXPECT_TRUE(rotation_90_Clock.TransformRRectF(&rrect));
+  EXPECT_EQ(expected.ToString(), rrect.ToString());
+
+  Transform scale;
+  scale.Scale(2.f, 2.f);
+  rrect = RRectF(gfx::RectF(0, 0, 20.f, 25.f),
+                 gfx::RoundedCornersF(1.f, 2.f, 3.f, 4.f));
+  expected = RRectF(gfx::RectF(0, 0, 40.f, 50.f),
+                    gfx::RoundedCornersF(2.f, 4.f, 6.f, 8.f));
+  EXPECT_TRUE(scale.TransformRRectF(&rrect));
+  EXPECT_EQ(expected.ToString(), rrect.ToString());
+}
+
 TEST(XFormTest, TransformBox) {
   Transform translation;
   translation.Translate3d(3.f, 7.f, 6.f);
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 83bf49f..903e67b 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -509,6 +509,10 @@
   'names': ['glDisable'],
   'arguments': 'GLenum cap', },
 { 'return_type': 'void',
+  'versions': [{ 'name': 'glDisableExtensionANGLE',
+                 'extensions': ['GL_ANGLE_request_extension'] }],
+  'arguments': 'const char* name', },
+{ 'return_type': 'void',
   'names': ['glDisableVertexAttribArray'],
   'arguments': 'GLuint index', },
 { 'return_type': 'void',
@@ -612,7 +616,9 @@
                {'name': 'glFlushMappedBufferRangeEXT'}],
   'arguments': 'GLenum target, GLintptr offset, GLsizeiptr length', },
 { 'return_type': 'void',
-  'names': ['glFramebufferParameteri'],
+  'versions': [{'name': 'glFramebufferParameteri'},
+               {'name': 'glFramebufferParameteriMESA',
+                'extensions': ['GL_MESA_framebuffer_flip_y']}],
   'arguments': 'GLenum target, GLenum pname, GLint param', },
 { 'return_type': 'void',
   'names': ['glFramebufferRenderbufferEXT', 'glFramebufferRenderbuffer'],
diff --git a/ui/gl/gl_bindings.h b/ui/gl/gl_bindings.h
index c8e9cd9..439cccc 100644
--- a/ui/gl/gl_bindings.h
+++ b/ui/gl/gl_bindings.h
@@ -127,7 +127,7 @@
 
 // GL_ANGLE_request_extension
 #define GL_REQUESTABLE_EXTENSIONS_ANGLE 0x93A8
-#define GL_NUM_REQUESTABLE_EXTENSIONS_ANGLE 0x93A8
+#define GL_NUM_REQUESTABLE_EXTENSIONS_ANGLE 0x93A9
 
 // GL_ANGLE_memory_size
 #define GL_MEMORY_SIZE_ANGLE 0x93AD
diff --git a/ui/gl/gl_bindings_api_autogen_gl.h b/ui/gl/gl_bindings_api_autogen_gl.h
index fbc547b..ba6e23f5 100644
--- a/ui/gl/gl_bindings_api_autogen_gl.h
+++ b/ui/gl/gl_bindings_api_autogen_gl.h
@@ -332,6 +332,7 @@
 void glDepthRangefFn(GLclampf zNear, GLclampf zFar) override;
 void glDetachShaderFn(GLuint program, GLuint shader) override;
 void glDisableFn(GLenum cap) override;
+void glDisableExtensionANGLEFn(const char* name) override;
 void glDisableVertexAttribArrayFn(GLuint index) override;
 void glDiscardFramebufferEXTFn(GLenum target,
                                GLsizei numAttachments,
diff --git a/ui/gl/gl_bindings_autogen_gl.cc b/ui/gl/gl_bindings_autogen_gl.cc
index c0daa7c..aa49c5e 100644
--- a/ui/gl/gl_bindings_autogen_gl.cc
+++ b/ui/gl/gl_bindings_autogen_gl.cc
@@ -417,6 +417,8 @@
   ext.b_GL_KHR_parallel_shader_compile =
       gfx::HasExtension(extensions, "GL_KHR_parallel_shader_compile");
   ext.b_GL_KHR_robustness = gfx::HasExtension(extensions, "GL_KHR_robustness");
+  ext.b_GL_MESA_framebuffer_flip_y =
+      gfx::HasExtension(extensions, "GL_MESA_framebuffer_flip_y");
   ext.b_GL_NV_blend_equation_advanced =
       gfx::HasExtension(extensions, "GL_NV_blend_equation_advanced");
   ext.b_GL_NV_fence = gfx::HasExtension(extensions, "GL_NV_fence");
@@ -900,6 +902,12 @@
         reinterpret_cast<glDepthRangefProc>(GetGLProcAddress("glDepthRangef"));
   }
 
+  if (ext.b_GL_ANGLE_request_extension) {
+    fn.glDisableExtensionANGLEFn =
+        reinterpret_cast<glDisableExtensionANGLEProc>(
+            GetGLProcAddress("glDisableExtensionANGLE"));
+  }
+
   if (ext.b_GL_EXT_discard_framebuffer) {
     fn.glDiscardFramebufferEXTFn =
         reinterpret_cast<glDiscardFramebufferEXTProc>(
@@ -1049,6 +1057,10 @@
     fn.glFramebufferParameteriFn =
         reinterpret_cast<glFramebufferParameteriProc>(
             GetGLProcAddress("glFramebufferParameteri"));
+  } else if (ext.b_GL_MESA_framebuffer_flip_y) {
+    fn.glFramebufferParameteriFn =
+        reinterpret_cast<glFramebufferParameteriProc>(
+            GetGLProcAddress("glFramebufferParameteriMESA"));
   }
 
   if (ver->IsAtLeastGL(3u, 0u) || ver->is_es) {
@@ -3482,6 +3494,10 @@
   driver_->fn.glDisableFn(cap);
 }
 
+void GLApiBase::glDisableExtensionANGLEFn(const char* name) {
+  driver_->fn.glDisableExtensionANGLEFn(name);
+}
+
 void GLApiBase::glDisableVertexAttribArrayFn(GLuint index) {
   driver_->fn.glDisableVertexAttribArrayFn(index);
 }
@@ -6806,6 +6822,11 @@
   gl_api_->glDisableFn(cap);
 }
 
+void TraceGLApi::glDisableExtensionANGLEFn(const char* name) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glDisableExtensionANGLE")
+  gl_api_->glDisableExtensionANGLEFn(name);
+}
+
 void TraceGLApi::glDisableVertexAttribArrayFn(GLuint index) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glDisableVertexAttribArray")
   gl_api_->glDisableVertexAttribArrayFn(index);
@@ -10813,6 +10834,12 @@
   gl_api_->glDisableFn(cap);
 }
 
+void DebugGLApi::glDisableExtensionANGLEFn(const char* name) {
+  GL_SERVICE_LOG("glDisableExtensionANGLE"
+                 << "(" << name << ")");
+  gl_api_->glDisableExtensionANGLEFn(name);
+}
+
 void DebugGLApi::glDisableVertexAttribArrayFn(GLuint index) {
   GL_SERVICE_LOG("glDisableVertexAttribArray"
                  << "(" << index << ")");
@@ -15454,6 +15481,10 @@
   NoContextHelper("glDisable");
 }
 
+void NoContextGLApi::glDisableExtensionANGLEFn(const char* name) {
+  NoContextHelper("glDisableExtensionANGLE");
+}
+
 void NoContextGLApi::glDisableVertexAttribArrayFn(GLuint index) {
   NoContextHelper("glDisableVertexAttribArray");
 }
diff --git a/ui/gl/gl_bindings_autogen_gl.h b/ui/gl/gl_bindings_autogen_gl.h
index 0c0a32de..40fe42a 100644
--- a/ui/gl/gl_bindings_autogen_gl.h
+++ b/ui/gl/gl_bindings_autogen_gl.h
@@ -381,6 +381,7 @@
 typedef void(GL_BINDING_CALL* glDetachShaderProc)(GLuint program,
                                                   GLuint shader);
 typedef void(GL_BINDING_CALL* glDisableProc)(GLenum cap);
+typedef void(GL_BINDING_CALL* glDisableExtensionANGLEProc)(const char* name);
 typedef void(GL_BINDING_CALL* glDisableVertexAttribArrayProc)(GLuint index);
 typedef void(GL_BINDING_CALL* glDiscardFramebufferEXTProc)(
     GLenum target,
@@ -1934,6 +1935,7 @@
   bool b_GL_KHR_debug;
   bool b_GL_KHR_parallel_shader_compile;
   bool b_GL_KHR_robustness;
+  bool b_GL_MESA_framebuffer_flip_y;
   bool b_GL_NV_blend_equation_advanced;
   bool b_GL_NV_fence;
   bool b_GL_NV_framebuffer_mixed_samples;
@@ -2051,6 +2053,7 @@
   glDepthRangefProc glDepthRangefFn;
   glDetachShaderProc glDetachShaderFn;
   glDisableProc glDisableFn;
+  glDisableExtensionANGLEProc glDisableExtensionANGLEFn;
   glDisableVertexAttribArrayProc glDisableVertexAttribArrayFn;
   glDiscardFramebufferEXTProc glDiscardFramebufferEXTFn;
   glDispatchComputeProc glDispatchComputeFn;
@@ -2782,6 +2785,7 @@
   virtual void glDepthRangefFn(GLclampf zNear, GLclampf zFar) = 0;
   virtual void glDetachShaderFn(GLuint program, GLuint shader) = 0;
   virtual void glDisableFn(GLenum cap) = 0;
+  virtual void glDisableExtensionANGLEFn(const char* name) = 0;
   virtual void glDisableVertexAttribArrayFn(GLuint index) = 0;
   virtual void glDiscardFramebufferEXTFn(GLenum target,
                                          GLsizei numAttachments,
@@ -4244,6 +4248,8 @@
 #define glDepthRangef ::gl::g_current_gl_context->glDepthRangefFn
 #define glDetachShader ::gl::g_current_gl_context->glDetachShaderFn
 #define glDisable ::gl::g_current_gl_context->glDisableFn
+#define glDisableExtensionANGLE \
+  ::gl::g_current_gl_context->glDisableExtensionANGLEFn
 #define glDisableVertexAttribArray \
   ::gl::g_current_gl_context->glDisableVertexAttribArrayFn
 #define glDiscardFramebufferEXT \
diff --git a/ui/gl/gl_bindings_autogen_mock.cc b/ui/gl/gl_bindings_autogen_mock.cc
index 6867ceb1..e65a9f3 100644
--- a/ui/gl/gl_bindings_autogen_mock.cc
+++ b/ui/gl/gl_bindings_autogen_mock.cc
@@ -1115,6 +1115,12 @@
 }
 
 void GL_BINDING_CALL
+MockGLInterface::Mock_glDisableExtensionANGLE(const char* name) {
+  MakeGlMockFunctionUnique("glDisableExtensionANGLE");
+  interface_->DisableExtensionANGLE(name);
+}
+
+void GL_BINDING_CALL
 MockGLInterface::Mock_glDisableVertexAttribArray(GLuint index) {
   MakeGlMockFunctionUnique("glDisableVertexAttribArray");
   interface_->DisableVertexAttribArray(index);
@@ -1376,6 +1382,14 @@
 }
 
 void GL_BINDING_CALL
+MockGLInterface::Mock_glFramebufferParameteriMESA(GLenum target,
+                                                  GLenum pname,
+                                                  GLint param) {
+  MakeGlMockFunctionUnique("glFramebufferParameteriMESA");
+  interface_->FramebufferParameteri(target, pname, param);
+}
+
+void GL_BINDING_CALL
 MockGLInterface::Mock_glFramebufferRenderbuffer(GLenum target,
                                                 GLenum attachment,
                                                 GLenum renderbuffertarget,
@@ -5349,6 +5363,9 @@
     return reinterpret_cast<GLFunctionPointerType>(Mock_glDetachShader);
   if (strcmp(name, "glDisable") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glDisable);
+  if (strcmp(name, "glDisableExtensionANGLE") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDisableExtensionANGLE);
   if (strcmp(name, "glDisableVertexAttribArray") == 0)
     return reinterpret_cast<GLFunctionPointerType>(
         Mock_glDisableVertexAttribArray);
@@ -5440,6 +5457,9 @@
   if (strcmp(name, "glFramebufferParameteri") == 0)
     return reinterpret_cast<GLFunctionPointerType>(
         Mock_glFramebufferParameteri);
+  if (strcmp(name, "glFramebufferParameteriMESA") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glFramebufferParameteriMESA);
   if (strcmp(name, "glFramebufferRenderbuffer") == 0)
     return reinterpret_cast<GLFunctionPointerType>(
         Mock_glFramebufferRenderbuffer);
diff --git a/ui/gl/gl_bindings_autogen_mock.h b/ui/gl/gl_bindings_autogen_mock.h
index ba9d5cc..a5b213b 100644
--- a/ui/gl/gl_bindings_autogen_mock.h
+++ b/ui/gl/gl_bindings_autogen_mock.h
@@ -485,6 +485,7 @@
 static void GL_BINDING_CALL Mock_glDepthRangef(GLclampf zNear, GLclampf zFar);
 static void GL_BINDING_CALL Mock_glDetachShader(GLuint program, GLuint shader);
 static void GL_BINDING_CALL Mock_glDisable(GLenum cap);
+static void GL_BINDING_CALL Mock_glDisableExtensionANGLE(const char* name);
 static void GL_BINDING_CALL Mock_glDisableVertexAttribArray(GLuint index);
 static void GL_BINDING_CALL
 Mock_glDiscardFramebufferEXT(GLenum target,
@@ -575,6 +576,9 @@
 static void GL_BINDING_CALL Mock_glFramebufferParameteri(GLenum target,
                                                          GLenum pname,
                                                          GLint param);
+static void GL_BINDING_CALL Mock_glFramebufferParameteriMESA(GLenum target,
+                                                             GLenum pname,
+                                                             GLint param);
 static void GL_BINDING_CALL
 Mock_glFramebufferRenderbuffer(GLenum target,
                                GLenum attachment,
diff --git a/ui/gl/gl_enums_implementation_autogen.h b/ui/gl/gl_enums_implementation_autogen.h
index 1d35215..a3e265c 100644
--- a/ui/gl/gl_enums_implementation_autogen.h
+++ b/ui/gl/gl_enums_implementation_autogen.h
@@ -26,7 +26,7 @@
     },
     {
         0x00000001,
-        "GL_SYNC_FLUSH_COMMANDS_BIT_APPLE",
+        "GL_SUBGROUP_FEATURE_BASIC_BIT_KHR",
     },
     {
         0x00000002,
@@ -34,7 +34,7 @@
     },
     {
         0x00000004,
-        "GL_GEOMETRY_SHADER_BIT_OES",
+        "GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR",
     },
     {
         0x00000008,
@@ -42,19 +42,19 @@
     },
     {
         0x00000010,
-        "GL_TESS_EVALUATION_SHADER_BIT_OES",
+        "GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR",
     },
     {
         0x00000020,
-        "GL_COLOR_BUFFER_BIT5_QCOM",
+        "GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR",
     },
     {
         0x00000040,
-        "GL_COLOR_BUFFER_BIT6_QCOM",
+        "GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR",
     },
     {
         0x00000080,
-        "GL_COLOR_BUFFER_BIT7_QCOM",
+        "GL_SUBGROUP_FEATURE_QUAD_BIT_KHR",
     },
     {
         0x00000100,
@@ -2097,6 +2097,10 @@
         "GL_VERTEX_ATTRIB_ARRAY_POINTER",
     },
     {
+        0x864F,
+        "GL_DEPTH_CLAMP_EXT",
+    },
+    {
         0x86A1,
         "GL_TEXTURE_COMPRESSED",
     },
@@ -3977,6 +3981,70 @@
         "GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET",
     },
     {
+        0x8E60,
+        "GL_MAX_MESH_UNIFORM_BLOCKS_NV",
+    },
+    {
+        0x8E61,
+        "GL_MAX_MESH_TEXTURE_IMAGE_UNITS_NV",
+    },
+    {
+        0x8E62,
+        "GL_MAX_MESH_IMAGE_UNIFORMS_NV",
+    },
+    {
+        0x8E63,
+        "GL_MAX_MESH_UNIFORM_COMPONENTS_NV",
+    },
+    {
+        0x8E64,
+        "GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_NV",
+    },
+    {
+        0x8E65,
+        "GL_MAX_MESH_ATOMIC_COUNTERS_NV",
+    },
+    {
+        0x8E66,
+        "GL_MAX_MESH_SHADER_STORAGE_BLOCKS_NV",
+    },
+    {
+        0x8E67,
+        "GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_NV",
+    },
+    {
+        0x8E68,
+        "GL_MAX_TASK_UNIFORM_BLOCKS_NV",
+    },
+    {
+        0x8E69,
+        "GL_MAX_TASK_TEXTURE_IMAGE_UNITS_NV",
+    },
+    {
+        0x8E6A,
+        "GL_MAX_TASK_IMAGE_UNIFORMS_NV",
+    },
+    {
+        0x8E6B,
+        "GL_MAX_TASK_UNIFORM_COMPONENTS_NV",
+    },
+    {
+        0x8E6C,
+        "GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_NV",
+    },
+    {
+        0x8E6D,
+        "GL_MAX_TASK_ATOMIC_COUNTERS_NV",
+    },
+    {
+        0x8E6E,
+        "GL_MAX_TASK_SHADER_STORAGE_BLOCKS_NV",
+    },
+    {
+        0x8E6F,
+        "GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_NV",
+    },
+    {
         0x8E72,
         "GL_PATCH_VERTICES_OES",
     },
@@ -4261,6 +4329,10 @@
         "GL_PERFMON_GLOBAL_MODE_QCOM",
     },
     {
+        0x8FA1,
+        "GL_MAX_SHADER_SUBSAMPLED_IMAGE_UNITS_QCOM",
+    },
+    {
         0x8FB0,
         "GL_BINNING_CONTROL_HINT_QCOM",
     },
@@ -5637,6 +5709,10 @@
         "GL_FRAGMENT_COVERAGE_COLOR_NV",
     },
     {
+        0x92DF,
+        "GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_NV",
+    },
+    {
         0x92E0,
         "GL_DEBUG_OUTPUT_KHR",
     },
@@ -6037,6 +6113,10 @@
         "GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV",
     },
     {
+        0x937F,
+        "GL_REPRESENTATIVE_FRAGMENT_TEST_NV",
+    },
+    {
         0x9380,
         "GL_NUM_SAMPLE_COUNTS",
     },
@@ -6333,6 +6413,66 @@
         "GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT",
     },
     {
+        0x9532,
+        "GL_SUBGROUP_SIZE_KHR",
+    },
+    {
+        0x9533,
+        "GL_SUBGROUP_SUPPORTED_STAGES_KHR",
+    },
+    {
+        0x9534,
+        "GL_SUBGROUP_SUPPORTED_FEATURES_KHR",
+    },
+    {
+        0x9535,
+        "GL_SUBGROUP_QUAD_ALL_STAGES_KHR",
+    },
+    {
+        0x9536,
+        "GL_MAX_MESH_TOTAL_MEMORY_SIZE_NV",
+    },
+    {
+        0x9537,
+        "GL_MAX_TASK_TOTAL_MEMORY_SIZE_NV",
+    },
+    {
+        0x9538,
+        "GL_MAX_MESH_OUTPUT_VERTICES_NV",
+    },
+    {
+        0x9539,
+        "GL_MAX_MESH_OUTPUT_PRIMITIVES_NV",
+    },
+    {
+        0x953A,
+        "GL_MAX_TASK_OUTPUT_COUNT_NV",
+    },
+    {
+        0x953B,
+        "GL_MAX_MESH_WORK_GROUP_SIZE_NV",
+    },
+    {
+        0x953C,
+        "GL_MAX_TASK_WORK_GROUP_SIZE_NV",
+    },
+    {
+        0x953D,
+        "GL_MAX_DRAW_MESH_TASKS_COUNT_NV",
+    },
+    {
+        0x953E,
+        "GL_MESH_WORK_GROUP_SIZE_NV",
+    },
+    {
+        0x953F,
+        "GL_TASK_WORK_GROUP_SIZE_NV",
+    },
+    {
+        0x9543,
+        "GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_NV",
+    },
+    {
         0x954D,
         "GL_CONSERVATIVE_RASTER_MODE_NV",
     },
@@ -6349,6 +6489,126 @@
         "GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV",
     },
     {
+        0x9555,
+        "GL_SCISSOR_TEST_EXCLUSIVE_NV",
+    },
+    {
+        0x9556,
+        "GL_SCISSOR_BOX_EXCLUSIVE_NV",
+    },
+    {
+        0x9557,
+        "GL_MAX_MESH_VIEWS_NV",
+    },
+    {
+        0x9559,
+        "GL_MESH_SHADER_NV",
+    },
+    {
+        0x955A,
+        "GL_TASK_SHADER_NV",
+    },
+    {
+        0x955B,
+        "GL_SHADING_RATE_IMAGE_BINDING_NV",
+    },
+    {
+        0x955C,
+        "GL_SHADING_RATE_IMAGE_TEXEL_WIDTH_NV",
+    },
+    {
+        0x955D,
+        "GL_SHADING_RATE_IMAGE_TEXEL_HEIGHT_NV",
+    },
+    {
+        0x955E,
+        "GL_SHADING_RATE_IMAGE_PALETTE_SIZE_NV",
+    },
+    {
+        0x955F,
+        "GL_MAX_COARSE_FRAGMENT_SAMPLES_NV",
+    },
+    {
+        0x9563,
+        "GL_SHADING_RATE_IMAGE_NV",
+    },
+    {
+        0x9564,
+        "GL_SHADING_RATE_NO_INVOCATIONS_NV",
+    },
+    {
+        0x9565,
+        "GL_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV",
+    },
+    {
+        0x9566,
+        "GL_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV",
+    },
+    {
+        0x9567,
+        "GL_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV",
+    },
+    {
+        0x9568,
+        "GL_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV",
+    },
+    {
+        0x9569,
+        "GL_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV",
+    },
+    {
+        0x956A,
+        "GL_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV",
+    },
+    {
+        0x956B,
+        "GL_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV",
+    },
+    {
+        0x956C,
+        "GL_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV",
+    },
+    {
+        0x956D,
+        "GL_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV",
+    },
+    {
+        0x956E,
+        "GL_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV",
+    },
+    {
+        0x956F,
+        "GL_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV",
+    },
+    {
+        0x9579,
+        "GL_MESH_VERTICES_OUT_NV",
+    },
+    {
+        0x957A,
+        "GL_MESH_PRIMITIVES_OUT_NV",
+    },
+    {
+        0x957B,
+        "GL_MESH_OUTPUT_TYPE_NV",
+    },
+    {
+        0x957C,
+        "GL_MESH_SUBROUTINE_NV",
+    },
+    {
+        0x957D,
+        "GL_TASK_SUBROUTINE_NV",
+    },
+    {
+        0x957E,
+        "GL_MESH_SUBROUTINE_UNIFORM_NV",
+    },
+    {
+        0x957F,
+        "GL_TASK_SUBROUTINE_UNIFORM_NV",
+    },
+    {
         0x9580,
         "GL_TEXTURE_TILING_EXT",
     },
@@ -6461,6 +6721,90 @@
         "GL_PROTECTED_MEMORY_OBJECT_EXT",
     },
     {
+        0x959C,
+        "GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_NV",
+    },
+    {
+        0x959D,
+        "GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_NV",
+    },
+    {
+        0x959E,
+        "GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_NV",
+    },
+    {
+        0x959F,
+        "GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_NV",
+    },
+    {
+        0x95A0,
+        "GL_REFERENCED_BY_MESH_SHADER_NV",
+    },
+    {
+        0x95A1,
+        "GL_REFERENCED_BY_TASK_SHADER_NV",
+    },
+    {
+        0x95A2,
+        "GL_MAX_MESH_WORK_GROUP_INVOCATIONS_NV",
+    },
+    {
+        0x95A3,
+        "GL_MAX_TASK_WORK_GROUP_INVOCATIONS_NV",
+    },
+    {
+        0x95A4,
+        "GL_ATTACHED_MEMORY_OBJECT_NV",
+    },
+    {
+        0x95A5,
+        "GL_ATTACHED_MEMORY_OFFSET_NV",
+    },
+    {
+        0x95A6,
+        "GL_MEMORY_ATTACHABLE_ALIGNMENT_NV",
+    },
+    {
+        0x95A7,
+        "GL_MEMORY_ATTACHABLE_SIZE_NV",
+    },
+    {
+        0x95A8,
+        "GL_MEMORY_ATTACHABLE_NV",
+    },
+    {
+        0x95A9,
+        "GL_DETACHED_MEMORY_INCARNATION_NV",
+    },
+    {
+        0x95AA,
+        "GL_DETACHED_TEXTURES_NV",
+    },
+    {
+        0x95AB,
+        "GL_DETACHED_BUFFERS_NV",
+    },
+    {
+        0x95AC,
+        "GL_MAX_DETACHED_TEXTURES_NV",
+    },
+    {
+        0x95AD,
+        "GL_MAX_DETACHED_BUFFERS_NV",
+    },
+    {
+        0x95AE,
+        "GL_SHADING_RATE_SAMPLE_ORDER_DEFAULT_NV",
+    },
+    {
+        0x95AF,
+        "GL_SHADING_RATE_SAMPLE_ORDER_PIXEL_MAJOR_NV",
+    },
+    {
+        0x95B0,
+        "GL_SHADING_RATE_SAMPLE_ORDER_SAMPLE_MAJOR_NV",
+    },
+    {
         0x9630,
         "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR",
     },
diff --git a/ui/gl/gl_mock_autogen_gl.h b/ui/gl/gl_mock_autogen_gl.h
index 629f1b06..d8b0d20d 100644
--- a/ui/gl/gl_mock_autogen_gl.h
+++ b/ui/gl/gl_mock_autogen_gl.h
@@ -291,6 +291,7 @@
 MOCK_METHOD2(DepthRangef, void(GLclampf zNear, GLclampf zFar));
 MOCK_METHOD2(DetachShader, void(GLuint program, GLuint shader));
 MOCK_METHOD1(Disable, void(GLenum cap));
+MOCK_METHOD1(DisableExtensionANGLE, void(const char* name));
 MOCK_METHOD1(DisableVertexAttribArray, void(GLuint index));
 MOCK_METHOD3(DiscardFramebufferEXT,
              void(GLenum target,
diff --git a/ui/gl/gl_stub_autogen_gl.h b/ui/gl/gl_stub_autogen_gl.h
index 97ce1ba..2a48e5f 100644
--- a/ui/gl/gl_stub_autogen_gl.h
+++ b/ui/gl/gl_stub_autogen_gl.h
@@ -332,6 +332,7 @@
 void glDepthRangefFn(GLclampf zNear, GLclampf zFar) override {}
 void glDetachShaderFn(GLuint program, GLuint shader) override {}
 void glDisableFn(GLenum cap) override {}
+void glDisableExtensionANGLEFn(const char* name) override {}
 void glDisableVertexAttribArrayFn(GLuint index) override {}
 void glDiscardFramebufferEXTFn(GLenum target,
                                GLsizei numAttachments,
diff --git a/ui/gl/init/create_gr_gl_interface.cc b/ui/gl/init/create_gr_gl_interface.cc
index 3b6d3bd..9a95fbf 100644
--- a/ui/gl/init/create_gr_gl_interface.cc
+++ b/ui/gl/init/create_gr_gl_interface.cc
@@ -660,7 +660,7 @@
   // GL_EXT_window_rectangles
   functions->fWindowRectangles = gl->glWindowRectanglesEXTFn;
 
-  // EXT_window_rectangles
+  // GL_QCOM_tiled_rendering
   functions->fStartTiling = gl->glStartTilingQCOMFn;
   functions->fEndTiling = gl->glEndTilingQCOMFn;
 
diff --git a/ui/login/oobe.css b/ui/login/oobe.css
index 5c8872c..de526a2 100644
--- a/ui/login/oobe.css
+++ b/ui/login/oobe.css
@@ -56,7 +56,7 @@
   --oobe-dialog-side-margin: 0px;
 }
 
-@media screen and (max-width: 864px) {
+@media screen and (max-width: 864px), (max-height: 736px) {
   html[screen=oobe] {
     --oobe-dialog-footer-height: 80px;
     --oobe-dialog-footer-padding: 24px;
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc b/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
index d0fe454..3ec9ff2f 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/files/platform_file.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/gpu_fence.h"
 #include "ui/ozone/common/linux/drm_util_linux.h"
@@ -57,15 +58,15 @@
 
 DrmOverlayValidator::~DrmOverlayValidator() {}
 
-std::vector<OverlayCheckReturn_Params> DrmOverlayValidator::TestPageFlip(
-    const std::vector<OverlayCheck_Params>& params,
+OverlayStatusList DrmOverlayValidator::TestPageFlip(
+    const OverlaySurfaceCandidateList& params,
     const DrmOverlayPlaneList& last_used_planes) {
-  std::vector<OverlayCheckReturn_Params> returns(params.size());
+  OverlayStatusList returns(params.size());
   HardwareDisplayController* controller = window_->GetController();
   if (!controller) {
     // The controller is not yet installed.
     for (auto& param : returns)
-      param.status = OVERLAY_STATUS_NOT;
+      param = OVERLAY_STATUS_NOT;
 
     return returns;
   }
@@ -78,8 +79,8 @@
     reusable_buffers.push_back(plane.buffer);
 
   for (size_t i = 0; i < params.size(); ++i) {
-    if (!params[i].is_overlay_candidate) {
-      returns[i].status = OVERLAY_STATUS_NOT;
+    if (!params[i].overlay_handled) {
+      returns[i] = OVERLAY_STATUS_NOT;
       continue;
     }
 
@@ -88,12 +89,13 @@
         GetFourCCFormatFromBufferFormat(params[i].format), &reusable_buffers);
 
     DrmOverlayPlane plane(buffer, params[i].plane_z_order, params[i].transform,
-                          params[i].display_rect, params[i].crop_rect,
+                          gfx::ToNearestRect(params[i].display_rect),
+                          params[i].crop_rect,
                           /* enable_blend */ true, /* gpu_fence */ nullptr);
     test_list.push_back(std::move(plane));
 
     if (buffer && controller->TestPageFlip(test_list)) {
-      returns[i].status = OVERLAY_STATUS_ABLE;
+      returns[i] = OVERLAY_STATUS_ABLE;
     } else {
       // If test failed here, platform cannot support this configuration
       // with current combination of layers. This is usually the case when this
@@ -101,7 +103,7 @@
       // hardware resources and they might be already in use by other planes.
       // For example this plane has requested scaling capabilities and all
       // available scalars are already in use by other planes.
-      returns[i].status = OVERLAY_STATUS_NOT;
+      returns[i] = OVERLAY_STATUS_NOT;
       test_list.pop_back();
     }
   }
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator.h b/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
index 0d7bbdaa..eedf103d 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
@@ -7,12 +7,11 @@
 
 #include "base/containers/mru_cache.h"
 #include "ui/ozone/platform/drm/gpu/drm_overlay_plane.h"
+#include "ui/ozone/public/overlay_surface_candidate.h"
 
 namespace ui {
 
 class DrmWindow;
-struct OverlayCheck_Params;
-struct OverlayCheckReturn_Params;
 
 class DrmOverlayValidator {
  public:
@@ -22,9 +21,8 @@
   // Tests if configurations |params| are compatible with |window_| and finds
   // which of these configurations can be promoted to Overlay composition
   // without failing the page flip. It expects |params| to be sorted by z_order.
-  std::vector<OverlayCheckReturn_Params> TestPageFlip(
-      const std::vector<OverlayCheck_Params>& params,
-      const DrmOverlayPlaneList& last_used_planes);
+  OverlayStatusList TestPageFlip(const OverlaySurfaceCandidateList& params,
+                                 const DrmOverlayPlaneList& last_used_planes);
 
  private:
   DrmWindow* const window_;  // Not owned.
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc b/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
index f2a54c4..8b3c2a2c 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/files/platform_file.h"
 #include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/gpu_fence.h"
 #include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
 #include "ui/ozone/common/linux/drm_util_linux.h"
@@ -61,7 +62,7 @@
     return nullptr;
   }
 
-  void AddPlane(const ui::OverlayCheck_Params& params);
+  void AddPlane(const ui::OverlaySurfaceCandidate& params);
 
   scoped_refptr<ui::DrmFramebuffer> CreateBuffer() {
     auto gbm_buffer = drm_->gbm_device()->CreateBuffer(
@@ -95,7 +96,7 @@
   std::unique_ptr<ui::DrmDeviceManager> drm_device_manager_;
   ui::DrmWindow* window_;
   std::unique_ptr<ui::DrmOverlayValidator> overlay_validator_;
-  std::vector<ui::OverlayCheck_Params> overlay_params_;
+  std::vector<ui::OverlaySurfaceCandidate> overlay_params_;
   ui::DrmOverlayPlaneList plane_list_;
 
   int on_swap_buffers_count_;
@@ -141,18 +142,20 @@
 
   primary_rect_ = gfx::Rect(0, 0, kDefaultMode.hdisplay, kDefaultMode.vdisplay);
 
-  ui::OverlayCheck_Params primary_candidate;
+  ui::OverlaySurfaceCandidate primary_candidate;
   primary_candidate.buffer_size = primary_rect_.size();
-  primary_candidate.display_rect = primary_rect_;
+  primary_candidate.display_rect = gfx::RectF(primary_rect_);
   primary_candidate.format = gfx::BufferFormat::BGRX_8888;
+  primary_candidate.overlay_handled = true;
   overlay_params_.push_back(primary_candidate);
   AddPlane(primary_candidate);
 
-  ui::OverlayCheck_Params overlay_candidate;
+  ui::OverlaySurfaceCandidate overlay_candidate;
   overlay_candidate.buffer_size = overlay_rect_.size();
-  overlay_candidate.display_rect = overlay_rect_;
+  overlay_candidate.display_rect = gfx::RectF(overlay_rect_);
   overlay_candidate.plane_z_order = 1;
   overlay_candidate.format = gfx::BufferFormat::BGRX_8888;
+  overlay_candidate.overlay_handled = true;
   overlay_params_.push_back(overlay_candidate);
   AddPlane(overlay_candidate);
 }
@@ -218,14 +221,16 @@
                         /* use_atomic= */ true);
 }
 
-void DrmOverlayValidatorTest::AddPlane(const ui::OverlayCheck_Params& params) {
+void DrmOverlayValidatorTest::AddPlane(
+    const ui::OverlaySurfaceCandidate& params) {
   scoped_refptr<ui::DrmDevice> drm = window_->GetController()->GetDrmDevice();
 
   scoped_refptr<ui::DrmFramebuffer> drm_framebuffer = CreateOverlayBuffer(
       ui::GetFourCCFormatFromBufferFormat(params.format), params.buffer_size);
   plane_list_.push_back(ui::DrmOverlayPlane(
       std::move(drm_framebuffer), params.plane_z_order, params.transform,
-      params.display_rect, params.crop_rect, true, nullptr));
+      gfx::ToNearestRect(params.display_rect), params.crop_rect, true,
+      nullptr));
 }
 
 void DrmOverlayValidatorTest::TearDown() {
@@ -239,42 +244,39 @@
   // present.
   ui::HardwareDisplayController* controller = window_->GetController();
   window_->SetController(nullptr);
-  std::vector<ui::OverlayCheckReturn_Params> returns =
-      overlay_validator_->TestPageFlip(overlay_params_,
-                                       ui::DrmOverlayPlaneList());
-  EXPECT_EQ(returns.front().status, ui::OVERLAY_STATUS_NOT);
-  EXPECT_EQ(returns.back().status, ui::OVERLAY_STATUS_NOT);
+  std::vector<ui::OverlayStatus> returns = overlay_validator_->TestPageFlip(
+      overlay_params_, ui::DrmOverlayPlaneList());
+  EXPECT_EQ(returns.front(), ui::OVERLAY_STATUS_NOT);
+  EXPECT_EQ(returns.back(), ui::OVERLAY_STATUS_NOT);
   window_->SetController(controller);
 }
 
 TEST_F(DrmOverlayValidatorTest, DontPromoteMoreLayersThanAvailablePlanes) {
-  std::vector<ui::OverlayCheckReturn_Params> returns =
-      overlay_validator_->TestPageFlip(overlay_params_,
-                                       ui::DrmOverlayPlaneList());
-  EXPECT_EQ(returns.front().status, ui::OVERLAY_STATUS_ABLE);
-  EXPECT_EQ(returns.back().status, ui::OVERLAY_STATUS_NOT);
+  std::vector<ui::OverlayStatus> returns = overlay_validator_->TestPageFlip(
+      overlay_params_, ui::DrmOverlayPlaneList());
+  EXPECT_EQ(returns.front(), ui::OVERLAY_STATUS_ABLE);
+  EXPECT_EQ(returns.back(), ui::OVERLAY_STATUS_NOT);
 }
 
 TEST_F(DrmOverlayValidatorTest, DontCollapseOverlayToPrimaryInFullScreen) {
   // Overlay Validator should not collapse planes during validation.
   overlay_params_.back().buffer_size = primary_rect_.size();
-  overlay_params_.back().display_rect = primary_rect_;
+  overlay_params_.back().display_rect = gfx::RectF(primary_rect_);
   plane_list_.back().display_bounds = primary_rect_;
 
-  std::vector<ui::OverlayCheckReturn_Params> returns =
-      overlay_validator_->TestPageFlip(overlay_params_,
-                                       ui::DrmOverlayPlaneList());
+  std::vector<ui::OverlayStatus> returns = overlay_validator_->TestPageFlip(
+      overlay_params_, ui::DrmOverlayPlaneList());
   // Second candidate should be marked as Invalid as we have only one plane
   // per CRTC.
-  EXPECT_EQ(returns.front().status, ui::OVERLAY_STATUS_ABLE);
-  EXPECT_EQ(returns.back().status, ui::OVERLAY_STATUS_NOT);
+  EXPECT_EQ(returns.front(), ui::OVERLAY_STATUS_ABLE);
+  EXPECT_EQ(returns.back(), ui::OVERLAY_STATUS_NOT);
 }
 
 TEST_F(DrmOverlayValidatorTest, OverlayFormat_XRGB) {
   // This test checks for optimal format in case of non full screen video case.
   // This should be XRGB when overlay doesn't support YUV.
   overlay_params_.back().buffer_size = overlay_rect_.size();
-  overlay_params_.back().display_rect = overlay_rect_;
+  overlay_params_.back().display_rect = gfx::RectF(overlay_rect_);
   plane_list_.back().display_bounds = overlay_rect_;
 
   CrtcState state = {
@@ -286,12 +288,11 @@
   };
   InitializeDrmState(std::vector<CrtcState>(1, state));
 
-  std::vector<ui::OverlayCheckReturn_Params> returns =
-      overlay_validator_->TestPageFlip(overlay_params_,
-                                       ui::DrmOverlayPlaneList());
+  std::vector<ui::OverlayStatus> returns = overlay_validator_->TestPageFlip(
+      overlay_params_, ui::DrmOverlayPlaneList());
   EXPECT_EQ(2u, returns.size());
   for (const auto& param : returns)
-    EXPECT_EQ(param.status, ui::OVERLAY_STATUS_ABLE);
+    EXPECT_EQ(param, ui::OVERLAY_STATUS_ABLE);
 }
 
 TEST_F(DrmOverlayValidatorTest, OverlayFormat_YUV) {
@@ -300,7 +301,7 @@
   // needed.
   gfx::RectF crop_rect = gfx::RectF(0, 0, 0.5, 0.5);
   overlay_params_.back().buffer_size = overlay_rect_.size();
-  overlay_params_.back().display_rect = overlay_rect_;
+  overlay_params_.back().display_rect = gfx::RectF(overlay_rect_);
   overlay_params_.back().crop_rect = crop_rect;
   overlay_params_.back().format = gfx::BufferFormat::YUV_420_BIPLANAR;
   plane_list_.pop_back();
@@ -315,19 +316,18 @@
   };
   InitializeDrmState(std::vector<CrtcState>(1, state));
 
-  std::vector<ui::OverlayCheckReturn_Params> returns =
-      overlay_validator_->TestPageFlip(overlay_params_,
-                                       ui::DrmOverlayPlaneList());
+  std::vector<ui::OverlayStatus> returns = overlay_validator_->TestPageFlip(
+      overlay_params_, ui::DrmOverlayPlaneList());
   EXPECT_EQ(2u, returns.size());
   for (const auto& param : returns)
-    EXPECT_EQ(param.status, ui::OVERLAY_STATUS_ABLE);
+    EXPECT_EQ(param, ui::OVERLAY_STATUS_ABLE);
 }
 
 TEST_F(DrmOverlayValidatorTest, RejectYUVBuffersIfNotSupported) {
   // Check case where buffer storage format is already UYVY but planes dont
   // support it.
   overlay_params_.back().buffer_size = overlay_rect_.size();
-  overlay_params_.back().display_rect = overlay_rect_;
+  overlay_params_.back().display_rect = gfx::RectF(overlay_rect_);
   overlay_params_.back().format = gfx::BufferFormat::YUV_420_BIPLANAR;
   plane_list_.pop_back();
   AddPlane(overlay_params_.back());
@@ -341,12 +341,11 @@
   };
   InitializeDrmState(std::vector<CrtcState>(1, state));
 
-  std::vector<ui::OverlayCheck_Params> validated_params = overlay_params_;
-  std::vector<ui::OverlayCheckReturn_Params> returns =
-      overlay_validator_->TestPageFlip(validated_params,
-                                       ui::DrmOverlayPlaneList());
+  std::vector<ui::OverlaySurfaceCandidate> validated_params = overlay_params_;
+  std::vector<ui::OverlayStatus> returns = overlay_validator_->TestPageFlip(
+      validated_params, ui::DrmOverlayPlaneList());
   EXPECT_EQ(2u, returns.size());
-  EXPECT_EQ(returns.back().status, ui::OVERLAY_STATUS_NOT);
+  EXPECT_EQ(returns.back(), ui::OVERLAY_STATUS_NOT);
 }
 
 TEST_F(DrmOverlayValidatorTest,
@@ -379,18 +378,17 @@
 
   gfx::RectF crop_rect = gfx::RectF(0, 0, 0.5, 0.5);
   overlay_params_.back().buffer_size = overlay_rect_.size();
-  overlay_params_.back().display_rect = overlay_rect_;
+  overlay_params_.back().display_rect = gfx::RectF(overlay_rect_);
   overlay_params_.back().crop_rect = crop_rect;
   plane_list_.back().display_bounds = overlay_rect_;
   plane_list_.back().crop_rect = crop_rect;
 
-  std::vector<ui::OverlayCheck_Params> validated_params = overlay_params_;
+  std::vector<ui::OverlaySurfaceCandidate> validated_params = overlay_params_;
   validated_params.back().format = gfx::BufferFormat::YUV_420_BIPLANAR;
-  std::vector<ui::OverlayCheckReturn_Params> returns =
-      overlay_validator_->TestPageFlip(validated_params,
-                                       ui::DrmOverlayPlaneList());
+  std::vector<ui::OverlayStatus> returns = overlay_validator_->TestPageFlip(
+      validated_params, ui::DrmOverlayPlaneList());
   EXPECT_EQ(2u, returns.size());
-  EXPECT_EQ(returns.back().status, ui::OVERLAY_STATUS_ABLE);
+  EXPECT_EQ(returns.back(), ui::OVERLAY_STATUS_ABLE);
 
   // This configuration should not be promoted to Overlay when either of the
   // controllers dont support UYVY format.
@@ -402,7 +400,7 @@
   returns = overlay_validator_->TestPageFlip(validated_params,
                                              ui::DrmOverlayPlaneList());
   EXPECT_EQ(2u, returns.size());
-  EXPECT_EQ(returns.back().status, ui::OVERLAY_STATUS_NOT);
+  EXPECT_EQ(returns.back(), ui::OVERLAY_STATUS_NOT);
 
   // Check case where we dont have support for packed formats in primary
   // display.
@@ -413,7 +411,7 @@
   returns = overlay_validator_->TestPageFlip(validated_params,
                                              ui::DrmOverlayPlaneList());
   EXPECT_EQ(2u, returns.size());
-  EXPECT_EQ(returns.back().status, ui::OVERLAY_STATUS_NOT);
+  EXPECT_EQ(returns.back(), ui::OVERLAY_STATUS_NOT);
   controller->RemoveCrtc(drm_, kCrtcIdBase + 1);
 }
 
@@ -444,14 +442,13 @@
   EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode));
 
   overlay_params_.back().buffer_size = overlay_rect_.size();
-  overlay_params_.back().display_rect = overlay_rect_;
+  overlay_params_.back().display_rect = gfx::RectF(overlay_rect_);
   plane_list_.back().display_bounds = overlay_rect_;
 
-  std::vector<ui::OverlayCheckReturn_Params> returns =
-      overlay_validator_->TestPageFlip(overlay_params_,
-                                       ui::DrmOverlayPlaneList());
+  std::vector<ui::OverlayStatus> returns = overlay_validator_->TestPageFlip(
+      overlay_params_, ui::DrmOverlayPlaneList());
   EXPECT_EQ(2u, returns.size());
-  EXPECT_EQ(returns.back().status, ui::OVERLAY_STATUS_ABLE);
+  EXPECT_EQ(returns.back(), ui::OVERLAY_STATUS_ABLE);
 
   // Check case where we dont have support for packed formats in Mirrored CRTC.
   crtc_states[1].planes[1].formats = {DRM_FORMAT_XRGB8888};
@@ -459,7 +456,7 @@
 
   returns = overlay_validator_->TestPageFlip(overlay_params_,
                                              ui::DrmOverlayPlaneList());
-  EXPECT_EQ(returns.back().status, ui::OVERLAY_STATUS_ABLE);
+  EXPECT_EQ(returns.back(), ui::OVERLAY_STATUS_ABLE);
 
   // Check case where we dont have support for packed formats in primary
   // display.
@@ -470,7 +467,7 @@
   returns = overlay_validator_->TestPageFlip(overlay_params_,
                                              ui::DrmOverlayPlaneList());
   EXPECT_EQ(2u, returns.size());
-  EXPECT_EQ(returns.back().status, ui::OVERLAY_STATUS_ABLE);
+  EXPECT_EQ(returns.back(), ui::OVERLAY_STATUS_ABLE);
 
   controller->RemoveCrtc(drm_, kCrtcIdBase + 1);
 }
@@ -480,9 +477,31 @@
   // In that case we should reject the overlay candidate.
   gbm_->set_allocation_failure(true);
 
-  std::vector<ui::OverlayCheckReturn_Params> returns =
-      overlay_validator_->TestPageFlip(overlay_params_,
-                                       ui::DrmOverlayPlaneList());
+  std::vector<ui::OverlayStatus> returns = overlay_validator_->TestPageFlip(
+      overlay_params_, ui::DrmOverlayPlaneList());
   EXPECT_EQ(2u, returns.size());
-  EXPECT_EQ(returns.front().status, ui::OVERLAY_STATUS_NOT);
+  EXPECT_EQ(returns.front(), ui::OVERLAY_STATUS_NOT);
+}
+
+// This test verifies that the Ozone/DRM implementation does not reject overlay
+// candidates purely on the basis of having non-integer bounds. Instead, they
+// should be rounded to the nearest integer.
+TEST_F(DrmOverlayValidatorTest, NonIntegerDisplayRect) {
+  overlay_params_.back().display_rect.Inset(0.005f, 0.005f);
+  plane_list_.pop_back();
+  AddPlane(overlay_params_.back());
+  CrtcState state = {
+      /* .planes = */
+      {
+          {/* .formats = */ {DRM_FORMAT_XRGB8888}},
+          {/* .formats = */ {DRM_FORMAT_XRGB8888, DRM_FORMAT_NV12}},
+      },
+  };
+  InitializeDrmState(std::vector<CrtcState>(1, state));
+
+  std::vector<ui::OverlayStatus> returns = overlay_validator_->TestPageFlip(
+      overlay_params_, ui::DrmOverlayPlaneList());
+  EXPECT_EQ(2u, returns.size());
+  for (const auto& param : returns)
+    EXPECT_EQ(param, ui::OVERLAY_STATUS_ABLE);
 }
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.cc b/ui/ozone/platform/drm/gpu/drm_thread.cc
index 43be05a..0dfcf9f 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread.cc
@@ -303,11 +303,9 @@
                             const OverlayStatusList&)> callback) {
   TRACE_EVENT0("drm,hwoverlays", "DrmThread::CheckOverlayCapabilities");
 
-  auto params = CreateParamsFromOverlaySurfaceCandidate(overlays);
   std::move(callback).Run(
       widget, overlays,
-      CreateOverlayStatusListFrom(
-          screen_manager_->GetWindow(widget)->TestPageFlip(params)));
+      screen_manager_->GetWindow(widget)->TestPageFlip(overlays));
 }
 
 void DrmThread::GetDeviceCursor(
diff --git a/ui/ozone/platform/drm/gpu/drm_window.cc b/ui/ozone/platform/drm/gpu/drm_window.cc
index 49802589..310e2e726 100644
--- a/ui/ozone/platform/drm/gpu/drm_window.cc
+++ b/ui/ozone/platform/drm/gpu/drm_window.cc
@@ -128,8 +128,8 @@
                                 std::move(presentation_callback));
 }
 
-std::vector<OverlayCheckReturn_Params> DrmWindow::TestPageFlip(
-    const std::vector<OverlayCheck_Params>& overlay_params) {
+OverlayStatusList DrmWindow::TestPageFlip(
+    const OverlaySurfaceCandidateList& overlay_params) {
   return overlay_validator_->TestPageFlip(overlay_params,
                                           last_submitted_planes_);
 }
diff --git a/ui/ozone/platform/drm/gpu/drm_window.h b/ui/ozone/platform/drm/gpu/drm_window.h
index a9aba6d..ea2a272 100644
--- a/ui/ozone/platform/drm/gpu/drm_window.h
+++ b/ui/ozone/platform/drm/gpu/drm_window.h
@@ -17,6 +17,7 @@
 #include "ui/gfx/vsync_provider.h"
 #include "ui/ozone/platform/drm/gpu/drm_overlay_plane.h"
 #include "ui/ozone/platform/drm/gpu/page_flip_request.h"
+#include "ui/ozone/public/overlay_surface_candidate.h"
 #include "ui/ozone/public/swap_completion_callback.h"
 
 class SkBitmap;
@@ -31,8 +32,6 @@
 class DrmDeviceManager;
 class DrmOverlayValidator;
 class HardwareDisplayController;
-struct OverlayCheck_Params;
-struct OverlayCheckReturn_Params;
 class ScreenManager;
 
 // The GPU object representing a window.
@@ -82,8 +81,8 @@
   void SchedulePageFlip(std::vector<DrmOverlayPlane> planes,
                         SwapCompletionOnceCallback submission_callback,
                         PresentationOnceCallback presentation_callback);
-  std::vector<OverlayCheckReturn_Params> TestPageFlip(
-      const std::vector<OverlayCheck_Params>& overlay_params);
+  OverlayStatusList TestPageFlip(
+      const OverlaySurfaceCandidateList& overlay_params);
 
   // Returns the last buffer associated with this window.
   const DrmOverlayPlane* GetLastModesetBuffer() const;
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 3c290930..dc8c8fa 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -861,6 +861,12 @@
       <message name="IDS_SHELF_STATE_AUTO_HIDE" desc="Accessibility announcement notifying users that the shelf will now hide itself automatically">
         Shelf auto hidden
       </message>
+      <message name="IDS_SHELF_NEXT" desc="Tooltip for the shelf arrow button that scrolls the shelf forward">
+        Next
+      </message>
+      <message name="IDS_SHELF_PREVIOUS" desc="Tooltip for the shelf arrow button that scrolls the shelf backward">
+        Previous
+      </message>
       <message name="IDS_REMOVE_ZERO_STATE_SUGGESTION_TITLE" desc="Titlebar of removing zero state suggestion confirmation dialog">
         Delete this search from your history?
       </message>
@@ -1014,6 +1020,19 @@
       <message name="IDS_BROWSER_SHARING_SHARED_CLIPBOARD_ERROR_DIALOG_TEXT_PAYLOAD_TOO_LARGE" desc="The text to be shown on the dialog when the text shared is too large.">
         Try sharing the text in smaller chunks.
       </message>
+
+      <if expr="is_android">
+        <!-- DevUI DFM Loader error strings -->
+        <message name="IDS_DEV_UI_LOADER_ERROR_HEADING" desc="Heading text for error page shown when Chrome fails to dynamically install the DevUI module.">
+          Something went wrong
+        </message>
+        <message name="IDS_DEV_UI_LOADER_ERROR_SUGGEST_RELOAD" desc="Suggests to reload page to retry install of the DevUI module.">
+          Reloading this page
+        </message>
+        <message name="IDS_DEV_UI_LOADER_ERROR_SUGGEST_CHECK_INTERNET" desc="Suggests to check internet connection if retrying to install the DevUI module.">
+          Checking your internet connection
+        </message>
+      </if>
     </messages>
   </release>
 </grit>
diff --git a/ui/views/animation/compositor_animation_runner.cc b/ui/views/animation/compositor_animation_runner.cc
index 07aa4e4..f6a35410 100644
--- a/ui/views/animation/compositor_animation_runner.cc
+++ b/ui/views/animation/compositor_animation_runner.cc
@@ -59,11 +59,12 @@
   compositor_ = nullptr;
 }
 
-void CompositorAnimationRunner::OnStart(base::TimeDelta min_interval) {
+void CompositorAnimationRunner::OnStart(base::TimeDelta min_interval,
+                                        base::TimeDelta elapsed) {
   if (!compositor_)
     return;
 
-  last_tick_ = base::TimeTicks::Now();
+  last_tick_ = base::TimeTicks::Now() - elapsed;
   min_interval_ = min_interval;
   DCHECK(!compositor_->HasAnimationObserver(this));
   compositor_->AddAnimationObserver(this);
diff --git a/ui/views/animation/compositor_animation_runner.h b/ui/views/animation/compositor_animation_runner.h
index b1da6213..3833f22 100644
--- a/ui/views/animation/compositor_animation_runner.h
+++ b/ui/views/animation/compositor_animation_runner.h
@@ -30,7 +30,7 @@
 
  protected:
   // gfx::AnimationRunner:
-  void OnStart(base::TimeDelta min_interval) override;
+  void OnStart(base::TimeDelta min_interval, base::TimeDelta elapsed) override;
 
  private:
   // This observes Compositor's destruction and helps CompositorAnimationRunner
diff --git a/ui/views/controls/button/label_button_label_unittest.cc b/ui/views/controls/button/label_button_label_unittest.cc
index 043b13a..44ae839 100644
--- a/ui/views/controls/button/label_button_label_unittest.cc
+++ b/ui/views/controls/button/label_button_label_unittest.cc
@@ -97,7 +97,7 @@
   // buttons use label colors. See LabelButton::ResetColorsFromNativeTheme().
   theme1_.Set(ui::NativeTheme::kColorId_LabelEnabledColor, SK_ColorGREEN);
   theme1_.Set(ui::NativeTheme::kColorId_LabelDisabledColor, SK_ColorYELLOW);
-  label_->SetNativeTheme(&theme1_);
+  label_->SetNativeThemeForTesting(&theme1_);
 
   // Setting the theme should paint.
   EXPECT_EQ(SK_ColorGREEN, last_color_);
@@ -110,7 +110,7 @@
   // Widget triggers it (which it can do as a friend of RootView).
   theme2_.Set(ui::NativeTheme::kColorId_LabelEnabledColor, SK_ColorBLUE);
   theme2_.Set(ui::NativeTheme::kColorId_LabelDisabledColor, SK_ColorGRAY);
-  label_->SetNativeTheme(&theme2_);
+  label_->SetNativeThemeForTesting(&theme2_);
 
   EXPECT_EQ(SK_ColorGRAY, last_color_);
 
@@ -130,7 +130,7 @@
   EXPECT_EQ(SK_ColorMAGENTA, last_color_);
 
   // Disabled still overridden after a theme change.
-  label_->SetNativeTheme(&theme1_);
+  label_->SetNativeThemeForTesting(&theme1_);
   EXPECT_EQ(SK_ColorMAGENTA, last_color_);
 
   // The enabled color still gets its value from the theme.
diff --git a/ui/views/controls/focus_ring.cc b/ui/views/controls/focus_ring.cc
index 085ad5e..3f99aa6 100644
--- a/ui/views/controls/focus_ring.cc
+++ b/ui/views/controls/focus_ring.cc
@@ -41,11 +41,6 @@
       return highlight_path;
   }
 
-  // TODO(pbos): Remove kHighlightPathKey in favor of HighlightPathGenerators.
-  SkPath* highlight_path = view->GetProperty(kHighlightPathKey);
-  if (highlight_path)
-    return *highlight_path;
-
   const double corner_radius = GetCornerRadius();
   return SkPath().addRRect(SkRRect::MakeRectXY(
       RectToSkRect(view->GetLocalBounds()), corner_radius, corner_radius));
diff --git a/ui/views/view.cc b/ui/views/view.cc
index b8b9e4a1..988b1d47 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -1053,7 +1053,7 @@
   return ui::NativeTheme::GetInstanceForNativeUi();
 }
 
-void View::SetNativeTheme(ui::NativeTheme* theme) {
+void View::SetNativeThemeForTesting(ui::NativeTheme* theme) {
   ui::NativeTheme* original_native_theme = GetNativeTheme();
   native_theme_ = theme;
   if (native_theme_ != original_native_theme)
diff --git a/ui/views/view.h b/ui/views/view.h
index c402349..918a3906 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -872,7 +872,7 @@
   const ui::NativeTheme* GetNativeTheme() const;
 
   // Sets the native theme and informs descendants.
-  void SetNativeTheme(ui::NativeTheme* theme);
+  void SetNativeThemeForTesting(ui::NativeTheme* theme);
 
   // RTL painting --------------------------------------------------------------
 
diff --git a/ui/views/view_class_properties.cc b/ui/views/view_class_properties.cc
index f41edd9..0409ce2d 100644
--- a/ui/views/view_class_properties.cc
+++ b/ui/views/view_class_properties.cc
@@ -22,7 +22,6 @@
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT,
                                        views::BubbleDialogDelegateView*)
 
-DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, SkPath*)
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT,
                                        views::HighlightPathGenerator*)
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::FlexSpecification*)
@@ -35,7 +34,6 @@
 DEFINE_UI_CLASS_PROPERTY_KEY(views::BubbleDialogDelegateView*,
                              kAnchoredDialogKey,
                              nullptr)
-DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(SkPath, kHighlightPathKey, nullptr)
 DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(views::HighlightPathGenerator,
                                    kHighlightPathGeneratorKey,
                                    nullptr)
diff --git a/ui/views/view_class_properties.h b/ui/views/view_class_properties.h
index d3ce2cd..0ecf17f 100644
--- a/ui/views/view_class_properties.h
+++ b/ui/views/view_class_properties.h
@@ -8,8 +8,6 @@
 #include "ui/base/class_property.h"
 #include "ui/views/views_export.h"
 
-class SkPath;
-
 namespace gfx {
 class Insets;
 }  // namespace gfx
@@ -46,11 +44,6 @@
 VIEWS_EXPORT extern const ui::ClassProperty<BubbleDialogDelegateView*>* const
     kAnchoredDialogKey;
 
-// A property to store a highlight path related to the view. This is nominally
-// used by the default inkdrop and focus ring that are both used to highlight
-// the view in different ways.
-VIEWS_EXPORT extern const ui::ClassProperty<SkPath*>* const kHighlightPathKey;
-
 // A property to store a highlight-path generator. This generator is used to
 // generate a highlight path for focus rings or ink-drop effects.
 VIEWS_EXPORT extern const ui::ClassProperty<HighlightPathGenerator*>* const
@@ -71,7 +64,6 @@
 DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, gfx::Insets*)
 DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT,
                                         views::BubbleDialogDelegateView*)
-DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, SkPath*)
 DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT,
                                         views::HighlightPathGenerator*)
 DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::FlexSpecification*)
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 3e4c62c..e9204b2 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -399,6 +399,9 @@
 #endif
   native_widget_initialized_ = true;
   native_widget_->OnWidgetInitDone();
+
+  if (delegate)
+    delegate->OnWidgetInitialized();
 }
 
 void Widget::ShowEmojiPanel() {
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
index 30232d12..014cac1 100644
--- a/ui/views/widget/widget.h
+++ b/ui/views/widget/widget.h
@@ -769,6 +769,8 @@
     return non_client_view_ ? non_client_view_->client_view() : nullptr;
   }
 
+  // Returns the compositor for this Widget, note that this may change during
+  // the Widget's lifetime (e.g. when switching monitors on Chrome OS).
   ui::Compositor* GetCompositor() {
     return const_cast<ui::Compositor*>(
         const_cast<const Widget*>(this)->GetCompositor());
diff --git a/ui/views/widget/widget_delegate.h b/ui/views/widget/widget_delegate.h
index e71776db..3388f25 100644
--- a/ui/views/widget/widget_delegate.h
+++ b/ui/views/widget/widget_delegate.h
@@ -44,6 +44,9 @@
   // menu bars, etc.) changes in size.
   virtual void OnWorkAreaChanged();
 
+  // Called when the widget's initialization is complete.
+  virtual void OnWidgetInitialized() {}
+
   // Called when the window has been requested to close, after all other checks
   // have run. Returns whether the window should be allowed to close (default is
   // true).
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc
index 46dd3ab..4fd3623 100644
--- a/ui/views/window/dialog_client_view.cc
+++ b/ui/views/window/dialog_client_view.cc
@@ -286,32 +286,33 @@
     return;
   }
 
-  if (!*member) {
-    // In theory, this should only need to assign a newly constructed Button to
-    // |*member|. DialogDelegate::UpdateButton(), and any overrides of that,
-    // should be responsible for the rest. TODO(tapted): When there is only
-    // MdTextButton, make it so. Note that some overrides may not always update
-    // the title (they should). See http://crbug.com/697303 .
-    const base::string16 title = delegate->GetDialogButtonLabel(type);
-    std::unique_ptr<LabelButton> button;
+  const bool is_default = delegate->GetDefaultDialogButton() == type &&
+                          (type != ui::DIALOG_BUTTON_CANCEL ||
+                           PlatformStyle::kDialogDefaultButtonCanBeCancel);
+  const base::string16 title = delegate->GetDialogButtonLabel(type);
 
-    const bool is_default = delegate->GetDefaultDialogButton() == type &&
-                            (type != ui::DIALOG_BUTTON_CANCEL ||
-                             PlatformStyle::kDialogDefaultButtonCanBeCancel);
-
-    button = is_default ? MdTextButton::CreateSecondaryUiBlueButton(this, title)
-                        : MdTextButton::CreateSecondaryUiButton(this, title);
-
-    const int minimum_width = LayoutProvider::Get()->GetDistanceMetric(
-        views::DISTANCE_DIALOG_BUTTON_MINIMUM_WIDTH);
-    button->SetMinSize(gfx::Size(minimum_width, 0));
-
-    button->SetGroup(kButtonGroup);
-
-    *member = button_row_container_->AddChildView(std::move(button));
+  if (*member) {
+    LabelButton* button = *member;
+    button->SetEnabled(delegate->IsDialogButtonEnabled(type));
+    button->SetIsDefault(is_default);
+    button->SetText(title);
+    return;
   }
 
-  delegate->UpdateButton(*member, type);
+  std::unique_ptr<LabelButton> button =
+      is_default ? MdTextButton::CreateSecondaryUiBlueButton(this, title)
+                 : MdTextButton::CreateSecondaryUiButton(this, title);
+
+  button->SetIsDefault(is_default);
+  button->SetEnabled(delegate->IsDialogButtonEnabled(type));
+
+  const int minimum_width = LayoutProvider::Get()->GetDistanceMetric(
+      views::DISTANCE_DIALOG_BUTTON_MINIMUM_WIDTH);
+  button->SetMinSize(gfx::Size(minimum_width, 0));
+
+  button->SetGroup(kButtonGroup);
+
+  *member = button_row_container_->AddChildView(std::move(button));
 }
 
 int DialogClientView::GetExtraViewSpacing() const {
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index db5ac3b..d286e04 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -164,17 +164,6 @@
   return Accept();
 }
 
-void DialogDelegate::UpdateButton(LabelButton* button, ui::DialogButton type) {
-  button->SetText(GetDialogButtonLabel(type));
-  button->SetEnabled(IsDialogButtonEnabled(type));
-  bool is_default = type == GetDefaultDialogButton();
-  if (!PlatformStyle::kDialogDefaultButtonCanBeCancel &&
-      type == ui::DIALOG_BUTTON_CANCEL) {
-    is_default = false;
-  }
-  button->SetIsDefault(is_default);
-}
-
 View* DialogDelegate::GetInitiallyFocusedView() {
   // Focus the default button if any.
   const DialogClientView* dcv = GetDialogClientView();
@@ -248,6 +237,18 @@
   return GetWidget()->client_view()->AsDialogClientView();
 }
 
+views::LabelButton* DialogDelegate::GetOkButton() {
+  DCHECK(GetWidget()) << "Don't call this before OnDialogInitialized";
+  auto* client = GetDialogClientView();
+  return client ? client->ok_button() : nullptr;
+}
+
+views::LabelButton* DialogDelegate::GetCancelButton() {
+  DCHECK(GetWidget()) << "Don't call this before OnDialogInitialized";
+  auto* client = GetDialogClientView();
+  return client ? client->cancel_button() : nullptr;
+}
+
 void DialogDelegate::AddObserver(DialogObserver* observer) {
   observer_list_.AddObserver(observer);
 }
@@ -270,6 +271,10 @@
   return ax::mojom::Role::kDialog;
 }
 
+void DialogDelegate::OnWidgetInitialized() {
+  OnDialogInitialized();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // DialogDelegateView:
 
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index 9787d33..bbbf072 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -73,6 +73,16 @@
                                                       gfx::NativeView parent,
                                                       const gfx::Rect& bounds);
 
+  // Called when the DialogDelegate and its frame have finished initializing but
+  // not been shown yet. Override this to perform customizations to the dialog
+  // that need to happen after the dialog's widget, border, buttons, and so on
+  // are ready.
+  //
+  // Overrides of this method should be quite rare - prefer to do dialog
+  // customization before the frame/widget/etc are ready if at all possible, via
+  // other setters on this class.
+  virtual void OnDialogInitialized() {}
+
   // Returns a mask specifying which of the available DialogButtons are visible
   // for the dialog. Note: Dialogs with just an OK button are frowned upon.
   virtual int GetDialogButtons() const;
@@ -120,11 +130,6 @@
   // must remain open.
   virtual bool Close();
 
-  // Updates the properties and appearance of |button| which has been created
-  // for type |type|. Override to do special initialization above and beyond
-  // the typical.
-  virtual void UpdateButton(LabelButton* button, ui::DialogButton type);
-
   // Overridden from WidgetDelegate:
   View* GetInitiallyFocusedView() override;
   DialogDelegate* AsDialogDelegate() override;
@@ -141,6 +146,11 @@
   const DialogClientView* GetDialogClientView() const;
   DialogClientView* GetDialogClientView();
 
+  // Helpers for accessing parts of the DialogClientView without needing to know
+  // about DialogClientView. Do not call these before OnDialogInitialized.
+  views::LabelButton* GetOkButton();
+  views::LabelButton* GetCancelButton();
+
   // Add or remove an observer notified by calls to DialogModelChanged().
   void AddObserver(DialogObserver* observer);
   void RemoveObserver(DialogObserver* observer);
@@ -168,6 +178,10 @@
   const Params& GetParams() const { return params_; }
 
  private:
+  // Overridden from WidgetDelegate. If you need to hook after widget
+  // initialization, use OnDialogInitialized above.
+  void OnWidgetInitialized() final;
+
   // The margins between the content and the inside of the border.
   // TODO(crbug.com/733040): Most subclasses assume they must set their own
   // margins explicitly, so we set them to 0 here for now to avoid doubled
diff --git a/ui/webui/resources/js/cr/ui/tree.js b/ui/webui/resources/js/cr/ui/tree.js
index bc115f0..15a3948 100644
--- a/ui/webui/resources/js/cr/ui/tree.js
+++ b/ui/webui/resources/js/cr/ui/tree.js
@@ -14,6 +14,14 @@
   const INDENT = 20;
 
   /**
+   * A custom rowElement depth (indent) style handler where undefined uses the
+   * default depth INDENT styling, see cr.ui.TreeItem.setDepth_().
+   *
+   * @type {function(!cr.ui.TreeItem,number)|undefined}
+   */
+  let customRowElementDepthStyleHandler = undefined;
+
+  /**
    * Returns the computed style for an element.
    * @param {!Element} el The element to get the computed style for.
    * @return {!CSSStyleDeclaration} The computed style.
@@ -65,6 +73,25 @@
     },
 
     /**
+     * Returns the tree item rowElement style handler.
+     *
+     * @return {function(!cr.ui.TreeItem,number)|undefined}
+     */
+    get rowElementDepthStyleHandler() {
+      return customRowElementDepthStyleHandler;
+    },
+
+    /**
+     * Sets a tree item rowElement style handler, which allows Tree users to
+     * customize the depth (indent) style of tree item rowElements.
+     *
+     * @param {function(!cr.ui.TreeItem,number)|undefined} handler
+     */
+    set rowElementDepthStyleHandler(handler) {
+      customRowElementDepthStyleHandler = handler;
+    },
+
+    /**
      * Returns the tree item that are children of this tree.
      */
     get items() {
@@ -325,8 +352,13 @@
      */
     setDepth_: function(depth) {
       if (depth != this.depth_) {
-        this.rowElement.style.paddingInlineStart =
-            Math.max(0, depth - 1) * INDENT + 'px';
+        const rowDepth = Math.max(0, depth - 1);
+        if (!customRowElementDepthStyleHandler) {
+          this.rowElement.style.paddingInlineStart = rowDepth * INDENT + 'px';
+        } else {
+          customRowElementDepthStyleHandler(this, rowDepth);
+        }
+
         this.depth_ = depth;
         const items = this.items;
         for (let i = 0, item; item = items[i]; i++) {
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index 7c072928a..51decabd 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -60,6 +60,7 @@
     "common/content_client_impl.h",
     "public/browser_controller.h",
     "public/browser_observer.h",
+    "public/fullscreen_delegate.h",
     "public/main.h",
     "public/navigation.h",
     "public/navigation_controller.h",
@@ -153,6 +154,8 @@
       "browser/browser_observer_proxy.h",
       "browser/content_view_render_view.cc",
       "browser/content_view_render_view.h",
+      "browser/fullscreen_delegate_proxy.cc",
+      "browser/fullscreen_delegate_proxy.h",
       "browser/top_controls_container_view.cc",
       "browser/top_controls_container_view.h",
     ]
diff --git a/weblayer/browser/browser_controller_impl.cc b/weblayer/browser/browser_controller_impl.cc
index 3b8ddc7..4be05e6c 100644
--- a/weblayer/browser/browser_controller_impl.cc
+++ b/weblayer/browser/browser_controller_impl.cc
@@ -4,13 +4,16 @@
 
 #include "weblayer/browser/browser_controller_impl.h"
 
+#include "base/auto_reset.h"
 #include "base/logging.h"
 #include "content/public/browser/interstitial_page.h"
+#include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/browser_controls_state.h"
 #include "weblayer/browser/navigation_controller_impl.h"
 #include "weblayer/browser/profile_impl.h"
 #include "weblayer/public/browser_observer.h"
+#include "weblayer/public/fullscreen_delegate.h"
 
 #if !defined(OS_ANDROID)
 #include "ui/views/controls/webview/webview.h"
@@ -26,6 +29,14 @@
 
 namespace {
 
+// Pointer value of this is used as a key in base::SupportsUserData for
+// WebContents. Value of the key is an instance of |UserData|.
+constexpr int kWebContentsUserDataKey = 0;
+
+struct UserData : public base::SupportsUserData::Data {
+  BrowserControllerImpl* controller = nullptr;
+};
+
 #if defined(OS_ANDROID)
 BrowserController* g_last_browser_controller;
 #endif
@@ -40,6 +51,9 @@
   content::WebContents::CreateParams create_params(
       profile_->GetBrowserContext());
   web_contents_ = content::WebContents::Create(create_params);
+  std::unique_ptr<UserData> user_data = std::make_unique<UserData>();
+  user_data->controller = this;
+  web_contents_->SetUserData(&kWebContentsUserDataKey, std::move(user_data));
 
   web_contents_->SetDelegate(this);
   Observe(web_contents_.get());
@@ -53,6 +67,36 @@
   web_contents_.reset();
 }
 
+// static
+BrowserControllerImpl* BrowserControllerImpl::FromWebContents(
+    content::WebContents* web_contents) {
+  return reinterpret_cast<UserData*>(
+             web_contents->GetUserData(&kWebContentsUserDataKey))
+      ->controller;
+}
+
+void BrowserControllerImpl::SetFullscreenDelegate(
+    FullscreenDelegate* delegate) {
+  if (delegate == fullscreen_delegate_)
+    return;
+
+  const bool had_delegate = (fullscreen_delegate_ != nullptr);
+  const bool has_delegate = (delegate != nullptr);
+
+  // If currently fullscreen, and the delegate is being set to null, force an
+  // exit now (otherwise the delegate can't take us out of fullscreen).
+  if (is_fullscreen_ && fullscreen_delegate_ && had_delegate != has_delegate)
+    OnExitFullscreen();
+
+  fullscreen_delegate_ = delegate;
+  // Whether fullscreen is enabled depends upon whether there is a delegate. If
+  // having a delegate changed, then update the renderer (which is where
+  // fullscreen enabled is tracked).
+  content::RenderViewHost* host = web_contents_->GetRenderViewHost();
+  if (had_delegate != has_delegate && host)
+    host->OnWebkitPreferencesChanged();
+}
+
 void BrowserControllerImpl::AddObserver(BrowserObserver* observer) {
   observers_.AddObserver(observer);
 }
@@ -134,6 +178,39 @@
   return true;
 }
 
+bool BrowserControllerImpl::EmbedsFullscreenWidget() {
+  return true;
+}
+
+void BrowserControllerImpl::EnterFullscreenModeForTab(
+    content::WebContents* web_contents,
+    const GURL& origin,
+    const blink::mojom::FullscreenOptions& options) {
+  // TODO: support |options|.
+  is_fullscreen_ = true;
+  auto exit_fullscreen_closure = base::BindOnce(
+      &BrowserControllerImpl::OnExitFullscreen, weak_ptr_factory_.GetWeakPtr());
+  base::AutoReset<bool> reset(&processing_enter_fullscreen_, true);
+  fullscreen_delegate_->EnterFullscreen(std::move(exit_fullscreen_closure));
+}
+
+void BrowserControllerImpl::ExitFullscreenModeForTab(
+    content::WebContents* web_contents) {
+  is_fullscreen_ = false;
+  fullscreen_delegate_->ExitFullscreen();
+}
+
+bool BrowserControllerImpl::IsFullscreenForTabOrPending(
+    const content::WebContents* web_contents) {
+  return is_fullscreen_;
+}
+
+blink::mojom::DisplayMode BrowserControllerImpl::GetDisplayMode(
+    const content::WebContents* web_contents) {
+  return is_fullscreen_ ? blink::mojom::DisplayMode::kFullscreen
+                        : blink::mojom::DisplayMode::kBrowser;
+}
+
 void BrowserControllerImpl::DidFirstVisuallyNonEmptyPaint() {
   for (auto& observer : observers_)
     observer.FirstContentfulPaint();
@@ -156,6 +233,15 @@
 #endif
 }
 
+void BrowserControllerImpl::OnExitFullscreen() {
+  // If |processing_enter_fullscreen_| is true, it means the callback is being
+  // called while processing EnterFullscreenModeForTab(). WebContents doesn't
+  // deal well with this. FATAL as Android generally doesn't run with DCHECKs.
+  LOG_IF(FATAL, !processing_enter_fullscreen_)
+      << "exiting fullscreen while entering fullscreen is not supported";
+  web_contents_->ExitFullscreen(/* will_cause_resize */ false);
+}
+
 std::unique_ptr<BrowserController> BrowserController::Create(Profile* profile) {
   return std::make_unique<BrowserControllerImpl>(
       static_cast<ProfileImpl*>(profile));
diff --git a/weblayer/browser/browser_controller_impl.h b/weblayer/browser/browser_controller_impl.h
index d46923a..577d85f6 100644
--- a/weblayer/browser/browser_controller_impl.h
+++ b/weblayer/browser/browser_controller_impl.h
@@ -5,7 +5,10 @@
 #ifndef WEBLAYER_BROWSER_BROWSER_CONTROLLER_IMPL_H_
 #define WEBLAYER_BROWSER_BROWSER_CONTROLLER_IMPL_H_
 
+#include <memory>
+
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "build/build_config.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -21,6 +24,7 @@
 }
 
 namespace weblayer {
+class FullscreenDelegate;
 class NavigationControllerImpl;
 class ProfileImpl;
 
@@ -35,6 +39,11 @@
   explicit BrowserControllerImpl(ProfileImpl* profile);
   ~BrowserControllerImpl() override;
 
+  // Returns the BrowserControllerImpl from the specified WebContents, or null
+  // if BrowserControllerImpl was not created by a BrowserControllerImpl.
+  static BrowserControllerImpl* FromWebContents(
+      content::WebContents* web_contents);
+
   content::WebContents* web_contents() const { return web_contents_.get(); }
 
 #if defined(OS_ANDROID)
@@ -47,8 +56,11 @@
       jlong native_top_controls_container_view);
 #endif
 
+  FullscreenDelegate* fullscreen_delegate() { return fullscreen_delegate_; }
+
  private:
   // BrowserController implementation:
+  void SetFullscreenDelegate(FullscreenDelegate* delegate) override;
   void AddObserver(BrowserObserver* observer) override;
   void RemoveObserver(BrowserObserver* observer) override;
   NavigationController* GetNavigationController() override;
@@ -66,12 +78,26 @@
   int GetTopControlsHeight() override;
   bool DoBrowserControlsShrinkRendererSize(
       const content::WebContents* web_contents) override;
+  bool EmbedsFullscreenWidget() override;
+  void EnterFullscreenModeForTab(
+      content::WebContents* web_contents,
+      const GURL& origin,
+      const blink::mojom::FullscreenOptions& options) override;
+  void ExitFullscreenModeForTab(content::WebContents* web_contents) override;
+  bool IsFullscreenForTabOrPending(
+      const content::WebContents* web_contents) override;
+  blink::mojom::DisplayMode GetDisplayMode(
+      const content::WebContents* web_contents) override;
 
   // content::WebContentsObserver implementation:
   void DidFirstVisuallyNonEmptyPaint() override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
 
+  // Called from closure supplied to delegate to exit fullscreen.
+  void OnExitFullscreen();
+
+  FullscreenDelegate* fullscreen_delegate_ = nullptr;
   ProfileImpl* profile_;
   std::unique_ptr<content::WebContents> web_contents_;
   std::unique_ptr<NavigationControllerImpl> navigation_controller_;
@@ -80,6 +106,12 @@
   TopControlsContainerView* top_controls_container_view_ = nullptr;
 #endif
 
+  bool is_fullscreen_ = false;
+  // Set to true doing EnterFullscreenModeForTab().
+  bool processing_enter_fullscreen_ = false;
+
+  base::WeakPtrFactory<BrowserControllerImpl> weak_ptr_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(BrowserControllerImpl);
 };
 
diff --git a/weblayer/browser/content_browser_client_impl.cc b/weblayer/browser/content_browser_client_impl.cc
index 5b6cf9d..114469f0 100644
--- a/weblayer/browser/content_browser_client_impl.cc
+++ b/weblayer/browser/content_browser_client_impl.cc
@@ -17,14 +17,17 @@
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/common/user_agent.h"
+#include "content/public/common/web_preferences.h"
 #include "services/network/network_service.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "url/gurl.h"
 #include "url/origin.h"
+#include "weblayer/browser/browser_controller_impl.h"
 #include "weblayer/browser/browser_main_parts_impl.h"
 #include "weblayer/browser/weblayer_content_browser_overlay_manifest.h"
+#include "weblayer/public/fullscreen_delegate.h"
 #include "weblayer/public/main.h"
 
 #if defined(OS_ANDROID)
@@ -99,6 +102,19 @@
   return metadata;
 }
 
+void ContentBrowserClientImpl::OverrideWebkitPrefs(
+    content::RenderViewHost* render_view_host,
+    content::WebPreferences* prefs) {
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderViewHost(render_view_host);
+  if (!web_contents)
+    return;
+  BrowserControllerImpl* browser_controller =
+      BrowserControllerImpl::FromWebContents(web_contents);
+  prefs->fullscreen_supported =
+      browser_controller && browser_controller->fullscreen_delegate();
+}
+
 mojo::Remote<network::mojom::NetworkContext>
 ContentBrowserClientImpl::CreateNetworkContext(
     content::BrowserContext* context,
diff --git a/weblayer/browser/content_browser_client_impl.h b/weblayer/browser/content_browser_client_impl.h
index 9b8a2487..905e034a 100644
--- a/weblayer/browser/content_browser_client_impl.h
+++ b/weblayer/browser/content_browser_client_impl.h
@@ -35,6 +35,8 @@
       base::StringPiece name) override;
   std::string GetUserAgent() override;
   blink::UserAgentMetadata GetUserAgentMetadata() override;
+  void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
+                           content::WebPreferences* prefs) override;
   mojo::Remote<network::mojom::NetworkContext> CreateNetworkContext(
       content::BrowserContext* context,
       bool in_memory,
diff --git a/weblayer/browser/fullscreen_delegate_proxy.cc b/weblayer/browser/fullscreen_delegate_proxy.cc
new file mode 100644
index 0000000..46ae748
--- /dev/null
+++ b/weblayer/browser/fullscreen_delegate_proxy.cc
@@ -0,0 +1,63 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "weblayer/browser/fullscreen_delegate_proxy.h"
+
+#include "base/android/jni_string.h"
+#include "url/gurl.h"
+#include "weblayer/browser/browser_controller_impl.h"
+#include "weblayer/browser/java/jni/FullscreenDelegateProxy_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ScopedJavaLocalRef;
+
+namespace weblayer {
+
+FullscreenDelegateProxy::FullscreenDelegateProxy(
+    JNIEnv* env,
+    jobject obj,
+    BrowserController* browser_controller)
+    : browser_controller_(browser_controller), java_observer_(env, obj) {
+  browser_controller_->SetFullscreenDelegate(this);
+}
+
+FullscreenDelegateProxy::~FullscreenDelegateProxy() {
+  browser_controller_->SetFullscreenDelegate(nullptr);
+}
+
+void FullscreenDelegateProxy::EnterFullscreen(base::OnceClosure exit_closure) {
+  exit_fullscreen_closure_ = std::move(exit_closure);
+  Java_FullscreenDelegateProxy_enterFullscreen(AttachCurrentThread(),
+                                               java_observer_);
+}
+
+void FullscreenDelegateProxy::ExitFullscreen() {
+  Java_FullscreenDelegateProxy_exitFullscreen(AttachCurrentThread(),
+                                              java_observer_);
+}
+
+void FullscreenDelegateProxy::DoExitFullscreen(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& caller) {
+  if (exit_fullscreen_closure_)
+    std::move(exit_fullscreen_closure_).Run();
+}
+
+static jlong JNI_FullscreenDelegateProxy_CreateFullscreenDelegateProxy(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& proxy,
+    jlong browser_controller) {
+  return reinterpret_cast<jlong>(new FullscreenDelegateProxy(
+      env, proxy,
+      reinterpret_cast<BrowserControllerImpl*>(browser_controller)));
+}
+
+static void JNI_FullscreenDelegateProxy_DeleteFullscreenDelegateProxy(
+    JNIEnv* env,
+    jlong proxy) {
+  delete reinterpret_cast<FullscreenDelegateProxy*>(proxy);
+}
+
+}  // namespace weblayer
diff --git a/weblayer/browser/fullscreen_delegate_proxy.h b/weblayer/browser/fullscreen_delegate_proxy.h
new file mode 100644
index 0000000..d28b57b
--- /dev/null
+++ b/weblayer/browser/fullscreen_delegate_proxy.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBLAYER_BROWSER_FULLSCREEN_DELEGATE_PROXY_H_
+#define WEBLAYER_BROWSER_FULLSCREEN_DELEGATE_PROXY_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "weblayer/public/fullscreen_delegate.h"
+
+namespace weblayer {
+
+class BrowserController;
+
+// FullscreenDelegateProxy forwards all FullscreenDelegate functions to the
+// Java side. There is at most one FullscreenDelegateProxy per
+// BrowserController.
+class FullscreenDelegateProxy : public FullscreenDelegate {
+ public:
+  FullscreenDelegateProxy(JNIEnv* env,
+                          jobject obj,
+                          BrowserController* browser_controller);
+  ~FullscreenDelegateProxy() override;
+
+  // FullscreenDelegate:
+  void EnterFullscreen(base::OnceClosure exit_closure) override;
+  void ExitFullscreen() override;
+
+  // Called from the Java side to exit fullscreen.
+  void DoExitFullscreen(JNIEnv* env,
+                        const base::android::JavaParamRef<jobject>& caller);
+
+ private:
+  BrowserController* browser_controller_;
+  base::android::ScopedJavaGlobalRef<jobject> java_observer_;
+  base::OnceClosure exit_fullscreen_closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(FullscreenDelegateProxy);
+};
+
+}  // namespace weblayer
+
+#endif  // WEBLAYER_BROWSER_FULLSCREEN_DELEGATE_PROXY_H_
diff --git a/weblayer/browser/java/BUILD.gn b/weblayer/browser/java/BUILD.gn
index daee181..f1f772a 100644
--- a/weblayer/browser/java/BUILD.gn
+++ b/weblayer/browser/java/BUILD.gn
@@ -18,6 +18,7 @@
     "org/chromium/weblayer_private/BrowserObserverProxy.java",
     "org/chromium/weblayer_private/ContentView.java",
     "org/chromium/weblayer_private/ContentViewRenderView.java",
+    "org/chromium/weblayer_private/FullscreenDelegateProxy.java",
     "org/chromium/weblayer_private/NavigationControllerImpl.java",
     "org/chromium/weblayer_private/NavigationImpl.java",
     "org/chromium/weblayer_private/ProfileImpl.java",
@@ -44,6 +45,7 @@
     "org/chromium/weblayer_private/BrowserControllerImpl.java",
     "org/chromium/weblayer_private/BrowserObserverProxy.java",
     "org/chromium/weblayer_private/ContentViewRenderView.java",
+    "org/chromium/weblayer_private/FullscreenDelegateProxy.java",
     "org/chromium/weblayer_private/NavigationControllerImpl.java",
     "org/chromium/weblayer_private/NavigationImpl.java",
     "org/chromium/weblayer_private/ProfileImpl.java",
@@ -71,6 +73,7 @@
     "org/chromium/weblayer_private/aidl/IBrowserFragmentController.aidl",
     "org/chromium/weblayer_private/aidl/IChildProcessService.aidl",
     "org/chromium/weblayer_private/aidl/IClientNavigation.aidl",
+    "org/chromium/weblayer_private/aidl/IFullscreenDelegateClient.aidl",
     "org/chromium/weblayer_private/aidl/INavigation.aidl",
     "org/chromium/weblayer_private/aidl/INavigationController.aidl",
     "org/chromium/weblayer_private/aidl/INavigationControllerClient.aidl",
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
index e7d3fc2..2a97b2c 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
@@ -21,6 +21,7 @@
 import org.chromium.ui.base.ViewAndroidDelegate;
 import org.chromium.weblayer_private.aidl.IBrowserController;
 import org.chromium.weblayer_private.aidl.IBrowserControllerClient;
+import org.chromium.weblayer_private.aidl.IFullscreenDelegateClient;
 import org.chromium.weblayer_private.aidl.INavigationControllerClient;
 import org.chromium.weblayer_private.aidl.IObjectWrapper;
 import org.chromium.weblayer_private.aidl.ObjectWrapper;
@@ -42,6 +43,7 @@
     private WebContents mWebContents;
     private BrowserObserverProxy mBrowserObserverProxy;
     private NavigationControllerImpl mNavigationController;
+    private FullscreenDelegateProxy mFullscreenDelegateProxy;
 
     private static class InternalAccessDelegateImpl
             implements ViewEventSink.InternalAccessDelegate {
@@ -127,13 +129,34 @@
         mBrowserObserverProxy = new BrowserObserverProxy(mNativeBrowserController, client);
     }
 
+    @Override
+    public void setFullscreenDelegateClient(IFullscreenDelegateClient client) {
+        if (client != null) {
+            if (mFullscreenDelegateProxy == null) {
+                mFullscreenDelegateProxy =
+                        new FullscreenDelegateProxy(mNativeBrowserController, client);
+            } else {
+                mFullscreenDelegateProxy.setClient(client);
+            }
+        } else if (mFullscreenDelegateProxy != null) {
+            mFullscreenDelegateProxy.destroy();
+            mFullscreenDelegateProxy = null;
+        }
+    }
+
     public void destroy() {
         BrowserControllerImplJni.get().setTopControlsContainerView(
                 mNativeBrowserController, BrowserControllerImpl.this, 0);
         mTopControlsContainerView.destroy();
         mContentViewRenderView.destroy();
-        if (mBrowserObserverProxy != null) mBrowserObserverProxy.destroy();
-        mBrowserObserverProxy = null;
+        if (mBrowserObserverProxy != null) {
+            mBrowserObserverProxy.destroy();
+            mBrowserObserverProxy = null;
+        }
+        if (mFullscreenDelegateProxy != null) {
+            mFullscreenDelegateProxy.destroy();
+            mFullscreenDelegateProxy = null;
+        }
         mNavigationController = null;
         BrowserControllerImplJni.get().deleteBrowserController(mNativeBrowserController);
         mNativeBrowserController = 0;
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/FullscreenDelegateProxy.java b/weblayer/browser/java/org/chromium/weblayer_private/FullscreenDelegateProxy.java
new file mode 100644
index 0000000..3da597a
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/FullscreenDelegateProxy.java
@@ -0,0 +1,80 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.os.RemoteException;
+import android.webkit.ValueCallback;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer_private.aidl.APICallException;
+import org.chromium.weblayer_private.aidl.IFullscreenDelegateClient;
+import org.chromium.weblayer_private.aidl.ObjectWrapper;
+
+/**
+ * Owns the c++ FullscreenDelegateProxy class, which is responsible for forwarding all
+ * FullscreenDelegate delegate calls to this class, which in turn forwards to the
+ * FullscreenDelegateClient.
+ */
+@JNINamespace("weblayer")
+public final class FullscreenDelegateProxy {
+    private long mNativeFullscreenDelegateProxy;
+    private IFullscreenDelegateClient mClient;
+
+    FullscreenDelegateProxy(long browserController, IFullscreenDelegateClient client) {
+        assert client != null;
+        mClient = client;
+        mNativeFullscreenDelegateProxy =
+                FullscreenDelegateProxyJni.get().createFullscreenDelegateProxy(
+                        this, browserController);
+    }
+
+    public void setClient(IFullscreenDelegateClient client) {
+        assert client != null;
+        mClient = client;
+    }
+
+    public void destroy() {
+        FullscreenDelegateProxyJni.get().deleteFullscreenDelegateProxy(
+                mNativeFullscreenDelegateProxy);
+        mNativeFullscreenDelegateProxy = 0;
+    }
+
+    @CalledByNative
+    private void enterFullscreen() {
+        try {
+            ValueCallback<Void> exitFullscreenCallback = new ValueCallback<Void>() {
+                @Override
+                public void onReceiveValue(Void result) {
+                    if (mNativeFullscreenDelegateProxy == 0) {
+                        throw new IllegalStateException("Called after destroy()");
+                    }
+                    FullscreenDelegateProxyJni.get().doExitFullscreen(
+                            mNativeFullscreenDelegateProxy, FullscreenDelegateProxy.this);
+                }
+            };
+            mClient.enterFullscreen(ObjectWrapper.wrap(exitFullscreenCallback));
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
+    @CalledByNative
+    private void exitFullscreen() {
+        try {
+            mClient.exitFullscreen();
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
+    @NativeMethods
+    interface Natives {
+        long createFullscreenDelegateProxy(FullscreenDelegateProxy proxy, long browserController);
+        void deleteFullscreenDelegateProxy(long proxy);
+        void doExitFullscreen(long nativeFullscreenDelegateProxy, FullscreenDelegateProxy proxy);
+    }
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java b/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java
index d0e5b17..3974653 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java
@@ -12,6 +12,7 @@
 import android.view.ViewParent;
 import android.widget.FrameLayout;
 
+import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.content_public.browser.WebContents;
@@ -31,6 +32,8 @@
     // ID used with ViewResourceAdapter.
     private static final int TOP_CONTROLS_ID = 1001;
 
+    private static final long SYSTEM_UI_VIEWPORT_UPDATE_DELAY_MS = 500;
+
     private long mNativeTopControlsContainerView;
 
     private ViewResourceAdapter mViewResourceAdapter;
@@ -50,6 +53,11 @@
     // True if scrolling.
     private boolean mInTopControlsScroll;
 
+    private boolean mIsFullscreen;
+
+    // Used to delay processing fullscreen requests.
+    private Runnable mSystemUiFullscreenResizeRunnable;
+
     // Used to  delay updating the image for the layer.
     private final Runnable mRefreshResourceIdRunnable = () -> {
         if (mView == null) return;
@@ -76,7 +84,7 @@
                 });
         mNativeTopControlsContainerView =
                 TopControlsContainerViewJni.get().createTopControlsContainerView(
-                        webContents, contentViewRenderView.getNativeHandle());
+                        this, webContents, contentViewRenderView.getNativeHandle());
     }
 
     public void destroy() {
@@ -117,6 +125,7 @@
             view.layout(0, 0, getWidth(), getHeight());
             createAdapterAndLayer();
         }
+        if (mIsFullscreen) hideTopControls();
     }
 
     public View getView() {
@@ -128,6 +137,7 @@
      */
     public void onTopControlsChanged(int topControlsOffsetY, int topContentOffsetY) {
         if (mView == null) return;
+        if (mIsFullscreen) return;
         if (topContentOffsetY == getHeight()) {
             finishTopControlsScroll(topContentOffsetY);
             return;
@@ -225,10 +235,35 @@
         if (mView != null) mView.setVisibility(View.VISIBLE);
     }
 
+    @CalledByNative
+    private void didToggleFullscreenModeForTab(final boolean isFullscreen) {
+        // Delay hiding until after the animation. This comes from Chrome code.
+        if (mSystemUiFullscreenResizeRunnable != null) {
+            getHandler().removeCallbacks(mSystemUiFullscreenResizeRunnable);
+        }
+        mSystemUiFullscreenResizeRunnable = () -> processFullscreenChanged(isFullscreen);
+        long delay = isFullscreen ? SYSTEM_UI_VIEWPORT_UPDATE_DELAY_MS : 0;
+        postDelayed(mSystemUiFullscreenResizeRunnable, delay);
+    }
+
+    private void processFullscreenChanged(boolean isFullscreen) {
+        mSystemUiFullscreenResizeRunnable = null;
+        if (mIsFullscreen == isFullscreen) return;
+        mIsFullscreen = isFullscreen;
+        if (mView == null) return;
+        if (mIsFullscreen) {
+            hideTopControls();
+            setTopControlsOffset(-mLastHeight, 0);
+        } else {
+            showTopControls();
+            setTopControlsOffset(0, mLastHeight);
+        }
+    }
+
     @NativeMethods
     interface Natives {
-        long createTopControlsContainerView(
-                WebContents webContents, long nativeContentViewRenderView);
+        long createTopControlsContainerView(TopControlsContainerView view, WebContents webContents,
+                long nativeContentViewRenderView);
         void deleteTopControlsContainerView(
                 long nativeTopControlsContainerView, TopControlsContainerView caller);
         void createTopControlsLayer(
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl
index fdbf9c2..3304e19 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl
@@ -5,6 +5,7 @@
 package org.chromium.weblayer_private.aidl;
 
 import org.chromium.weblayer_private.aidl.IBrowserControllerClient;
+import org.chromium.weblayer_private.aidl.IFullscreenDelegateClient;
 import org.chromium.weblayer_private.aidl.INavigationController;
 import org.chromium.weblayer_private.aidl.INavigationControllerClient;
 
@@ -12,4 +13,6 @@
   void setClient(in IBrowserControllerClient client) = 0;
 
   INavigationController createNavigationController(in INavigationControllerClient client) = 1;
+
+  void setFullscreenDelegateClient(in IFullscreenDelegateClient client) = 2;
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IFullscreenDelegateClient.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IFullscreenDelegateClient.aidl
new file mode 100644
index 0000000..dc3b0b3
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IFullscreenDelegateClient.aidl
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.aidl;
+
+import org.chromium.weblayer_private.aidl.IObjectWrapper;
+
+/**
+ * Used to forward FullscreenDelegate calls to the client.
+ */
+interface IFullscreenDelegateClient {
+  // exitFullscreenWrapper is a ValueCallback<Void> that when run exits
+  // fullscreen.
+  void enterFullscreen(in IObjectWrapper exitFullscreenWrapper) = 0;
+  void exitFullscreen() = 1;
+}
diff --git a/weblayer/browser/top_controls_container_view.cc b/weblayer/browser/top_controls_container_view.cc
index ede809d..7f84c9e 100644
--- a/weblayer/browser/top_controls_container_view.cc
+++ b/weblayer/browser/top_controls_container_view.cc
@@ -20,12 +20,15 @@
 namespace weblayer {
 
 TopControlsContainerView::TopControlsContainerView(
+    const base::android::JavaParamRef<jobject>&
+        java_top_controls_container_view,
     content::WebContents* web_contents,
     ContentViewRenderView* content_view_render_view)
-    : content_view_render_view_(content_view_render_view),
-      web_contents_(web_contents) {
+    : java_top_controls_container_view_(java_top_controls_container_view),
+      content_view_render_view_(content_view_render_view) {
   DCHECK(content_view_render_view_);
-  DCHECK(web_contents_);
+  DCHECK(web_contents);
+  Observe(web_contents);
 }
 
 TopControlsContainerView::~TopControlsContainerView() = default;
@@ -70,7 +73,7 @@
     int top_content_offset_y) {
   DCHECK(top_controls_layer_);
   top_controls_layer_->SetPosition(gfx::PointF(0, top_controls_offset_y));
-  web_contents_->GetNativeView()->GetLayer()->SetPosition(
+  web_contents()->GetNativeView()->GetLayer()->SetPosition(
       gfx::PointF(0, top_content_offset_y));
 }
 
@@ -96,11 +99,22 @@
       top_controls_resource->ui_resource()->id());
 }
 
+void TopControlsContainerView::DidToggleFullscreenModeForTab(
+    bool entered_fullscreen,
+    bool will_cause_resize) {
+  Java_TopControlsContainerView_didToggleFullscreenModeForTab(
+      AttachCurrentThread(), java_top_controls_container_view_,
+      entered_fullscreen);
+}
+
 static jlong JNI_TopControlsContainerView_CreateTopControlsContainerView(
     JNIEnv* env,
+    const base::android::JavaParamRef<jobject>&
+        java_top_controls_container_view,
     const base::android::JavaParamRef<jobject>& web_contents,
     jlong native_content_view_render_view) {
   return reinterpret_cast<jlong>(new TopControlsContainerView(
+      java_top_controls_container_view,
       content::WebContents::FromJavaWebContents(web_contents),
       reinterpret_cast<ContentViewRenderView*>(
           native_content_view_render_view)));
diff --git a/weblayer/browser/top_controls_container_view.h b/weblayer/browser/top_controls_container_view.h
index b39e2b3..21f501c0 100644
--- a/weblayer/browser/top_controls_container_view.h
+++ b/weblayer/browser/top_controls_container_view.h
@@ -9,6 +9,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "build/build_config.h"
+#include "content/public/browser/web_contents_observer.h"
 
 namespace cc {
 class UIResourceLayer;
@@ -25,11 +26,13 @@
 // Native side of TopControlsContainerView. Responsible for creating and
 // positioning the cc::Layer that contains an image of the contents of the
 // top-control.
-class TopControlsContainerView {
+class TopControlsContainerView : public content::WebContentsObserver {
  public:
-  TopControlsContainerView(content::WebContents* web_contents,
+  TopControlsContainerView(const base::android::JavaParamRef<jobject>&
+                               java_top_controls_container_view,
+                           content::WebContents* web_contents,
                            ContentViewRenderView* content_view_render_view);
-  ~TopControlsContainerView();
+  ~TopControlsContainerView() override;
 
   // Height needed to display the top-control.
   int GetTopControlsHeight();
@@ -69,8 +72,12 @@
       const base::android::JavaParamRef<jobject>& caller);
 
  private:
+  // WebContentsObserver:
+  void DidToggleFullscreenModeForTab(bool entered_fullscreen,
+                                     bool will_cause_resize) override;
+
+  base::android::ScopedJavaGlobalRef<jobject> java_top_controls_container_view_;
   ContentViewRenderView* content_view_render_view_;
-  content::WebContents* web_contents_;
   int top_controls_resource_id_ = -1;
 
   // Layer containing showing the image for the top-controls. This is a sibling
diff --git a/weblayer/public/browser_controller.h b/weblayer/public/browser_controller.h
index eab6012..33f5baf 100644
--- a/weblayer/public/browser_controller.h
+++ b/weblayer/public/browser_controller.h
@@ -17,6 +17,7 @@
 
 namespace weblayer {
 class BrowserObserver;
+class FullscreenDelegate;
 class Profile;
 class NavigationController;
 
@@ -31,6 +32,10 @@
 
   virtual ~BrowserController() {}
 
+  // Sets the FullscreenDelegate. Setting a non-null value implicitly enables
+  // fullscreen.
+  virtual void SetFullscreenDelegate(FullscreenDelegate* delegate) = 0;
+
   virtual void AddObserver(BrowserObserver* observer) = 0;
 
   virtual void RemoveObserver(BrowserObserver* observer) = 0;
diff --git a/weblayer/public/fullscreen_delegate.h b/weblayer/public/fullscreen_delegate.h
new file mode 100644
index 0000000..dcb0fa20
--- /dev/null
+++ b/weblayer/public/fullscreen_delegate.h
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBLAYER_PUBLIC_FULLSCREEN_DELEGATE_H_
+#define WEBLAYER_PUBLIC_FULLSCREEN_DELEGATE_H_
+
+#include "base/callback_forward.h"
+
+namespace weblayer {
+
+class FullscreenDelegate {
+ public:
+  // Called when the page has requested to go fullscreen.
+  virtual void EnterFullscreen(base::OnceClosure exit_closure) = 0;
+
+  // Informs the delegate the page has exited fullscreen.
+  virtual void ExitFullscreen() = 0;
+
+ protected:
+  virtual ~FullscreenDelegate() {}
+};
+
+}  // namespace weblayer
+
+#endif  // WEBLAYER_PUBLIC_FULLSCREEN_DELEGATE_H_
diff --git a/weblayer/public/java/BUILD.gn b/weblayer/public/java/BUILD.gn
index 070cc6bf..b60e5d0c 100644
--- a/weblayer/public/java/BUILD.gn
+++ b/weblayer/public/java/BUILD.gn
@@ -27,6 +27,7 @@
       "org/chromium/weblayer/BrowserFragmentController.java",
       "org/chromium/weblayer/BrowserObserver.java",
       "org/chromium/weblayer/Callback.java",
+      "org/chromium/weblayer/FullscreenDelegate.java",
       "org/chromium/weblayer/ListenableFuture.java",
       "org/chromium/weblayer/ListenableResult.java",
       "org/chromium/weblayer/Navigation.java",
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserController.java b/weblayer/public/java/org/chromium/weblayer/BrowserController.java
index d9c0bde..ff1aa7c 100644
--- a/weblayer/public/java/org/chromium/weblayer/BrowserController.java
+++ b/weblayer/public/java/org/chromium/weblayer/BrowserController.java
@@ -6,13 +6,18 @@
 
 import android.net.Uri;
 import android.os.RemoteException;
+import android.webkit.ValueCallback;
 
 import org.chromium.weblayer_private.aidl.APICallException;
 import org.chromium.weblayer_private.aidl.IBrowserController;
 import org.chromium.weblayer_private.aidl.IBrowserControllerClient;
+import org.chromium.weblayer_private.aidl.IFullscreenDelegateClient;
+import org.chromium.weblayer_private.aidl.IObjectWrapper;
+import org.chromium.weblayer_private.aidl.ObjectWrapper;
 
 public final class BrowserController {
     private final IBrowserController mImpl;
+    private FullscreenDelegateClientImpl mFullscreenDelegateClient;
     private final NavigationController mNavigationController;
     private final ObserverList<BrowserObserver> mObservers;
 
@@ -28,6 +33,23 @@
         mNavigationController = NavigationController.create(mImpl);
     }
 
+    public void setFullscreenDelegate(FullscreenDelegate delegate) {
+        try {
+            if (delegate != null) {
+                mFullscreenDelegateClient = new FullscreenDelegateClientImpl(delegate);
+                mImpl.setFullscreenDelegateClient(mFullscreenDelegateClient);
+            } else {
+                mImpl.setFullscreenDelegateClient(null);
+            }
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
+    public FullscreenDelegate getFullscreenDelegate() {
+        return mFullscreenDelegateClient != null ? mFullscreenDelegateClient.getDelegate() : null;
+    }
+
     @Override
     protected void finalize() {
         // TODO(sky): figure out right assertion here if mProfile is non-null.
@@ -72,4 +94,28 @@
             }
         }
     }
+
+    private final class FullscreenDelegateClientImpl extends IFullscreenDelegateClient.Stub {
+        private FullscreenDelegate mDelegate;
+
+        /* package */ FullscreenDelegateClientImpl(FullscreenDelegate delegate) {
+            mDelegate = delegate;
+        }
+
+        public FullscreenDelegate getDelegate() {
+            return mDelegate;
+        }
+
+        @Override
+        public void enterFullscreen(IObjectWrapper exitFullscreenWrapper) {
+            ValueCallback<Void> exitFullscreenCallback = (ValueCallback<Void>) ObjectWrapper.unwrap(
+                    exitFullscreenWrapper, ValueCallback.class);
+            mDelegate.enterFullscreen(() -> exitFullscreenCallback.onReceiveValue(null));
+        }
+
+        @Override
+        public void exitFullscreen() {
+            mDelegate.exitFullscreen();
+        }
+    }
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/FullscreenDelegate.java b/weblayer/public/java/org/chromium/weblayer/FullscreenDelegate.java
new file mode 100644
index 0000000..7de84fce
--- /dev/null
+++ b/weblayer/public/java/org/chromium/weblayer/FullscreenDelegate.java
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer;
+
+/**
+ * Used to configure fullscreen related state. HTML fullscreen support is only enabled if a
+ * FullscreenDelegate is set.
+ */
+public abstract class FullscreenDelegate {
+    /**
+     * Called when the page has requested to go fullscreen. The delegate is responsible for
+     *  putting the system into fullscreen mode. The delegate can exit out of fullscreen by
+     * running the supplied Runnable (calling exitFullscreenRunner.Run() results in calling
+     * exitFullscreen()).
+     *
+     * NOTE: the Runnable must not be used synchronously.
+     */
+    public abstract void enterFullscreen(Runnable exitFullscreenRunner);
+
+    /**
+     * The page has exited fullscreen mode and the system should be moved out of fullscreen mode.
+     */
+    public abstract void exitFullscreen();
+}
diff --git a/weblayer/shell/android/shell_apk/AndroidManifest.xml b/weblayer/shell/android/shell_apk/AndroidManifest.xml
index 3d5adf5..fabcd43 100644
--- a/weblayer/shell/android/shell_apk/AndroidManifest.xml
+++ b/weblayer/shell/android/shell_apk/AndroidManifest.xml
@@ -13,7 +13,8 @@
         <activity android:name="WebLayerShellActivity"
                   android:launchMode="singleTask"
                   android:theme="@android:style/Theme.Holo.Light.NoActionBar"
-                  android:windowSoftInputMode="adjustPan|stateUnspecified">
+                  android:windowSoftInputMode="adjustPan|stateUnspecified"
+                  android:configChanges="orientation|screenSize">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
diff --git a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
index 76b80b8..e4db1874 100644
--- a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
+++ b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
@@ -17,6 +17,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
 import android.view.inputmethod.EditorInfo;
 import android.widget.EditText;
 import android.widget.LinearLayout;
@@ -29,6 +30,7 @@
 import org.chromium.weblayer.BrowserFragment;
 import org.chromium.weblayer.BrowserFragmentController;
 import org.chromium.weblayer.BrowserObserver;
+import org.chromium.weblayer.FullscreenDelegate;
 import org.chromium.weblayer.NavigationController;
 import org.chromium.weblayer.Profile;
 import org.chromium.weblayer.UnsupportedVersionException;
@@ -130,6 +132,43 @@
 
         mFragment = getOrCreateBrowserFragment(savedInstanceState);
         mBrowserFragmentController = mFragment.getController();
+        mBrowserFragmentController.getBrowserController().setFullscreenDelegate(
+                new FullscreenDelegate() {
+                    private int mSystemVisibilityToRestore;
+
+                    @Override
+                    public void enterFullscreen(Runnable exitFullscreenRunnable) {
+                        // This comes from Chrome code to avoid an extra resize.
+                        final WindowManager.LayoutParams attrs = getWindow().getAttributes();
+                        attrs.flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+                        getWindow().setAttributes(attrs);
+
+                        View decorView = getWindow().getDecorView();
+                        // Caching the system ui visibility is ok for shell, but likely not ok for
+                        // real code.
+                        mSystemVisibilityToRestore = decorView.getSystemUiVisibility();
+                        decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
+                                | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
+                                | View.SYSTEM_UI_FLAG_LOW_PROFILE
+                                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+                    }
+
+                    @Override
+                    public void exitFullscreen() {
+                        View decorView = getWindow().getDecorView();
+                        decorView.setSystemUiVisibility(mSystemVisibilityToRestore);
+
+                        final WindowManager.LayoutParams attrs = getWindow().getAttributes();
+                        if ((attrs.flags & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
+                                != 0) {
+                            attrs.flags &= ~WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+                            getWindow().setAttributes(attrs);
+                        }
+                    }
+                });
         mProfile = mBrowserFragmentController.getProfile();
 
         mBrowserFragmentController.setTopView(mTopContentsContainer);
@@ -172,7 +211,7 @@
             }
         }
 
-        File profile = new File(getDataDir(), "defaultProfile");
+        File profile = new File(getFilesDir(), "defaultProfile");
         BrowserFragment fragment = WebLayer.createBrowserFragment(profile.getPath());
         FragmentTransaction transaction = fragmentManager.beginTransaction();
         transaction.add(mMainViewId, fragment);
diff --git a/weblayer/test/BUILD.gn b/weblayer/test/BUILD.gn
index f0b855c..2fe2438 100644
--- a/weblayer/test/BUILD.gn
+++ b/weblayer/test/BUILD.gn
@@ -121,5 +121,6 @@
         "${target_gen_dir}/weblayer_browsertests_manifest/AndroidManifest.xml"
     android_manifest_dep = ":weblayer_browsertests_manifest"
     use_default_launcher = false
+    shared_resources = true
   }
 }