diff --git a/DEPS b/DEPS
index 79c7f531..96c6e9b 100644
--- a/DEPS
+++ b/DEPS
@@ -145,7 +145,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'b2c5a94b1c7a40a223c198573aa9bd83998945e4',
+  'skia_revision': '4d557e3df433400b55373b684123920b03f2f728',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -157,7 +157,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'b8ec98fe171b1598286f90c0073cfffff633864c',
+  'angle_revision': 'eb0479e245f0b6ba5bbe00ef7299ecaa82f8c6e1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -208,7 +208,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '5472a5a44c1cbbef4e3843a7bc1df6f090d37613',
+  'catapult_revision': '30604c6c8ec1c53b63a2340d0a81acfb1cb12f99',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -280,7 +280,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'e9fabf59160bd2881108f58760998372ec91017b',
+  'dawn_revision': 'ef2fac0b9447cb4cbb93616bc91508ffb6fef599',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -814,7 +814,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ecc1e12f780d1b3674f894a51299fbe6d8314972',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '24a749c4b23fbc61729ccf2ade3559a95f3164f6',
       'condition': 'checkout_linux',
   },
 
@@ -1156,7 +1156,7 @@
 
   'src/third_party/nasm': {
       'url': Var('chromium_git') + '/chromium/deps/nasm.git' + '@' +
-      'f564874f49556d930882645a348fcd6ddc6847b0'
+      '21eb595319746a669a742d210eaa413c728e7fad'
   },
 
   'src/third_party/netty-tcnative/src': {
@@ -1212,7 +1212,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '0756495a26fb6580a64aee923f97593a13a16b1a',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '64c217c797c45107bba6768bb0ee6a210ffc11a8',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1380,7 +1380,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'abaae129d9a0c6e1e092067e0b105475df43352e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '287bff32f45eba47aa68f2c4b0bdbc834e8e1cb8',
+    Var('webrtc_git') + '/src.git' + '@' + '5e4af85c479608e006e071f48eba819aefd0215c',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1421,7 +1421,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8b062b2de99f50da2ce91702124f8fb1594efa4b',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0f851e59ee6ed9a6dfa81383ed0aa884d957b5cd',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index 63e3206..eab07ca 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -452,4 +452,8 @@
   return base::android::ScopedJavaLocalRef<jobject>(obj_);
 }
 
+jlong AwBrowserContext::GetQuotaManagerBridge(JNIEnv* env) {
+  return reinterpret_cast<intptr_t>(GetQuotaManagerBridge());
+}
+
 }  // namespace android_webview
diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h
index d33e8c9..c3d9129c 100644
--- a/android_webview/browser/aw_browser_context.h
+++ b/android_webview/browser/aw_browser_context.h
@@ -74,6 +74,8 @@
   void AddVisitedURLs(const std::vector<GURL>& urls);
 
   AwQuotaManagerBridge* GetQuotaManagerBridge();
+  jlong GetQuotaManagerBridge(JNIEnv* env);
+
   AwFormDatabaseService* GetFormDatabaseService();
   autofill::AutocompleteHistoryManager* GetAutocompleteHistoryManager();
 
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
index a5bca535..17db873 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -28,7 +28,6 @@
 import org.chromium.android_webview.AwLocaleConfig;
 import org.chromium.android_webview.AwNetworkChangeNotifierRegistrationPolicy;
 import org.chromium.android_webview.AwProxyController;
-import org.chromium.android_webview.AwQuotaManagerBridge;
 import org.chromium.android_webview.AwServiceWorkerController;
 import org.chromium.android_webview.AwTracingController;
 import org.chromium.android_webview.HttpAuthDatabase;
@@ -205,7 +204,8 @@
                 AwBrowserContext awBrowserContext = getBrowserContextOnUiThread();
                 mGeolocationPermissions = new GeolocationPermissionsAdapter(
                         mFactory, awBrowserContext.getGeolocationPermissions());
-                mWebStorage = new WebStorageAdapter(mFactory, AwQuotaManagerBridge.getInstance());
+                mWebStorage =
+                        new WebStorageAdapter(mFactory, mBrowserContext.getQuotaManagerBridge());
                 mAwTracingController = getTracingController();
                 mServiceWorkerController = awBrowserContext.getServiceWorkerController();
                 mAwProxyController = new AwProxyController();
diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java
index 16b430b2..0e8e156 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java
@@ -33,6 +33,7 @@
     private AwGeolocationPermissions mGeolocationPermissions;
     private AwFormDatabase mFormDatabase;
     private AwServiceWorkerController mServiceWorkerController;
+    private AwQuotaManagerBridge mQuotaManagerBridge;
 
     /** Pointer to the Native-side AwBrowserContext. */
     private long mNativeAwBrowserContext;
@@ -85,6 +86,14 @@
         return mServiceWorkerController;
     }
 
+    public AwQuotaManagerBridge getQuotaManagerBridge() {
+        if (mQuotaManagerBridge == null) {
+            mQuotaManagerBridge =
+                    new AwQuotaManagerBridge(nativeGetQuotaManagerBridge(mNativeAwBrowserContext));
+        }
+        return mQuotaManagerBridge;
+    }
+
     /**
      * @see android.webkit.WebView#pauseTimers()
      */
@@ -121,4 +130,5 @@
     }
 
     private static native AwBrowserContext nativeGetDefaultJava();
+    private static native long nativeGetQuotaManagerBridge(long nativeAwBrowserContext);
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwQuotaManagerBridge.java b/android_webview/java/src/org/chromium/android_webview/AwQuotaManagerBridge.java
index 4958fd4..82b1120a 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwQuotaManagerBridge.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwQuotaManagerBridge.java
@@ -14,24 +14,9 @@
 /**
  * Bridge between android.webview.WebStorage and native QuotaManager. This object is owned by Java
  * AwBrowserContext and the native side is owned by the native AwBrowserContext.
- *
- * TODO(boliu): Actually make this true after Java AwBrowserContext is added.
  */
 @JNINamespace("android_webview")
 public class AwQuotaManagerBridge {
-    // TODO(boliu): This should be obtained from Java AwBrowserContext that owns this.
-    private static native long nativeGetDefaultNativeAwQuotaManagerBridge();
-
-    // TODO(boliu): This should be owned by Java AwBrowserContext, not a singleton.
-    private static AwQuotaManagerBridge sInstance;
-    public static AwQuotaManagerBridge getInstance() {
-        ThreadUtils.assertOnUiThread();
-        if (sInstance == null) {
-            sInstance = new AwQuotaManagerBridge(nativeGetDefaultNativeAwQuotaManagerBridge());
-        }
-        return sInstance;
-    }
-
     /**
      * This class represent the callback value of android.webview.WebStorage.getOrigins. The values
      * are optimized for JNI convenience and need to be converted.
@@ -59,7 +44,7 @@
     private SparseArray<Callback<Long>> mPendingGetQuotaForOriginCallbacks;
     private SparseArray<Callback<Long>> mPendingGetUsageForOriginCallbacks;
 
-    private AwQuotaManagerBridge(long nativeAwQuotaManagerBridge) {
+    public AwQuotaManagerBridge(long nativeAwQuotaManagerBridge) {
         mNativeAwQuotaManagerBridge = nativeAwQuotaManagerBridge;
         mPendingGetOriginCallbacks = new SparseArray<Callback<Origins>>();
         mPendingGetQuotaForOriginCallbacks = new SparseArray<Callback<Long>>();
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java
index 1a67db4..9c6c7a2 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java
@@ -63,12 +63,14 @@
     }
 
     private void deleteAllData() throws Exception {
-        final AwQuotaManagerBridge bridge = AwQuotaManagerBridgeTestUtil.getQuotaManagerBridge();
+        final AwQuotaManagerBridge bridge =
+                mActivityTestRule.getAwBrowserContext().getQuotaManagerBridge();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> bridge.deleteAllData());
     }
 
     private void deleteOrigin(final String origin) throws Exception {
-        final AwQuotaManagerBridge bridge = AwQuotaManagerBridgeTestUtil.getQuotaManagerBridge();
+        final AwQuotaManagerBridge bridge =
+                mActivityTestRule.getAwBrowserContext().getQuotaManagerBridge();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
                 () -> bridge.deleteOrigin(origin));
     }
@@ -89,7 +91,8 @@
 
     private long getQuotaForOrigin() throws Exception {
         final LongValueCallbackHelper callbackHelper = new LongValueCallbackHelper();
-        final AwQuotaManagerBridge bridge = AwQuotaManagerBridgeTestUtil.getQuotaManagerBridge();
+        final AwQuotaManagerBridge bridge =
+                mActivityTestRule.getAwBrowserContext().getQuotaManagerBridge();
 
         int callCount = callbackHelper.getCallCount();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
@@ -102,7 +105,8 @@
 
     private long getUsageForOrigin(final String origin) throws Exception {
         final LongValueCallbackHelper callbackHelper = new LongValueCallbackHelper();
-        final AwQuotaManagerBridge bridge = AwQuotaManagerBridgeTestUtil.getQuotaManagerBridge();
+        final AwQuotaManagerBridge bridge =
+                mActivityTestRule.getAwBrowserContext().getQuotaManagerBridge();
 
         int callCount = callbackHelper.getCallCount();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
@@ -176,11 +180,12 @@
     @DisabledTest(message = "crbug.com/609977")
     public void testGetResultsMatch() throws Exception {
         useAppCache();
-
+        AwQuotaManagerBridge bridge =
+                mActivityTestRule.getAwBrowserContext().getQuotaManagerBridge();
         AwActivityTestRule.pollInstrumentationThread(
-                () -> AwQuotaManagerBridgeTestUtil.getOrigins().mOrigins.length > 0);
+                () -> AwQuotaManagerBridgeTestUtil.getOrigins(bridge).mOrigins.length > 0);
 
-        AwQuotaManagerBridge.Origins origins = AwQuotaManagerBridgeTestUtil.getOrigins();
+        AwQuotaManagerBridge.Origins origins = AwQuotaManagerBridgeTestUtil.getOrigins(bridge);
         Assert.assertEquals(origins.mOrigins.length, origins.mUsages.length);
         Assert.assertEquals(origins.mOrigins.length, origins.mQuotas.length);
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/StandaloneAwQuotaManagerBridgeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/StandaloneAwQuotaManagerBridgeTest.java
index 57854820..d85162c 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/StandaloneAwQuotaManagerBridgeTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/StandaloneAwQuotaManagerBridgeTest.java
@@ -28,7 +28,8 @@
     @SmallTest
     public void testStartup() throws Exception {
         // AwQuotaManager should run without any issue.
-        AwQuotaManagerBridge.Origins origins = AwQuotaManagerBridgeTestUtil.getOrigins();
+        AwQuotaManagerBridge.Origins origins = AwQuotaManagerBridgeTestUtil.getOrigins(
+                mActivityTestRule.getAwBrowserContext().getQuotaManagerBridge());
         Assert.assertEquals(origins.mOrigins.length, 0);
         Assert.assertEquals(AwContents.getNativeInstanceCount(), 0);
     }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java b/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java
index abbee44..9b48e659 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java
@@ -8,16 +8,11 @@
 
 import org.chromium.android_webview.AwQuotaManagerBridge;
 import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 /**
  * This class provides common methods for AwQuotaManagerBridge related tests
  */
 public class AwQuotaManagerBridgeTestUtil {
-    public static AwQuotaManagerBridge getQuotaManagerBridge() throws Exception {
-        return TestThreadUtils.runOnUiThreadBlocking(() -> AwQuotaManagerBridge.getInstance());
-    }
-
     private static class GetOriginsCallbackHelper extends CallbackHelper {
         private AwQuotaManagerBridge.Origins mOrigins;
 
@@ -32,9 +27,9 @@
         }
     }
 
-    public static AwQuotaManagerBridge.Origins getOrigins() throws Exception {
+    public static AwQuotaManagerBridge.Origins getOrigins(AwQuotaManagerBridge bridge)
+            throws Exception {
         final GetOriginsCallbackHelper callbackHelper = new GetOriginsCallbackHelper();
-        final AwQuotaManagerBridge bridge = getQuotaManagerBridge();
 
         int callCount = callbackHelper.getCallCount();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
@@ -43,5 +38,4 @@
 
         return callbackHelper.getOrigins();
     }
-
 }
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc
index d3856e81..42c488f5 100644
--- a/ash/assistant/assistant_interaction_controller.cc
+++ b/ash/assistant/assistant_interaction_controller.cc
@@ -22,6 +22,7 @@
 #include "ash/assistant/util/histogram_util.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/ash_pref_names.h"
+#include "ash/public/cpp/assistant/assistant_setup.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -347,6 +348,13 @@
 
 void AssistantInteractionController::OnInteractionStarted(
     bool is_voice_interaction) {
+  // Stop the interaction if the opt-in window is active.
+  auto* assistant_setup = AssistantSetup::GetInstance();
+  if (assistant_setup && assistant_setup->BounceOptInWindowIfActive()) {
+    StopActiveInteraction(true);
+    return;
+  }
+
   if (is_voice_interaction) {
     // If the Assistant UI is not visible yet, and |is_voice_interaction| is
     // true, then it will be sure that Assistant is fired via OKG. ShowUi will
diff --git a/ash/assistant/assistant_ui_controller.cc b/ash/assistant/assistant_ui_controller.cc
index 423ebfe..bdd20cfec 100644
--- a/ash/assistant/assistant_ui_controller.cc
+++ b/ash/assistant/assistant_ui_controller.cc
@@ -15,6 +15,7 @@
 #include "ash/keyboard/ui/keyboard_ui_controller.h"
 #include "ash/multi_user/multi_user_window_manager_impl.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
+#include "ash/public/cpp/assistant/assistant_setup.h"
 #include "ash/public/cpp/toast_data.h"
 #include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/session/session_controller_impl.h"
@@ -362,6 +363,11 @@
 }
 
 void AssistantUiController::ShowUi(AssistantEntryPoint entry_point) {
+  // Skip if the opt-in window is active.
+  auto* assistant_setup = AssistantSetup::GetInstance();
+  if (assistant_setup && assistant_setup->BounceOptInWindowIfActive())
+    return;
+
   auto* voice_interaction_controller = VoiceInteractionController::Get();
 
   if (!voice_interaction_controller->settings_enabled().value_or(false) ||
diff --git a/ash/public/cpp/app_list/app_list_features.cc b/ash/public/cpp/app_list/app_list_features.cc
index edf8dd01..e15d89f 100644
--- a/ash/public/cpp/app_list/app_list_features.cc
+++ b/ash/public/cpp/app_list/app_list_features.cc
@@ -25,8 +25,8 @@
     "EnableZeroStateSuggestions", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableAppListSearchAutocomplete{
     "EnableAppListSearchAutocomplete", base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kEnableQueryBasedAppsRanker{
-    "EnableQueryBasedAppsRanker", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kEnableAppRanker{"EnableAppRanker",
+                                     base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableZeroStateAppsRanker{
     "EnableZeroStateAppsRanker", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableQueryBasedMixedTypesRanker{
@@ -76,8 +76,8 @@
   return base::FeatureList::IsEnabled(kEnableAppListSearchAutocomplete);
 }
 
-bool IsQueryBasedAppsRankerEnabled() {
-  return base::FeatureList::IsEnabled(kEnableQueryBasedAppsRanker);
+bool IsAppRankerEnabled() {
+  return base::FeatureList::IsEnabled(kEnableAppRanker);
 }
 
 bool IsZeroStateAppsRankerEnabled() {
diff --git a/ash/public/cpp/app_list/app_list_features.h b/ash/public/cpp/app_list/app_list_features.h
index ad05896..d48b73b 100644
--- a/ash/public/cpp/app_list/app_list_features.h
+++ b/ash/public/cpp/app_list/app_list_features.h
@@ -41,10 +41,13 @@
 // Enables the feature to autocomplete text typed in the AppList search box.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableAppListSearchAutocomplete;
 
-// Enable an model that ranks query based apps search result.
-ASH_PUBLIC_EXPORT extern const base::Feature kEnableQueryBasedAppsRanker;
+// Enable app ranking models.
+ASH_PUBLIC_EXPORT extern const base::Feature kEnableAppRanker;
 
 // Enable an model that ranks zero-state apps search result.
+// TODO(crbug.com/989350): This flag can be removed once the
+// AppSearchResultRanker is removed. Same with the
+// AppSearchResultRankerPredictorName.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableZeroStateAppsRanker;
 
 // Enable an model that ranks query based non-apps result.
@@ -75,7 +78,7 @@
 bool ASH_PUBLIC_EXPORT IsSettingsShortcutSearchEnabled();
 bool ASH_PUBLIC_EXPORT IsZeroStateSuggestionsEnabled();
 bool ASH_PUBLIC_EXPORT IsAppListSearchAutocompleteEnabled();
-bool ASH_PUBLIC_EXPORT IsQueryBasedAppsRankerEnabled();
+bool ASH_PUBLIC_EXPORT IsAppRankerEnabled();
 bool ASH_PUBLIC_EXPORT IsZeroStateAppsRankerEnabled();
 bool ASH_PUBLIC_EXPORT IsQueryBasedMixedTypesRankerEnabled();
 bool ASH_PUBLIC_EXPORT IsZeroStateMixedTypesRankerEnabled();
diff --git a/ash/public/cpp/assistant/assistant_setup.h b/ash/public/cpp/assistant/assistant_setup.h
index 80b0cf4..f7b556f3 100644
--- a/ash/public/cpp/assistant/assistant_setup.h
+++ b/ash/public/cpp/assistant/assistant_setup.h
@@ -34,6 +34,9 @@
       FlowType type,
       StartAssistantOptInFlowCallback on_completed) = 0;
 
+  // Returns true and bounces the opt-in window if it is active.
+  virtual bool BounceOptInWindowIfActive() = 0;
+
  protected:
   AssistantSetup();
   virtual ~AssistantSetup();
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 32257ca0..4cee2902 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -46,6 +46,13 @@
   # file name) is saved.
   enable_location_source = true
 
+  # When enabled, iterators will validate that they are not used in ways that
+  # violate spatial safety (i.e. out-of-bounds memory accesses).
+  # TODO(https://crbug.com/817982): currently guarded by a buildflag since
+  # there are non-trivial binary size regressions which are especially
+  # noticeable on Android.
+  enable_checked_iterators = !is_android
+
   # Unsafe developer build. Has developer-friendly features that may weaken or
   # disable security measures like sandboxing or ASLR.
   # IMPORTANT: Unsafe developer builds should never be distributed to end users.
@@ -1267,6 +1274,7 @@
     ":build_date",
     ":cfi_buildflags",
     ":debugging_buildflags",
+    ":iterator_buildflags",
     ":logging_buildflags",
     ":orderfile_buildflags",
     ":partition_alloc_buildflags",
@@ -2081,6 +2089,12 @@
   ]
 }
 
+buildflag_header("iterator_buildflags") {
+  header = "iterator_buildflags.h"
+  header_dir = "base/containers"
+  flags = [ "ENABLE_CHECKED_ITERATORS=$enable_checked_iterators" ]
+}
+
 buildflag_header("logging_buildflags") {
   header = "logging_buildflags.h"
 
diff --git a/base/callback_internal.h b/base/callback_internal.h
index 777397d..fdfdf7f8 100644
--- a/base/callback_internal.h
+++ b/base/callback_internal.h
@@ -45,11 +45,10 @@
 // DoInvoke function to perform the function execution.  This allows
 // us to shield the Callback class from the types of the bound argument via
 // "type erasure."
-// At the base level, the only task is to add reference counting data. Don't use
-// RefCountedThreadSafe since it requires the destructor to be a virtual method.
-// Creating a vtable for every BindState template instantiation results in a lot
-// of bloat. Its only task is to call the destructor which can be done with a
-// function pointer.
+// At the base level, the only task is to add reference counting data. Avoid
+// using or inheriting any virtual functions. Creating a vtable for every
+// BindState template instantiation results in a lot of bloat. Its only task is
+// to call the destructor which can be done with a function pointer.
 class BASE_EXPORT BindStateBase
     : public RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits> {
  public:
diff --git a/base/containers/checked_iterators.h b/base/containers/checked_iterators.h
index b7eb7b4..e9fdf916b 100644
--- a/base/containers/checked_iterators.h
+++ b/base/containers/checked_iterators.h
@@ -8,8 +8,20 @@
 #include <iterator>
 #include <memory>
 
+#include "base/containers/iterator_buildflags.h"
 #include "base/containers/util.h"
 #include "base/logging.h"
+#include "build/build_config.h"
+
+#if BUILDFLAG(ENABLE_CHECKED_ITERATORS)
+#define ITERATOR_CHECK CHECK
+#define ITERATOR_CHECK_EQ CHECK_EQ
+#define ITERATOR_CHECK_LE CHECK_LE
+#else
+#define ITERATOR_CHECK DCHECK
+#define ITERATOR_CHECK_EQ DCHECK_EQ
+#define ITERATOR_CHECK_LE DCHECK_LE
+#endif
 
 namespace base {
 
@@ -32,8 +44,8 @@
       : CheckedRandomAccessIterator(start, start, end) {}
   CheckedRandomAccessIterator(T* start, T* current, const T* end)
       : start_(start), current_(current), end_(end) {
-    CHECK(start <= current);
-    CHECK(current <= end);
+    ITERATOR_CHECK(start <= current);
+    ITERATOR_CHECK(current <= end);
   }
   CheckedRandomAccessIterator(const CheckedRandomAccessIterator& other) =
       default;
@@ -63,7 +75,7 @@
   }
 
   CheckedRandomAccessIterator& operator++() {
-    CHECK(current_ != end_);
+    ITERATOR_CHECK(current_ != end_);
     ++current_;
     return *this;
   }
@@ -75,7 +87,7 @@
   }
 
   CheckedRandomAccessIterator& operator--() {
-    CHECK(current_ != start_);
+    ITERATOR_CHECK(current_ != start_);
     --current_;
     return *this;
   }
@@ -88,9 +100,9 @@
 
   CheckedRandomAccessIterator& operator+=(difference_type rhs) {
     if (rhs > 0) {
-      CHECK_LE(rhs, end_ - current_);
+      ITERATOR_CHECK_LE(rhs, end_ - current_);
     } else {
-      CHECK_LE(-rhs, current_ - start_);
+      ITERATOR_CHECK_LE(-rhs, current_ - start_);
     }
     current_ += rhs;
     return *this;
@@ -104,9 +116,9 @@
 
   CheckedRandomAccessIterator& operator-=(difference_type rhs) {
     if (rhs < 0) {
-      CHECK_LE(rhs, end_ - current_);
+      ITERATOR_CHECK_LE(rhs, end_ - current_);
     } else {
-      CHECK_LE(-rhs, current_ - start_);
+      ITERATOR_CHECK_LE(-rhs, current_ - start_);
     }
     current_ -= rhs;
     return *this;
@@ -120,18 +132,18 @@
 
   friend difference_type operator-(const CheckedRandomAccessIterator& lhs,
                                    const CheckedRandomAccessIterator& rhs) {
-    CHECK(lhs.start_ == rhs.start_);
-    CHECK(lhs.end_ == rhs.end_);
+    ITERATOR_CHECK(lhs.start_ == rhs.start_);
+    ITERATOR_CHECK(lhs.end_ == rhs.end_);
     return lhs.current_ - rhs.current_;
   }
 
   reference operator*() const {
-    CHECK(current_ != end_);
+    ITERATOR_CHECK(current_ != end_);
     return *current_;
   }
 
   pointer operator->() const {
-    CHECK(current_ != end_);
+    ITERATOR_CHECK(current_ != end_);
     return current_;
   }
 
@@ -153,8 +165,8 @@
 
  private:
   void CheckComparable(const CheckedRandomAccessIterator& other) const {
-    CHECK_EQ(start_, other.start_);
-    CHECK_EQ(end_, other.end_);
+    ITERATOR_CHECK_EQ(start_, other.start_);
+    ITERATOR_CHECK_EQ(end_, other.end_);
   }
 
   const T* start_ = nullptr;
@@ -176,16 +188,16 @@
       : CheckedRandomAccessConstIterator(start, start, end) {}
   CheckedRandomAccessConstIterator(T* start, T* current, const T* end)
       : start_(start), current_(current), end_(end) {
-    CHECK(start <= current);
-    CHECK(current <= end);
+    ITERATOR_CHECK(start <= current);
+    ITERATOR_CHECK(current <= end);
   }
   CheckedRandomAccessConstIterator(
       const CheckedRandomAccessConstIterator& other) = default;
   CheckedRandomAccessConstIterator(const CheckedRandomAccessIterator<T>& other)
       : start_(other.start_), current_(other.current_), end_(other.end_) {
     // We explicitly don't delegate to the 3-argument constructor here. Its
-    // CHECKs would be redundant, since we expect |other| to maintain its own
-    // invariant. However, DCHECKs never hurt anybody. Presumably.
+    // ITERATOR_CHECKs would be redundant, since we expect |other| to maintain
+    // its own invariant. However, DCHECKs never hurt anybody. Presumably.
     DCHECK(other.start_ <= other.current_);
     DCHECK(other.current_ <= other.end_);
   }
@@ -218,7 +230,7 @@
   }
 
   CheckedRandomAccessConstIterator& operator++() {
-    CHECK(current_ != end_);
+    ITERATOR_CHECK(current_ != end_);
     ++current_;
     return *this;
   }
@@ -230,7 +242,7 @@
   }
 
   CheckedRandomAccessConstIterator& operator--() {
-    CHECK(current_ != start_);
+    ITERATOR_CHECK(current_ != start_);
     --current_;
     return *this;
   }
@@ -243,9 +255,9 @@
 
   CheckedRandomAccessConstIterator& operator+=(difference_type rhs) {
     if (rhs > 0) {
-      CHECK_LE(rhs, end_ - current_);
+      ITERATOR_CHECK_LE(rhs, end_ - current_);
     } else {
-      CHECK_LE(-rhs, current_ - start_);
+      ITERATOR_CHECK_LE(-rhs, current_ - start_);
     }
     current_ += rhs;
     return *this;
@@ -259,9 +271,9 @@
 
   CheckedRandomAccessConstIterator& operator-=(difference_type rhs) {
     if (rhs < 0) {
-      CHECK_LE(rhs, end_ - current_);
+      ITERATOR_CHECK_LE(rhs, end_ - current_);
     } else {
-      CHECK_LE(-rhs, current_ - start_);
+      ITERATOR_CHECK_LE(-rhs, current_ - start_);
     }
     current_ -= rhs;
     return *this;
@@ -276,18 +288,18 @@
   friend difference_type operator-(
       const CheckedRandomAccessConstIterator& lhs,
       const CheckedRandomAccessConstIterator& rhs) {
-    CHECK(lhs.start_ == rhs.start_);
-    CHECK(lhs.end_ == rhs.end_);
+    ITERATOR_CHECK(lhs.start_ == rhs.start_);
+    ITERATOR_CHECK(lhs.end_ == rhs.end_);
     return lhs.current_ - rhs.current_;
   }
 
   reference operator*() const {
-    CHECK(current_ != end_);
+    ITERATOR_CHECK(current_ != end_);
     return *current_;
   }
 
   pointer operator->() const {
-    CHECK(current_ != end_);
+    ITERATOR_CHECK(current_ != end_);
     return current_;
   }
 
@@ -309,8 +321,8 @@
 
  private:
   void CheckComparable(const CheckedRandomAccessConstIterator& other) const {
-    CHECK_EQ(start_, other.start_);
-    CHECK_EQ(end_, other.end_);
+    ITERATOR_CHECK_EQ(start_, other.start_);
+    ITERATOR_CHECK_EQ(end_, other.end_);
   }
 
   const T* start_ = nullptr;
@@ -320,4 +332,8 @@
 
 }  // namespace base
 
+#undef ITERATOR_CHECK
+#undef ITERATOR_CHECK_EQ
+#undef ITERATOR_CHECK_LE
+
 #endif  // BASE_CONTAINERS_CHECKED_ITERATORS_H_
diff --git a/base/containers/circular_deque.h b/base/containers/circular_deque.h
index bf42a95..93f1e79 100644
--- a/base/containers/circular_deque.h
+++ b/base/containers/circular_deque.h
@@ -791,12 +791,12 @@
       return iterator(this, first.index_);
     } else if (first.index_ < last.index_) {
       // Contiguous range.
-      buffer_.DestructRange(&buffer_[first.index_], &buffer_[last.index_]);
+      buffer_.DestructRange(buffer_.begin() + first.index_,
+                            buffer_.begin() + last.index_);
     } else {
       // Deleted range wraps around.
-      buffer_.DestructRange(&buffer_[first.index_],
-                            &buffer_[buffer_.capacity()]);
-      buffer_.DestructRange(&buffer_[0], &buffer_[last.index_]);
+      buffer_.DestructRange(buffer_.begin() + first.index_, buffer_.end());
+      buffer_.DestructRange(buffer_.begin(), buffer_.begin() + last.index_);
     }
 
     if (first.index_ == begin_) {
@@ -812,9 +812,9 @@
     iterator move_src_end = end();
     iterator move_dest(this, first.index_);
     for (; move_src < move_src_end; move_src++, move_dest++) {
-      buffer_.MoveRange(&buffer_[move_src.index_],
-                        &buffer_[move_src.index_ + 1],
-                        &buffer_[move_dest.index_]);
+      buffer_.MoveRange(buffer_.begin() + move_src.index_,
+                        buffer_.begin() + move_src.index_ + 1,
+                        buffer_.begin() + move_dest.index_);
     }
 
     end_ = move_dest.index_;
@@ -860,7 +860,8 @@
 
   void pop_front() {
     DCHECK(size());
-    buffer_.DestructRange(&buffer_[begin_], &buffer_[begin_ + 1]);
+    buffer_.DestructRange(buffer_.begin() + begin_,
+                          buffer_.begin() + begin_ + 1);
     begin_++;
     if (begin_ == buffer_.capacity())
       begin_ = 0;
@@ -879,7 +880,7 @@
       end_ = buffer_.capacity() - 1;
     else
       end_--;
-    buffer_.DestructRange(&buffer_[end_], &buffer_[end_ + 1]);
+    buffer_.DestructRange(buffer_.begin() + end_, buffer_.begin() + end_ + 1);
 
     ShrinkCapacityIfNecessary();
 
@@ -917,17 +918,17 @@
     *to_begin = 0;
     if (from_begin < from_end) {
       // Contiguous.
-      from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_end],
-                         to_buf->begin());
+      from_buf.MoveRange(from_buf.begin() + from_begin,
+                         from_buf.begin() + from_end, to_buf->begin());
       *to_end = from_end - from_begin;
     } else if (from_begin > from_end) {
       // Discontiguous, copy the right side to the beginning of the new buffer.
-      from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_capacity],
+      from_buf.MoveRange(from_buf.begin() + from_begin, from_buf.end(),
                          to_buf->begin());
       size_t right_size = from_capacity - from_begin;
       // Append the left side.
-      from_buf.MoveRange(&from_buf[0], &from_buf[from_end],
-                         &(*to_buf)[right_size]);
+      from_buf.MoveRange(from_buf.begin(), from_buf.begin() + from_end,
+                         to_buf->begin() + right_size);
       *to_end = right_size + from_end;
     } else {
       // No items.
@@ -998,10 +999,10 @@
     if (end == begin) {
       return;
     } else if (end > begin) {
-      buffer_.DestructRange(&buffer_[begin], &buffer_[end]);
+      buffer_.DestructRange(buffer_.begin() + begin, buffer_.begin() + end);
     } else {
-      buffer_.DestructRange(&buffer_[begin], &buffer_[buffer_.capacity()]);
-      buffer_.DestructRange(&buffer_[0], &buffer_[end]);
+      buffer_.DestructRange(buffer_.begin() + begin, buffer_.end());
+      buffer_.DestructRange(buffer_.begin(), buffer_.begin() + end);
     }
   }
 
@@ -1035,8 +1036,9 @@
         break;
       --src;
       --dest;
-      buffer_.MoveRange(&buffer_[src.index_], &buffer_[src.index_ + 1],
-                        &buffer_[dest.index_]);
+      buffer_.MoveRange(buffer_.begin() + src.index_,
+                        buffer_.begin() + src.index_ + 1,
+                        buffer_.begin() + dest.index_);
     }
   }
 
diff --git a/base/containers/vector_buffer.h b/base/containers/vector_buffer.h
index 83cd2ac..09c312ce 100644
--- a/base/containers/vector_buffer.h
+++ b/base/containers/vector_buffer.h
@@ -11,10 +11,12 @@
 #include <type_traits>
 #include <utility>
 
+#include "base/containers/checked_iterators.h"
 #include "base/containers/util.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/numerics/checked_math.h"
+#include "build/build_config.h"
 
 namespace base {
 namespace internal {
@@ -40,6 +42,8 @@
 class VectorBuffer {
  public:
   constexpr VectorBuffer() = default;
+  using iterator = CheckedRandomAccessIterator<T>;
+  using const_iterator = CheckedRandomAccessConstIterator<T>;
 
 #if defined(__clang__) && !defined(__native_client__)
   // This constructor converts an uninitialized void* to a T* which triggers
@@ -86,8 +90,12 @@
     return buffer_[i];
   }
 
-  T* begin() { return buffer_; }
-  T* end() { return &buffer_[capacity_]; }
+  iterator begin() const noexcept {
+    return iterator(buffer_, buffer_ + capacity_);
+  }
+  iterator end() const noexcept {
+    return iterator(buffer_, buffer_ + capacity_, buffer_ + capacity_);
+  }
 
   // DestructRange ------------------------------------------------------------
 
@@ -95,15 +103,15 @@
   template <typename T2 = T,
             typename std::enable_if<std::is_trivially_destructible<T2>::value,
                                     int>::type = 0>
-  void DestructRange(T* begin, T* end) {}
+  void DestructRange(iterator begin, iterator end) {}
 
   // Non-trivially destructible objects must have their destructors called
   // individually.
   template <typename T2 = T,
             typename std::enable_if<!std::is_trivially_destructible<T2>::value,
                                     int>::type = 0>
-  void DestructRange(T* begin, T* end) {
-    CHECK_LE(begin, end);
+  void DestructRange(iterator begin, iterator end) {
+    CHECK(begin <= end);
     while (begin != end) {
       begin->~T();
       begin++;
@@ -119,16 +127,15 @@
   // and the address of the first element to copy to. There must be sufficient
   // room in the destination for all items in the range [begin, end).
 
-  // Trivially copyable types can use memcpy. trivially copyable implies
-  // that there is a trivial destructor as we don't have to call it.
+  // Trivially copyable types can use memcpy. Trivially copyable implies that
+  // there is a trivial destructor as we don't have to call it.
   template <typename T2 = T,
             typename std::enable_if<base::is_trivially_copyable<T2>::value,
                                     int>::type = 0>
-  static void MoveRange(T* from_begin, T* from_end, T* to) {
-    CHECK(!RangesOverlap(from_begin, from_end, to));
-    memcpy(
-        to, from_begin,
-        CheckSub(get_uintptr(from_end), get_uintptr(from_begin)).ValueOrDie());
+  static void MoveRange(iterator from_begin, iterator from_end, iterator to) {
+    CHECK(iterator::IsRangeMoveSafe(from_begin, from_end, to));
+    memcpy(&(*to), &(*from_begin),
+           std::distance(from_begin, from_end) * sizeof(T));
   }
 
   // Not trivially copyable, but movable: call the move constructor and
@@ -137,10 +144,10 @@
             typename std::enable_if<std::is_move_constructible<T2>::value &&
                                         !base::is_trivially_copyable<T2>::value,
                                     int>::type = 0>
-  static void MoveRange(T* from_begin, T* from_end, T* to) {
-    CHECK(!RangesOverlap(from_begin, from_end, to));
+  static void MoveRange(iterator from_begin, iterator from_end, iterator to) {
+    CHECK(iterator::IsRangeMoveSafe(from_begin, from_end, to));
     while (from_begin != from_end) {
-      new (to) T(std::move(*from_begin));
+      new (&(*to)) T(std::move(*from_begin));
       from_begin->~T();
       from_begin++;
       to++;
@@ -153,10 +160,10 @@
             typename std::enable_if<!std::is_move_constructible<T2>::value &&
                                         !base::is_trivially_copyable<T2>::value,
                                     int>::type = 0>
-  static void MoveRange(T* from_begin, T* from_end, T* to) {
-    CHECK(!RangesOverlap(from_begin, from_end, to));
+  static void MoveRange(iterator from_begin, iterator from_end, iterator to) {
+    CHECK(iterator::IsRangeMoveSafe(from_begin, from_end, to));
     while (from_begin != from_end) {
-      new (to) T(*from_begin);
+      new (&(*to)) T(*from_begin);
       from_begin->~T();
       from_begin++;
       to++;
@@ -164,18 +171,6 @@
   }
 
  private:
-  static bool RangesOverlap(const T* from_begin,
-                            const T* from_end,
-                            const T* to) {
-    const auto from_begin_uintptr = get_uintptr(from_begin);
-    const auto from_end_uintptr = get_uintptr(from_end);
-    const auto to_uintptr = get_uintptr(to);
-    return !(
-        to >= from_end ||
-        CheckAdd(to_uintptr, CheckSub(from_end_uintptr, from_begin_uintptr))
-                .ValueOrDie() <= from_begin_uintptr);
-  }
-
   T* buffer_ = nullptr;
   size_t capacity_ = 0;
 
diff --git a/base/containers/vector_buffer_unittest.cc b/base/containers/vector_buffer_unittest.cc
index 6d49505..bf838639 100644
--- a/base/containers/vector_buffer_unittest.cc
+++ b/base/containers/vector_buffer_unittest.cc
@@ -15,26 +15,26 @@
   constexpr int size = 10;
   VectorBuffer<int> buffer(size);
   for (int i = 0; i < size; i++)
-    buffer.begin()[i] = i + 1;
+    buffer[i] = i + 1;
 
   buffer.DestructRange(buffer.begin(), buffer.end());
 
   // Delete should do nothing.
   for (int i = 0; i < size; i++)
-    EXPECT_EQ(i + 1, buffer.begin()[i]);
+    EXPECT_EQ(i + 1, buffer[i]);
 }
 
 TEST(VectorBuffer, DeleteMoveOnly) {
   constexpr int size = 10;
   VectorBuffer<MoveOnlyInt> buffer(size);
   for (int i = 0; i < size; i++)
-    new (buffer.begin() + i) MoveOnlyInt(i + 1);
+    buffer[i] = MoveOnlyInt(i + 1);
 
   buffer.DestructRange(buffer.begin(), buffer.end());
 
   // Delete should have reset all of the values to 0.
   for (int i = 0; i < size; i++)
-    EXPECT_EQ(0, buffer.begin()[i].data());
+    EXPECT_EQ(0, buffer[i].data());
 }
 
 TEST(VectorBuffer, PODMove) {
@@ -43,11 +43,11 @@
 
   VectorBuffer<int> original(size);
   for (int i = 0; i < size; i++)
-    original.begin()[i] = i + 1;
+    original[i] = i + 1;
 
   original.MoveRange(original.begin(), original.end(), dest.begin());
   for (int i = 0; i < size; i++)
-    EXPECT_EQ(i + 1, dest.begin()[i]);
+    EXPECT_EQ(i + 1, dest[i]);
 }
 
 TEST(VectorBuffer, MovableMove) {
@@ -56,14 +56,13 @@
 
   VectorBuffer<MoveOnlyInt> original(size);
   for (int i = 0; i < size; i++)
-    new (original.begin() + i) MoveOnlyInt(i + 1);
+    original[i] = MoveOnlyInt(i + 1);
 
   original.MoveRange(original.begin(), original.end(), dest.begin());
-
   // Moving from a MoveOnlyInt resets to 0.
   for (int i = 0; i < size; i++) {
-    EXPECT_EQ(0, original.begin()[i].data());
-    EXPECT_EQ(i + 1, dest.begin()[i].data());
+    EXPECT_EQ(0, original[i].data());
+    EXPECT_EQ(i + 1, dest[i].data());
   }
 }
 
@@ -73,15 +72,15 @@
 
   VectorBuffer<CopyOnlyInt> original(size);
   for (int i = 0; i < size; i++)
-    new (original.begin() + i) CopyOnlyInt(i + 1);
+    new (&original[i]) CopyOnlyInt(i + 1);
 
   original.MoveRange(original.begin(), original.end(), dest.begin());
 
   // The original should have been destructed, which should reset the value to
   // 0. Technically this dereferences the destructed object.
   for (int i = 0; i < size; i++) {
-    EXPECT_EQ(0, original.begin()[i].data());
-    EXPECT_EQ(i + 1, dest.begin()[i].data());
+    EXPECT_EQ(0, original[i].data());
+    EXPECT_EQ(i + 1, dest[i].data());
   }
 }
 
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index c9dbf9ae..39880c6 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8905686391863036768
\ No newline at end of file
+8905658232335495936
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index ab8a1342..eb1e7fa 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8905689717449691136
\ No newline at end of file
+8905658267152347024
\ No newline at end of file
diff --git a/chrome/VERSION b/chrome/VERSION
index b4056f3..e7ae934 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=78
 MINOR=0
-BUILD=3878
+BUILD=3879
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
index 65da435..48cf0dba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
@@ -222,6 +222,10 @@
                                         context, cancelIntent, downloadUpdate.getNotificationId()),
                                 cancelActionType);
 
+                // On touchless devices, the only access point for downloads is the notification, so
+                // keep the notification persistent if not in the completed or failure state.
+                if (FeatureUtilities.isNoTouchModeEnabled()) builder.setOngoing(true);
+
                 if (!downloadUpdate.getIsOffTheRecord())
                     builder.setLargeIcon(downloadUpdate.getIcon());
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/tooltip/FeedTooltipTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/tooltip/FeedTooltipTest.java
index ac1b879..d91ed3d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/tooltip/FeedTooltipTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/tooltip/FeedTooltipTest.java
@@ -13,8 +13,9 @@
 import static com.google.android.libraries.feed.basicstream.internal.viewholders.ViewHolderType.TYPE_CARD;
 
 import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
 
-import android.os.Build;
 import android.support.test.espresso.contrib.RecyclerViewActions;
 import android.support.test.espresso.matcher.RootMatchers;
 import android.support.test.filters.MediumTest;
@@ -31,7 +32,6 @@
 
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -112,8 +112,6 @@
 
     @Test
     @MediumTest
-    @DisableIf.
-    Build(sdk_is_greater_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/990135")
     @Feature({"FeedNewTabPage"})
     public void testShowTooltip() throws Exception {
         int callCount = mTestObserver.firstCardShownCallback.getCallCount();
@@ -124,7 +122,8 @@
                 .perform(RecyclerViewActions.scrollToPosition(FIRST_CARD_POSITION),
                         RecyclerViewActions.actionOnItemAtPosition(FIRST_CARD_POSITION, click()));
         onView(withText(TOOLTIP_TEXT))
-                .inRoot(RootMatchers.isPlatformPopup())
+                .inRoot(RootMatchers.withDecorView(
+                        not(is(mActivityTestRule.getActivity().getWindow().getDecorView()))))
                 .check(matches(isDisplayed()));
     }
 }
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a75b4d38..06b0ffd 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3023,11 +3023,6 @@
       "first_run/first_run_internal_linux.cc",
       "first_run/first_run_internal_mac.mm",
       "first_run/first_run_internal_win.cc",
-      "first_run/upgrade_util.h",
-      "first_run/upgrade_util_mac.h",
-      "first_run/upgrade_util_mac.mm",
-      "first_run/upgrade_util_win.cc",
-      "first_run/upgrade_util_win.h",
       "font_family_cache.cc",
       "font_family_cache.h",
       "hid/chrome_hid_delegate.cc",
@@ -3701,7 +3696,6 @@
       "browser_switcher/browser_switcher_service_win.h",
       "downgrade/user_data_downgrade.cc",
       "downgrade/user_data_downgrade.h",
-      "first_run/upgrade_util.cc",
       "notifications/win/notification_image_retainer.cc",
       "notifications/win/notification_image_retainer.h",
       "notifications/win/notification_template_builder.cc",
@@ -3867,7 +3861,6 @@
   if (is_desktop_linux) {
     # Desktop linux, doesn't count ChromeOS.
     sources += [
-      "first_run/upgrade_util.cc",
       "first_run/upgrade_util_linux.cc",
       "first_run/upgrade_util_linux.h",
       "icon_loader_auralinux.cc",
@@ -3983,6 +3976,12 @@
 
   if (!is_android && !is_chromeos) {
     sources += [
+      "first_run/upgrade_util.cc",
+      "first_run/upgrade_util.h",
+      "first_run/upgrade_util_mac.h",
+      "first_run/upgrade_util_mac.mm",
+      "first_run/upgrade_util_win.cc",
+      "first_run/upgrade_util_win.h",
       "lifetime/switch_utils.cc",
       "lifetime/switch_utils.h",
       "metrics/upgrade_metrics_provider.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4b2ae36..faa3a23 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2526,13 +2526,6 @@
      kOsAll | kExpireM77,
      FEATURE_VALUE_TYPE(
          autofill::features::kAutofillUpstreamEditableExpirationDate)},
-    {"enable-autofill-do-not-upload-save-unsupported-cards",
-     flag_descriptions::kEnableAutofillDoNotUploadSaveUnsupportedCardsName,
-     flag_descriptions::
-         kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription,
-     kOsAll | kExpireM77,
-     FEATURE_VALUE_TYPE(
-         autofill::features::kAutofillDoNotUploadSaveUnsupportedCards)},
     {"enable-autofill-import-dynamic-forms",
      flag_descriptions::kEnableAutofillImportDynamicFormsName,
      flag_descriptions::kEnableAutofillImportDynamicFormsDescription,
diff --git a/chrome/browser/android/shortcut_info.h b/chrome/browser/android/shortcut_info.h
index c4230e05..35a93ea 100644
--- a/chrome/browser/android/shortcut_info.h
+++ b/chrome/browser/android/shortcut_info.h
@@ -16,7 +16,7 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "url/gurl.h"
 
-// https://pr-preview.s3.amazonaws.com/ewilligers/web-share-target/pull/53.html#sharetargetfiles-and-its-members
+// https://wicg.github.io/web-share-target/level-2/#sharetargetfiles-and-its-members
 struct ShareTargetParamsFile {
   base::string16 name;
   std::vector<base::string16> accept;
diff --git a/chrome/browser/android/webapk/webapk.proto b/chrome/browser/android/webapk/webapk.proto
index 7b84ad9f..8e08c9b 100644
--- a/chrome/browser/android/webapk/webapk.proto
+++ b/chrome/browser/android/webapk/webapk.proto
@@ -123,7 +123,7 @@
 }
 
 // A proto representing a ShareTargetParamsFile
-// https://pr-preview.s3.amazonaws.com/ewilligers/web-share-target/pull/53.html#sharetargetfiles-and-its-members
+// https://wicg.github.io/web-share-target/level-2/#sharetargetfiles-and-its-members
 message ShareTargetParamsFile {
   optional string name = 1;
   repeated string accept = 2;
@@ -133,7 +133,7 @@
 // https://wicg.github.io/web-share-target/#dom-sharetargetparams
 // Each field corresponds to key in ShareData. These are the query parameter
 // keys to be used for the data supplied in a ShareData instance.
-// ShareData: https://wicg.github.io/web-share#dom-sharedata
+// ShareData: https://w3c.github.io/web-share#dom-sharedata
 message ShareTargetParams {
   optional string title = 1;
   optional string text = 2;
diff --git a/chrome/browser/apps/app_service/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon_factory.cc
index da0b36a..f0c8a4cd 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.cc
+++ b/chrome/browser/apps/app_service/app_icon_factory.cc
@@ -264,7 +264,6 @@
                            IconEffects icon_effects,
                            apps::mojom::Publisher::LoadIconCallback callback) {
   constexpr bool is_placeholder_icon = false;
-  int size_hint_in_px = apps_util::ConvertDipToPx(size_hint_in_dip);
 
   // This is the default icon for AppType::kExtension. Other app types might
   // use a different default icon, such as IDR_LOGO_CROSTINI_DEFAULT_192.
@@ -276,7 +275,7 @@
           ->GetInstalledExtension(extension_id);
   if (extension) {
     extensions::ExtensionResource ext_resource =
-        extensions::IconsInfo::GetIconResource(extension, size_hint_in_px,
+        extensions::IconsInfo::GetIconResource(extension, size_hint_in_dip,
                                                ExtensionIconSet::MATCH_BIGGER);
 
     switch (icon_compression) {
@@ -286,7 +285,7 @@
       case apps::mojom::IconCompression::kUncompressed: {
         extensions::ImageLoader::Get(context)->LoadImageAsync(
             extension, std::move(ext_resource),
-            gfx::Size(size_hint_in_px, size_hint_in_px),
+            gfx::Size(size_hint_in_dip, size_hint_in_dip),
             base::BindOnce(&RunCallbackWithUncompressedImage, size_hint_in_dip,
                            default_icon_resource, is_placeholder_icon,
                            icon_effects, std::move(callback)));
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index ee551ce..1879d54e 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -26,7 +26,7 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/mojom/installation/installation.mojom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -185,7 +185,7 @@
 
   // App has been installed (possibly by the user), page may no longer request
   // install prompt.
-  binding_.Close();
+  receiver_.reset();
 }
 
 void AppBannerManager::SendBannerAccepted() {
@@ -221,7 +221,7 @@
 }
 
 bool AppBannerManager::IsPromptAvailableForTesting() const {
-  return binding_.is_bound();
+  return receiver_.is_bound();
 }
 
 AppBannerManager::AppBannerManager(content::WebContents* web_contents)
@@ -230,7 +230,6 @@
           Profile::FromBrowserContext(web_contents->GetBrowserContext()))),
       state_(State::INACTIVE),
       manager_(InstallableManager::FromWebContents(web_contents)),
-      binding_(this),
       has_sufficient_engagement_(false),
       load_finished_(false),
       status_reporter_(std::make_unique<NullStatusReporter>()),
@@ -432,7 +431,7 @@
 }
 
 void AppBannerManager::ResetBindings() {
-  binding_.Close();
+  receiver_.reset();
   event_.reset();
 }
 
@@ -529,18 +528,16 @@
   // Any existing binding is invalid when we send a new beforeinstallprompt.
   ResetBindings();
 
-  blink::mojom::AppBannerControllerPtr controller;
+  mojo::Remote<blink::mojom::AppBannerController> controller;
   web_contents()->GetMainFrame()->GetRemoteInterfaces()->GetInterface(
-      mojo::MakeRequest(&controller));
-
-  blink::mojom::AppBannerServicePtr banner_proxy;
-  binding_.Bind(mojo::MakeRequest(&banner_proxy));
+      controller.BindNewPipeAndPassReceiver());
 
   // Get a raw controller pointer before we move out of the smart pointer to
   // avoid crashing with MSVC's order of evaluation.
   blink::mojom::AppBannerController* controller_ptr = controller.get();
   controller_ptr->BannerPromptRequest(
-      std::move(banner_proxy), mojo::MakeRequest(&event_), {GetBannerType()},
+      receiver_.BindNewPipeAndPassRemote(), event_.BindNewPipeAndPassReceiver(),
+      {GetBannerType()},
       base::BindOnce(&AppBannerManager::OnBannerPromptReply, GetWeakPtr(),
                      std::move(controller)));
 }
@@ -709,7 +706,7 @@
 }
 
 void AppBannerManager::OnBannerPromptReply(
-    blink::mojom::AppBannerControllerPtr controller,
+    mojo::Remote<blink::mojom::AppBannerController> controller,
     blink::mojom::AppBannerPromptReply reply) {
   // The renderer might have requested the prompt to be canceled. They may
   // request that it is redisplayed later, so don't Terminate() here. However,
@@ -781,7 +778,7 @@
 
 void AppBannerManager::DisplayAppBanner() {
   // Prevent this from being called multiple times on the same connection.
-  binding_.Close();
+  receiver_.reset();
 
   if (state_ == State::PENDING_PROMPT) {
     ShowBanner();
diff --git a/chrome/browser/banners/app_banner_manager.h b/chrome/browser/banners/app_banner_manager.h
index 6186fda2..620e952 100644
--- a/chrome/browser/banners/app_banner_manager.h
+++ b/chrome/browser/banners/app_banner_manager.h
@@ -20,7 +20,8 @@
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "content/public/browser/media_player_id.h"
 #include "content/public/browser/web_contents_observer.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/manifest/web_display_mode.h"
 #include "third_party/blink/public/mojom/app_banner/app_banner.mojom.h"
 #include "url/gurl.h"
@@ -343,7 +344,7 @@
   // intention to show a prompt. The renderer will send a message back with the
   // opportunity to cancel.
   virtual void OnBannerPromptReply(
-      blink::mojom::AppBannerControllerPtr controller,
+      mojo::Remote<blink::mojom::AppBannerController> controller,
       blink::mojom::AppBannerPromptReply reply);
 
   // Does the non-platform specific parts of showing the app banner.
@@ -371,9 +372,8 @@
   // background will appear when the tab is reactivated.
   std::vector<content::MediaPlayerId> active_media_players_;
 
-  // Mojo bindings and interface pointers.
-  mojo::Binding<blink::mojom::AppBannerService> binding_;
-  blink::mojom::AppBannerEventPtr event_;
+  mojo::Receiver<blink::mojom::AppBannerService> receiver_{this};
+  mojo::Remote<blink::mojom::AppBannerEvent> event_;
 
   // If a banner is requested before the page has finished loading, defer
   // triggering the pipeline until the load is complete.
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index 365c8aace..0dd7bf2d 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -103,8 +103,9 @@
     }
   }
 
-  void OnBannerPromptReply(blink::mojom::AppBannerControllerPtr controller,
-                           blink::mojom::AppBannerPromptReply reply) override {
+  void OnBannerPromptReply(
+      mojo::Remote<blink::mojom::AppBannerController> controller,
+      blink::mojom::AppBannerPromptReply reply) override {
     AppBannerManager::OnBannerPromptReply(std::move(controller), reply);
     if (on_banner_prompt_reply_) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index bb4b772..a68d42d 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -1458,7 +1458,7 @@
   DLOG(WARNING) << "Shutting down current instance of the browser.";
   chrome::AttemptExit();
 
-  upgrade_util::SetNewCommandLine(new_cl.release());
+  upgrade_util::SetNewCommandLine(std::move(new_cl));
 }
 
 void BrowserProcessImpl::OnAutoupdateTimer() {
diff --git a/chrome/browser/chromeos/assistant/OWNERS b/chrome/browser/chromeos/assistant/OWNERS
index 1f575b1..0c730475 100644
--- a/chrome/browser/chromeos/assistant/OWNERS
+++ b/chrome/browser/chromeos/assistant/OWNERS
@@ -1,3 +1,3 @@
 file://chromeos/assistant/OWNERS
 
-# COMPONENTS: UI>Shell>Assistant
+# COMPONENT: UI>Shell>Assistant
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc
index 8a3f79fa..98433245 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -86,7 +86,8 @@
     OnLaunchFailed(app_id);
     if (browser && browser->window())
       browser->window()->Close();
-    if (result == crostini::CrostiniResult::OFFLINE_WHEN_UPGRADE_REQUIRED) {
+    if (result == crostini::CrostiniResult::OFFLINE_WHEN_UPGRADE_REQUIRED ||
+        result == crostini::CrostiniResult::LOAD_COMPONENT_FAILED) {
       ShowCrostiniUpgradeView(profile, crostini::CrostiniUISurface::kAppList);
     }
     return;
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
index baf06b56..022b913 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
@@ -552,14 +552,17 @@
 
 // FRE not explicitly required and the state keys are missing. Should proceed to
 // normal signin.
-IN_PROC_BROWSER_TEST_F(AutoEnrollmentNoStateKeys, FRENotRequired) {
+// Test is flaky, and crashes occasionally. https://crbug.com/992022
+IN_PROC_BROWSER_TEST_F(AutoEnrollmentNoStateKeys, DISABLED_FRENotRequired) {
   host()->StartWizard(AutoEnrollmentCheckScreenView::kScreenId);
   OobeScreenWaiter(GaiaView::kScreenId).Wait();
 }
 
 // FRE explicitly not required in VPD, so it should not even contact the policy
 // server.
-IN_PROC_BROWSER_TEST_F(AutoEnrollmentWithStatistics, FREExplicitlyNotRequired) {
+// Test is flaky, and crashes occasionally. https://crbug.com/992022
+IN_PROC_BROWSER_TEST_F(AutoEnrollmentWithStatistics,
+                       DISABLED_FREExplicitlyNotRequired) {
   SetFRERequiredKey("0");
 
   // Should be ignored.
@@ -574,7 +577,9 @@
 }
 
 // FRE is not required when VPD is valid and activate date is not there.
-IN_PROC_BROWSER_TEST_F(AutoEnrollmentWithStatistics, MachineNotActivated) {
+// Test is flaky, and crashes occasionally. https://crbug.com/992022
+IN_PROC_BROWSER_TEST_F(AutoEnrollmentWithStatistics,
+                       DISABLED_MachineNotActivated) {
   // Should be ignored.
   EXPECT_TRUE(policy_server_.SetDeviceStateRetrievalResponse(
       state_keys_broker(),
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 6038117b..5db5041f 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -4907,9 +4907,11 @@
       extensions::ChromeExtensionCookies::Get(profile())
           ->GetCookieStoreForTesting();
   ASSERT_TRUE(cookie_store);
-  cookie_store->SetCookieWithOptionsAsync(
-      ext_url, "dummy=value", net::CookieOptions(),
-      base::nullopt /* server_time */,
+  auto cookie =
+      net::CanonicalCookie::Create(ext_url, "dummy=value", base::Time::Now(),
+                                   base::nullopt /* server_time */);
+  cookie_store->SetCanonicalCookieAsync(
+      std::move(cookie), ext_url.scheme(), net::CookieOptions(),
       base::BindOnce(&ExtensionCookieCallback::SetCookieCallback,
                      base::Unretained(&callback)));
   content::RunAllTasksUntilIdle();
diff --git a/chrome/browser/first_run/scoped_relaunch_chrome_browser_override.cc b/chrome/browser/first_run/scoped_relaunch_chrome_browser_override.cc
new file mode 100644
index 0000000..fb46d8a
--- /dev/null
+++ b/chrome/browser/first_run/scoped_relaunch_chrome_browser_override.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.
+
+#include "chrome/browser/first_run/scoped_relaunch_chrome_browser_override.h"
+
+#include <utility>
+
+namespace upgrade_util {
+
+ScopedRelaunchChromeBrowserOverride::ScopedRelaunchChromeBrowserOverride(
+    RelaunchChromeBrowserCallback callback)
+    : previous_(
+          SetRelaunchChromeBrowserCallbackForTesting(std::move(callback))) {}
+
+ScopedRelaunchChromeBrowserOverride::~ScopedRelaunchChromeBrowserOverride() {
+  SetRelaunchChromeBrowserCallbackForTesting(std::move(previous_));
+}
+
+}  // namespace upgrade_util
diff --git a/chrome/browser/first_run/scoped_relaunch_chrome_browser_override.h b/chrome/browser/first_run/scoped_relaunch_chrome_browser_override.h
new file mode 100644
index 0000000..e3cfda6
--- /dev/null
+++ b/chrome/browser/first_run/scoped_relaunch_chrome_browser_override.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_FIRST_RUN_SCOPED_RELAUNCH_CHROME_BROWSER_OVERRIDE_H_
+#define CHROME_BROWSER_FIRST_RUN_SCOPED_RELAUNCH_CHROME_BROWSER_OVERRIDE_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chrome/browser/first_run/upgrade_util.h"
+
+namespace upgrade_util {
+
+// A test helper that overrides RelaunchChromeBrowser with a given callback for
+// the lifetime of an instance. The previous callback (or none) is restored
+// upon deletion.
+class ScopedRelaunchChromeBrowserOverride {
+ public:
+  explicit ScopedRelaunchChromeBrowserOverride(
+      RelaunchChromeBrowserCallback callback);
+  ~ScopedRelaunchChromeBrowserOverride();
+
+ private:
+  RelaunchChromeBrowserCallback previous_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedRelaunchChromeBrowserOverride);
+};
+
+}  // namespace upgrade_util
+
+#endif  // CHROME_BROWSER_FIRST_RUN_SCOPED_RELAUNCH_CHROME_BROWSER_OVERRIDE_H_
diff --git a/chrome/browser/first_run/upgrade_util.cc b/chrome/browser/first_run/upgrade_util.cc
index 7fe942a..68b4dfb 100644
--- a/chrome/browser/first_run/upgrade_util.cc
+++ b/chrome/browser/first_run/upgrade_util.cc
@@ -4,19 +4,44 @@
 
 #include "chrome/browser/first_run/upgrade_util.h"
 
+#include <utility>
+
+#include "base/callback.h"
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "build/build_config.h"
+#include "content/public/browser/browser_thread.h"
 
 namespace {
 
-base::CommandLine* command_line;
+#if !defined(OS_MACOSX)
+base::CommandLine* command_line = nullptr;
+#endif
+
+// A test seam for whole-browser tests to override browser relaunch.
+upgrade_util::RelaunchChromeBrowserCallback*
+    relaunch_chrome_browser_callback_for_testing = nullptr;
 
 }  // namespace
 
 namespace upgrade_util {
 
-void SetNewCommandLine(base::CommandLine* new_command_line) {
-  command_line = new_command_line;
+// Forward-declaration of the platform-specific implementation.
+bool RelaunchChromeBrowserImpl(const base::CommandLine& command_line);
+
+bool RelaunchChromeBrowser(const base::CommandLine& command_line) {
+  if (relaunch_chrome_browser_callback_for_testing)
+    return relaunch_chrome_browser_callback_for_testing->Run(command_line);
+
+  return RelaunchChromeBrowserImpl(command_line);
+}
+
+#if !defined(OS_MACOSX)
+
+void SetNewCommandLine(std::unique_ptr<base::CommandLine> new_command_line) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  delete command_line;
+  command_line = new_command_line.release();
 }
 
 void RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
@@ -27,8 +52,33 @@
       DLOG(WARNING) << "Launched a new instance of the browser.";
     }
     delete command_line;
-    command_line = NULL;
+    command_line = nullptr;
   }
 }
 
+#endif  // !defined(OS_MACOSX)
+
+RelaunchChromeBrowserCallback SetRelaunchChromeBrowserCallbackForTesting(
+    RelaunchChromeBrowserCallback callback) {
+  // Take ownership of the current test callback so it can be returned.
+  RelaunchChromeBrowserCallback previous =
+      relaunch_chrome_browser_callback_for_testing
+          ? std::move(*relaunch_chrome_browser_callback_for_testing)
+          : RelaunchChromeBrowserCallback();
+
+  // Move the caller's callback into the global, alloc'ing or freeing as needed.
+  auto memory = base::WrapUnique(relaunch_chrome_browser_callback_for_testing);
+  if (callback) {
+    if (!memory)
+      memory = std::make_unique<RelaunchChromeBrowserCallback>();
+    *memory = std::move(callback);
+  } else if (memory) {
+    memory.reset();
+  }
+  relaunch_chrome_browser_callback_for_testing = memory.release();
+
+  // Return the previous callback.
+  return previous;
+}
+
 }  // namespace upgrade_util
diff --git a/chrome/browser/first_run/upgrade_util.h b/chrome/browser/first_run/upgrade_util.h
index 93e1c6d..859b9a09 100644
--- a/chrome/browser/first_run/upgrade_util.h
+++ b/chrome/browser/first_run/upgrade_util.h
@@ -5,16 +5,15 @@
 #ifndef CHROME_BROWSER_FIRST_RUN_UPGRADE_UTIL_H_
 #define CHROME_BROWSER_FIRST_RUN_UPGRADE_UTIL_H_
 
+#include <memory>
+
+#include "base/callback_forward.h"
 #include "build/build_config.h"
 
 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
 #error Not used on Android or ChromeOS
 #endif
 
-#if defined(OS_WIN)
-#include <string>
-#endif
-
 namespace base {
 class CommandLine;
 }
@@ -27,10 +26,12 @@
 
 #if !defined(OS_MACOSX)
 
-void SetNewCommandLine(base::CommandLine* new_command_line);
+// Sets a command line to be used to relaunch the browser upon exit.
+void SetNewCommandLine(std::unique_ptr<base::CommandLine> new_command_line);
 
-// Launches a new instance of the browser if the current instance in persistent
-// mode an upgrade is detected.
+// Launches a new instance of the browser using a command line previously
+// provided to SetNewCommandLine. This is typically used to finalize an in-use
+// update that was detected while the browser was in persistent mode.
 void RelaunchChromeBrowserWithNewCommandLineIfNeeded();
 
 // Windows:
@@ -42,6 +43,15 @@
 
 #endif  // !defined(OS_MACOSX)
 
+using RelaunchChromeBrowserCallback =
+    base::RepeatingCallback<bool(const base::CommandLine&)>;
+
+// Sets |callback| to be run to process a RelaunchChromeBrowser request. This
+// is a test seam for whole-browser tests. See
+// ScopedRelaunchChromeBrowserOverride for convenience.
+RelaunchChromeBrowserCallback SetRelaunchChromeBrowserCallbackForTesting(
+    RelaunchChromeBrowserCallback callback);
+
 }  // namespace upgrade_util
 
 #endif  // CHROME_BROWSER_FIRST_RUN_UPGRADE_UTIL_H_
diff --git a/chrome/browser/first_run/upgrade_util_linux.cc b/chrome/browser/first_run/upgrade_util_linux.cc
index 7396dc6..eef5298 100644
--- a/chrome/browser/first_run/upgrade_util_linux.cc
+++ b/chrome/browser/first_run/upgrade_util_linux.cc
@@ -21,7 +21,7 @@
 
 namespace upgrade_util {
 
-bool RelaunchChromeBrowser(const base::CommandLine& command_line) {
+bool RelaunchChromeBrowserImpl(const base::CommandLine& command_line) {
   base::LaunchOptions options;
   // Don't set NO_NEW_PRIVS on the relaunched browser process.
   options.allow_new_privs = true;
diff --git a/chrome/browser/first_run/upgrade_util_mac.mm b/chrome/browser/first_run/upgrade_util_mac.mm
index f24c53e..d08b7f5 100644
--- a/chrome/browser/first_run/upgrade_util_mac.mm
+++ b/chrome/browser/first_run/upgrade_util_mac.mm
@@ -286,7 +286,7 @@
   return !other_instances_exist;
 }
 
-bool RelaunchChromeBrowser(const base::CommandLine& command_line) {
+bool RelaunchChromeBrowserImpl(const base::CommandLine& command_line) {
   upgrade_util::ThisAndOtherUserCounts counts =
       upgrade_util::GetCountOfOtherInstancesOfThisBinary();
   const int other_instances = counts.this_user_count + counts.other_user_count;
diff --git a/chrome/browser/first_run/upgrade_util_win.cc b/chrome/browser/first_run/upgrade_util_win.cc
index 8d77b39..f8fab15 100644
--- a/chrome/browser/first_run/upgrade_util_win.cc
+++ b/chrome/browser/first_run/upgrade_util_win.cc
@@ -95,7 +95,7 @@
 
 namespace upgrade_util {
 
-bool RelaunchChromeBrowser(const base::CommandLine& command_line) {
+bool RelaunchChromeBrowserImpl(const base::CommandLine& command_line) {
   base::FilePath chrome_exe;
   if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
     NOTREACHED();
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index e7d0554..ef3680d 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -922,11 +922,6 @@
     "expiry_milestone": 80
   },
   {
-    "name": "enable-autofill-do-not-upload-save-unsupported-cards",
-    "owners": [ "annelim@google.com", "jsaul@google.com" ],
-    "expiry_milestone": 79
-  },
-  {
     "name": "enable-autofill-import-dynamic-forms",
     "owners": [ "hozhng@google.com", "jiahuiguo@google.com" ],
     "expiry_milestone": 79
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 0db3636..7baa396e 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -472,12 +472,6 @@
     "If enabled, local cards from unsupported networks will not be offered "
     "local card migration.";
 
-const char kEnableAutofillDoNotUploadSaveUnsupportedCardsName[] =
-    "Prevents upload save on cards from unsupported networks";
-const char kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription[] =
-    "If enabled, cards from unsupported networks will not be offered upload "
-    "save, and will instead be offered local save.";
-
 const char kEnableAutofillImportDynamicFormsName[] =
     "Allow credit card import from dynamic forms after entry";
 const char kEnableAutofillImportDynamicFormsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 0b18ad27..78b6e77b 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -293,9 +293,6 @@
 extern const char kEnableAutofillDoNotMigrateUnsupportedLocalCardsName[];
 extern const char kEnableAutofillDoNotMigrateUnsupportedLocalCardsDescription[];
 
-extern const char kEnableAutofillDoNotUploadSaveUnsupportedCardsName[];
-extern const char kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription[];
-
 extern const char kEnableAutofillImportDynamicFormsName[];
 extern const char kEnableAutofillImportDynamicFormsDescription[];
 
diff --git a/chrome/browser/lookalikes/safety_tips/safety_tips.proto b/chrome/browser/lookalikes/safety_tips/safety_tips.proto
index 0105e65..381de90 100644
--- a/chrome/browser/lookalikes/safety_tips/safety_tips.proto
+++ b/chrome/browser/lookalikes/safety_tips/safety_tips.proto
@@ -14,9 +14,9 @@
     BAD_REP = 1;
     YOUNG_DOMAIN = 2;
   }
-  // |url| is a full URL such as
-  // http://example.test/test-path-for-safety-tips/test.html.
-  optional string url = 1;
+  // |pattern| is a full URL, without scheme/username/password/port, such as
+  // example.test/test-path-for-safety-tips/test.html.
+  optional string pattern = 1;
   optional FlagType type = 2;
 }
 
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
index da22be7d..aad1b661 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -13,15 +13,15 @@
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
 #include "base/task_runner_util.h"
+#include "components/optimization_guide/bloom_filter.h"
 #include "components/optimization_guide/hint_cache.h"
 #include "components/optimization_guide/hint_cache_store.h"
-#include "components/optimization_guide/hints_component_info.h"
 #include "components/optimization_guide/hints_component_util.h"
 #include "components/optimization_guide/hints_processing_util.h"
+#include "components/optimization_guide/optimization_filter.h"
 #include "components/optimization_guide/optimization_guide_prefs.h"
 #include "components/optimization_guide/optimization_guide_service.h"
 #include "components/optimization_guide/optimization_guide_switches.h"
-#include "components/optimization_guide/proto/hints.pb.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
@@ -33,27 +33,6 @@
 // will have a newer version than it.
 constexpr char kManualConfigComponentVersion[] = "0.0.0";
 
-std::unique_ptr<optimization_guide::HintUpdateData> ProcessHintsComponent(
-    const optimization_guide::HintsComponentInfo& info,
-    std::unique_ptr<optimization_guide::HintUpdateData> update_data) {
-  optimization_guide::ProcessHintsComponentResult out_result;
-  std::unique_ptr<optimization_guide::proto::Configuration> config =
-      optimization_guide::ProcessHintsComponent(info, &out_result);
-  if (!config) {
-    optimization_guide::RecordProcessHintsComponentResult(out_result);
-    return nullptr;
-  }
-
-  bool did_process_hints = optimization_guide::ProcessHints(
-      config->mutable_hints(), update_data.get());
-  optimization_guide::RecordProcessHintsComponentResult(
-      did_process_hints
-          ? optimization_guide::ProcessHintsComponentResult::kSuccess
-          : optimization_guide::ProcessHintsComponentResult::kProcessedNoHints);
-
-  return update_data;
-}
-
 void MaybeRunUpdateClosure(base::OnceClosure update_closure) {
   if (update_closure)
     std::move(update_closure).Run();
@@ -138,16 +117,6 @@
     return;
   }
 
-  std::unique_ptr<optimization_guide::HintUpdateData> update_data =
-      hint_cache_->MaybeCreateUpdateDataForComponentHints(info.version);
-  if (!update_data) {
-    optimization_guide::RecordProcessHintsComponentResult(
-        optimization_guide::ProcessHintsComponentResult::
-            kSkippedProcessingHints);
-    MaybeRunUpdateClosure(std::move(next_update_closure_));
-    return;
-  }
-
   if (!CanProcessComponentVersion(pref_service_, info.version)) {
     optimization_guide::RecordProcessHintsComponentResult(
         optimization_guide::ProcessHintsComponentResult::
@@ -156,6 +125,9 @@
     return;
   }
 
+  std::unique_ptr<optimization_guide::HintUpdateData> update_data =
+      hint_cache_->MaybeCreateUpdateDataForComponentHints(info.version);
+
   // Processes the hints from the newly available component on a background
   // thread, providing a HintUpdateData for component update from the hint
   // cache, so that each hint within the component can be moved into it. In the
@@ -165,10 +137,107 @@
   // created PreviewsHints, it is initialized in UpdateHints() on the UI thread.
   base::PostTaskAndReplyWithResult(
       background_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&ProcessHintsComponent, info, std::move(update_data)),
+      base::BindOnce(&OptimizationGuideHintsManager::ProcessHintsComponent,
+                     base::Unretained(this), info,
+                     registered_optimization_types_, std::move(update_data)),
       base::BindOnce(&OptimizationGuideHintsManager::UpdateComponentHints,
                      ui_weak_ptr_factory_.GetWeakPtr(),
                      std::move(next_update_closure_)));
+
+  // Only replace hints component info if it is not the same - otherwise we will
+  // destruct the object and it will be invalid later.
+  if (!hints_component_info_ ||
+      hints_component_info_->version.CompareTo(info.version) != 0) {
+    hints_component_info_.emplace(info.version, info.path);
+  }
+}
+
+std::unique_ptr<optimization_guide::HintUpdateData>
+OptimizationGuideHintsManager::ProcessHintsComponent(
+    const optimization_guide::HintsComponentInfo& info,
+    const std::unordered_set<optimization_guide::proto::OptimizationType>&
+        registered_optimization_types,
+    std::unique_ptr<optimization_guide::HintUpdateData> update_data) {
+  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+
+  optimization_guide::ProcessHintsComponentResult out_result;
+  std::unique_ptr<optimization_guide::proto::Configuration> config =
+      optimization_guide::ProcessHintsComponent(info, &out_result);
+  if (!config) {
+    optimization_guide::RecordProcessHintsComponentResult(out_result);
+    return nullptr;
+  }
+
+  ProcessOptimizationFilters(config->optimization_blacklists(),
+                             registered_optimization_types);
+
+  if (update_data) {
+    bool did_process_hints = optimization_guide::ProcessHints(
+        config->mutable_hints(), update_data.get());
+    optimization_guide::RecordProcessHintsComponentResult(
+        did_process_hints
+            ? optimization_guide::ProcessHintsComponentResult::kSuccess
+            : optimization_guide::ProcessHintsComponentResult::
+                  kProcessedNoHints);
+  } else {
+    optimization_guide::RecordProcessHintsComponentResult(
+        optimization_guide::ProcessHintsComponentResult::
+            kSkippedProcessingHints);
+  }
+
+  return update_data;
+}
+
+void OptimizationGuideHintsManager::ProcessOptimizationFilters(
+    const google::protobuf::RepeatedPtrField<
+        optimization_guide::proto::OptimizationFilter>&
+        blacklist_optimization_filters,
+    const std::unordered_set<optimization_guide::proto::OptimizationType>&
+        registered_optimization_types) {
+  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+  base::AutoLock lock(optimization_filters_lock_);
+
+  available_optimization_filters_.clear();
+  blacklist_optimization_filters_.clear();
+  for (const auto& filter : blacklist_optimization_filters) {
+    if (filter.optimization_type() !=
+        optimization_guide::proto::TYPE_UNSPECIFIED) {
+      available_optimization_filters_.insert(filter.optimization_type());
+    }
+
+    // Do not put anything in memory that we don't have registered.
+    if (registered_optimization_types.find(filter.optimization_type()) ==
+        registered_optimization_types.end()) {
+      continue;
+    }
+
+    optimization_guide::RecordOptimizationFilterStatus(
+        filter.optimization_type(),
+        optimization_guide::OptimizationFilterStatus::
+            kFoundServerBlacklistConfig);
+
+    // Do not parse duplicate optimization filters.
+    if (blacklist_optimization_filters_.find(filter.optimization_type()) !=
+        blacklist_optimization_filters_.end()) {
+      optimization_guide::RecordOptimizationFilterStatus(
+          filter.optimization_type(),
+          optimization_guide::OptimizationFilterStatus::
+              kFailedServerBlacklistDuplicateConfig);
+      continue;
+    }
+
+    // Parse optimization filter.
+    optimization_guide::OptimizationFilterStatus status;
+    std::unique_ptr<optimization_guide::OptimizationFilter>
+        optimization_filter =
+            optimization_guide::ProcessOptimizationFilter(filter, &status);
+    if (optimization_filter) {
+      blacklist_optimization_filters_.insert(
+          {filter.optimization_type(), std::move(optimization_filter)});
+    }
+    optimization_guide::RecordOptimizationFilterStatus(
+        filter.optimization_type(), status);
+  }
 }
 
 void OptimizationGuideHintsManager::OnHintCacheInitialized() {
@@ -266,3 +335,43 @@
   // tests.
   std::move(callback).Run();
 }
+
+void OptimizationGuideHintsManager::RegisterOptimizationTypes(
+    std::vector<optimization_guide::proto::OptimizationType>
+        optimization_types) {
+  bool should_load_new_optimization_filter = false;
+  for (const auto optimization_type : optimization_types) {
+    if (optimization_type == optimization_guide::proto::TYPE_UNSPECIFIED)
+      continue;
+
+    if (registered_optimization_types_.find(optimization_type) !=
+        registered_optimization_types_.end()) {
+      continue;
+    }
+    registered_optimization_types_.insert(optimization_type);
+
+    if (!should_load_new_optimization_filter) {
+      base::AutoLock lock(optimization_filters_lock_);
+      if (available_optimization_filters_.find(optimization_type) !=
+          available_optimization_filters_.end()) {
+        should_load_new_optimization_filter = true;
+      }
+    }
+  }
+
+  if (should_load_new_optimization_filter) {
+    DCHECK(hints_component_info_);
+
+    OnHintsComponentAvailable(*hints_component_info_);
+  } else {
+    MaybeRunUpdateClosure(std::move(next_update_closure_));
+  }
+}
+
+bool OptimizationGuideHintsManager::HasLoadedOptimizationFilter(
+    optimization_guide::proto::OptimizationType optimization_type) {
+  base::AutoLock lock(optimization_filters_lock_);
+
+  return blacklist_optimization_filters_.find(optimization_type) !=
+         blacklist_optimization_filters_.end();
+}
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
index 70844473..bdad43b 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
@@ -6,11 +6,18 @@
 #define CHROME_BROWSER_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_HINTS_MANAGER_H_
 
 #include <memory>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
 
 #include "base/callback_forward.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/sequenced_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "components/optimization_guide/hints_component_info.h"
 #include "components/optimization_guide/optimization_guide_service_observer.h"
+#include "components/optimization_guide/proto/hints.pb.h"
 
 namespace base {
 class FilePath;
@@ -25,12 +32,9 @@
 }  // namespace leveldb_proto
 
 namespace optimization_guide {
-namespace proto {
-class Hint;
-}  // namespace proto
 class HintCache;
 class HintUpdateData;
-struct HintsComponentInfo;
+class OptimizationFilter;
 class OptimizationGuideService;
 }  // namespace optimization_guide
 
@@ -65,7 +69,44 @@
   void LoadHintForNavigation(content::NavigationHandle* navigation_handle,
                              base::OnceClosure callback);
 
+  // Registers the optimization types that have the potential for hints to be
+  // called by consumers of the Optimization Guide.
+  void RegisterOptimizationTypes(
+      std::vector<optimization_guide::proto::OptimizationType>
+          optimization_types);
+
+  // Returns whether there have been any optimization types registered.
+  bool HasRegisteredOptimizationTypes() const {
+    return !registered_optimization_types_.empty();
+  }
+
+  // Returns whether there is an optimization filter loaded for
+  // |optimization_type|.
+  bool HasLoadedOptimizationFilter(
+      optimization_guide::proto::OptimizationType optimization_type);
+
  private:
+  // Processes the hints component.
+  //
+  // Should always be called on the thread that belongs to
+  // |background_task_runner_|.
+  std::unique_ptr<optimization_guide::HintUpdateData> ProcessHintsComponent(
+      const optimization_guide::HintsComponentInfo& info,
+      const std::unordered_set<optimization_guide::proto::OptimizationType>&
+          registered_optimization_types,
+      std::unique_ptr<optimization_guide::HintUpdateData> update_data);
+
+  // Processes the optimization filters contained in the hints component.
+  //
+  // Should always be called on the thread that belongs to
+  // |background_task_runner_|.
+  void ProcessOptimizationFilters(
+      const google::protobuf::RepeatedPtrField<
+          optimization_guide::proto::OptimizationFilter>&
+          blacklist_optimization_filters,
+      const std::unordered_set<optimization_guide::proto::OptimizationType>&
+          registered_optimization_types);
+
   // Callback run after the hint cache is fully initialized. At this point, the
   // OptimizationGuideHintsManager is ready to process hints.
   void OnHintCacheInitialized();
@@ -88,6 +129,31 @@
   optimization_guide::OptimizationGuideService* const
       optimization_guide_service_;
 
+  // The information of the latest component delivered by
+  // |optimization_guide_service_|.
+  base::Optional<optimization_guide::HintsComponentInfo> hints_component_info_;
+
+  // The set of optimization types that have been registered with the hints
+  // manager.
+  //
+  // Should only be read and modified on the UI thread.
+  std::unordered_set<optimization_guide::proto::OptimizationType>
+      registered_optimization_types_;
+
+  // Synchronizes access to member variables related to optimization filters.
+  base::Lock optimization_filters_lock_;
+
+  // The set of optimization types that the component specified by
+  // |component_info_| has optimization filters for.
+  std::unordered_set<optimization_guide::proto::OptimizationType>
+      available_optimization_filters_ GUARDED_BY(optimization_filters_lock_);
+
+  // A map from optimization type to the host filter that holds the blacklist
+  // for that type.
+  std::unordered_map<optimization_guide::proto::OptimizationType,
+                     std::unique_ptr<optimization_guide::OptimizationFilter>>
+      blacklist_optimization_filters_ GUARDED_BY(optimization_filters_lock_);
+
   // Background thread where hints processing should be performed.
   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
 
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
index bea4ff1..ca2c3af 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -10,16 +10,49 @@
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "components/optimization_guide/bloom_filter.h"
 #include "components/optimization_guide/hints_component_util.h"
+#include "components/optimization_guide/optimization_guide_features.h"
 #include "components/optimization_guide/optimization_guide_prefs.h"
 #include "components/optimization_guide/optimization_guide_service.h"
 #include "components/optimization_guide/optimization_guide_switches.h"
-#include "components/optimization_guide/proto/hints.pb.h"
 #include "components/optimization_guide/proto_database_provider_test_base.h"
 #include "components/prefs/testing_pref_service.h"
 #include "content/public/test/mock_navigation_handle.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 
+namespace {
+
+const int kBlackBlacklistBloomFilterNumHashFunctions = 7;
+const int kBlackBlacklistBloomFilterNumBits = 511;
+
+void PopulateBlackBlacklistBloomFilter(
+    optimization_guide::BloomFilter* bloom_filter) {
+  bloom_filter->Add("black.com");
+}
+
+void AddBlacklistBloomFilterToConfig(
+    optimization_guide::proto::OptimizationType optimization_type,
+    const optimization_guide::BloomFilter& blacklist_bloom_filter,
+    int num_hash_functions,
+    int num_bits,
+    optimization_guide::proto::Configuration* config) {
+  std::string blacklist_data(
+      reinterpret_cast<const char*>(&blacklist_bloom_filter.bytes()[0]),
+      blacklist_bloom_filter.bytes().size());
+  optimization_guide::proto::OptimizationFilter* blacklist_proto =
+      config->add_optimization_blacklists();
+  blacklist_proto->set_optimization_type(optimization_type);
+  std::unique_ptr<optimization_guide::proto::BloomFilter> bloom_filter_proto =
+      std::make_unique<optimization_guide::proto::BloomFilter>();
+  bloom_filter_proto->set_num_hash_functions(num_hash_functions);
+  bloom_filter_proto->set_num_bits(num_bits);
+  bloom_filter_proto->set_data(blacklist_data);
+  blacklist_proto->set_allocated_bloom_filter(bloom_filter_proto.release());
+}
+
+}  // namespace
+
 class TestOptimizationGuideService
     : public optimization_guide::OptimizationGuideService {
  public:
@@ -477,3 +510,194 @@
 
   histogram_tester.ExpectTotalCount("OptimizationGuide.LoadedHint.Result", 0);
 }
+
+TEST_F(OptimizationGuideHintsManagerTest,
+       OptimizationFiltersAreOnlyLoadedIfTypeIsRegistered) {
+  optimization_guide::proto::Configuration config;
+  optimization_guide::BloomFilter blacklist_bloom_filter(
+      kBlackBlacklistBloomFilterNumHashFunctions,
+      kBlackBlacklistBloomFilterNumBits);
+  PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
+  AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
+                                  blacklist_bloom_filter,
+                                  kBlackBlacklistBloomFilterNumHashFunctions,
+                                  kBlackBlacklistBloomFilterNumBits, &config);
+  AddBlacklistBloomFilterToConfig(optimization_guide::proto::NOSCRIPT,
+                                  blacklist_bloom_filter,
+                                  kBlackBlacklistBloomFilterNumHashFunctions,
+                                  kBlackBlacklistBloomFilterNumBits, &config);
+
+  {
+    base::HistogramTester histogram_tester;
+
+    ProcessHints(config, "1.0.0.0");
+
+    histogram_tester.ExpectTotalCount(
+        "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect", 0);
+    histogram_tester.ExpectTotalCount(
+        "OptimizationGuide.OptimizationFilterStatus.NoScript", 0);
+  }
+
+  // Now register the optimization type and see that it is loaded.
+  {
+    base::HistogramTester histogram_tester;
+
+    base::RunLoop run_loop;
+    hints_manager()->ListenForNextUpdateForTesting(run_loop.QuitClosure());
+    hints_manager()->RegisterOptimizationTypes(
+        {optimization_guide::proto::LITE_PAGE_REDIRECT});
+    run_loop.Run();
+
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
+        optimization_guide::OptimizationFilterStatus::
+            kFoundServerBlacklistConfig,
+        1);
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
+        optimization_guide::OptimizationFilterStatus::kCreatedServerBlacklist,
+        1);
+    histogram_tester.ExpectTotalCount(
+        "OptimizationGuide.OptimizationFilterStatus.NoScript", 0);
+    EXPECT_TRUE(hints_manager()->HasLoadedOptimizationFilter(
+        optimization_guide::proto::LITE_PAGE_REDIRECT));
+    EXPECT_FALSE(hints_manager()->HasLoadedOptimizationFilter(
+        optimization_guide::proto::NOSCRIPT));
+  }
+
+  // Re-registering the same optimization type does not re-load the filter.
+  {
+    base::HistogramTester histogram_tester;
+
+    base::RunLoop run_loop;
+    hints_manager()->ListenForNextUpdateForTesting(run_loop.QuitClosure());
+    hints_manager()->RegisterOptimizationTypes(
+        {optimization_guide::proto::LITE_PAGE_REDIRECT});
+    run_loop.Run();
+
+    histogram_tester.ExpectTotalCount(
+        "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect", 0);
+    histogram_tester.ExpectTotalCount(
+        "OptimizationGuide.OptimizationFilterStatus.NoScript", 0);
+  }
+
+  // Registering a new optimization type without a filter does not trigger a
+  // reload of the filter.
+  {
+    base::HistogramTester histogram_tester;
+
+    base::RunLoop run_loop;
+    hints_manager()->ListenForNextUpdateForTesting(run_loop.QuitClosure());
+    hints_manager()->RegisterOptimizationTypes(
+        {optimization_guide::proto::DEFER_ALL_SCRIPT});
+    run_loop.Run();
+
+    histogram_tester.ExpectTotalCount(
+        "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect", 0);
+    histogram_tester.ExpectTotalCount(
+        "OptimizationGuide.OptimizationFilterStatus.NoScript", 0);
+  }
+
+  // Registering a new optimization type with a filter does trigger a
+  // reload of the filters.
+  {
+    base::HistogramTester histogram_tester;
+
+    base::RunLoop run_loop;
+    hints_manager()->ListenForNextUpdateForTesting(run_loop.QuitClosure());
+    hints_manager()->RegisterOptimizationTypes(
+        {optimization_guide::proto::NOSCRIPT});
+    run_loop.Run();
+
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
+        optimization_guide::OptimizationFilterStatus::
+            kFoundServerBlacklistConfig,
+        1);
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
+        optimization_guide::OptimizationFilterStatus::kCreatedServerBlacklist,
+        1);
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.OptimizationFilterStatus.NoScript",
+        optimization_guide::OptimizationFilterStatus::
+            kFoundServerBlacklistConfig,
+        1);
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.OptimizationFilterStatus.NoScript",
+        optimization_guide::OptimizationFilterStatus::kCreatedServerBlacklist,
+        1);
+    EXPECT_TRUE(hints_manager()->HasLoadedOptimizationFilter(
+        optimization_guide::proto::LITE_PAGE_REDIRECT));
+    EXPECT_TRUE(hints_manager()->HasLoadedOptimizationFilter(
+        optimization_guide::proto::NOSCRIPT));
+  }
+}
+
+TEST_F(OptimizationGuideHintsManagerTest,
+       OptimizationFiltersOnlyLoadOncePerType) {
+  hints_manager()->RegisterOptimizationTypes(
+      {optimization_guide::proto::LITE_PAGE_REDIRECT});
+
+  base::HistogramTester histogram_tester;
+
+  optimization_guide::proto::Configuration config;
+  optimization_guide::BloomFilter blacklist_bloom_filter(
+      kBlackBlacklistBloomFilterNumHashFunctions,
+      kBlackBlacklistBloomFilterNumBits);
+  PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
+  AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
+                                  blacklist_bloom_filter,
+                                  kBlackBlacklistBloomFilterNumHashFunctions,
+                                  kBlackBlacklistBloomFilterNumBits, &config);
+  AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
+                                  blacklist_bloom_filter,
+                                  kBlackBlacklistBloomFilterNumHashFunctions,
+                                  kBlackBlacklistBloomFilterNumBits, &config);
+  ProcessHints(config, "1.0.0.0");
+
+  // We found 2 LPR blacklists: parsed one and duped the other.
+  histogram_tester.ExpectBucketCount(
+      "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
+      optimization_guide::OptimizationFilterStatus::kFoundServerBlacklistConfig,
+      2);
+  histogram_tester.ExpectBucketCount(
+      "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
+      optimization_guide::OptimizationFilterStatus::kCreatedServerBlacklist, 1);
+  histogram_tester.ExpectBucketCount(
+      "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
+      optimization_guide::OptimizationFilterStatus::
+          kFailedServerBlacklistDuplicateConfig,
+      1);
+}
+
+TEST_F(OptimizationGuideHintsManagerTest, InvalidOptimizationFilterNotLoaded) {
+  hints_manager()->RegisterOptimizationTypes(
+      {optimization_guide::proto::LITE_PAGE_REDIRECT});
+
+  base::HistogramTester histogram_tester;
+
+  int too_many_bits =
+      optimization_guide::features::MaxServerBloomFilterByteSize() * 8 + 1;
+
+  optimization_guide::proto::Configuration config;
+  optimization_guide::BloomFilter blacklist_bloom_filter(
+      kBlackBlacklistBloomFilterNumHashFunctions, too_many_bits);
+  PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
+  AddBlacklistBloomFilterToConfig(
+      optimization_guide::proto::LITE_PAGE_REDIRECT, blacklist_bloom_filter,
+      kBlackBlacklistBloomFilterNumHashFunctions, too_many_bits, &config);
+  ProcessHints(config, "1.0.0.0");
+
+  histogram_tester.ExpectBucketCount(
+      "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
+      optimization_guide::OptimizationFilterStatus::kFoundServerBlacklistConfig,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
+      optimization_guide::OptimizationFilterStatus::
+          kFailedServerBlacklistTooBig,
+      1);
+  EXPECT_FALSE(hints_manager()->HasLoadedOptimizationFilter(
+      optimization_guide::proto::LITE_PAGE_REDIRECT));
+}
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index 3e49c63c..e15509df 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -41,17 +41,15 @@
 void OptimizationGuideKeyedService::RegisterOptimizationTypes(
     std::vector<optimization_guide::proto::OptimizationType>
         optimization_types) {
-  for (const auto optimization_type : optimization_types) {
-    registered_optimization_types_.insert(optimization_type);
-  }
+  DCHECK(hints_manager_);
+
+  hints_manager_->RegisterOptimizationTypes(optimization_types);
 }
 
 void OptimizationGuideKeyedService::MaybeLoadHintForNavigation(
     content::NavigationHandle* navigation_handle) {
-  if (!hints_manager_ || registered_optimization_types_.empty())
-    return;
-
-  hints_manager_->LoadHintForNavigation(navigation_handle, base::DoNothing());
+  if (hints_manager_ && hints_manager_->HasRegisteredOptimizationTypes())
+    hints_manager_->LoadHintForNavigation(navigation_handle, base::DoNothing());
 }
 
 void OptimizationGuideKeyedService::Shutdown() {
diff --git a/chrome/browser/previews/previews_content_util.cc b/chrome/browser/previews/previews_content_util.cc
index 6fc242e0..700be43 100644
--- a/chrome/browser/previews/previews_content_util.cc
+++ b/chrome/browser/previews/previews_content_util.cc
@@ -154,7 +154,6 @@
 
   // This should always be at the end, but before the control group check.
   if (decider->NeedsToNotifyUser()) {
-    decider->NotifyUser(web_contents);
     PreviewsLitePageNavigationThrottle::LogIneligibleReason(
         PreviewsLitePageNavigationThrottle::IneligibleReason::kInfoBarNotSeen);
     return false;
diff --git a/chrome/browser/previews/previews_ui_tab_helper.cc b/chrome/browser/previews/previews_ui_tab_helper.cc
index d411ef0..2c738c8 100644
--- a/chrome/browser/previews/previews_ui_tab_helper.cc
+++ b/chrome/browser/previews/previews_ui_tab_helper.cc
@@ -26,7 +26,6 @@
 #include "components/network_time/network_time_tracker.h"
 #include "components/offline_pages/buildflags/buildflags.h"
 #include "components/offline_pages/core/offline_page_item.h"
-#include "components/optimization_guide/optimization_guide_features.h"
 #include "components/previews/content/previews_decider_impl.h"
 #include "components/previews/content/previews_ui_service.h"
 #include "components/previews/core/previews_experiments.h"
@@ -251,9 +250,9 @@
   }
 }
 
-void PreviewsUITabHelper::MaybeShowInfoBarForHintsFetcher() {
-  if (!base::FeatureList::IsEnabled(
-          optimization_guide::features::kOptimizationHintsFetching))
+void PreviewsUITabHelper::MaybeShowInfoBar(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->GetURL().SchemeIsHTTPOrHTTPS())
     return;
 
   PreviewsService* previews_service = PreviewsServiceFactory::GetForProfile(
@@ -287,7 +286,7 @@
 
   DataSaverTopHostProvider::MaybeUpdateTopHostBlacklist(navigation_handle);
 
-  MaybeShowInfoBarForHintsFetcher();
+  MaybeShowInfoBar(navigation_handle);
 }
 
 void PreviewsUITabHelper::DidFinishNavigation(
diff --git a/chrome/browser/previews/previews_ui_tab_helper.h b/chrome/browser/previews/previews_ui_tab_helper.h
index b1a1bb9..3507d72 100644
--- a/chrome/browser/previews/previews_ui_tab_helper.h
+++ b/chrome/browser/previews/previews_ui_tab_helper.h
@@ -136,8 +136,8 @@
   void MaybeRecordPreviewReload(content::NavigationHandle* navigation_handle);
 
   // Show the user the Infobar if they need to be to notified that Lite mode
-  // now optimizes HTTPS pages when HintsFetcher is enabled.
-  void MaybeShowInfoBarForHintsFetcher();
+  // now optimizes HTTPS pages.
+  void MaybeShowInfoBar(content::NavigationHandle* navigation_handle);
 
   // True if the UI for a preview has been shown for the page.
   bool displayed_preview_ui_ = false;
diff --git a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
index c8541a8..5f92fda 100644
--- a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
+++ b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
@@ -33,7 +33,6 @@
 #include "components/offline_pages/buildflags/buildflags.h"
 #include "components/offline_pages/core/offline_page_item.h"
 #include "components/offline_pages/core/request_header/offline_page_header.h"
-#include "components/optimization_guide/optimization_guide_features.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/previews/content/previews_user_data.h"
 #include "components/previews/core/previews_features.h"
@@ -376,11 +375,7 @@
   EXPECT_TRUE(transition_type & ui::PAGE_TRANSITION_RELOAD);
 }
 
-TEST_F(PreviewsUITabHelperUnitTest, TestInfoBarShownOnHintsFetcherEnabled) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {optimization_guide::features::kOptimizationHintsFetching}, {});
-
+TEST_F(PreviewsUITabHelperUnitTest, TestInfoBarShownOnlyWhenNotSeen) {
   data_reduction_proxy::DataReductionProxySettings::
       SetDataSaverEnabledForTesting(drp_test_context_->pref_service(), true);
 
@@ -396,20 +391,3 @@
   content::WebContentsTester::For(web_contents())->NavigateAndCommit(test_url);
   EXPECT_FALSE(decider->NeedsToNotifyUser());
 }
-
-TEST_F(PreviewsUITabHelperUnitTest, TestInfoBarNotShownOnHintsFetcherDisabled) {
-  data_reduction_proxy::DataReductionProxySettings::
-      SetDataSaverEnabledForTesting(drp_test_context_->pref_service(), true);
-
-  PreviewsService* previews_service = PreviewsServiceFactory::GetForProfile(
-      Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
-
-  PreviewsHTTPSNotificationInfoBarDecider* decider =
-      previews_service->previews_https_notification_infobar_decider();
-
-  EXPECT_TRUE(decider->NeedsToNotifyUser());
-
-  GURL test_url("https://tribbles.com");
-  content::WebContentsTester::For(web_contents())->NavigateAndCommit(test_url);
-  EXPECT_TRUE(decider->NeedsToNotifyUser());
-}
diff --git a/chrome/browser/reputation/OWNERS b/chrome/browser/reputation/OWNERS
new file mode 100644
index 0000000..de6c3759
--- /dev/null
+++ b/chrome/browser/reputation/OWNERS
@@ -0,0 +1,3 @@
+jdeblasio@chromium.org
+estark@chromium.org
+meacer@chromium.org
diff --git a/chrome/browser/reputation/README b/chrome/browser/reputation/README
new file mode 100644
index 0000000..4323e6a
--- /dev/null
+++ b/chrome/browser/reputation/README
@@ -0,0 +1,9 @@
+This directory stores files related to Chrome's reputation warnings.
+
+If Chrome believes a site a user is visiting is low reputation, either due to
+failing heuristics or models in Chrome, or as determined server-side, Chrome may
+warn users about this state. This warning may either be a full-page interstitial
+warning or a pop-up while visiting the page.
+
+This warning will not trigger on sites that are not likely to be actively
+malicious, such as popular sites or sites with high engagement scores.
diff --git a/chrome/browser/resources/chromeos/network_ui/network_ui.js b/chrome/browser/resources/chromeos/network_ui/network_ui.js
index 3224232..00c3116 100644
--- a/chrome/browser/resources/chromeos/network_ui/network_ui.js
+++ b/chrome/browser/resources/chromeos/network_ui/network_ui.js
@@ -64,9 +64,9 @@
    * This UI will use both the networkingPrivate extension API and the
    * networkConfig mojo API until we provide all of the required functionality
    * in networkConfig. TODO(stevenjb): Remove use of networkingPrivate api.
-   * @type {?mojom.CrosNetworkConfigProxy}
+   * @type {?mojom.CrosNetworkConfigRemote}
    */
-  let networkConfigProxy = null;
+  let networkConfig = null;
 
   /**
    * Creates and returns a typed HTMLTableCellElement.
@@ -326,7 +326,7 @@
     if (selectedId == 'shill') {
       chrome.send('getShillNetworkProperties', [guid]);
     } else if (selectedId == 'state') {
-      networkConfigProxy.getNetworkState(guid)
+      networkConfig.getNetworkState(guid)
           .then((responseParams) => {
             if (responseParams && responseParams.result) {
               showDetail(detailCell, responseParams.result);
@@ -339,7 +339,7 @@
             showDetailError(detailCell, 'Mojo service failure: ' + error);
           });
     } else if (selectedId == 'managed') {
-      networkConfigProxy.getManagedProperties(guid)
+      networkConfig.getManagedProperties(guid)
           .then((responseParams) => {
             if (responseParams && responseParams.result) {
               showDetail(detailCell, responseParams.result);
@@ -475,7 +475,7 @@
    * Requests an update of all network info.
    */
   const requestNetworks = function() {
-    networkConfigProxy
+    networkConfig
         .getNetworkStateList({
           filter: mojom.FilterType.kVisible,
           networkType: mojom.NetworkType.kAll,
@@ -485,7 +485,7 @@
           onVisibleNetworksReceived(responseParams.result);
         });
 
-    networkConfigProxy
+    networkConfig
         .getNetworkStateList({
           filter: mojom.FilterType.kConfigured,
           networkType: mojom.NetworkType.kAll,
@@ -495,7 +495,7 @@
           onFavoriteNetworksReceived(responseParams.result);
         });
 
-    networkConfigProxy.getDeviceStateList().then((responseParams) => {
+    networkConfig.getDeviceStateList().then((responseParams) => {
       onDeviceStatesReceived(responseParams.result);
     });
   };
@@ -512,8 +512,8 @@
 
   /** Initialize NetworkUI state. */
   const init = function() {
-    networkConfigProxy = network_config.MojoInterfaceProviderImpl.getInstance()
-                             .getMojoServiceProxy();
+    networkConfig = network_config.MojoInterfaceProviderImpl.getInstance()
+                        .getMojoServiceRemote();
 
     /** Set the refresh rate if the interval is found in the url. */
     const interval = new URL(window.location.href).searchParams.get('refresh');
diff --git a/chrome/browser/resources/local_ntp/customize.js b/chrome/browser/resources/local_ntp/customize.js
index 920fb6c..019c0c9f3 100644
--- a/chrome/browser/resources/local_ntp/customize.js
+++ b/chrome/browser/resources/local_ntp/customize.js
@@ -619,6 +619,7 @@
 
   // Accessibility support for screen readers.
   tileBackground.setAttribute('role', 'button');
+  tileBackground.setAttribute('aria-pressed', false);
 
   tileBackground.onclick = onClickInteraction;
   tileBackground.onkeydown = onKeyInteraction;
@@ -1209,7 +1210,7 @@
       tileId = 'coll_' + collIndex + '_' + tileId;
     }
     const tile = customize.createTileThumbnail(
-        tileId, collImg[i].imageUrl, dataset, tileOnClickInteraction,
+        tileId, collImg[i].thumbnailImageUrl, dataset, tileOnClickInteraction,
         customize.tileOnKeyDownInteraction);
 
     tile.setAttribute('aria-label', collImg[i].attributions[0]);
@@ -2238,8 +2239,8 @@
         customize.tileOnKeyDownInteraction);
     const label = configData.translatedStrings.colorLabelPrefix + ' ' +
         colorArrayToHex(colorsColl[i].color);
-    tile.setAttribute('aria-label', label);
-    tile.setAttribute('title', label);
+    tile.firstElementChild.setAttribute('aria-label', label);
+    tile.firstElementChild.setAttribute('title', label);
 
     $(customize.IDS.COLORS_MENU).appendChild(tile);
   }
diff --git a/chrome/browser/resources/local_ntp/local_ntp.html b/chrome/browser/resources/local_ntp/local_ntp.html
index beff1e8..47e2dbb 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.html
+++ b/chrome/browser/resources/local_ntp/local_ntp.html
@@ -230,7 +230,7 @@
         <div id="backgrounds-upload-wrapper" class="bg-sel-tile-wrapper">
           <div id="backgrounds-upload" class="bg-sel-tile-bg" tabindex="-1"
               role="button" aria-label="$i18n{uploadImage}"
-              title="$i18n{uploadImage}">
+              title="$i18n{uploadImage}" aria-pressed="false">
             <div id="backgrounds-upload-icon" class="bg-sel-tile">
               <div id="backgrounds-upload-arrow"></div>
               <div id="backgrounds-upload-text">$i18n{uploadImage}</div>
@@ -240,7 +240,7 @@
         <div id="backgrounds-default-wrapper" class="bg-sel-tile-wrapper">
           <div id="backgrounds-default" class="bg-sel-tile-bg" tabindex="-1"
               role="button" aria-label="$i18n{noBackground}"
-              title="$i18n{noBackground}">
+              title="$i18n{noBackground}" aria-pressed="false">
             <div id="backgrounds-default-icon" class="bg-sel-tile">
               <div class="mini-page">
                 <div class="mini-header-colorful"></div>
@@ -327,7 +327,8 @@
         <div id="color-picker-wrapper" class="bg-sel-tile-wrapper">
           <div id="color-picker-container" class="bg-sel-tile-bg"
               aria-label="$i18n{colorPickerLabel}"
-              title="$i18n{colorPickerLabel}"  tabindex="-1">
+              title="$i18n{colorPickerLabel}"  tabindex="-1"
+              role="button" aria-pressed="false">
             <div id="color-picker-tile" class="bg-sel-tile">
               <div id="left-semicircle"></div>
               <div id="color-picker-icon"></div>
@@ -339,7 +340,8 @@
         <div id="colors-default-wrapper" class="bg-sel-tile-wrapper">
           <div id="colors-default" class="bg-sel-tile-bg"
               aria-label="$i18n{defaultThemeLabel}"
-              title="$i18n{defaultThemeLabel}" tabindex="-1">
+              title="$i18n{defaultThemeLabel}" tabindex="-1"
+              role="button" aria-pressed="false">
             <div id="colors-default-icon" class="bg-sel-tile"></div>
           </div>
         </div>
diff --git a/chrome/browser/resources/safety_tips/PRESUBMIT.py b/chrome/browser/resources/safety_tips/PRESUBMIT.py
index 766fec6..012844a2 100644
--- a/chrome/browser/resources/safety_tips/PRESUBMIT.py
+++ b/chrome/browser/resources/safety_tips/PRESUBMIT.py
@@ -30,6 +30,12 @@
       'Do not check in the full safety_tips.asciipb proto. '
       'Only increment |version_id|.')]
 
+  # This proto should not contain any actually-flagged pages. ContainsLine
+  # checks if any line *starts with* the given string.
+  if ContainsLine(contents, 'flagged_page:'):
+    return [output_api.PresubmitError(
+        'Remove entries from Chromium safety_tips.asciipb before submitting.')]
+
   # It's enticing to do something fancy like checking whether the ID was in fact
   # incremented or whether this is a whitespace-only or comment-only change.
   # However, currently deleted lines don't show up in ChangedContents() and
diff --git a/chrome/browser/resources/safety_tips/README.md b/chrome/browser/resources/safety_tips/README.md
index 7fc21e08..b02c20d 100644
--- a/chrome/browser/resources/safety_tips/README.md
+++ b/chrome/browser/resources/safety_tips/README.md
@@ -1,29 +1 @@
-### Update Instructions for Safety Tips Component
-
-Safety Tips component pushes binary protos to clients via Chrome's component
-updater. The proto files are stored in a Google Cloud Storage bucket
-(`gs://chrome-components-safety-tips`) under versioned directories
-(e.g. `gs://chrome-components-safety-tips/5/all` for version `5`, `all` for all
-platforms).
-
-Follow these instructions to write binary protos under versioned directories:
-
- 1. Overwrite `safety_tips.asciipb` with the data. Don't forget to increment
- `version_id`.
- 2. Build the binary proto: `ninja -C out/Release`. This will write the binary
-proto at `out/Release/gen/chrome/browser/resources/safety_tips/safety_tips.pb`.
- 3. Push the binary proto to cloud storage:
- `chrome/browser/resources/safety_tips/push_proto.py -d out/Release`
- 4. Revert `safety_tips.asciipb`, update `version_id` only for future reference
- and check it in.
- 5. In a day or two, navigate to chrome://components on Canary and check that
- the version of the "Safety Tips" component is updated (you might need to press
- the "Check for update" button).
-
- **Do not check in the full proto**.
-
-#### In case of emergency
-
-If the Safety Tips component needs to be disabled immediately, increment the
-version number and push an empty proto.
-
+See go/safety-tips-component-update for Safety Tips component update playbook.
diff --git a/chrome/browser/resources/safety_tips/gen_safety_tips_proto.py b/chrome/browser/resources/safety_tips/gen_safety_tips_proto.py
index 42f3e88..e4ec99a 100755
--- a/chrome/browser/resources/safety_tips/gen_safety_tips_proto.py
+++ b/chrome/browser/resources/safety_tips/gen_safety_tips_proto.py
@@ -43,13 +43,13 @@
 
   def ValidatePb(self, opts, pb):
     assert pb.version_id > 0
-    assert len(pb.flagged_page) > 0
     for flagged_page in pb.flagged_page:
-      assert flagged_page.url
+      assert flagged_page.pattern
       assert flagged_page.type != safety_tips_pb2.FlaggedPage.UNKNOWN
 
-    flagged_urls = [flagged_page.url for flagged_page in pb.flagged_page]
-    assert sorted(flagged_urls) == flagged_urls, "Please sort flagged_page entries by URL."
+    flagged_patterns = [page.pattern for page in pb.flagged_page]
+    assert sorted(flagged_patterns) == flagged_patterns, (
+        "Please sort flagged_page entries by pattern.")
 
   def ProcessPb(self, opts, pb):
     binary_pb_str = pb.SerializeToString()
diff --git a/chrome/browser/resources/safety_tips/safety_tips.asciipb b/chrome/browser/resources/safety_tips/safety_tips.asciipb
index 8f58d67..0afdd4ff 100644
--- a/chrome/browser/resources/safety_tips/safety_tips.asciipb
+++ b/chrome/browser/resources/safety_tips/safety_tips.asciipb
@@ -2,17 +2,19 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# Placeholder for list of known bad URLs.
-# This file must be kept as-is and the full list must not be checked in to
-# Chromium source. When you want to update it, overwrite it locally, generate
-# and push the protobuffer to the storage bucket but do not check in the
-# changes.
+# Placeholder for list of known bad patterns.
+# This file must be kept as-is except for version_id. The full list must not be
+# checked in to Chromium source. When you want to update it, overwrite it
+# locally, generate and push the protobuffer to the storage bucket but do not
+# check in the changes.
 
 version_id: 2
 
 # See chrome/browser/lookalikes/safety_tips.proto for the full format.
-# These entries must be sorted by url.
-flagged_page {
-  url: "http://example.test/test-path-for-safety-tips/test.html"
-  type: BAD_REP
-}
+# These entries must be sorted alphabetically by pattern.
+
+# Example entry:
+# flagged_page {
+#  pattern: "example.test/test-path-for-safety-tips/test.html"
+#  type: BAD_REP
+#}
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
index eb4e26e..9997b58 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/chromeos/cr_picture/cr_png_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
@@ -7,6 +8,7 @@
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/icon.html">
 <link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
@@ -99,7 +101,11 @@
           <div class="settings-box first two-line">
             <template is="dom-if" if="[[syncStatus]]">
               <!-- Does not use <cr-link-row> due to custom aria label. -->
-              <div class="start two-line no-min-width"
+              <div id="profile-icon"
+                  style="background-image: [[getIconImageSet_(
+                      profileIconUrl_)]]">
+              </div>
+              <div class="middle two-line no-min-width"
                   on-click="onAccountManagerTap_"
                   actionable>
                 <div class="flex text-elide settings-box-text">
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
index 1bbaaa0..eac69cd2 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
@@ -11,6 +11,7 @@
 
   behaviors: [
     settings.RouteObserverBehavior,
+    CrPngBehavior,
     I18nBehavior,
     WebUIListenerBehavior,
     LockStateBehavior,
@@ -61,6 +62,12 @@
     },
 
     /**
+     * The currently selected profile icon URL. May be a data URL.
+     * @private
+     */
+    profileIconUrl_: String,
+
+    /**
      * The current profile name.
      * @private
      */
@@ -205,13 +212,20 @@
   },
 
   /**
-   * Handler for when the profile's name is updated.
+   * Handler for when the profile's icon and name is updated.
    * @private
    * @param {!settings.ProfileInfo} info
    */
   handleProfileInfo_: function(info) {
     this.profileName_ = info.name;
-    // info.iconUrl is not used for this page.
+    // Extract first frame from image by creating a single frame PNG using
+    // url as input if base64 encoded and potentially animated.
+    if (info.iconUrl.startsWith('data:image/png;base64')) {
+      this.profileIconUrl_ =
+          CrPngBehavior.convertImageSequenceToPng([info.iconUrl]);
+      return;
+    }
+    this.profileIconUrl_ = info.iconUrl;
   },
 
   /**
@@ -421,6 +435,15 @@
   },
 
   /**
+   * @param {string} iconUrl
+   * @return {string} A CSS image-set for multiple scale factors.
+   * @private
+   */
+  getIconImageSet_: function(iconUrl) {
+    return cr.icon.getImage(iconUrl);
+  },
+
+  /**
    * @param {!settings.SyncStatus} syncStatus
    * @return {boolean} Whether to show the "Sign in to Chrome" button.
    * @private
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
index cc9cca88..c91d9ace 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
@@ -209,16 +209,15 @@
    * This UI will use both the networkingPrivate extension API and the
    * networkConfig mojo API until we provide all of the required functionality
    * in networkConfig. TODO(stevenjb): Remove use of networkingPrivate api.
-   * @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy}
+   * @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote}
    */
-  networkConfigProxy_: null,
+  networkConfig_: null,
 
   /** @override */
   created: function() {
     this.browserProxy_ = settings.InternetPageBrowserProxyImpl.getInstance();
-    this.networkConfigProxy_ =
-        network_config.MojoInterfaceProviderImpl.getInstance()
-            .getMojoServiceProxy();
+    this.networkConfig_ = network_config.MojoInterfaceProviderImpl.getInstance()
+                              .getMojoServiceRemote();
   },
 
   /**
@@ -455,7 +454,7 @@
   getNetworkDetails_: function() {
     assert(this.guid);
     if (this.isSecondaryUser_) {
-      this.networkConfigProxy_.getNetworkState(this.guid).then(response => {
+      this.networkConfig_.getNetworkState(this.guid).then(response => {
         this.getStateCallback_(response.result);
       });
     } else {
diff --git a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
index c095580..03f950b 100644
--- a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
@@ -64,15 +64,14 @@
    * This UI will use both the networkingPrivate extension API and the
    * networkConfig mojo API until we provide all of the required functionality
    * in networkConfig. TODO(stevenjb): Remove use of networkingPrivate api.
-   * @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy}
+   * @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote}
    */
-  networkConfigProxy_: null,
+  networkConfig_: null,
 
   /** @override */
   created: function() {
-    this.networkConfigProxy_ =
-        network_config.MojoInterfaceProviderImpl.getInstance()
-            .getMojoServiceProxy();
+    this.networkConfig_ = network_config.MojoInterfaceProviderImpl.getInstance()
+                              .getMojoServiceRemote();
   },
 
   /** CrosNetworkConfigObserver impl */
@@ -99,7 +98,7 @@
       limit: chromeos.networkConfig.mojom.kNoLimit,
       networkType: OncMojo.getNetworkTypeFromString(this.networkType),
     };
-    this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+    this.networkConfig_.getNetworkStateList(filter).then(response => {
       this.networkStateList_ = response.result;
     });
   },
@@ -162,7 +161,7 @@
     // We need to make a round trip to Chrome in order to retrieve the managed
     // properties for the network. The delay is not noticeable (~5ms) and is
     // preferable to initiating a query for every known network at load time.
-    this.networkConfigProxy_.getManagedProperties(this.selectedGuid_)
+    this.networkConfig_.getManagedProperties(this.selectedGuid_)
         .then(response => {
           const properties = response.result;
           if (!properties) {
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.js b/chrome/browser/resources/settings/internet_page/internet_page.js
index bb18ded6..fc5a660b 100644
--- a/chrome/browser/resources/settings/internet_page/internet_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_page.js
@@ -162,16 +162,15 @@
    * This UI will use both the networkingPrivate extension API and the
    * networkConfig mojo API until we provide all of the required functionality
    * in networkConfig. TODO(stevenjb): Remove use of networkingPrivate api.
-   * @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy}
+   * @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote}
    */
-  networkConfigProxy_: null,
+  networkConfig_: null,
 
   /** @override */
   created: function() {
     this.browserProxy_ = settings.InternetPageBrowserProxyImpl.getInstance();
-    this.networkConfigProxy_ =
-        network_config.MojoInterfaceProviderImpl.getInstance()
-            .getMojoServiceProxy();
+    this.networkConfig_ = network_config.MojoInterfaceProviderImpl.getInstance()
+                              .getMojoServiceRemote();
   },
 
   /** @override */
@@ -287,7 +286,7 @@
    * @private
    */
   onDeviceEnabledToggled_: function(event) {
-    this.networkConfigProxy_.setNetworkTypeEnabledState(
+    this.networkConfig_.setNetworkTypeEnabledState(
         event.detail.type, event.detail.enabled);
   },
 
diff --git a/chrome/browser/resources/settings/internet_page/internet_subpage.js b/chrome/browser/resources/settings/internet_page/internet_subpage.js
index 5bd9634..2714d50e 100644
--- a/chrome/browser/resources/settings/internet_page/internet_subpage.js
+++ b/chrome/browser/resources/settings/internet_page/internet_subpage.js
@@ -136,16 +136,15 @@
    * This UI will use both the networkingPrivate extension API and the
    * networkConfig mojo API until we provide all of the required functionality
    * in networkConfig. TODO(stevenjb): Remove use of networkingPrivate api.
-   * @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy}
+   * @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote}
    */
-  networkConfigProxy_: null,
+  networkConfig_: null,
 
   /** @override */
   created: function() {
     this.browserProxy_ = settings.InternetPageBrowserProxyImpl.getInstance();
-    this.networkConfigProxy_ =
-        network_config.MojoInterfaceProviderImpl.getInstance()
-            .getMojoServiceProxy();
+    this.networkConfig_ = network_config.MojoInterfaceProviderImpl.getInstance()
+                              .getMojoServiceRemote();
   },
 
   /** @override */
@@ -246,9 +245,9 @@
       return;
     }
     const INTERVAL_MS = 10 * 1000;
-    this.networkConfigProxy_.requestNetworkScan(this.deviceState.type);
+    this.networkConfig_.requestNetworkScan(this.deviceState.type);
     this.scanIntervalId_ = window.setInterval(() => {
-      this.networkConfigProxy_.requestNetworkScan(this.deviceState.type);
+      this.networkConfig_.requestNetworkScan(this.deviceState.type);
     }, INTERVAL_MS);
   },
 
@@ -271,7 +270,7 @@
       limit: chromeos.networkConfig.mojom.kNoLimit,
       networkType: this.deviceState.type,
     };
-    this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+    this.networkConfig_.getNetworkStateList(filter).then(response => {
       this.onGetNetworks_(response.result);
     });
   },
@@ -294,7 +293,7 @@
         limit: chromeos.networkConfig.mojom.kNoLimit,
         networkType: mojom.NetworkType.kTether,
       };
-      this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+      this.networkConfig_.getNetworkStateList(filter).then(response => {
         const tetherNetworkStates = response.result;
         this.networkStateList_ = networkStates.concat(tetherNetworkStates);
       });
diff --git a/chrome/browser/resources/settings/internet_page/network_summary.js b/chrome/browser/resources/settings/internet_page/network_summary.js
index 842e1f7..d8a2a016 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary.js
+++ b/chrome/browser/resources/settings/internet_page/network_summary.js
@@ -76,8 +76,8 @@
     },
   },
 
-  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
-  networkConfigProxy_: null,
+  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
+  networkConfig_: null,
 
   /**
    * Set of GUIDs identifying active networks, one for each type.
@@ -87,9 +87,8 @@
 
   /** @override */
   created: function() {
-    this.networkConfigProxy_ =
-        network_config.MojoInterfaceProviderImpl.getInstance()
-            .getMojoServiceProxy();
+    this.networkConfig_ = network_config.MojoInterfaceProviderImpl.getInstance()
+                              .getMojoServiceRemote();
   },
 
   /** @override */
@@ -135,7 +134,7 @@
    */
   getNetworkLists_: function() {
     // First get the device states.
-    this.networkConfigProxy_.getDeviceStateList().then(response => {
+    this.networkConfig_.getDeviceStateList().then(response => {
       // Second get the network states.
       this.getNetworkStates_(response.result);
     });
@@ -154,7 +153,7 @@
       limit: chromeos.networkConfig.mojom.kNoLimit,
       networkType: mojom.NetworkType.kAll,
     };
-    this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+    this.networkConfig_.getNetworkStateList(filter).then(response => {
       this.updateNetworkStates_(response.result, deviceStateList);
     });
   },
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js b/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js
index 8dc4cf2..fe69ece9 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js
@@ -56,14 +56,13 @@
     },
   },
 
-  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
-  networkConfigProxy_: null,
+  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
+  networkConfig_: null,
 
   /** @override */
   created: function() {
-    this.networkConfigProxy_ =
-        network_config.MojoInterfaceProviderImpl.getInstance()
-            .getMojoServiceProxy();
+    this.networkConfig_ = network_config.MojoInterfaceProviderImpl.getInstance()
+                              .getMojoServiceRemote();
   },
 
   /** @override */
@@ -87,7 +86,7 @@
     if (!networks.find(network => network.guid == guid)) {
       return;
     }
-    this.networkConfigProxy_.getNetworkState(guid).then(response => {
+    this.networkConfig_.getNetworkState(guid).then(response => {
       if (response.result) {
         this.activeNetworkState_ = response.result;
       }
@@ -113,7 +112,7 @@
    * @private
    */
   updateTetherDeviceState_: function() {
-    this.networkConfigProxy_.getDeviceStateList().then(response => {
+    this.networkConfig_.getDeviceStateList().then(response => {
       const kTether = chromeos.networkConfig.mojom.NetworkType.kTether;
       const deviceStates = response.result;
       const deviceState =
@@ -143,7 +142,7 @@
       limit: 1,
       networkType: kTether,
     };
-    this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+    this.networkConfig_.getNetworkStateList(filter).then(response => {
       const networks = response.result;
       this.activeNetworkState_ =
           networks[0] || OncMojo.getDefaultNetworkState(kTether);
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html
index f2a605b..8be24f5 100644
--- a/chrome/browser/resources/settings/people_page/people_page.html
+++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -151,12 +151,13 @@
 </if>
           <div id="picture-subpage-trigger" class="settings-box first two-line">
             <template is="dom-if" if="[[syncStatus]]">
-              <div id="profile-icon" on-click="onProfileTap_" actionable
+              <div id="profile-icon" on-click="onProfileTap_"
+                  actionable$="[[allowChangePicture_]]"
                   style="background-image: [[getIconImageSet_(
                       profileIconUrl_)]]">
               </div>
               <div class="middle two-line no-min-width" on-click="onProfileTap_"
-                  actionable>
+                  actionable$="[[allowChangePicture_]]">
                 <div class="flex text-elide settings-box-text">
                   <span id="profile-name">[[profileName_]]</span>
                   <div class="secondary" hidden="[[!syncStatus.signedIn]]">
@@ -170,6 +171,8 @@
 </if>
 <if expr="chromeos">
                 <cr-icon-button class="subpage-arrow"
+                    id="profile-subpage-arrow"
+                    hidden="[[!allowChangePicture_]]"
                     aria-label="$i18n{changePictureTitle}"
                     aria-describedby="profile-name"></cr-icon-button>
 </if>
diff --git a/chrome/browser/resources/settings/people_page/people_page.js b/chrome/browser/resources/settings/people_page/people_page.js
index cce3987..c7481d27 100644
--- a/chrome/browser/resources/settings/people_page/people_page.js
+++ b/chrome/browser/resources/settings/people_page/people_page.js
@@ -91,6 +91,20 @@
     profileIconUrl_: String,
 
     /**
+     * Whether clicking on the profile row should take the user to the
+     * "change picture" sub-page.
+     * @private
+     */
+    allowChangePicture_: {
+      type: Boolean,
+      value: function() {
+        // On Chrome OS, only allow when SplitSettings is disabled.
+        return cr.isChromeOS ? loadTimeData.getBoolean('showOSSettings') : true;
+      },
+      readOnly: true,
+    },
+
+    /**
      * The current profile name.
      * @private
      */
@@ -342,7 +356,11 @@
   /** @private */
   onProfileTap_: function() {
     // <if expr="chromeos">
-    settings.navigateTo(settings.routes.CHANGE_PICTURE);
+    if (this.allowChangePicture_) {
+      // Testing allowChangePicture_ is simpler than conditionally removing
+      // on-click handlers in the HTML.
+      settings.navigateTo(settings.routes.CHANGE_PICTURE);
+    }
     // </if>
     // <if expr="not chromeos">
     settings.navigateTo(settings.routes.MANAGE_PROFILE);
diff --git a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
index 2832048c..93b0f487 100644
--- a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
+++ b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
@@ -125,14 +125,13 @@
     'onModelChanged_(pendingPrinter_.ppdModel)',
   ],
 
-  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
-  networkConfigProxy_: null,
+  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
+  networkConfig_: null,
 
   /** @override */
   created: function() {
-    this.networkConfigProxy_ =
-        network_config.MojoInterfaceProviderImpl.getInstance()
-            .getMojoServiceProxy();
+    this.networkConfig_ = network_config.MojoInterfaceProviderImpl.getInstance()
+                              .getMojoServiceRemote();
   },
 
   /** @override */
@@ -431,7 +430,7 @@
    * @private
    */
   refreshNetworks_: function() {
-    this.networkConfigProxy_
+    this.networkConfig_
         .getNetworkStateList({
           filter: chromeos.networkConfig.mojom.FilterType.kActive,
           networkType: chromeos.networkConfig.mojom.NetworkType.kAll,
diff --git a/chrome/browser/resources/settings/printing_page/cups_printers.js b/chrome/browser/resources/settings/printing_page/cups_printers.js
index acb850ca..4ad5e80 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printers.js
+++ b/chrome/browser/resources/settings/printing_page/cups_printers.js
@@ -65,19 +65,19 @@
         'openManufacturerModelDialogForSpecifiedPrinter_',
   },
 
-  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
-  networkConfigProxy_: null,
+  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
+  networkConfig_: null,
 
   /** @override */
   created: function() {
-    this.networkConfigProxy_ =
+    this.networkConfig_ =
         network_config.MojoInterfaceProviderImpl.getInstance()
-            .getMojoServiceProxy();
+            .getMojoServiceRemote();
   },
 
   /** @override */
   attached: function() {
-    this.networkConfigProxy_
+    this.networkConfig_
         .getNetworkStateList({
           filter: chromeos.networkConfig.mojom.FilterType.kActive,
           networkType: chromeos.networkConfig.mojom.NetworkType.kAll,
diff --git a/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc b/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc
index f726d1e..fd3da450 100644
--- a/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc
+++ b/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc
@@ -221,8 +221,8 @@
   }
 
   void SetUpOnMainThread() override {
-    base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
-                             base::BindOnce(&AddURLRequestFilterOnIOThread));
+    base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+                   base::BindOnce(&AddURLRequestFilterOnIOThread));
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/browser/ssl/captive_portal_helper_android.cc b/chrome/browser/ssl/captive_portal_helper_android.cc
index 1c5adb84..789e1b1 100644
--- a/chrome/browser/ssl/captive_portal_helper_android.cc
+++ b/chrome/browser/ssl/captive_portal_helper_android.cc
@@ -29,10 +29,9 @@
     const base::android::JavaParamRef<jstring>& jhash) {
   auto default_proto =
       SSLErrorAssistant::GetErrorAssistantProtoFromResourceBundle();
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(SSLErrorHandler::SetErrorAssistantProto,
-                     std::move(default_proto)));
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(SSLErrorHandler::SetErrorAssistantProto,
+                                std::move(default_proto)));
 
   const std::string hash = ConvertJavaStringToUTF8(env, jhash);
   auto config_proto =
@@ -40,16 +39,15 @@
   config_proto->set_version_id(INT_MAX);
   config_proto->add_captive_portal_cert()->set_sha256_hash(hash);
 
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(SSLErrorHandler::SetErrorAssistantProto,
-                     std::move(config_proto)));
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(SSLErrorHandler::SetErrorAssistantProto,
+                                std::move(config_proto)));
 }
 
 void JNI_CaptivePortalHelper_SetOSReportsCaptivePortalForTesting(
     JNIEnv* env,
     jboolean os_reports_captive_portal) {
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(SSLErrorHandler::SetOSReportsCaptivePortalForTesting,
                      os_reports_captive_portal));
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index bf2a90e..5a068ecb3 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -1274,7 +1274,7 @@
   void SetUpOnMainThread() override {
     ASSERT_TRUE(embedded_test_server()->Start());
 
-    base::PostTaskWithTraits(
+    base::PostTask(
         FROM_HERE, {content::BrowserThread::IO},
         base::BindOnce(&InstallLoadingInterceptor,
                        embedded_test_server()->GetURL("/title1.html").host()));
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 75fe2cb..0fb7a5b2 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -475,8 +475,7 @@
       json_reader.ReadToValueDeprecated(request.content);
   EXPECT_TRUE(value);
 
-  base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
-                           quit_closure);
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI}, quit_closure);
 
   if (hung_response)
     return std::make_unique<net::test_server::HungResponse>();
@@ -935,8 +934,8 @@
 
   void RunOnIOThreadBlocking(base::OnceClosure task) {
     base::RunLoop run_loop;
-    base::PostTaskWithTraitsAndReply(FROM_HERE, {content::BrowserThread::IO},
-                                     std::move(task), run_loop.QuitClosure());
+    base::PostTaskAndReply(FROM_HERE, {content::BrowserThread::IO},
+                           std::move(task), run_loop.QuitClosure());
     run_loop.Run();
   }
 
@@ -4993,14 +4992,14 @@
   void SetUpOnMainThread() override {
     CertVerifierBrowserTest::SetUpOnMainThread();
     host_resolver()->AddRule("*", "127.0.0.1");
-    base::PostTaskWithTraits(
+    base::PostTask(
         FROM_HERE, {content::BrowserThread::IO},
         base::BindOnce(&SetUpHttpNameMismatchPingInterceptorOnIOThread));
   }
 
   void TearDownOnMainThread() override {
-    base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
-                             base::BindOnce(&CleanUpOnIOThread));
+    base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+                   base::BindOnce(&CleanUpOnIOThread));
     CertVerifierBrowserTest::TearDownOnMainThread();
   }
 
@@ -7059,7 +7058,7 @@
     return;
   }
 
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {content::BrowserThread::IO},
       base::BindOnce(&net::TransportSecurityState::SetShouldRequireCTForTesting,
                      base::Owned(new bool(false))));
@@ -7714,8 +7713,8 @@
  private:
   void RunOnIOThreadBlocking(base::OnceClosure task) {
     base::RunLoop run_loop;
-    base::PostTaskWithTraitsAndReply(FROM_HERE, {content::BrowserThread::IO},
-                                     std::move(task), run_loop.QuitClosure());
+    base::PostTaskAndReply(FROM_HERE, {content::BrowserThread::IO},
+                           std::move(task), run_loop.QuitClosure());
     run_loop.Run();
   }
 
@@ -7813,8 +7812,8 @@
   }
 
   void TearDownOnMainThread() override {
-    base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
-                             base::BindOnce(&CleanUpOnIOThread));
+    base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+                   base::BindOnce(&CleanUpOnIOThread));
     CertVerifierBrowserTest::TearDownOnMainThread();
   }
 
diff --git a/chrome/browser/ssl/ssl_client_certificate_selector_test.cc b/chrome/browser/ssl/ssl_client_certificate_selector_test.cc
index fd1d508..b4070f09 100644
--- a/chrome/browser/ssl/ssl_client_certificate_selector_test.cc
+++ b/chrome/browser/ssl/ssl_client_certificate_selector_test.cc
@@ -38,7 +38,7 @@
 }
 
 void SSLClientCertificateSelectorTestBase::SetUpOnMainThread() {
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(&SSLClientCertificateSelectorTestBase::SetUpOnIOThread,
                      base::Unretained(this)));
@@ -52,7 +52,7 @@
 // Have to release our reference to the auth handler during the test to allow
 // it to be destroyed while the Browser and its IO thread still exist.
 void SSLClientCertificateSelectorTestBase::TearDownOnMainThread() {
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(&SSLClientCertificateSelectorTestBase::TearDownOnIOThread,
                      base::Unretained(this)));
diff --git a/chrome/browser/ssl/ssl_error_controller_client.cc b/chrome/browser/ssl/ssl_error_controller_client.cc
index abfeb18a..4998692 100644
--- a/chrome/browser/ssl/ssl_error_controller_client.cc
+++ b/chrome/browser/ssl/ssl_error_controller_client.cc
@@ -219,9 +219,10 @@
   chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
       ProfileManager::GetActiveUserProfile(), chrome::kDateTimeSubPage);
 #else
-  base::PostTaskWithTraits(FROM_HERE,
-                           {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
-                           base::BindOnce(&LaunchDateAndTimeSettingsImpl));
+  base::PostTask(
+      FROM_HERE,
+      {base::ThreadPool(), base::TaskPriority::USER_VISIBLE, base::MayBlock()},
+      base::BindOnce(&LaunchDateAndTimeSettingsImpl));
 #endif
 }
 
diff --git a/chrome/browser/ssl/ssl_error_handler_unittest.cc b/chrome/browser/ssl/ssl_error_handler_unittest.cc
index 8950c2f9..5ddb4db 100644
--- a/chrome/browser/ssl/ssl_error_handler_unittest.cc
+++ b/chrome/browser/ssl/ssl_error_handler_unittest.cc
@@ -125,8 +125,7 @@
 std::unique_ptr<net::test_server::HttpResponse> WaitForRequest(
     const base::Closure& quit_closure,
     const net::test_server::HttpRequest& request) {
-  base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
-                           quit_closure);
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI}, quit_closure);
   return std::make_unique<net::test_server::HungResponse>();
 }
 
@@ -615,7 +614,7 @@
     base::RunLoop run_loop;
     std::unique_ptr<network::SharedURLLoaderFactoryInfo>
         url_loader_factory_info;
-    base::PostTaskWithTraitsAndReply(
+    base::PostTaskAndReply(
         FROM_HERE, {content::BrowserThread::IO},
         base::BindOnce(CreateURLLoaderFactory, &url_loader_factory_info),
         run_loop.QuitClosure());
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index 492535c..cfe9b3a4 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -1009,9 +1009,6 @@
   // with the frame/tab behind them.
   GenerateMissingTextColors();
 
-  // Generates missing NTP related colors.
-  GenerateMissingNtpColors();
-
   // Make sure the |images_on_file_thread_| has bitmaps for supported
   // scale factors before passing to FILE thread.
   images_on_file_thread_ = images_;
@@ -1038,6 +1035,10 @@
   for (size_t i = 0; i < base::size(kPreloadIDs); ++i) {
     GenerateRawImageForAllSupportedScales(kPreloadIDs[i]);
   }
+
+  // Generates missing NTP related colors. Should be called after theme images
+  // are prepared.
+  GenerateMissingNtpColors();
 }
 
 void BrowserThemePack::InitEmptyPack() {
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc b/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc
index 48e3240..1ef494aa 100644
--- a/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc
+++ b/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc
@@ -10,9 +10,7 @@
 #include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
-#include "ui/base/ui_base_features.h"
 
 CrostiniAppContextMenu::CrostiniAppContextMenu(
     Profile* profile,
@@ -33,10 +31,6 @@
     return true;
   }
 
-  if (!base::FeatureList::IsEnabled(features::kCrostiniAppUninstallGui)) {
-    return false;
-  }
-
   crostini::CrostiniRegistryService* registry_service =
       crostini::CrostiniRegistryServiceFactory::GetForProfile(profile());
   base::Optional<crostini::CrostiniRegistryService::Registration> registration =
diff --git a/chrome/browser/ui/ash/assistant/OWNERS b/chrome/browser/ui/ash/assistant/OWNERS
index 1f575b1..0c730475 100644
--- a/chrome/browser/ui/ash/assistant/OWNERS
+++ b/chrome/browser/ui/ash/assistant/OWNERS
@@ -1,3 +1,3 @@
 file://chromeos/assistant/OWNERS
 
-# COMPONENTS: UI>Shell>Assistant
+# COMPONENT: UI>Shell>Assistant
diff --git a/chrome/browser/ui/ash/assistant/assistant_setup.cc b/chrome/browser/ui/ash/assistant/assistant_setup.cc
index 5395f853..52220e3 100644
--- a/chrome/browser/ui/ash/assistant/assistant_setup.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_setup.cc
@@ -40,6 +40,10 @@
   chromeos::AssistantOptInDialog::Show(type, std::move(callback));
 }
 
+bool AssistantSetup::BounceOptInWindowIfActive() {
+  return chromeos::AssistantOptInDialog::BounceIfActive();
+}
+
 void AssistantSetup::OnStateChanged(ash::mojom::VoiceInteractionState state) {
   if (state == ash::mojom::VoiceInteractionState::NOT_READY)
     return;
diff --git a/chrome/browser/ui/ash/assistant/assistant_setup.h b/chrome/browser/ui/ash/assistant/assistant_setup.h
index 2f571a34..d801b0c7 100644
--- a/chrome/browser/ui/ash/assistant/assistant_setup.h
+++ b/chrome/browser/ui/ash/assistant/assistant_setup.h
@@ -26,6 +26,7 @@
   void StartAssistantOptInFlow(
       ash::FlowType type,
       StartAssistantOptInFlowCallback callback) override;
+  bool BounceOptInWindowIfActive() override;
 
   // If prefs::kVoiceInteractionConsentStatus is nullptr, means the
   // pref is not set by user. Therefore we need to start OOBE.
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index cb85d507..95ac680 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -459,9 +459,10 @@
       signin_metrics::AccessPoint access_point,
       bool is_source_keyboard) = 0;
 
-  // Shows User Happiness Tracking Survey's invitation bubble anchored to the
-  // app menu button that will lead to a survey identified by |site_id|.
-  virtual void ShowHatsBubbleFromAppMenuButton(const std::string& site_id) = 0;
+  // Shows User Happiness Tracking Survey's invitation bubble when possible
+  // (such as having the proper anchor view).
+  // |site_id| is the site identification of the survey the bubble leads to.
+  virtual void ShowHatsBubble(const std::string& site_id) = 0;
 
   // Executes |command| registered by |extension|.
   virtual void ExecuteExtensionCommand(const extensions::Extension* extension,
diff --git a/chrome/browser/ui/hats/hats_service.cc b/chrome/browser/ui/hats/hats_service.cc
index c964abce..39ec09d7 100644
--- a/chrome/browser/ui/hats/hats_service.cc
+++ b/chrome/browser/ui/hats/hats_service.cc
@@ -13,6 +13,7 @@
 #include "base/version.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/common/chrome_features.h"
@@ -84,8 +85,10 @@
 void HatsService::LaunchSatisfactionSurvey() {
   if (ShouldShowSurvey(kHatsSurveyTriggerSatisfaction)) {
     Browser* browser = chrome::FindLastActive();
-    if (browser && browser->is_type_tabbed()) {
-      browser->window()->ShowHatsBubbleFromAppMenuButton(en_site_id_);
+    // Never show HaTS bubble in Incognito mode.
+    if (browser && browser->is_type_tabbed() &&
+        profiles::IsRegularOrGuestSession(browser)) {
+      browser->window()->ShowHatsBubble(en_site_id_);
 
       DictionaryPrefUpdate update(profile_->GetPrefs(),
                                   prefs::kHatsSurveyMetadata);
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 3b430030..30c034a 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -3013,19 +3013,8 @@
   ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::ICON_AVATAR_BUBBLE);
 }
 
-void BrowserView::ShowHatsBubbleFromAppMenuButton(const std::string& site_id) {
-  // Never show any HaTS bubble in Incognito.
-  if (!IsRegularOrGuestSession())
-    return;
-
-  AppMenuButton* app_menu_button =
-      toolbar_button_provider()->GetAppMenuButton();
-
-  // Do not show HaTS bubble if there is no avatar menu button to anchor to.
-  if (!app_menu_button)
-    return;
-
-  HatsBubbleView::Show(browser(), app_menu_button, site_id);
+void BrowserView::ShowHatsBubble(const std::string& site_id) {
+  HatsBubbleView::Show(browser(), site_id);
 }
 
 void BrowserView::ExecuteExtensionCommand(
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 26290fa4..89d7a5d 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -425,7 +425,7 @@
       const signin::ManageAccountsParams& manage_accounts_params,
       signin_metrics::AccessPoint access_point,
       bool is_source_keyboard) override;
-  void ShowHatsBubbleFromAppMenuButton(const std::string& site_id) override;
+  void ShowHatsBubble(const std::string& site_id) override;
   void ExecuteExtensionCommand(const extensions::Extension* extension,
                                const extensions::Command& command) override;
   ExclusiveAccessContext* GetExclusiveAccessContext() override;
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 0ff6fa4..42bb7ad4 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
@@ -7,9 +7,17 @@
 #include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl.h"
 #include "ui/views/layout/box_layout.h"
 
+namespace {
+
+constexpr int kMediaListMaxHeight = 478;
+
+}  // anonymous namespace
+
 MediaNotificationListView::MediaNotificationListView() {
-  SetLayoutManager(std::make_unique<views::BoxLayout>(
+  SetContents(std::make_unique<views::View>());
+  contents()->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical));
+  ClipHeightTo(0, kMediaListMaxHeight);
 }
 
 MediaNotificationListView::~MediaNotificationListView() = default;
@@ -20,7 +28,9 @@
   DCHECK(!base::Contains(notifications_, id));
   DCHECK_NE(nullptr, notification.get());
 
-  notifications_[id] = AddChildView(std::move(notification));
+  notifications_[id] = contents()->AddChildView(std::move(notification));
+
+  contents()->InvalidateLayout();
   PreferredSizeChanged();
 }
 
@@ -28,7 +38,9 @@
   if (!base::Contains(notifications_, id))
     return;
 
-  RemoveChildView(notifications_[id]);
+  contents()->RemoveChildView(notifications_[id]);
   notifications_.erase(id);
+
+  contents()->InvalidateLayout();
   PreferredSizeChanged();
 }
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_list_view.h b/chrome/browser/ui/views/global_media_controls/media_notification_list_view.h
index 302deec8..e094acbe 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_list_view.h
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_list_view.h
@@ -8,13 +8,13 @@
 #include <map>
 #include <memory>
 
-#include "ui/views/view.h"
+#include "ui/views/controls/scroll_view.h"
 
 class MediaNotificationContainerImpl;
 
 // MediaNotificationListView is a container that holds a list of active media
 // sessions.
-class MediaNotificationListView : public views::View {
+class MediaNotificationListView : public views::ScrollView {
  public:
   MediaNotificationListView();
   ~MediaNotificationListView() override;
diff --git a/chrome/browser/ui/views/hats/hats_browsertest.cc b/chrome/browser/ui/views/hats/hats_browsertest.cc
index 1995322..0a3ff7f 100644
--- a/chrome/browser/ui/views/hats/hats_browsertest.cc
+++ b/chrome/browser/ui/views/hats/hats_browsertest.cc
@@ -18,7 +18,7 @@
   void ShowUi(const std::string& name) override {
     ASSERT_TRUE(browser()->is_type_tabbed());
     BrowserView::GetBrowserViewForBrowser(InProcessBrowserTest::browser())
-        ->ShowHatsBubbleFromAppMenuButton(std::string());
+        ->ShowHatsBubble(std::string());
   }
 
  private:
diff --git a/chrome/browser/ui/views/hats/hats_bubble_view.cc b/chrome/browser/ui/views/hats/hats_bubble_view.cc
index 37edd5d..79a0bf7 100644
--- a/chrome/browser/ui/views/hats/hats_bubble_view.cc
+++ b/chrome/browser/ui/views/hats/hats_bubble_view.cc
@@ -10,6 +10,8 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
 #include "chrome/browser/ui/views/hats/hats_web_dialog.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -36,8 +38,14 @@
 
 // static
 void HatsBubbleView::Show(Browser* browser,
-                          AppMenuButton* anchor_button,
                           const std::string& site_id) {
+  AppMenuButton* anchor_button = BrowserView::GetBrowserViewForBrowser(browser)
+                                     ->toolbar_button_provider()
+                                     ->GetAppMenuButton();
+  // Do not show HaTS bubble if there is no avatar menu button to anchor to.
+  if (!anchor_button)
+    return;
+
   base::RecordAction(base::UserMetricsAction("HatsBubble.Show"));
 
   DCHECK(anchor_button->GetWidget());
diff --git a/chrome/browser/ui/views/hats/hats_bubble_view.h b/chrome/browser/ui/views/hats/hats_bubble_view.h
index 9f509363..6c504d5 100644
--- a/chrome/browser/ui/views/hats/hats_bubble_view.h
+++ b/chrome/browser/ui/views/hats/hats_bubble_view.h
@@ -22,11 +22,9 @@
  public:
   // Returns a pointer to the Hats Bubble being shown. For testing only.
   static views::BubbleDialogDelegateView* GetHatsBubble();
-  // Creates and shows the bubble anchored to the |anchor_button| and leads to
-  // a survey identified by |site_id|.
-  static void Show(Browser* browser,
-                   AppMenuButton* anchor_button,
-                   const std::string& site_id);
+  // Creates and shows the bubble that leads to a survey identified by
+  // |site_id|.
+  static void Show(Browser* browser, const std::string& site_id);
 
  protected:
   // views::WidgetDelegate:
diff --git a/chrome/browser/ui/views/hover_button_controller.cc b/chrome/browser/ui/views/hover_button_controller.cc
index 6a60247..bb7df31d 100644
--- a/chrome/browser/ui/views/hover_button_controller.cc
+++ b/chrome/browser/ui/views/hover_button_controller.cc
@@ -54,3 +54,9 @@
     ButtonController::OnMouseReleased(event);
   }
 }
+
+void HoverButtonController::OnGestureEvent(ui::GestureEvent* event) {
+  if (event->type() == ui::ET_GESTURE_TAP && listener_) {
+    listener_->ButtonPressed(button(), *event);
+  }
+}
diff --git a/chrome/browser/ui/views/hover_button_controller.h b/chrome/browser/ui/views/hover_button_controller.h
index 767ed900..5b263234 100644
--- a/chrome/browser/ui/views/hover_button_controller.h
+++ b/chrome/browser/ui/views/hover_button_controller.h
@@ -30,6 +30,7 @@
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
   bool OnKeyPressed(const ui::KeyEvent& event) override;
+  void OnGestureEvent(ui::GestureEvent* event) override;
 
  private:
   // Listener to be called when button is clicked.
diff --git a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
index 82a40c4..94e0201 100644
--- a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
@@ -435,6 +435,14 @@
   autofill::GetAddressComponents(chosen_country_code,
                                  state()->GetApplicationLocale(),
                                  components.get(), &language_code_);
+
+  // Insert the Country combobox at the top.
+  editor_fields_.emplace_back(
+      autofill::ADDRESS_HOME_COUNTRY,
+      l10n_util::GetStringUTF16(IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL),
+      EditorField::LengthHint::HINT_SHORT, /*required=*/true,
+      EditorField::ControlType::COMBOBOX);
+
   for (size_t line_index = 0; line_index < components->GetSize();
        ++line_index) {
     const base::ListValue* line = nullptr;
@@ -482,17 +490,9 @@
       editor_fields_.emplace_back(server_field_type,
                                   base::UTF8ToUTF16(field_name), length_hint,
                                   autofill::i18n::IsFieldRequired(
-                                      server_field_type, chosen_country_code),
+                                      server_field_type, chosen_country_code) ||
+                                      server_field_type == autofill::NAME_FULL,
                                   control_type);
-      // Insert the Country combobox right after NAME_FULL.
-      if (server_field_type == autofill::NAME_FULL) {
-        editor_fields_.emplace_back(
-            autofill::ADDRESS_HOME_COUNTRY,
-            l10n_util::GetStringUTF16(
-                IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL),
-            EditorField::LengthHint::HINT_SHORT, /*required=*/true,
-            EditorField::ControlType::COMBOBOX);
-      }
     }
   }
   // Always add phone number at the end.
diff --git a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc
index 4febe74b..0678d7c 100644
--- a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/containers/flat_map.h"
@@ -93,10 +94,11 @@
   const auto* layout_provider = ChromeLayoutProvider::Get();
 
   // Add the text field for editing the title along with a label above it.
-  AddChildView(std::make_unique<views::Label>(base::ASCIIToUTF16("New title:"),
-                                              views::style::CONTEXT_LABEL,
-                                              views::style::STYLE_PRIMARY));
+  View* title_label = AddChildView(std::make_unique<views::Label>(
+      base::ASCIIToUTF16("New title"), views::style::CONTEXT_LABEL,
+      views::style::STYLE_PRIMARY));
   title_field_ = AddChildView(std::make_unique<views::Textfield>());
+  title_field_->SetAssociatedLabel(title_label);
   title_field_->SetDefaultWidthInChars(15);
 
   title_field_->SetProperty(
@@ -112,11 +114,13 @@
   auto combobox_model = std::make_unique<ui::SimpleComboboxModel>(color_names);
 
   // Add the color selector with label above it.
-  AddChildView(std::make_unique<views::Label>(base::ASCIIToUTF16("New color:"),
+  base::string16 color_label_text = base::ASCIIToUTF16("New color");
+  AddChildView(std::make_unique<views::Label>(color_label_text,
                                               views::style::CONTEXT_LABEL,
                                               views::style::STYLE_PRIMARY));
   color_selector_ = AddChildView(
       std::make_unique<views::Combobox>(std::move(combobox_model)));
+  color_selector_->SetTooltipText(color_label_text);
 
   // Layout vertically with margin collapsing. This allows us to use spacer
   // views with |DISTANCE_UNRELATED_CONTROL_VERTICAL| margins without worrying
diff --git a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc
new file mode 100644
index 0000000..6d2fbbf
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h"
+
+#include "base/time/time.h"
+#include "chrome/browser/ui/tabs/tab_group_id.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/tabs/tab_group_header.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "ui/events/event.h"
+#include "ui/gfx/geometry/point_f.h"
+
+class TabGroupEditorBubbleViewDialogBrowserTest : public DialogBrowserTest {
+ protected:
+  void ShowUi(const std::string& name) override {
+    TabGroupId group = browser()->tab_strip_model()->AddToNewGroup({0});
+
+    BrowserView* browser_view = static_cast<BrowserView*>(browser()->window());
+    TabGroupHeader* header =
+        browser_view->tabstrip()->group_headers_[group].get();
+    ASSERT_NE(nullptr, header);
+
+    ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, gfx::PointF(),
+                                 gfx::PointF(), base::TimeTicks(), 0, 0);
+    header->OnMousePressed(pressed_event);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(TabGroupEditorBubbleViewDialogBrowserTest,
+                       InvokeUi_default) {
+  ShowAndVerifyUi();
+}
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 6eff6ea..b52eac7 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -310,6 +310,7 @@
 
   friend class TabDragControllerTest;
   friend class TabDragContextImpl;
+  friend class TabGroupEditorBubbleViewDialogBrowserTest;
   friend class TabHoverCardBubbleViewBrowserTest;
   friend class TabHoverCardBubbleViewInteractiveUiTest;
   friend class TabStripTest;
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
index a2ec8114..1a7006d 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/in_product_help/in_product_help.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
 #include "chrome/browser/ui/views/toolbar/app_menu.h"
@@ -307,6 +308,8 @@
 }
 
 bool BrowserAppMenuButton::CanDrop(const ui::OSExchangeData& data) {
+  if (base::FeatureList::IsEnabled(features::kExtensionsToolbarMenu))
+    return false;
   return BrowserActionDragData::CanDrop(data,
                                         toolbar_view_->browser()->profile());
 }
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
index 3c3e38d..c616a69 100644
--- a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
@@ -30,6 +30,7 @@
 #include "content/public/common/content_features.h"
 #include "net/base/url_util.h"
 #include "ui/views/widget/widget.h"
+#include "ui/wm/core/window_animations.h"
 
 namespace chromeos {
 
@@ -135,6 +136,17 @@
   g_dialog->ShowSystemDialog();
 }
 
+// static
+bool AssistantOptInDialog::BounceIfActive() {
+  if (!g_dialog)
+    return false;
+
+  g_dialog->Focus();
+  wm::AnimateWindow(g_dialog->dialog_window(),
+                    wm::WINDOW_ANIMATION_TYPE_BOUNCE);
+  return true;
+}
+
 AssistantOptInDialog::AssistantOptInDialog(
     ash::FlowType type,
     ash::AssistantSetup::StartAssistantOptInFlowCallback callback)
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h
index d4b7b6b..a93c248 100644
--- a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h
@@ -48,6 +48,9 @@
                    ash::AssistantSetup::StartAssistantOptInFlowCallback
                        callback = base::DoNothing());
 
+  // Returns true and bounces the window if the dialog is active.
+  static bool BounceIfActive();
+
  protected:
   AssistantOptInDialog(
       ash::FlowType type,
diff --git a/chrome/browser/web_applications/components/install_finalizer.cc b/chrome/browser/web_applications/components/install_finalizer.cc
index 35094af..3b693838 100644
--- a/chrome/browser/web_applications/components/install_finalizer.cc
+++ b/chrome/browser/web_applications/components/install_finalizer.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/web_applications/components/install_finalizer.h"
 
 #include "base/logging.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_ui_manager.h"
 
 namespace web_app {
@@ -25,6 +27,12 @@
 
 bool InstallFinalizer::CanReparentTab(const AppId& app_id,
                                       bool shortcut_created) const {
+  // Reparent the web contents into its own window only if that is the
+  // app's launch type.
+  DCHECK(registrar_);
+  if (registrar_->GetAppLaunchContainer(app_id) != LaunchContainer::kWindow)
+    return false;
+
   return ui_manager().CanReparentAppTabToWindow(app_id, shortcut_created);
 }
 
diff --git a/chrome/browser/web_applications/components/install_finalizer.h b/chrome/browser/web_applications/components/install_finalizer.h
index e0254bc..805ec9a 100644
--- a/chrome/browser/web_applications/components/install_finalizer.h
+++ b/chrome/browser/web_applications/components/install_finalizer.h
@@ -63,6 +63,7 @@
   virtual bool CanAddAppToQuickLaunchBar() const;
   virtual void AddAppToQuickLaunchBar(const AppId& app_id);
 
+  // |virtual| for testing.
   virtual bool CanReparentTab(const AppId& app_id, bool shortcut_created) const;
   virtual void ReparentTab(const AppId& app_id,
                            bool shortcut_created,
diff --git a/chrome/browser/web_applications/components/web_app_install_utils.cc b/chrome/browser/web_applications/components/web_app_install_utils.cc
index bb6d7b4..e0d23f52 100644
--- a/chrome/browser/web_applications/components/web_app_install_utils.cc
+++ b/chrome/browser/web_applications/components/web_app_install_utils.cc
@@ -24,22 +24,86 @@
 
 namespace {
 
-void ReplaceWebAppIcons(std::map<int, BitmapAndSource> bitmap_map,
+// Returns icon sizes to be generated from downloaded icons.
+std::set<int> SizesToGenerate() {
+  return std::set<int>({
+      icon_size::k32,
+      icon_size::k64,
+      icon_size::k48,
+      icon_size::k96,
+      icon_size::k128,
+      icon_size::k256,
+  });
+}
+
+// Get a list of non-empty square icons from |web_app_info|.
+void FilterSquareIconsFromInfo(const WebApplicationInfo& web_app_info,
+                               std::vector<BitmapAndSource>* square_icons) {
+  // Add all existing icons from WebApplicationInfo.
+  for (const WebApplicationInfo::IconInfo& icon_info : web_app_info.icons) {
+    const SkBitmap& icon = icon_info.data;
+    if (!icon.drawsNothing() && icon.width() == icon.height())
+      square_icons->push_back(BitmapAndSource(icon_info.url, icon));
+  }
+}
+
+// Get a list of non-empty square icons from |icons_map|.
+void FilterSquareIconsFromMap(const IconsMap& icons_map,
+                              std::vector<BitmapAndSource>* square_icons) {
+  for (const std::pair<GURL, std::vector<SkBitmap>>& url_icon : icons_map) {
+    for (const SkBitmap& icon : url_icon.second) {
+      if (!icon.empty() && icon.width() == icon.height())
+        square_icons->push_back(BitmapAndSource(url_icon.first, icon));
+    }
+  }
+}
+
+// This function replaces |web_app_info| icons with the image data of any icon
+// from |size_map|.
+void ReplaceWebAppIcons(std::map<int, BitmapAndSource> size_map,
                         WebApplicationInfo* web_app_info) {
   web_app_info->icons.clear();
 
   // Populate the icon data into the WebApplicationInfo we are using to
   // install the bookmark app.
-  for (const auto& pair : bitmap_map) {
+  for (const auto& size_and_icon : size_map) {
     WebApplicationInfo::IconInfo icon_info;
-    icon_info.data = pair.second.bitmap;
-    icon_info.url = pair.second.source_url;
+    icon_info.data = size_and_icon.second.bitmap;
+    icon_info.url = size_and_icon.second.source_url;
     icon_info.width = icon_info.data.width();
     icon_info.height = icon_info.data.height();
     web_app_info->icons.push_back(icon_info);
   }
 }
 
+// This function updates |web_app_info| with the image data of any icon from
+// |size_map| that has a URL and size matching that in |web_app_info|, as
+// well as adding any new images from |size_map| that have no URL.
+void UpdateWebAppIconsWithoutChangingLinks(
+    const std::map<int, BitmapAndSource>& size_map,
+    WebApplicationInfo* web_app_info) {
+  // First add in the icon data that have urls with the url / size data from the
+  // original web app info, and the data from the new icons (if any).
+  for (auto& icon : web_app_info->icons) {
+    if (!icon.url.is_empty() && icon.data.empty()) {
+      const auto& it = size_map.find(icon.width);
+      if (it != size_map.end() && it->second.source_url == icon.url)
+        icon.data = it->second.bitmap;
+    }
+  }
+
+  // Now add in any icons from the updated list that don't have URLs.
+  for (const auto& pair : size_map) {
+    if (pair.second.source_url.is_empty()) {
+      WebApplicationInfo::IconInfo icon_info;
+      icon_info.data = pair.second.bitmap;
+      icon_info.width = pair.first;
+      icon_info.height = pair.first;
+      web_app_info->icons.push_back(icon_info);
+    }
+  }
+}
+
 }  // namespace
 
 void UpdateWebAppInfoFromManifest(const blink::Manifest& manifest,
@@ -91,14 +155,6 @@
   web_app_info->file_handler = manifest.file_handler;
 }
 
-std::set<int> SizesToGenerate() {
-  // Generate container icons from smaller icons.
-  return std::set<int>({
-      icon_size::k32, icon_size::k64, icon_size::k48, icon_size::k96,
-      icon_size::k128, icon_size::k256,
-  });
-}
-
 std::vector<GURL> GetValidIconUrlsToDownload(
     const WebApplicationInfo& web_app_info,
     const InstallableData* data) {
@@ -118,60 +174,38 @@
   return web_app_info_icon_urls;
 }
 
-void FilterSquareIconsFromInfo(const WebApplicationInfo& web_app_info,
-                               std::vector<BitmapAndSource>* square_icons) {
-  // Add all existing icons from WebApplicationInfo.
-  for (const WebApplicationInfo::IconInfo& icon_info : web_app_info.icons) {
-    const SkBitmap& icon = icon_info.data;
-    if (!icon.drawsNothing() && icon.width() == icon.height())
-      square_icons->push_back(BitmapAndSource(icon_info.url, icon));
-  }
-}
+void FilterAndResizeIconsGenerateMissing(WebApplicationInfo* web_app_info,
+                                         const IconsMap* icons_map,
+                                         bool is_for_sync) {
+  // Ensure that all icons that are in web_app_info are present, by generating
+  // icons for any sizes which have failed to download. This ensures that the
+  // created manifest for the web app does not contain links to icons
+  // which are not actually created and linked on disk.
+  std::vector<BitmapAndSource> square_icons;
+  if (icons_map)
+    FilterSquareIconsFromMap(*icons_map, &square_icons);
+  if (!is_for_sync)
+    FilterSquareIconsFromInfo(*web_app_info, &square_icons);
 
-void FilterSquareIconsFromMap(const IconsMap& icons_map,
-                              std::vector<BitmapAndSource>* square_icons) {
-  for (const std::pair<GURL, std::vector<SkBitmap>>& url_icon : icons_map) {
-    for (const SkBitmap& icon : url_icon.second) {
-      if (!icon.empty() && icon.width() == icon.height())
-        square_icons->push_back(BitmapAndSource(url_icon.first, icon));
-    }
+  std::set<int> sizes_to_generate = SizesToGenerate();
+  if (is_for_sync) {
+    // Ensure that all icon widths in the web app info icon array are present in
+    // the sizes to generate set. This ensures that we will have all of the
+    // icon sizes from when the app was originally added, even if icon URLs are
+    // no longer accessible.
+    for (const auto& icon : web_app_info->icons)
+      sizes_to_generate.insert(icon.width);
   }
-}
 
-void ResizeDownloadedIconsGenerateMissing(
-    std::vector<BitmapAndSource> downloaded_icons,
-    WebApplicationInfo* web_app_info) {
   web_app_info->generated_icon_color = SK_ColorTRANSPARENT;
-  std::map<int, BitmapAndSource> size_to_icons = ResizeIconsAndGenerateMissing(
-      downloaded_icons, SizesToGenerate(), web_app_info->app_url,
+  std::map<int, BitmapAndSource> size_to_icon = ResizeIconsAndGenerateMissing(
+      square_icons, sizes_to_generate, web_app_info->app_url,
       &web_app_info->generated_icon_color);
 
-  ReplaceWebAppIcons(size_to_icons, web_app_info);
-}
-
-void UpdateWebAppIconsWithoutChangingLinks(
-    const std::map<int, BitmapAndSource>& size_map,
-    WebApplicationInfo* web_app_info) {
-  // First add in the icon data that have urls with the url / size data from the
-  // original web app info, and the data from the new icons (if any).
-  for (auto& icon : web_app_info->icons) {
-    if (!icon.url.is_empty() && icon.data.empty()) {
-      const auto& it = size_map.find(icon.width);
-      if (it != size_map.end() && it->second.source_url == icon.url)
-        icon.data = it->second.bitmap;
-    }
-  }
-
-  // Now add in any icons from the updated list that don't have URLs.
-  for (const auto& pair : size_map) {
-    if (pair.second.source_url.is_empty()) {
-      WebApplicationInfo::IconInfo icon_info;
-      icon_info.data = pair.second.bitmap;
-      icon_info.width = pair.first;
-      icon_info.height = pair.first;
-      web_app_info->icons.push_back(icon_info);
-    }
-  }
+  if (is_for_sync)
+    UpdateWebAppIconsWithoutChangingLinks(size_to_icon, web_app_info);
+  else
+    ReplaceWebAppIcons(size_to_icon, web_app_info);
 }
 
 void RecordAppBanner(content::WebContents* contents, const GURL& app_url) {
diff --git a/chrome/browser/web_applications/components/web_app_install_utils.h b/chrome/browser/web_applications/components/web_app_install_utils.h
index 137fbe8f..c49831c 100644
--- a/chrome/browser/web_applications/components/web_app_install_utils.h
+++ b/chrome/browser/web_applications/components/web_app_install_utils.h
@@ -28,7 +28,6 @@
 
 enum class ExternalInstallSource;
 enum class InstallResultCode;
-struct BitmapAndSource;
 
 enum class ForInstallableSite {
   kYes,
@@ -36,17 +35,11 @@
   kUnknown,
 };
 
-// A map of icon urls to the bitmaps provided by that url.
-using IconsMap = std::map<GURL, std::vector<SkBitmap>>;
-
 // Update the given WebApplicationInfo with information from the manifest.
 void UpdateWebAppInfoFromManifest(const blink::Manifest& manifest,
                                   WebApplicationInfo* web_app_info,
                                   ForInstallableSite installable_site);
 
-// Returns icon sizes to be generated from downloaded icons.
-std::set<int> SizesToGenerate();
-
 // Form a list of icons to download: Remove icons with invalid urls. If |data|
 // is specified and contains a valid primary icon, we skip downloading any
 // matching icon in |web_app_info|.
@@ -54,31 +47,21 @@
     const WebApplicationInfo& web_app_info,
     const InstallableData* data);
 
-// Get a list of non-empty square icons from |web_app_info|.
-void FilterSquareIconsFromInfo(const WebApplicationInfo& web_app_info,
-                               std::vector<BitmapAndSource>* square_icons);
+// A map of icon urls to the bitmaps provided by that url.
+using IconsMap = std::map<GURL, std::vector<SkBitmap>>;
 
-// Get a list of non-empty square icons from |icons_map|.
-void FilterSquareIconsFromMap(const IconsMap& icons_map,
-                              std::vector<BitmapAndSource>* square_icons);
-
-// Ensure that the necessary-sized icons are available by resizing larger
-// icons down to smaller sizes, and generating icons for sizes where resizing
-// is not possible.
-void ResizeDownloadedIconsGenerateMissing(
-    std::vector<BitmapAndSource> downloaded_icons,
-    WebApplicationInfo* web_app_info);
-
-// It is important that the linked app information in any web app that
+// Filter out square icons, ensure that the necessary-sized icons are available
+// by resizing larger icons down to smaller sizes, and generating icons for
+// sizes where resizing is not possible. |icons_map| is optional.
+//
+// Sync: It is important that the linked app information in any web app that
 // gets created from sync matches the linked app information that came from
 // sync. If there are any changes, they will be synced back to other devices
-// and could potentially create a never ending sync cycle.
-// This function updates |web_app_info| with the image data of any icon from
-// |size_map| that has a URL and size matching that in |web_app_info|, as
-// well as adding any new images from |size_map| that have no URL.
-void UpdateWebAppIconsWithoutChangingLinks(
-    const std::map<int, BitmapAndSource>& size_map,
-    WebApplicationInfo* web_app_info);
+// and could potentially create a never ending sync cycle. If |is_for_sync| is
+// true then icon links won't be changed.
+void FilterAndResizeIconsGenerateMissing(WebApplicationInfo* web_app_info,
+                                         const IconsMap* icons_map,
+                                         bool is_for_sync);
 
 // Record an app banner added to homescreen event to ensure banners are not
 // shown for this app.
diff --git a/chrome/browser/web_applications/components/web_app_provider_base.h b/chrome/browser/web_applications/components/web_app_provider_base.h
index 9f4d003..6ea004a 100644
--- a/chrome/browser/web_applications/components/web_app_provider_base.h
+++ b/chrome/browser/web_applications/components/web_app_provider_base.h
@@ -35,9 +35,7 @@
   virtual PendingAppManager& pending_app_manager() = 0;
   // Clients can use WebAppPolicyManager to request updates of policy installed
   // Web Apps.
-  // TODO(crbug.com/916381): Make a reference once WebAppPolicyManager is always
-  // present. It's currently only present for Bookmark Apps.
-  virtual WebAppPolicyManager* policy_manager() = 0;
+  virtual WebAppPolicyManager& policy_manager() = 0;
 
   virtual WebAppUiManager& ui_manager() = 0;
 
diff --git a/chrome/browser/web_applications/components/web_app_tab_helper_base.cc b/chrome/browser/web_applications/components/web_app_tab_helper_base.cc
index c5d8150..dd68e9a 100644
--- a/chrome/browser/web_applications/components/web_app_tab_helper_base.cc
+++ b/chrome/browser/web_applications/components/web_app_tab_helper_base.cc
@@ -131,11 +131,7 @@
       Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
   DCHECK(provider);
 
-  // WebAppPolicyManager might be nullptr in the non-extensions implementation.
-  if (!provider->policy_manager())
-    return;
-
-  provider->policy_manager()->ReinstallPlaceholderAppIfNecessary(url);
+  provider->policy_manager().ReinstallPlaceholderAppIfNecessary(url);
 }
 
 AppId WebAppTabHelperBase::FindAppIdWithUrlInScope(const GURL& url) const {
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 a96ed34..3fb06959 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
@@ -166,20 +166,6 @@
                                std::move(callback));
 }
 
-bool BookmarkAppInstallFinalizer::CanReparentTab(const web_app::AppId& app_id,
-                                                 bool shortcut_created) const {
-  const Extension* app = GetExtensionById(app_id);
-  // Reparent the web contents into its own window only if that is the
-  // app's launch type.
-  if (!app ||
-      extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
-                                app) != extensions::LAUNCH_TYPE_WINDOW) {
-    return false;
-  }
-
-  return InstallFinalizer::CanReparentTab(app_id, shortcut_created);
-}
-
 bool BookmarkAppInstallFinalizer::CanRevealAppShim() const {
 #if defined(OS_MACOSX)
   return true;
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 12ea1a7..e4de680 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
@@ -43,8 +43,6 @@
   void CreateOsShortcuts(const web_app::AppId& app_id,
                          bool add_to_desktop,
                          CreateOsShortcutsCallback callback) override;
-  bool CanReparentTab(const web_app::AppId& app_id,
-                      bool shortcut_created) const override;
   bool CanRevealAppShim() const override;
   void RevealAppShim(const web_app::AppId& app_id) override;
   bool CanSkipAppUpdateForSync(
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index 9b00269..f088d32 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -121,13 +121,6 @@
       base::BindOnce(std::move(callback), false /* shortcuts_created */));
 }
 
-bool WebAppInstallFinalizer::CanReparentTab(const AppId& app_id,
-                                            bool shortcut_created) const {
-  // TODO(loyso): Return false here if the app's launch container is not
-  // kWindow.
-  return InstallFinalizer::CanReparentTab(app_id, shortcut_created);
-}
-
 bool WebAppInstallFinalizer::CanRevealAppShim() const {
   // TODO(loyso): Implement it.
   NOTIMPLEMENTED();
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h
index 3d3a5768..83fff05 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.h
+++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -35,8 +35,6 @@
   void CreateOsShortcuts(const AppId& app_id,
                          bool add_to_desktop,
                          CreateOsShortcutsCallback callback) override;
-  bool CanReparentTab(const AppId& app_id,
-                      bool shortcut_created) const override;
   bool CanRevealAppShim() const override;
   void RevealAppShim(const AppId& app_id) override;
   bool CanSkipAppUpdateForSync(
diff --git a/chrome/browser/web_applications/web_app_install_task.cc b/chrome/browser/web_applications/web_app_install_task.cc
index 53fecee2..768d691 100644
--- a/chrome/browser/web_applications/web_app_install_task.cc
+++ b/chrome/browser/web_applications/web_app_install_task.cc
@@ -117,10 +117,9 @@
   DCHECK(AreWebAppsUserInstallable(profile_));
   CheckInstallPreconditions();
 
-  std::vector<BitmapAndSource> square_icons;
-  FilterSquareIconsFromInfo(*web_application_info, &square_icons);
-  ResizeDownloadedIconsGenerateMissing(std::move(square_icons),
-                                       web_application_info.get());
+  FilterAndResizeIconsGenerateMissing(web_application_info.get(),
+                                      /*icons_map*/ nullptr,
+                                      /*is_for_sync*/ false);
 
   install_source_ = install_source;
   background_installation_ = true;
@@ -395,28 +394,10 @@
 
   DCHECK(web_app_info);
 
-  std::vector<BitmapAndSource> downloaded_icons;
-  FilterSquareIconsFromMap(icons_map, &downloaded_icons);
-
-  // Ensure that all icons that are in web_app_info are present, by generating
-  // icons for any sizes which have failed to download. This ensures that the
-  // created manifest for the web app does not contain links to icons
-  // which are not actually created and linked on disk.
-
-  // Ensure that all icon widths in the web app info icon array are present in
-  // the sizes to generate set. This ensures that we will have all of the
-  // icon sizes from when the app was originally added, even if icon URLs are
-  // no longer accessible.
-  std::set<int> sizes_to_generate = SizesToGenerate();
-  for (const auto& icon : web_app_info->icons)
-    sizes_to_generate.insert(icon.width);
-
-  web_app_info->generated_icon_color = SK_ColorTRANSPARENT;
-  std::map<int, BitmapAndSource> size_map = ResizeIconsAndGenerateMissing(
-      downloaded_icons, sizes_to_generate, web_app_info->app_url,
-      &web_app_info->generated_icon_color);
-
-  UpdateWebAppIconsWithoutChangingLinks(size_map, web_app_info.get());
+  // 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);
 
   InstallFinalizer::FinalizeOptions options;
   options.install_source = install_source_;
@@ -437,12 +418,8 @@
 
   DCHECK(web_app_info);
 
-  std::vector<BitmapAndSource> downloaded_icons;
-  FilterSquareIconsFromMap(icons_map, &downloaded_icons);
-  FilterSquareIconsFromInfo(*web_app_info, &downloaded_icons);
-
-  ResizeDownloadedIconsGenerateMissing(std::move(downloaded_icons),
-                                       web_app_info.get());
+  FilterAndResizeIconsGenerateMissing(web_app_info.get(), &icons_map,
+                                      /*is_for_sync*/ false);
 
   if (background_installation_) {
     DCHECK(!dialog_callback_);
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index d957a5b10..8f461fae 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -91,9 +91,9 @@
   return *pending_app_manager_;
 }
 
-WebAppPolicyManager* WebAppProvider::policy_manager() {
+WebAppPolicyManager& WebAppProvider::policy_manager() {
   CheckIsConnected();
-  return web_app_policy_manager_.get();
+  return *web_app_policy_manager_;
 }
 
 WebAppUiManager& WebAppProvider::ui_manager() {
@@ -117,45 +117,42 @@
 
 void WebAppProvider::CreateCommonSubsystems(Profile* profile) {
   audio_focus_id_map_ = std::make_unique<WebAppAudioFocusIdMap>();
+  ui_manager_ = WebAppUiManager::Create(profile);
   install_manager_ = std::make_unique<WebAppInstallManager>(profile);
   pending_app_manager_ = std::make_unique<PendingAppManagerImpl>(profile);
+  external_web_app_manager_ = std::make_unique<ExternalWebAppManager>(profile);
   system_web_app_manager_ = std::make_unique<SystemWebAppManager>(profile);
-  ui_manager_ = WebAppUiManager::Create(profile);
+  web_app_policy_manager_ = std::make_unique<WebAppPolicyManager>(profile);
 }
 
 void WebAppProvider::CreateWebAppsSubsystems(Profile* profile) {
   database_factory_ = std::make_unique<WebAppDatabaseFactory>(profile);
   database_ = std::make_unique<WebAppDatabase>(database_factory_.get());
   registrar_ = std::make_unique<WebAppRegistrar>(profile, database_.get());
+  sync_manager_ = std::make_unique<WebAppSyncManager>();
   icon_manager_ = std::make_unique<WebAppIconManager>(
       profile, std::make_unique<FileUtilsWrapper>());
   install_finalizer_ =
       std::make_unique<WebAppInstallFinalizer>(icon_manager_.get());
-  sync_manager_ = std::make_unique<WebAppSyncManager>();
 }
 
 void WebAppProvider::CreateBookmarkAppsSubsystems(Profile* profile) {
   registrar_ = std::make_unique<extensions::BookmarkAppRegistrar>(profile);
   install_finalizer_ =
       std::make_unique<extensions::BookmarkAppInstallFinalizer>(profile);
-  external_web_app_manager_ = std::make_unique<ExternalWebAppManager>(profile);
-  web_app_policy_manager_ = std::make_unique<WebAppPolicyManager>(profile);
 }
 
 void WebAppProvider::ConnectSubsystems() {
   DCHECK(!started_);
 
-  install_manager_->SetSubsystems(registrar_.get(), install_finalizer_.get());
   install_finalizer_->SetSubsystems(registrar_.get(), ui_manager_.get());
+  install_manager_->SetSubsystems(registrar_.get(), install_finalizer_.get());
   pending_app_manager_->SetSubsystems(registrar_.get(), ui_manager_.get(),
                                       install_finalizer_.get());
+  external_web_app_manager_->SetSubsystems(pending_app_manager_.get());
   system_web_app_manager_->SetSubsystems(pending_app_manager_.get(),
                                          registrar_.get(), ui_manager_.get());
-
-  if (!base::FeatureList::IsEnabled(features::kDesktopPWAsWithoutExtensions)) {
-    external_web_app_manager_->SetSubsystems(pending_app_manager_.get());
-    web_app_policy_manager_->SetSubsystems(pending_app_manager_.get());
-  }
+  web_app_policy_manager_->SetSubsystems(pending_app_manager_.get());
 
   connected_ = true;
 }
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
index a24e981..b2fecf9 100644
--- a/chrome/browser/web_applications/web_app_provider.h
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -29,25 +29,21 @@
 namespace web_app {
 
 // Forward declarations of generalized interfaces.
-class AppRegistrar;
 class ExternalWebAppManager;
 class InstallFinalizer;
-class PendingAppManager;
 class SystemWebAppManager;
 class WebAppAudioFocusIdMap;
 class WebAppInstallManager;
+class WebAppPolicyManager;
 class WebAppTabHelperBase;
 class WebAppUiManager;
 
 // Forward declarations for new extension-independent subsystems.
-class WebAppDatabase;
 class WebAppDatabaseFactory;
+class WebAppDatabase;
 class WebAppIconManager;
 class WebAppSyncManager;
 
-// Forward declarations for legacy extension-based subsystems.
-class WebAppPolicyManager;
-
 // Connects Web App features, such as the installation of default and
 // policy-managed web apps, with Profiles (as WebAppProvider is a
 // Profile-linked KeyedService) and their associated PrefService.
@@ -73,7 +69,7 @@
   AppRegistrar& registrar() override;
   InstallManager& install_manager() override;
   PendingAppManager& pending_app_manager() override;
-  WebAppPolicyManager* policy_manager() override;
+  WebAppPolicyManager& policy_manager() override;
   WebAppUiManager& ui_manager() override;
 
   WebAppDatabaseFactory& database_factory() { return *database_factory_; }
@@ -112,23 +108,21 @@
   void CheckIsConnected() const;
 
   // New extension-independent subsystems:
-  std::unique_ptr<WebAppAudioFocusIdMap> audio_focus_id_map_;
   std::unique_ptr<WebAppDatabaseFactory> database_factory_;
   std::unique_ptr<WebAppDatabase> database_;
   std::unique_ptr<WebAppIconManager> icon_manager_;
   std::unique_ptr<WebAppSyncManager> sync_manager_;
-  std::unique_ptr<WebAppUiManager> ui_manager_;
 
-  // New generalized subsystems:
+  // Generalized subsystems:
   std::unique_ptr<AppRegistrar> registrar_;
-  std::unique_ptr<InstallFinalizer> install_finalizer_;
-  std::unique_ptr<WebAppInstallManager> install_manager_;
-  std::unique_ptr<PendingAppManager> pending_app_manager_;
   std::unique_ptr<ExternalWebAppManager> external_web_app_manager_;
+  std::unique_ptr<InstallFinalizer> install_finalizer_;
+  std::unique_ptr<PendingAppManager> pending_app_manager_;
   std::unique_ptr<SystemWebAppManager> system_web_app_manager_;
-
-  // Legacy extension-based subsystems:
+  std::unique_ptr<WebAppAudioFocusIdMap> audio_focus_id_map_;
+  std::unique_ptr<WebAppInstallManager> install_manager_;
   std::unique_ptr<WebAppPolicyManager> web_app_policy_manager_;
+  std::unique_ptr<WebAppUiManager> ui_manager_;
 
   base::OneShotEvent on_registry_ready_;
 
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc
index fe29f6e..22bc74f8 100644
--- a/chrome/browser/web_applications/web_app_registrar.cc
+++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -139,7 +139,7 @@
 
 LaunchContainer WebAppRegistrar::GetAppLaunchContainer(
     const AppId& app_id) const {
-  NOTIMPLEMENTED();
+  // TODO(loyso): Implement it.
   return LaunchContainer::kTab;
 }
 
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 85f13e4..0ca0ae3 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -185,12 +185,6 @@
 const base::Feature kCrostiniAdvancedAccessControls{
     "CrostiniAdvancedAccessControls", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables an uninstall option in the right-click menu of Crostini (Linux)
-// applications.
-// TODO(crbug.com/955797): Remove this flag entirely.
-const base::Feature kCrostiniAppUninstallGui{"CrostiniAppUninstallGui",
-                                             base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables infrastructure for applying Ansible playbook to default Crostini
 // container.
 const base::Feature kCrostiniAnsibleInfrastructure{
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 2d43a972..658e230 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -114,8 +114,6 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kCrostiniAdvancedAccessControls;
 COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kCrostiniAppUninstallGui;
-COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kCrostiniAnsibleInfrastructure;
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kCupsPrintersUiOverhaul;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index d243f99..04a5a129 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1406,7 +1406,6 @@
         "//chrome/test/data/webui:browser_tests_js_mojo_lite_webui",
         "//chrome/test/data/webui:browser_tests_js_webui",
       ]
-      data += [ "$root_gen_dir/chrome/test/data/webui/test_util.m.js" ]
     }
 
     if (!is_fuchsia) {
@@ -1881,6 +1880,7 @@
         "../browser/ui/views/status_bubble_views_browsertest_mac.mm",
         "../browser/ui/views/sync/inline_login_ui_browsertest.cc",
         "../browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc",
+        "../browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc",
         "../browser/ui/views/task_manager_view_browsertest.cc",
         "../browser/ui/views/toolbar/browser_actions_container_browsertest.cc",
         "../browser/ui/views/translate/translate_bubble_test_utils_views.cc",
@@ -5158,6 +5158,11 @@
         "//chromeos/services/device_sync",
         "//chromeos/services/device_sync:test_support",
       ]
+    } else {
+      sources += [
+        "../browser/first_run/scoped_relaunch_chrome_browser_override.cc",
+        "../browser/first_run/scoped_relaunch_chrome_browser_override.h",
+      ]
     }
   }
 
@@ -5417,7 +5422,6 @@
 
     if (include_js_tests) {
       deps += [ "//chrome/test/data/webui:interactive_ui_tests_js_webui" ]
-      data += [ "$root_gen_dir/chrome/test/data/webui/test_util.m.js" ]
     }
 
     # Runtime dependencies
diff --git a/chrome/test/base/chrome_test_launcher.cc b/chrome/test/base/chrome_test_launcher.cc
index 17b0bcb..ad45e43b 100644
--- a/chrome/test/base/chrome_test_launcher.cc
+++ b/chrome/test/base/chrome_test_launcher.cc
@@ -65,6 +65,12 @@
 #include "chrome/installer/util/firewall_manager_win.h"
 #endif
 
+#if defined(OS_WIN) || defined(OS_MACOSX) || \
+    (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+#include "chrome/browser/first_run/scoped_relaunch_chrome_browser_override.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#endif
+
 ChromeTestSuiteRunner::ChromeTestSuiteRunner() {}
 ChromeTestSuiteRunner::~ChromeTestSuiteRunner() {}
 
@@ -242,5 +248,19 @@
       base::Bind(&ash::mojo_test_interface_factory::RegisterInterfaces));
 #endif
 
+#if defined(OS_WIN) || defined(OS_MACOSX) || \
+    (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+  // Cause a test failure for any test that triggers an unexpected relaunch.
+  // Tests that fail here should likely be restructured to put the "before
+  // relaunch" code into a PRE_ test with its own
+  // ScopedRelaunchChromeBrowserOverride and the "after relaunch" code into the
+  // normal non-PRE_ test.
+  upgrade_util::ScopedRelaunchChromeBrowserOverride fail_on_relaunch(
+      base::BindRepeating([](const base::CommandLine&) {
+        ADD_FAILURE() << "Unexpected call to RelaunchChromeBrowser";
+        return false;
+      }));
+#endif
+
   return content::LaunchTests(delegate, parallel_jobs, argc, argv);
 }
diff --git a/chrome/test/base/js2gtest.gni b/chrome/test/base/js2gtest.gni
index e8e95aa..fcd5c7d 100644
--- a/chrome/test/base/js2gtest.gni
+++ b/chrome/test/base/js2gtest.gni
@@ -15,6 +15,7 @@
 #       in the tests and therefore considered input to the C++ generation step.
 #   extra_js_files: List of javascript files needed by the test at runtime,
 #       typically listed in the extraLibraries member of the test fixture.
+#   data
 #   defines
 #   deps
 #   visibility
@@ -31,6 +32,11 @@
 
   gen_source_pattern = "{{source_gen_dir}}/{{source_name_part}}-gen.cc"
 
+  data = []
+  if (defined(invoker.data)) {
+    data += invoker.data
+  }
+
   action_foreach(action_name) {
     testonly = true
     visibility = [ ":$source_set_name" ]
@@ -64,7 +70,7 @@
       copied_source_pattern,
       gen_source_pattern,
     ]
-    data = process_file_template(sources, [ copied_source_pattern ])
+    data += process_file_template(sources, [ copied_source_pattern ])
 
     args = []
     if (defined(invoker.deps_js)) {
@@ -135,7 +141,7 @@
       deps += invoker.deps
     }
     if (defined(invoker.gen_include_files)) {
-      data = invoker.gen_include_files
+      data += invoker.gen_include_files
     }
     if (defined(invoker.extra_js_files)) {
       data_deps = [
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index b21cb18..cb4124ae 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -175,7 +175,7 @@
 
 #if defined(OS_CHROMEOS) || defined(OS_MACOSX) || defined(OS_WIN) || \
     defined(OS_LINUX)
-  void ShowHatsBubbleFromAppMenuButton(const std::string& site_id) override {}
+  void ShowHatsBubble(const std::string& site_id) override {}
 #endif
 
   void ExecuteExtensionCommand(const extensions::Extension* extension,
diff --git a/chrome/test/data/local_ntp/local_ntp_browsertest.html b/chrome/test/data/local_ntp/local_ntp_browsertest.html
index 33ec7bef..e1e2a96 100644
--- a/chrome/test/data/local_ntp/local_ntp_browsertest.html
+++ b/chrome/test/data/local_ntp/local_ntp_browsertest.html
@@ -220,7 +220,7 @@
           <div id="backgrounds-upload-wrapper" class="bg-sel-tile-wrapper">
             <div id="backgrounds-upload" class="bg-sel-tile-bg" tabindex="-1"
                 role="button" aria-label="$i18n{uploadImage}"
-                title="$i18n{uploadImage}">
+                title="$i18n{uploadImage}" aria-pressed="false">
               <div id="backgrounds-upload-icon" class="bg-sel-tile">
                 <div id="backgrounds-upload-arrow"></div>
                 <div id="backgrounds-upload-text">$i18n{uploadImage}</div>
@@ -230,7 +230,7 @@
           <div id="backgrounds-default-wrapper" class="bg-sel-tile-wrapper">
             <div id="backgrounds-default" class="bg-sel-tile-bg" tabindex="-1"
                 role="button" aria-label="$i18n{noBackground}"
-                title="$i18n{noBackground}">
+                title="$i18n{noBackground}" aria-pressed="false">
               <div id="backgrounds-default-icon" class="bg-sel-tile">
                 <div class="mini-page">
                   <div class="mini-header-colorful"></div>
@@ -319,7 +319,8 @@
           <div id="color-picker-wrapper" class="bg-sel-tile-wrapper">
             <div id="color-picker-container" class="bg-sel-tile-bg"
                 aria-label="$i18n{colorPickerLabel}"
-                title="$i18n{colorPickerLabel}"  tabindex="-1">
+                title="$i18n{colorPickerLabel}"  tabindex="-1"
+                role="button" aria-pressed="false">
               <div id="color-picker-tile" class="bg-sel-tile">
                 <div id="left-semicircle"></div>
                 <div id="color-picker-icon"></div>
@@ -331,7 +332,8 @@
           <div id="colors-default-wrapper" class="bg-sel-tile-wrapper">
             <div id="colors-default" class="bg-sel-tile-bg"
                 aria-label="$i18n{defaultThemeLabel}"
-                title="$i18n{defaultThemeLabel}" tabindex="-1">
+                title="$i18n{defaultThemeLabel}" tabindex="-1"
+                role="button" aria-pressed="false">
               <div id="colors-default-icon" class="bg-sel-tile"></div>
             </div>
           </div>
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 04822565..5b00dae 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -30,7 +30,10 @@
     ":modulize",
     "//chrome/browser/ui",
   ]
-
+  data = [
+    "$root_gen_dir/chrome/test/data/webui/test_browser_proxy.m.js",
+    "$root_gen_dir/chrome/test/data/webui/test_util.m.js",
+  ]
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 }
 
@@ -147,6 +150,10 @@
     "//chrome/browser/ui",
     "//skia",
   ]
+  data = [
+    "$root_gen_dir/chrome/test/data/webui/test_browser_proxy.m.js",
+    "$root_gen_dir/chrome/test/data/webui/test_util.m.js",
+  ]
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 }
 
@@ -224,5 +231,8 @@
 }
 
 js_modulizer("modulize") {
-  input_files = [ "test_util.js" ]
+  input_files = [
+    "test_browser_proxy.js",
+    "test_util.js",
+  ]
 }
diff --git a/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js b/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
index d34a792..ec16b9e 100644
--- a/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
@@ -202,13 +202,13 @@
   /** @type {NetworkingPrivate} */
   let api_;
 
-  /** @type {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
+  /** @type {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
   let mojoApi_;
 
   suiteSetup(function() {
     api_ = new chrome.FakeNetworkingPrivate();
     mojoApi_ = new FakeNetworkConfig(api_);
-    network_config.MojoInterfaceProviderImpl.getInstance().proxy_ = mojoApi_;
+    network_config.MojoInterfaceProviderImpl.getInstance().remote_ = mojoApi_;
   });
 
   setup(function() {
@@ -411,7 +411,7 @@
   /** @type {?NetworkingPrivate} */
   let api_;
 
-  /** @type {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
+  /** @type {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
   let mojoApi_;
 
   /** @type {!Array<crOnc.NetworkStateProperties>} networks */
@@ -438,7 +438,7 @@
   suiteSetup(function() {
     api_ = new chrome.FakeNetworkingPrivate();
     mojoApi_ = new FakeNetworkConfig(api_);
-    network_config.MojoInterfaceProviderImpl.getInstance().proxy_ = mojoApi_;
+    network_config.MojoInterfaceProviderImpl.getInstance().remote_ = mojoApi_;
   });
 
   setup(function() {
diff --git a/chrome/test/data/webui/settings/chromeos/internet_page_tests.js b/chrome/test/data/webui/settings/chromeos/internet_page_tests.js
index d1e8d41..49a19d92 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/internet_page_tests.js
@@ -12,7 +12,7 @@
   /** @type {?NetworkingPrivate} */
   let api_;
 
-  /** @type {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
+  /** @type {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
   let mojoApi_;
 
   suiteSetup(function() {
@@ -47,7 +47,7 @@
 
     api_ = new chrome.FakeNetworkingPrivate();
     mojoApi_ = new FakeNetworkConfig(api_);
-    network_config.MojoInterfaceProviderImpl.getInstance().proxy_ = mojoApi_;
+    network_config.MojoInterfaceProviderImpl.getInstance().remote_ = mojoApi_;
 
     // Disable animations so sub-pages open within one event loop.
     testing.Test.disableAnimationsAndTransitions();
diff --git a/chrome/test/data/webui/settings/chromeos/os_people_page_test.js b/chrome/test/data/webui/settings/chromeos/os_people_page_test.js
index 4eaf7d8..01e04e2 100644
--- a/chrome/test/data/webui/settings/chromeos/os_people_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_people_page_test.js
@@ -116,13 +116,19 @@
       assertEquals(
           browserProxy.fakeProfileInfo.name,
           peoplePage.$$('#profile-name').textContent.trim());
+      const bg = peoplePage.$$('#profile-icon').style.backgroundImage;
+      assertTrue(bg.includes(browserProxy.fakeProfileInfo.iconUrl));
 
+      const iconDataUrl = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEA' +
+          'LAAAAAABAAEAAAICTAEAOw==';
       cr.webUIListenerCallback(
-          'profile-info-changed', {name: 'pushedName', iconUrl: ''});
+          'profile-info-changed', {name: 'pushedName', iconUrl: iconDataUrl});
 
       Polymer.dom.flush();
       assertEquals(
           'pushedName', peoplePage.$$('#profile-name').textContent.trim());
+      const newBg = peoplePage.$$('#profile-icon').style.backgroundImage;
+      assertTrue(newBg.includes(iconDataUrl));
 
       // Rather than trying to mock cr.sendWithPromise('getPluralString', ...)
       // just force an update.
diff --git a/chrome/test/data/webui/settings/people_page_test.js b/chrome/test/data/webui/settings/people_page_test.js
index 24b74506..a3a82c0 100644
--- a/chrome/test/data/webui/settings/people_page_test.js
+++ b/chrome/test/data/webui/settings/people_page_test.js
@@ -585,4 +585,61 @@
       assertEquals(settings.getCurrentRoute(), settings.routes.SYNC);
     });
   });
+
+  if (cr.isChromeOS) {
+    suite('Chrome OS with SplitSettings', function() {
+      /** @type {SettingsPeoplePageElement} */
+      let peoplePage = null;
+      /** @type {settings.SyncBrowserProxy} */
+      let browserProxy = null;
+      /** @type {settings.ProfileInfoBrowserProxy} */
+      let profileInfoBrowserProxy = null;
+
+      suiteSetup(function() {
+        loadTimeData.overrideValues({
+          // Simulate SplitSettings (OS settings in their own surface).
+          showOSSettings: false,
+        });
+      });
+
+      setup(async function() {
+        browserProxy = new TestSyncBrowserProxy();
+        settings.SyncBrowserProxyImpl.instance_ = browserProxy;
+
+        profileInfoBrowserProxy = new TestProfileInfoBrowserProxy();
+        settings.ProfileInfoBrowserProxyImpl.instance_ =
+            profileInfoBrowserProxy;
+
+        PolymerTest.clearBody();
+        peoplePage = document.createElement('settings-people-page');
+        peoplePage.pageVisibility = settings.pageVisibility;
+        document.body.appendChild(peoplePage);
+
+        Polymer.dom.flush();
+        await browserProxy.whenCalled('getSyncStatus');
+      });
+
+      teardown(function() {
+        peoplePage.remove();
+      });
+
+      test('clicking profile row does not open change picture page', () => {
+        // Simulate a signed-in user.
+        sync_test_util.simulateSyncStatus({
+          signedIn: true,
+        });
+
+        // Profile row items aren't actionable.
+        const profileIcon = assert(peoplePage.$$('#profile-icon'));
+        assertFalse(profileIcon.hasAttribute('actionable'));
+        const subpageArrow = assert(peoplePage.$$('#profile-subpage-arrow'));
+        assertFalse(subpageArrow.hasAttribute('actionable'));
+
+        // Clicking on profile icon doesn't navigate to a new route.
+        const oldRoute = settings.getCurrentRoute();
+        profileIcon.click();
+        assertEquals(oldRoute, settings.getCurrentRoute());
+      });
+    });
+  }
 });
diff --git a/chrome/test/data/webui/test_browser_proxy.js b/chrome/test/data/webui/test_browser_proxy.js
index a20a8cec..442a0be 100644
--- a/chrome/test/data/webui/test_browser_proxy.js
+++ b/chrome/test/data/webui/test_browser_proxy.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// #import {assert} from 'chrome://resources/js/assert.m.js';
+// #import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
+
 /**
  * @typedef {{resolver: !PromiseResolver,
  *            callCount: number}}
@@ -36,7 +39,7 @@
  * });
  * --------------------------------------------------------------------------
  */
-class TestBrowserProxy {
+/* #export */ class TestBrowserProxy {
   /**
    * @param {!Array<string>} methodNames Names of all methods whose calls
    *     need to be tracked.
diff --git a/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc b/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc
index 5f5cefb..33d47d5 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc
@@ -9,7 +9,9 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
+#include "base/time/default_clock.h"
 #include "chromeos/components/multidevice/logging/logging.h"
+#include "chromeos/services/device_sync/async_execution_time_metrics_logger.h"
 #include "chromeos/services/device_sync/cryptauth_client.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_constants.h"
 #include "chromeos/services/device_sync/cryptauth_key_creator_impl.h"
@@ -45,13 +47,15 @@
     cryptauthv2::EnrollKeysResponse::EnrollSingleKeyResponse;
 
 // Timeout values for asynchronous operations.
-// TODO(https://crbug.com/933656): Tune these values.
+// TODO(https://crbug.com/933656): Use async execution time metrics to tune
+// these timeout values. For now, set these timeouts to the max execution time
+// recorded by the metrics.
 constexpr base::TimeDelta kWaitingForSyncKeysResponseTimeout =
-    base::TimeDelta::FromSeconds(10);
+    kMaxAsyncExecutionTime;
 constexpr base::TimeDelta kWaitingForKeyCreationTimeout =
-    base::TimeDelta::FromSeconds(10);
+    kMaxAsyncExecutionTime;
 constexpr base::TimeDelta kWaitingForEnrollKeysResponseTimeout =
-    base::TimeDelta::FromSeconds(10);
+    kMaxAsyncExecutionTime;
 
 CryptAuthEnrollmentResult::ResultCode SyncKeysNetworkRequestErrorToResultCode(
     NetworkRequestError error) {
@@ -311,6 +315,21 @@
   return base::nullopt;
 }
 
+void RecordSyncKeysMetrics(const base::TimeDelta& execution_time) {
+  LogAsyncExecutionTimeMetric("CryptAuth.EnrollmentV2.ExecutionTime.SyncKeys",
+                              execution_time);
+}
+
+void RecordKeyCreationMetrics(const base::TimeDelta& execution_time) {
+  LogAsyncExecutionTimeMetric(
+      "CryptAuth.EnrollmentV2.ExecutionTime.KeyCreation", execution_time);
+}
+
+void RecordEnrollKeysMetrics(const base::TimeDelta& execution_time) {
+  LogAsyncExecutionTimeMetric("CryptAuth.EnrollmentV2.ExecutionTime.EnrollKeys",
+                              execution_time);
+}
+
 }  // namespace
 
 // static
@@ -373,7 +392,7 @@
 
 // static
 base::Optional<CryptAuthEnrollmentResult::ResultCode>
-CryptAuthV2EnrollerImpl::ResultCodeErrorFromState(State state) {
+CryptAuthV2EnrollerImpl::ResultCodeErrorFromTimeoutDuringState(State state) {
   switch (state) {
     case State::kWaitingForSyncKeysResponse:
       return CryptAuthEnrollmentResult::ResultCode::
@@ -413,22 +432,42 @@
 
   PA_LOG(INFO) << "Transitioning from " << state_ << " to " << state;
   state_ = state;
+  last_state_change_timestamp_ = base::DefaultClock::GetInstance()->Now();
 
   base::Optional<base::TimeDelta> timeout_for_state = GetTimeoutForState(state);
   if (!timeout_for_state)
     return;
 
-  base::Optional<CryptAuthEnrollmentResult::ResultCode> error_code =
-      ResultCodeErrorFromState(state);
+  // TODO(https://crbug.com/936273): Add metrics to track failure rates due
+  // to async timeouts.
+  timer_->Start(FROM_HERE, *timeout_for_state,
+                base::BindOnce(&CryptAuthV2EnrollerImpl::OnTimeout,
+                               base::Unretained(this)));
+}
 
+void CryptAuthV2EnrollerImpl::OnTimeout() {
   // If there's a timeout specified, there should be a corresponding error code.
+  base::Optional<CryptAuthEnrollmentResult::ResultCode> error_code =
+      ResultCodeErrorFromTimeoutDuringState(state_);
   DCHECK(error_code);
 
-  // TODO(https://crbug.com/936273): Add metrics to track failure rates due to
-  // async timeouts.
-  timer_->Start(FROM_HERE, *timeout_for_state,
-                base::BindOnce(&CryptAuthV2EnrollerImpl::FinishAttempt,
-                               base::Unretained(this), *error_code));
+  base::TimeDelta execution_time =
+      base::DefaultClock::GetInstance()->Now() - last_state_change_timestamp_;
+  switch (state_) {
+    case State::kWaitingForSyncKeysResponse:
+      RecordSyncKeysMetrics(execution_time);
+      break;
+    case State::kWaitingForKeyCreation:
+      RecordKeyCreationMetrics(execution_time);
+      break;
+    case State::kWaitingForEnrollKeysResponse:
+      RecordEnrollKeysMetrics(execution_time);
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  FinishAttempt(*error_code);
 }
 
 SyncKeysRequest CryptAuthV2EnrollerImpl::BuildSyncKeysRequest(
@@ -502,6 +541,9 @@
     const SyncKeysResponse& response) {
   DCHECK(state_ == State::kWaitingForSyncKeysResponse);
 
+  RecordSyncKeysMetrics(base::DefaultClock::GetInstance()->Now() -
+                        last_state_change_timestamp_);
+
   if (response.server_status() == SyncKeysResponse::SERVER_OVERLOADED) {
     FinishAttempt(
         CryptAuthEnrollmentResult::ResultCode::kErrorCryptAuthServerOverloaded);
@@ -677,6 +719,9 @@
 }
 
 void CryptAuthV2EnrollerImpl::OnSyncKeysFailure(NetworkRequestError error) {
+  RecordSyncKeysMetrics(base::DefaultClock::GetInstance()->Now() -
+                        last_state_change_timestamp_);
+
   FinishAttempt(SyncKeysNetworkRequestErrorToResultCode(error));
 }
 
@@ -688,6 +733,9 @@
     const base::Optional<CryptAuthKey>& client_ephemeral_dh) {
   DCHECK(state_ == State::kWaitingForKeyCreation);
 
+  RecordKeyCreationMetrics(base::DefaultClock::GetInstance()->Now() -
+                           last_state_change_timestamp_);
+
   EnrollKeysRequest request;
   request.set_random_session_id(session_id);
   if (client_ephemeral_dh)
@@ -743,6 +791,9 @@
     const EnrollKeysResponse& response) {
   DCHECK(state_ == State::kWaitingForEnrollKeysResponse);
 
+  RecordEnrollKeysMetrics(base::DefaultClock::GetInstance()->Now() -
+                          last_state_change_timestamp_);
+
   for (const std::pair<CryptAuthKeyBundle::Name, CryptAuthKey>& new_key :
        new_keys) {
     key_registry_->AddKey(new_key.first, new_key.second);
@@ -758,6 +809,9 @@
 }
 
 void CryptAuthV2EnrollerImpl::OnEnrollKeysFailure(NetworkRequestError error) {
+  RecordEnrollKeysMetrics(base::DefaultClock::GetInstance()->Now() -
+                          last_state_change_timestamp_);
+
   FinishAttempt(EnrollKeysNetworkRequestErrorToResultCode(error));
 }
 
diff --git a/chromeos/services/device_sync/cryptauth_v2_enroller_impl.h b/chromeos/services/device_sync/cryptauth_v2_enroller_impl.h
index 2fcc0c7d..46e88a672 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enroller_impl.h
+++ b/chromeos/services/device_sync/cryptauth_v2_enroller_impl.h
@@ -72,7 +72,7 @@
 
   static base::Optional<base::TimeDelta> GetTimeoutForState(State state);
   static base::Optional<CryptAuthEnrollmentResult::ResultCode>
-  ResultCodeErrorFromState(State state);
+  ResultCodeErrorFromTimeoutDuringState(State state);
 
   // CryptAuthV2Enroller:
   void OnAttemptStarted(
@@ -91,6 +91,7 @@
                           std::unique_ptr<base::OneShotTimer> timer);
 
   void SetState(State state);
+  void OnTimeout();
 
   // Constructs a SyncKeysRequest with information about every key bundle
   // contained in CryptAuthKeyBundle::AllEnrollableNames().
@@ -158,6 +159,9 @@
 
   State state_ = State::kNotStarted;
 
+  // The time of the last state change. Used for execution time metrics.
+  base::Time last_state_change_timestamp_;
+
   // The new ClientDirective from SyncKeysResponse. This value is stored in the
   // CryptAuthEnrollmentResult which is passed to the
   // EnrollmentAttemptFinishedCallback. It should be passed as null if a failure
diff --git a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
index f1bdfc05..98c57a8 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
@@ -14,6 +14,7 @@
 #include "base/timer/timer.h"
 #include "base/values.h"
 #include "chromeos/components/multidevice/logging/logging.h"
+#include "chromeos/services/device_sync/async_execution_time_metrics_logger.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_constants.h"
 #include "chromeos/services/device_sync/cryptauth_key_registry.h"
 #include "chromeos/services/device_sync/cryptauth_v2_enroller_impl.h"
@@ -30,11 +31,13 @@
 namespace {
 
 // Timeout values for asynchronous operations.
-// TODO(https://crbug.com/933656): Tune these values.
+// TODO(https://crbug.com/933656): Use async execution time metrics to tune
+// these timeout values. For now, set these timeouts to the max execution time
+// recorded by the metrics.
 constexpr base::TimeDelta kWaitingForGcmRegistrationTimeout =
-    base::TimeDelta::FromSeconds(10);
+    kMaxAsyncExecutionTime;
 constexpr base::TimeDelta kWaitingForClientAppMetadataTimeout =
-    base::TimeDelta::FromSeconds(10);
+    kMaxAsyncExecutionTime;
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
@@ -119,6 +122,18 @@
                                 result.result_code());
 }
 
+void RecordGcmRegistrationMetrics(const base::TimeDelta& execution_time) {
+  LogAsyncExecutionTimeMetric(
+      "CryptAuth.EnrollmentV2.ExecutionTime.GcmRegistration", execution_time);
+}
+
+void RecordClientAppMetadataFetchMetrics(
+    const base::TimeDelta& execution_time) {
+  LogAsyncExecutionTimeMetric(
+      "CryptAuth.EnrollmentV2.ExecutionTime.ClientAppMetadataFetch",
+      execution_time);
+}
+
 }  // namespace
 
 // static
@@ -168,7 +183,8 @@
 
 // static
 base::Optional<CryptAuthEnrollmentResult::ResultCode>
-CryptAuthV2EnrollmentManagerImpl::ResultCodeErrorFromState(State state) {
+CryptAuthV2EnrollmentManagerImpl::ResultCodeErrorFromTimeoutDuringState(
+    State state) {
   switch (state) {
     case State::kWaitingForGcmRegistration:
       return CryptAuthEnrollmentResult::ResultCode::
@@ -352,6 +368,8 @@
   if (state_ != State::kWaitingForGcmRegistration)
     return;
 
+  RecordGcmRegistrationMetrics(clock_->Now() - last_state_change_timestamp_);
+
   if (!success || gcm_manager_->GetRegistrationId().empty()) {
     OnEnrollmentFinished(CryptAuthEnrollmentResult(
         CryptAuthEnrollmentResult::ResultCode::kErrorGcmRegistrationFailed,
@@ -372,6 +390,9 @@
     const base::Optional<cryptauthv2::ClientAppMetadata>& client_app_metadata) {
   DCHECK(state_ == State::kWaitingForClientAppMetadata);
 
+  RecordClientAppMetadataFetchMetrics(clock_->Now() -
+                                      last_state_change_timestamp_);
+
   if (!client_app_metadata) {
     OnEnrollmentFinished(
         CryptAuthEnrollmentResult(CryptAuthEnrollmentResult::ResultCode::
@@ -464,26 +485,39 @@
 
   PA_LOG(INFO) << "Transitioning from " << state_ << " to " << state;
   state_ = state;
+  last_state_change_timestamp_ = clock_->Now();
 
   base::Optional<base::TimeDelta> timeout_for_state = GetTimeoutForState(state);
   if (!timeout_for_state)
     return;
 
-  base::Optional<CryptAuthEnrollmentResult::ResultCode> error_code =
-      ResultCodeErrorFromState(state);
-
-  // If there's a timeout specified, there should be a corresponding error
-  // code.
-  DCHECK(error_code);
-
   // TODO(https://crbug.com/936273): Add metrics to track failure rates due to
   // async timeouts.
-  timer_->Start(
-      FROM_HERE, *timeout_for_state,
-      base::BindOnce(&CryptAuthV2EnrollmentManagerImpl::OnEnrollmentFinished,
-                     callback_weak_ptr_factory_.GetWeakPtr(),
-                     CryptAuthEnrollmentResult(
-                         *error_code, base::nullopt /*client_directive */)));
+  timer_->Start(FROM_HERE, *timeout_for_state,
+                base::BindOnce(&CryptAuthV2EnrollmentManagerImpl::OnTimeout,
+                               callback_weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthV2EnrollmentManagerImpl::OnTimeout() {
+  // If there's a timeout specified, there should be a corresponding error code.
+  base::Optional<CryptAuthEnrollmentResult::ResultCode> error_code =
+      ResultCodeErrorFromTimeoutDuringState(state_);
+  DCHECK(error_code);
+
+  base::TimeDelta execution_time = clock_->Now() - last_state_change_timestamp_;
+  switch (state_) {
+    case State::kWaitingForGcmRegistration:
+      RecordGcmRegistrationMetrics(execution_time);
+      break;
+    case State::kWaitingForClientAppMetadata:
+      RecordClientAppMetadataFetchMetrics(execution_time);
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  OnEnrollmentFinished(CryptAuthEnrollmentResult(
+      *error_code, base::nullopt /*client_directive */));
 }
 
 std::string CryptAuthV2EnrollmentManagerImpl::GetV1UserPublicKey() const {
diff --git a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h
index 93306e1..58dcdfe 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h
+++ b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h
@@ -111,7 +111,7 @@
 
   static base::Optional<base::TimeDelta> GetTimeoutForState(State state);
   static base::Optional<CryptAuthEnrollmentResult::ResultCode>
-  ResultCodeErrorFromState(State state);
+  ResultCodeErrorFromTimeoutDuringState(State state);
 
   // CryptAuthEnrollmentManager:
   void Start() override;
@@ -150,6 +150,7 @@
   void OnEnrollmentFinished(const CryptAuthEnrollmentResult& enrollment_result);
 
   void SetState(State state);
+  void OnTimeout();
 
   std::string GetV1UserPublicKey() const;
   std::string GetV1UserPrivateKey() const;
@@ -168,7 +169,12 @@
   std::unique_ptr<base::OneShotTimer> timer_;
 
   bool initial_v1_and_v2_user_key_pairs_disagree_ = false;
+
   State state_ = State::kIdle;
+
+  // The time of the last state change. Used for execution time metrics.
+  base::Time last_state_change_timestamp_;
+
   base::Optional<cryptauthv2::ClientMetadata> current_client_metadata_;
   base::Optional<cryptauthv2::PolicyReference>
       client_directive_policy_reference_;
diff --git a/chromeos/services/ime/public/cpp/BUILD.gn b/chromeos/services/ime/public/cpp/BUILD.gn
index 63a22aed..a3d6981a 100644
--- a/chromeos/services/ime/public/cpp/BUILD.gn
+++ b/chromeos/services/ime/public/cpp/BUILD.gn
@@ -116,6 +116,7 @@
 
   deps = [
     "//base",
+    "//chromeos/services/ime/public/cpp:buildflags",
     "//chromeos/services/ime/public/mojom",
     "//chromeos/strings",
     "//services/service_manager/public/cpp",
diff --git a/chromeos/services/ime/public/cpp/manifest.cc b/chromeos/services/ime/public/cpp/manifest.cc
index fbd03ea..3c6fb60 100644
--- a/chromeos/services/ime/public/cpp/manifest.cc
+++ b/chromeos/services/ime/public/cpp/manifest.cc
@@ -5,6 +5,8 @@
 #include "chromeos/services/ime/public/cpp/manifest.h"
 
 #include "base/no_destructor.h"
+#include "build/buildflag.h"
+#include "chromeos/services/ime/public/cpp/buildflags.h"
 #include "chromeos/services/ime/public/mojom/constants.mojom.h"
 #include "chromeos/services/ime/public/mojom/input_engine.mojom.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
@@ -13,6 +15,16 @@
 namespace chromeos {
 namespace ime {
 
+namespace {
+
+#if BUILDFLAG(ENABLE_CROS_IME_DECODER)
+const char kImeServiceSandboxType[] = "ime";
+#else
+const char kImeServiceSandboxType[] = "utility";
+#endif
+
+}  // namespace
+
 const service_manager::Manifest& GetManifest() {
   static base::NoDestructor<service_manager::Manifest> manifest{
       service_manager::ManifestBuilder()
@@ -22,7 +34,7 @@
               service_manager::ManifestOptionsBuilder()
                   .WithExecutionMode(service_manager::Manifest::ExecutionMode::
                                          kOutOfProcessBuiltin)
-                  .WithSandboxType("utility")
+                  .WithSandboxType(kImeServiceSandboxType)
                   .WithInstanceSharingPolicy(
                       service_manager::Manifest::InstanceSharingPolicy::
                           kSharedAcrossGroups)
diff --git a/chromeos/services/network_config/public/mojom/BUILD.gn b/chromeos/services/network_config/public/mojom/BUILD.gn
index 69198972..f64b33b 100644
--- a/chromeos/services/network_config/public/mojom/BUILD.gn
+++ b/chromeos/services/network_config/public/mojom/BUILD.gn
@@ -13,7 +13,4 @@
   deps = [
     "//services/network/public/mojom:mojom_ip_address",
   ]
-
-  # TODO(https://crbug.com/968369): Change to use new names.
-  use_old_js_lite_bindings_names = true
 }
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager.cc b/components/autofill/core/browser/payments/credit_card_save_manager.cc
index 7549e89..05b70b7 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager.cc
@@ -354,9 +354,7 @@
   if (result == AutofillClient::SUCCESS) {
     // Do *not* call payments_client_->Prepare() here. We shouldn't send
     // credentials until the user has explicitly accepted a prompt to upload.
-    if (base::FeatureList::IsEnabled(
-            features::kAutofillDoNotUploadSaveUnsupportedCards) &&
-        !supported_card_bin_ranges.empty() &&
+    if (!supported_card_bin_ranges.empty() &&
         !payments::IsCreditCardNumberSupported(upload_request_.card.number(),
                                                supported_card_bin_ranges)) {
       // Attempt local card save if card not already saved.
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
index 2a6a856e..9b3e87b 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
@@ -797,8 +797,6 @@
 // upload failed because of unsupported bin and falling back to local save.
 TEST_F(CreditCardSaveManagerTest,
        Local_UnsupportedCard_SaveCreditCardOptions_WithNonFocusableField) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillDoNotUploadSaveUnsupportedCards);
   credit_card_save_manager_->SetCreditCardUploadEnabled(true);
   // Set supported bin range so that the used card is unsupported.
   std::vector<std::pair<int, int>> supported_card_bin_ranges{
@@ -968,8 +966,6 @@
 // when upload failed because of unsupported bin and falling back to local save.
 TEST_F(CreditCardSaveManagerTest,
        Local_UnsupportedCard_SaveCreditCardOptions_WithDynamicForms) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillDoNotUploadSaveUnsupportedCards);
   credit_card_save_manager_->SetCreditCardUploadEnabled(true);
   // Set supported bin range so that the used card is unsupported.
   std::vector<std::pair<int, int>> supported_card_bin_ranges{
@@ -5090,8 +5086,6 @@
 // Tests that if a card doesn't fall in any of the supported bin ranges, local
 // save is offered rather than upload save.
 TEST_F(CreditCardSaveManagerTest, UploadSaveNotOfferedForUnsupportedCard) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillDoNotUploadSaveUnsupportedCards);
   std::vector<std::pair<int, int>> supported_card_bin_ranges{
       std::make_pair(4111, 4113), std::make_pair(34, 34),
       std::make_pair(300, 305)};
@@ -5118,8 +5112,6 @@
 // Tests that if a card doesn't fall in any of the supported bin ranges, but is
 // already saved, then local save is not offered.
 TEST_F(CreditCardSaveManagerTest, LocalSaveNotOfferedForSavedUnsupportedCard) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillDoNotUploadSaveUnsupportedCards);
   std::vector<std::pair<int, int>> supported_card_bin_ranges{
       std::make_pair(4111, 4113), std::make_pair(34, 34),
       std::make_pair(300, 305)};
@@ -5152,8 +5144,6 @@
 // Tests that if a card falls in one of the supported bin ranges, upload save
 // is offered.
 TEST_F(CreditCardSaveManagerTest, UploadSaveOfferedForSupportedCard) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillDoNotUploadSaveUnsupportedCards);
   // Set supported BIN ranges.
   std::vector<std::pair<int, int>> supported_card_bin_ranges{
       std::make_pair(4111, 4113)};
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc
index 1ee46bd..72a9f33 100644
--- a/components/autofill/core/common/autofill_payments_features.cc
+++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -52,10 +52,6 @@
     "AutofillDoNotMigrateUnsupportedLocalCards",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kAutofillDoNotUploadSaveUnsupportedCards{
-    "AutofillDoNotUploadSaveUnsupportedCards",
-    base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Controls whether the credit card downstream keyboard accessory shows
 // the Google Pay logo animation on iOS.
 const base::Feature kAutofillDownstreamUseGooglePayBrandingOniOS{
diff --git a/components/autofill/core/common/autofill_payments_features.h b/components/autofill/core/common/autofill_payments_features.h
index c803e4d..b0f0e28 100644
--- a/components/autofill/core/common/autofill_payments_features.h
+++ b/components/autofill/core/common/autofill_payments_features.h
@@ -23,7 +23,6 @@
 extern const base::Feature kAutofillCreditCardAuthentication;
 extern const base::Feature kAutofillCreditCardUploadFeedback;
 extern const base::Feature kAutofillDoNotMigrateUnsupportedLocalCards;
-extern const base::Feature kAutofillDoNotUploadSaveUnsupportedCards;
 extern const base::Feature kAutofillDownstreamUseGooglePayBrandingOniOS;
 extern const base::Feature kAutofillEnableLocalCardMigrationForNonSyncUser;
 extern const base::Feature kAutofillEnableToolbarStatusChip;
diff --git a/components/gwp_asan/BUILD.gn b/components/gwp_asan/BUILD.gn
index 7e6cc7a..47299f8 100644
--- a/components/gwp_asan/BUILD.gn
+++ b/components/gwp_asan/BUILD.gn
@@ -14,7 +14,7 @@
     "//components/gwp_asan/common:unit_tests",
     "//testing/gtest",
   ]
-  if (is_win || is_mac || is_linux) {
+  if (is_win || is_mac || is_linux || is_android) {
     deps += [
       "//components/gwp_asan/client:unit_tests",
       "//components/gwp_asan/crash_handler:unit_tests",
diff --git a/components/gwp_asan/buildflags/buildflags.gni b/components/gwp_asan/buildflags/buildflags.gni
index 9fe77972..08ed1d4d 100644
--- a/components/gwp_asan/buildflags/buildflags.gni
+++ b/components/gwp_asan/buildflags/buildflags.gni
@@ -6,7 +6,9 @@
 import("//build/config/allocator.gni")
 
 # Windows/x86 is disabled due to https://crbug.com/969146
-supported_platform = is_mac || (is_win && current_cpu == "x64")
+# Android component builds are disabled due to https://crbug.com/976399
+supported_platform = is_mac || (is_win && current_cpu == "x64") ||
+                     (is_android && !is_component_build)
 
 declare_args() {
   # Is GWP-ASan malloc/PartitionAlloc hooking enabled for chrome/ on a given
diff --git a/components/previews/content/previews_optimization_guide.cc b/components/previews/content/previews_optimization_guide.cc
index 49a8a9ed..3702ecb 100644
--- a/components/previews/content/previews_optimization_guide.cc
+++ b/components/previews/content/previews_optimization_guide.cc
@@ -343,9 +343,6 @@
   // If the client is eligible to fetch hints, currently controlled by a feature
   // flag |kOptimizationHintsFetching|, fetch hints from the remote Optimization
   // Guide Service.
-  //
-  // TODO(mcrouse): Add a check if Infobar notification needs to be shown to the
-  // user.
   if (!data_reduction_proxy::DataReductionProxySettings::
           IsDataSaverEnabledByUser(pref_service_)) {
     return;
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index 112a813..f2ffbf5 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -19,7 +19,7 @@
 // (OOP-D).
 // TODO(dnicoara): Look at enabling Chromecast support when ChromeOS support is
 // ready.
-#if defined(IS_CHROMECAST)
+#if defined(IS_CHROMECAST) && !defined(OS_ANDROID)
 const base::Feature kVizDisplayCompositor{"VizDisplayCompositor",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 #else
diff --git a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
index 6f22d98..984eae39 100644
--- a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
+++ b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
@@ -1095,6 +1095,261 @@
 }
 
 IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest,
+                       MoveEndpointByUnitParagraphPreservedWhiteSpace) {
+  LoadInitialAccessibilityTreeFromHtml(
+      R"HTML(<!DOCTYPE html>
+      <html>
+      <body>
+        <div>start</div>
+        <span style='white-space: pre'>
+          First Paragraph
+          Second Paragraph
+        </span>
+        <!--
+          Intentional nesting to test that ancestor positions can
+          resolve to the newline at the start of preserved whitespace.
+        -->
+        <div>
+          <div style='white-space: pre-line'>
+            Third Paragraph
+            Fourth Paragraph
+          </div>
+        </div>
+        <div style='white-space: pre-wrap; width: 10em;'>
+          Fifth               Paragraph
+          Sixth               Paragraph
+        </div>
+        <div style='white-space: break-spaces; width: 10em;'>
+          Seventh             Paragraph
+          Eighth              Paragraph
+        </div>
+        <div>end</div>
+      </body>
+      </html>)HTML");
+  BrowserAccessibility* start_node =
+      FindNode(ax::mojom::Role::kStaticText, "start");
+  ASSERT_NE(nullptr, start_node);
+  BrowserAccessibility* end_node =
+      FindNode(ax::mojom::Role::kStaticText, "end");
+  ASSERT_NE(nullptr, end_node);
+
+  ComPtr<ITextRangeProvider> text_range_provider;
+
+  std::vector<base::string16> paragraphs = {
+      L"start\n",
+      L"          First Paragraph\n",
+      L"          Second Paragraph\n        \n",
+      L"Third Paragraph\n",
+      L"Fourth Paragraph\n\n          ",
+      L"Fifth               Paragraph\n          ",
+      L"Sixth               Paragraph\n        \n          ",
+      L"Seventh             Paragraph\n          ",
+      L"Eighth              Paragraph\n        ",
+      L"end",
+  };
+
+  // FORWARD NAVIGATION
+  GetTextRangeProviderFromTextNode(*start_node, &text_range_provider);
+  ASSERT_NE(nullptr, text_range_provider.Get());
+  EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"start");
+
+  // The first paragraph extends beyond the end of the "start" node, because
+  // the preserved whitespace node begins with a line break, so
+  // move once to capture that.
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ paragraphs[0].c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ (paragraphs[0] + paragraphs[1]).c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ paragraphs[1].c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ (paragraphs[1] + paragraphs[2]).c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ paragraphs[2].c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ (paragraphs[2] + paragraphs[3]).c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ paragraphs[3].c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ (paragraphs[3] + paragraphs[4]).c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ paragraphs[4].c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ (paragraphs[4] + paragraphs[5]).c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ paragraphs[5].c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ (paragraphs[5] + paragraphs[6]).c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ paragraphs[6].c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ (paragraphs[6] + paragraphs[7]).c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ paragraphs[7].c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ (paragraphs[7] + paragraphs[8]).c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ paragraphs[8].c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ (paragraphs[8] + paragraphs[9]).c_str(),
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ paragraphs[9].c_str(),
+      /*expected_count*/ 1);
+
+  // REVERSE NAVIGATION
+  GetTextRangeProviderFromTextNode(*end_node, &text_range_provider);
+  ASSERT_NE(nullptr, text_range_provider.Get());
+  EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"end");
+
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ (paragraphs[8] + paragraphs[9]).c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ paragraphs[8].c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ (paragraphs[7] + paragraphs[8]).c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ paragraphs[7].c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ (paragraphs[6] + paragraphs[7]).c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ paragraphs[6].c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ (paragraphs[5] + paragraphs[6]).c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ paragraphs[5].c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ (paragraphs[4] + paragraphs[5]).c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ paragraphs[4].c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ (paragraphs[3] + paragraphs[4]).c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ paragraphs[3].c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ (paragraphs[2] + paragraphs[3]).c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ paragraphs[2].c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ (paragraphs[1] + paragraphs[2]).c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ paragraphs[1].c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ (paragraphs[0] + paragraphs[1]).c_str(),
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ paragraphs[0].c_str(),
+      /*expected_count*/ -1);
+}
+
+IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest,
                        IFrameTraversal) {
   LoadInitialAccessibilityTreeFromUrl(embedded_test_server()->GetURL(
       "/accessibility/html/iframe-cross-process.html"));
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index c0b9d53a..3cb42ef 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -4,6 +4,10 @@
 
 #include "content/browser/browser_interface_binders.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/browser/worker_host/dedicated_worker_host.h"
+#include "content/browser/worker_host/shared_worker_host.h"
+#include "content/browser/worker_host/shared_worker_instance.h"
 #include "third_party/blink/public/mojom/webaudio/audio_context_manager.mojom.h"
 
 namespace content {
@@ -13,20 +17,66 @@
                           service_manager::BinderMap* map) {
   map->Add<blink::mojom::AudioContextManager>(base::BindRepeating(
       &RenderFrameHostImpl::GetAudioContextManager, base::Unretained(rfhi)));
+
+  map->Add<blink::mojom::FileSystemManager>(base::BindRepeating(
+      &RenderFrameHostImpl::GetFileSystemManager, base::Unretained(rfhi)));
 }
 
+void PopulateBinderMapWithContext(
+    RenderFrameHostImpl* rfhi,
+    service_manager::BinderMapWithContext<RenderFrameHost*>* map) {}
+
 void PopulateBinderMap(RenderFrameHostImpl* rfhi,
                        service_manager::BinderMap* map) {
   PopulateFrameBinders(rfhi, map);
 }
 
+RenderFrameHost* GetContextForHost(RenderFrameHostImpl* rfhi) {
+  return rfhi;
+}
+
 void PopulateDedicatedWorkerBinders(DedicatedWorkerHost* dwh,
                                     service_manager::BinderMap* map) {}
 
+const url::Origin& GetContextForHost(DedicatedWorkerHost* dwh) {
+  return dwh->GetOrigin();
+}
+
+void PopulateBinderMapWithContext(
+    DedicatedWorkerHost* dwh,
+    service_manager::BinderMapWithContext<const url::Origin&>* map) {
+  RenderProcessHostImpl* rphi =
+      static_cast<RenderProcessHostImpl*>(dwh->GetProcessHost());
+  // TODO(https://crbug.com/873661): Pass origin to FileSystemManager.
+  map->Add<blink::mojom::FileSystemManager>(base::BindRepeating(
+      &RenderProcessHostImpl::BindFileSystemManager, base::Unretained(rphi)));
+}
+
 void PopulateBinderMap(DedicatedWorkerHost* dwh,
                        service_manager::BinderMap* map) {
   PopulateDedicatedWorkerBinders(dwh, map);
 }
 
+url::Origin GetContextForHost(SharedWorkerHost* swh) {
+  return url::Origin::Create(swh->instance()->url());
+}
+
+void PopulateSharedWorkerBinders(SharedWorkerHost* swh,
+                                 service_manager::BinderMap* map) {}
+
+void PopulateBinderMapWithContext(
+    SharedWorkerHost* swh,
+    service_manager::BinderMapWithContext<const url::Origin&>* map) {
+  RenderProcessHostImpl* rphi =
+      static_cast<RenderProcessHostImpl*>(swh->GetProcessHost());
+  // TODO(https://crbug.com/873661): Pass origin to FileSystemManager.
+  map->Add<blink::mojom::FileSystemManager>(base::BindRepeating(
+      &RenderProcessHostImpl::BindFileSystemManager, base::Unretained(rphi)));
+}
+
+void PopulateBinderMap(SharedWorkerHost* swh, service_manager::BinderMap* map) {
+  PopulateSharedWorkerBinders(swh, map);
+}
+
 }  // namespace internal
 }  // namespace content
\ No newline at end of file
diff --git a/content/browser/browser_interface_binders.h b/content/browser/browser_interface_binders.h
index 58b7e5b..a6476c1a 100644
--- a/content/browser/browser_interface_binders.h
+++ b/content/browser/browser_interface_binders.h
@@ -6,11 +6,14 @@
 #define CONTENT_BROWSER_BROWSER_INTERFACE_BINDERS_H_
 
 #include "services/service_manager/public/cpp/binder_map.h"
+#include "url/origin.h"
 
 namespace content {
 
+class RenderFrameHost;
 class RenderFrameHostImpl;
 class DedicatedWorkerHost;
+class SharedWorkerHost;
 
 namespace internal {
 
@@ -26,10 +29,25 @@
 // Registers the handlers for interfaces requested by frames.
 void PopulateBinderMap(RenderFrameHostImpl* rfhi,
                        service_manager::BinderMap* map);
+void PopulateBinderMapWithContext(
+    RenderFrameHostImpl* rfhi,
+    service_manager::BinderMapWithContext<RenderFrameHost*>* map);
+RenderFrameHost* GetContextForHost(RenderFrameHostImpl* rfhi);
 
 // Registers the handlers for interfaces requested by dedicated workers.
 void PopulateBinderMap(DedicatedWorkerHost* dwh,
                        service_manager::BinderMap* map);
+void PopulateBinderMapWithContext(
+    DedicatedWorkerHost* dwh,
+    service_manager::BinderMapWithContext<const url::Origin&>* map);
+const url::Origin& GetContextForHost(DedicatedWorkerHost* dwh);
+
+// Registers the handlers for interfaces requested by shared workers.
+void PopulateBinderMap(SharedWorkerHost* swh, service_manager::BinderMap* map);
+void PopulateBinderMapWithContext(
+    SharedWorkerHost* swh,
+    service_manager::BinderMapWithContext<const url::Origin&>* map);
+url::Origin GetContextForHost(SharedWorkerHost* swh);
 
 }  // namespace internal
 }  // namespace content
diff --git a/content/browser/browser_interface_broker_impl.h b/content/browser/browser_interface_broker_impl.h
index 80312040..664c600c 100644
--- a/content/browser/browser_interface_broker_impl.h
+++ b/content/browser/browser_interface_broker_impl.h
@@ -16,11 +16,12 @@
 // internal::PopulateBinderMap).
 // Note: this mechanism will eventually replace the usage of InterfaceProvider
 // and browser manifests, as well as DocumentInterfaceBroker.
-template <typename ExecutionContextHost>
+template <typename ExecutionContextHost, typename InterfaceBinderContext>
 class BrowserInterfaceBrokerImpl : public blink::mojom::BrowserInterfaceBroker {
  public:
   BrowserInterfaceBrokerImpl(ExecutionContextHost* host) : host_(host) {
     internal::PopulateBinderMap(host, &binder_map_);
+    internal::PopulateBinderMapWithContext(host, &binder_map_with_context);
   }
 
   // blink::mojom::BrowserInterfaceBroker
@@ -28,12 +29,17 @@
     DCHECK(receiver.interface_name().has_value());
     auto interface_name = receiver.interface_name().value();
     auto pipe = receiver.PassPipe();
-    binder_map_.TryBind(interface_name, &pipe);
+    if (!binder_map_.TryBind(interface_name, &pipe)) {
+      binder_map_with_context.TryBind(internal::GetContextForHost(host_),
+                                      interface_name, &pipe);
+    }
   }
 
  private:
   ExecutionContextHost* const host_;
   service_manager::BinderMap binder_map_;
+  service_manager::BinderMapWithContext<InterfaceBinderContext>
+      binder_map_with_context;
 
   DISALLOW_COPY_AND_ASSIGN(BrowserInterfaceBrokerImpl);
 };
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index beda6d4..02bdeb9 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -13,6 +13,7 @@
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
 #include "base/rand_util.h"
@@ -3113,6 +3114,9 @@
                         render_frame_host()->GetProcess()->IsReady());
   UMA_HISTOGRAM_ENUMERATION("Navigation.CommitTimeout.Scheme",
                             GetScheme(common_params_->url));
+  UMA_HISTOGRAM_BOOLEAN("Navigation.CommitTimeout.IsMainFrame",
+                        frame_tree_node_->IsMainFrame());
+  base::UmaHistogramSparse("Navigation.CommitTimeout.ErrorCode", -net_error_);
   render_process_blocked_state_changed_subscription_.reset();
   render_frame_host()->GetRenderWidgetHost()->RendererIsUnresponsive(
       base::BindRepeating(&NavigationRequest::RestartCommitTimeout,
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 2b1e992..269bffc6 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -396,7 +396,7 @@
 
 base::Optional<url::Origin> GetOriginForURLLoaderFactoryUnchecked(
     NavigationRequest* navigation_request) {
-  // Return a safe unique origin when there is no |navigation_request| (e.g.
+  // Return a safe opaque origin when there is no |navigation_request| (e.g.
   // when RFHI::CommitNavigation is called via RFHI::NavigateToInterstitialURL).
   if (!navigation_request)
     return url::Origin();
@@ -422,14 +422,14 @@
     CHECK(navigation_request->browser_initiated());
 
     // loadDataWithBaseUrl submits a data: |common_params.url| (which has a
-    // unique origin), but commits that URL as if it came from
+    // opaque origin), but commits that URL as if it came from
     // |common_params.base_url_for_data_url|.  See also
     // https://crbug.com/976253.
     return url::Origin::Create(common_params.base_url_for_data_url);
   }
 
-  // MHTML frames should commit as unique origin (and should not be able to make
-  // network requests on behalf of the real origin).
+  // MHTML frames should commit as a opaque origin (and should not be able to
+  // make network requests on behalf of the real origin).
   //
   // TODO(lukasza): Cover MHTML main frames here.
   if (navigation_request->IsForMhtmlSubframe())
@@ -4339,10 +4339,6 @@
       GetProcess()->GetID(),
       GetProcess()->GetStoragePartition()->GetFileSystemContext(),
       ChromeBlobStorageContext::GetFor(GetProcess()->GetBrowserContext())));
-  registry_->AddInterface(
-      base::BindRepeating(&FileSystemManagerImpl::BindRequest,
-                          base::Unretained(file_system_manager_.get())),
-      base::CreateSingleThreadTaskRunner({BrowserThread::IO}));
 
   registry_->AddInterface(base::BindRepeating(
       &BackgroundFetchServiceImpl::CreateForFrame, GetProcess(), routing_id_));
@@ -4407,37 +4403,88 @@
   network_service_connection_error_handler_holder_.reset();
 }
 
-bool RenderFrameHostImpl::CanCommitOrigin(
+RenderFrameHostImpl::CanCommitStatus RenderFrameHostImpl::CanCommitOriginAndUrl(
     const url::Origin& origin,
     const GURL& url) {
   // If the --disable-web-security flag is specified, all bets are off and the
   // renderer process can send any origin it wishes.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kDisableWebSecurity)) {
-    return true;
+    return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
   }
 
-  // It is safe to commit into a unique origin, regardless of the URL, as it is
+  // Renderer-debug URLs can never be committed.
+  if (IsRendererDebugURL(url))
+    return CanCommitStatus::CANNOT_COMMIT_URL;
+
+  // TODO(creis): We should also check for WebUI pages here.  Also, when the
+  // out-of-process iframes implementation is ready, we should check for
+  // cross-site URLs that are not allowed to commit in this process.
+
+  // MHTML subframes can supply URLs at commit time that do not match the
+  // process lock. For example, it can be either "cid:..." or arbitrary URL at
+  // which the frame was at the time of generating the MHTML
+  // (e.g. "http://localhost"). In such cases, don't verify the URL, but require
+  // the URL to commit in the process of the main frame.
+  if (!frame_tree_node()->IsMainFrame()) {
+    RenderFrameHostImpl* main_frame =
+        frame_tree_node()->frame_tree()->GetMainFrame();
+    if (main_frame->is_mhtml_document()) {
+      if (IsSameSiteInstance(main_frame))
+        return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
+
+      // If an MHTML subframe commits in a different process (even one that
+      // appears correct for the subframe's URL), then we aren't correctly
+      // loading it from the archive and should kill the renderer.
+      base::debug::SetCrashKeyString(
+          base::debug::AllocateCrashKeyString(
+              "oopif_in_mhtml_page", base::debug::CrashKeySize::Size32),
+          is_mhtml_document() ? "is_mhtml_doc" : "not_mhtml_doc");
+      return CanCommitStatus::CANNOT_COMMIT_URL;
+    }
+  }
+
+  // Give the client a chance to disallow URLs from committing.
+  if (!GetContentClient()->browser()->CanCommitURL(GetProcess(), url))
+    return CanCommitStatus::CANNOT_COMMIT_URL;
+
+  // TODO(nasko): This check should be updated to apply to all URLs, not just
+  // standard ones.
+  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
+  if (url.IsStandard() &&
+      !policy->CanAccessDataForOrigin(GetProcess()->GetID(), url)) {
+    return CanCommitStatus::CANNOT_COMMIT_URL;
+  }
+
+  // It is safe to commit into a opaque origin, regardless of the URL, as it is
   // restricted from accessing other origins.
   if (origin.opaque())
-    return true;
+    return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
 
   // Standard URLs must match the reported origin.
   if (url.IsStandard() && !origin.IsSameOriginWith(url::Origin::Create(url)))
-    return false;
+    return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
 
-  // A non-unique origin must be a valid URL, which allows us to safely do a
+  // A non-opaque origin must be a valid URL, which allows us to safely do a
   // conversion to GURL.
   GURL origin_url = origin.GetURL();
 
-  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
   if (!policy->CanAccessDataForOrigin(GetProcess()->GetID(), origin))
-    return false;
+    return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
 
   // Verify that the origin is allowed to commit in this process.
   // Note: This also handles non-standard cases for |url|, such as
   // about:blank, data, and blob URLs.
-  return CanCommitURL(origin_url);
+
+  // Renderer-debug URLs can never be committed.
+  if (IsRendererDebugURL(origin_url))
+    return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
+
+  // Give the client a chance to disallow URLs from committing.
+  if (!GetContentClient()->browser()->CanCommitURL(GetProcess(), origin_url))
+    return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
+
+  return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
 }
 
 void RenderFrameHostImpl::NavigateToInterstitialURL(const GURL& data_url) {
@@ -5521,53 +5568,6 @@
   Send(new FrameMsg_ClearFocusedElement(GetRoutingID()));
 }
 
-bool RenderFrameHostImpl::CanCommitURL(const GURL& url) {
-  // Renderer-debug URLs can never be committed.
-  if (IsRendererDebugURL(url))
-    return false;
-
-  // TODO(creis): We should also check for WebUI pages here.  Also, when the
-  // out-of-process iframes implementation is ready, we should check for
-  // cross-site URLs that are not allowed to commit in this process.
-
-  // MHTML subframes can supply URLs at commit time that do not match the
-  // process lock. For example, it can be either "cid:..." or arbitrary URL at
-  // which the frame was at the time of generating the MHTML
-  // (e.g. "http://localhost"). In such cases, don't verify the URL, but require
-  // the URL to commit in the process of the main frame.
-  if (!frame_tree_node()->IsMainFrame()) {
-    RenderFrameHostImpl* main_frame =
-        frame_tree_node()->frame_tree()->GetMainFrame();
-    if (main_frame->is_mhtml_document()) {
-      if (IsSameSiteInstance(main_frame))
-        return true;
-
-      // If an MHTML subframe commits in a different process (even one that
-      // appears correct for the subframe's URL), then we aren't correctly
-      // loading it from the archive and should kill the renderer.
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "oopif_in_mhtml_page", base::debug::CrashKeySize::Size32),
-          is_mhtml_document() ? "is_mhtml_doc" : "not_mhtml_doc");
-      return false;
-    }
-  }
-
-  // Give the client a chance to disallow URLs from committing.
-  if (!GetContentClient()->browser()->CanCommitURL(GetProcess(), url))
-    return false;
-
-  // TODO(nasko): This check should be updated to apply to all URLs, not just
-  // standard ones.
-  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  if (url.IsStandard() &&
-      !policy->CanAccessDataForOrigin(GetProcess()->GetID(), url)) {
-    return false;
-  }
-
-  return true;
-}
-
 void RenderFrameHostImpl::BlockRequestsForFrame() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
@@ -6233,6 +6233,15 @@
   AudioContextManagerImpl::Create(this, std::move(receiver));
 }
 
+void RenderFrameHostImpl::GetFileSystemManager(
+    mojo::PendingReceiver<blink::mojom::FileSystemManager> receiver) {
+  // This is safe because file_system_manager_ is deleted on the IO thread
+  base::PostTask(FROM_HERE, {BrowserThread::IO},
+                 base::BindOnce(&FileSystemManagerImpl::BindRequest,
+                                base::Unretained(file_system_manager_.get()),
+                                std::move(receiver)));
+}
+
 void RenderFrameHostImpl::GetAuthenticator(
     mojo::PendingReceiver<blink::mojom::Authenticator> receiver) {
 #if !defined(OS_ANDROID)
@@ -6464,8 +6473,8 @@
   RenderProcessHost* process = GetProcess();
 
   // Error pages may sometimes commit a URL in the wrong process, which requires
-  // an exception for the CanCommitURL checks.  This is ok as long as the origin
-  // is unique.
+  // an exception for the CanCommitOriginAndUrl() checks.  This is ok as long
+  // as the origin is opaque.
   bool bypass_checks_for_error_page = false;
   if (SiteIsolationPolicy::IsErrorPageIsolationEnabled(
           frame_tree_node_->IsMainFrame())) {
@@ -6514,29 +6523,30 @@
     // Attempts to commit certain off-limits URL should be caught more strictly
     // than our FilterURL checks.  If a renderer violates this policy, it
     // should be killed.
-    if (!CanCommitURL(validated_params->url)) {
-      VLOG(1) << "Blocked URL " << validated_params->url.spec();
-      LogCannotCommitUrlCrashKeys(validated_params->url,
-                                  is_same_document_navigation,
-                                  navigation_request);
+    switch (CanCommitOriginAndUrl(validated_params->origin,
+                                  validated_params->url)) {
+      case CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL:
+        // The origin and URL are safe to commit.
+        break;
+      case CanCommitStatus::CANNOT_COMMIT_URL:
+        VLOG(1) << "Blocked URL " << validated_params->url.spec();
+        LogCannotCommitUrlCrashKeys(validated_params->url,
+                                    is_same_document_navigation,
+                                    navigation_request);
 
-      // Kills the process.
-      bad_message::ReceivedBadMessage(process,
-                                      bad_message::RFH_CAN_COMMIT_URL_BLOCKED);
-      return false;
-    }
+        // Kills the process.
+        bad_message::ReceivedBadMessage(
+            process, bad_message::RFH_CAN_COMMIT_URL_BLOCKED);
+        return false;
+      case CanCommitStatus::CANNOT_COMMIT_ORIGIN:
+        DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, validated_params->origin);
+        LogCannotCommitOriginCrashKeys(is_same_document_navigation,
+                                       navigation_request);
 
-    // Verify that the origin passed from the renderer process is valid and can
-    // be allowed to commit in this RenderFrameHost.
-    if (!CanCommitOrigin(validated_params->origin, validated_params->url)) {
-      DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, validated_params->origin);
-      LogCannotCommitOriginCrashKeys(is_same_document_navigation,
-                                     navigation_request);
-
-      // Kills the process.
-      bad_message::ReceivedBadMessage(
-          process, bad_message::RFH_INVALID_ORIGIN_ON_COMMIT);
-      return false;
+        // Kills the process.
+        bad_message::ReceivedBadMessage(
+            process, bad_message::RFH_INVALID_ORIGIN_ON_COMMIT);
+        return false;
     }
   }
 
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 6cedd4c..a4fe73ed 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -769,11 +769,6 @@
 
   void ClearFocusedElement();
 
-  // Returns whether the given URL is allowed to commit in the current process.
-  // This is a more conservative check than RenderProcessHost::FilterURL, since
-  // it will be used to kill processes that commit unauthorized URLs.
-  bool CanCommitURL(const GURL& url);
-
   // Returns the PreviewsState of the last successful navigation
   // that made a network request. The PreviewsState is a bitmask of potentially
   // several Previews optimizations.
@@ -998,6 +993,9 @@
   void GetAudioContextManager(
       mojo::PendingReceiver<blink::mojom::AudioContextManager> receiver);
 
+  void GetFileSystemManager(
+      mojo::PendingReceiver<blink::mojom::FileSystemManager> receiver);
+
   // https://mikewest.github.io/corpp/#initialize-embedder-policy-for-global
   network::mojom::CrossOriginEmbedderPolicy cross_origin_embedder_policy()
       const {
@@ -1337,12 +1335,18 @@
   // relevant.
   void ResetWaitingState();
 
-  // Returns whether the given origin is allowed to commit in the current
-  // RenderFrameHost. The |url| is used to ensure it matches the origin in cases
-  // where it is applicable. This is a more conservative check than
+  // Returns whether the given origin and URL is allowed to commit in the
+  // current RenderFrameHost. The |url| is used to ensure it matches the origin
+  // in cases where it is applicable. This is a more conservative check than
   // RenderProcessHost::FilterURL, since it will be used to kill processes that
   // commit unauthorized origins.
-  bool CanCommitOrigin(const url::Origin& origin, const GURL& url);
+  enum class CanCommitStatus {
+    CAN_COMMIT_ORIGIN_AND_URL,
+    CANNOT_COMMIT_ORIGIN,
+    CANNOT_COMMIT_URL
+  };
+  CanCommitStatus CanCommitOriginAndUrl(const url::Origin& origin,
+                                        const GURL& url);
 
   // Asserts that the given RenderFrameHostImpl is part of the same browser
   // context (and crashes if not), then returns whether the given frame is
@@ -2159,7 +2163,8 @@
   // BrowserInterfaceBroker implementation through which this
   // RenderFrameHostImpl exposes document-scoped Mojo services to the currently
   // active document in the corresponding RenderFrame.
-  BrowserInterfaceBrokerImpl<RenderFrameHostImpl> broker_{this};
+  BrowserInterfaceBrokerImpl<RenderFrameHostImpl, RenderFrameHost*> broker_{
+      this};
   mojo::Receiver<blink::mojom::BrowserInterfaceBroker> broker_receiver_{
       &broker_};
 
diff --git a/content/browser/loader/prefetch_url_loader.cc b/content/browser/loader/prefetch_url_loader.cc
index 6399b98d..a10f4cc 100644
--- a/content/browser/loader/prefetch_url_loader.cc
+++ b/content/browser/loader/prefetch_url_loader.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/feature_list.h"
+#include "base/metrics/histogram_functions.h"
 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
 #include "content/browser/web_package/prefetched_signed_exchange_cache_adapter.h"
 #include "content/browser/web_package/signed_exchange_prefetch_handler.h"
@@ -15,6 +16,7 @@
 #include "services/network/loader_util.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace content {
 
@@ -53,6 +55,7 @@
           std::move(signed_exchange_prefetch_metric_recorder)),
       accept_langs_(accept_langs) {
   DCHECK(network_loader_factory_);
+  RecordPrefetchRedirectHistogram(PrefetchRedirect::kPrefetchMade);
 
   if (IsSignedExchangeHandlingEnabled()) {
     // Set the SignedExchange accept header.
@@ -80,6 +83,16 @@
 
 PrefetchURLLoader::~PrefetchURLLoader() = default;
 
+void PrefetchURLLoader::RecordPrefetchRedirectHistogram(
+    PrefetchRedirect event) {
+  // We only want to record prefetch vs prefetch redirects when we're not
+  // experimenting with a request's redirect mode.
+  if (base::FeatureList::IsEnabled(blink::features::kPrefetchRedirectError))
+    return;
+
+  base::UmaHistogramEnumeration("Prefetch.Redirect", event);
+}
+
 void PrefetchURLLoader::FollowRedirect(
     const std::vector<std::string>& removed_headers,
     const net::HttpRequestHeaders& modified_headers,
@@ -90,12 +103,17 @@
   DCHECK(!new_url) << "Redirect with modified URL was not "
                       "supported yet. crbug.com/845683";
   if (signed_exchange_prefetch_handler_) {
+    RecordPrefetchRedirectHistogram(
+        PrefetchRedirect::kPrefetchRedirectedSXGHandler);
+
     // Rebind |client_binding_| and |loader_|.
     client_binding_.Bind(signed_exchange_prefetch_handler_->FollowRedirect(
         mojo::MakeRequest(&loader_)));
     return;
   }
 
+  RecordPrefetchRedirectHistogram(PrefetchRedirect::kPrefetchRedirected);
+
   DCHECK(loader_);
   loader_->FollowRedirect(removed_headers,
                           net::HttpRequestHeaders() /* modified_headers */,
diff --git a/content/browser/loader/prefetch_url_loader.h b/content/browser/loader/prefetch_url_loader.h
index 9dedca5..9fa03c4 100644
--- a/content/browser/loader/prefetch_url_loader.h
+++ b/content/browser/loader/prefetch_url_loader.h
@@ -82,6 +82,16 @@
       const network::URLLoaderCompletionStatus& completion_status);
 
  private:
+  // This enum is used to record a histogram and should not be renumbered.
+  enum class PrefetchRedirect {
+    kPrefetchMade = 0,
+    kPrefetchRedirected = 1,
+    kPrefetchRedirectedSXGHandler = 2,
+    kMaxValue = kPrefetchRedirectedSXGHandler
+  };
+
+  void RecordPrefetchRedirectHistogram(PrefetchRedirect event);
+
   // network::mojom::URLLoader overrides:
   void FollowRedirect(const std::vector<std::string>& removed_headers,
                       const net::HttpRequestHeaders& modified_headers,
diff --git a/content/browser/network_service_instance_impl.cc b/content/browser/network_service_instance_impl.cc
index fda0186e..f3a6b96 100644
--- a/content/browser/network_service_instance_impl.cc
+++ b/content/browser/network_service_instance_impl.cc
@@ -13,6 +13,7 @@
 #include "base/deferred_sequenced_task_runner.h"
 #include "base/environment.h"
 #include "base/feature_list.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -200,7 +201,20 @@
           ->SetClient(std::move(client_ptr), CreateNetworkServiceParams());
       g_network_service_is_responding = false;
       g_network_service_ptr->QueryVersion(base::BindRepeating(
-          [](uint32_t) { g_network_service_is_responding = true; }));
+          [](base::Time start_time, uint32_t) {
+            g_network_service_is_responding = true;
+            base::TimeDelta delta = base::Time::Now() - start_time;
+            UMA_HISTOGRAM_MEDIUM_TIMES("NetworkService.TimeToFirstResponse",
+                                       delta);
+            if (g_last_network_service_crash.is_null()) {
+              UMA_HISTOGRAM_MEDIUM_TIMES(
+                  "NetworkService.TimeToFirstResponse.OnStartup", delta);
+            } else {
+              UMA_HISTOGRAM_MEDIUM_TIMES(
+                  "NetworkService.TimeToFirstResponse.AfterCrash", delta);
+            }
+          },
+          base::Time::Now()));
 
       delete g_client;  // In case we're recreating the network service.
       g_client = new NetworkServiceClient(std::move(client_request));
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 27e6b1fd..c91eb3b8 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1947,13 +1947,14 @@
 }
 
 void RenderProcessHostImpl::BindFileSystemManager(
-    blink::mojom::FileSystemManagerRequest request) {
+    const url::Origin& origin,
+    mojo::PendingReceiver<blink::mojom::FileSystemManager> receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::PostTask(
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(&FileSystemManagerImpl::BindRequest,
                      base::Unretained(file_system_manager_impl_.get()),
-                     std::move(request)));
+                     std::move(receiver)));
 }
 
 void RenderProcessHostImpl::CancelProcessShutdownDelayForUnload() {
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index b476555..496ea4b 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -480,7 +480,9 @@
 
   // Binds request to the FileSystemManager instance owned by the render process
   // host, and is used by workers via RendererInterfaceBinders.
-  void BindFileSystemManager(blink::mojom::FileSystemManagerRequest request);
+  void BindFileSystemManager(
+      const url::Origin& origin,
+      mojo::PendingReceiver<blink::mojom::FileSystemManager> receiver);
   FileSystemManagerImpl* GetFileSystemManagerForTesting() {
     return file_system_manager_impl_.get();
   }
diff --git a/content/browser/renderer_interface_binders.cc b/content/browser/renderer_interface_binders.cc
index 798e4f17..95bed38 100644
--- a/content/browser/renderer_interface_binders.cc
+++ b/content/browser/renderer_interface_binders.cc
@@ -160,7 +160,7 @@
       [](blink::mojom::FileSystemManagerRequest request,
          RenderProcessHost* host, const url::Origin& origin) {
         static_cast<RenderProcessHostImpl*>(host)->BindFileSystemManager(
-            std::move(request));
+            origin, std::move(request));
       }));
   if (base::FeatureList::IsEnabled(blink::features::kNativeFileSystemAPI)) {
     parameterized_binder_registry_.AddInterface(base::BindRepeating(
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc
index d511f80..73336cbde 100644
--- a/content/browser/webauth/authenticator_common.cc
+++ b/content/browser/webauth/authenticator_common.cc
@@ -283,21 +283,6 @@
   return base::nullopt;
 }
 
-device::CtapMakeCredentialRequest CreateCtapMakeCredentialRequest(
-    const std::string& client_data_json,
-    const blink::mojom::PublicKeyCredentialCreationOptionsPtr& options,
-    bool is_incognito) {
-  device::CtapMakeCredentialRequest make_credential_param(
-      client_data_json, options->relying_party, options->user,
-      device::PublicKeyCredentialParams(options->public_key_parameters));
-
-  make_credential_param.exclude_list = options->exclude_credentials;
-
-  make_credential_param.hmac_secret = options->hmac_create_secret;
-  make_credential_param.is_incognito_mode = is_incognito;
-  return make_credential_param;
-}
-
 // The application parameter is the SHA-256 hash of the UTF-8 encoding of
 // the application identity (i.e. relying_party_id) of the application
 // requesting the registration.
@@ -698,6 +683,19 @@
     return;
   }
 
+  base::Optional<std::string> appid_exclude;
+  if (options->appid_exclude) {
+    appid_exclude =
+        ProcessAppIdExtension(*options->appid_exclude, caller_origin);
+    if (!appid_exclude) {
+      InvokeCallbackAndCleanup(
+          std::move(callback),
+          blink::mojom::AuthenticatorStatus::INVALID_DOMAIN, nullptr,
+          Focus::kDontCheck);
+      return;
+    }
+  }
+
   if (!IsAPrioriAuthenticatedUrl(options->user.icon_url) ||
       !IsAPrioriAuthenticatedUrl(options->relying_party.icon_url)) {
     ReportSecurityCheckFailure(
@@ -808,8 +806,14 @@
       "WebAuthentication.MakeCredentialExcludeCredentialsCount",
       options->exclude_credentials.size());
 
-  ctap_make_credential_request_ = CreateCtapMakeCredentialRequest(
-      client_data_json_, options, browser_context()->IsOffTheRecord());
+  ctap_make_credential_request_ = device::CtapMakeCredentialRequest(
+      client_data_json_, options->relying_party, options->user,
+      device::PublicKeyCredentialParams(options->public_key_parameters));
+  ctap_make_credential_request_->exclude_list = options->exclude_credentials;
+  ctap_make_credential_request_->hmac_secret = options->hmac_create_secret;
+  ctap_make_credential_request_->app_id = std::move(appid_exclude);
+  ctap_make_credential_request_->is_incognito_mode =
+      browser_context()->IsOffTheRecord();
   // On dual protocol CTAP2/U2F devices, force credential creation over U2F.
   ctap_make_credential_request_->is_u2f_only =
       OriginIsCryptoTokenExtension(caller_origin_);
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 55372f1..33816cb 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -450,6 +450,26 @@
     return callback_receiver.status();
   }
 
+  AuthenticatorStatus TryRegistrationWithAppIdExclude(
+      const std::string& origin,
+      const std::string& appid_exclude) {
+    const GURL origin_url(origin);
+    NavigateAndCommit(origin_url);
+    mojo::Remote<blink::mojom::Authenticator> authenticator =
+        ConnectToAuthenticator();
+    PublicKeyCredentialCreationOptionsPtr options =
+        GetTestPublicKeyCredentialCreationOptions();
+    options->relying_party.id = origin_url.host();
+    options->appid_exclude = appid_exclude;
+
+    TestMakeCredentialCallback callback_receiver;
+    authenticator->MakeCredential(std::move(options),
+                                  callback_receiver.callback());
+    callback_receiver.WaitForCallback();
+
+    return callback_receiver.status();
+  }
+
   bool SupportsTransportProtocol(::device::FidoTransportProtocol protocol) {
     return base::Contains(
         authenticator_impl_->get_authenticator_common_for_testing()
@@ -858,6 +878,10 @@
     EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR,
               TryAuthenticationWithAppId(test_case.origin,
                                          test_case.claimed_authority));
+
+    EXPECT_EQ(AuthenticatorStatus::SUCCESS,
+              TryRegistrationWithAppIdExclude(test_case.origin,
+                                              test_case.claimed_authority));
   }
 
   // All the invalid relying party test cases should also be invalid as AppIDs.
@@ -873,6 +897,10 @@
     EXPECT_EQ(AuthenticatorStatus::INVALID_DOMAIN,
               TryAuthenticationWithAppId(test_case.origin,
                                          test_case.claimed_authority));
+
+    EXPECT_EQ(AuthenticatorStatus::INVALID_DOMAIN,
+              TryRegistrationWithAppIdExclude(test_case.origin,
+                                              test_case.claimed_authority));
   }
 }
 
@@ -1154,6 +1182,49 @@
   }
 }
 
+TEST_F(AuthenticatorImplTest, AppIdExcludeExtension) {
+  SimulateNavigation(GURL(kTestOrigin1));
+  auto authenticator = ConnectToAuthenticator();
+
+  // Attempt to register a credential using the appidExclude extension. It
+  // should fail when the registration already exists on the authenticator.
+  for (bool credential_already_exists : {false, true}) {
+    SCOPED_TRACE(credential_already_exists);
+
+    for (bool is_ctap2 : {false, true}) {
+      SCOPED_TRACE(is_ctap2);
+
+      ResetVirtualDevice();
+      virtual_device_factory_->SetSupportedProtocol(
+          is_ctap2 ? device::ProtocolVersion::kCtap2
+                   : device::ProtocolVersion::kU2f);
+
+      PublicKeyCredentialCreationOptionsPtr options =
+          GetTestPublicKeyCredentialCreationOptions();
+      options->appid_exclude = kTestOrigin1;
+      options->exclude_credentials = GetTestCredentials();
+
+      if (credential_already_exists) {
+        ASSERT_TRUE(
+            virtual_device_factory_->mutable_state()->InjectRegistration(
+                options->exclude_credentials[0].id(), kTestOrigin1));
+      }
+
+      TestMakeCredentialCallback callback_receiver;
+      authenticator->MakeCredential(std::move(options),
+                                    callback_receiver.callback());
+      callback_receiver.WaitForCallback();
+
+      if (credential_already_exists) {
+        ASSERT_EQ(AuthenticatorStatus::CREDENTIAL_EXCLUDED,
+                  callback_receiver.status());
+      } else {
+        ASSERT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status());
+      }
+    }
+  }
+}
+
 TEST_F(AuthenticatorImplTest, TestGetAssertionTimeout) {
   // The VirtualFidoAuthenticator simulates a tap immediately after it gets the
   // request. Replace by the real discovery that will wait until timeout.
diff --git a/content/browser/webauth/webauth_browsertest.cc b/content/browser/webauth/webauth_browsertest.cc
index 425c5fd..e8f466f2 100644
--- a/content/browser/webauth/webauth_browsertest.cc
+++ b/content/browser/webauth/webauth_browsertest.cc
@@ -483,7 +483,8 @@
         device::AuthenticatorSelectionCriteria(),
         device::AttestationConveyancePreference::kNone, nullptr,
         false /* no hmac_secret */, blink::mojom::ProtectionPolicy::UNSPECIFIED,
-        false /* protection policy not enforced */);
+        false /* protection policy not enforced */,
+        base::nullopt /* no appid_exclude */);
 
     return mojo_options;
   }
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index 7d808211..136a0d5 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -21,142 +21,140 @@
 #include "content/browser/worker_host/worker_script_fetch_initiator.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "net/base/network_isolation_key.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/mojom/interface_provider.mojom.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom.h"
 #include "third_party/blink/public/mojom/usb/web_usb_service.mojom.h"
 #include "url/origin.h"
 
 namespace content {
-namespace {
 
-// A host for a single dedicated worker. Its lifetime is managed by the
-// DedicatedWorkerGlobalScope of the corresponding worker in the renderer via a
-// StrongBinding. This lives on the UI thread.
-class DedicatedWorkerHost : public service_manager::mojom::InterfaceProvider {
- public:
-  DedicatedWorkerHost(int worker_process_id,
-                      int ancestor_render_frame_id,
-                      const url::Origin& origin)
-      : worker_process_id_(worker_process_id),
-        ancestor_render_frame_id_(ancestor_render_frame_id),
-        origin_(origin) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    RegisterMojoInterfaces();
-  }
+DedicatedWorkerHost::DedicatedWorkerHost(int worker_process_id,
+                                         int ancestor_render_frame_id,
+                                         const url::Origin& origin)
+    : worker_process_id_(worker_process_id),
+      ancestor_render_frame_id_(ancestor_render_frame_id),
+      origin_(origin) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  RegisterMojoInterfaces();
+}
 
-  // service_manager::mojom::InterfaceProvider:
-  void GetInterface(const std::string& interface_name,
-                    mojo::ScopedMessagePipeHandle interface_pipe) override {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    auto* worker_process_host = RenderProcessHost::FromID(worker_process_id_);
-    if (!worker_process_host)
-      return;
+DedicatedWorkerHost::~DedicatedWorkerHost() {}
 
-    // See if the registry that is specific to this worker host wants to handle
-    // the interface request.
-    if (registry_.TryBindInterface(interface_name, &interface_pipe))
-      return;
+// service_manager::mojom::InterfaceProvider:
+void DedicatedWorkerHost::GetInterface(
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  auto* worker_process_host = RenderProcessHost::FromID(worker_process_id_);
+  if (!worker_process_host)
+    return;
 
-    BindWorkerInterface(interface_name, std::move(interface_pipe),
-                        worker_process_host, origin_);
-  }
+  // See if the registry that is specific to this worker host wants to handle
+  // the interface request.
+  if (registry_.TryBindInterface(interface_name, &interface_pipe))
+    return;
+
+  BindWorkerInterface(interface_name, std::move(interface_pipe),
+                      worker_process_host, origin_);
+}
+
+void DedicatedWorkerHost::BindBrowserInterfaceBrokerReceiver(
+    mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> receiver) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(receiver.is_valid());
+  broker_receiver_.Bind(std::move(receiver));
+}
 
   // PlzDedicatedWorker:
-  void StartScriptLoad(
-      const GURL& script_url,
-      const url::Origin& request_initiator_origin,
-      network::mojom::CredentialsMode credentials_mode,
-      blink::mojom::FetchClientSettingsObjectPtr
-          outside_fetch_client_settings_object,
-      blink::mojom::BlobURLTokenPtr blob_url_token,
-      blink::mojom::DedicatedWorkerHostFactoryClientPtr client) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    DCHECK(blink::features::IsPlzDedicatedWorkerEnabled());
+void DedicatedWorkerHost::StartScriptLoad(
+    const GURL& script_url,
+    const url::Origin& request_initiator_origin,
+    network::mojom::CredentialsMode credentials_mode,
+    blink::mojom::FetchClientSettingsObjectPtr
+        outside_fetch_client_settings_object,
+    blink::mojom::BlobURLTokenPtr blob_url_token,
+    blink::mojom::DedicatedWorkerHostFactoryClientPtr client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(blink::features::IsPlzDedicatedWorkerEnabled());
 
-    DCHECK(!client_);
-    DCHECK(client);
-    client_ = std::move(client);
+  DCHECK(!client_);
+  DCHECK(client);
+  client_ = std::move(client);
 
-    // Get a storage partition.
-    auto* worker_process_host = RenderProcessHost::FromID(worker_process_id_);
-    if (!worker_process_host) {
-      client_->OnScriptLoadStartFailed();
+  // Get a storage partition.
+  auto* worker_process_host = RenderProcessHost::FromID(worker_process_id_);
+  if (!worker_process_host) {
+    client_->OnScriptLoadStartFailed();
+    return;
+  }
+  auto* storage_partition_impl = static_cast<StoragePartitionImpl*>(
+      worker_process_host->GetStoragePartition());
+
+  // Get a storage domain.
+  RenderFrameHostImpl* ancestor_render_frame_host =
+      GetAncestorRenderFrameHost();
+  if (!ancestor_render_frame_host) {
+    client_->OnScriptLoadStartFailed();
+    return;
+  }
+  SiteInstance* site_instance = ancestor_render_frame_host->GetSiteInstance();
+  if (!site_instance) {
+    client_->OnScriptLoadStartFailed();
+    return;
+  }
+  std::string storage_domain;
+  std::string partition_name;
+  bool in_memory;
+  GetContentClient()->browser()->GetStoragePartitionConfigForSite(
+      storage_partition_impl->browser_context(), site_instance->GetSiteURL(),
+      /*can_be_default=*/true, &storage_domain, &partition_name, &in_memory);
+
+  scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
+  if (script_url.SchemeIsBlob()) {
+    if (!blob_url_token) {
+      mojo::ReportBadMessage("DWH_NO_BLOB_URL_TOKEN");
       return;
     }
-    auto* storage_partition_impl = static_cast<StoragePartitionImpl*>(
-        worker_process_host->GetStoragePartition());
-
-    // Get a storage domain.
-    RenderFrameHostImpl* ancestor_render_frame_host =
-        GetAncestorRenderFrameHost();
-    if (!ancestor_render_frame_host) {
-      client_->OnScriptLoadStartFailed();
-      return;
-    }
-    SiteInstance* site_instance = ancestor_render_frame_host->GetSiteInstance();
-    if (!site_instance) {
-      client_->OnScriptLoadStartFailed();
-      return;
-    }
-    std::string storage_domain;
-    std::string partition_name;
-    bool in_memory;
-    GetContentClient()->browser()->GetStoragePartitionConfigForSite(
-        storage_partition_impl->browser_context(), site_instance->GetSiteURL(),
-        /*can_be_default=*/true, &storage_domain, &partition_name, &in_memory);
-
-    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
-    if (script_url.SchemeIsBlob()) {
-      if (!blob_url_token) {
-        mojo::ReportBadMessage("DWH_NO_BLOB_URL_TOKEN");
-        return;
-      }
-      blob_url_loader_factory =
-          ChromeBlobStorageContext::URLLoaderFactoryForToken(
-              storage_partition_impl->browser_context(),
-              std::move(blob_url_token));
-    } else if (blob_url_token) {
-      mojo::ReportBadMessage("DWH_NOT_BLOB_URL");
-      return;
-    }
-
-    appcache_handle_ = std::make_unique<AppCacheNavigationHandle>(
-        storage_partition_impl->GetAppCacheService(), worker_process_id_);
-
-    service_worker_handle_ = std::make_unique<ServiceWorkerNavigationHandle>(
-        storage_partition_impl->GetServiceWorkerContext());
-
-    WorkerScriptFetchInitiator::Start(
-        worker_process_id_, script_url, request_initiator_origin,
-        credentials_mode, std::move(outside_fetch_client_settings_object),
-        ResourceType::kWorker,
-        storage_partition_impl->GetServiceWorkerContext(),
-        service_worker_handle_.get(), appcache_handle_->core(),
-        std::move(blob_url_loader_factory), nullptr, storage_partition_impl,
-        storage_domain,
-        base::BindOnce(&DedicatedWorkerHost::DidStartScriptLoad,
-                       weak_factory_.GetWeakPtr()));
+    blob_url_loader_factory =
+        ChromeBlobStorageContext::URLLoaderFactoryForToken(
+            storage_partition_impl->browser_context(),
+            std::move(blob_url_token));
+  } else if (blob_url_token) {
+    mojo::ReportBadMessage("DWH_NOT_BLOB_URL");
+    return;
   }
 
- private:
-  void RegisterMojoInterfaces() {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    registry_.AddInterface(
-        base::BindRepeating(&DedicatedWorkerHost::CreateWebSocketConnector,
-                            base::Unretained(this)));
-    registry_.AddInterface(base::BindRepeating(
-        &DedicatedWorkerHost::CreateWebUsbService, base::Unretained(this)));
-    registry_.AddInterface(base::BindRepeating(
-        &DedicatedWorkerHost::CreateDedicatedWorker, base::Unretained(this)));
-    registry_.AddInterface(base::BindRepeating(
-        &DedicatedWorkerHost::CreateIdleManager, base::Unretained(this)));
-  }
+  appcache_handle_ = std::make_unique<AppCacheNavigationHandle>(
+      storage_partition_impl->GetAppCacheService(), worker_process_id_);
+
+  service_worker_handle_ = std::make_unique<ServiceWorkerNavigationHandle>(
+      storage_partition_impl->GetServiceWorkerContext());
+
+  WorkerScriptFetchInitiator::Start(
+      worker_process_id_, script_url, request_initiator_origin,
+      credentials_mode, std::move(outside_fetch_client_settings_object),
+      ResourceType::kWorker, storage_partition_impl->GetServiceWorkerContext(),
+      service_worker_handle_.get(), appcache_handle_->core(),
+      std::move(blob_url_loader_factory), nullptr, storage_partition_impl,
+      storage_domain,
+      base::BindOnce(&DedicatedWorkerHost::DidStartScriptLoad,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void DedicatedWorkerHost::RegisterMojoInterfaces() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  registry_.AddInterface(base::BindRepeating(
+      &DedicatedWorkerHost::CreateWebSocketConnector, base::Unretained(this)));
+  registry_.AddInterface(base::BindRepeating(
+      &DedicatedWorkerHost::CreateWebUsbService, base::Unretained(this)));
+  registry_.AddInterface(base::BindRepeating(
+      &DedicatedWorkerHost::CreateDedicatedWorker, base::Unretained(this)));
+  registry_.AddInterface(base::BindRepeating(
+      &DedicatedWorkerHost::CreateIdleManager, base::Unretained(this)));
+}
 
   // Called from WorkerScriptFetchInitiator. Continues starting the dedicated
   // worker in the renderer process.
@@ -173,182 +171,159 @@
   // |controller| contains information about the service worker controller. Once
   // a ServiceWorker object about the controller is prepared, it is registered
   // to |controller_service_worker_object_host|.
-  void DidStartScriptLoad(
-      std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
-          subresource_loader_factories,
-      blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
-      blink::mojom::ControllerServiceWorkerInfoPtr controller,
-      base::WeakPtr<ServiceWorkerObjectHost>
-          controller_service_worker_object_host,
-      bool success) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    DCHECK(blink::features::IsPlzDedicatedWorkerEnabled());
+void DedicatedWorkerHost::DidStartScriptLoad(
+    std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
+        subresource_loader_factories,
+    blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
+    blink::mojom::ControllerServiceWorkerInfoPtr controller,
+    base::WeakPtr<ServiceWorkerObjectHost>
+        controller_service_worker_object_host,
+    bool success) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(blink::features::IsPlzDedicatedWorkerEnabled());
 
-    if (!success) {
-      client_->OnScriptLoadStartFailed();
-      return;
-    }
-
-    // TODO(https://crbug.com/986188): Check if the main script's final response
-    // URL is commitable.
-
-    // TODO(cammie): Change this approach when we support shared workers
-    // creating dedicated workers, as there might be no ancestor frame.
-    RenderFrameHostImpl* ancestor_render_frame_host =
-        GetAncestorRenderFrameHost();
-    if (!ancestor_render_frame_host) {
-      client_->OnScriptLoadStartFailed();
-      return;
-    }
-
-    // Set up the default network loader factory.
-    mojo::PendingRemote<network::mojom::URLLoaderFactory>
-        pending_default_factory;
-    CreateNetworkFactory(
-        pending_default_factory.InitWithNewPipeAndPassReceiver(),
-        ancestor_render_frame_host);
-    subresource_loader_factories->pending_default_factory() =
-        std::move(pending_default_factory);
-
-    // Prepare the controller service worker info to pass to the renderer.
-    // |object_info| can be nullptr when the service worker context or the
-    // service worker version is gone during dedicated worker startup.
-    blink::mojom::ServiceWorkerObjectAssociatedPtrInfo
-        service_worker_remote_object;
-    blink::mojom::ServiceWorkerState service_worker_state;
-    if (controller && controller->object_info) {
-      controller->object_info->request =
-          mojo::MakeRequest(&service_worker_remote_object);
-      service_worker_state = controller->object_info->state;
-    }
-
-    client_->OnScriptLoadStarted(service_worker_handle_->TakeProviderInfo(),
-                                 std::move(main_script_load_params),
-                                 std::move(subresource_loader_factories),
-                                 std::move(controller));
-
-    // |service_worker_remote_object| is an associated interface ptr, so calls
-    // can't be made on it until its request endpoint is sent. Now that the
-    // request endpoint was sent, it can be used, so add it to
-    // ServiceWorkerObjectHost.
-    if (service_worker_remote_object) {
-      base::PostTask(
-          FROM_HERE, {BrowserThread::IO},
-          base::BindOnce(
-              &ServiceWorkerObjectHost::AddRemoteObjectPtrAndUpdateState,
-              controller_service_worker_object_host,
-              std::move(service_worker_remote_object), service_worker_state));
-    }
+  if (!success) {
+    client_->OnScriptLoadStartFailed();
+    return;
   }
 
-  void CreateNetworkFactory(network::mojom::URLLoaderFactoryRequest request,
-                            RenderFrameHostImpl* render_frame_host) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    DCHECK(render_frame_host);
-    network::mojom::TrustedURLLoaderHeaderClientPtrInfo no_header_client;
+  // TODO(https://crbug.com/986188): Check if the main script's final response
+  // URL is commitable.
 
-    // Get the origin of the frame tree's root to use as top-frame origin.
-    // TODO(crbug.com/986167): Resolve issue of potential race condition.
-    url::Origin top_frame_origin(render_frame_host->frame_tree_node()
-                                     ->frame_tree()
-                                     ->root()
-                                     ->current_origin());
-
-    RenderProcessHost* worker_process_host = render_frame_host->GetProcess();
-    DCHECK(worker_process_host);
-    worker_process_host->CreateURLLoaderFactory(
-        origin_, render_frame_host->cross_origin_embedder_policy(),
-        nullptr /* preferences */,
-        net::NetworkIsolationKey(top_frame_origin, origin_),
-        std::move(no_header_client), std::move(request));
+  // TODO(cammie): Change this approach when we support shared workers
+  // creating dedicated workers, as there might be no ancestor frame.
+  RenderFrameHostImpl* ancestor_render_frame_host =
+      GetAncestorRenderFrameHost();
+  if (!ancestor_render_frame_host) {
+    client_->OnScriptLoadStartFailed();
+    return;
   }
 
-  void CreateWebUsbService(blink::mojom::WebUsbServiceRequest request) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    RenderFrameHostImpl* ancestor_render_frame_host =
-        GetAncestorRenderFrameHost();
-    // TODO(nhiroki): Check if |ancestor_render_frame_host| is valid.
-    GetContentClient()->browser()->CreateWebUsbService(
-        ancestor_render_frame_host, std::move(request));
+  // Set up the default network loader factory.
+  mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_default_factory;
+  CreateNetworkFactory(pending_default_factory.InitWithNewPipeAndPassReceiver(),
+                       ancestor_render_frame_host);
+  subresource_loader_factories->pending_default_factory() =
+      std::move(pending_default_factory);
+
+  // Prepare the controller service worker info to pass to the renderer.
+  // |object_info| can be nullptr when the service worker context or the
+  // service worker version is gone during dedicated worker startup.
+  blink::mojom::ServiceWorkerObjectAssociatedPtrInfo
+      service_worker_remote_object;
+  blink::mojom::ServiceWorkerState service_worker_state;
+  if (controller && controller->object_info) {
+    controller->object_info->request =
+        mojo::MakeRequest(&service_worker_remote_object);
+    service_worker_state = controller->object_info->state;
   }
 
-  void CreateWebSocketConnector(
-      blink::mojom::WebSocketConnectorRequest request) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  client_->OnScriptLoadStarted(service_worker_handle_->TakeProviderInfo(),
+                               std::move(main_script_load_params),
+                               std::move(subresource_loader_factories),
+                               std::move(controller));
 
-    RenderFrameHostImpl* ancestor_render_frame_host =
-        GetAncestorRenderFrameHost();
-    if (!ancestor_render_frame_host) {
-      // In some cases |ancestor_render_frame_host| can be null. In such cases
-      // the worker will soon be terminated too, so let's abort the connection.
-      request.ResetWithReason(network::mojom::WebSocket::kInsufficientResources,
-                              "The parent frame has already been gone.");
-      return;
-    }
-    mojo::MakeStrongBinding(
-        std::make_unique<WebSocketConnectorImpl>(
-            worker_process_id_, ancestor_render_frame_id_, origin_),
-        std::move(request));
+  // |service_worker_remote_object| is an associated interface ptr, so calls
+  // can't be made on it until its request endpoint is sent. Now that the
+  // request endpoint was sent, it can be used, so add it to
+  // ServiceWorkerObjectHost.
+  if (service_worker_remote_object) {
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(
+            &ServiceWorkerObjectHost::AddRemoteObjectPtrAndUpdateState,
+            controller_service_worker_object_host,
+            std::move(service_worker_remote_object), service_worker_state));
   }
+}
 
-  void CreateDedicatedWorker(
-      blink::mojom::DedicatedWorkerHostFactoryRequest request) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    CreateDedicatedWorkerHostFactory(worker_process_id_,
-                                     ancestor_render_frame_id_, origin_,
-                                     std::move(request));
+void DedicatedWorkerHost::CreateNetworkFactory(
+    network::mojom::URLLoaderFactoryRequest request,
+    RenderFrameHostImpl* render_frame_host) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(render_frame_host);
+  network::mojom::TrustedURLLoaderHeaderClientPtrInfo no_header_client;
+
+  // Get the origin of the frame tree's root to use as top-frame origin.
+  // TODO(crbug.com/986167): Resolve issue of potential race condition.
+  url::Origin top_frame_origin(render_frame_host->frame_tree_node()
+                                   ->frame_tree()
+                                   ->root()
+                                   ->current_origin());
+
+  RenderProcessHost* worker_process_host = render_frame_host->GetProcess();
+  DCHECK(worker_process_host);
+  worker_process_host->CreateURLLoaderFactory(
+      origin_, render_frame_host->cross_origin_embedder_policy(),
+      nullptr /* preferences */,
+      net::NetworkIsolationKey(top_frame_origin, origin_),
+      std::move(no_header_client), std::move(request));
+}
+
+void DedicatedWorkerHost::CreateWebUsbService(
+    blink::mojom::WebUsbServiceRequest request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  RenderFrameHostImpl* ancestor_render_frame_host =
+      GetAncestorRenderFrameHost();
+  // TODO(nhiroki): Check if |ancestor_render_frame_host| is valid.
+  GetContentClient()->browser()->CreateWebUsbService(ancestor_render_frame_host,
+                                                     std::move(request));
+}
+
+void DedicatedWorkerHost::CreateWebSocketConnector(
+    blink::mojom::WebSocketConnectorRequest request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  RenderFrameHostImpl* ancestor_render_frame_host =
+      GetAncestorRenderFrameHost();
+  if (!ancestor_render_frame_host) {
+    // In some cases |ancestor_render_frame_host| can be null. In such cases
+    // the worker will soon be terminated too, so let's abort the connection.
+    request.ResetWithReason(network::mojom::WebSocket::kInsufficientResources,
+                            "The parent frame has already been gone.");
+    return;
   }
+  mojo::MakeStrongBinding(
+      std::make_unique<WebSocketConnectorImpl>(
+          worker_process_id_, ancestor_render_frame_id_, origin_),
+      blink::mojom::WebSocketConnectorRequest(std::move(request)));
+}
 
-  void CreateIdleManager(blink::mojom::IdleManagerRequest request) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    RenderFrameHostImpl* ancestor_render_frame_host =
-        GetAncestorRenderFrameHost();
-    // TODO(nhiroki): Check if |ancestor_render_frame_host| is valid.
-    if (!ancestor_render_frame_host->IsFeatureEnabled(
-            blink::mojom::FeaturePolicyFeature::kIdleDetection)) {
-      mojo::ReportBadMessage("Feature policy blocks access to IdleDetection.");
-      return;
-    }
-    static_cast<StoragePartitionImpl*>(
-        ancestor_render_frame_host->GetProcess()->GetStoragePartition())
-        ->GetIdleManager()
-        ->CreateService(std::move(request));
+void DedicatedWorkerHost::CreateDedicatedWorker(
+    blink::mojom::DedicatedWorkerHostFactoryRequest request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  CreateDedicatedWorkerHostFactory(worker_process_id_,
+                                   ancestor_render_frame_id_, origin_,
+                                   std::move(request));
+}
+
+void DedicatedWorkerHost::CreateIdleManager(
+    blink::mojom::IdleManagerRequest request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  RenderFrameHostImpl* ancestor_render_frame_host =
+      GetAncestorRenderFrameHost();
+  // TODO(nhiroki): Check if |ancestor_render_frame_host| is valid.
+  if (!ancestor_render_frame_host->IsFeatureEnabled(
+          blink::mojom::FeaturePolicyFeature::kIdleDetection)) {
+    mojo::ReportBadMessage("Feature policy blocks access to IdleDetection.");
+    return;
   }
+  static_cast<StoragePartitionImpl*>(
+      ancestor_render_frame_host->GetProcess()->GetStoragePartition())
+      ->GetIdleManager()
+      ->CreateService(std::move(request));
+}
 
-  // May return a nullptr.
-  RenderFrameHostImpl* GetAncestorRenderFrameHost() {
-    // Use |worker_process_id_| as the ancestor render frame's process ID as the
-    // frame surely lives in the same process for dedicated workers.
-    const int ancestor_render_frame_process_id = worker_process_id_;
-    return RenderFrameHostImpl::FromID(ancestor_render_frame_process_id,
-                                       ancestor_render_frame_id_);
-  }
+// May return a nullptr.
+RenderFrameHostImpl* DedicatedWorkerHost::GetAncestorRenderFrameHost() {
+  // Use |worker_process_id_| as the ancestor render frame's process ID as the
+  // frame surely lives in the same process for dedicated workers.
+  const int ancestor_render_frame_process_id = worker_process_id_;
+  return RenderFrameHostImpl::FromID(ancestor_render_frame_process_id,
+                                     ancestor_render_frame_id_);
+}
 
-  // The ID of the render process host that hosts this worker.
-  const int worker_process_id_;
-
-  // The ID of the frame that owns this worker, either directly, or (in the case
-  // of nested workers) indirectly via a tree of dedicated workers.
-  const int ancestor_render_frame_id_;
-
-  const url::Origin origin_;
-
-  // This is kept alive during the lifetime of the dedicated worker, since it's
-  // associated with Mojo interfaces (ServiceWorkerContainer and
-  // URLLoaderFactory) that are needed to stay alive while the worker is
-  // starting or running.
-  blink::mojom::DedicatedWorkerHostFactoryClientPtr client_;
-
-  std::unique_ptr<AppCacheNavigationHandle> appcache_handle_;
-  std::unique_ptr<ServiceWorkerNavigationHandle> service_worker_handle_;
-
-  service_manager::BinderRegistry registry_;
-
-  base::WeakPtrFactory<DedicatedWorkerHost> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(DedicatedWorkerHost);
-};
-
+namespace {
 // A factory for creating DedicatedWorkerHosts. Its lifetime is managed by
 // the renderer over mojo via a StrongBinding. This lives on the UI thread.
 class DedicatedWorkerHostFactoryImpl
@@ -366,7 +341,9 @@
   // blink::mojom::DedicatedWorkerHostFactory:
   void CreateWorkerHost(
       const url::Origin& origin,
-      service_manager::mojom::InterfaceProviderRequest request) override {
+      service_manager::mojom::InterfaceProviderRequest request,
+      mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
+          broker_receiver) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     if (blink::features::IsPlzDedicatedWorkerEnabled()) {
       mojo::ReportBadMessage("DWH_INVALID_WORKER_CREATION");
@@ -377,12 +354,13 @@
     // with the request for |DedicatedWorkerHostFactory|, enforce that
     // the worker's origin either matches the origin of the creating context
     // (Document or DedicatedWorkerGlobalScope), or is unique.
-    mojo::MakeStrongBinding(
-        std::make_unique<DedicatedWorkerHost>(
-            creator_process_id_, ancestor_render_frame_id_, origin),
-        FilterRendererExposedInterfaces(
-            blink::mojom::kNavigation_DedicatedWorkerSpec, creator_process_id_,
-            std::move(request)));
+    auto host = std::make_unique<DedicatedWorkerHost>(
+        creator_process_id_, ancestor_render_frame_id_, origin);
+    host->BindBrowserInterfaceBrokerReceiver(std::move(broker_receiver));
+    mojo::MakeStrongBinding(std::move(host),
+                            FilterRendererExposedInterfaces(
+                                blink::mojom::kNavigation_DedicatedWorkerSpec,
+                                creator_process_id_, std::move(request)));
   }
 
   // PlzDedicatedWorker:
@@ -410,6 +388,9 @@
     auto host = std::make_unique<DedicatedWorkerHost>(creator_process_id_,
                                                       ancestor_render_frame_id_,
                                                       request_initiator_origin);
+    mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> broker;
+    host->BindBrowserInterfaceBrokerReceiver(
+        broker.InitWithNewPipeAndPassReceiver());
     auto* host_raw = host.get();
     service_manager::mojom::InterfaceProviderPtr interface_provider;
     mojo::MakeStrongBinding(
@@ -417,7 +398,8 @@
         FilterRendererExposedInterfaces(
             blink::mojom::kNavigation_DedicatedWorkerSpec, creator_process_id_,
             mojo::MakeRequest(&interface_provider)));
-    client->OnWorkerHostCreated(std::move(interface_provider));
+    client->OnWorkerHostCreated(std::move(interface_provider),
+                                std::move(broker));
     host_raw->StartScriptLoad(script_url, request_initiator_origin,
                               credentials_mode,
                               std::move(outside_fetch_client_settings_object),
diff --git a/content/browser/worker_host/dedicated_worker_host.h b/content/browser/worker_host/dedicated_worker_host.h
index a6cb121..07b6da6 100644
--- a/content/browser/worker_host/dedicated_worker_host.h
+++ b/content/browser/worker_host/dedicated_worker_host.h
@@ -5,6 +5,15 @@
 #ifndef CONTENT_BROWSER_WORKER_HOST_DEDICATED_WORKER_HOST_H_
 #define CONTENT_BROWSER_WORKER_HOST_DEDICATED_WORKER_HOST_H_
 
+#include "content/browser/browser_interface_broker_impl.h"
+#include "content/public/browser/render_process_host.h"
+
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/mojom/interface_provider.mojom.h"
+#include "third_party/blink/public/mojom/idle/idle_manager.mojom-forward.h"
+#include "third_party/blink/public/mojom/usb/web_usb_service.mojom-forward.h"
+#include "third_party/blink/public/mojom/websockets/websocket_connector.mojom-forward.h"
 #include "third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom.h"
 
 namespace url {
@@ -13,6 +22,10 @@
 
 namespace content {
 
+class AppCacheNavigationHandle;
+class ServiceWorkerNavigationHandle;
+class ServiceWorkerObjectHost;
+
 // Creates a host factory for a dedicated worker. This must be called on the UI
 // thread.
 void CreateDedicatedWorkerHostFactory(
@@ -21,6 +34,111 @@
     const url::Origin& origin,
     blink::mojom::DedicatedWorkerHostFactoryRequest request);
 
+// A host for a single dedicated worker. Its lifetime is managed by the
+// DedicatedWorkerGlobalScope of the corresponding worker in the renderer via a
+// StrongBinding. This lives on the UI thread.
+class DedicatedWorkerHost : public service_manager::mojom::InterfaceProvider {
+ public:
+  DedicatedWorkerHost(int worker_process_id,
+                      int ancestor_render_frame_id,
+                      const url::Origin& origin);
+  ~DedicatedWorkerHost() final;
+
+  void BindBrowserInterfaceBrokerReceiver(
+      mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> receiver);
+
+  // May return nullptr.
+  RenderProcessHost* GetProcessHost() {
+    return RenderProcessHost::FromID(worker_process_id_);
+  }
+  const url::Origin& GetOrigin() { return origin_; }
+
+  // service_manager::mojom::InterfaceProvider:
+  void GetInterface(const std::string& interface_name,
+                    mojo::ScopedMessagePipeHandle interface_pipe) override;
+
+  // PlzDedicatedWorker:
+  void StartScriptLoad(
+      const GURL& script_url,
+      const url::Origin& request_initiator_origin,
+      network::mojom::CredentialsMode credentials_mode,
+      blink::mojom::FetchClientSettingsObjectPtr
+          outside_fetch_client_settings_object,
+      blink::mojom::BlobURLTokenPtr blob_url_token,
+      blink::mojom::DedicatedWorkerHostFactoryClientPtr client);
+
+ private:
+  void RegisterMojoInterfaces();
+
+  // Called from WorkerScriptFetchInitiator. Continues starting the dedicated
+  // worker in the renderer process.
+  //
+  // |main_script_load_params| is sent to the renderer process and to be used to
+  // load the dedicated worker main script pre-requested by the browser process.
+  //
+  // |subresource_loader_factories| is sent to the renderer process and is to be
+  // used to request subresources where applicable. For example, this allows the
+  // dedicated worker to load chrome-extension:// URLs which the renderer's
+  // default loader factory can't load.
+  //
+  // NetworkService (PlzWorker):
+  // |controller| contains information about the service worker controller. Once
+  // a ServiceWorker object about the controller is prepared, it is registered
+  // to |controller_service_worker_object_host|.
+  void DidStartScriptLoad(
+      std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
+          subresource_loader_factories,
+      blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
+      blink::mojom::ControllerServiceWorkerInfoPtr controller,
+      base::WeakPtr<ServiceWorkerObjectHost>
+          controller_service_worker_object_host,
+      bool success);
+
+  void CreateNetworkFactory(network::mojom::URLLoaderFactoryRequest request,
+                            RenderFrameHostImpl* render_frame_host);
+
+  void CreateWebUsbService(blink::mojom::WebUsbServiceRequest request);
+
+  void CreateWebSocketConnector(
+      blink::mojom::WebSocketConnectorRequest request);
+
+  void CreateDedicatedWorker(
+      blink::mojom::DedicatedWorkerHostFactoryRequest request);
+
+  void CreateIdleManager(blink::mojom::IdleManagerRequest request);
+
+  // May return a nullptr.
+  RenderFrameHostImpl* GetAncestorRenderFrameHost();
+  // The ID of the render process host that hosts this worker.
+  const int worker_process_id_;
+
+  // The ID of the frame that owns this worker, either directly, or (in the case
+  // of nested workers) indirectly via a tree of dedicated workers.
+  const int ancestor_render_frame_id_;
+
+  const url::Origin origin_;
+
+  // This is kept alive during the lifetime of the dedicated worker, since it's
+  // associated with Mojo interfaces (ServiceWorkerContainer and
+  // URLLoaderFactory) that are needed to stay alive while the worker is
+  // starting or running.
+  blink::mojom::DedicatedWorkerHostFactoryClientPtr client_;
+
+  std::unique_ptr<AppCacheNavigationHandle> appcache_handle_;
+  std::unique_ptr<ServiceWorkerNavigationHandle> service_worker_handle_;
+
+  service_manager::BinderRegistry registry_;
+
+  BrowserInterfaceBrokerImpl<DedicatedWorkerHost, const url::Origin&> broker_{
+      this};
+  mojo::Receiver<blink::mojom::BrowserInterfaceBroker> broker_receiver_{
+      &broker_};
+
+  base::WeakPtrFactory<DedicatedWorkerHost> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(DedicatedWorkerHost);
+};
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_WORKER_HOST_DEDICATED_WORKER_HOST_H_
diff --git a/content/browser/worker_host/mock_shared_worker.cc b/content/browser/worker_host/mock_shared_worker.cc
index d0b63e46..70f5536 100644
--- a/content/browser/worker_host/mock_shared_worker.cc
+++ b/content/browser/worker_host/mock_shared_worker.cc
@@ -108,7 +108,9 @@
     blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
     blink::mojom::SharedWorkerHostPtr host,
     blink::mojom::SharedWorkerRequest request,
-    service_manager::mojom::InterfaceProviderPtr interface_provider) {
+    service_manager::mojom::InterfaceProviderPtr interface_provider,
+    mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
+        browser_interface_broker) {
   DCHECK(!create_params_);
   create_params_ = std::make_unique<CreateParams>();
   create_params_->info = std::move(info);
diff --git a/content/browser/worker_host/mock_shared_worker.h b/content/browser/worker_host/mock_shared_worker.h
index 1c6e650..f3da5fc 100644
--- a/content/browser/worker_host/mock_shared_worker.h
+++ b/content/browser/worker_host/mock_shared_worker.h
@@ -86,7 +86,9 @@
       blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
       blink::mojom::SharedWorkerHostPtr host,
       blink::mojom::SharedWorkerRequest request,
-      service_manager::mojom::InterfaceProviderPtr interface_provider) override;
+      service_manager::mojom::InterfaceProviderPtr interface_provider,
+      mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
+          browser_interface_broker) override;
 
   struct CreateParams {
     CreateParams();
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index f3274bd..261582457 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -185,6 +185,12 @@
       blink::mojom::kNavigation_SharedWorkerSpec, worker_process_id_,
       mojo::MakeRequest(&interface_provider)));
 
+  // Set up BrowserInterfaceBroker interface
+  mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
+      browser_interface_broker;
+  broker_receiver_.Bind(
+      browser_interface_broker.InitWithNewPipeAndPassReceiver());
+
   // Set the default factory to the bundle for subresource loading to pass to
   // the renderer.
   mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_default_factory;
@@ -218,7 +224,7 @@
       std::move(main_script_load_params),
       std::move(subresource_loader_factories), std::move(controller),
       std::move(host), std::move(worker_request_),
-      std::move(interface_provider));
+      std::move(interface_provider), std::move(browser_interface_broker));
 
   // |service_worker_remote_object| is an associated interface ptr, so calls
   // can't be made on it until its request endpoint is sent. Now that the
diff --git a/content/browser/worker_host/shared_worker_host.h b/content/browser/worker_host/shared_worker_host.h
index 1e79c140..f9c60308 100644
--- a/content/browser/worker_host/shared_worker_host.h
+++ b/content/browser/worker_host/shared_worker_host.h
@@ -15,8 +15,10 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "base/unguessable_token.h"
+#include "content/browser/browser_interface_broker_impl.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/global_routing_id.h"
+#include "content/public/browser/render_process_host.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
@@ -57,6 +59,11 @@
                    int worker_process_id);
   ~SharedWorkerHost() override;
 
+  // May return nullptr.
+  RenderProcessHost* GetProcessHost() {
+    return RenderProcessHost::FromID(worker_process_id_);
+  }
+
   // Allows overriding the URLLoaderFactory creation for subresources.
   // Passing a null callback will restore the default behavior.
   // This method must be called either on the UI thread or before threads start.
@@ -195,6 +202,11 @@
   mojo::Binding<service_manager::mojom::InterfaceProvider>
       interface_provider_binding_;
 
+  BrowserInterfaceBrokerImpl<SharedWorkerHost, const url::Origin&> broker_{
+      this};
+  mojo::Receiver<blink::mojom::BrowserInterfaceBroker> broker_receiver_{
+      &broker_};
+
   // The handle owns the precreated AppCacheHost until it's claimed by the
   // renderer after main script loading finishes.
   std::unique_ptr<AppCacheNavigationHandle> appcache_handle_;
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java
index 8d3500b..b4b58db 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java
@@ -195,8 +195,7 @@
     @Test
     @SmallTest
     @Feature({"TextSelection"})
-    @DisableIf.
-    Build(sdk_is_less_than = Build.VERSION_CODES.N, message = "Drag and drop not enabled pre-N")
+    @DisabledTest(message = "https://crbug.com/980733")
     public void testSelectionPreservedAfterDragAndDrop() throws Throwable {
         DOMUtils.longPressNode(mWebContents, "plain_text_1");
         waitForSelectActionBarVisible(true);
diff --git a/content/public/app/content_browser_manifest.cc b/content/public/app/content_browser_manifest.cc
index 1c6919d..23d3741 100644
--- a/content/public/app/content_browser_manifest.cc
+++ b/content/public/app/content_browser_manifest.cc
@@ -163,8 +163,8 @@
           .ExposeInterfaceFilterCapability_Deprecated(
               "navigation:shared_worker", "renderer",
               std::set<const char*>{
-                  "blink.mojom.CacheStorage", "blink.mojom.FileSystemManager",
-                  "blink.mojom.IDBFactory", "blink.mojom.LockManager",
+                  "blink.mojom.CacheStorage", "blink.mojom.IDBFactory",
+                  "blink.mojom.LockManager",
                   "blink.mojom.NativeFileSystemManager",
                   "blink.mojom.NotificationService",
                   "blink.mojom.PermissionService",
@@ -180,8 +180,8 @@
               std::set<const char*>{
                   "blink.mojom.CacheStorage",
                   "blink.mojom.DedicatedWorkerHostFactory",
-                  "blink.mojom.FileSystemManager", "blink.mojom.IDBFactory",
-                  "blink.mojom.IdleManager", "blink.mojom.LockManager",
+                  "blink.mojom.IDBFactory", "blink.mojom.IdleManager",
+                  "blink.mojom.LockManager",
                   "blink.mojom.NativeFileSystemManager",
                   "blink.mojom.NotificationService",
                   "blink.mojom.PermissionService",
@@ -227,7 +227,6 @@
                   "blink.mojom.DisplayCutoutHost",
                   "blink.mojom.DedicatedWorkerHostFactory",
                   "blink.mojom.FileChooser",
-                  "blink.mojom.FileSystemManager",
                   "blink.mojom.GeolocationService",
                   "blink.mojom.HidService",
                   "blink.mojom.IDBFactory",
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 036cd8331..d950f5ba 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2536,7 +2536,11 @@
       // frame when a commit (and ownership transfer) is imminent.
       // TODO(dcheng): This is the case of https://crbug.com/838348.
       DCHECK(is_main_frame_);
+#if !defined(OS_ANDROID)
+      // This check is not enabled on Android, since it seems like it's much
+      // easier to trigger data races there.
       CHECK(!in_frame_tree_);
+#endif  // !defined(OS_ANDROID)
       break;
   }
 
diff --git a/content/renderer/worker/dedicated_worker_host_factory_client.cc b/content/renderer/worker/dedicated_worker_host_factory_client.cc
index 18f8a9c..4e7b181 100644
--- a/content/renderer/worker/dedicated_worker_host_factory_client.cc
+++ b/content/renderer/worker/dedicated_worker_host_factory_client.cc
@@ -35,9 +35,13 @@
     const blink::WebSecurityOrigin& script_origin) {
   DCHECK(!blink::features::IsPlzDedicatedWorkerEnabled());
   service_manager::mojom::InterfaceProviderPtr interface_provider_ptr;
-  factory_->CreateWorkerHost(script_origin,
-                             mojo::MakeRequest(&interface_provider_ptr));
-  OnWorkerHostCreated(std::move(interface_provider_ptr));
+  mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
+      browser_interface_broker;
+  factory_->CreateWorkerHost(
+      script_origin, mojo::MakeRequest(&interface_provider_ptr),
+      browser_interface_broker.InitWithNewPipeAndPassReceiver());
+  OnWorkerHostCreated(std::move(interface_provider_ptr),
+                      std::move(browser_interface_broker));
 }
 
 void DedicatedWorkerHostFactoryClient::CreateWorkerHost(
@@ -113,8 +117,11 @@
 }
 
 void DedicatedWorkerHostFactoryClient::OnWorkerHostCreated(
-    service_manager::mojom::InterfaceProviderPtr interface_provider) {
-  worker_->OnWorkerHostCreated(interface_provider.PassInterface().PassHandle());
+    service_manager::mojom::InterfaceProviderPtr interface_provider,
+    mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
+        browser_interface_broker) {
+  worker_->OnWorkerHostCreated(interface_provider.PassInterface().PassHandle(),
+                               browser_interface_broker.PassPipe());
 }
 
 void DedicatedWorkerHostFactoryClient::OnScriptLoadStarted(
diff --git a/content/renderer/worker/dedicated_worker_host_factory_client.h b/content/renderer/worker/dedicated_worker_host_factory_client.h
index 9ba54f8..363cb9e 100644
--- a/content/renderer/worker/dedicated_worker_host_factory_client.h
+++ b/content/renderer/worker/dedicated_worker_host_factory_client.h
@@ -63,7 +63,9 @@
  private:
   // Implements blink::mojom::DedicatedWorkerHostFactoryClient.
   void OnWorkerHostCreated(
-      service_manager::mojom::InterfaceProviderPtr interface_provider) override;
+      service_manager::mojom::InterfaceProviderPtr interface_provider,
+      mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
+          browser_interface_broker) override;
   void OnScriptLoadStarted(
       blink::mojom::ServiceWorkerProviderInfoForClientPtr
           service_worker_provider_info,
diff --git a/content/renderer/worker/embedded_shared_worker_stub.cc b/content/renderer/worker/embedded_shared_worker_stub.cc
index a1298ab..c65aae96 100644
--- a/content/renderer/worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/worker/embedded_shared_worker_stub.cc
@@ -56,7 +56,9 @@
     blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
     blink::mojom::SharedWorkerHostPtr host,
     blink::mojom::SharedWorkerRequest request,
-    service_manager::mojom::InterfaceProviderPtr interface_provider)
+    service_manager::mojom::InterfaceProviderPtr interface_provider,
+    mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
+        browser_interface_broker)
     : binding_(this, std::move(request)),
       host_(std::move(host)),
       url_(info->url),
@@ -124,7 +126,8 @@
                                 renderer_preferences_.enable_referrers),
       subresource_loader_factory_bundle_,
       content_settings.PassInterface().PassHandle(),
-      interface_provider.PassInterface().PassHandle());
+      interface_provider.PassInterface().PassHandle(),
+      browser_interface_broker.PassPipe());
 
   // If the host drops its connection, then self-destruct.
   binding_.set_connection_error_handler(base::BindOnce(
diff --git a/content/renderer/worker/embedded_shared_worker_stub.h b/content/renderer/worker/embedded_shared_worker_stub.h
index 87fdd73..436c1144 100644
--- a/content/renderer/worker/embedded_shared_worker_stub.h
+++ b/content/renderer/worker/embedded_shared_worker_stub.h
@@ -15,6 +15,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
+#include "third_party/blink/public/mojom/browser_interface_broker.mojom.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
 #include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
@@ -71,7 +72,9 @@
       blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
       blink::mojom::SharedWorkerHostPtr host,
       blink::mojom::SharedWorkerRequest request,
-      service_manager::mojom::InterfaceProviderPtr interface_provider);
+      service_manager::mojom::InterfaceProviderPtr interface_provider,
+      mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
+          browser_interface_broker);
   ~EmbeddedSharedWorkerStub() override;
 
   // blink::WebSharedWorkerClient implementation.
diff --git a/content/renderer/worker/shared_worker_factory_impl.cc b/content/renderer/worker/shared_worker_factory_impl.cc
index 42d3eb1..4c9ecc1 100644
--- a/content/renderer/worker/shared_worker_factory_impl.cc
+++ b/content/renderer/worker/shared_worker_factory_impl.cc
@@ -38,7 +38,9 @@
     blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
     blink::mojom::SharedWorkerHostPtr host,
     blink::mojom::SharedWorkerRequest request,
-    service_manager::mojom::InterfaceProviderPtr interface_provider) {
+    service_manager::mojom::InterfaceProviderPtr interface_provider,
+    mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
+        browser_interface_broker) {
   // Bound to the lifetime of the underlying blink::WebSharedWorker instance.
   new EmbeddedSharedWorkerStub(
       std::move(info), user_agent, pause_on_start, devtools_worker_token,
@@ -47,7 +49,8 @@
       appcache_host_id.value_or(base::UnguessableToken()),
       std::move(main_script_load_params),
       std::move(subresource_loader_factories), std::move(controller_info),
-      std::move(host), std::move(request), std::move(interface_provider));
+      std::move(host), std::move(request), std::move(interface_provider),
+      std::move(browser_interface_broker));
 }
 
 }  // namespace content
diff --git a/content/renderer/worker/shared_worker_factory_impl.h b/content/renderer/worker/shared_worker_factory_impl.h
index 50cd29d..0d49c34 100644
--- a/content/renderer/worker/shared_worker_factory_impl.h
+++ b/content/renderer/worker/shared_worker_factory_impl.h
@@ -40,7 +40,9 @@
       blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
       blink::mojom::SharedWorkerHostPtr host,
       blink::mojom::SharedWorkerRequest request,
-      service_manager::mojom::InterfaceProviderPtr interface_provider) override;
+      service_manager::mojom::InterfaceProviderPtr interface_provider,
+      mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
+          browser_interface_broker) override;
 
   DISALLOW_COPY_AND_ASSIGN(SharedWorkerFactoryImpl);
 };
diff --git a/content/shell/renderer/web_test/blink_test_runner.cc b/content/shell/renderer/web_test/blink_test_runner.cc
index 35825bf..49f58e78 100644
--- a/content/shell/renderer/web_test/blink_test_runner.cc
+++ b/content/shell/renderer/web_test/blink_test_runner.cc
@@ -639,10 +639,11 @@
     const std::vector<std::string>& event_platforms,
     base::OnceCallback<void(bool)> callback) {
   app_banner_service_.reset(new test_runner::AppBannerService());
-  blink::mojom::AppBannerControllerRequest request =
-      mojo::MakeRequest(&app_banner_service_->controller());
   render_view()->GetMainRenderFrame()->BindLocalInterface(
-      blink::mojom::AppBannerController::Name_, request.PassMessagePipe());
+      blink::mojom::AppBannerController::Name_,
+      app_banner_service_->controller()
+          .BindNewPipeAndPassReceiver()
+          .PassPipe());
   app_banner_service_->SendBannerPromptRequest(event_platforms,
                                                std::move(callback));
 }
diff --git a/content/shell/test_runner/app_banner_service.cc b/content/shell/test_runner/app_banner_service.cc
index b4024806..21c8696 100644
--- a/content/shell/test_runner/app_banner_service.cc
+++ b/content/shell/test_runner/app_banner_service.cc
@@ -7,9 +7,9 @@
 
 namespace test_runner {
 
-AppBannerService::AppBannerService() : binding_(this) {}
+AppBannerService::AppBannerService() = default;
 
-AppBannerService::~AppBannerService() {}
+AppBannerService::~AppBannerService() = default;
 
 void AppBannerService::ResolvePromise(const std::string& platform) {
   if (!event_.is_bound())
@@ -28,10 +28,9 @@
   if (!controller_.is_bound())
     return;
 
-  blink::mojom::AppBannerServicePtr proxy;
-  binding_.Bind(mojo::MakeRequest(&proxy));
   controller_->BannerPromptRequest(
-      std::move(proxy), mojo::MakeRequest(&event_), platforms,
+      receiver_.BindNewPipeAndPassRemote(), event_.BindNewPipeAndPassReceiver(),
+      platforms,
       base::BindOnce(&AppBannerService::OnBannerPromptReply,
                      base::Unretained(this), std::move(callback)));
 }
diff --git a/content/shell/test_runner/app_banner_service.h b/content/shell/test_runner/app_banner_service.h
index a42a956..5f39667b 100644
--- a/content/shell/test_runner/app_banner_service.h
+++ b/content/shell/test_runner/app_banner_service.h
@@ -11,7 +11,8 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "content/shell/test_runner/test_runner_export.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/app_banner/app_banner.mojom.h"
 
 namespace test_runner {
@@ -24,7 +25,9 @@
   AppBannerService();
   ~AppBannerService() override;
 
-  blink::mojom::AppBannerControllerPtr& controller() { return controller_; }
+  mojo::Remote<blink::mojom::AppBannerController>& controller() {
+    return controller_;
+  }
   void ResolvePromise(const std::string& platform);
   void SendBannerPromptRequest(const std::vector<std::string>& platforms,
                                base::OnceCallback<void(bool)> callback);
@@ -36,9 +39,9 @@
   void OnBannerPromptReply(base::OnceCallback<void(bool)> callback,
                            blink::mojom::AppBannerPromptReply);
 
-  mojo::Binding<blink::mojom::AppBannerService> binding_;
-  blink::mojom::AppBannerEventPtr event_;
-  blink::mojom::AppBannerControllerPtr controller_;
+  mojo::Receiver<blink::mojom::AppBannerService> receiver_{this};
+  mojo::Remote<blink::mojom::AppBannerEvent> event_;
+  mojo::Remote<blink::mojom::AppBannerController> controller_;
 
   DISALLOW_COPY_AND_ASSIGN(AppBannerService);
 };
diff --git a/device/fido/ctap_make_credential_request.h b/device/fido/ctap_make_credential_request.h
index 12eb63d6..6b5d893 100644
--- a/device/fido/ctap_make_credential_request.h
+++ b/device/fido/ctap_make_credential_request.h
@@ -69,6 +69,8 @@
   base::Optional<uint8_t> pin_protocol;
   AttestationConveyancePreference attestation_preference =
       AttestationConveyancePreference::kNone;
+  // U2F AppID for excluding credentials.
+  base::Optional<std::string> app_id;
 
   // cred_protect indicates the level of protection afforded to a credential.
   // This depends on a CTAP2 extension that not all authenticators will support.
diff --git a/device/fido/make_credential_task.cc b/device/fido/make_credential_task.cc
index ac5559ea..5156d25 100644
--- a/device/fido/make_credential_task.cc
+++ b/device/fido/make_credential_task.cc
@@ -125,8 +125,9 @@
 CtapGetAssertionRequest MakeCredentialTask::NextSilentSignRequest() {
   DCHECK(request_.exclude_list &&
          current_credential_ < request_.exclude_list->size());
-  CtapGetAssertionRequest request(request_.rp.id,
-                                  /*client_data_json=*/"");
+  CtapGetAssertionRequest request(
+      probing_alternative_rp_id_ ? *request_.app_id : request_.rp.id,
+      /*client_data_json=*/"");
   request.allow_list = {{request_.exclude_list->at(current_credential_)}};
   request.user_presence_required = false;
   request.user_verification = UserVerificationRequirement::kDiscouraged;
@@ -135,8 +136,12 @@
 
 void MakeCredentialTask::MakeCredential() {
   // Silently probe each credential in the allow list to work around
-  // authenticators rejecting lists over a certain size.
-  if (request_.exclude_list && request_.exclude_list->size() > 1) {
+  // authenticators rejecting lists over a certain size. Also do this if a
+  // second RP ID needs to be tested because the site used the appidExclude
+  // extension.
+  if (request_.exclude_list &&
+      (request_.exclude_list->size() > 1 ||
+       (!request_.exclude_list->empty() && request_.app_id))) {
     silent_sign_operation_ = std::make_unique<Ctap2DeviceOperation<
         CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>(
         device(), NextSilentSignRequest(),
@@ -172,6 +177,9 @@
   if (response_code == CtapDeviceResponseCode::kSuccess) {
     CtapMakeCredentialRequest request = request_;
     request.exclude_list = {{request_.exclude_list->at(current_credential_)}};
+    if (probing_alternative_rp_id_) {
+      request.rp.id = *request_.app_id;
+    }
     register_operation_ = std::make_unique<Ctap2DeviceOperation<
         CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
         device(), std::move(request), std::move(callback_),
@@ -202,7 +210,16 @@
 
   // The authenticator doesn't recognize this particular credential from the
   // exclude list. Try the next one.
-  if (++current_credential_ < request_.exclude_list->size()) {
+  current_credential_++;
+  if (current_credential_ == request_.exclude_list->size() &&
+      !probing_alternative_rp_id_ && request_.app_id) {
+    // All elements of |request_.exclude_list| have been tested, but there's a
+    // second RP ID so they need to be tested again.
+    current_credential_ = 0;
+    probing_alternative_rp_id_ = true;
+  }
+
+  if (current_credential_ < request_.exclude_list->size()) {
     silent_sign_operation_ = std::make_unique<Ctap2DeviceOperation<
         CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>(
         device(), NextSilentSignRequest(),
diff --git a/device/fido/make_credential_task.h b/device/fido/make_credential_task.h
index 6c846ab..f7db14e 100644
--- a/device/fido/make_credential_task.h
+++ b/device/fido/make_credential_task.h
@@ -73,6 +73,10 @@
   std::unique_ptr<SignOperation> silent_sign_operation_;
   MakeCredentialTaskCallback callback_;
   size_t current_credential_ = 0;
+  // probing_alternative_rp_id_ is true if |app_id| is set in |request_| and
+  // thus the exclude list is being probed a second time with the alternative RP
+  // ID.
+  bool probing_alternative_rp_id_ = false;
   bool canceled_ = false;
 
   base::WeakPtrFactory<MakeCredentialTask> weak_factory_;
diff --git a/device/fido/u2f_register_operation.cc b/device/fido/u2f_register_operation.cc
index d69d436..90700ea7 100644
--- a/device/fido/u2f_register_operation.cc
+++ b/device/fido/u2f_register_operation.cc
@@ -49,8 +49,17 @@
 }
 
 void U2fRegisterOperation::TrySign() {
+  base::Optional<std::vector<uint8_t>> sign_command;
+  if (probing_alternative_rp_id_) {
+    CtapMakeCredentialRequest sign_request(request());
+    sign_request.rp.id = *request().app_id;
+    sign_command = ConvertToU2fSignCommand(sign_request, excluded_key_handle());
+  } else {
+    sign_command = ConvertToU2fSignCommand(request(), excluded_key_handle());
+  }
+
   DispatchDeviceRequest(
-      ConvertToU2fSignCommand(request(), excluded_key_handle()),
+      std::move(sign_command),
       base::BindOnce(&U2fRegisterOperation::OnCheckForExcludedKeyHandle,
                      weak_factory_.GetWeakPtr()));
 }
@@ -99,7 +108,15 @@
     case apdu::ApduResponse::Status::SW_WRONG_LENGTH:
       // Continue to iterate through the provided key handles in the exclude
       // list to check for already registered keys.
-      if (++current_key_handle_index_ < request().exclude_list->size()) {
+      current_key_handle_index_++;
+      if (current_key_handle_index_ == request().exclude_list->size() &&
+          !probing_alternative_rp_id_ && request().app_id) {
+        // All elements of |request().exclude_list| have been tested, but
+        // there's a second AppID so they need to be tested again.
+        probing_alternative_rp_id_ = true;
+        current_key_handle_index_ = 0;
+      }
+      if (current_key_handle_index_ < request().exclude_list->size()) {
         TrySign();
       } else {
         // Reached the end of exclude list with no duplicate credential.
diff --git a/device/fido/u2f_register_operation.h b/device/fido/u2f_register_operation.h
index e9ee470..bc05811 100644
--- a/device/fido/u2f_register_operation.h
+++ b/device/fido/u2f_register_operation.h
@@ -54,6 +54,10 @@
 
   size_t current_key_handle_index_ = 0;
   bool canceled_ = false;
+  // probing_alternative_rp_id_ is true if |app_id| is set in |request()| and
+  // thus the exclude list is being probed a second time with the alternative RP
+  // ID.
+  bool probing_alternative_rp_id_ = false;
   base::WeakPtrFactory<U2fRegisterOperation> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(U2fRegisterOperation);
diff --git a/docs/README.md b/docs/README.md
index e1f2150..86f7b11c 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -102,6 +102,10 @@
     update the clang-format binaries that come with a checkout of Chromium
 
 ### General Development
+*   [Contributing to Chromium](contributing.md) - Reference workflow process for
+    contributing to the Chromium code base.
+*   [Commit Checklist](commit_checklist.md) - Streamlined checklist to go
+    through before uploading CLs on Gerrit.
 *   [Code Reviews](code_reviews.md) - Code review requirements and guidelines
 *   [Respectful Code Reviews](cr_respect.md) - A guide for code reviewers
 *   [Respectful Changes](cl_respect.md) - A guide for code authors
diff --git a/docs/commit_checklist.md b/docs/commit_checklist.md
new file mode 100644
index 0000000..7a63b72
--- /dev/null
+++ b/docs/commit_checklist.md
@@ -0,0 +1,126 @@
+# Commit Checklist for Chromium Workflow
+
+Here is a helpful checklist to go through before uploading change lists (CLs)
+on Gerrit. This checklist is designed to be streamlined. See [contributing to
+Chromium][contributing] for a more thorough reference.
+
+[TOC]
+
+## 1. Create a new branch
+
+You should create a new branch before starting any development work. It's
+helpful to branch early and to branch often in Git.
+
+    git new-branch <branch_name>
+
+which is equivalent to
+
+    git checkout -b <branch_name> --track origin/master
+
+## 2. Make sure the code builds correctly
+
+After making your changes, check that common targets build correctly:
+
+*   chrome (for Linux, ChromeOS, etc.)
+*   unit_tests
+*   browser_tests
+
+It's easy to inadvertently break one of the other builds you're not currently
+working on without realizing it. Even though the Commit Queue should catch any
+build errors, checking locally first can save you some time since the CQ Dry Run
+can take a while.
+
+## 3. Test your changes
+
+Make sure you hit every code path you changed.
+
+## 4. Write unit or browser tests for any new code
+
+Consider automating any manual testing you did in the previous step.
+
+## 5. Ensure the code is formatted nicely
+
+Run `git cl format --js`. The `--js` option also formats JavaScript changes.
+
+## 6. Check over your changes
+
+Run `git upstream-diff` to check over all of the changes you've made from
+the most recent checkpoint on the remote repository.
+
+## 7. Stage relevant files for commit
+
+Run `git add <path_to_file>` for all of the relevant files you've modified.
+
+## 8. Commit your changes
+
+Run `git commit`. Here are some
+[tips for writing good commit messages][uploading-a-change-for-review].
+
+## 9. Rebase your local repository
+
+Run `git rebase-update`. This command updates all of your local branches with
+remote changes that have landed since you started development work, which
+could've been a while ago. It also deletes any branches that match the remote
+repository, such as after the CL associated with that branch had merged. You
+may run into rebase conflicts which should be manually fixed before proceeding
+with `git rebase --continue`. Rebasing prevents unintended changes from creeping
+into your CL.
+
+Note that rebasing has the potential to break your build, so you might want to
+try re-building afterwards.
+
+## 10. Upload the CL to Gerrit
+
+Run `git cl upload`. Some useful options include:
+
+* `--cq-dry-run` (or `-d`) will set the patchset to do a CQ Dry Run.
+* `-r <chromium_username>` will add reviewers.
+* `-b <bug_number>` automatically populates the bug reference line of the commit
+message.
+
+## 11. Check the CL again in Gerrit
+
+Run `git cl web` to go to the Gerrit URL associated with the current branch.
+Open the latest patch set and verify that all of the uploaded files are
+correct. Click `Expand All` to check over all of the individual line-by-line
+changes again.
+
+## 12. Make sure all auto-regression tests pass
+
+Click `CQ Dry Run`. Fix any errors because otherwise the CL won't pass the
+commit queue (CQ) checks. Consider waiting for the CQ Dry Run to pass before
+notifying your reviewers, in case the results require major changes in your CL.
+
+## 13. Add reviewers to review your code
+
+Click `Find Owners` or run `git cl owners` to find file owners to review your
+code and instruct them about which parts you want them to focus on. Add anyone
+else you think should review your code. For your CL to land, you need an
+approval from an owner for each file you've changed, unless you are an owner of
+some files, in which case you don't need separate owner approval for those
+files.
+
+## 14. Implement feedback from your reviewers
+
+Then go through this commit checklist again. Reply to all comments from the
+reviewers on Gerrit and mark all resolved issues as resolved (clicking `Done` or
+`Ack` will do this automatically). Click `Reply` to ensure that your reviewers
+receive a notification. Doing this signals that your CL is ready for review
+again, since the assumption is that your CL is not ready for review until you
+hit reply.
+
+## 15. Land your CL
+
+Once you have obtained a Looks Good To Me (LGTM), which is reflected by a
+Code-Review+1 in Gerrit, from at least one owner for each file, then you have
+the minimum prerequisite to land your changes. It may be helpful to wait for all
+of your reviewers to approve your changes as well, even if they're not owners.
+Click `Submit to CQ` to try your change in the commit queue (CQ), which will
+land it if successful.
+
+After your CL is landed, you can use `git rebase-update` or `git cl archive` to
+clean up your local branches.
+
+[//]: # (the reference link section should be alphabetically sorted)
+[contributing]: contributing.md
+[uploading-a-change-for-review]: contributing.md#Uploading-a-change-for-review
diff --git a/docs/contributing.md b/docs/contributing.md
index cf5924b..32239ca 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -12,7 +12,9 @@
 
 - [Life of a Chromium Developer][life-of-a-chromium-developer], which is mostly
   up-to-date.
-- [Tutorial][noms-tutorial] by committer emeritus noms@chromium.org
+- [Tutorial][noms-tutorial] by committer emeritus noms@chromium.org.
+- [Commit Checklist][commit-checklist], a useful checklist to go through before
+  submitting each CL on Gerrit.
 
 ## Communicate
 
@@ -108,6 +110,9 @@
 
 ## Uploading a change for review
 
+Note: go through the [commit checklist][commit-checklist] for Chromium before
+uploading a change for review.
+
 Chromium uses a Gerrit instance hosted at
 <https://chromium-review.googlesource.com> for code reviews. In order to upload
 your local change to Gerrit, use `git-cl` from
@@ -306,6 +311,7 @@
 [cl-footer-syntax]: https://dev.chromium.org/developers/contributing-code/-bug-syntax
 [code-reviews-owners]: code_reviews.md#OWNERS-files
 [code-reviews]: code_reviews.md
+[commit-checklist]: commit_checklist.md
 [commit-queue]: infra/cq.md
 [core-principles]: https://www.chromium.org/developers/core-principles
 [corporate-cla]: https://cla.developers.google.com/about/google-corporate?csw=1
diff --git a/docs/security/ev-to-page-info-images/chrome-76-ev-bar.png b/docs/security/ev-to-page-info-images/chrome-76-ev-bar.png
new file mode 100644
index 0000000..d5a3b76
--- /dev/null
+++ b/docs/security/ev-to-page-info-images/chrome-76-ev-bar.png
Binary files differ
diff --git a/docs/security/ev-to-page-info-images/chrome-77-page-info.png b/docs/security/ev-to-page-info-images/chrome-77-page-info.png
new file mode 100644
index 0000000..570beaf
--- /dev/null
+++ b/docs/security/ev-to-page-info-images/chrome-77-page-info.png
Binary files differ
diff --git a/docs/security/ev-to-page-info.md b/docs/security/ev-to-page-info.md
new file mode 100644
index 0000000..8097c52
--- /dev/null
+++ b/docs/security/ev-to-page-info.md
@@ -0,0 +1,99 @@
+# EV UI Moving to Page Info
+
+As part of a series of data-driven
+[changes](https://blog.chromium.org/2018/05/evolving-chromes-security-indicators.html)
+to Chrome’s security indicators, the Chrome Security UX team is announcing a
+change to the
+[Extended Validation](https://en.wikipedia.org/wiki/Extended_Validation_Certificate)
+certificate indicator on certain websites starting in Chrome 77. This doc
+explains what’s being changed and why, as well as the supporting research
+that guided this decision.
+
+On HTTPS websites using [EV](https://en.wikipedia.org/wiki/Extended_Validation_Certificate)
+certificates, Chrome 76 currently displays an EV badge to the left
+of the URL bar that looks like this:
+
+![Chrome 76 EV UI](ev-to-page-info-images/chrome-76-ev-bar.png "Chrome 76 EV
+UI")
+
+Starting in Version 77, Chrome will move this UI to Page Info, which is accessed
+by clicking the lock icon:
+
+![Chrome 77 Page Info UI](ev-to-page-info-images/chrome-77-page-info.png "Chrome
+77 Page Info UI")
+
+Through our own research as well as a survey of prior academic work, the Chrome
+Security UX team has determined that the EV UI does not protect users as
+intended (see [Further Reading](#Further-Reading) below). Users do not appear
+to make secure choices (such as not entering password or credit card
+information) when the UI is altered or removed, as would be necessary for EV UI
+to provide meaningful protection. Further, the EV badge takes up valuable
+screen real estate, can present
+[actively confusing company names](https://www.typewritten.net/writer/ev-phishing/)
+in prominent UI, and interferes with Chrome's product direction towards
+neutral, rather than positive,
+[display for secure connections](https://blog.chromium.org/2018/05/evolving-chromes-security-indicators.html).
+Because of these problems and its limited utility, we believe it belongs better
+in Page Info.
+
+Altering the EV UI is a part of a wider trend among browsers to improve their
+Security UI surfaces in light of recent advances in understanding of this
+problem space. In 2018, Apple
+[announced a similar change](https://cabforum.org/2018/06/06/minutes-for-ca-browser-forum-f2f-meeting-44-london-6-7-june-2018/#Apple-Root-Program-Update)
+to Safari that coincided with the release of iOS 12 and macOS 10.14 and has
+been implemented as such ever since.
+
+### Information for embedders
+
+This change is being incorporated into the Chrome-specific UI code and will not
+affect embedders that are based solely on the underlying content layer.
+Embedders that incorporate the Chrome-specific code will either take up these
+changes or maintain a diff from the master Chromium branch.
+
+
+## Further Reading
+
+A series of academic research in the 2000s studied the EV UI in lab and survey
+settings, and found that the EV UI was not protecting against phishing attacks
+as intended. The Chrome Security UX team recently published a study that updated
+these findings with a large-scale field experiment, as well as a series of
+survey experiments.
+
+No one single study conclusively determines that EV UI is completely ineffective
+or cannot be made to be effective. However, we believe that the body of
+research, as well as the product principles outlined above, together strongly
+suggest that the EV UI does not belong in Chrome’s most visible UI surface.
+
+### External Research:
+
+*   [An evaluation of extended validation and picture-in-picture phishing attacks](https://www.adambarth.com/papers/2007/jackson-simon-tan-barth.pdf):
+	surveys participants about IE 7’s EV UI and concludes that it did not help
+    	users identify two types of phishing attacks, even after participants
+    	received education about the UI.
+*   [Exploring User Reactions to New Browser Cues for Extended Validation Certificates](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.543.2117&rep=rep1&type=pdf):
+	studies Firefox 3’s EV UI and found
+	that users did not notice it. The researchers presented a re-designed
+	indicator which some users did notice but did not use in their decision-
+	making.
+*   [Browser interfaces and extended validation SSL certificates: An empirical study](http://people.scs.carleton.ca/~paulv/papers/ccsw09.pdf):
+	explores a new EV UI design in comparison to IE 7’s design. The researchers
+	showed promising results on some axes but did not study whether the new
+	design actually helps users detect attacks.
+*   [The Emperor’s New Security Indicators: An evaluation of website authentication and the effect of role playing on usability studies](http://andyozment.com/papers/emperor.pdf):
+	does not study EV specifically, but studies other positive (non-warning)
+	security indicators for website authentication via lab study and finds that
+	users do not notice their absence.
+
+### Chrome Research:
+
+*   [The Web’s Identity Crisis: Understanding the Effectiveness of Website Identity Indicators](https://storage.googleapis.com/pub-tools-public-publication-data/pdf/400599205ab5a1c9efa03e2a7c127eb8200bf288.pdf):
+	a large-scale field experiment in which the EV UI was removed for a random
+	subset of users, and a wide variety of user behavior metrics did not change,
+	suggesting that the EV UI is not having its intended effect. Survey
+	experiments also confirm that users do not react as intended to positive or
+	neutral security UI.
+*   [Rethinking Connection Security Indicators](https://storage.googleapis.com/pub-tools-public-publication-data/pdf/45366.pdf):
+	does not study EV specifically, but studies users’ reaction to other
+	connection security indicators like the lock icon via survey, and finds that
+	users are widely confused about their meaning. Informs Chrome’s overall
+	direction to remove positive security indicators.
diff --git a/extensions/browser/api/declarative_net_request/flat/extension_ruleset.fbs b/extensions/browser/api/declarative_net_request/flat/extension_ruleset.fbs
index 1fa30e7..8eafd6c6 100644
--- a/extensions/browser/api/declarative_net_request/flat/extension_ruleset.fbs
+++ b/extensions/browser/api/declarative_net_request/flat/extension_ruleset.fbs
@@ -30,7 +30,12 @@
    /// If valid, doesn't include the '?' separator.
    query : string;
 
+   /// Query params to be removed. These are already sorted and escaped using
+   /// net::EscapeQueryParamValue.
    remove_query_params : [string];
+
+   /// Query params to be added/replaced. These are already escaped using
+   /// net::EscapeQueryParamValue.
    add_or_replace_query_params : [QueryKeyValue];
 
    clear_fragment : bool = false;
diff --git a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc
index a51e7ce..ee2b2d0 100644
--- a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc
+++ b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "extensions/browser/api/declarative_net_request/indexed_rule.h"
+#include "net/base/escape.h"
 
 namespace extensions {
 namespace declarative_net_request {
@@ -28,17 +29,18 @@
 using FlatStringListOffset = FlatVectorOffset<flatbuffers::String>;
 
 // Writes to |builder| a flatbuffer vector of shared strings corresponding to
-// |vec| and returns the offset to it. If |vec| is empty, returns an empty
-// offset.
+// |container| and returns the offset to it. If |container| is empty, returns an
+// empty offset.
+template <typename T>
 FlatStringListOffset BuildVectorOfSharedStrings(
     flatbuffers::FlatBufferBuilder* builder,
-    const std::vector<std::string>& vec) {
-  if (vec.empty())
+    const T& container) {
+  if (container.empty())
     return FlatStringListOffset();
 
   std::vector<FlatStringOffset> offsets;
-  offsets.reserve(vec.size());
-  for (const auto& str : vec)
+  offsets.reserve(container.size());
+  for (const std::string& str : container)
     offsets.push_back(builder->CreateSharedString(str));
   return builder->CreateVector(offsets);
 }
@@ -110,9 +112,18 @@
   FlatStringOffset password = create_string_offset(transform.password);
 
   FlatStringListOffset remove_query_params;
+  const bool use_plus = true;
   if (transform.query_transform && transform.query_transform->remove_params) {
-    remove_query_params = BuildVectorOfSharedStrings(
-        builder, *transform.query_transform->remove_params);
+    // Escape, sort and remove duplicates.
+    std::set<std::string> remove_params_escaped;
+    for (const std::string& remove_param :
+         *transform.query_transform->remove_params) {
+      remove_params_escaped.insert(
+          net::EscapeQueryParamValue(remove_param, use_plus));
+    }
+
+    remove_query_params =
+        BuildVectorOfSharedStrings(builder, remove_params_escaped);
   }
 
   FlatVectorOffset<flat::QueryKeyValue> add_or_replace_params;
@@ -124,9 +135,12 @@
         transform.query_transform->add_or_replace_params->size());
     for (const dnr_api::QueryKeyValue& query_pair :
          *transform.query_transform->add_or_replace_params) {
-      add_or_replace_queries.push_back(flat::CreateQueryKeyValue(
-          *builder, builder->CreateSharedString(query_pair.key),
-          builder->CreateSharedString(query_pair.value)));
+      FlatStringOffset key = builder->CreateSharedString(
+          net::EscapeQueryParamValue(query_pair.key, use_plus));
+      FlatStringOffset value = builder->CreateSharedString(
+          net::EscapeQueryParamValue(query_pair.value, use_plus));
+      add_or_replace_queries.push_back(
+          flat::CreateQueryKeyValue(*builder, key, value));
     }
     add_or_replace_params = builder->CreateVector(add_or_replace_queries);
   }
diff --git a/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc b/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc
index e08e583..2667906b 100644
--- a/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc
@@ -125,7 +125,7 @@
   EXPECT_EQ(StripCommentsAndWhitespace(kFlatbufferSchemaExpected),
             StripCommentsAndWhitespace(flatbuffer_schema))
       << "Schema change detected; update this test and the schema version.";
-  EXPECT_EQ(10, GetIndexedRulesetFormatVersionForTesting())
+  EXPECT_EQ(11, GetIndexedRulesetFormatVersionForTesting())
       << "Update this test if you update the schema version.";
 }
 
diff --git a/extensions/browser/api/declarative_net_request/ruleset_matcher.cc b/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
index 8c08200..0e8e5a3 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
+++ b/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
@@ -22,7 +22,6 @@
 #include "extensions/browser/api/web_request/web_request_info.h"
 #include "extensions/common/api/declarative_net_request.h"
 #include "extensions/common/api/declarative_net_request/utils.h"
-#include "net/base/escape.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/base/url_util.h"
 #include "url/url_constants.h"
@@ -152,6 +151,12 @@
   return base::StringPiece(str.c_str(), str.size());
 }
 
+// Returns true if the given |vec| is nullptr or empty.
+template <typename T>
+bool IsEmpty(const flatbuffers::Vector<T>* vec) {
+  return !vec || vec->size() == 0;
+}
+
 // Performs any required query transformations on the |url|. Returns true if the
 // query should be modified and populates |modified_query|.
 bool GetModifiedQuery(const GURL& url,
@@ -159,41 +164,44 @@
                       std::string* modified_query) {
   DCHECK(modified_query);
 
-  // TODO(crbug.com/983685): We should be able to reduce the work here by
-  // storing the escaped query params in the indexed format.
-  const bool use_plus = true;
-  std::set<std::string> escaped_remove_query_params;
-  if (transform.remove_query_params()) {
-    for (const ::flatbuffers::String* str : *transform.remove_query_params()) {
-      escaped_remove_query_params.insert(
-          net::EscapeQueryParamValue(CreateStringPiece(*str), use_plus));
-    }
+  // |remove_query_params| should always be sorted.
+  DCHECK(
+      IsEmpty(transform.remove_query_params()) ||
+      std::is_sorted(transform.remove_query_params()->begin(),
+                     transform.remove_query_params()->end(),
+                     [](const flatbuffers::String* x1,
+                        const flatbuffers::String* x2) { return *x1 < *x2; }));
+
+  // Return early if there's nothing to modify.
+  if (IsEmpty(transform.remove_query_params()) &&
+      IsEmpty(transform.add_or_replace_query_params())) {
+    return false;
+  }
+
+  std::vector<base::StringPiece> remove_query_params;
+  if (!IsEmpty(transform.remove_query_params())) {
+    remove_query_params.reserve(transform.remove_query_params()->size());
+    for (const ::flatbuffers::String* str : *transform.remove_query_params())
+      remove_query_params.push_back(CreateStringPiece(*str));
   }
 
   // We don't use a map from keys to vector of values to ensure the relative
-  // order of different params specified by the extension is respected. We use
-  // a std::list to support fast removal from middle of the list.
-  std::list<std::pair<std::string, std::string>>
-      escaped_add_or_replace_query_params;
-  if (transform.add_or_replace_query_params()) {
+  // order of different params specified by the extension is respected. We use a
+  // std::list to support fast removal from middle of the list. Note that the
+  // key value pairs should already be escaped.
+  std::list<std::pair<base::StringPiece, base::StringPiece>>
+      add_or_replace_query_params;
+  if (!IsEmpty(transform.add_or_replace_query_params())) {
     for (const flat::QueryKeyValue* query_pair :
          *transform.add_or_replace_query_params()) {
       DCHECK(query_pair->key());
       DCHECK(query_pair->value());
-      std::string key = net::EscapeQueryParamValue(
-          CreateStringPiece(*query_pair->key()), use_plus);
-      std::string value = net::EscapeQueryParamValue(
-          CreateStringPiece(*query_pair->value()), use_plus);
-      escaped_add_or_replace_query_params.emplace_back(std::move(key),
-                                                       std::move(value));
+      add_or_replace_query_params.emplace_back(
+          CreateStringPiece(*query_pair->key()),
+          CreateStringPiece(*query_pair->value()));
     }
   }
 
-  if (escaped_add_or_replace_query_params.empty() &&
-      escaped_remove_query_params.empty()) {
-    return false;
-  }
-
   std::vector<std::string> query_parts;
 
   auto create_query_part = [](base::StringPiece key, base::StringPiece value) {
@@ -204,20 +212,20 @@
   for (net::QueryIterator it(url); !it.IsAtEnd(); it.Advance()) {
     std::string key = it.GetKey();
     // Remove query param.
-    if (base::Contains(escaped_remove_query_params, key)) {
+    if (std::binary_search(remove_query_params.begin(),
+                           remove_query_params.end(), key)) {
       query_changed = true;
       continue;
     }
 
-    auto replace_iterator =
-        std::find_if(escaped_add_or_replace_query_params.begin(),
-                     escaped_add_or_replace_query_params.end(),
-                     [&key](const std::pair<std::string, std::string>& param) {
-                       return param.first == key;
-                     });
+    auto replace_iterator = std::find_if(
+        add_or_replace_query_params.begin(), add_or_replace_query_params.end(),
+        [&key](const std::pair<base::StringPiece, base::StringPiece>& param) {
+          return param.first == key;
+        });
 
     // Nothing to do.
-    if (replace_iterator == escaped_add_or_replace_query_params.end()) {
+    if (replace_iterator == add_or_replace_query_params.end()) {
       query_parts.push_back(create_query_part(key, it.GetValue()));
       continue;
     }
@@ -225,11 +233,11 @@
     // Replace query param.
     query_changed = true;
     query_parts.push_back(create_query_part(key, replace_iterator->second));
-    escaped_add_or_replace_query_params.erase(replace_iterator);
+    add_or_replace_query_params.erase(replace_iterator);
   }
 
   // Append any remaining query params.
-  for (const auto& params : escaped_add_or_replace_query_params) {
+  for (const auto& params : add_or_replace_query_params) {
     query_changed = true;
     query_parts.push_back(create_query_part(params.first, params.second));
   }
diff --git a/extensions/browser/api/declarative_net_request/utils.cc b/extensions/browser/api/declarative_net_request/utils.cc
index 2a1a19f..5da9751a 100644
--- a/extensions/browser/api/declarative_net_request/utils.cc
+++ b/extensions/browser/api/declarative_net_request/utils.cc
@@ -33,7 +33,7 @@
 // url_pattern_index.fbs. Whenever an extension with an indexed ruleset format
 // version different from the one currently used by Chrome is loaded, the
 // extension ruleset will be reindexed.
-constexpr int kIndexedRulesetFormatVersion = 10;
+constexpr int kIndexedRulesetFormatVersion = 11;
 
 // This static assert is meant to catch cases where
 // url_pattern_index::kUrlPatternIndexFormatVersion is incremented without
diff --git a/fuchsia/cipd/BUILD.gn b/fuchsia/cipd/BUILD.gn
index c99925a..ecaf66b3 100644
--- a/fuchsia/cipd/BUILD.gn
+++ b/fuchsia/cipd/BUILD.gn
@@ -57,6 +57,7 @@
 #   cipd_path: The path where the package will be located inside the CIPD
 #              repository.
 #   cipd_description: Sets the "description" field in CIPD metadata.
+#   install_mode: String, should be either "symlink" or "copy".
 #   deps: A list of targets to build prior to copying files.
 #   sources: A list of files to copy into the staging root.
 template("cipd_archive") {
@@ -65,15 +66,21 @@
                            "cipd_manifest_name",
                            "cipd_path",
                            "cipd_description",
+                           "install_mode",
                            "deps",
                            "sources",
                          ])
   archive_staging_dir = "${target_gen_dir}/${target_name}"
-
+  if (!defined(invoker.install_mode)) {
+    install_mode = "symlink"
+  }
+  assert(install_mode == "copy" || install_mode == "symlink",
+         "\"install_mode\" arg should be either \"copy\" or \"symlink\".")
   yaml_contents = [
     "package: ${cipd_path}",
     "description: ${cipd_description}",
     "root: \${outdir}/" + rebase_path(archive_staging_dir, root_build_dir),
+    "install_mode: ${install_mode}",
     "data:",
     "  - file: LICENSE",
   ]
@@ -178,7 +185,7 @@
   cipd_manifest_name = "chromedriver.yaml"
   cipd_path = "chromium/fuchsia/chromedriver/\${os}-\${arch}"
   cipd_description = "Prebuilt Chromedriver binary for Fuchsia host."
-
+  install_mode = "copy"
   deps = [
     ":strip_chromedriver_binary",
   ]
diff --git a/gpu/command_buffer/client/BUILD.gn b/gpu/command_buffer/client/BUILD.gn
index 5683ffe..05ea3f0 100644
--- a/gpu/command_buffer/client/BUILD.gn
+++ b/gpu/command_buffer/client/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/jumbo.gni")
+import("//ui/gl/features.gni")
 
 declare_args() {
   # Enable GPU client logging without DCHECK being on.
@@ -311,6 +312,12 @@
     "webgpu_implementation_autogen.h",
     "webgpu_implementation_impl_autogen.h",
   ]
+  if (use_dawn) {
+    sources += [
+      "dawn_client_memory_transfer_service.cc",
+      "dawn_client_memory_transfer_service.h",
+    ]
+  }
 }
 
 # Library emulates GLES2 using command_buffers.
diff --git a/gpu/command_buffer/client/dawn_client_memory_transfer_service.cc b/gpu/command_buffer/client/dawn_client_memory_transfer_service.cc
new file mode 100644
index 0000000..f54f576
--- /dev/null
+++ b/gpu/command_buffer/client/dawn_client_memory_transfer_service.cc
@@ -0,0 +1,165 @@
+// 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 "gpu/command_buffer/client/dawn_client_memory_transfer_service.h"
+
+#include "gpu/command_buffer/client/mapped_memory.h"
+#include "gpu/command_buffer/common/dawn_memory_transfer_handle.h"
+
+namespace gpu {
+namespace webgpu {
+
+class DawnClientMemoryTransferService::ReadHandleImpl
+    : public dawn_wire::client::MemoryTransferService::ReadHandle {
+ public:
+  ReadHandleImpl(void* ptr,
+                 MemoryTransferHandle handle,
+                 DawnClientMemoryTransferService* service)
+      : ReadHandle(), ptr_(ptr), handle_(handle), service_(service) {}
+
+  ~ReadHandleImpl() override {
+    // The shared memory can't be freed until the server consumes it. Add the
+    // the pointer to a list of blocks to process on the next Flush.
+    service_->MarkHandleFree(ptr_);
+  }
+
+  // Get the serialization size of SerializeCreate.
+  size_t SerializeCreateSize() override { return sizeof(MemoryTransferHandle); }
+
+  // Serialize the handle into |serialize_pointer| so it can be received by the
+  // service.
+  void SerializeCreate(void* serialize_pointer) override {
+    *reinterpret_cast<MemoryTransferHandle*>(serialize_pointer) = handle_;
+  }
+
+  // Load initial data and open the handle for reading.
+  // This function takes in the serialized result of
+  // ReadHandle::SerializeInitialData.
+  // It writes to |data| and |data_length| the pointer and size
+  // of the mapped data for reading.
+  // The allocation must live at least until the ReadHandle is destructed.
+  bool DeserializeInitialData(const void* deserialize_pointer,
+                              size_t deserialize_size,
+                              const void** data,
+                              size_t* data_length) override {
+    // No data is deserialized because we're using shared memory.
+    DCHECK_EQ(deserialize_size, 0u);
+    DCHECK(data);
+    DCHECK(data_length);
+
+    // Write the pointer and size of the shared memory allocation.
+    // |data| and |data_length| are provided by the dawn_wire client.
+    *data = ptr_;
+    *data_length = handle_.size;
+
+    return true;
+  }
+
+ private:
+  void* ptr_;  // Pointer to client-side shared memory.
+  MemoryTransferHandle handle_;
+  DawnClientMemoryTransferService* service_;
+};
+
+class DawnClientMemoryTransferService::WriteHandleImpl
+    : public dawn_wire::client::MemoryTransferService::WriteHandle {
+ public:
+  WriteHandleImpl(void* ptr,
+                  MemoryTransferHandle handle,
+                  DawnClientMemoryTransferService* service)
+      : WriteHandle(), ptr_(ptr), handle_(handle), service_(service) {}
+
+  ~WriteHandleImpl() override {
+    // The shared memory can't be freed until the server consumes it. Add
+    // the pointer to a list of blocks to process on the next Flush.
+    service_->MarkHandleFree(ptr_);
+  }
+
+  // Get the serialization size of SerializeCreate.
+  size_t SerializeCreateSize() override { return sizeof(MemoryTransferHandle); }
+
+  // Serialize the handle into |serialize_pointer| so it can be received by the
+  // service.
+  void SerializeCreate(void* serialize_pointer) override {
+    *reinterpret_cast<MemoryTransferHandle*>(serialize_pointer) = handle_;
+  }
+
+  // Open the handle for writing.
+  // The data returned must live at least until the WriteHandle is destructed.
+  std::pair<void*, size_t> Open() override {
+    return std::make_pair(ptr_, handle_.size);
+  }
+
+  size_t SerializeFlushSize() override {
+    // No data is serialized because we're using shared memory.
+    return 0;
+  }
+
+  void SerializeFlush(void* serialize_pointer) override {
+    // No data is serialized because we're using shared memory.
+  }
+
+ private:
+  void* ptr_;
+  MemoryTransferHandle handle_;
+  DawnClientMemoryTransferService* service_;
+};
+
+DawnClientMemoryTransferService::DawnClientMemoryTransferService(
+    MappedMemoryManager* mapped_memory)
+    : dawn_wire::client::MemoryTransferService(),
+      mapped_memory_(mapped_memory) {}
+
+DawnClientMemoryTransferService::~DawnClientMemoryTransferService() = default;
+
+dawn_wire::client::MemoryTransferService::ReadHandle*
+DawnClientMemoryTransferService::CreateReadHandle(size_t size) {
+  MemoryTransferHandle handle = {};
+  void* ptr = AllocateHandle(size, &handle);
+  if (ptr == nullptr) {
+    return nullptr;
+  }
+  return new ReadHandleImpl(ptr, handle, this);
+}
+
+dawn_wire::client::MemoryTransferService::WriteHandle*
+DawnClientMemoryTransferService::CreateWriteHandle(size_t size) {
+  MemoryTransferHandle handle = {};
+  void* ptr = AllocateHandle(size, &handle);
+  if (ptr == nullptr) {
+    return nullptr;
+  }
+  // Zero-initialize the data.
+  memset(ptr, 0, handle.size);
+  return new WriteHandleImpl(ptr, handle, this);
+}
+
+void* DawnClientMemoryTransferService::AllocateHandle(
+    size_t size,
+    MemoryTransferHandle* handle) {
+  if (size > std::numeric_limits<uint32_t>::max()) {
+    return nullptr;
+  }
+
+  DCHECK(handle);
+  handle->size = static_cast<uint32_t>(size);
+
+  DCHECK(mapped_memory_);
+  return mapped_memory_->Alloc(handle->size, &handle->shm_id,
+                               &handle->shm_offset);
+}
+
+void DawnClientMemoryTransferService::MarkHandleFree(void* ptr) {
+  free_blocks_.push_back(ptr);
+}
+
+void DawnClientMemoryTransferService::FreeHandlesPendingToken(int32_t token) {
+  std::vector<void*> to_free = std::move(free_blocks_);
+  for (void* ptr : to_free) {
+    mapped_memory_->FreePendingToken(ptr, token);
+  }
+}
+
+}  // namespace webgpu
+}  // namespace gpu
diff --git a/gpu/command_buffer/client/dawn_client_memory_transfer_service.h b/gpu/command_buffer/client/dawn_client_memory_transfer_service.h
new file mode 100644
index 0000000..4f59c59
--- /dev/null
+++ b/gpu/command_buffer/client/dawn_client_memory_transfer_service.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 GPU_COMMAND_BUFFER_CLIENT_DAWN_CLIENT_MEMORY_TRANSFER_SERVICE_H_
+#define GPU_COMMAND_BUFFER_CLIENT_DAWN_CLIENT_MEMORY_TRANSFER_SERVICE_H_
+
+#include <dawn_wire/WireClient.h>
+#include <vector>
+
+namespace gpu {
+
+class MappedMemoryManager;
+
+namespace webgpu {
+
+struct MemoryTransferHandle;
+
+class DawnClientMemoryTransferService final
+    : public dawn_wire::client::MemoryTransferService {
+ public:
+  DawnClientMemoryTransferService(MappedMemoryManager* mapped_memory);
+  ~DawnClientMemoryTransferService() override;
+
+  // Create a handle for reading shared memory data.
+  // This may fail and return nullptr.
+  ReadHandle* CreateReadHandle(size_t size) override;
+
+  // Create a handle for writing shared memory data.
+  // This may fail and return nullptr.
+  WriteHandle* CreateWriteHandle(size_t size) override;
+
+  // Free shared memory allocations after the token passes on the GPU process.
+  void FreeHandlesPendingToken(int32_t token);
+
+ private:
+  class ReadHandleImpl;
+  class WriteHandleImpl;
+
+  // Allocate a shared memory handle for the memory transfer.
+  void* AllocateHandle(size_t size, MemoryTransferHandle* handle);
+
+  // Mark a shared memory allocation as free. This should not be called more
+  // than once per block.
+  void MarkHandleFree(void* ptr);
+
+  MappedMemoryManager* mapped_memory_;
+  // Pointers to memory allocated by the MappedMemoryManager to free after
+  // the next Flush.
+  std::vector<void*> free_blocks_;
+};
+
+}  // namespace webgpu
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_CLIENT_DAWN_CLIENT_MEMORY_TRANSFER_SERVICE_H_
diff --git a/gpu/command_buffer/client/webgpu_implementation.cc b/gpu/command_buffer/client/webgpu_implementation.cc
index 9af1f5af..b50308c 100644
--- a/gpu/command_buffer/client/webgpu_implementation.cc
+++ b/gpu/command_buffer/client/webgpu_implementation.cc
@@ -9,6 +9,7 @@
 
 #include "base/numerics/checked_math.h"
 #include "base/trace_event/trace_event.h"
+#include "gpu/command_buffer/client/dawn_client_memory_transfer_service.h"
 #include "gpu/command_buffer/client/gpu_control.h"
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 
@@ -28,19 +29,15 @@
     GpuControl* gpu_control)
     : ImplementationBase(helper, transfer_buffer, gpu_control),
       helper_(helper),
-#if BUILDFLAG(USE_DAWN)
-      wire_client_([this]() {
-        dawn_wire::WireClientDescriptor descriptor = {};
-        descriptor.serializer = this;
-
-        return new dawn_wire::WireClient(descriptor);
-      }()),
-      procs_(wire_client_->GetProcs()),
-#endif
       c2s_buffer_(helper, transfer_buffer) {
 }
 
-WebGPUImplementation::~WebGPUImplementation() {}
+WebGPUImplementation::~WebGPUImplementation() {
+  // Wait for all commands to finish or we may free shared memory while
+  // commands are still in flight.
+  Flush();
+  helper_->Finish();
+}
 
 gpu::ContextResult WebGPUImplementation::Initialize(
     const SharedMemoryLimits& limits) {
@@ -53,6 +50,19 @@
   c2s_buffer_default_size_ = limits.start_transfer_buffer_size;
   DCHECK_GT(c2s_buffer_default_size_, 0u);
 
+#if BUILDFLAG(USE_DAWN)
+  memory_transfer_service_.reset(
+      new DawnClientMemoryTransferService(mapped_memory_.get()));
+
+  dawn_wire::WireClientDescriptor descriptor = {};
+  descriptor.serializer = this;
+  descriptor.memoryTransferService = memory_transfer_service_.get();
+
+  wire_client_.reset(new dawn_wire::WireClient(descriptor));
+
+  procs_ = wire_client_->GetProcs();
+#endif
+
   return gpu::ContextResult::kSuccess;
 }
 
@@ -278,6 +288,9 @@
     c2s_put_offset_ = 0;
     c2s_buffer_.Release();
   }
+#if BUILDFLAG(USE_DAWN)
+  memory_transfer_service_->FreeHandlesPendingToken(helper_->InsertToken());
+#endif
   return true;
 }
 
diff --git a/gpu/command_buffer/client/webgpu_implementation.h b/gpu/command_buffer/client/webgpu_implementation.h
index fe7adeb..744c2045 100644
--- a/gpu/command_buffer/client/webgpu_implementation.h
+++ b/gpu/command_buffer/client/webgpu_implementation.h
@@ -24,6 +24,8 @@
 namespace gpu {
 namespace webgpu {
 
+class DawnClientMemoryTransferService;
+
 class WEBGPU_EXPORT WebGPUImplementation final
     : public dawn_wire::CommandSerializer,
       public WebGPUInterface,
@@ -122,6 +124,7 @@
 
   WebGPUCmdHelper* helper_;
 #if BUILDFLAG(USE_DAWN)
+  std::unique_ptr<DawnClientMemoryTransferService> memory_transfer_service_;
   std::unique_ptr<dawn_wire::WireClient> wire_client_;
 #endif
   DawnProcTable procs_ = {};
diff --git a/gpu/command_buffer/common/BUILD.gn b/gpu/command_buffer/common/BUILD.gn
index 8988618..7728218 100644
--- a/gpu/command_buffer/common/BUILD.gn
+++ b/gpu/command_buffer/common/BUILD.gn
@@ -177,6 +177,7 @@
   visibility = [ "//gpu/*" ]
 
   sources = [
+    "dawn_memory_transfer_handle.h",
     "webgpu_cmd_format.cc",
     "webgpu_cmd_format.h",
     "webgpu_cmd_format_autogen.h",
diff --git a/gpu/command_buffer/common/dawn_memory_transfer_handle.h b/gpu/command_buffer/common/dawn_memory_transfer_handle.h
new file mode 100644
index 0000000..957cfce
--- /dev/null
+++ b/gpu/command_buffer/common/dawn_memory_transfer_handle.h
@@ -0,0 +1,25 @@
+// 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 GPU_COMMAND_BUFFER_COMMON_DAWN_MEMORY_TRANSFER_HANDLE_H_
+#define GPU_COMMAND_BUFFER_COMMON_DAWN_MEMORY_TRANSFER_HANDLE_H_
+
+namespace gpu {
+namespace webgpu {
+
+// This struct holds information describing a shared memory allocation used for
+// bulk data transfers between the Dawn client and service. The shared memory is
+// allocated by the client using MappedMemoryManager.
+// On the GPU service, shared memory is received using
+// CommonDecoder::GetSharedMemoryAs which checks the memory region is valid.
+struct MemoryTransferHandle {
+  uint32_t size;
+  int32_t shm_id;
+  uint32_t shm_offset;
+};
+
+}  // namespace webgpu
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_COMMON_DAWN_MEMORY_TRANSFER_HANDLE_H_
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index 96c3ceac..ca343d88 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -268,6 +268,8 @@
 
   if (use_dawn) {
     sources += [
+      "dawn_service_memory_transfer_service.cc",
+      "dawn_service_memory_transfer_service.h",
       "webgpu_decoder_impl.cc",
       "webgpu_decoder_impl.h",
     ]
diff --git a/gpu/command_buffer/service/dawn_service_memory_transfer_service.cc b/gpu/command_buffer/service/dawn_service_memory_transfer_service.cc
new file mode 100644
index 0000000..3f546c4
--- /dev/null
+++ b/gpu/command_buffer/service/dawn_service_memory_transfer_service.cc
@@ -0,0 +1,129 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpu/command_buffer/service/dawn_service_memory_transfer_service.h"
+
+#include "gpu/command_buffer/common/dawn_memory_transfer_handle.h"
+#include "gpu/command_buffer/service/common_decoder.h"
+
+namespace gpu {
+namespace webgpu {
+
+namespace {
+
+class ReadHandleImpl
+    : public dawn_wire::server::MemoryTransferService::ReadHandle {
+ public:
+  ReadHandleImpl(void* ptr, uint32_t size)
+      : ReadHandle(), ptr_(ptr), size_(size) {}
+
+  ~ReadHandleImpl() override = default;
+
+  size_t SerializeInitialDataSize(const void* data,
+                                  size_t data_length) override {
+    // Nothing is serialized because we're using shared memory.
+    return 0;
+  }
+
+  void SerializeInitialData(const void* data,
+                            size_t data_length,
+                            void* serialize_pointer) override {
+    DCHECK_EQ(data_length, size_);
+    // Copy the initial data into the shared memory allocation.
+    // In the case of buffer mapping, this is the mapped GPU memory which we
+    // copy into client-visible shared memory.
+    memcpy(ptr_, data, data_length);
+  }
+
+ private:
+  void* ptr_;
+  uint32_t size_;
+};
+
+class WriteHandleImpl
+    : public dawn_wire::server::MemoryTransferService::WriteHandle {
+ public:
+  WriteHandleImpl(const void* ptr, uint32_t size)
+      : WriteHandle(), ptr_(ptr), size_(size) {}
+
+  ~WriteHandleImpl() override = default;
+
+  bool DeserializeFlush(const void* deserialize_pointer,
+                        size_t deserialize_size) override {
+    // Nothing is serialized because we're using shared memory.
+    DCHECK_EQ(deserialize_size, 0u);
+    DCHECK_EQ(mDataLength, size_);
+    DCHECK(mTargetData);
+    DCHECK(ptr_);
+
+    // Copy from shared memory into the target buffer.
+    memcpy(mTargetData, ptr_, size_);
+    return true;
+  }
+
+ private:
+  const void* ptr_;  // Pointer to client-visible shared memory.
+  uint32_t size_;
+};
+
+}  // namespace
+
+DawnServiceMemoryTransferService::DawnServiceMemoryTransferService(
+    CommonDecoder* decoder)
+    : dawn_wire::server::MemoryTransferService(), decoder_(decoder) {}
+
+DawnServiceMemoryTransferService::~DawnServiceMemoryTransferService() = default;
+
+bool DawnServiceMemoryTransferService::DeserializeReadHandle(
+    const void* deserialize_pointer,
+    size_t deserialize_size,
+    ReadHandle** read_handle) {
+  DCHECK(deserialize_pointer);
+  DCHECK_EQ(deserialize_size, sizeof(MemoryTransferHandle));
+  const volatile MemoryTransferHandle* handle =
+      reinterpret_cast<const volatile MemoryTransferHandle*>(
+          deserialize_pointer);
+
+  uint32_t size = handle->size;
+  int32_t shm_id = handle->shm_id;
+  uint32_t shm_offset = handle->shm_offset;
+
+  void* ptr = decoder_->GetAddressAndCheckSize(shm_id, shm_offset, size);
+  if (ptr == nullptr) {
+    return false;
+  }
+
+  DCHECK(read_handle);
+  *read_handle = new ReadHandleImpl(ptr, size);
+
+  return true;
+}
+
+bool DawnServiceMemoryTransferService::DeserializeWriteHandle(
+    const void* deserialize_pointer,
+    size_t deserialize_size,
+    WriteHandle** write_handle) {
+  DCHECK(deserialize_pointer);
+  DCHECK_EQ(deserialize_size, sizeof(MemoryTransferHandle));
+  const volatile MemoryTransferHandle* handle =
+      reinterpret_cast<const volatile MemoryTransferHandle*>(
+          deserialize_pointer);
+
+  uint32_t size = handle->size;
+  int32_t shm_id = handle->shm_id;
+  uint32_t shm_offset = handle->shm_offset;
+
+  void* ptr = decoder_->GetAddressAndCheckSize(shm_id, shm_offset, size);
+  if (ptr == nullptr) {
+    return false;
+  }
+
+  DCHECK(write_handle);
+  *write_handle = new WriteHandleImpl(ptr, size);
+
+  return true;
+}
+
+}  // namespace webgpu
+}  // namespace gpu
diff --git a/gpu/command_buffer/service/dawn_service_memory_transfer_service.h b/gpu/command_buffer/service/dawn_service_memory_transfer_service.h
new file mode 100644
index 0000000..e6a3ddf
--- /dev/null
+++ b/gpu/command_buffer/service/dawn_service_memory_transfer_service.h
@@ -0,0 +1,45 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GPU_COMMAND_BUFFER_SERVICE_DAWN_SERVICE_MEMORY_TRANSFER_SERVICE_H_
+#define GPU_COMMAND_BUFFER_SERVICE_DAWN_SERVICE_MEMORY_TRANSFER_SERVICE_H_
+
+#include <dawn_wire/WireServer.h>
+
+namespace gpu {
+
+class CommonDecoder;
+
+namespace webgpu {
+
+class DawnServiceMemoryTransferService final
+    : public dawn_wire::server::MemoryTransferService {
+ public:
+  DawnServiceMemoryTransferService(CommonDecoder* decoder);
+  ~DawnServiceMemoryTransferService() override;
+
+  // Deserialize data to create Read/Write handles. These handles are for the
+  // client to Read/Write data. The serialized data is a MemoryTransferHandle
+  // which contains the id, offset, and size of a shared memory region. If the
+  // decoded region is invalid, these functions return false and result in a
+  // context lost.
+  // The Read and Write handles, respectively, are used to
+  //  1) Copy from GPU service memory into client-visible shared memory.
+  //  2) Copy from client-visible shared memory into GPU service memory.
+  bool DeserializeReadHandle(const void* deserialize_pointer,
+                             size_t deserialize_size,
+                             ReadHandle** read_handle) override;
+
+  bool DeserializeWriteHandle(const void* deserialize_pointer,
+                              size_t deserialize_size,
+                              WriteHandle** write_handle) override;
+
+ private:
+  CommonDecoder* decoder_;
+};
+
+}  // namespace webgpu
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_SERVICE_DAWN_SERVICE_MEMORY_TRANSFER_SERVICE_H_
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 52e27ae..772178f 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -18,6 +18,7 @@
 #include "gpu/command_buffer/common/webgpu_cmd_format.h"
 #include "gpu/command_buffer/common/webgpu_cmd_ids.h"
 #include "gpu/command_buffer/service/command_buffer_service.h"
+#include "gpu/command_buffer/service/dawn_service_memory_transfer_service.h"
 #include "gpu/command_buffer/service/decoder_client.h"
 #include "gpu/command_buffer/service/shared_image_factory.h"
 #include "gpu/command_buffer/service/shared_image_manager.h"
@@ -335,6 +336,7 @@
       associated_shared_image_map_;
 
   std::unique_ptr<WireServerCommandSerializer> wire_serializer_;
+  std::unique_ptr<DawnServiceMemoryTransferService> memory_transfer_service_;
   std::unique_ptr<dawn_native::Instance> dawn_instance_;
   DawnProcTable dawn_procs_;
   DawnDevice dawn_device_ = nullptr;
@@ -377,6 +379,7 @@
               shared_image_manager,
               memory_tracker)),
       wire_serializer_(new WireServerCommandSerializer(client)),
+      memory_transfer_service_(new DawnServiceMemoryTransferService(this)),
       dawn_instance_(new dawn_native::Instance()),
       dawn_procs_(dawn_native::GetProcs()) {}
 
@@ -401,6 +404,7 @@
   descriptor.device = dawn_device_;
   descriptor.procs = &dawn_procs_;
   descriptor.serializer = wire_serializer_.get();
+  descriptor.memoryTransferService = memory_transfer_service_.get();
 
   wire_server_ = std::make_unique<dawn_wire::WireServer>(descriptor);
 
diff --git a/gpu/config/gpu_crash_keys.cc b/gpu/config/gpu_crash_keys.cc
index 075630f..5650578 100644
--- a/gpu/config/gpu_crash_keys.cc
+++ b/gpu/config/gpu_crash_keys.cc
@@ -26,6 +26,8 @@
     "available-physical-memory-in-mb");
 crash_reporter::CrashKeyString<1024> current_shader_0("current-shader-0");
 crash_reporter::CrashKeyString<1024> current_shader_1("current-shader-1");
+crash_reporter::CrashKeyString<4> gpu_watchdog_kill_after_power_resume(
+    "gpu-watchdog-kill-after-power-resume");
 
 }  // namespace crash_keys
 }  // namespace gpu
diff --git a/gpu/config/gpu_crash_keys.h b/gpu/config/gpu_crash_keys.h
index e757abe..3bceaa16e 100644
--- a/gpu/config/gpu_crash_keys.h
+++ b/gpu/config/gpu_crash_keys.h
@@ -31,6 +31,8 @@
     available_physical_memory_in_mb;
 extern GPU_EXPORT crash_reporter::CrashKeyString<1024> current_shader_0;
 extern GPU_EXPORT crash_reporter::CrashKeyString<1024> current_shader_1;
+extern GPU_EXPORT crash_reporter::CrashKeyString<4>
+    gpu_watchdog_kill_after_power_resume;
 
 }  // namespace crash_keys
 }  // namespace gpu
diff --git a/gpu/ipc/service/gpu_watchdog_thread_v2.cc b/gpu/ipc/service/gpu_watchdog_thread_v2.cc
index 74f49ab..f754418 100644
--- a/gpu/ipc/service/gpu_watchdog_thread_v2.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread_v2.cc
@@ -13,6 +13,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "gpu/config/gpu_crash_keys.h"
 
 namespace gpu {
 
@@ -130,6 +131,7 @@
   in_power_suspension_ = false;
   RestartWatchdogTimeoutTask();
   resume_timeticks_ = base::TimeTicks::Now();
+  is_first_timeout_after_power_resume = true;
 }
 
 // Running on the watchdog thread.
@@ -162,6 +164,8 @@
         FROM_HERE,
         base::BindOnce(&GpuWatchdogThreadImplV2::OnWatchdogTimeout, weak_ptr_),
         watchdog_timeout_ * kRestartFactor);
+    last_on_watchdog_timeout_timeticks_ = base::TimeTicks::Now();
+    last_on_watchdog_timeout_time_ = base::Time::Now();
   }
 }
 
@@ -202,6 +206,8 @@
   if (disarmed || gpu_makes_progress) {
     last_on_watchdog_timeout_timeticks_ = base::TimeTicks::Now();
     last_on_watchdog_timeout_time_ = base::Time::Now();
+    is_first_timeout_after_power_resume = false;
+
     task_runner()->PostDelayedTask(
         FROM_HERE,
         base::BindOnce(&GpuWatchdogThreadImplV2::OnWatchdogTimeout, weak_ptr_),
@@ -253,6 +259,9 @@
 
   GpuWatchdogHistogram(GpuWatchdogThreadEvent::kGpuWatchdogKill);
 
+  crash_keys::gpu_watchdog_kill_after_power_resume.Set(
+      is_first_timeout_after_power_resume ? "1" : "0");
+
   // Deliberately crash the process to create a crash dump.
   *((volatile int*)0) = 0xdeadface;
 }
diff --git a/gpu/ipc/service/gpu_watchdog_thread_v2.h b/gpu/ipc/service/gpu_watchdog_thread_v2.h
index f5359ce8..3c19615 100644
--- a/gpu/ipc/service/gpu_watchdog_thread_v2.h
+++ b/gpu/ipc/service/gpu_watchdog_thread_v2.h
@@ -91,6 +91,9 @@
   // The system has entered the power suspension mode.
   bool in_power_suspension_ = false;
 
+  // OnWatchdogTimeout() is called for the first time after power resume.
+  bool is_first_timeout_after_power_resume = false;
+
   // Chrome is running on the background on Android. Gpu is probably very slow
   // or stalled.
   bool is_backgrounded_ = false;
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index 70af8adb..49f30bb 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -1595,13 +1595,6 @@
     }
 
     builders {
-      name: "linux-blink-heap-unified-gc"
-      mixins: "fyi-ci"
-      mixins: "linux-xenial"
-      mixins: "builderless"
-    }
-
-    builders {
       name: "linux_chromium_component_updater"
       mixins: "linux-ci"
       mixins: "builderless"
@@ -2244,6 +2237,7 @@
     builders {
       name: "UBSanVptr Linux"
       mixins: "clang-linux-ci"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "linux-win_cross-rel"
@@ -2566,6 +2560,7 @@
       mixins: "fuzz-ci"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "Mac ASAN Release"
@@ -2617,6 +2612,7 @@
       mixins: "fuzz-ci"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "ChromiumOS ASAN Release"
@@ -4264,6 +4260,7 @@
       mixins: "linux-try"
       name: "linux_chromium_ubsan_rel_ng"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       mixins: "linux-dawn-try"
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index a1dd093e..9d2ac5e6 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -2249,11 +2249,6 @@
     short_name: "CM"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/linux-blink-heap-unified-gc"
-    category: "linux|blink"
-    short_name: "UGC"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/linux-blink-heap-verification"
     category: "linux|blink"
     short_name: "VF"
@@ -3092,6 +3087,20 @@
     category: "week8|tsan"
     short_name: "dbg"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/UBSan Release"
+    category: "week9|ubsan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/UBSan vptr Release"
+    category: "week9|ubsan|vptr"
+    short_name: "rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/UBSanVptr Linux"
+    category: "week9|ubsan|vptr"
+    short_name: "lnx"
+  }
 }
 
 consoles {
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg
index 2175d98..d12d4f70 100644
--- a/infra/config/luci-scheduler.cfg
+++ b/infra/config/luci-scheduler.cfg
@@ -1704,17 +1704,6 @@
 }
 
 job {
-  id: "linux-blink-heap-unified-gc"
-  # Triggered by "Linux Builder (dbg)"
-  acl_sets: "triggered-by-parent-builders"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "linux-blink-heap-unified-gc"
-  }
-}
-
-job {
   id: "linux-blink-heap-verification"
   acl_sets: "default"
   buildbucket: {
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index e63eef94..502536f5 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -521,13 +521,6 @@
      flag_descriptions::kEnableAutofillImportDynamicFormsDescription,
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(autofill::features::kAutofillImportDynamicForms)},
-    {"enable-autofill-do-not-upload-save-unsupported-cards",
-     flag_descriptions::kEnableAutofillDoNotUploadSaveUnsupportedCardsName,
-     flag_descriptions::
-         kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription,
-     flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(
-         autofill::features::kAutofillDoNotUploadSaveUnsupportedCards)},
     {"enable-send-tab-to-self-broadcast",
      flag_descriptions::kSendTabToSelfBroadcastName,
      flag_descriptions::kSendTabToSelfBroadcastDescription, flags_ui::kOsIos,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 6fa8c3ee..c43534c 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -171,12 +171,6 @@
     "If enabled, changes the server save card prompt's explanation to mention "
     "the saving of the billing address.";
 
-const char kEnableAutofillDoNotUploadSaveUnsupportedCardsName[] =
-    "Prevents upload save on cards from unsupported networks";
-const char kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription[] =
-    "If enabled, cards from unsupported networks will not be offered upload "
-    "save, and will instead be offered local save.";
-
 const char kEnableAutofillSaveCardShowNoThanksName[] =
     "Show explicit decline option in credit card save prompts";
 const char kEnableAutofillSaveCardShowNoThanksDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 5d946bd..61b38a8 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -137,11 +137,6 @@
 extern const char
     kEnableAutofillCreditCardUploadUpdatePromptExplanationDescription[];
 
-// Title and description for the flag to control if cards from unsupported
-// networks should be prevented form being uploaded.
-extern const char kEnableAutofillDoNotUploadSaveUnsupportedCardsName[];
-extern const char kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription[];
-
 // Title and description for the flag to control if no thanks button should be
 // shown when saving a card.
 extern const char kEnableAutofillSaveCardShowNoThanksName[];
diff --git a/ios/chrome/browser/net/cookie_util_unittest.mm b/ios/chrome/browser/net/cookie_util_unittest.mm
index d15ae00..f22f094 100644
--- a/ios/chrome/browser/net/cookie_util_unittest.mm
+++ b/ios/chrome/browser/net/cookie_util_unittest.mm
@@ -102,9 +102,12 @@
   options.set_include_httponly();
   std::string cookie_line = base::SysNSStringToUTF8(cookie_name) + "=" +
                             base::SysNSStringToUTF8(cookie_value);
-  cookie_store->SetCookieWithOptionsAsync(
-      test_url, cookie_line, options, base::nullopt /* server_time */,
-      net::CookieStore::SetCookiesCallback());
+  auto canonical_cookie =
+      net::CanonicalCookie::Create(test_url, cookie_line, base::Time::Now(),
+                                   base::nullopt /* server_time */);
+  cookie_store->SetCanonicalCookieAsync(std::move(canonical_cookie),
+                                        test_url.scheme(), options,
+                                        net::CookieStore::SetCookiesCallback());
 
   __block NSArray<NSHTTPCookie*>* result_cookies = nil;
   __block bool callback_called = false;
diff --git a/ios/net/cookies/cookie_store_ios.h b/ios/net/cookies/cookie_store_ios.h
index eacb87b4..c7b40e83 100644
--- a/ios/net/cookies/cookie_store_ios.h
+++ b/ios/net/cookies/cookie_store_ios.h
@@ -18,7 +18,6 @@
 #include "base/containers/linked_list.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/optional.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "ios/net/cookies/cookie_cache.h"
@@ -84,11 +83,6 @@
   void SetMetricsEnabled();
 
   // Implementation of the net::CookieStore interface.
-  void SetCookieWithOptionsAsync(const GURL& url,
-                                 const std::string& cookie_line,
-                                 const net::CookieOptions& options,
-                                 base::Optional<base::Time> server_time,
-                                 SetCookiesCallback callback) override;
   void SetCanonicalCookieAsync(std::unique_ptr<CanonicalCookie> cookie,
                                std::string source_scheme,
                                const net::CookieOptions& options,
@@ -258,7 +252,7 @@
   // hence running callbacks).
   //
   // When this CookieStoreIOS object is not synchronized, the various mutator
-  // methods (SetCookieWithOptionsAsync &c) instead store their state in a
+  // methods (SetCanonicalCookieAsync &c) instead store their state in a
   // CookieMonster object to be written back when the system store synchronizes.
   // To deliver notifications in a timely manner, the mutators have to ensure
   // that hooks get run, but only after the changes have been written back to
diff --git a/ios/net/cookies/cookie_store_ios.mm b/ios/net/cookies/cookie_store_ios.mm
index 358921c..925bbd8 100644
--- a/ios/net/cookies/cookie_store_ios.mm
+++ b/ios/net/cookies/cookie_store_ios.mm
@@ -101,43 +101,6 @@
 
 #pragma mark Utility functions
 
-// Builds a NSHTTPCookie from a header cookie line ("Set-Cookie: xxx") and a
-// URL.
-NSHTTPCookie* GetNSHTTPCookieFromCookieLine(
-    const std::string& cookie_line,
-    const GURL& url,
-    base::Optional<base::Time> server_time) {
-  NSURL* nsurl = net::NSURLWithGURL(url);
-  NSString* ns_cookie_line = base::SysUTF8ToNSString(cookie_line);
-  if (!ns_cookie_line) {
-    DLOG(ERROR) << "Cookie line is not UTF8: " << cookie_line;
-    return nil;
-  }
-  NSArray* cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:@{
-    @"Set-Cookie" : ns_cookie_line
-  } forURL:nsurl];
-  if ([cookies count] != 1)
-    return nil;
-
-  NSHTTPCookie* cookie = [cookies objectAtIndex:0];
-  if (![cookie expiresDate] || !server_time.has_value() ||
-      server_time->is_null()) {
-    return cookie;
-  }
-
-  // Perform clock skew correction.
-  base::TimeDelta clock_skew = base::Time::Now() - server_time.value();
-  NSDate* corrected_expire_date =
-      [[cookie expiresDate] dateByAddingTimeInterval:clock_skew.InSecondsF()];
-  NSMutableDictionary* properties =
-      [NSMutableDictionary dictionaryWithDictionary:[cookie properties]];
-  [properties setObject:corrected_expire_date forKey:NSHTTPCookieExpires];
-  NSHTTPCookie* corrected_cookie =
-      [NSHTTPCookie cookieWithProperties:properties];
-  DCHECK(corrected_cookie);
-  return corrected_cookie;
-}
-
 // Returns an empty closure if |callback| is null callback or binds the
 // callback to |status|.
 base::OnceClosure BindSetCookiesCallback(
@@ -160,13 +123,6 @@
   }
 }
 
-// Returns whether the specified cookie line has an explicit Domain attribute or
-// not.
-bool HasExplicitDomain(const std::string& cookie_line) {
-  ParsedCookie cookie(cookie_line);
-  return cookie.HasDomain();
-}
-
 }  // namespace
 
 #pragma mark -
@@ -271,75 +227,6 @@
 #pragma mark -
 #pragma mark CookieStore methods
 
-void CookieStoreIOS::SetCookieWithOptionsAsync(
-    const GURL& url,
-    const std::string& cookie_line,
-    const net::CookieOptions& options,
-    base::Optional<base::Time> server_time,
-    SetCookiesCallback callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  // If cookies are not allowed, a CookieStoreIOS subclass should be used
-  // instead.
-  DCHECK(SystemCookiesAllowed());
-
-  // The exclude_httponly() option would only be used by a javascript
-  // engine.
-  DCHECK(!options.exclude_httponly());
-
-  NSHTTPCookie* cookie =
-      GetNSHTTPCookieFromCookieLine(cookie_line, url, server_time);
-  DLOG_IF(WARNING, !cookie) << "Could not create cookie for line: "
-                            << cookie_line;
-
-  // On iOS, [cookie domain] is not empty when the cookie domain is not
-  // specified: it is inferred from the URL instead. The only case when it
-  // is empty is when the domain attribute is incorrectly formatted.
-  std::string domain_string(base::SysNSStringToUTF8([cookie domain]));
-  std::string dummy;
-  bool has_explicit_domain = HasExplicitDomain(cookie_line);
-  bool has_valid_domain =
-      net::cookie_util::GetCookieDomainWithString(url, domain_string, &dummy);
-  net::CanonicalCookie::CookieInclusionStatus status =
-      net::CanonicalCookie::CookieInclusionStatus::INCLUDE;
-
-  // A cookie can be set if all of the following conditions are met:
-  //   a) The cookie line is well-formed
-  //   b) The Domain attribute, if present, was not malformed
-  //   c) At least one of:
-  //       1) The cookie had no explicit Domain, so the Domain was inferred
-  //          from the URL, or
-  //       2) The cookie had an explicit Domain for which the URL is allowed
-  //          to set cookies.
-
-  // If |cookie| is nil, the cookie line was not well-formed.
-  if (cookie == nil)
-    status =
-        net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE;
-
-  // If |domain_string| is empty, the domain was not well-formed.
-  if (domain_string.empty())
-    status =
-        net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN;
-
-  // If the cookie had an explicit domain and it's not a domain it's allowed to
-  // set cookies to.
-  if (has_explicit_domain && !has_valid_domain)
-    status =
-        net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN;
-
-  if (status == net::CanonicalCookie::CookieInclusionStatus::INCLUDE) {
-    system_store_->SetCookieAsync(
-        cookie,
-        BindSetCookiesCallback(
-            &callback, net::CanonicalCookie::CookieInclusionStatus::INCLUDE));
-    return;
-  }
-
-  if (!callback.is_null())
-    std::move(callback).Run(status);
-}
-
 void CookieStoreIOS::SetCanonicalCookieAsync(
     std::unique_ptr<net::CanonicalCookie> cookie,
     std::string source_scheme,
diff --git a/ios/net/cookies/cookie_store_ios_persistent.h b/ios/net/cookies/cookie_store_ios_persistent.h
index 73c0838..c5557bc1 100644
--- a/ios/net/cookies/cookie_store_ios_persistent.h
+++ b/ios/net/cookies/cookie_store_ios_persistent.h
@@ -7,7 +7,6 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "base/optional.h"
 #include "base/time/time.h"
 #import "ios/net/cookies/cookie_store_ios.h"
 #include "net/cookies/cookie_monster.h"
@@ -43,11 +42,6 @@
   ~CookieStoreIOSPersistent() override;
 
   // Inherited CookieStore methods.
-  void SetCookieWithOptionsAsync(const GURL& url,
-                                 const std::string& cookie_line,
-                                 const net::CookieOptions& options,
-                                 base::Optional<base::Time> server_time,
-                                 SetCookiesCallback callback) override;
   void SetCanonicalCookieAsync(std::unique_ptr<CanonicalCookie> cookie,
                                std::string source_scheme,
                                const net::CookieOptions& options,
diff --git a/ios/net/cookies/cookie_store_ios_persistent.mm b/ios/net/cookies/cookie_store_ios_persistent.mm
index 415857d..abf203d 100644
--- a/ios/net/cookies/cookie_store_ios_persistent.mm
+++ b/ios/net/cookies/cookie_store_ios_persistent.mm
@@ -57,19 +57,6 @@
 #pragma mark -
 #pragma mark CookieStoreIOSPersistent methods
 
-void CookieStoreIOSPersistent::SetCookieWithOptionsAsync(
-    const GURL& url,
-    const std::string& cookie_line,
-    const net::CookieOptions& options,
-    base::Optional<base::Time> server_time,
-    SetCookiesCallback callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  cookie_monster()->SetCookieWithOptionsAsync(
-      url, cookie_line, options, server_time,
-      WrapSetCallback(std::move(callback)));
-}
-
 void CookieStoreIOSPersistent::SetCanonicalCookieAsync(
     std::unique_ptr<CanonicalCookie> cookie,
     std::string source_scheme,
diff --git a/ios/net/cookies/cookie_store_ios_test_util.mm b/ios/net/cookies/cookie_store_ios_test_util.mm
index 2f9cc4da..66312bd9 100644
--- a/ios/net/cookies/cookie_store_ios_test_util.mm
+++ b/ios/net/cookies/cookie_store_ios_test_util.mm
@@ -134,9 +134,11 @@
                net::CookieStore* store) {
   net::CookieOptions options;
   options.set_include_httponly();
-  store->SetCookieWithOptionsAsync(url, cookie_line, options,
-                                   base::nullopt /* server_time */,
-                                   base::DoNothing());
+  auto canonical_cookie = net::CanonicalCookie::Create(
+      url, cookie_line, base::Time::Now(), base::nullopt /* server_time */);
+  ASSERT_TRUE(canonical_cookie);
+  store->SetCanonicalCookieAsync(std::move(canonical_cookie), url.scheme(),
+                                 options, base::DoNothing());
   net::CookieStoreIOS::NotifySystemCookiesChanged();
   // Wait until the flush is posted.
   base::RunLoop().RunUntilIdle();
diff --git a/ios/net/cookies/cookie_store_ios_unittest.mm b/ios/net/cookies/cookie_store_ios_unittest.mm
index 3a6a657..8a87ede 100644
--- a/ios/net/cookies/cookie_store_ios_unittest.mm
+++ b/ios/net/cookies/cookie_store_ios_unittest.mm
@@ -332,9 +332,12 @@
   // Add a cookie.
   net::CookieOptions options;
   options.set_include_httponly();
-  cookie_store->SetCookieWithOptionsAsync(
-      kTestCookieURLFooBar, "a=b", options, base::nullopt /* server_time */,
-      net::CookieStore::SetCookiesCallback());
+  auto canonical_cookie = net::CanonicalCookie::Create(
+      kTestCookieURLFooBar, "a=b", base::Time::Now(),
+      base::nullopt /* server_time */);
+  cookie_store->SetCanonicalCookieAsync(std::move(canonical_cookie),
+                                        kTestCookieURLFooBar.scheme(), options,
+                                        net::CookieStore::SetCookiesCallback());
   // Check we can get the cookie.
   GetAllCookiesCallback callback;
   cookie_store->GetAllCookiesForURLAsync(
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index eec3b7f..15e9fe77 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -475,6 +475,8 @@
       "android/mock_abstract_texture.h",
       "android/mock_android_video_surface_chooser.cc",
       "android/mock_android_video_surface_chooser.h",
+      "android/mock_codec_buffer_wait_coordinator.cc",
+      "android/mock_codec_buffer_wait_coordinator.h",
       "android/mock_codec_image.cc",
       "android/mock_codec_image.h",
       "android/mock_device_info.cc",
diff --git a/media/gpu/android/codec_image_unittest.cc b/media/gpu/android/codec_image_unittest.cc
index 2b7db53..da201b7 100644
--- a/media/gpu/android/codec_image_unittest.cc
+++ b/media/gpu/android/codec_image_unittest.cc
@@ -15,6 +15,7 @@
 #include "media/base/android/mock_media_codec_bridge.h"
 #include "media/gpu/android/codec_image.h"
 #include "media/gpu/android/mock_abstract_texture.h"
+#include "media/gpu/android/mock_codec_buffer_wait_coordinator.h"
 #include "media/gpu/android/mock_texture_owner.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/media/gpu/android/mock_codec_buffer_wait_coordinator.cc b/media/gpu/android/mock_codec_buffer_wait_coordinator.cc
new file mode 100644
index 0000000..bee33e50
--- /dev/null
+++ b/media/gpu/android/mock_codec_buffer_wait_coordinator.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 "media/gpu/android/mock_codec_buffer_wait_coordinator.h"
+
+namespace media {
+
+using testing::Invoke;
+using testing::Return;
+
+MockCodecBufferWaitCoordinator::MockCodecBufferWaitCoordinator(
+    scoped_refptr<NiceMock<MockTextureOwner>> texture_owner)
+    : CodecBufferWaitCoordinator(texture_owner),
+      mock_texture_owner(std::move(texture_owner)),
+      expecting_frame_available(false) {
+  ON_CALL(*this, texture_owner()).WillByDefault(Return(mock_texture_owner));
+
+  ON_CALL(*this, SetReleaseTimeToNow())
+      .WillByDefault(Invoke(
+          this, &MockCodecBufferWaitCoordinator::FakeSetReleaseTimeToNow));
+  ON_CALL(*this, IsExpectingFrameAvailable())
+      .WillByDefault(Invoke(
+          this,
+          &MockCodecBufferWaitCoordinator::FakeIsExpectingFrameAvailable));
+  ON_CALL(*this, WaitForFrameAvailable())
+      .WillByDefault(Invoke(
+          this, &MockCodecBufferWaitCoordinator::FakeWaitForFrameAvailable));
+}
+
+MockCodecBufferWaitCoordinator::~MockCodecBufferWaitCoordinator() = default;
+
+}  // namespace media
diff --git a/media/gpu/android/mock_codec_buffer_wait_coordinator.h b/media/gpu/android/mock_codec_buffer_wait_coordinator.h
new file mode 100644
index 0000000..5bfb5f4
--- /dev/null
+++ b/media/gpu/android/mock_codec_buffer_wait_coordinator.h
@@ -0,0 +1,39 @@
+// 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_GPU_ANDROID_MOCK_CODEC_BUFFER_WAIT_COORDINATOR_H_
+#define MEDIA_GPU_ANDROID_MOCK_CODEC_BUFFER_WAIT_COORDINATOR_H_
+
+#include "media/gpu/android/codec_buffer_wait_coordinator.h"
+#include "media/gpu/android/mock_texture_owner.h"
+
+namespace media {
+
+// Mock class with mostly fake functions.
+class MockCodecBufferWaitCoordinator : public CodecBufferWaitCoordinator {
+ public:
+  MockCodecBufferWaitCoordinator(
+      scoped_refptr<NiceMock<MockTextureOwner>> texture_owner);
+
+  MOCK_CONST_METHOD0(texture_owner,
+                     scoped_refptr<NiceMock<MockTextureOwner>>());
+  MOCK_METHOD0(SetReleaseTimeToNow, void());
+  MOCK_METHOD0(IsExpectingFrameAvailable, bool());
+  MOCK_METHOD0(WaitForFrameAvailable, void());
+
+  // Fake implementations that the mocks will call by default.
+  void FakeSetReleaseTimeToNow() { expecting_frame_available = true; }
+  bool FakeIsExpectingFrameAvailable() { return expecting_frame_available; }
+  void FakeWaitForFrameAvailable() { expecting_frame_available = false; }
+
+  scoped_refptr<NiceMock<MockTextureOwner>> mock_texture_owner;
+  bool expecting_frame_available;
+
+ protected:
+  ~MockCodecBufferWaitCoordinator();
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_ANDROID_MOCK_CODEC_BUFFER_WAIT_COORDINATOR_H_
diff --git a/media/gpu/android/mock_texture_owner.cc b/media/gpu/android/mock_texture_owner.cc
index ff7a914..3aefd5a 100644
--- a/media/gpu/android/mock_texture_owner.cc
+++ b/media/gpu/android/mock_texture_owner.cc
@@ -33,25 +33,4 @@
   ClearAbstractTexture();
 }
 
-MockCodecBufferWaitCoordinator::MockCodecBufferWaitCoordinator(
-    scoped_refptr<NiceMock<MockTextureOwner>> texture_owner)
-    : CodecBufferWaitCoordinator(texture_owner),
-      mock_texture_owner(std::move(texture_owner)),
-      expecting_frame_available(false) {
-  ON_CALL(*this, texture_owner()).WillByDefault(Return(mock_texture_owner));
-
-  ON_CALL(*this, SetReleaseTimeToNow())
-      .WillByDefault(Invoke(
-          this, &MockCodecBufferWaitCoordinator::FakeSetReleaseTimeToNow));
-  ON_CALL(*this, IsExpectingFrameAvailable())
-      .WillByDefault(Invoke(
-          this,
-          &MockCodecBufferWaitCoordinator::FakeIsExpectingFrameAvailable));
-  ON_CALL(*this, WaitForFrameAvailable())
-      .WillByDefault(Invoke(
-          this, &MockCodecBufferWaitCoordinator::FakeWaitForFrameAvailable));
-}
-
-MockCodecBufferWaitCoordinator::~MockCodecBufferWaitCoordinator() = default;
-
 }  // namespace media
diff --git a/media/gpu/android/mock_texture_owner.h b/media/gpu/android/mock_texture_owner.h
index 26109c3..3c92743 100644
--- a/media/gpu/android/mock_texture_owner.h
+++ b/media/gpu/android/mock_texture_owner.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/android/scoped_hardware_buffer_fence_sync.h"
-#include "media/gpu/android/codec_buffer_wait_coordinator.h"
 #include "media/gpu/android/texture_owner.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -54,30 +53,6 @@
   ~MockTextureOwner();
 };
 
-// Mock class with mostly fake functions.
-class MockCodecBufferWaitCoordinator : public CodecBufferWaitCoordinator {
- public:
-  MockCodecBufferWaitCoordinator(
-      scoped_refptr<NiceMock<MockTextureOwner>> texture_owner);
-
-  MOCK_CONST_METHOD0(texture_owner,
-                     scoped_refptr<NiceMock<MockTextureOwner>>());
-  MOCK_METHOD0(SetReleaseTimeToNow, void());
-  MOCK_METHOD0(IsExpectingFrameAvailable, bool());
-  MOCK_METHOD0(WaitForFrameAvailable, void());
-
-  // Fake implementations that the mocks will call by default.
-  void FakeSetReleaseTimeToNow() { expecting_frame_available = true; }
-  bool FakeIsExpectingFrameAvailable() { return expecting_frame_available; }
-  void FakeWaitForFrameAvailable() { expecting_frame_available = false; }
-
-  scoped_refptr<NiceMock<MockTextureOwner>> mock_texture_owner;
-  bool expecting_frame_available;
-
- protected:
-  ~MockCodecBufferWaitCoordinator();
-};
-
 }  // namespace media
 
 #endif  // MEDIA_GPU_ANDROID_MOCK_TEXTURE_OWNER_H_
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn
index 54187ba..f459fd33 100644
--- a/media/gpu/v4l2/BUILD.gn
+++ b/media/gpu/v4l2/BUILD.gn
@@ -43,6 +43,8 @@
     "v4l2_slice_video_decoder.h",
     "v4l2_stateful_workaround.cc",
     "v4l2_stateful_workaround.h",
+    "v4l2_vda_helpers.cc",
+    "v4l2_vda_helpers.h",
     "v4l2_video_decode_accelerator.cc",
     "v4l2_video_decode_accelerator.h",
     "v4l2_video_encode_accelerator.cc",
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
index e809953..cac4d8e6 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -837,37 +837,6 @@
   OutputRecord& output_record = output_buffer_map_[index];
   DCHECK_NE(output_record.picture_id, -1);
 
-  if (output_record.egl_fence) {
-    TRACE_EVENT0("media,gpu",
-                 "V4L2SVDA::EnqueueOutputRecord: "
-                 "GLFenceEGL::ClientWaitWithTimeoutNanos");
-
-    // If we have to wait for completion, wait. Note that free_output_buffers_
-    // is a FIFO queue, so we always wait on the buffer that has been in the
-    // queue the longest. Every 100ms we check whether the decoder is shutting
-    // down, or we might get stuck waiting on a fence that will never come:
-    // https://crbug.com/845645
-    while (!IsDestroyPending()) {
-      const EGLTimeKHR wait_ns =
-          base::TimeDelta::FromMilliseconds(100).InNanoseconds();
-      EGLint result =
-          output_record.egl_fence->ClientWaitWithTimeoutNanos(wait_ns);
-      if (result == EGL_CONDITION_SATISFIED_KHR) {
-        break;
-      } else if (result == EGL_FALSE) {
-        // This will cause tearing, but is safe otherwise.
-        DVLOGF(1) << "GLFenceEGL::ClientWaitWithTimeoutNanos failed!";
-        break;
-      }
-      DCHECK_EQ(result, EGL_TIMEOUT_EXPIRED_KHR);
-    }
-
-    if (IsDestroyPending())
-      return false;
-
-    output_record.egl_fence.reset();
-  }
-
   bool ret;
   if (output_mode_ == Config::OutputMode::ALLOCATE)
     ret = std::move(output_buffer).QueueMMap();
@@ -1161,8 +1130,6 @@
     return true;
 
   for (auto& output_record : output_buffer_map_) {
-    output_record.egl_fence.reset();
-
     picture_buffers_to_dismiss.push_back(output_record.picture_id);
   }
 
@@ -1198,6 +1165,9 @@
   // Release all buffers waiting for an import buffer event.
   output_wait_map_.clear();
 
+  // Release all buffers awaiting a fence since we are about to destroy them.
+  surfaces_awaiting_fence_ = {};
+
   // It's ok to do this, client will retain references to textures, but we are
   // not interested in reusing the surfaces anymore.
   // This will prevent us from reusing old surfaces in case we have some
@@ -1320,7 +1290,6 @@
 
   for (size_t i = 0; i < buffers.size(); i++) {
     OutputRecord& output_record = output_buffer_map_[i];
-    DCHECK(!output_record.egl_fence);
     DCHECK_EQ(output_record.picture_id, -1);
     DCHECK(output_record.dmabuf_fds.empty());
     DCHECK_EQ(output_record.cleared, false);
@@ -1431,7 +1400,6 @@
   }
 
   OutputRecord& output_record = output_buffer_map_[buffer_index];
-  DCHECK(!output_record.egl_fence);
   DCHECK_EQ(output_wait_map_.count(picture_buffer_id), 1u);
 
   if (output_mode_ == Config::OutputMode::IMPORT) {
@@ -1612,7 +1580,8 @@
   if (output_record.num_times_sent_to_client == 0) {
     output_record.at_client = false;
     // Take ownership of the EGL fence.
-    output_record.egl_fence = std::move(egl_fence);
+    surfaces_awaiting_fence_.push(
+        std::make_pair(std::move(egl_fence), std::move(it->second)));
 
     surfaces_at_display_.erase(it);
   }
@@ -1908,6 +1877,33 @@
   output_record.cleared = true;
 }
 
+void V4L2SliceVideoDecodeAccelerator::CheckGLFences() {
+  DVLOGF(4);
+  DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread());
+
+  while (!surfaces_awaiting_fence_.empty() &&
+         surfaces_awaiting_fence_.front().first->HasCompleted()) {
+    // Buffer at the front of the queue goes back to V4L2Queue's free list
+    // and can be reused.
+    surfaces_awaiting_fence_.pop();
+  }
+
+  // If we have no free buffers available, then preemptively schedule a
+  // call to DecodeBufferTask() in a short time, otherwise we may starve out
+  // of buffers because fences will not call back into us once they are
+  // signaled. The delay chosen roughly corresponds to the time a frame is
+  // displayed, which should be optimal in most cases.
+  if (output_queue_->FreeBuffersCount() == 0) {
+    constexpr int64_t kRescheduleDelayMs = 17;
+
+    decoder_thread_.task_runner()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&V4L2SliceVideoDecodeAccelerator::DecodeBufferTask,
+                       base::Unretained(this)),
+        base::TimeDelta::FromMilliseconds(kRescheduleDelayMs));
+  }
+}
+
 scoped_refptr<V4L2DecodeSurface>
 V4L2SliceVideoDecodeAccelerator::CreateSurface() {
   DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread());
@@ -1924,12 +1920,17 @@
                     GetNumOfOutputRecordsAtClient(), "at device",
                     GetNumOfOutputRecordsAtDevice());
 
+  // Release some output buffers if their fence has been signaled.
+  CheckGLFences();
+
   if (input_queue_->FreeBuffersCount() == 0 ||
       output_queue_->FreeBuffersCount() == 0)
     return nullptr;
 
   V4L2WritableBufferRef input_buffer = input_queue_->GetFreeBuffer();
   DCHECK(input_buffer.IsValid());
+  // All buffers that are returned to the output free queue have their GL
+  // fence signaled, so we can use them directly.
   V4L2WritableBufferRef output_buffer = output_queue_->GetFreeBuffer();
   DCHECK(output_buffer.IsValid());
 
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
index 83900d9f..70bee17 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
@@ -88,7 +88,6 @@
     int32_t picture_id;
     GLuint client_texture_id;
     GLuint texture_id;
-    std::unique_ptr<gl::GLFenceEGL> egl_fence;
     std::vector<base::ScopedFD> dmabuf_fds;
     bool cleared;
   };
@@ -128,6 +127,10 @@
   // Below methods are used by accelerator implementations.
   //
   // V4L2DecodeSurfaceHandler implementation.
+
+  // Release surfaces awaiting for their fence to be signaled.
+  void CheckGLFences();
+
   scoped_refptr<V4L2DecodeSurface> CreateSurface() override;
   // SurfaceReady() uses |decoder_display_queue_| to guarantee that decoding
   // of |dec_surface| happens in order.
@@ -430,6 +433,12 @@
       std::map<int32_t, scoped_refptr<V4L2DecodeSurface>>;
   V4L2DecodeSurfaceByPictureBufferId surfaces_at_display_;
 
+  // Queue of surfaces that have been returned by the client, but which fence
+  // hasn't been signaled yet.
+  std::queue<std::pair<std::unique_ptr<gl::GLFenceEGL>,
+                       scoped_refptr<V4L2DecodeSurface>>>
+      surfaces_awaiting_fence_;
+
   // Record for decoded pictures that can be sent to PictureReady.
   struct PictureRecord {
     PictureRecord(bool cleared, const Picture& picture);
diff --git a/media/gpu/v4l2/v4l2_vda_helpers.cc b/media/gpu/v4l2/v4l2_vda_helpers.cc
new file mode 100644
index 0000000..8e23e9d
--- /dev/null
+++ b/media/gpu/v4l2/v4l2_vda_helpers.cc
@@ -0,0 +1,147 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/v4l2/v4l2_vda_helpers.h"
+
+#include "media/gpu/macros.h"
+#include "media/gpu/v4l2/v4l2_device.h"
+#include "media/gpu/v4l2/v4l2_image_processor.h"
+
+namespace media {
+namespace v4l2_vda_helpers {
+
+namespace {
+base::Optional<VideoFrameLayout> CreateLayout(uint32_t fourcc,
+                                              const gfx::Size& size) {
+  // 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.
+  if (fourcc == V4L2_PIX_FMT_MT21C) {
+    size_t num_planes = 2;
+    return VideoFrameLayout::CreateMultiPlanar(
+        PIXEL_FORMAT_NV12, size,
+        std::vector<VideoFrameLayout::Plane>(num_planes));
+  } else {
+    VideoPixelFormat pixel_format =
+        V4L2Device::V4L2PixFmtToVideoPixelFormat(fourcc);
+    if (pixel_format == PIXEL_FORMAT_UNKNOWN)
+      return base::nullopt;
+    size_t num_planes = VideoFrame::NumPlanes(pixel_format);
+    if (num_planes == 1) {
+      return VideoFrameLayout::Create(pixel_format, size);
+    } else {
+      return VideoFrameLayout::CreateMultiPlanar(
+          pixel_format, size, std::vector<VideoFrameLayout::Plane>(num_planes));
+    }
+  }
+}
+}  // namespace
+
+uint32_t FindImageProcessorInputFormat(V4L2Device* vda_device) {
+  std::vector<uint32_t> processor_input_formats =
+      V4L2ImageProcessor::GetSupportedInputFormats();
+
+  struct v4l2_fmtdesc fmtdesc = {};
+  fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+  while (vda_device->Ioctl(VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
+    if (std::find(processor_input_formats.begin(),
+                  processor_input_formats.end(),
+                  fmtdesc.pixelformat) != processor_input_formats.end()) {
+      DVLOGF(3) << "Image processor input format=" << fmtdesc.description;
+      return fmtdesc.pixelformat;
+    }
+    ++fmtdesc.index;
+  }
+  return 0;
+}
+
+uint32_t FindImageProcessorOutputFormat(V4L2Device* ip_device) {
+  // Prefer YVU420 and NV12 because ArcGpuVideoDecodeAccelerator only supports
+  // single physical plane.
+  static constexpr uint32_t kPreferredFormats[] = {V4L2_PIX_FMT_NV12,
+                                                   V4L2_PIX_FMT_YVU420};
+  auto preferred_formats_first = [](uint32_t a, uint32_t b) -> bool {
+    auto* iter_a = std::find(std::begin(kPreferredFormats),
+                             std::end(kPreferredFormats), a);
+    auto* iter_b = std::find(std::begin(kPreferredFormats),
+                             std::end(kPreferredFormats), b);
+    return iter_a < iter_b;
+  };
+
+  std::vector<uint32_t> processor_output_formats =
+      V4L2ImageProcessor::GetSupportedOutputFormats();
+
+  // Move the preferred formats to the front.
+  std::sort(processor_output_formats.begin(), processor_output_formats.end(),
+            preferred_formats_first);
+
+  for (uint32_t processor_output_format : processor_output_formats) {
+    if (ip_device->CanCreateEGLImageFrom(processor_output_format)) {
+      DVLOGF(3) << "Image processor output format=" << processor_output_format;
+      return processor_output_format;
+    }
+  }
+
+  return 0;
+}
+
+std::unique_ptr<ImageProcessor> CreateImageProcessor(
+    uint32_t vda_output_format,
+    uint32_t ip_output_format,
+    const gfx::Size& vda_output_coded_size,
+    const gfx::Size& ip_output_coded_size,
+    const gfx::Size& visible_size,
+    size_t nb_buffers,
+    scoped_refptr<V4L2Device> image_processor_device,
+    ImageProcessor::OutputMode image_processor_output_mode,
+    ImageProcessor::ErrorCB error_cb) {
+  base::Optional<VideoFrameLayout> input_layout =
+      CreateLayout(vda_output_format, vda_output_coded_size);
+  if (!input_layout) {
+    VLOGF(1) << "Invalid input layout";
+    return nullptr;
+  }
+
+  base::Optional<VideoFrameLayout> output_layout =
+      CreateLayout(ip_output_format, ip_output_coded_size);
+  if (!output_layout) {
+    VLOGF(1) << "Invalid output layout";
+    return nullptr;
+  }
+
+  // TODO(crbug.com/917798): Use ImageProcessorFactory::Create() once we remove
+  //     |image_processor_device_| from V4L2VideoDecodeAccelerator.
+  auto image_processor = V4L2ImageProcessor::Create(
+      image_processor_device,
+      ImageProcessor::PortConfig(*input_layout, vda_output_format, visible_size,
+                                 {VideoFrame::STORAGE_DMABUFS}),
+      ImageProcessor::PortConfig(*output_layout, visible_size,
+                                 {VideoFrame::STORAGE_DMABUFS}),
+      image_processor_output_mode, nb_buffers, std::move(error_cb));
+  if (!image_processor)
+    return nullptr;
+
+  if (image_processor->output_layout().coded_size() != ip_output_coded_size) {
+    VLOGF(1) << "Image processor should be able to use the requested output "
+             << "coded size " << ip_output_coded_size.ToString()
+             << " without adjusting to "
+             << image_processor->output_layout().coded_size().ToString();
+    return nullptr;
+  }
+
+  if (image_processor->input_layout().coded_size() != vda_output_coded_size) {
+    VLOGF(1) << "Image processor should be able to take the output coded "
+             << "size of decoder " << vda_output_coded_size.ToString()
+             << " without adjusting to "
+             << image_processor->input_layout().coded_size().ToString();
+    return nullptr;
+  }
+
+  return image_processor;
+}
+
+}  // namespace v4l2_vda_helpers
+}  // namespace media
diff --git a/media/gpu/v4l2/v4l2_vda_helpers.h b/media/gpu/v4l2/v4l2_vda_helpers.h
new file mode 100644
index 0000000..f63be23
--- /dev/null
+++ b/media/gpu/v4l2/v4l2_vda_helpers.h
@@ -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.
+
+#ifndef MEDIA_GPU_V4L2_V4L2_VDA_HELPERS_H_
+#define MEDIA_GPU_V4L2_V4L2_VDA_HELPERS_H_
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "media/gpu/image_processor.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+class V4L2Device;
+
+// Helper static methods to be shared between V4L2VideoDecodeAccelerator and
+// V4L2SliceVideoDecodeAccelerator. This avoids some code duplication between
+// these very similar classes.
+// Note: this namespace can be removed once the V4L2VDA is deprecated.
+namespace v4l2_vda_helpers {
+
+// Returns a usable input format of image processor. Return 0 if not found.
+uint32_t FindImageProcessorInputFormat(V4L2Device* vda_device);
+// Return a usable output format of image processor. Return 0 if not found.
+uint32_t FindImageProcessorOutputFormat(V4L2Device* ip_device);
+
+// Create and return an image processor for the given parameters, or nullptr
+// if it cannot be created.
+//
+// |vda_output_format| is the output format of the VDA, i.e. the IP's input
+// format.
+// |ip_output_format| is the output format that the IP must produce.
+// |vda_output_coded_size| is the coded size of the VDA output buffers (i.e.
+// the input coded size for the IP).
+// |ip_output_coded_size| is the coded size of the output buffers that the IP
+// must produce.
+// |visible_size| is the visible size of both the input and output buffers.
+// |nb_buffers| is the exact number of output buffers that the IP must create.
+// |image_processor_output_mode| specifies whether the IP must allocate its
+// own buffers or rely on imported ones.
+// |error_cb| is the error callback passed to V4L2ImageProcessor::Create().
+std::unique_ptr<ImageProcessor> CreateImageProcessor(
+    uint32_t vda_output_format,
+    uint32_t ip_output_format,
+    const gfx::Size& vda_output_coded_size,
+    const gfx::Size& ip_output_coded_size,
+    const gfx::Size& visible_size,
+    size_t nb_buffers,
+    scoped_refptr<V4L2Device> image_processor_device,
+    ImageProcessor::OutputMode image_processor_output_mode,
+    ImageProcessor::ErrorCB error_cb);
+
+}  // namespace v4l2_vda_helpers
+}  // namespace media
+
+#endif  // MEDIA_GPU_V4L2_V4L2_VDA_HELPERS_H_
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
index 2f32961..05db371 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -35,6 +35,7 @@
 #include "media/gpu/macros.h"
 #include "media/gpu/v4l2/v4l2_image_processor.h"
 #include "media/gpu/v4l2/v4l2_stateful_workaround.h"
+#include "media/gpu/v4l2/v4l2_vda_helpers.h"
 #include "media/video/h264_parser.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gl/gl_context.h"
@@ -2328,12 +2329,14 @@
       VLOGF(1) << "Image processor not available";
       return false;
     }
-    output_format_fourcc_ = FindImageProcessorInputFormat();
+    output_format_fourcc_ =
+        v4l2_vda_helpers::FindImageProcessorInputFormat(device_.get());
     if (output_format_fourcc_ == 0) {
       VLOGF(1) << "Can't find a usable input format from image processor";
       return false;
     }
-    egl_image_format_fourcc_ = FindImageProcessorOutputFormat();
+    egl_image_format_fourcc_ =
+        v4l2_vda_helpers::FindImageProcessorOutputFormat(device_.get());
     if (egl_image_format_fourcc_ == 0) {
       VLOGF(1) << "Can't find a usable output format from image processor";
       return false;
@@ -2361,55 +2364,6 @@
   return true;
 }
 
-uint32_t V4L2VideoDecodeAccelerator::FindImageProcessorInputFormat() {
-  std::vector<uint32_t> processor_input_formats =
-      V4L2ImageProcessor::GetSupportedInputFormats();
-
-  struct v4l2_fmtdesc fmtdesc;
-  memset(&fmtdesc, 0, sizeof(fmtdesc));
-  fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-  while (device_->Ioctl(VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
-    if (std::find(processor_input_formats.begin(),
-                  processor_input_formats.end(),
-                  fmtdesc.pixelformat) != processor_input_formats.end()) {
-      DVLOGF(3) << "Image processor input format=" << fmtdesc.description;
-      return fmtdesc.pixelformat;
-    }
-    ++fmtdesc.index;
-  }
-  return 0;
-}
-
-uint32_t V4L2VideoDecodeAccelerator::FindImageProcessorOutputFormat() {
-  // Prefer YVU420 and NV12 because ArcGpuVideoDecodeAccelerator only supports
-  // single physical plane.
-  static const uint32_t kPreferredFormats[] = {V4L2_PIX_FMT_NV12,
-                                               V4L2_PIX_FMT_YVU420};
-  auto preferred_formats_first = [](uint32_t a, uint32_t b) -> bool {
-    auto* iter_a = std::find(std::begin(kPreferredFormats),
-                             std::end(kPreferredFormats), a);
-    auto* iter_b = std::find(std::begin(kPreferredFormats),
-                             std::end(kPreferredFormats), b);
-    return iter_a < iter_b;
-  };
-
-  std::vector<uint32_t> processor_output_formats =
-      V4L2ImageProcessor::GetSupportedOutputFormats();
-
-  // Move the preferred formats to the front.
-  std::sort(processor_output_formats.begin(), processor_output_formats.end(),
-            preferred_formats_first);
-
-  for (uint32_t processor_output_format : processor_output_formats) {
-    if (device_->CanCreateEGLImageFrom(processor_output_format)) {
-      DVLOGF(3) << "Image processor output format=" << processor_output_format;
-      return processor_output_format;
-    }
-  }
-
-  return 0;
-}
-
 bool V4L2VideoDecodeAccelerator::ResetImageProcessor() {
   VLOGF(2);
   DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
@@ -2431,80 +2385,23 @@
       (output_mode_ == Config::OutputMode::ALLOCATE
            ? ImageProcessor::OutputMode::ALLOCATE
            : ImageProcessor::OutputMode::IMPORT);
-  size_t num_planes = 0;
-  base::Optional<VideoFrameLayout> input_layout;
-  // 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.
-  if (output_format_fourcc_ == V4L2_PIX_FMT_MT21C) {
-    num_planes = 2;
-    input_layout = VideoFrameLayout::CreateMultiPlanar(
-        PIXEL_FORMAT_NV12, coded_size_,
-        std::vector<VideoFrameLayout::Plane>(num_planes));
-  } else {
-    num_planes = V4L2Device::GetNumPlanesOfV4L2PixFmt(output_format_fourcc_);
-    if (num_planes == 1) {
-      input_layout = VideoFrameLayout::Create(
-          V4L2Device::V4L2PixFmtToVideoPixelFormat(output_format_fourcc_),
-          coded_size_);
-    } else {
-      input_layout = VideoFrameLayout::CreateMultiPlanar(
-          V4L2Device::V4L2PixFmtToVideoPixelFormat(output_format_fourcc_),
-          coded_size_, std::vector<VideoFrameLayout::Plane>(num_planes));
-    }
-  }
-  if (!input_layout) {
-    VLOGF(1) << "Invalid input layout";
-    return false;
-  }
 
-  base::Optional<VideoFrameLayout> output_layout;
-  num_planes = V4L2Device::GetNumPlanesOfV4L2PixFmt(egl_image_format_fourcc_);
-  if (num_planes == 1) {
-    output_layout = VideoFrameLayout::Create(
-        V4L2Device::V4L2PixFmtToVideoPixelFormat(egl_image_format_fourcc_),
-        egl_image_size_);
-  } else {
-    output_layout = VideoFrameLayout::CreateMultiPlanar(
-        V4L2Device::V4L2PixFmtToVideoPixelFormat(egl_image_format_fourcc_),
-        egl_image_size_, std::vector<VideoFrameLayout::Plane>(num_planes));
-  }
-  if (!output_layout) {
-    VLOGF(1) << "Invalid output layout";
-    return false;
-  }
-
-  // Unretained(this) is safe for ErrorCB because |decoder_thread_| is owned by
-  // this V4L2VideoDecodeAccelerator and |this| must be valid when ErrorCB is
-  // executed.
-  // TODO(crbug.com/917798): Use ImageProcessorFactory::Create() once we remove
-  //     |image_processor_device_| from V4L2VideoDecodeAccelerator.
-  image_processor_ = V4L2ImageProcessor::Create(
-      image_processor_device_,
-      ImageProcessor::PortConfig(*input_layout, output_format_fourcc_,
-                                 visible_size_, {VideoFrame::STORAGE_DMABUFS}),
-      ImageProcessor::PortConfig(*output_layout, visible_size_,
-                                 {VideoFrame::STORAGE_DMABUFS}),
-      image_processor_output_mode, output_buffer_map_.size(),
+  image_processor_ = v4l2_vda_helpers::CreateImageProcessor(
+      output_format_fourcc_, egl_image_format_fourcc_, coded_size_,
+      egl_image_size_, visible_size_, output_buffer_map_.size(),
+      image_processor_device_, image_processor_output_mode,
+      // Unretained(this) is safe for ErrorCB because |decoder_thread_| is owned
+      // by this V4L2VideoDecodeAccelerator and |this| must be valid when
+      // ErrorCB is executed.
       base::BindRepeating(&V4L2VideoDecodeAccelerator::ImageProcessorError,
                           base::Unretained(this)));
 
   if (!image_processor_) {
-    VLOGF(1) << "Initialize image processor failed";
+    VLOGF(1) << "Error creating image processor";
     NOTIFY_ERROR(PLATFORM_FAILURE);
     return false;
   }
-  DCHECK(image_processor_->output_layout().coded_size() == egl_image_size_);
-  if (image_processor_->input_layout().coded_size() != coded_size_) {
-    VLOGF(1) << "Image processor should be able to take the output coded "
-             << "size of decoder " << coded_size_.ToString()
-             << " without adjusting to "
-             << image_processor_->input_layout().coded_size().ToString();
-    NOTIFY_ERROR(PLATFORM_FAILURE);
-    return false;
-  }
+
   return true;
 }
 
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
index e42749a..f99d0d69 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
@@ -391,10 +391,6 @@
 
   // Set input and output formats before starting decode.
   bool SetupFormats();
-  // Return a usable input format of image processor. Return 0 if not found.
-  uint32_t FindImageProcessorInputFormat();
-  // Return a usable output format of image processor. Return 0 if not found.
-  uint32_t FindImageProcessorOutputFormat();
   // Reset image processor and drop all processing frames.
   bool ResetImageProcessor();
 
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 8704d9f34..2ab1d71 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -319,6 +319,15 @@
   EXPECT_TRUE(tvp->WaitForFrameProcessors());
 }
 
+// Test initializing the video decoder for the specified video. Initialization
+// will be successful if the video decoder is capable of decoding the test
+// video's configuration (e.g. codec and resolution). The test only verifies
+// initialization and doesn't decode the video.
+TEST_F(VideoDecoderTest, Initialize) {
+  auto tvp = CreateVideoPlayer(g_env->Video());
+  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kInitialized), 1u);
+}
+
 // Test video decoder re-initialization. Re-initialization is only supported by
 // the media::VideoDecoder interface, so the test will be skipped if --use_vd
 // is not specified.
diff --git a/net/android/java/src/org/chromium/net/MimeTypeFilter.java b/net/android/java/src/org/chromium/net/MimeTypeFilter.java
index 75e274d..2c55fb6 100644
--- a/net/android/java/src/org/chromium/net/MimeTypeFilter.java
+++ b/net/android/java/src/org/chromium/net/MimeTypeFilter.java
@@ -18,7 +18,7 @@
  *  A mime type filter that supports filtering both mime types and file extensions.
  *  Note that this class is used specifically to implement
  *  the mime type filtering for web share target spec:
- *  https://pr-preview.s3.amazonaws.com/ewilligers/web-share-target/pull/53.html#determining-if-a-file-is-accepted.
+ *  https://wicg.github.io/web-share-target/level-2/#determining-if-a-file-is-accepted
  *  It is also used inside chrome/android/java/src/org/chromium/chrome/browser/photo_picker.
  */
 public class MimeTypeFilter implements FileFilter {
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 918b669f..ae2e77ba 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -413,22 +413,6 @@
       domain);
 }
 
-void CookieMonster::SetCookieWithOptionsAsync(
-    const GURL& url,
-    const std::string& cookie_line,
-    const CookieOptions& options,
-    base::Optional<base::Time> server_time,
-    SetCookiesCallback callback) {
-  DoCookieCallbackForURL(
-      base::BindOnce(
-          // base::Unretained is safe as DoCookieCallbackForURL stores
-          // the callback on |*this|, so the callback will not outlive
-          // the object.
-          &CookieMonster::SetCookieWithOptions, base::Unretained(this), url,
-          cookie_line, options, server_time, std::move(callback)),
-      url);
-}
-
 void CookieMonster::GetCookieListWithOptionsAsync(
     const GURL& url,
     const CookieOptions& options,
@@ -673,40 +657,6 @@
                               : base::OnceClosure()));
 }
 
-void CookieMonster::SetCookieWithOptions(const GURL& url,
-                                         const std::string& cookie_line,
-                                         const CookieOptions& options,
-                                         base::Optional<base::Time> server_time,
-                                         SetCookiesCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (!HasCookieableScheme(url)) {
-    MaybeRunCookieCallback(
-        std::move(callback),
-        CanonicalCookie::CookieInclusionStatus::EXCLUDE_NONCOOKIEABLE_SCHEME);
-    return;
-  }
-
-  DVLOG(net::cookie_util::kVlogSetCookies)
-      << "SetCookie() line: " << cookie_line;
-
-  CanonicalCookie::CookieInclusionStatus status;
-
-  std::unique_ptr<CanonicalCookie> cc(CanonicalCookie::Create(
-      url, cookie_line, Time::Now(), server_time, &status));
-
-  if (status != CanonicalCookie::CookieInclusionStatus::INCLUDE) {
-    DCHECK(!cc);
-    DVLOG(net::cookie_util::kVlogSetCookies)
-        << "WARNING: Failed to allocate CanonicalCookie";
-    MaybeRunCookieCallback(std::move(callback), status);
-    return;
-  }
-
-  DCHECK(cc);
-  SetCanonicalCookie(std::move(cc), url.scheme(), options, std::move(callback));
-}
-
 void CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie,
                                           DeleteCallback callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 7d253bd..2751158 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -156,11 +156,6 @@
   void SetAllCookiesAsync(const CookieList& list, SetCookiesCallback callback);
 
   // CookieStore implementation.
-  void SetCookieWithOptionsAsync(const GURL& url,
-                                 const std::string& cookie_line,
-                                 const CookieOptions& options,
-                                 base::Optional<base::Time> server_time,
-                                 SetCookiesCallback callback) override;
   void SetCanonicalCookieAsync(std::unique_ptr<CanonicalCookie> cookie,
                                std::string source_scheme,
                                const CookieOptions& options,
@@ -369,12 +364,6 @@
   void DeleteAllMatchingInfo(net::CookieDeletionInfo delete_info,
                              DeleteCallback callback);
 
-  void SetCookieWithOptions(const GURL& url,
-                            const std::string& cookie_line,
-                            const CookieOptions& options,
-                            base::Optional<base::Time> server_time,
-                            SetCookiesCallback callback);
-
   void DeleteCanonicalCookie(const CanonicalCookie& cookie,
                              DeleteCallback callback);
 
diff --git a/net/cookies/cookie_monster_perftest.cc b/net/cookies/cookie_monster_perftest.cc
index 228cfcd..6cc7308 100644
--- a/net/cookies/cookie_monster_perftest.cc
+++ b/net/cookies/cookie_monster_perftest.cc
@@ -8,10 +8,12 @@
 #include "base/bind.h"
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/perf_time_logger.h"
+#include "base/time/time.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/cookies/cookie_monster_store_test.h"
@@ -60,9 +62,11 @@
  public:
   void SetCookie(CookieMonster* cm,
                  const GURL& gurl,
-                 const std::string& cookie) {
-    cm->SetCookieWithOptionsAsync(
-        gurl, cookie, options_, base::nullopt /* server_time */,
+                 const std::string& cookie_line) {
+    auto cookie = CanonicalCookie::Create(gurl, cookie_line, base::Time::Now(),
+                                          base::nullopt /* server_time */);
+    cm->SetCanonicalCookieAsync(
+        std::move(cookie), gurl.scheme(), options_,
         base::BindOnce(&SetCookieCallback::Run, base::Unretained(this)));
     WaitForCallback();
   }
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index f761ce4..6b5ff6d 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -1256,8 +1256,8 @@
   EXPECT_TRUE(SetCookie(cm.get(), http_www_foo_.url(), kValidCookieLine));
   EXPECT_EQ("A=B", GetCookies(cm.get(), http_www_foo_.url()));
 
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(), http_www_foo_.url(),
-                                   "C=D; httponly", options));
+  EXPECT_TRUE(CreateAndSetCookie(cm.get(), http_www_foo_.url(), "C=D; httponly",
+                                 options));
   EXPECT_EQ("A=B; C=D",
             GetCookiesWithOptions(cm.get(), http_www_foo_.url(), options));
 
@@ -1454,7 +1454,7 @@
   base::Time now = base::Time::Now();
   base::Optional<base::Time> server_time = base::nullopt;
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), http_url, "x=1"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url, "x=1"));
   EXPECT_EQ(
       CanonicalCookie::CookieInclusionStatus::INCLUDE,
       SetCanonicalCookieReturnStatus(
@@ -1463,7 +1463,7 @@
 
   EXPECT_EQ(
       CanonicalCookie::CookieInclusionStatus::EXCLUDE_NONCOOKIEABLE_SCHEME,
-      SetCookieReturnStatus(cm.get(), foo_url, "x=1"));
+      CreateAndSetCookieReturnStatus(cm.get(), foo_url, "x=1"));
   EXPECT_EQ(
       CanonicalCookie::CookieInclusionStatus::EXCLUDE_NONCOOKIEABLE_SCHEME,
       SetCanonicalCookieReturnStatus(
@@ -1471,7 +1471,7 @@
           "foo", false /*modify_httponly*/));
 
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm_foo.get(), foo_url, "x=1"));
+            CreateAndSetCookieReturnStatus(cm_foo.get(), foo_url, "x=1"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
             SetCanonicalCookieReturnStatus(
                 cm_foo.get(),
@@ -1480,7 +1480,7 @@
 
   EXPECT_EQ(
       CanonicalCookie::CookieInclusionStatus::EXCLUDE_NONCOOKIEABLE_SCHEME,
-      SetCookieReturnStatus(cm_foo.get(), http_url, "x=1"));
+      CreateAndSetCookieReturnStatus(cm_foo.get(), http_url, "x=1"));
   EXPECT_EQ(
       CanonicalCookie::CookieInclusionStatus::EXCLUDE_NONCOOKIEABLE_SCHEME,
       SetCanonicalCookieReturnStatus(
@@ -1497,18 +1497,18 @@
   CookieOptions options;
   options.set_include_httponly();
 
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(), http_www_foo_.url(),
-                                   "A=B; httponly", options));
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(), http_www_foo_.url(),
-                                   http_www_foo_.Format("C=D; domain=.%D"),
-                                   options));
-  EXPECT_TRUE(SetCookieWithOptions(
+  EXPECT_TRUE(CreateAndSetCookie(cm.get(), http_www_foo_.url(), "A=B; httponly",
+                                 options));
+  EXPECT_TRUE(CreateAndSetCookie(cm.get(), http_www_foo_.url(),
+                                 http_www_foo_.Format("C=D; domain=.%D"),
+                                 options));
+  EXPECT_TRUE(CreateAndSetCookie(
       cm.get(), https_www_foo_.url(),
       http_www_foo_.Format("E=F; domain=.%D; secure"), options));
 
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(), http_www_bar_.url(),
-                                   http_www_bar_.Format("G=H; domain=.%D"),
-                                   options));
+  EXPECT_TRUE(CreateAndSetCookie(cm.get(), http_www_bar_.url(),
+                                 http_www_bar_.Format("G=H; domain=.%D"),
+                                 options));
 
   const Time last_access_date(GetFirstCookieAccessDate(cm.get()));
 
@@ -1569,12 +1569,12 @@
   CookieOptions options;
   options.set_include_httponly();
 
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(), http_www_foo_.url(),
-                                   "A=B; httponly", options));
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(), http_www_foo_.url(),
-                                   http_www_foo_.Format("C=D; domain=.%D"),
-                                   options));
-  EXPECT_TRUE(SetCookieWithOptions(
+  EXPECT_TRUE(CreateAndSetCookie(cm.get(), http_www_foo_.url(), "A=B; httponly",
+                                 options));
+  EXPECT_TRUE(CreateAndSetCookie(cm.get(), http_www_foo_.url(),
+                                 http_www_foo_.Format("C=D; domain=.%D"),
+                                 options));
+  EXPECT_TRUE(CreateAndSetCookie(
       cm.get(), https_www_foo_.url(),
       http_www_foo_.Format("E=F; domain=.%D; secure"), options));
 
@@ -1637,12 +1637,12 @@
   std::unique_ptr<CookieMonster> cm(new CookieMonster(nullptr, &net_log_));
   CookieOptions options;
 
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(), www_foo_foo_.url(),
-                                   "A=B; path=/foo;", options));
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(), www_foo_bar_.url(),
-                                   "C=D; path=/bar;", options));
+  EXPECT_TRUE(CreateAndSetCookie(cm.get(), www_foo_foo_.url(),
+                                 "A=B; path=/foo;", options));
+  EXPECT_TRUE(CreateAndSetCookie(cm.get(), www_foo_bar_.url(),
+                                 "C=D; path=/bar;", options));
   EXPECT_TRUE(
-      SetCookieWithOptions(cm.get(), http_www_foo_.url(), "E=F;", options));
+      CreateAndSetCookie(cm.get(), http_www_foo_.url(), "E=F;", options));
 
   CookieList cookies = GetAllCookiesForURL(cm.get(), www_foo_foo_.url());
   auto it = cookies.begin();
@@ -1675,12 +1675,12 @@
   std::unique_ptr<CookieMonster> cm(new CookieMonster(nullptr, &net_log_));
   CookieOptions options;
 
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(), www_foo_foo_.url(),
-                                   "A=B; path=/foo;", options));
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(), www_foo_bar_.url(),
-                                   "C=D; path=/bar;", options));
+  EXPECT_TRUE(CreateAndSetCookie(cm.get(), www_foo_foo_.url(),
+                                 "A=B; path=/foo;", options));
+  EXPECT_TRUE(CreateAndSetCookie(cm.get(), www_foo_bar_.url(),
+                                 "C=D; path=/bar;", options));
   EXPECT_TRUE(
-      SetCookieWithOptions(cm.get(), http_www_foo_.url(), "E=F;", options));
+      CreateAndSetCookie(cm.get(), http_www_foo_.url(), "E=F;", options));
 
   CookieStatusList excluded_cookies =
       GetExcludedCookiesForURL(cm.get(), www_foo_foo_.url());
@@ -2195,12 +2195,12 @@
   store->set_store_load_commands(true);
   std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), &net_log_));
 
-  // Get all cookies task that queues a task to set a cookie when executed.
+  auto cookie = CanonicalCookie::Create(kUrl, "a=b", base::Time::Now(),
+                                        base::nullopt /* server_time */);
   ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus>
       set_cookie_callback;
-  cm->SetCookieWithOptionsAsync(kUrl, "a=b", CookieOptions(),
-                                base::nullopt /* server_time */,
-                                set_cookie_callback.MakeCallback());
+  cm->SetCanonicalCookieAsync(std::move(cookie), kUrl.scheme(), CookieOptions(),
+                              set_cookie_callback.MakeCallback());
 
   GetCookieListCallback get_cookie_list_callback1;
   cm->GetAllCookiesAsync(get_cookie_list_callback1.MakeCallback());
@@ -2284,11 +2284,12 @@
   GetCookieListCallback get_cookie_list_callback1;
   cm->GetAllCookiesAsync(get_cookie_list_callback1.MakeCallback());
 
+  auto cookie = CanonicalCookie::Create(kUrl, "a=b", base::Time::Now(),
+                                        base::nullopt /* server_time */);
   ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus>
       set_cookie_callback;
-  cm->SetCookieWithOptionsAsync(kUrl, "a=b", CookieOptions(),
-                                base::nullopt /* server_time */,
-                                set_cookie_callback.MakeCallback());
+  cm->SetCanonicalCookieAsync(std::move(cookie), kUrl.scheme(), CookieOptions(),
+                              set_cookie_callback.MakeCallback());
 
   GetCookieListCallback get_cookie_list_callback2;
   cm->GetAllCookiesAsync(get_cookie_list_callback2.MakeCallback());
@@ -2332,13 +2333,15 @@
   std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), &net_log_));
 
   // Get all cookies task that queues a task to set a cookie when executed.
+  auto cookie = CanonicalCookie::Create(kUrl, "a=b", base::Time::Now(),
+                                        base::nullopt /* server_time */);
   ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus>
       set_cookie_callback;
   cm->GetAllCookiesAsync(base::BindOnce(
       &RunClosureOnCookieListReceived,
-      base::BindOnce(&CookieStore::SetCookieWithOptionsAsync,
-                     base::Unretained(cm.get()), kUrl, "a=b", CookieOptions(),
-                     base::nullopt /* server_time */,
+      base::BindOnce(&CookieStore::SetCanonicalCookieAsync,
+                     base::Unretained(cm.get()), std::move(cookie),
+                     kUrl.scheme(), CookieOptions(),
                      set_cookie_callback.MakeCallback())));
 
   // Get cookie task. Queued before the delete task is executed, so should not
@@ -2841,31 +2844,31 @@
   // A non-secure cookie can be created from either a URL with a secure or
   // insecure scheme.
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), http_url, "A=C;"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url, "A=C;"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url, "A=B;"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=B;"));
 
   // A secure cookie cannot be created from a URL with an insecure scheme.
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY,
-            SetCookieReturnStatus(cm.get(), http_url, "A=B; Secure"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url, "A=B; Secure"));
 
   // A secure cookie can be created from a URL with a secure scheme.
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
 
   // If a non-secure cookie is created from a URL with an insecure scheme, and a
   // secure cookie with the same name already exists, do not update the cookie.
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-            SetCookieReturnStatus(cm.get(), http_url, "A=C;"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url, "A=C;"));
 
   // If a non-secure cookie is created from a URL with an secure scheme, and a
   // secure cookie with the same name already exists, update the cookie.
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url, "A=C;"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=C;"));
 
   // If a non-secure cookie is created from a URL with an insecure scheme, and
   // a secure cookie with the same name already exists, do not update the cookie
@@ -2874,11 +2877,12 @@
   // With an existing cookie whose path is '/', a cookie with the same name
   // cannot be set on the same domain, regardless of path:
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-            SetCookieReturnStatus(cm.get(), http_url, "A=C; path=/"));
-  EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-            SetCookieReturnStatus(cm.get(), http_url, "A=C; path=/my/path"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url, "A=C; path=/"));
+  EXPECT_EQ(
+      CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
+      CreateAndSetCookieReturnStatus(cm.get(), http_url, "A=C; path=/my/path"));
 
   // But if the existing cookie has a path somewhere under the root, cookies
   // with the same name may be set for paths which don't overlap the existing
@@ -2886,27 +2890,30 @@
   EXPECT_TRUE(
       SetCookie(cm.get(), https_url, "WITH_PATH=B; Secure; path=/my/path"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), http_url, "WITH_PATH=C"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url, "WITH_PATH=C"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), http_url, "WITH_PATH=C; path=/"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url,
+                                           "WITH_PATH=C; path=/"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), http_url,
-                                  "WITH_PATH=C; path=/your/path"));
-  EXPECT_EQ(
-      CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-      SetCookieReturnStatus(cm.get(), http_url, "WITH_PATH=C; path=/my/path"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url,
+                                           "WITH_PATH=C; path=/your/path"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-            SetCookieReturnStatus(cm.get(), http_url,
-                                  "WITH_PATH=C; path=/my/path/sub"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url,
+                                           "WITH_PATH=C; path=/my/path"));
+  EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
+            CreateAndSetCookieReturnStatus(cm.get(), http_url,
+                                           "WITH_PATH=C; path=/my/path/sub"));
 
   DeleteAll(cm.get());
 
   // If a secure cookie is set on top of an existing insecure cookie but with a
   // different path, both are retained.
+  EXPECT_EQ(
+      CanonicalCookie::CookieInclusionStatus::INCLUDE,
+      CreateAndSetCookieReturnStatus(cm.get(), http_url, "A=B; path=/foo"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), http_url, "A=B; path=/foo"));
-  EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url, "A=C; Secure; path=/"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url,
+                                           "A=C; Secure; path=/"));
 
   // Querying from an insecure url gets only the insecure cookie, but querying
   // from a secure url returns both.
@@ -2917,10 +2924,11 @@
   // Attempting to set an insecure cookie (from an insecure scheme) that domain-
   // matches and path-matches the secure cookie fails i.e. the secure cookie is
   // left alone...
+  EXPECT_EQ(
+      CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
+      CreateAndSetCookieReturnStatus(cm.get(), http_url, "A=D; path=/foo"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-            SetCookieReturnStatus(cm.get(), http_url, "A=D; path=/foo"));
-  EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-            SetCookieReturnStatus(cm.get(), http_url, "A=D; path=/"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url, "A=D; path=/"));
   EXPECT_THAT(GetCookies(cm.get(), https_foo_url), testing::HasSubstr("A=C"));
 
   // ...but the original insecure cookie is still retained.
@@ -2930,7 +2938,7 @@
 
   // Deleting the secure cookie leaves only the original insecure cookie.
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(
+            CreateAndSetCookieReturnStatus(
                 cm.get(), https_url,
                 "A=C; path=/; Expires=Thu, 01-Jan-1970 00:00:01 GMT"));
   EXPECT_EQ("A=B", GetCookies(cm.get(), https_foo_url));
@@ -2939,35 +2947,38 @@
   // a secure cookie with the same name already exists, if the domain strings
   // domain-match, do not update the cookie.
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-            SetCookieReturnStatus(cm.get(), http_url, "A=C; domain=foo.com"));
-  EXPECT_EQ(
-      CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-      SetCookieReturnStatus(cm.get(), http_url, "A=C; domain=www.foo.com"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url,
+                                           "A=C; domain=foo.com"));
+  EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
+            CreateAndSetCookieReturnStatus(cm.get(), http_url,
+                                           "A=C; domain=www.foo.com"));
 
   // Since A=B was set above with no domain string, set a different cookie here
   // so the insecure examples aren't trying to overwrite the one above.
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url,
-                                  "B=C; Secure; domain=foo.com"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url,
+                                           "B=C; Secure; domain=foo.com"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-            SetCookieReturnStatus(cm.get(), http_url, "B=D; domain=foo.com"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url,
+                                           "B=D; domain=foo.com"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-            SetCookieReturnStatus(cm.get(), http_url, "B=D"));
-  EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
-            SetCookieReturnStatus(cm.get(), http_superdomain_url, "B=D"));
+            CreateAndSetCookieReturnStatus(cm.get(), http_url, "B=D"));
+  EXPECT_EQ(
+      CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE,
+      CreateAndSetCookieReturnStatus(cm.get(), http_superdomain_url, "B=D"));
 
   // Verify that if an httponly version of the cookie exists, adding a Secure
   // version of the cookie still does not overwrite it.
   CookieOptions include_httponly;
   include_httponly.set_include_httponly();
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(), https_url, "C=D; httponly",
-                                   include_httponly));
+  EXPECT_TRUE(CreateAndSetCookie(cm.get(), https_url, "C=D; httponly",
+                                 include_httponly));
   // Note that the lack of an explicit options object below uses the default,
   // which in this case includes "exclude_httponly = true".
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY,
-            SetCookieReturnStatus(cm.get(), https_url, "C=E; Secure"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url, "C=E; Secure"));
 
   auto entries = net_log_.GetEntries();
   ExpectLogContainsSomewhere(
@@ -3108,18 +3119,19 @@
   // Tests that non-equivalent cookies because of the path attribute can be set
   // successfully.
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
-  EXPECT_EQ(
-      CanonicalCookie::CookieInclusionStatus::INCLUDE,
-      SetCookieReturnStatus(cm.get(), https_url, "A=C; path=/some/other/path"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
+  EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
+            CreateAndSetCookieReturnStatus(cm.get(), https_url,
+                                           "A=C; path=/some/other/path"));
   EXPECT_FALSE(SetCookie(cm.get(), http_url, "A=D; path=/some/other/path"));
 
   // Tests that non-equivalent cookies because of the domain attribute can be
   // set successfully.
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=B; Secure"));
   EXPECT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
-            SetCookieReturnStatus(cm.get(), https_url, "A=C; domain=foo.com"));
+            CreateAndSetCookieReturnStatus(cm.get(), https_url,
+                                           "A=C; domain=foo.com"));
   EXPECT_FALSE(SetCookie(cm.get(), http_url, "A=D; domain=foo.com"));
 }
 
diff --git a/net/cookies/cookie_store.h b/net/cookies/cookie_store.h
index f550571..08ed860 100644
--- a/net/cookies/cookie_store.h
+++ b/net/cookies/cookie_store.h
@@ -54,23 +54,6 @@
 
   virtual ~CookieStore();
 
-  // Sets the cookies specified by |cookie_line| returned from |url|
-  // with options |options| in effect.  Expects a cookie line, like
-  // "a=1; domain=b.com".
-  //
-  // |server_time| indicates what the server sending us the Cookie
-  // thought the current time was when the cookie was produced.  This is used to
-  // adjust for clock skew between server and host.
-  //
-  // Fails either if the cookie is invalid or if this is a non-HTTPONLY cookie
-  // and it would overwrite an existing HTTPONLY cookie.
-  // Returns true if the cookie is successfully set.
-  virtual void SetCookieWithOptionsAsync(const GURL& url,
-                                         const std::string& cookie_line,
-                                         const CookieOptions& options,
-                                         base::Optional<base::Time> server_time,
-                                         SetCookiesCallback callback) = 0;
-
   // Set the cookie on the cookie store.  |cookie.IsCanonical()| must
   // be true.  |source_scheme| denotes the scheme of the resource setting this.
   //
diff --git a/net/cookies/cookie_store_change_unittest.h b/net/cookies/cookie_store_change_unittest.h
index b1e202d..00b0491 100644
--- a/net/cookies/cookie_store_change_unittest.h
+++ b/net/cookies/cookie_store_change_unittest.h
@@ -366,9 +366,9 @@
   // overwrite the non-http-only version.
   CookieOptions allow_httponly;
   allow_httponly.set_include_httponly();
-  EXPECT_TRUE(this->SetCookieWithOptions(cs, this->http_www_foo_.url(),
-                                         "A=C; path=/path1; httponly",
-                                         allow_httponly));
+  EXPECT_TRUE(this->CreateAndSetCookie(cs, this->http_www_foo_.url(),
+                                       "A=C; path=/path1; httponly",
+                                       allow_httponly));
   this->DeliverChangeNotifications();
 
   ASSERT_LE(1u, cookie_changes.size());
@@ -1092,9 +1092,9 @@
   // overwrite the non-http-only version.
   CookieOptions allow_httponly;
   allow_httponly.set_include_httponly();
-  EXPECT_TRUE(this->SetCookieWithOptions(cs, this->http_www_foo_.url(),
-                                         "A=C; path=/foo; httponly",
-                                         allow_httponly));
+  EXPECT_TRUE(this->CreateAndSetCookie(cs, this->http_www_foo_.url(),
+                                       "A=C; path=/foo; httponly",
+                                       allow_httponly));
   this->DeliverChangeNotifications();
 
   ASSERT_LE(1u, cookie_changes.size());
@@ -2063,9 +2063,9 @@
   // overwrite the non-http-only version.
   CookieOptions allow_httponly;
   allow_httponly.set_include_httponly();
-  EXPECT_TRUE(this->SetCookieWithOptions(cs, this->http_www_foo_.url(),
-                                         "abc=hij; path=/foo; httponly",
-                                         allow_httponly));
+  EXPECT_TRUE(this->CreateAndSetCookie(cs, this->http_www_foo_.url(),
+                                       "abc=hij; path=/foo; httponly",
+                                       allow_httponly));
   this->DeliverChangeNotifications();
 
   ASSERT_LE(1u, cookie_changes.size());
diff --git a/net/cookies/cookie_store_test_helpers.cc b/net/cookies/cookie_store_test_helpers.cc
index 45b2e3f..e103f87 100644
--- a/net/cookies/cookie_store_test_helpers.cc
+++ b/net/cookies/cookie_store_test_helpers.cc
@@ -85,25 +85,6 @@
   did_run_ = true;
 }
 
-void DelayedCookieMonster::SetCookieWithOptionsAsync(
-    const GURL& url,
-    const std::string& cookie_line,
-    const CookieOptions& options,
-    base::Optional<base::Time> server_time,
-    CookieMonster::SetCookiesCallback callback) {
-  did_run_ = false;
-  cookie_monster_->SetCookieWithOptionsAsync(
-      url, cookie_line, options, server_time,
-      base::Bind(&DelayedCookieMonster::SetCookiesInternalCallback,
-                 base::Unretained(this)));
-  DCHECK_EQ(did_run_, true);
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&DelayedCookieMonster::InvokeSetCookiesCallback,
-                     base::Unretained(this), std::move(callback)),
-      base::TimeDelta::FromMilliseconds(kDelayedTime));
-}
-
 void DelayedCookieMonster::SetCanonicalCookieAsync(
     std::unique_ptr<CanonicalCookie> cookie,
     std::string source_scheme,
@@ -156,14 +137,6 @@
     std::move(callback).Run(cookie_list_, CookieStatusList());
 }
 
-bool DelayedCookieMonster::SetCookieWithOptions(
-    const GURL& url,
-    const std::string& cookie_line,
-    const CookieOptions& options) {
-  ADD_FAILURE();
-  return false;
-}
-
 void DelayedCookieMonster::DeleteCanonicalCookieAsync(
     const CanonicalCookie& cookie,
     DeleteCallback callback) {
diff --git a/net/cookies/cookie_store_test_helpers.h b/net/cookies/cookie_store_test_helpers.h
index ca5e355..6a23ecc 100644
--- a/net/cookies/cookie_store_test_helpers.h
+++ b/net/cookies/cookie_store_test_helpers.h
@@ -53,13 +53,6 @@
   // invoke the internal callback.
   // Post a delayed task to invoke the original callback with the results.
 
-  void SetCookieWithOptionsAsync(
-      const GURL& url,
-      const std::string& cookie_line,
-      const CookieOptions& options,
-      base::Optional<base::Time> server_time,
-      CookieMonster::SetCookiesCallback callback) override;
-
   void SetCanonicalCookieAsync(std::unique_ptr<CanonicalCookie> cookie,
                                std::string source_scheme,
                                const CookieOptions& options,
@@ -71,10 +64,6 @@
 
   void GetAllCookiesAsync(GetCookieListCallback callback) override;
 
-  virtual bool SetCookieWithOptions(const GURL& url,
-                                    const std::string& cookie_line,
-                                    const CookieOptions& options);
-
   void DeleteCanonicalCookieAsync(const CanonicalCookie& cookie,
                                   DeleteCallback callback) override;
 
diff --git a/net/cookies/cookie_store_unittest.h b/net/cookies/cookie_store_unittest.h
index 06ca074..b977175 100644
--- a/net/cookies/cookie_store_unittest.h
+++ b/net/cookies/cookie_store_unittest.h
@@ -127,6 +127,7 @@
   // Helper methods for the asynchronous Cookie Store API that call the
   // asynchronous method and then pump the loop until the callback is invoked,
   // finally returning the value.
+  // TODO(chlily): Consolidate some of these.
 
   std::string GetCookies(CookieStore* cs, const GURL& url) {
     DCHECK(cs);
@@ -185,16 +186,20 @@
     return callback.cookies();
   }
 
-  bool SetCookieWithOptions(
+  bool CreateAndSetCookie(
       CookieStore* cs,
       const GURL& url,
       const std::string& cookie_line,
       const CookieOptions& options,
       base::Optional<base::Time> server_time = base::nullopt) {
+    auto cookie = CanonicalCookie::Create(url, cookie_line, base::Time::Now(),
+                                          server_time);
+    if (!cookie)
+      return false;
     DCHECK(cs);
     ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback;
-    cs->SetCookieWithOptionsAsync(url, cookie_line, options, server_time,
-                                  callback.MakeCallback());
+    cs->SetCanonicalCookieAsync(std::move(cookie), url.scheme(), options,
+                                callback.MakeCallback());
     callback.WaitUntilDone();
     return callback.result() == CanonicalCookie::CookieInclusionStatus::INCLUDE;
   }
@@ -223,8 +228,8 @@
     CookieOptions options;
     if (!CookieStoreTestTraits::supports_http_only)
       options.set_include_httponly();
-    return SetCookieWithOptions(cs, url, cookie_line, options,
-                                base::make_optional(server_time));
+    return CreateAndSetCookie(cs, url, cookie_line, options,
+                              base::make_optional(server_time));
   }
 
   bool SetCookie(CookieStore* cs,
@@ -233,13 +238,20 @@
     CookieOptions options;
     if (!CookieStoreTestTraits::supports_http_only)
       options.set_include_httponly();
-    return SetCookieWithOptions(cs, url, cookie_line, options);
+    return CreateAndSetCookie(cs, url, cookie_line, options);
   }
 
-  CanonicalCookie::CookieInclusionStatus SetCookieReturnStatus(
+  CanonicalCookie::CookieInclusionStatus CreateAndSetCookieReturnStatus(
       CookieStore* cs,
       const GURL& url,
       const std::string& cookie_line) {
+    CanonicalCookie::CookieInclusionStatus create_status;
+    auto cookie = CanonicalCookie::Create(url, cookie_line, base::Time::Now(),
+                                          base::nullopt /* server_time */,
+                                          &create_status);
+    if (!cookie)
+      return create_status;
+
     CookieOptions options;
     if (!CookieStoreTestTraits::supports_http_only)
       options.set_include_httponly();
@@ -249,9 +261,8 @@
 
     DCHECK(cs);
     ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback;
-    cs->SetCookieWithOptionsAsync(url, cookie_line, options,
-                                  base::nullopt /* server_time */,
-                                  callback.MakeCallback());
+    cs->SetCanonicalCookieAsync(std::move(cookie), url.scheme(), options,
+                                callback.MakeCallback());
     callback.WaitUntilDone();
     return callback.result();
   }
@@ -845,20 +856,38 @@
 // IE and Safari do not. Assert the expected policy here.
 TYPED_TEST_P(CookieStoreTest, DomainWithTrailingDotTest) {
   CookieStore* cs = this->GetCookieStore();
+  // These two cases fail because the url, http://www.foo.com, does not match
+  // the domain given in the cookie line (due to the trailing dots), so the
+  // cookie is not created.
+  EXPECT_FALSE(this->SetCookie(cs, this->http_www_foo_.url(),
+                               "a=1; domain=.www.foo.com."));
+  EXPECT_FALSE(this->SetCookie(cs, this->http_www_foo_.url(),
+                               "b=2; domain=.www.foo.com.."));
+  this->MatchCookieLines(std::string(),
+                         this->GetCookies(cs, this->http_www_foo_.url()));
+
+  GURL url_with_dot("http://www.foo.com.");
+  GURL url_with_double_dot("http://www.foo.com..");
+
+  // This succeeds because the urls match.
+  EXPECT_TRUE(this->SetCookie(cs, url_with_dot, "a=1; domain=.www.foo.com."));
+  // This fails because two trailing dots are not allowed, so the cookie is not
+  // created.
+  EXPECT_FALSE(
+      this->SetCookie(cs, url_with_double_dot, "b=2; domain=.www.foo.com.."));
+
   if (TypeParam::preserves_trailing_dots) {
-    EXPECT_FALSE(this->SetCookie(cs, this->http_www_foo_.url(),
-                                 "a=1; domain=.www.foo.com."));
-    EXPECT_FALSE(this->SetCookie(cs, this->http_www_foo_.url(),
-                                 "b=2; domain=.www.foo.com.."));
+    // If the CookieStore preserves trailing dots, then .www.foo.com is not
+    // considered the same as .www.foo.com.
     this->MatchCookieLines(std::string(),
                            this->GetCookies(cs, this->http_www_foo_.url()));
+    this->MatchCookieLines("a=1", this->GetCookies(cs, url_with_dot));
   } else {
-    EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(),
-                                "a=1; domain=.www.foo.com."));
-    EXPECT_FALSE(this->SetCookie(cs, this->http_www_foo_.url(),
-                                 "b=2; domain=.www.foo.com.."));
+    // If the CookieStore does not preserve trailing dots, the domains will both
+    // be folded into one.
     this->MatchCookieLines("a=1",
                            this->GetCookies(cs, this->http_www_foo_.url()));
+    this->MatchCookieLines("a=1", this->GetCookies(cs, url_with_dot));
   }
 }
 
@@ -1106,20 +1135,23 @@
   GURL url_with_dot("http://www.foo.com.");
   EXPECT_TRUE(this->SetCookie(cs, url, "a=1"));
   this->MatchCookieLines("a=1", this->GetCookies(cs, url));
+  // This fails because the url does not match the domain, so the cookie cannot
+  // be created.
+  EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.www.foo.com."));
+  this->MatchCookieLines("a=1", this->GetCookies(cs, url));
+  // This cookie can be created because the url matches the domain, and it can
+  // be set, but the get-cookie result differs depending on whether the
+  // CookieStore preserves trailing dots.
+  EXPECT_TRUE(this->SetCookie(cs, url_with_dot, "b=2; domain=.foo.com."));
 
   // Do not share cookie space with the dot version of domain.
   // Note: this is not what FireFox does, but it _is_ what IE+Safari do.
   if (TypeParam::preserves_trailing_dots) {
-    EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.www.foo.com."));
     this->MatchCookieLines("a=1", this->GetCookies(cs, url));
-    EXPECT_TRUE(this->SetCookie(cs, url_with_dot, "b=2; domain=.foo.com."));
     this->MatchCookieLines("b=2", this->GetCookies(cs, url_with_dot));
   } else {
-    EXPECT_TRUE(this->SetCookie(cs, url, "b=2; domain=.www.foo.com."));
     this->MatchCookieLines("a=1 b=2", this->GetCookies(cs, url));
-    // Setting this cookie should fail, since the trailing dot on the domain
-    // isn't preserved, and then the domain mismatches the URL.
-    EXPECT_FALSE(this->SetCookie(cs, url_with_dot, "b=2; domain=.foo.com."));
+    this->MatchCookieLines("a=1 b=2", this->GetCookies(cs, url_with_dot));
   }
 
   // Make sure there weren't any side effects.
@@ -1185,18 +1217,18 @@
       "ACSTM=20130308043820420042; path=/; domain=ipdl.inpit.go.jp; Expires=";
   std::string cookie_line = "ACSTM=20130308043820420042";
 
-  this->SetCookieWithOptions(cs, url, set_cookie_line, options);
+  this->CreateAndSetCookie(cs, url, set_cookie_line, options);
   this->MatchCookieLines(cookie_line,
                          this->GetCookiesWithOptions(cs, url, options));
 
   base::Optional<base::Time> server_time =
       base::make_optional(base::Time::Now() - base::TimeDelta::FromHours(1));
-  this->SetCookieWithOptions(cs, url, set_cookie_line, options, server_time);
+  this->CreateAndSetCookie(cs, url, set_cookie_line, options, server_time);
   this->MatchCookieLines(cookie_line,
                          this->GetCookiesWithOptions(cs, url, options));
 
   server_time = base::Time::Now() + base::TimeDelta::FromHours(1);
-  this->SetCookieWithOptions(cs, url, set_cookie_line, options, server_time);
+  this->CreateAndSetCookie(cs, url, set_cookie_line, options, server_time);
   this->MatchCookieLines(cookie_line,
                          this->GetCookiesWithOptions(cs, url, options));
 }
@@ -1210,8 +1242,8 @@
   options.set_include_httponly();
 
   // Create a httponly cookie.
-  EXPECT_TRUE(this->SetCookieWithOptions(cs, this->http_www_foo_.url(),
-                                         "A=B; httponly", options));
+  EXPECT_TRUE(this->CreateAndSetCookie(cs, this->http_www_foo_.url(),
+                                       "A=B; httponly", options));
 
   // Check httponly read protection.
   this->MatchCookieLines(std::string(),
@@ -1225,8 +1257,8 @@
                          this->GetCookies(cs, this->http_www_foo_.url()));
   this->MatchCookieLines("A=B", this->GetCookiesWithOptions(
                                     cs, this->http_www_foo_.url(), options));
-  EXPECT_TRUE(this->SetCookieWithOptions(cs, this->http_www_foo_.url(), "A=C",
-                                         options));
+  EXPECT_TRUE(
+      this->CreateAndSetCookie(cs, this->http_www_foo_.url(), "A=C", options));
   this->MatchCookieLines("A=C",
                          this->GetCookies(cs, this->http_www_foo_.url()));
 
@@ -1234,8 +1266,8 @@
   EXPECT_FALSE(this->SetCookie(cs, this->http_www_foo_.url(), "B=A; httponly"));
   this->MatchCookieLines("A=C", this->GetCookiesWithOptions(
                                     cs, this->http_www_foo_.url(), options));
-  EXPECT_TRUE(this->SetCookieWithOptions(cs, this->http_www_foo_.url(),
-                                         "B=A; httponly", options));
+  EXPECT_TRUE(this->CreateAndSetCookie(cs, this->http_www_foo_.url(),
+                                       "B=A; httponly", options));
   this->MatchCookieLines(
       "A=C; B=A",
       this->GetCookiesWithOptions(cs, this->http_www_foo_.url(), options));
@@ -1494,10 +1526,10 @@
   // overwrite the non-http-only version.
   CookieOptions allow_httponly;
   allow_httponly.set_include_httponly();
-  EXPECT_TRUE(this->SetCookieWithOptions(cs, url_foo,
-                                         "b=val2; path=/path1; httponly; "
-                                         "expires=Mon, 18-Apr-22 22:50:14 GMT",
-                                         allow_httponly));
+  EXPECT_TRUE(this->CreateAndSetCookie(cs, url_foo,
+                                       "b=val2; path=/path1; httponly; "
+                                       "expires=Mon, 18-Apr-22 22:50:14 GMT",
+                                       allow_httponly));
 
   // Insert a cookie "a" for path "/path1". This should overwrite.
   EXPECT_TRUE(this->SetCookie(cs, url_foo,
@@ -1548,14 +1580,14 @@
   CookieOptions options;
   if (!TypeParam::supports_http_only)
     options.set_include_httponly();
-  EXPECT_TRUE(this->SetCookieWithOptions(cs, url_foo, "a", options));
+  EXPECT_TRUE(this->CreateAndSetCookie(cs, url_foo, "a", options));
   CookieList list = this->GetAllCookiesForURL(cs, url_foo);
   EXPECT_EQ(1u, list.size());
   EXPECT_EQ("", list[0].Name());
   EXPECT_EQ("a", list[0].Value());
   EXPECT_EQ(1u, this->DeleteAll(cs));
 
-  EXPECT_TRUE(this->SetCookieWithOptions(cs, url_foo, "=b", options));
+  EXPECT_TRUE(this->CreateAndSetCookie(cs, url_foo, "=b", options));
   list = this->GetAllCookiesForURL(cs, url_foo);
   EXPECT_EQ(1u, list.size());
   EXPECT_EQ("", list[0].Name());
diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc
index 072e9a0f..aba1361 100644
--- a/net/dns/dns_transaction_unittest.cc
+++ b/net/dns/dns_transaction_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/bind.h"
 #include "base/containers/circular_deque.h"
 #include "base/message_loop/message_loop.h"
+#include "base/optional.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
@@ -1809,11 +1810,13 @@
   callback.WaitUntilDone();
   EXPECT_EQ(0u, callback.cookie_list_size());
   callback.Reset();
-  helper1.request_context()->cookie_store()->SetCookieWithOptionsAsync(
-      GURL(GetURLFromTemplateWithoutParameters(
-          config_.dns_over_https_servers[0].server_template)),
-      "test-cookie=you-still-fail", CookieOptions(),
-      base::nullopt /* server_time */,
+  GURL cookie_url(GetURLFromTemplateWithoutParameters(
+      config_.dns_over_https_servers[0].server_template));
+  auto cookie = CanonicalCookie::Create(
+      cookie_url, "test-cookie=you-still-fail", base::Time::Now(),
+      base::nullopt /* server_time */);
+  helper1.request_context()->cookie_store()->SetCanonicalCookieAsync(
+      std::move(cookie), cookie_url.scheme(), CookieOptions(),
       base::Bind(&CookieCallback::SetCookieCallback,
                  base::Unretained(&callback)));
   EXPECT_TRUE(helper1.RunUntilDone(transaction_factory_.get()));
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc b/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc
index 1daf7053..6d1fcb8 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc
@@ -1234,9 +1234,11 @@
   EXPECT_TRUE(cookie_scheme_callback1.result());
   ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus>
       set_cookie_callback;
-  cookie_monster->SetCookieWithOptionsAsync(
+  auto cookie = CanonicalCookie::Create(
       GURL("gopher://subdomain.gopheriffic.com/page"), "A=B; max-age=3600",
-      CookieOptions(), base::nullopt /* server_time */,
+      base::Time::Now(), base::nullopt /* server_time */);
+  cookie_monster->SetCanonicalCookieAsync(
+      std::move(cookie), "gopher", CookieOptions(),
       base::BindOnce(&ResultSavingCookieCallback<
                          CanonicalCookie::CookieInclusionStatus>::Run,
                      base::Unretained(&set_cookie_callback)));
@@ -1249,9 +1251,12 @@
   for (int i = 0; i < 50; ++i) {
     ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus>
         set_cookie_callback2;
-    cookie_monster->SetCookieWithOptionsAsync(
+    auto canonical_cookie = CanonicalCookie::Create(
         GURL(base::StringPrintf("http://example%d.com/", i)),
-        "A=B; max-age=3600", CookieOptions(), base::nullopt /* server_time */,
+        "A=B; max-age=3600", base::Time::Now(),
+        base::nullopt /* server_time */);
+    cookie_monster->SetCanonicalCookieAsync(
+        std::move(canonical_cookie), "http", CookieOptions(),
         base::BindOnce(&ResultSavingCookieCallback<
                            CanonicalCookie::CookieInclusionStatus>::Run,
                        base::Unretained(&set_cookie_callback2)));
@@ -1303,9 +1308,11 @@
 
   ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus>
       set_cookie_callback;
-  cookie_monster->SetCookieWithOptionsAsync(
-      GURL("http://www.example.com/"), "A=B; max-age=3600", CookieOptions(),
-      base::nullopt /* server_time */,
+  auto cookie = CanonicalCookie::Create(GURL("http://www.example.com/"),
+                                        "A=B; max-age=3600", base::Time::Now(),
+                                        base::nullopt /* server_time */);
+  cookie_monster->SetCanonicalCookieAsync(
+      std::move(cookie), "http", CookieOptions(),
       base::BindOnce(&ResultSavingCookieCallback<
                          CanonicalCookie::CookieInclusionStatus>::Run,
                      base::Unretained(&set_cookie_callback)));
diff --git a/net/reporting/reporting_uploader_unittest.cc b/net/reporting/reporting_uploader_unittest.cc
index 32293fa..4767b4c 100644
--- a/net/reporting/reporting_uploader_unittest.cc
+++ b/net/reporting/reporting_uploader_unittest.cc
@@ -442,12 +442,12 @@
 
   ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus>
       cookie_callback;
-  context_.cookie_store()->SetCookieWithOptionsAsync(
-      server_.GetURL("/"), "foo=bar", CookieOptions(),
-      base::nullopt /* server_time */,
-      base::BindRepeating(&ResultSavingCookieCallback<
-                              CanonicalCookie::CookieInclusionStatus>::Run,
-                          base::Unretained(&cookie_callback)));
+  GURL url = server_.GetURL("/");
+  auto cookie = CanonicalCookie::Create(url, "foo=bar", base::Time::Now(),
+                                        base::nullopt /* server_time */);
+  context_.cookie_store()->SetCanonicalCookieAsync(
+      std::move(cookie), url.scheme(), CookieOptions(),
+      cookie_callback.MakeCallback());
   cookie_callback.WaitUntilDone();
   ASSERT_EQ(CanonicalCookie::CookieInclusionStatus::INCLUDE,
             cookie_callback.result());
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index c6c013b..0432d6b 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -763,11 +763,14 @@
       continue;
     }
 
-    request_->context()->cookie_store()->SetCookieWithOptionsAsync(
-        request_->url(), cookie_string, options, server_time,
+    // Make a copy for the make_optional.
+    CanonicalCookie cookie_copy = *cookie;
+
+    request_->context()->cookie_store()->SetCanonicalCookieAsync(
+        std::move(cookie), request_->url().scheme(), options,
         base::BindOnce(&URLRequestHttpJob::OnSetCookieResult,
                        weak_factory_.GetWeakPtr(), options,
-                       base::make_optional<CanonicalCookie>(*cookie),
+                       base::make_optional<CanonicalCookie>(cookie_copy),
                        cookie_string));
   }
   // Removing the 1 that |num_cookie_lines_left| started with, signifing that
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index b37083e..d56e39c5 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -2684,17 +2684,22 @@
   TestDelegate sync_delegate;
 
   // Add a secure cookie so we can try to set an insecure cookie and have
-  // SetCanonicalCookie (and therefore SetCookieWithOptions) fail.
+  // SetCanonicalCookie fail.
   GURL::Replacements replace_scheme;
   replace_scheme.SetSchemeStr("https");
   GURL url = test_server.base_url().ReplaceComponents(replace_scheme);
 
-  delayed_cm->SetCookieWithOptionsAsync(
-      url, "AlreadySetCookie=1;Secure", CookieOptions(),
-      base::nullopt /* server_time */, CookieStore::SetCookiesCallback());
-  cm->SetCookieWithOptionsAsync(
-      url, "AlreadySetCookie=1;Secure", CookieOptions(),
-      base::nullopt /* server_time */, CookieStore::SetCookiesCallback());
+  auto cookie1 = CanonicalCookie::Create(url, "AlreadySetCookie=1;Secure",
+                                         base::Time::Now(),
+                                         base::nullopt /* server_time */);
+  delayed_cm->SetCanonicalCookieAsync(std::move(cookie1), url.scheme(),
+                                      CookieOptions(),
+                                      CookieStore::SetCookiesCallback());
+  auto cookie2 = CanonicalCookie::Create(url, "AlreadySetCookie=1;Secure",
+                                         base::Time::Now(),
+                                         base::nullopt /* server_time */);
+  cm->SetCanonicalCookieAsync(std::move(cookie2), url.scheme(), CookieOptions(),
+                              CookieStore::SetCookiesCallback());
 
   std::vector<std::string> cookie_lines(
       {// Fails in SetCanonicalCookie for trying to set a secure cookie
@@ -8747,9 +8752,12 @@
 
     std::unique_ptr<CookieMonster> cm =
         std::make_unique<CookieMonster>(nullptr, nullptr);
-    cm->SetCookieWithOptionsAsync(
-        url_requiring_auth_wo_cookies, "another_cookie=true", CookieOptions(),
-        base::nullopt /* server_time */, CookieStore::SetCookiesCallback());
+    auto another_cookie = CanonicalCookie::Create(
+        url_requiring_auth_wo_cookies, "another_cookie=true", base::Time::Now(),
+        base::nullopt /* server_time */);
+    cm->SetCanonicalCookieAsync(
+        std::move(another_cookie), url_requiring_auth_wo_cookies.scheme(),
+        CookieOptions(), CookieStore::SetCookiesCallback());
     context.set_cookie_store(cm.get());
     context.Init();
 
@@ -8774,9 +8782,12 @@
     // Check maybe_sent_cookies on second roundtrip.
     request->set_maybe_sent_cookies({});
     cm->DeleteAllAsync(CookieStore::DeleteCallback());
-    cm->SetCookieWithOptionsAsync(
-        url_requiring_auth_wo_cookies, "one_more_cookie=true", CookieOptions(),
-        base::nullopt /* server_time */, CookieStore::SetCookiesCallback());
+    auto one_more_cookie = CanonicalCookie::Create(
+        url_requiring_auth_wo_cookies, "one_more_cookie=true",
+        base::Time::Now(), base::nullopt /* server_time */);
+    cm->SetCanonicalCookieAsync(
+        std::move(one_more_cookie), url_requiring_auth_wo_cookies.scheme(),
+        CookieOptions(), CookieStore::SetCookiesCallback());
 
     request->SetAuth(AuthCredentials(kUser, kSecret));
     delegate.RunUntilComplete();
@@ -9145,9 +9156,12 @@
     context.set_network_delegate(&filtering_network_delegate);
     std::unique_ptr<CookieMonster> cm =
         std::make_unique<CookieMonster>(nullptr, nullptr);
-    cm->SetCookieWithOptionsAsync(
-        original_url, "another_cookie=true", CookieOptions(),
-        base::nullopt /* server_time */, CookieStore::SetCookiesCallback());
+    auto another_cookie = CanonicalCookie::Create(
+        original_url, "another_cookie=true", base::Time::Now(),
+        base::nullopt /* server_time */);
+    cm->SetCanonicalCookieAsync(std::move(another_cookie),
+                                original_url.scheme(), CookieOptions(),
+                                CookieStore::SetCookiesCallback());
     context.set_cookie_store(cm.get());
     context.Init();
 
@@ -9171,9 +9185,12 @@
     // Check maybe_sent_cookies on second round trip
     request->set_maybe_sent_cookies({});
     cm->DeleteAllAsync(CookieStore::DeleteCallback());
-    cm->SetCookieWithOptionsAsync(
-        original_url_wo_cookie, "one_more_cookie=true", CookieOptions(),
-        base::nullopt /* server_time */, CookieStore::SetCookiesCallback());
+    auto one_more_cookie = CanonicalCookie::Create(
+        original_url_wo_cookie, "one_more_cookie=true", base::Time::Now(),
+        base::nullopt /* server_time */);
+    cm->SetCanonicalCookieAsync(
+        std::move(one_more_cookie), original_url_wo_cookie.scheme(),
+        CookieOptions(), CookieStore::SetCookiesCallback());
 
     request->FollowDeferredRedirect(base::nullopt, base::nullopt);
     delegate.RunUntilComplete();
diff --git a/net/websockets/websocket_stream_cookie_test.cc b/net/websockets/websocket_stream_cookie_test.cc
index 4c0087b..ffd59ce 100644
--- a/net/websockets/websocket_stream_cookie_test.cc
+++ b/net/websockets/websocket_stream_cookie_test.cc
@@ -149,8 +149,11 @@
   base::WeakPtrFactory<bool> weak_set_cookie_result(&set_cookie_result);
 
   base::RunLoop run_loop;
-  store->SetCookieWithOptionsAsync(
-      cookie_url, cookie_line, CookieOptions(), base::nullopt /* server_time */,
+  auto cookie =
+      CanonicalCookie::Create(cookie_url, cookie_line, base::Time::Now(),
+                              base::nullopt /* server_time */);
+  store->SetCanonicalCookieAsync(
+      std::move(cookie), cookie_url.scheme(), CookieOptions(),
       base::Bind(&SetCookieHelperFunction, run_loop.QuitClosure(),
                  weak_is_called.GetWeakPtr(),
                  weak_set_cookie_result.GetWeakPtr()));
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index 5a7809ff..f41028dd 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -3742,9 +3742,12 @@
     // Set the devtools id to trigger the RawResponse call
     request.devtools_request_id = "TEST";
 
-    context()->cookie_store()->SetCookieWithOptionsAsync(
-        test_server()->GetURL("/"), "a=b", net::CookieOptions(),
-        base::nullopt /* server_time */, base::DoNothing());
+    GURL cookie_url = test_server()->GetURL("/");
+    auto cookie = net::CanonicalCookie::Create(
+        cookie_url, "a=b", base::Time::Now(), base::nullopt /* server_time */);
+    context()->cookie_store()->SetCanonicalCookieAsync(
+        std::move(cookie), cookie_url.scheme(), net::CookieOptions(),
+        base::DoNothing());
 
     base::RunLoop delete_run_loop;
     mojom::URLLoaderPtr loader;
@@ -3786,9 +3789,12 @@
     request.devtools_request_id = "TEST";
 
     // Set the path to an irrelevant url to block the cookie from sending
-    context()->cookie_store()->SetCookieWithOptionsAsync(
-        test_server()->GetURL("/"), "a=b;Path=/something-else",
-        net::CookieOptions(), base::nullopt /* server_time */,
+    GURL cookie_url = test_server()->GetURL("/");
+    auto cookie = net::CanonicalCookie::Create(
+        cookie_url, "a=b;Path=/something-else", base::Time::Now(),
+        base::nullopt /* server_time */);
+    context()->cookie_store()->SetCanonicalCookieAsync(
+        std::move(cookie), cookie_url.scheme(), net::CookieOptions(),
         base::DoNothing());
 
     base::RunLoop delete_run_loop;
diff --git a/services/tracing/public/cpp/trace_startup.cc b/services/tracing/public/cpp/trace_startup.cc
index 5b02671..7ccc0789 100644
--- a/services/tracing/public/cpp/trace_startup.cc
+++ b/services/tracing/public/cpp/trace_startup.cc
@@ -37,14 +37,17 @@
   auto* startup_config = TraceStartupConfig::GetInstance();
 
   if (startup_config->IsEnabled()) {
+    const TraceConfig& trace_config = startup_config->GetTraceConfig();
     if (TracingUsesPerfettoBackend()) {
-      TracingSamplerProfiler::SetupStartupTracing();
+      if (trace_config.IsCategoryGroupEnabled(
+              TRACE_DISABLED_BY_DEFAULT("cpu_profiler"))) {
+        TracingSamplerProfiler::SetupStartupTracing();
+      }
       TraceEventDataSource::GetInstance()->SetupStartupTracing(
           startup_config->GetSessionOwner() ==
           TraceStartupConfig::SessionOwner::kBackgroundTracing);
     }
 
-    const TraceConfig& trace_config = startup_config->GetTraceConfig();
     uint8_t modes = TraceLog::RECORDING_MODE;
     if (!trace_config.event_filters().empty())
       modes |= TraceLog::FILTERING_MODE;
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 7ffde0e..3814828 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -9720,835 +9720,6 @@
       }
     ]
   },
-  "linux-blink-heap-unified-gc": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "base_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "base_util_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "blink_common_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "blink_heap_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "blink_platform_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "webkit_unit_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "blink_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "boringssl_crypto_tests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "boringssl_ssl_tests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "cacheinvalidation_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=-*UsingRealWebcam*",
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "capture_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "cast_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "components_browsertests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "components_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ],
-          "shards": 6
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "crypto_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "events_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "gcm_unit_tests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "gin_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "google_apis_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "gwp_asan_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ipc_tests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "latency_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "libjingle_xmpp_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "media_blink_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "media_service_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "media_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "midi_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "mojo_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "net_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "perfetto_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "services_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "shell_dialogs_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "skia_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "sql_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "storage_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ui_base_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ui_touch_selection_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "url_unittests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "wtf_unittests"
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "--num-retries=3",
-          "--debug",
-          "--additional-driver-flag=--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "isolate_name": "blink_web_tests_exparchive",
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "webkit_layout_tests",
-        "results_handler": "layout tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ],
-          "shards": 12
-        }
-      }
-    ]
-  },
   "linux-blink-heap-verification": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 229c469..211d70a 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -8225,7 +8225,7 @@
               "os": "Ubuntu-16.04"
             }
           ],
-          "shards": 25
+          "shards": 30
         },
         "test": "browser_tests"
       },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index ad3042e..08f2783 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -212,7 +212,7 @@
         # These are very slow on the Chrome OS MSAN trybot for some reason.
         # crbug.com/865455
         'swarming': {
-          'shards': 25,
+          'shards': 30,
         },
       },
       'Linux Chromium OS ASan LSan Tests (1)': {
@@ -1808,12 +1808,6 @@
           '--additional-driver-flag=--enable-features=BlinkHeapConcurrentMarking',
         ],
       },
-      'linux-blink-heap-unified-gc': {
-        'args': [
-          '--debug',
-          '--additional-driver-flag=--enable-blink-features=HeapUnifiedGarbageCollection',
-        ],
-      },
       'mac10.10-blink-rel-dummy': {
         'swarming': {
           'dimension_sets': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 2d1d2880..5c8d2ae 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1753,18 +1753,6 @@
           '--enable-features=BlinkHeapConcurrentMarking',
         ]
       },
-      'linux-blink-heap-unified-gc': {
-        'mixins': [
-          'linux-xenial',
-        ],
-        'test_suites': {
-          'gtest_tests': 'chromium_gtests',
-          'isolated_scripts': 'chromium_webkit_isolated_scripts',
-        },
-        'gtest_args': [
-          '--enable-blink-features=HeapUnifiedGarbageCollection',
-        ],
-      },
       'linux-blink-heap-verification': {
         'mixins': [
           'linux-xenial',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 36bba5c6..69fbc99 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -504,6 +504,21 @@
             ]
         }
     ],
+    "AppListAppRanker": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "AppListAppRanker",
+                    "enable_features": [
+                        "EnableAppRanker"
+                    ]
+                }
+            ]
+        }
+    ],
     "AppListLaunchRecorder": [
         {
             "platforms": [
@@ -3916,6 +3931,25 @@
             ]
         }
     ],
+    "PaymentRequestAlwaysAllowJustInTimePaymentApp": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AlwaysAllowJustInTimePaymentApp"
+                    ]
+                }
+            ]
+        }
+    ],
     "PaymentRequestNoCreditCardAbort": [
         {
             "platforms": [
diff --git a/third_party/binutils/download.py b/third_party/binutils/download.py
index 78c8b4f1..3289717 100755
--- a/third_party/binutils/download.py
+++ b/third_party/binutils/download.py
@@ -31,13 +31,13 @@
 
 
 def ReadFile(filename):
-  with file(filename, 'r') as f:
+  with open(filename, 'r') as f:
     return f.read().strip()
 
 
 def WriteFile(filename, content):
   assert not os.path.exists(filename)
-  with file(filename, 'w') as f:
+  with open(filename, 'w') as f:
     f.write(content)
     f.write('\n')
 
diff --git a/third_party/blink/public/common/manifest/manifest.h b/third_party/blink/public/common/manifest/manifest.h
index e53e85d..e9cac5d 100644
--- a/third_party/blink/public/common/manifest/manifest.h
+++ b/third_party/blink/public/common/manifest/manifest.h
@@ -162,10 +162,6 @@
   std::vector<ImageResource> icons;
 
   // Null if parsing failed or the field was not present.
-  // TODO(constantina): This field is non-standard and part of a Chrome
-  // experiment. See:
-  // https://github.com/WICG/web-share-target/blob/master/docs/interface.md
-  // As such, this field should not be exposed to web contents.
   base::Optional<ShareTarget> share_target;
 
   // Null if parsing failed or the field was not present.
diff --git a/third_party/blink/public/mojom/app_banner/app_banner.mojom b/third_party/blink/public/mojom/app_banner/app_banner.mojom
index 7c2d0e7..170628b 100644
--- a/third_party/blink/public/mojom/app_banner/app_banner.mojom
+++ b/third_party/blink/public/mojom/app_banner/app_banner.mojom
@@ -11,8 +11,8 @@
 
 interface AppBannerController {
   // The browser asks the renderer if the app banner should be shown.
-  BannerPromptRequest(AppBannerService service,
-                      AppBannerEvent& event,
+  BannerPromptRequest(pending_remote<AppBannerService> service,
+                      pending_receiver<AppBannerEvent> event_receiver,
                       array<string> platform) => (AppBannerPromptReply reply);
 };
 
diff --git a/third_party/blink/public/mojom/manifest/manifest.mojom b/third_party/blink/public/mojom/manifest/manifest.mojom
index b199ff83..ff7ad5e 100644
--- a/third_party/blink/public/mojom/manifest/manifest.mojom
+++ b/third_party/blink/public/mojom/manifest/manifest.mojom
@@ -28,10 +28,6 @@
 
   array<ManifestImageResource> icons;
 
-  // TODO(constantina): This field is non-standard and part of a Chrome
-  // experiment. See:
-  // https://github.com/WICG/web-share-target/blob/master/docs/interface.md
-  // As such, this field should not be exposed to the drive-by web.
   ManifestShareTarget? share_target;
 
   // TODO(harrisjay): This field is non-standard and part of a Chrome
diff --git a/third_party/blink/public/mojom/webauthn/authenticator.mojom b/third_party/blink/public/mojom/webauthn/authenticator.mojom
index d5dd46b5..e0026e6 100644
--- a/third_party/blink/public/mojom/webauthn/authenticator.mojom
+++ b/third_party/blink/public/mojom/webauthn/authenticator.mojom
@@ -335,6 +335,10 @@
   // The value of the `enforceCredentialProtectionPolicy`, or false if none was
   // provided.
   bool enforce_protection_policy;
+
+  // The contents of the appidExclude extension, if any. See
+  // https://w3c.github.io/webauthn/#sctn-appid-exclude-extension
+  string? appid_exclude;
 };
 
 enum PublicKeyCredentialType {
diff --git a/third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom b/third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom
index 70f2cb8..a0db4b7a 100644
--- a/third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom
+++ b/third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom
@@ -7,6 +7,7 @@
 import "services/network/public/mojom/fetch_api.mojom";
 import "services/service_manager/public/mojom/interface_provider.mojom";
 import "third_party/blink/public/mojom/blob/blob_url_store.mojom";
+import "third_party/blink/public/mojom/browser_interface_broker.mojom";
 import "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom";
 import "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom";
 import "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom";
@@ -27,7 +28,12 @@
   // Called when the worker host is created.
   OnWorkerHostCreated(
       // Used for accessing services from the worker.
-      service_manager.mojom.InterfaceProvider interface_provider);
+      // TODO(crbug.com/990845): remove when no longer used.
+      service_manager.mojom.InterfaceProvider interface_provider,
+      // Used for accessing services from the worker.
+      // should replace the |interface_provider| above
+      pending_remote<blink.mojom.BrowserInterfaceBroker>
+       browser_interface_broker);
 
   // Called when the worker host started loading the main worker script. This
   // is called after OnWorkerHostCreated(). This is never called when
@@ -67,12 +73,18 @@
   // CreateWorkerHostAndStartScriptLoad() when PlzDedicatedWorker is disabled.
   //
   // Creates a new DedicatedWorkerHost, and requests |worker_interface_provider|
-  // to provide the worker access to mojo interfaces. |origin| must either be
+  // and |browser_interface_broker| (which is expected to replace
+  // |worker_interface_provider|) to provide the worker access to
+  // mojo interfaces.
+  // |origin| must either be
   // unique or match the origin of the creating context (Document or
   // DedicatedWorkerGlobalScope).
   CreateWorkerHost(
       url.mojom.Origin origin,
-      service_manager.mojom.InterfaceProvider& worker_interface_provider);
+      // TODO(crbug.com/990845): remove when no longer used.
+      service_manager.mojom.InterfaceProvider& worker_interface_provider,
+      pending_receiver<blink.mojom.BrowserInterfaceBroker>
+        browser_interface_broker);
 
   // PlzDedicatedWorker:
   // The factory client should call this instead of CreateWorkerHost() when
diff --git a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
index 733eafb..76445f9 100644
--- a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
+++ b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
@@ -6,6 +6,7 @@
 
 import "mojo/public/mojom/base/unguessable_token.mojom";
 import "services/service_manager/public/mojom/interface_provider.mojom";
+import "third_party/blink/public/mojom/browser_interface_broker.mojom";
 import "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom";
 import "third_party/blink/public/mojom/renderer_preference_watcher.mojom";
 import "third_party/blink/public/mojom/renderer_preferences.mojom";
@@ -66,5 +67,11 @@
 
       SharedWorkerHost host,
       SharedWorker& shared_worker,
-      service_manager.mojom.InterfaceProvider interface_provider);
+
+      // TODO(crbug.com/990845): remove when no longer used.
+      service_manager.mojom.InterfaceProvider interface_provider,
+
+      // BrowserInterfaceBroker for providing browser interfaces.
+      pending_remote<blink.mojom.BrowserInterfaceBroker>
+        browser_interface_broker);
 };
diff --git a/third_party/blink/public/platform/web_dedicated_worker.h b/third_party/blink/public/platform/web_dedicated_worker.h
index 479add5..736b2dc7 100644
--- a/third_party/blink/public/platform/web_dedicated_worker.h
+++ b/third_party/blink/public/platform/web_dedicated_worker.h
@@ -18,7 +18,8 @@
 
   // Called when content::DedicatedWorkerHost is created in the browser process.
   virtual void OnWorkerHostCreated(
-      mojo::ScopedMessagePipeHandle interface_provider) = 0;
+      mojo::ScopedMessagePipeHandle interface_provider,
+      mojo::ScopedMessagePipeHandle browser_interface_broker) = 0;
 
   // Called when content::DedicatedWorkerHost started loading the main worker
   // script in the browser process, and the script information is sent back to
diff --git a/third_party/blink/public/web/web_shared_worker.h b/third_party/blink/public/web/web_shared_worker.h
index d4bd876..6a60d9e1 100644
--- a/third_party/blink/public/web/web_shared_worker.h
+++ b/third_party/blink/public/web/web_shared_worker.h
@@ -74,7 +74,8 @@
       PrivacyPreferences privacy_preferences,
       scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
       mojo::ScopedMessagePipeHandle content_settings_handle,
-      mojo::ScopedMessagePipeHandle interface_provider) = 0;
+      mojo::ScopedMessagePipeHandle interface_provider,
+      mojo::ScopedMessagePipeHandle browser_interface_broker) = 0;
 
   // Sends a connect event to the SharedWorker context.
   virtual void Connect(MessagePortChannel) = 0;
diff --git a/third_party/blink/renderer/core/exported/prerendering_test.cc b/third_party/blink/renderer/core/exported/prerendering_test.cc
index 1c970dff..40e3236 100644
--- a/third_party/blink/renderer/core/exported/prerendering_test.cc
+++ b/third_party/blink/renderer/core/exported/prerendering_test.cc
@@ -35,7 +35,6 @@
 #include "base/memory/ptr_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/prerender/prerender_rel_type.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_cache.h"
 #include "third_party/blink/public/platform/web_prerender.h"
 #include "third_party/blink/public/platform/web_prerendering_support.h"
@@ -163,9 +162,7 @@
 class PrerenderingTest : public testing::Test {
  public:
   ~PrerenderingTest() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   void Initialize(const char* base_url, const char* file_name) {
diff --git a/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc b/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc
index bee856da..490682c5 100644
--- a/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc
+++ b/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc
@@ -6,7 +6,6 @@
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_cache.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/public/web/web_document.h"
@@ -131,9 +130,7 @@
 
   // testing::Test:
   void TearDown() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   SubresourceFilteringWebFrameClient client_;
diff --git a/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc b/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc
index 7dba928..561442d 100644
--- a/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc
@@ -31,7 +31,6 @@
 #include "third_party/blink/public/web/web_frame_serializer.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
@@ -69,9 +68,7 @@
   WebFrameSerializerSanitizationTest() { helper_.Initialize(); }
 
   ~WebFrameSerializerSanitizationTest() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   String GenerateMHTMLFromHtml(const String& url, const String& file_name) {
diff --git a/third_party/blink/renderer/core/exported/web_frame_serializer_test.cc b/third_party/blink/renderer/core/exported/web_frame_serializer_test.cc
index 5acb60a..b3b2b69 100644
--- a/third_party/blink/renderer/core/exported/web_frame_serializer_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_serializer_test.cc
@@ -31,7 +31,6 @@
 #include "third_party/blink/public/web/web_frame_serializer.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
@@ -69,9 +68,7 @@
   WebFrameSerializerTest() { helper_.Initialize(); }
 
   ~WebFrameSerializerTest() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   void RegisterMockedImageURLLoad(const String& url) {
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 68cc312..3b6b1be 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -48,7 +48,6 @@
 #include "third_party/blink/public/common/page/launching_process_state.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_cache.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_float_rect.h"
@@ -198,9 +197,7 @@
         chrome_url_("chrome://") {}
 
   ~WebFrameTest() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   void DisableRendererSchedulerThrottling() {
@@ -228,6 +225,13 @@
         WebString::FromUTF8(file_name));
   }
 
+  void RegisterMockedURLLoadWithCustomResponse(const WebURL& full_url,
+                                               const WebString& file_path,
+                                               WebURLResponse response) {
+    url_test_helpers::RegisterMockedURLLoadWithCustomResponse(
+        full_url, file_path, response);
+  }
+
   void RegisterMockedHttpURLLoadWithCSP(const std::string& file_name,
                                         const std::string& csp,
                                         bool report_only = false) {
@@ -238,7 +242,7 @@
                     : WebString("Content-Security-Policy"),
         WebString::FromUTF8(csp));
     std::string full_string = base_url_ + file_name;
-    url_test_helpers::RegisterMockedURLLoadWithCustomResponse(
+    RegisterMockedURLLoadWithCustomResponse(
         ToKURL(full_string),
         test::CoreTestDataPath(WebString::FromUTF8(file_name)), response);
   }
@@ -6998,13 +7002,13 @@
   redirect_response.SetMimeType("text/html");
   redirect_response.SetHttpStatusCode(302);
   redirect_response.SetHttpHeaderField("Location", redirect);
-  Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
-      test_url, redirect_response, file_path);
+  RegisterMockedURLLoadWithCustomResponse(test_url, file_path,
+                                          redirect_response);
 
   WebURLResponse final_response;
   final_response.SetMimeType("text/html");
-  Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
-      redirect_url, final_response, file_path);
+  RegisterMockedURLLoadWithCustomResponse(redirect_url, file_path,
+                                          final_response);
 
   frame_test_helpers::WebViewHelper web_view_helper;
   web_view_helper.InitializeAndLoad(base_url_ + "first_party_redirect.html");
@@ -10930,10 +10934,10 @@
   url_test_helpers::RegisterMockedURLLoad(
       ToKURL(url), test::CoreTestDataPath("not_an_image.ico"), "image/x-icon");
   MultipleDataChunkDelegate delegate;
-  Platform::Current()->GetURLLoaderMockFactory()->SetLoaderDelegate(&delegate);
+  url_test_helpers::SetLoaderDelegate(&delegate);
   frame_test_helpers::WebViewHelper helper;
   helper.InitializeAndLoad(url);
-  Platform::Current()->GetURLLoaderMockFactory()->SetLoaderDelegate(nullptr);
+  url_test_helpers::SetLoaderDelegate(nullptr);
 
   Document* document =
       To<LocalFrame>(helper.GetWebView()->GetPage()->MainFrame())
@@ -12400,7 +12404,7 @@
   // Because the child frame will have placeholder document loader, the main
   // frame will not finish loading, so
   // frame_test_helpers::PumpPendingRequestsForFrameToLoad doesn't work here.
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+  url_test_helpers::ServeAsynchronousRequests();
 
   // Overwrite the client-handled child frame navigation with about:blank.
   WebLocalFrame* child = main_frame->FirstChild()->ToWebLocalFrame();
diff --git a/third_party/blink/renderer/core/exported/web_layer_test.cc b/third_party/blink/renderer/core/exported/web_layer_test.cc
index ffbd4a4..c2d2c56 100644
--- a/third_party/blink/renderer/core/exported/web_layer_test.cc
+++ b/third_party/blink/renderer/core/exported/web_layer_test.cc
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 #include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
 #include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 
 namespace blink {
 
@@ -32,8 +33,7 @@
   }
 
   ~WebLayerListTest() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
+    platform_->GetURLLoaderMockFactory()
         ->UnregisterAllURLsAndClearMemoryCache();
   }
 
@@ -112,6 +112,7 @@
 
   frame_test_helpers::TestWebWidgetClient web_widget_client_;
   std::unique_ptr<frame_test_helpers::WebViewHelper> web_view_helper_;
+  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
 };
 
 INSTANTIATE_LAYER_LIST_TEST_SUITE_P(WebLayerListTest);
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
index ba31e537..cb801633 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
@@ -37,7 +37,6 @@
 #include "cc/layers/layer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_mouse_wheel_event.h"
 #include "third_party/blink/public/platform/web_pointer_event.h"
@@ -82,9 +81,7 @@
   WebPluginContainerTest() : base_url_("http://www.test.com/") {}
 
   void TearDown() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   void CalculateGeometry(WebPluginContainerImpl* plugin_container_impl,
diff --git a/third_party/blink/renderer/core/exported/web_searchable_form_data_test.cc b/third_party/blink/renderer/core/exported/web_searchable_form_data_test.cc
index 170095ee..810e296d 100644
--- a/third_party/blink/renderer/core/exported/web_searchable_form_data_test.cc
+++ b/third_party/blink/renderer/core/exported/web_searchable_form_data_test.cc
@@ -34,7 +34,6 @@
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/public/web/web_document.h"
 #include "third_party/blink/public/web/web_frame.h"
@@ -60,9 +59,7 @@
   WebSearchableFormDataTest() = default;
 
   ~WebSearchableFormDataTest() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   frame_test_helpers::WebViewHelper web_view_helper_;
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
index a19fd4d..cf34af7 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
@@ -185,7 +185,8 @@
     PrivacyPreferences privacy_preferences,
     scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
     mojo::ScopedMessagePipeHandle content_settings_handle,
-    mojo::ScopedMessagePipeHandle interface_provider) {
+    mojo::ScopedMessagePipeHandle interface_provider,
+    mojo::ScopedMessagePipeHandle browser_interface_broker) {
   DCHECK(IsMainThread());
   script_request_url_ = script_request_url;
   name_ = name;
@@ -196,6 +197,11 @@
       std::move(content_settings_handle), 0u);
   pending_interface_provider_.set_handle(std::move(interface_provider));
 
+  browser_interface_broker_ =
+      mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>(
+          std::move(browser_interface_broker),
+          mojom::blink::BrowserInterfaceBroker::Version_);
+
   devtools_worker_token_ = devtools_worker_token;
   // |shadow_page_| must be created after |devtools_worker_token_| because it
   // triggers creation of a InspectorNetworkAgent that tries to access the
@@ -275,7 +281,8 @@
       nullptr /* origin_trial_tokens */, devtools_worker_token_,
       std::move(worker_settings), kV8CacheOptionsDefault,
       nullptr /* worklet_module_response_map */,
-      std::move(pending_interface_provider_), BeginFrameProviderParams(),
+      std::move(pending_interface_provider_),
+      std::move(browser_interface_broker_), BeginFrameProviderParams(),
       nullptr /* parent_feature_policy */, base::UnguessableToken());
   StartWorkerThread(std::move(creation_params), script_request_url_,
                     String() /* source_code */, *outside_settings_object);
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
index 8c8c68a7..9c33996 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
@@ -40,6 +40,7 @@
 #include "services/network/public/mojom/ip_address_space.mojom-blink.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom-blink.h"
 #include "third_party/blink/public/common/privacy_preferences.h"
+#include "third_party/blink/public/mojom/browser_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/mojom/csp/content_security_policy.mojom-blink.h"
 #include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom-blink.h"
 #include "third_party/blink/public/web/web_shared_worker_client.h"
@@ -95,7 +96,8 @@
       PrivacyPreferences privacy_preferences,
       scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
       mojo::ScopedMessagePipeHandle content_settings_handle,
-      mojo::ScopedMessagePipeHandle interface_provider) override;
+      mojo::ScopedMessagePipeHandle interface_provider,
+      mojo::ScopedMessagePipeHandle browser_interface_broker) override;
   void Connect(MessagePortChannel) override;
   void TerminateWorkerContext() override;
   void PauseWorkerContextOnStart() override;
@@ -146,9 +148,13 @@
   WebString user_agent_;
   network::mojom::IPAddressSpace creation_address_space_;
 
+  // TODO(crbug.com/990845): remove when no longer used.
   service_manager::mojom::blink::InterfaceProviderPtrInfo
       pending_interface_provider_;
 
+  mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>
+      browser_interface_broker_;
+
   Persistent<ApplicationCacheHostForSharedWorker> appcache_host_;
 
   base::WeakPtrFactory<WebSharedWorkerImpl> weak_ptr_factory_{this};
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 143c6376..ec3b77a63 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -46,7 +46,6 @@
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
 #include "third_party/blink/public/common/manifest/web_display_mode.h"
 #include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_cursor_info.h"
 #include "third_party/blink/public/platform/web_drag_data.h"
@@ -245,9 +244,7 @@
 
   void TearDown() override {
     EventTiming::SetTickClockForTesting(nullptr);
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
  protected:
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 748ddb0a..3299a48 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1079,7 +1079,11 @@
   bool did_need_layout = NeedsLayout();
 
   PhysicalRect new_frame = layout->ReplacedContentRect();
-  DCHECK(!new_frame.size.HasFraction());
+#if DCHECK_IS_ON()
+  if (new_frame.Width() != LayoutUnit::Max().RawValue() &&
+      new_frame.Height() != LayoutUnit::Max().RawValue())
+    DCHECK(!new_frame.size.HasFraction());
+#endif
   bool bounds_will_change = PhysicalSize(Size()) != new_frame.size;
 
   // If frame bounds are changing mark the view for layout. Also check the
diff --git a/third_party/blink/renderer/core/frame/visual_viewport_test.cc b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
index ef25053..d82970c 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -10,7 +10,6 @@
 #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/platform.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/public/platform/web_layer_tree_view.h"
@@ -103,9 +102,7 @@
   }
 
   ~VisualViewportTest() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   void NavigateTo(const std::string& url) {
diff --git a/third_party/blink/renderer/core/html/forms/chooser_resource_loader.cc b/third_party/blink/renderer/core/html/forms/chooser_resource_loader.cc
index e5b10c3..f2343ba 100644
--- a/third_party/blink/renderer/core/html/forms/chooser_resource_loader.cc
+++ b/third_party/blink/renderer/core/html/forms/chooser_resource_loader.cc
@@ -10,138 +10,138 @@
 
 namespace blink {
 
-String ChooserResourceLoader::GetSuggestionPickerStyleSheet() {
+Vector<char> ChooserResourceLoader::GetSuggestionPickerStyleSheet() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_SUGGESTION_PICKER_CSS);
+  return UncompressResourceAsBinary(IDR_SUGGESTION_PICKER_CSS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetSuggestionPickerJS() {
+Vector<char> ChooserResourceLoader::GetSuggestionPickerJS() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_SUGGESTION_PICKER_JS);
+  return UncompressResourceAsBinary(IDR_SUGGESTION_PICKER_JS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetPickerButtonStyleSheet() {
+Vector<char> ChooserResourceLoader::GetPickerButtonStyleSheet() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_PICKER_BUTTON_CSS);
+  return UncompressResourceAsBinary(IDR_PICKER_BUTTON_CSS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetPickerCommonStyleSheet() {
+Vector<char> ChooserResourceLoader::GetPickerCommonStyleSheet() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_PICKER_COMMON_CSS);
+  return UncompressResourceAsBinary(IDR_PICKER_COMMON_CSS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetPickerCommonJS() {
+Vector<char> ChooserResourceLoader::GetPickerCommonJS() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_PICKER_COMMON_JS);
+  return UncompressResourceAsBinary(IDR_PICKER_COMMON_JS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetCalendarPickerStyleSheet() {
+Vector<char> ChooserResourceLoader::GetCalendarPickerStyleSheet() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_CALENDAR_PICKER_CSS);
+  return UncompressResourceAsBinary(IDR_CALENDAR_PICKER_CSS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetCalendarPickerJS() {
+Vector<char> ChooserResourceLoader::GetCalendarPickerJS() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_CALENDAR_PICKER_JS);
+  return UncompressResourceAsBinary(IDR_CALENDAR_PICKER_JS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetColorSuggestionPickerStyleSheet() {
+Vector<char> ChooserResourceLoader::GetColorSuggestionPickerStyleSheet() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_COLOR_SUGGESTION_PICKER_CSS);
+  return UncompressResourceAsBinary(IDR_COLOR_SUGGESTION_PICKER_CSS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetColorSuggestionPickerJS() {
+Vector<char> ChooserResourceLoader::GetColorSuggestionPickerJS() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_COLOR_SUGGESTION_PICKER_JS);
+  return UncompressResourceAsBinary(IDR_COLOR_SUGGESTION_PICKER_JS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetColorPickerStyleSheet() {
+Vector<char> ChooserResourceLoader::GetColorPickerStyleSheet() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_COLOR_PICKER_CSS);
+  return UncompressResourceAsBinary(IDR_COLOR_PICKER_CSS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetCalendarPickerRefreshStyleSheet() {
+Vector<char> ChooserResourceLoader::GetCalendarPickerRefreshStyleSheet() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_CALENDAR_PICKER_REFRESH_CSS);
+  return UncompressResourceAsBinary(IDR_CALENDAR_PICKER_REFRESH_CSS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetColorPickerJS() {
+Vector<char> ChooserResourceLoader::GetColorPickerJS() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_COLOR_PICKER_JS);
+  return UncompressResourceAsBinary(IDR_COLOR_PICKER_JS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetColorPickerCommonJS() {
+Vector<char> ChooserResourceLoader::GetColorPickerCommonJS() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_COLOR_PICKER_COMMON_JS);
+  return UncompressResourceAsBinary(IDR_COLOR_PICKER_COMMON_JS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetListPickerStyleSheet() {
+Vector<char> ChooserResourceLoader::GetListPickerStyleSheet() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_LIST_PICKER_CSS);
+  return UncompressResourceAsBinary(IDR_LIST_PICKER_CSS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
-String ChooserResourceLoader::GetListPickerJS() {
+Vector<char> ChooserResourceLoader::GetListPickerJS() {
 #if !defined(OS_ANDROID)
-  return UncompressResourceAsString(IDR_LIST_PICKER_JS);
+  return UncompressResourceAsBinary(IDR_LIST_PICKER_JS);
 #else
   NOTREACHED();
-  return String();
+  return Vector<char>();
 #endif
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/chooser_resource_loader.h b/third_party/blink/renderer/core/html/forms/chooser_resource_loader.h
index bd58bf2..ac68494 100644
--- a/third_party/blink/renderer/core/html/forms/chooser_resource_loader.h
+++ b/third_party/blink/renderer/core/html/forms/chooser_resource_loader.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_CHOOSER_RESOURCE_LOADER_H_
 
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
 
 namespace blink {
 
@@ -15,49 +15,49 @@
 
  public:
   // Returns the picker common stylesheet as a string.
-  static String GetPickerCommonStyleSheet();
+  static Vector<char> GetPickerCommonStyleSheet();
 
   // Returns the picker common javascript as a string.
-  static String GetPickerCommonJS();
+  static Vector<char> GetPickerCommonJS();
 
   // Returns the picker button stylesheet as a string.
-  static String GetPickerButtonStyleSheet();
+  static Vector<char> GetPickerButtonStyleSheet();
 
   // Returns the suggestion picker stylesheet as a string.
-  static String GetSuggestionPickerStyleSheet();
+  static Vector<char> GetSuggestionPickerStyleSheet();
 
   // Returns the suggestion picker javascript as a string.
-  static String GetSuggestionPickerJS();
+  static Vector<char> GetSuggestionPickerJS();
 
   // Returns the suggestion picker stylesheet as a string.
-  static String GetCalendarPickerStyleSheet();
+  static Vector<char> GetCalendarPickerStyleSheet();
 
   // Returns the calendar picker refresh stylesheet as a string.
-  static String GetCalendarPickerRefreshStyleSheet();
+  static Vector<char> GetCalendarPickerRefreshStyleSheet();
 
   // Returns the suggestion picker javascript as a string.
-  static String GetCalendarPickerJS();
+  static Vector<char> GetCalendarPickerJS();
 
   // Returns the color suggestion picker stylesheet as a string.
-  static String GetColorSuggestionPickerStyleSheet();
+  static Vector<char> GetColorSuggestionPickerStyleSheet();
 
   // Returns the color suggestion picker javascript as a string.
-  static String GetColorSuggestionPickerJS();
+  static Vector<char> GetColorSuggestionPickerJS();
 
   // Returns the color picker stylesheet as a string.
-  static String GetColorPickerStyleSheet();
+  static Vector<char> GetColorPickerStyleSheet();
 
   // Returns the color picker javascript as a string.
-  static String GetColorPickerJS();
+  static Vector<char> GetColorPickerJS();
 
   // Returns the color picker common javascript as a string.
-  static String GetColorPickerCommonJS();
+  static Vector<char> GetColorPickerCommonJS();
 
   // Returns the list picker stylesheet as a string.
-  static String GetListPickerStyleSheet();
+  static Vector<char> GetListPickerStyleSheet();
 
   // Returns the list picker javascript as a string.
-  static String GetListPickerJS();
+  static Vector<char> GetListPickerJS();
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc b/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
index d5d6fe8..71c2174c 100644
--- a/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
+++ b/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
@@ -99,8 +99,8 @@
 
   PagePopupClient::AddString(
       "<!DOCTYPE html><head><meta charset='UTF-8'><style>\n", data);
-  AddString(ChooserResourceLoader::GetPickerCommonStyleSheet(), data);
-  AddString(ChooserResourceLoader::GetColorPickerStyleSheet(), data);
+  data->Append(ChooserResourceLoader::GetPickerCommonStyleSheet());
+  data->Append(ChooserResourceLoader::GetColorPickerStyleSheet());
 
   PagePopupClient::AddString(
       "</style></head><body>\n"
@@ -113,9 +113,9 @@
   AddProperty("zoomFactor", ZoomFactor(), data);
   AddProperty("shouldShowColorSuggestionPicker", false, data);
   PagePopupClient::AddString("};\n", data);
-  AddString(ChooserResourceLoader::GetPickerCommonJS(), data);
-  AddString(ChooserResourceLoader::GetColorPickerJS(), data);
-  AddString(ChooserResourceLoader::GetColorPickerCommonJS(), data);
+  data->Append(ChooserResourceLoader::GetPickerCommonJS());
+  data->Append(ChooserResourceLoader::GetColorPickerJS());
+  data->Append(ChooserResourceLoader::GetColorPickerCommonJS());
   PagePopupClient::AddString("</script></body>\n", data);
 }
 
@@ -131,10 +131,10 @@
 
   PagePopupClient::AddString(
       "<!DOCTYPE html><head><meta charset='UTF-8'><style>\n", data);
-  AddString(ChooserResourceLoader::GetPickerCommonStyleSheet(), data);
-  AddString(ChooserResourceLoader::GetColorSuggestionPickerStyleSheet(), data);
+  data->Append(ChooserResourceLoader::GetPickerCommonStyleSheet());
+  data->Append(ChooserResourceLoader::GetColorSuggestionPickerStyleSheet());
   if (RuntimeEnabledFeatures::FormControlsRefreshEnabled())
-    AddString(ChooserResourceLoader::GetColorPickerStyleSheet(), data);
+    data->Append(ChooserResourceLoader::GetColorPickerStyleSheet());
 
   PagePopupClient::AddString(
       "</style></head><body>\n"
@@ -155,11 +155,11 @@
   AddProperty("isFormControlsRefreshEnabled",
               RuntimeEnabledFeatures::FormControlsRefreshEnabled(), data);
   PagePopupClient::AddString("};\n", data);
-  AddString(ChooserResourceLoader::GetPickerCommonJS(), data);
-  AddString(ChooserResourceLoader::GetColorSuggestionPickerJS(), data);
+  data->Append(ChooserResourceLoader::GetPickerCommonJS());
+  data->Append(ChooserResourceLoader::GetColorSuggestionPickerJS());
   if (RuntimeEnabledFeatures::FormControlsRefreshEnabled())
-    AddString(ChooserResourceLoader::GetColorPickerJS(), data);
-  AddString(ChooserResourceLoader::GetColorPickerCommonJS(), data);
+    data->Append(ChooserResourceLoader::GetColorPickerJS());
+  data->Append(ChooserResourceLoader::GetColorPickerCommonJS());
   PagePopupClient::AddString("</script></body>\n", data);
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc b/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc
index 6b8b5f1..b875ee2c 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc
@@ -122,14 +122,13 @@
 
   AddString("<!DOCTYPE html><head><meta charset='UTF-8'><style>\n", data);
 
-  AddString(ChooserResourceLoader::GetPickerCommonStyleSheet(), data);
+  data->Append(ChooserResourceLoader::GetPickerCommonStyleSheet());
   if (!RuntimeEnabledFeatures::FormControlsRefreshEnabled())
-    AddString(ChooserResourceLoader::GetPickerButtonStyleSheet(), data);
-  AddString(ChooserResourceLoader::GetSuggestionPickerStyleSheet(), data);
-  AddString(ChooserResourceLoader::GetCalendarPickerStyleSheet(), data);
+    data->Append(ChooserResourceLoader::GetPickerButtonStyleSheet());
+  data->Append(ChooserResourceLoader::GetSuggestionPickerStyleSheet());
+  data->Append(ChooserResourceLoader::GetCalendarPickerStyleSheet());
   if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) {
-    AddString(ChooserResourceLoader::GetCalendarPickerRefreshStyleSheet(),
-              data);
+    data->Append(ChooserResourceLoader::GetCalendarPickerRefreshStyleSheet());
   }
   AddString(
       "</style></head><body><div id=main>Loading...</div><script>\n"
@@ -215,9 +214,9 @@
   }
   AddString("}\n", data);
 
-  AddString(ChooserResourceLoader::GetPickerCommonJS(), data);
-  AddString(ChooserResourceLoader::GetSuggestionPickerJS(), data);
-  AddString(ChooserResourceLoader::GetCalendarPickerJS(), data);
+  data->Append(ChooserResourceLoader::GetPickerCommonJS());
+  data->Append(ChooserResourceLoader::GetSuggestionPickerJS());
+  data->Append(ChooserResourceLoader::GetCalendarPickerJS());
   AddString("</script></body>\n", data);
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/external_popup_menu_test.cc b/third_party/blink/renderer/core/html/forms/external_popup_menu_test.cc
index b1fda914..17dee47 100644
--- a/third_party/blink/renderer/core/html/forms/external_popup_menu_test.cc
+++ b/third_party/blink/renderer/core/html/forms/external_popup_menu_test.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/public/web/web_external_popup_menu.h"
 #include "third_party/blink/public/web/web_popup_menu_info.h"
@@ -103,9 +102,7 @@
     WebView()->SetUseExternalPopupMenus(true);
   }
   void TearDown() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   void RegisterMockedURLLoad(const std::string& file_name) {
diff --git a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
index a778eca..dd2d14b 100644
--- a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
+++ b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
@@ -233,8 +233,8 @@
   float scale_factor = chrome_client_->WindowToViewportScalar(1.f);
   PagePopupClient::AddString(
       "<!DOCTYPE html><head><meta charset='UTF-8'><style>\n", data);
-  AddString(ChooserResourceLoader::GetPickerCommonStyleSheet(), data);
-  AddString(ChooserResourceLoader::GetListPickerStyleSheet(), data);
+  data->Append(ChooserResourceLoader::GetPickerCommonStyleSheet());
+  data->Append(ChooserResourceLoader::GetListPickerStyleSheet());
   if (!RuntimeEnabledFeatures::ForceTallerSelectPopupEnabled())
     PagePopupClient::AddString("@media (any-pointer:coarse) {", data);
   int padding = static_cast<int>(roundf(4 * scale_factor));
@@ -289,8 +289,8 @@
                      : owner_element.ClientPaddingLeft().ToDouble(),
               data);
   PagePopupClient::AddString("};\n", data);
-  AddString(ChooserResourceLoader::GetPickerCommonJS(), data);
-  AddString(ChooserResourceLoader::GetListPickerJS(), data);
+  data->Append(ChooserResourceLoader::GetPickerCommonJS());
+  data->Append(ChooserResourceLoader::GetListPickerJS());
 
   PagePopupClient::AddString("</script></body>\n", data);
 }
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc
index 69e1fb5..766bfc88 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc
@@ -7,7 +7,6 @@
 #include <memory>
 #include "base/strings/stringprintf.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_client_hints_type.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
@@ -22,6 +21,7 @@
 #include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
 #include "third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
@@ -218,6 +218,8 @@
     kPreloadDisabled,
   };
 
+  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
+
   MediaValuesCached::MediaValuesCachedData CreateMediaValuesData() {
     MediaValuesCached::MediaValuesCachedData data;
     data.viewport_width = 500;
@@ -1152,7 +1154,7 @@
            network::mojom::ReferrerPolicy::kAlways);
 
   KURL preload_url("http://example.test/sheet.css");
-  Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
+  platform_->GetURLLoaderMockFactory()->RegisterURL(
       preload_url, WrappedResourceResponse(ResourceResponse()), "");
 
   ReferrerPolicyTestCase test_case = {
diff --git a/third_party/blink/renderer/core/input/ime_on_focus_test.cc b/third_party/blink/renderer/core/input/ime_on_focus_test.cc
index 69baae1..5f3bfe5 100644
--- a/third_party/blink/renderer/core/input/ime_on_focus_test.cc
+++ b/third_party/blink/renderer/core/input/ime_on_focus_test.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/public/web/web_document.h"
@@ -46,9 +45,7 @@
   ImeOnFocusTest() : base_url_("http://www.test.com/") {}
 
   void TearDown() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
  protected:
diff --git a/third_party/blink/renderer/core/input/touch_action_test.cc b/third_party/blink/renderer/core/input/touch_action_test.cc
index 86780a2..2729be22 100644
--- a/third_party/blink/renderer/core/input/touch_action_test.cc
+++ b/third_party/blink/renderer/core/input/touch_action_test.cc
@@ -29,7 +29,6 @@
  */
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_touch_event.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
@@ -105,9 +104,7 @@
   }
 
   void TearDown() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
  protected:
diff --git a/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.cc b/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.cc
index 99ca43c..342102e 100644
--- a/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.cc
+++ b/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.cc
@@ -51,7 +51,8 @@
       base::UnguessableToken::Create(), nullptr /* worker_settings */,
       kV8CacheOptionsDefault, module_responses_map,
       service_manager::mojom::blink::InterfaceProviderPtrInfo(),
-      BeginFrameProviderParams(), nullptr /* parent_feature_policy */,
+      mojo::NullRemote(), BeginFrameProviderParams(),
+      nullptr /* parent_feature_policy */,
       base::UnguessableToken() /* agent_cluster_id */);
   global_scope_ = LayoutWorkletGlobalScope::Create(
       frame, std::move(creation_params), *reporting_proxy_,
diff --git a/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc b/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc
index 079346f2..e8b06493 100644
--- a/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc
@@ -32,7 +32,6 @@
 
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -43,6 +42,7 @@
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
 
@@ -53,8 +53,7 @@
   LayoutGeometryMapTest() : base_url_("http://www.test.com/") {}
 
   void TearDown() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
+    platform_->GetURLLoaderMockFactory()
         ->UnregisterAllURLsAndClearMemoryCache();
   }
 
@@ -149,6 +148,7 @@
   }
 
   const std::string base_url_;
+  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
 };
 
 TEST_F(LayoutGeometryMapTest, SimpleGeometryMapTest) {
diff --git a/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.cc b/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.cc
index c3439ea..3975237c 100644
--- a/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.cc
+++ b/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.cc
@@ -270,4 +270,13 @@
   return nullptr;
 }
 
+bool LegacyAbstractInlineTextBox::IsLineBreak() const {
+  DCHECK(!inline_text_box_ ||
+         !inline_text_box_->GetLineLayoutItem().NeedsLayout());
+  if (!inline_text_box_)
+    return false;
+
+  return inline_text_box_->IsLineBreak();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.h b/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.h
index 81c5f70b1..c173fe2 100644
--- a/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.h
+++ b/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.h
@@ -75,6 +75,7 @@
   virtual bool IsLast() const = 0;
   virtual scoped_refptr<AbstractInlineTextBox> NextOnLine() const = 0;
   virtual scoped_refptr<AbstractInlineTextBox> PreviousOnLine() const = 0;
+  virtual bool IsLineBreak() const = 0;
 
  protected:
   explicit AbstractInlineTextBox(LineLayoutText line_layout_item);
@@ -118,6 +119,7 @@
   bool IsLast() const final;
   scoped_refptr<AbstractInlineTextBox> NextOnLine() const final;
   scoped_refptr<AbstractInlineTextBox> PreviousOnLine() const final;
+  bool IsLineBreak() const final;
 
   InlineTextBox* inline_text_box_;
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
index 4c1c7ee..cf430a6 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
@@ -256,4 +256,11 @@
   return nullptr;
 }
 
+bool NGAbstractInlineTextBox::IsLineBreak() const {
+  if (!fragment_)
+    return false;
+  DCHECK(!NeedsLayout());
+  return PhysicalTextFragment().IsLineBreak();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h
index fb2368e..551e5642 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h
@@ -50,6 +50,7 @@
   bool IsLast() const final;
   scoped_refptr<AbstractInlineTextBox> NextOnLine() const final;
   scoped_refptr<AbstractInlineTextBox> PreviousOnLine() const final;
+  bool IsLineBreak() const final;
 
   const NGPaintFragment* fragment_;
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
index 39c309a..ee66768 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
@@ -9,6 +9,30 @@
 
 namespace blink {
 
+NGFragmentItem::NGFragmentItem(const NGPhysicalTextFragment& text)
+    : layout_object_(text.GetLayoutObject()),
+      text_({text.TextShapeResult()}),
+      size_(text.Size()),
+      type_(kText),
+      style_variant_(static_cast<unsigned>(text.StyleVariant())) {}
+
+NGFragmentItem::NGFragmentItem(const NGPhysicalLineBoxFragment& line,
+                               wtf_size_t item_count)
+    : layout_object_(nullptr),
+      line_({line.Metrics(), To<NGInlineBreakToken>(line.BreakToken()),
+             item_count}),
+      size_(line.Size()),
+      type_(kLine),
+      style_variant_(static_cast<unsigned>(line.StyleVariant())) {}
+
+NGFragmentItem::NGFragmentItem(const NGPhysicalBoxFragment& box,
+                               wtf_size_t item_count)
+    : layout_object_(box.GetLayoutObject()),
+      box_({&box, item_count}),
+      size_(box.Size()),
+      type_(kBox),
+      style_variant_(static_cast<unsigned>(box.StyleVariant())) {}
+
 NGFragmentItem::~NGFragmentItem() {
   switch (Type()) {
     case kText:
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
index 3011edd..d1bc86f 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
@@ -55,13 +55,10 @@
 
   enum ItemType { kText, kGeneratedText, kLine, kBox };
 
-  NGFragmentItem(const LayoutObject& layout_object,
-                 NGStyleVariant style_variant,
-                 Text&& text)
-      : layout_object_(&layout_object),
-        text_(text),
-        type_(kText),
-        style_variant_(static_cast<unsigned>(style_variant)) {}
+  // TODO(kojii): Should be able to create without once creating fragments.
+  NGFragmentItem(const NGPhysicalTextFragment& text);
+  NGFragmentItem(const NGPhysicalBoxFragment& box, wtf_size_t item_count);
+  NGFragmentItem(const NGPhysicalLineBoxFragment& line, wtf_size_t item_count);
 
   ~NGFragmentItem() final;
 
@@ -109,10 +106,7 @@
     Box box_;
   };
 
-  union {
-    PhysicalOffset offset_;
-    LogicalOffset logical_offset_;
-  };
+  PhysicalOffset offset_;
   PhysicalSize size_;
 
   struct NGInkOverflowModel {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
index a869dfad..be9f3150 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
@@ -5,40 +5,104 @@
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h"
 
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 
 namespace blink {
 
-void NGFragmentItemsBuilder::AddChildren(const ChildList& children) {
-  items_.ReserveCapacity(items_.size() + children.size());
-  offsets_.ReserveCapacity(items_.size() + children.size());
+void NGFragmentItemsBuilder::SetTextContent(const NGInlineNode& node) {
+  const NGInlineItemsData& items_data = node.ItemsData(false);
+  text_content_ = items_data.text_content;
+  const NGInlineItemsData& first_line = node.ItemsData(true);
+  if (&items_data != &first_line)
+    first_line_text_content_ = first_line.text_content;
+}
+
+void NGFragmentItemsBuilder::AddLine(const NGPhysicalLineBoxFragment& line,
+                                     ChildList& children) {
+  DCHECK_EQ(items_.size(), offsets_.size());
+#if DCHECK_IS_ON()
+  DCHECK(!is_converted_to_physical_);
+#endif
+
+  // Reserve the capacity for (children + line box item).
+  wtf_size_t capacity = items_.size() + children.size() + 1;
+  items_.ReserveCapacity(capacity);
+  offsets_.ReserveCapacity(capacity);
+
+  // Add an empty item so that the start of the line can be set later.
+  wtf_size_t line_start_index = items_.size();
+  items_.Grow(line_start_index + 1);
+  offsets_.Grow(line_start_index + 1);
+
+  AddItems({children.begin(), children.size()});
+
+  // All children are added. Create an item for the start of the line.
+  wtf_size_t item_count = items_.size() - line_start_index;
+  items_[line_start_index] = std::make_unique<NGFragmentItem>(line, item_count);
+  // TODO(kojii): We probably need an end marker too for the reverse-order
+  // traversals.
+}
+
+void NGFragmentItemsBuilder::AddItems(base::span<Child> children) {
+  DCHECK_EQ(items_.size(), offsets_.size());
 
   for (auto& child : children) {
-    if (child.fragment) {
-      items_.push_back(std::make_unique<NGFragmentItem>(
-          *child.fragment->GetLayoutObject(), child.fragment->StyleVariant(),
-          NGFragmentItem::Text{child.fragment->TextShapeResult()}));
+    if (const NGPhysicalTextFragment* text = child.fragment.get()) {
+      DCHECK(text->TextShapeResult());
+      DCHECK_EQ(text->StartOffset(), text->TextShapeResult()->StartIndex());
+      DCHECK_EQ(text->EndOffset(), text->TextShapeResult()->EndIndex());
+      items_.push_back(std::make_unique<NGFragmentItem>(*text));
       offsets_.push_back(child.offset);
       continue;
     }
 
-    // TODO(kojii): Implement other cases.
+    if (child.layout_result) {
+      // Add an empty item so that the start of the box can be set later.
+      wtf_size_t box_start_index = items_.size();
+      items_.Grow(box_start_index + 1);
+      offsets_.push_back(child.offset);
+      // TODO(kojii): Add children and update children_count.
+      // All children are added. Create an item for the start of the box.
+      wtf_size_t item_count = items_.size() - box_start_index;
+      const NGPhysicalBoxFragment& box =
+          To<NGPhysicalBoxFragment>(child.layout_result->PhysicalFragment());
+      items_[box_start_index] =
+          std::make_unique<NGFragmentItem>(box, item_count);
+      continue;
+    }
+
+    DCHECK(!child.out_of_flow_positioned_box);
   }
 }
 
+// Convert internal logical offsets to physical. Items are kept with logical
+// offset until outer box size is determined.
 void NGFragmentItemsBuilder::ConvertToPhysical(WritingMode writing_mode,
                                                TextDirection direction,
-                                               PhysicalSize outer_size) {
+                                               const PhysicalSize& outer_size) {
   CHECK_EQ(items_.size(), offsets_.size());
+#if DCHECK_IS_ON()
+  DCHECK(!is_converted_to_physical_);
+#endif
+
   const LogicalOffset* offset_iter = offsets_.begin();
   for (auto& item : items_) {
-    PhysicalOffset offset = offset_iter->ConvertToPhysical(
-        writing_mode, direction, outer_size, item->Size());
-    item->SetOffset(offset);
+    item->SetOffset(offset_iter->ConvertToPhysical(writing_mode, direction,
+                                                   outer_size, item->Size()));
     ++offset_iter;
   }
+
+#if DCHECK_IS_ON()
+  is_converted_to_physical_ = true;
+#endif
 }
 
-void NGFragmentItemsBuilder::ToFragmentItems(void* data) {
+void NGFragmentItemsBuilder::ToFragmentItems(WritingMode writing_mode,
+                                             TextDirection direction,
+                                             const PhysicalSize& outer_size,
+                                             void* data) {
+  ConvertToPhysical(writing_mode, direction, outer_size);
   new (data) NGFragmentItems(this);
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h
index a0b1084..8b36f55 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h
@@ -14,6 +14,7 @@
 class NGBoxFragmentBuilder;
 class NGFragmentItem;
 class NGFragmentItems;
+class NGInlineNode;
 
 // This class builds |NGFragmentItems|.
 //
@@ -24,22 +25,41 @@
  public:
   NGFragmentItemsBuilder(NGBoxFragmentBuilder* box_builder) {}
 
+  const String& TextContent(bool first_line) const {
+    return UNLIKELY(first_line && first_line_text_content_)
+               ? first_line_text_content_
+               : text_content_;
+  }
+  void SetTextContent(const NGInlineNode& node);
+
+  // Add a line at once.
   using Child = NGLineBoxFragmentBuilder::Child;
   using ChildList = NGLineBoxFragmentBuilder::ChildList;
-  void AddChildren(const ChildList& children);
+  void AddLine(const NGPhysicalLineBoxFragment& line, ChildList& children);
+
+  // Build a |NGFragmentItems|. The builder cannot build twice because data set
+  // to this builder may be cleared.
+  void ToFragmentItems(WritingMode writing_mode,
+                       TextDirection direction,
+                       const PhysicalSize& outer_size,
+                       void* data);
+
+ private:
+  void AddItems(base::span<Child> children);
 
   void ConvertToPhysical(WritingMode writing_mode,
                          TextDirection direction,
-                         PhysicalSize outer_size);
+                         const PhysicalSize& outer_size);
 
-  void ToFragmentItems(void* data);
-
- private:
   Vector<std::unique_ptr<NGFragmentItem>> items_;
   Vector<LogicalOffset> offsets_;
   String text_content_;
   String first_line_text_content_;
 
+#if DCHECK_IS_ON()
+  bool is_converted_to_physical_ = false;
+#endif
+
   friend class NGFragmentItems;
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 8d4eb455..4084001 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -365,7 +365,6 @@
     container_builder_.SetIsSelfCollapsing();
     container_builder_.SetIsEmptyLineBox();
     container_builder_.SetBaseDirection(line_info->BaseDirection());
-    container_builder_.AddChildren(line_box_);
     return;
   }
 
@@ -379,7 +378,6 @@
   if (line_info->UseFirstLineStyle())
     container_builder_.SetStyleVariant(NGStyleVariant::kFirstLine);
   container_builder_.SetBaseDirection(line_info->BaseDirection());
-  container_builder_.AddChildren(line_box_);
   container_builder_.SetInlineSize(inline_size);
   container_builder_.SetMetrics(line_box_metrics);
   container_builder_.SetBfcBlockOffset(line_info->BfcOffset().block_offset);
@@ -957,6 +955,22 @@
 
   CHECK(is_line_created);
   container_builder_.SetExclusionSpace(std::move(exclusion_space));
+
+  if (NGFragmentItemsBuilder* items_builder = context_->ItemsBuilder()) {
+    DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
+    container_builder_.AddOutOfFlowChildren(line_box_);
+    scoped_refptr<const NGLayoutResult> layout_result =
+        container_builder_.ToLineBoxFragment();
+    if (items_builder->TextContent(false).IsNull())
+      items_builder->SetTextContent(Node());
+    items_builder->AddLine(
+        To<NGPhysicalLineBoxFragment>(layout_result->PhysicalFragment()),
+        line_box_);
+    return layout_result;
+  }
+
+  DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
+  container_builder_.AddChildren(line_box_);
   container_builder_.MoveOutOfFlowDescendantCandidatesToDescendants();
   return container_builder_.ToLineBoxFragment();
 }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
index 55cb128a..206b6163 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
@@ -108,6 +108,20 @@
   }
 }
 
+void NGLineBoxFragmentBuilder::AddOutOfFlowChildren(ChildList& children) {
+  for (auto& child : children) {
+    if (child.out_of_flow_positioned_box) {
+      AddOutOfFlowChildCandidate(
+          NGBlockNode(ToLayoutBox(child.out_of_flow_positioned_box)),
+          child.offset, child.container_direction);
+      child.out_of_flow_positioned_box = nullptr;
+    }
+  }
+
+  DCHECK(oof_positioned_descendants_.IsEmpty());
+  MoveOutOfFlowDescendantCandidatesToDescendants();
+}
+
 scoped_refptr<const NGLayoutResult>
 NGLineBoxFragmentBuilder::ToLineBoxFragment() {
   writing_mode_ = ToLineWritingMode(writing_mode_);
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
index 7142082a..07ce843 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
@@ -235,6 +235,11 @@
   // Add all items in ChildList. Skips null Child if any.
   void AddChildren(ChildList&);
 
+  // Add only out-of-flow items in ChildList. TODO(kojii): When |NGFragmentItem|
+  // is on, all objects should go to |NGFragmentItems| but OOF still uses
+  // fragments to propagate while in transition.
+  void AddOutOfFlowChildren(ChildList&);
+
   // Creates the fragment. Can only be called once.
   scoped_refptr<const NGLayoutResult> ToLineBoxFragment();
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index 3e6824ca..71c17da 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -83,11 +83,14 @@
           builder->BoxType()),
       baselines_(builder->baselines_) {
   DCHECK(GetLayoutObject() && GetLayoutObject()->IsBoxModelObject());
-  has_fragment_items_ = !!builder->ItemsBuilder();
-  if (has_fragment_items_) {
+  if (NGFragmentItemsBuilder* items_builder = builder->ItemsBuilder()) {
+    has_fragment_items_ = true;
     NGFragmentItems* items =
         const_cast<NGFragmentItems*>(ComputeItemsAddress());
-    builder->ItemsBuilder()->ToFragmentItems(items);
+    items_builder->ToFragmentItems(block_or_line_writing_mode,
+                                   builder->Direction(), Size(), items);
+  } else {
+    has_fragment_items_ = false;
   }
   has_borders_ = !borders.IsZero();
   if (has_borders_)
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
index 5ab1d63..6cea11d1 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
@@ -28,6 +28,8 @@
   scoped_refptr<const NGLayoutResult> CloneAsHiddenForPaint() const;
 
   ~NGPhysicalBoxFragment() {
+    if (has_fragment_items_)
+      ComputeItemsAddress()->~NGFragmentItems();
     for (const NGLink& child : Children())
       child.fragment->Release();
   }
diff --git a/third_party/blink/renderer/core/loader/link_loader_test.cc b/third_party/blink/renderer/core/loader/link_loader_test.cc
index a24e127..040a580 100644
--- a/third_party/blink/renderer/core/loader/link_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/link_loader_test.cc
@@ -10,7 +10,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_prescient_networking.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
@@ -126,8 +125,7 @@
   }
 
   ~LinkLoaderPreloadTestBase() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
+    platform_->GetURLLoaderMockFactory()
         ->UnregisterAllURLsAndClearMemoryCache();
   }
 
@@ -163,6 +161,7 @@
     }
   }
   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
+  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
 };
 
 struct PreloadTestParams {
@@ -670,8 +669,7 @@
                   resource->GetResourceRequest().GetReferrerPolicy());
       }
     }
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
+    platform_->GetURLLoaderMockFactory()
         ->UnregisterAllURLsAndClearMemoryCache();
   }
 }
diff --git a/third_party/blink/renderer/core/loader/ping_loader_test.cc b/third_party/blink/renderer/core/loader/ping_loader_test.cc
index f519b4d..e21c97c 100644
--- a/third_party/blink/renderer/core/loader/ping_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/ping_loader_test.cc
@@ -5,13 +5,13 @@
 #include "third_party/blink/renderer/core/loader/ping_loader.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/loader/empty_clients.h"
 #include "third_party/blink/renderer/core/loader/frame_loader.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
@@ -45,8 +45,7 @@
   }
 
   void TearDown() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
+    platform_->GetURLLoaderMockFactory()
         ->UnregisterAllURLsAndClearMemoryCache();
   }
 
@@ -70,12 +69,13 @@
     }
     // Serve the ping request, since it will otherwise bleed in to the next
     // test, and once begun there is no way to cancel it directly.
-    Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+    platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
     return ping_request;
   }
 
  protected:
   Persistent<PingLocalFrameClient> client_;
+  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
 };
 
 TEST_F(PingLoaderTest, HTTPSToHTTPS) {
@@ -113,7 +113,7 @@
   url_test_helpers::RegisterMockedURLLoad(
       ping_url, test::CoreTestDataPath("bar.html"), "text/html");
   PingLoader::SendLinkAuditPing(&GetFrame(), ping_url, destination_url);
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
   const ResourceRequest& request = client_->PingRequest();
   ASSERT_FALSE(request.IsNull());
   ASSERT_EQ(request.Url(), ping_url);
@@ -128,7 +128,7 @@
       ping_url, test::CoreTestDataPath("bar.html"), "text/html");
   PingLoader::SendViolationReport(&GetFrame(), ping_url,
                                   EncodedFormData::Create());
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
   const ResourceRequest& request = client_->PingRequest();
   ASSERT_FALSE(request.IsNull());
   ASSERT_EQ(request.Url(), ping_url);
@@ -142,7 +142,7 @@
   url_test_helpers::RegisterMockedURLLoad(
       ping_url, test::CoreTestDataPath("bar.html"), "text/html");
   PingLoader::SendBeacon(&GetFrame(), ping_url, "hello");
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
   const ResourceRequest& request = client_->PingRequest();
   ASSERT_FALSE(request.IsNull());
   ASSERT_EQ(request.Url(), ping_url);
diff --git a/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc b/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc
index bdff8d67..342c45c 100644
--- a/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc
+++ b/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/public/web/web_frame.h"
@@ -21,6 +20,7 @@
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
 
@@ -31,8 +31,7 @@
   ProgrammaticScrollTest() : base_url_("http://www.test.com/") {}
 
   void TearDown() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
+    platform_->GetURLLoaderMockFactory()
         ->UnregisterAllURLsAndClearMemoryCache();
   }
 
@@ -43,6 +42,7 @@
   }
 
   String base_url_;
+  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
 };
 
 TEST_F(ProgrammaticScrollTest, RestoreScrollPositionAndViewStateWithScale) {
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
index 887b0669..376c8a93 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
@@ -8,7 +8,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/renderer/core/css/css_font_face_src_value.h"
 #include "third_party/blink/renderer/core/loader/resource/mock_font_resource_client.h"
@@ -26,6 +25,7 @@
 #include "third_party/blink/renderer/platform/loader/testing/test_loader_factory.h"
 #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 
 namespace blink {
@@ -33,10 +33,11 @@
 class FontResourceTest : public testing::Test {
  public:
   void TearDown() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
+    platform_->GetURLLoaderMockFactory()
         ->UnregisterAllURLsAndClearMemoryCache();
   }
+
+  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
 };
 
 class CacheAwareFontResourceTest : public FontResourceTest {
@@ -59,7 +60,7 @@
   ResourceResponse response(url);
   response.SetHttpStatusCode(200);
   response.SetHttpHeaderField(http_names::kETag, "1234567890");
-  Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
+  platform_->GetURLLoaderMockFactory()->RegisterURL(
       url, WrappedResourceResponse(response), "");
 
   MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
@@ -75,7 +76,7 @@
   Resource* resource1 = FontResource::Fetch(fetch_params1, fetcher, nullptr);
   ASSERT_FALSE(resource1->ErrorOccurred());
   fetcher->StartLoad(resource1);
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
   EXPECT_TRUE(resource1->IsLoaded());
   EXPECT_FALSE(resource1->ErrorOccurred());
 
@@ -105,7 +106,7 @@
   // StartLoad() can be called from any initiator. Here, call it from the
   // latter.
   fetcher->StartLoad(resource3);
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
   EXPECT_TRUE(resource3->IsLoaded());
   EXPECT_FALSE(resource3->ErrorOccurred());
   EXPECT_TRUE(resource2->IsLoaded());
@@ -119,7 +120,7 @@
   KURL url("http://127.0.0.1:8000/font.woff");
   ResourceResponse response(url);
   response.SetHttpStatusCode(200);
-  Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
+  platform_->GetURLLoaderMockFactory()->RegisterURL(
       url, WrappedResourceResponse(response), "");
 
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
@@ -177,7 +178,7 @@
   EXPECT_TRUE(client3->FontLoadShortLimitExceededCalled());
   EXPECT_TRUE(client3->FontLoadLongLimitExceededCalled());
 
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
   GetMemoryCache()->Remove(&resource);
 }
 
diff --git a/third_party/blink/renderer/core/loader/threadable_loader_test.cc b/third_party/blink/renderer/core/loader/threadable_loader_test.cc
index 5d8fe7e..c6f3b97 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader_test.cc
@@ -10,7 +10,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_url_load_timing.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
@@ -31,6 +30,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
 #include "third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
@@ -91,16 +91,6 @@
   return KURL("http://example.com/loop").Copy();
 }
 
-void ServeAsynchronousRequests() {
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-}
-
-void UnregisterAllURLsAndClearMemoryCache() {
-  Platform::Current()
-      ->GetURLLoaderMockFactory()
-      ->UnregisterAllURLsAndClearMemoryCache();
-}
-
 void SetUpSuccessURL() {
   url_test_helpers::RegisterMockedURLLoad(
       SuccessURL(), test::CoreTestDataPath(kFileName), "text/html");
@@ -186,14 +176,17 @@
 
   void OnSetUp() { SetUpMockURLs(); }
 
-  void OnServeRequests() { ServeAsynchronousRequests(); }
+  void OnServeRequests() {
+    platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+  }
 
   void OnTearDown() {
     if (loader_) {
       loader_->Cancel();
       loader_ = nullptr;
     }
-    UnregisterAllURLsAndClearMemoryCache();
+    platform_->GetURLLoaderMockFactory()
+        ->UnregisterAllURLsAndClearMemoryCache();
   }
 
  private:
@@ -202,6 +195,7 @@
   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
   Checkpoint checkpoint_;
   Persistent<ThreadableLoader> loader_;
+  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
 };
 
 class ThreadableLoaderTest : public testing::Test {
diff --git a/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc b/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
index d78c20c..98b9f8f 100644
--- a/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
@@ -51,9 +51,7 @@
   }
 
   ~MainThreadScrollingReasonsTest() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   void NavigateTo(const String& url) {
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
index 72d0e7b0..07e310b 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/public/web/web_console_message.h"
@@ -64,9 +63,7 @@
 
   ~RootScrollerTest() override {
     features_backup_.Restore();
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   void SetAndSelectRootScroller(Document& document, Element* element) {
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc
index 866e423..1090575 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc
@@ -28,7 +28,6 @@
 #include "cc/layers/layer_sticky_position_constraint.h"
 #include "cc/layers/picture_layer.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_cache.h"
 #include "third_party/blink/public/platform/web_layer_tree_view.h"
 #include "third_party/blink/public/platform/web_rect.h"
@@ -110,9 +109,7 @@
   }
 
   ~ScrollingCoordinatorTest() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
   void NavigateTo(const std::string& url) {
diff --git a/third_party/blink/renderer/core/page/viewport_test.cc b/third_party/blink/renderer/core/page/viewport_test.cc
index 9d49455e..d759093 100644
--- a/third_party/blink/renderer/core/page/viewport_test.cc
+++ b/third_party/blink/renderer/core/page/viewport_test.cc
@@ -29,7 +29,6 @@
  */
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/public/web/web_console_message.h"
 #include "third_party/blink/public/web/web_frame.h"
@@ -53,6 +52,7 @@
 #include "third_party/blink/renderer/platform/geometry/int_size.h"
 #include "third_party/blink/renderer/platform/geometry/length.h"
 #include "third_party/blink/renderer/platform/testing/histogram_tester.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
 
@@ -64,8 +64,7 @@
       : base_url_("http://www.test.com/"), chrome_url_("chrome://") {}
 
   ~ViewportTest() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
+    platform_->GetURLLoaderMockFactory()
         ->UnregisterAllURLsAndClearMemoryCache();
   }
 
@@ -88,6 +87,7 @@
 
   std::string base_url_;
   std::string chrome_url_;
+  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
 };
 
 static void SetViewportSettings(WebSettings* settings) {
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc b/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
index e4b7e0b8..67eb7d5 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
@@ -30,7 +30,6 @@
 #include "cc/layers/picture_layer.h"
 #include "cc/trees/layer_tree_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_float_point.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/public/platform/web_size.h"
@@ -83,9 +82,7 @@
   }
 
   void TearDown() override {
-    Platform::Current()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
+    url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
 
     // Ensure we fully clean up while scoped settings are enabled. Without this,
     // garbage collection would occur after ScopedBlinkGenPropertyTreesForTest
diff --git a/third_party/blink/renderer/core/testing/sim/sim_network.cc b/third_party/blink/renderer/core/testing/sim/sim_network.cc
index 637f3e9..2004f9f9 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_network.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_network.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 #include <utility>
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_error.h"
 #include "third_party/blink/public/platform/web_url_loader.h"
 #include "third_party/blink/public/platform/web_url_loader_client.h"
@@ -16,22 +15,21 @@
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/platform/loader/static_data_navigation_body_loader.h"
+#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
 
 namespace blink {
 
 static SimNetwork* g_network = nullptr;
 
 SimNetwork::SimNetwork() : current_request_(nullptr) {
-  Platform::Current()->GetURLLoaderMockFactory()->SetLoaderDelegate(this);
+  url_test_helpers::SetLoaderDelegate(this);
   DCHECK(!g_network);
   g_network = this;
 }
 
 SimNetwork::~SimNetwork() {
-  Platform::Current()->GetURLLoaderMockFactory()->SetLoaderDelegate(nullptr);
-  Platform::Current()
-      ->GetURLLoaderMockFactory()
-      ->UnregisterAllURLsAndClearMemoryCache();
+  url_test_helpers::SetLoaderDelegate(nullptr);
+  url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   g_network = nullptr;
 }
 
@@ -41,7 +39,7 @@
 }
 
 void SimNetwork::ServePendingRequests() {
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+  url_test_helpers::ServeAsynchronousRequests();
 }
 
 void SimNetwork::DidReceiveResponse(WebURLLoaderClient* client,
@@ -106,13 +104,13 @@
   for (const auto& http_header : request.response_http_headers_)
     response.AddHttpHeaderField(http_header.key, http_header.value);
 
-  Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(request.url_,
-                                                              response, "");
+  url_test_helpers::RegisterMockedURLLoadWithCustomResponse(request.url_, "",
+                                                            response);
 }
 
 void SimNetwork::RemoveRequest(SimRequestBase& request) {
   requests_.erase(request.url_);
-  Platform::Current()->GetURLLoaderMockFactory()->UnregisterURL(request.url_);
+  url_test_helpers::RegisterMockedURLUnregister(request.url_);
 }
 
 bool SimNetwork::FillNavigationParamsResponse(WebNavigationParams* params) {
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.cc b/third_party/blink/renderer/core/workers/dedicated_worker.cc
index 2685064..ef9a68b 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.cc
@@ -328,11 +328,18 @@
 }
 
 void DedicatedWorker::OnWorkerHostCreated(
-    mojo::ScopedMessagePipeHandle interface_provider) {
+    mojo::ScopedMessagePipeHandle interface_provider,
+    mojo::ScopedMessagePipeHandle browser_interface_broker) {
   DCHECK(!interface_provider_);
   interface_provider_ = service_manager::mojom::blink::InterfaceProviderPtrInfo(
       std::move(interface_provider),
       service_manager::mojom::blink::InterfaceProvider::Version_);
+
+  DCHECK(!browser_interface_broker_);
+  browser_interface_broker_ =
+      mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>(
+          std::move(browser_interface_broker),
+          mojom::blink::BrowserInterfaceBroker::Version_);
 }
 
 void DedicatedWorker::OnScriptLoadStarted() {
@@ -466,7 +473,8 @@
       OriginTrialContext::GetTokens(GetExecutionContext()).get(),
       parent_devtools_token, std::move(settings), kV8CacheOptionsDefault,
       nullptr /* worklet_module_responses_map */,
-      std::move(interface_provider_), CreateBeginFrameProviderParams(),
+      std::move(interface_provider_), std::move(browser_interface_broker_),
+      CreateBeginFrameProviderParams(),
       GetExecutionContext()->GetSecurityContext().GetFeaturePolicy(),
       GetExecutionContext()->GetAgentClusterID());
 }
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.h b/third_party/blink/renderer/core/workers/dedicated_worker.h
index 8815366f..d08b4e7 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/mojom/browser_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/platform/web_dedicated_worker.h"
 #include "third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
@@ -107,7 +108,8 @@
   // Implements WebDedicatedWorker.
   // Called only when PlzDedicatedWorker is enabled.
   void OnWorkerHostCreated(
-      mojo::ScopedMessagePipeHandle interface_provider) override;
+      mojo::ScopedMessagePipeHandle interface_provider,
+      mojo::ScopedMessagePipeHandle browser_interface_broker) override;
   void OnScriptLoadStarted() override;
   void OnScriptLoadStartFailed() override;
 
@@ -160,6 +162,9 @@
 
   service_manager::mojom::blink::InterfaceProviderPtrInfo interface_provider_;
 
+  mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>
+      browser_interface_broker_;
+
   // Whether the worker is frozen due to a call from this context.
   bool requested_frozen_ = false;
 };
diff --git a/third_party/blink/renderer/core/workers/global_scope_creation_params.cc b/third_party/blink/renderer/core/workers/global_scope_creation_params.cc
index 0165bcb..27cabe55 100644
--- a/third_party/blink/renderer/core/workers/global_scope_creation_params.cc
+++ b/third_party/blink/renderer/core/workers/global_scope_creation_params.cc
@@ -34,6 +34,8 @@
     WorkletModuleResponsesMap* module_responses_map,
     service_manager::mojom::blink::InterfaceProviderPtrInfo
         interface_provider_info,
+    mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>
+        browser_interface_broker,
     BeginFrameProviderParams begin_frame_provider_params,
     const FeaturePolicy* parent_feature_policy,
     base::UnguessableToken agent_cluster_id)
@@ -55,6 +57,7 @@
       v8_cache_options(v8_cache_options),
       module_responses_map(module_responses_map),
       interface_provider(std::move(interface_provider_info)),
+      browser_interface_broker(std::move(browser_interface_broker)),
       begin_frame_provider_params(std::move(begin_frame_provider_params)),
       // At the moment, workers do not support their container policy being set,
       // so it will just be an empty ParsedFeaturePolicy for now.
diff --git a/third_party/blink/renderer/core/workers/global_scope_creation_params.h b/third_party/blink/renderer/core/workers/global_scope_creation_params.h
index 50220cb7..b77a367 100644
--- a/third_party/blink/renderer/core/workers/global_scope_creation_params.h
+++ b/third_party/blink/renderer/core/workers/global_scope_creation_params.h
@@ -13,6 +13,7 @@
 #include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom-blink.h"
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
+#include "third_party/blink/public/mojom/browser_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-blink.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "third_party/blink/public/platform/web_worker_fetch_context.h"
@@ -62,6 +63,8 @@
       V8CacheOptions,
       WorkletModuleResponsesMap*,
       service_manager::mojom::blink::InterfaceProviderPtrInfo = {},
+      mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>
+          browser_interface_broker = mojo::NullRemote(),
       BeginFrameProviderParams begin_frame_provider_params = {},
       const FeaturePolicy* parent_feature_policy = nullptr,
       base::UnguessableToken agent_cluster_id = {});
@@ -151,6 +154,9 @@
 
   service_manager::mojom::blink::InterfaceProviderPtrInfo interface_provider;
 
+  mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>
+      browser_interface_broker;
+
   BeginFrameProviderParams begin_frame_provider_params;
 
   std::unique_ptr<FeaturePolicy> worker_feature_policy;
diff --git a/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc b/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc
index 9496b123..97100de 100644
--- a/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc
+++ b/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc
@@ -70,8 +70,8 @@
           std::make_unique<WorkerSettings>(document->GetSettings()),
           kV8CacheOptionsDefault, module_responses_map,
           service_manager::mojom::blink::InterfaceProviderPtrInfo(),
-          BeginFrameProviderParams(), nullptr /* parent_feature_policy */,
-          document->GetAgentClusterID());
+          mojo::NullRemote(), BeginFrameProviderParams(),
+          nullptr /* parent_feature_policy */, document->GetAgentClusterID());
 
   // Worklets share the pre-initialized backing thread so that we don't have to
   // specify the backing thread startup data.
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index 42fdc3c..c9a8441 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -360,6 +360,11 @@
   return &interface_provider_;
 }
 
+const BrowserInterfaceBrokerProxy*
+WorkerGlobalScope::GetBrowserInterfaceBrokerProxy() const {
+  return &browser_interface_broker_proxy_;
+}
+
 ExecutionContext* WorkerGlobalScope::GetExecutionContext() const {
   return const_cast<WorkerGlobalScope*>(this);
 }
@@ -512,6 +517,13 @@
             service_manager::mojom::InterfaceProvider::Version_)));
   }
 
+  if (creation_params->browser_interface_broker.is_valid()) {
+    auto pipe = creation_params->browser_interface_broker.PassPipe();
+    browser_interface_broker_proxy_.Bind(
+        mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>(
+            std::move(pipe), blink::mojom::BrowserInterfaceBroker::Version_));
+  }
+
   // A FeaturePolicy is created by FeaturePolicy::CreateFromParentPolicy, even
   // if the parent policy is null.
   DCHECK(creation_params->worker_feature_policy);
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.h b/third_party/blink/renderer/core/workers/worker_global_scope.h
index d102fc92..98496ef 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.h
@@ -32,6 +32,7 @@
 #include "services/network/public/mojom/ip_address_space.mojom-blink.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom-blink.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/core_export.h"
@@ -128,6 +129,8 @@
   void AddConsoleMessageImpl(ConsoleMessage*, bool discard_duplicates) final;
   bool IsSecureContext(String& error_message) const override;
   service_manager::InterfaceProvider* GetInterfaceProvider() final;
+  const BrowserInterfaceBrokerProxy* GetBrowserInterfaceBrokerProxy()
+      const final;
 
   OffscreenFontSelector* GetFontSelector() { return font_selector_; }
 
@@ -267,6 +270,8 @@
 
   service_manager::InterfaceProvider interface_provider_;
 
+  blink::BrowserInterfaceBrokerProxy browser_interface_broker_proxy_;
+
   // State transition about worker-toplevel script evaluation.
   enum class ScriptEvalState {
     // Initial state: ReadyToRunWorkerScript() is not yet called.
diff --git a/third_party/blink/renderer/devtools/front_end/audits/AuditsReportRenderer.js b/third_party/blink/renderer/devtools/front_end/audits/AuditsReportRenderer.js
index bde4e07..44da600 100644
--- a/third_party/blink/renderer/devtools/front_end/audits/AuditsReportRenderer.js
+++ b/third_party/blink/renderer/devtools/front_end/audits/AuditsReportRenderer.js
@@ -14,11 +14,15 @@
     if (!artifacts || !artifacts.traces || !artifacts.traces.defaultPass)
       return;
 
+    const container = el.querySelector('.lh-audit-group');
+    const columnsEl = container.querySelector('.lh-columns');
+    // There will be no columns if just the PWA category.
+    if (!columnsEl)
+      return;
+
     const defaultPassTrace = artifacts.traces.defaultPass;
     const timelineButton = UI.createTextButton(Common.UIString('View Trace'), onViewTraceClick, 'view-trace');
-    const container = el.querySelector('.lh-audit-group');
-    container.insertBefore(timelineButton, container.querySelector('.lh-columns').nextSibling);
-    return el;
+    container.insertBefore(timelineButton, columnsEl.nextSibling);
 
     async function onViewTraceClick() {
       Host.userMetrics.actionTaken(Host.UserMetrics.Action.AuditsViewTrace);
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js b/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js
index eb2295a..07c25a8 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js
@@ -224,26 +224,6 @@
     ]
   },
   {
-    title: 'Edge',
-    values: [
-      {
-        title: 'Edge \u2014 Windows',
-        value:
-            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240'
-      },
-      {
-        title: 'Edge \u2014 Mobile',
-        value:
-            'Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 640 XL LTE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Mobile Safari/537.36 Edge/12.10166'
-      },
-      {
-        title: 'Edge \u2014 XBox',
-        value:
-            'Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/13.10586'
-      }
-    ]
-  },
-  {
     title: 'Firefox',
     values: [
       {
@@ -295,6 +275,51 @@
     ]
   },
   {
+    title: 'Microsoft Edge',
+    values: [
+      {
+        title: 'Microsoft Edge (Chromium) \u2014 Windows',
+        value:
+            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36 Edg/%s'
+      },
+      {
+        title: 'Microsoft Edge (Chromium) \u2014 Mac',
+        value:
+            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36 Edg/%s'
+      },
+      {
+        title: 'Microsoft Edge \u2014 iPhone',
+        value:
+            'Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 EdgiOS/44.5.0.10 Mobile/15E148 Safari/604.1'
+      },
+      {
+        title: 'Microsoft Edge \u2014 iPad',
+        value:
+            'Mozilla/5.0 (iPad; CPU OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 EdgiOS/44.5.2 Mobile/15E148 Safari/605.1.15'
+      },
+      {
+        title: 'Microsoft Edge \u2014 Android Mobile',
+        value:
+            'Mozilla/5.0 (Linux; Android 8.1.0; Pixel Build/OPM4.171019.021.D1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Mobile Safari/537.36 EdgA/42.0.0.2057'
+      },
+      {
+        title: 'Microsoft Edge \u2014 Android Tablet',
+        value:
+            'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Safari/537.36 EdgA/42.0.0.2057'
+      },
+      {
+        title: 'Microsoft Edge (EdgeHTML) \u2014 Windows',
+        value:
+            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362'
+      },
+      {
+        title: 'Microsoft Edge (EdgeHTML) \u2014 XBox',
+        value:
+            'Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362'
+      }
+    ]
+  },
+  {
     title: 'Opera',
     values: [
       {
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js b/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
index 25f0c827..9fdad45 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
@@ -952,10 +952,15 @@
    */
   static patchUserAgentWithChromeVersion(uaString) {
     // Patches Chrome/CriOS version from user agent ("1.2.3.4" when user agent is: "Chrome/1.2.3.4").
+    // Edge also contains an appVersion which should be patched to match the Chrome major version.
+    // Otherwise, ignore it. This assumes additional appVersions appear after the Chrome version.
     const chromeRegex = new RegExp('(?:^|\\W)Chrome/(\\S+)');
     const chromeMatch = navigator.userAgent.match(chromeRegex);
-    if (chromeMatch && chromeMatch.length > 1)
-      return String.sprintf(uaString, chromeMatch[1]);
+    if (chromeMatch && chromeMatch.length > 1) {
+      // "1.2.3.4" becomes "1.0.100.0"
+      const additionalAppVersion = chromeMatch[1].split('.', 1)[0] + '.0.100.0';
+      return String.sprintf(uaString, chromeMatch[1], additionalAppVersion);
+    }
     return uaString;
   }
 
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 7270769..e640d07 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -147,6 +147,7 @@
     "//third_party/blink/renderer/modules/push_messaging",
     "//third_party/blink/renderer/modules/quota",
     "//third_party/blink/renderer/modules/remoteplayback",
+    "//third_party/blink/renderer/modules/scheduler",
     "//third_party/blink/renderer/modules/screen_orientation",
     "//third_party/blink/renderer/modules/sensor",
     "//third_party/blink/renderer/modules/service_worker",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
index 0fd8fa7..16e8fbc 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
@@ -50,6 +50,17 @@
   inline_text_box_ = nullptr;
 }
 
+bool AXInlineTextBox::IsLineBreakingObject() const {
+  if (IsDetached())
+    return AXObject::IsLineBreakingObject();
+
+  // If this object is a forced line break, or the parent is a <br>
+  // element, then this object is line breaking.
+  const AXObject* parent = ParentObject();
+  return inline_text_box_->IsLineBreak() ||
+         (parent && parent->RoleValue() == ax::mojom::Role::kLineBreak);
+}
+
 void AXInlineTextBox::GetRelativeBounds(AXObject** out_container,
                                         FloatRect& out_bounds_in_container,
                                         SkMatrix44& out_container_transform,
diff --git a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h
index cae9182..648986ef 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h
@@ -48,6 +48,8 @@
   bool IsDetached() const override { return !inline_text_box_; }
   bool IsAXInlineTextBox() const override { return true; }
 
+  bool IsLineBreakingObject() const override;
+
  public:
   ax::mojom::Role RoleValue() const override {
     return ax::mojom::Role::kInlineTextBox;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
index 73fa619b..5ac051f8 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
@@ -332,5 +332,56 @@
   EXPECT_EQ("b", next->GetNode()->textContent());
 }
 
+TEST_F(AccessibilityTest, AxObjectPreservedWhitespaceIsLineBreakingObjects) {
+  SetBodyInnerHTML(R"HTML(
+    <span style='white-space: pre-line' id="preserved">
+      First Paragraph
+      Second Paragraph
+      Third Paragraph
+    </span>)HTML");
+
+  const AXObject* root = GetAXRootObject();
+  ASSERT_NE(nullptr, root);
+
+  const AXObject* preserved_span = GetAXObjectByElementId("preserved");
+  ASSERT_NE(nullptr, preserved_span);
+  ASSERT_EQ(ax::mojom::Role::kGenericContainer, preserved_span->RoleValue());
+  ASSERT_EQ(1, preserved_span->ChildCount());
+  EXPECT_FALSE(preserved_span->IsLineBreakingObject());
+
+  AXObject* preserved_text = preserved_span->FirstChild();
+  ASSERT_NE(nullptr, preserved_text);
+  ASSERT_EQ(ax::mojom::Role::kStaticText, preserved_text->RoleValue());
+  EXPECT_FALSE(preserved_text->IsLineBreakingObject());
+
+  // Expect 7 kInlineTextBox children
+  // 3 lines of text, and 4 newlines
+  preserved_text->LoadInlineTextBoxes();
+  ASSERT_EQ(7, preserved_text->ChildCount());
+  bool all_children_are_inline_text_boxes = true;
+  for (const AXObject* child : preserved_text->Children()) {
+    if (child->RoleValue() != ax::mojom::Role::kInlineTextBox) {
+      all_children_are_inline_text_boxes = false;
+      break;
+    }
+  }
+  ASSERT_TRUE(all_children_are_inline_text_boxes);
+
+  ASSERT_EQ(preserved_text->Children()[0]->ComputedName(), "\n");
+  EXPECT_TRUE(preserved_text->Children()[0]->IsLineBreakingObject());
+  ASSERT_EQ(preserved_text->Children()[1]->ComputedName(), "First Paragraph");
+  EXPECT_FALSE(preserved_text->Children()[1]->IsLineBreakingObject());
+  ASSERT_EQ(preserved_text->Children()[2]->ComputedName(), "\n");
+  EXPECT_TRUE(preserved_text->Children()[2]->IsLineBreakingObject());
+  ASSERT_EQ(preserved_text->Children()[3]->ComputedName(), "Second Paragraph");
+  EXPECT_FALSE(preserved_text->Children()[3]->IsLineBreakingObject());
+  ASSERT_EQ(preserved_text->Children()[4]->ComputedName(), "\n");
+  EXPECT_TRUE(preserved_text->Children()[4]->IsLineBreakingObject());
+  ASSERT_EQ(preserved_text->Children()[5]->ComputedName(), "Third Paragraph");
+  EXPECT_FALSE(preserved_text->Children()[5]->IsLineBreakingObject());
+  ASSERT_EQ(preserved_text->Children()[6]->ComputedName(), "\n");
+  EXPECT_TRUE(preserved_text->Children()[6]->IsLineBreakingObject());
+}
+
 }  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/app_banner/app_banner_controller.cc b/third_party/blink/renderer/modules/app_banner/app_banner_controller.cc
index e00b3d0..2e74f9c 100644
--- a/third_party/blink/renderer/modules/app_banner/app_banner_controller.cc
+++ b/third_party/blink/renderer/modules/app_banner/app_banner_controller.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 #include <utility>
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/event_type_names.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
@@ -19,18 +19,18 @@
 
 void AppBannerController::BindMojoRequest(
     LocalFrame* frame,
-    mojom::blink::AppBannerControllerRequest request) {
+    mojo::PendingReceiver<mojom::blink::AppBannerController> receiver) {
   DCHECK(frame);
 
   // See https://bit.ly/2S0zRAS for task types.
-  mojo::MakeStrongBinding(std::make_unique<AppBannerController>(*frame),
-                          std::move(request),
-                          frame->GetTaskRunner(TaskType::kMiscPlatformAPI));
+  mojo::MakeSelfOwnedReceiver(std::make_unique<AppBannerController>(*frame),
+                              std::move(receiver),
+                              frame->GetTaskRunner(TaskType::kMiscPlatformAPI));
 }
 
 void AppBannerController::BannerPromptRequest(
-    mojom::blink::AppBannerServicePtr service_ptr,
-    mojom::blink::AppBannerEventRequest event_request,
+    mojo::PendingRemote<mojom::blink::AppBannerService> service_remote,
+    mojo::PendingReceiver<mojom::blink::AppBannerEvent> event_receiver,
     const Vector<String>& platforms,
     BannerPromptRequestCallback callback) {
   // TODO(hajimehoshi): Add tests for the case the frame is detached.
@@ -42,7 +42,7 @@
   mojom::AppBannerPromptReply reply =
       frame_->DomWindow()->DispatchEvent(*BeforeInstallPromptEvent::Create(
           event_type_names::kBeforeinstallprompt, *frame_,
-          std::move(service_ptr), std::move(event_request), platforms)) ==
+          std::move(service_remote), std::move(event_receiver), platforms)) ==
               DispatchEventResult::kNotCanceled
           ? mojom::AppBannerPromptReply::NONE
           : mojom::AppBannerPromptReply::CANCEL;
diff --git a/third_party/blink/renderer/modules/app_banner/app_banner_controller.h b/third_party/blink/renderer/modules/app_banner/app_banner_controller.h
index fa40630..babd8bb 100644
--- a/third_party/blink/renderer/modules/app_banner/app_banner_controller.h
+++ b/third_party/blink/renderer/modules/app_banner/app_banner_controller.h
@@ -20,11 +20,12 @@
  public:
   explicit AppBannerController(LocalFrame&);
 
-  static void BindMojoRequest(LocalFrame*,
-                              mojom::blink::AppBannerControllerRequest);
+  static void BindMojoRequest(
+      LocalFrame*,
+      mojo::PendingReceiver<mojom::blink::AppBannerController>);
 
-  void BannerPromptRequest(mojom::blink::AppBannerServicePtr,
-                           mojom::blink::AppBannerEventRequest,
+  void BannerPromptRequest(mojo::PendingRemote<mojom::blink::AppBannerService>,
+                           mojo::PendingReceiver<mojom::blink::AppBannerEvent>,
                            const Vector<String>& platforms,
                            BannerPromptRequestCallback) override;
 
diff --git a/third_party/blink/renderer/modules/app_banner/before_install_prompt_event.cc b/third_party/blink/renderer/modules/app_banner/before_install_prompt_event.cc
index 80652c1..fdec96d 100644
--- a/third_party/blink/renderer/modules/app_banner/before_install_prompt_event.cc
+++ b/third_party/blink/renderer/modules/app_banner/before_install_prompt_event.cc
@@ -16,22 +16,22 @@
 BeforeInstallPromptEvent::BeforeInstallPromptEvent(
     const AtomicString& name,
     LocalFrame& frame,
-    mojom::blink::AppBannerServicePtr service_ptr,
-    mojom::blink::AppBannerEventRequest event_request,
+    mojo::PendingRemote<mojom::blink::AppBannerService> service_remote,
+    mojo::PendingReceiver<mojom::blink::AppBannerEvent> event_receiver,
     const Vector<String>& platforms)
     : Event(name, Bubbles::kNo, Cancelable::kYes),
       ContextClient(&frame),
-      banner_service_(std::move(service_ptr)),
-      binding_(this,
-               std::move(event_request),
-               frame.GetTaskRunner(TaskType::kApplicationLifeCycle)),
+      banner_service_remote_(std::move(service_remote)),
+      receiver_(this,
+                std::move(event_receiver),
+                frame.GetTaskRunner(TaskType::kApplicationLifeCycle)),
       platforms_(platforms),
       user_choice_(MakeGarbageCollected<UserChoiceProperty>(
           frame.GetDocument(),
           this,
           UserChoiceProperty::kUserChoice)) {
-  DCHECK(banner_service_);
-  DCHECK(binding_.is_bound());
+  DCHECK(banner_service_remote_);
+  DCHECK(receiver_.is_bound());
   UseCounter::Count(frame.GetDocument(), WebFeature::kBeforeInstallPromptEvent);
 }
 
@@ -39,7 +39,7 @@
     ExecutionContext* execution_context,
     const AtomicString& name,
     const BeforeInstallPromptEventInit* init)
-    : Event(name, init), ContextClient(execution_context), binding_(this) {
+    : Event(name, init), ContextClient(execution_context) {
   if (init->hasPlatforms())
     platforms_ = init->platforms();
 }
@@ -47,8 +47,8 @@
 BeforeInstallPromptEvent::~BeforeInstallPromptEvent() = default;
 
 void BeforeInstallPromptEvent::Dispose() {
-  banner_service_.reset();
-  binding_.Close();
+  banner_service_remote_.reset();
+  receiver_.reset();
 }
 
 Vector<String> BeforeInstallPromptEvent::platforms() const {
@@ -60,7 +60,7 @@
                     WebFeature::kBeforeInstallPromptEventUserChoice);
   // |m_binding| must be bound to allow the AppBannerService to resolve the
   // userChoice promise.
-  if (user_choice_ && binding_.is_bound())
+  if (user_choice_ && receiver_.is_bound())
     return user_choice_->Promise(script_state->World());
   return ScriptPromise::RejectWithDOMException(
       script_state, MakeGarbageCollected<DOMException>(
@@ -71,7 +71,7 @@
 ScriptPromise BeforeInstallPromptEvent::prompt(ScriptState* script_state) {
   // |m_bannerService| must be bound to allow us to inform the AppBannerService
   // to display the banner now.
-  if (!banner_service_.is_bound()) {
+  if (!banner_service_remote_.is_bound()) {
     return ScriptPromise::RejectWithDOMException(
         script_state, MakeGarbageCollected<DOMException>(
                           DOMExceptionCode::kInvalidStateError,
@@ -91,7 +91,7 @@
   }
 
   UseCounter::Count(context, WebFeature::kBeforeInstallPromptEventPrompt);
-  banner_service_->DisplayAppBanner();
+  banner_service_remote_->DisplayAppBanner();
   return user_choice_->Promise(script_state->World());
 }
 
diff --git a/third_party/blink/renderer/modules/app_banner/before_install_prompt_event.h b/third_party/blink/renderer/modules/app_banner/before_install_prompt_event.h
index 411ad81..80a681f 100644
--- a/third_party/blink/renderer/modules/app_banner/before_install_prompt_event.h
+++ b/third_party/blink/renderer/modules/app_banner/before_install_prompt_event.h
@@ -6,7 +6,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_APP_BANNER_BEFORE_INSTALL_PROMPT_EVENT_H_
 
 #include <utility>
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/app_banner/app_banner.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
@@ -38,8 +39,8 @@
  public:
   BeforeInstallPromptEvent(const AtomicString& name,
                            LocalFrame&,
-                           mojom::blink::AppBannerServicePtr,
-                           mojom::blink::AppBannerEventRequest,
+                           mojo::PendingRemote<mojom::blink::AppBannerService>,
+                           mojo::PendingReceiver<mojom::blink::AppBannerEvent>,
                            const Vector<String>& platforms);
   BeforeInstallPromptEvent(ExecutionContext*,
                            const AtomicString& name,
@@ -49,11 +50,11 @@
   static BeforeInstallPromptEvent* Create(
       const AtomicString& name,
       LocalFrame& frame,
-      mojom::blink::AppBannerServicePtr service_ptr,
-      mojom::blink::AppBannerEventRequest event_request,
+      mojo::PendingRemote<mojom::blink::AppBannerService> service_remote,
+      mojo::PendingReceiver<mojom::blink::AppBannerEvent> event_receiver,
       const Vector<String>& platforms) {
     return MakeGarbageCollected<BeforeInstallPromptEvent>(
-        name, frame, std::move(service_ptr), std::move(event_request),
+        name, frame, std::move(service_remote), std::move(event_receiver),
         platforms);
   }
 
@@ -84,8 +85,8 @@
   void BannerAccepted(const String& platform) override;
   void BannerDismissed() override;
 
-  mojom::blink::AppBannerServicePtr banner_service_;
-  mojo::Binding<mojom::blink::AppBannerEvent> binding_;
+  mojo::Remote<mojom::blink::AppBannerService> banner_service_remote_;
+  mojo::Receiver<mojom::blink::AppBannerEvent> receiver_{this};
   Vector<String> platforms_;
   Member<UserChoiceProperty> user_choice_;
 };
diff --git a/third_party/blink/renderer/modules/credentialmanager/authentication_extensions_client_inputs.idl b/third_party/blink/renderer/modules/credentialmanager/authentication_extensions_client_inputs.idl
index 6c05e22..4249187 100644
--- a/third_party/blink/renderer/modules/credentialmanager/authentication_extensions_client_inputs.idl
+++ b/third_party/blink/renderer/modules/credentialmanager/authentication_extensions_client_inputs.idl
@@ -7,6 +7,8 @@
 dictionary AuthenticationExtensionsClientInputs {
   // https://w3c.github.io/webauthn/#sctn-appid-extension
   USVString appid;
+  // https://w3c.github.io/webauthn/#sctn-appid-exclude-extension
+  USVString appidExclude;
   CableRegistrationData cableRegistration;
   sequence<CableAuthenticationData> cableAuthentication;
   // https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#sctn-hmac-secret-extension
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc
index 564f7a4..e0f087f 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc
@@ -468,6 +468,9 @@
     if (extensions->hasHmacCreateSecret()) {
       mojo_options->hmac_create_secret = extensions->hmacCreateSecret();
     }
+    if (extensions->hasAppidExclude()) {
+      mojo_options->appid_exclude = extensions->appidExclude();
+    }
 #if defined(OS_ANDROID)
     if (extensions->hasUvm()) {
       mojo_options->user_verification_methods = extensions->uvm();
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
index b0bd44a..15c576bb 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -710,6 +710,20 @@
             "legacy FIDO U2F API."));
         return promise;
       }
+      if (options->publicKey()->extensions()->hasAppidExclude()) {
+        const auto& appid_exclude =
+            options->publicKey()->extensions()->appidExclude();
+        if (!appid_exclude.IsEmpty()) {
+          KURL appid_exclude_url(appid_exclude);
+          if (!appid_exclude_url.IsValid()) {
+            resolver->Reject(MakeGarbageCollected<DOMException>(
+                DOMExceptionCode::kSyntaxError,
+                "The `appidExclude` extension value is neither "
+                "empty/null nor a valid URL."));
+            return promise;
+          }
+        }
+      }
       if (options->publicKey()->extensions()->hasCableAuthentication()) {
         resolver->Reject(MakeGarbageCollected<DOMException>(
             DOMExceptionCode::kNotSupportedError,
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_proxy.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_proxy.cc
index 9e51be77..5c432ef 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_proxy.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_proxy.cc
@@ -50,7 +50,8 @@
       base::UnguessableToken::Create(), nullptr /* worker_settings */,
       kV8CacheOptionsDefault, module_responses_map,
       service_manager::mojom::blink::InterfaceProviderPtrInfo(),
-      BeginFrameProviderParams(), nullptr /* parent_feature_policy */,
+      mojo::NullRemote(), BeginFrameProviderParams(),
+      nullptr /* parent_feature_policy */,
       base::UnguessableToken() /* agent_cluster_id */);
   global_scope_ = PaintWorkletGlobalScope::Create(
       frame, std::move(creation_params), *reporting_proxy_);
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
index 7c5abaf..16faff4 100644
--- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
+++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -415,8 +415,9 @@
         std::move(worker_settings),
         static_cast<V8CacheOptions>(worker_start_data_.v8_cache_options),
         nullptr /* worklet_module_respones_map */,
-        std::move(interface_provider_info_), BeginFrameProviderParams(),
-        nullptr /* parent_feature_policy */,
+        std::move(interface_provider_info_),
+        mojo::NullRemote(), /* TODO(crbug.com/985112) pass a real BIB */
+        BeginFrameProviderParams(), nullptr /* parent_feature_policy */,
         base::UnguessableToken() /* agent_cluster_id */);
     source_code = main_script_loader_->SourceText();
     cached_meta_data = main_script_loader_->ReleaseCachedMetadata();
@@ -436,8 +437,9 @@
         std::move(worker_settings),
         static_cast<V8CacheOptions>(worker_start_data_.v8_cache_options),
         nullptr /* worklet_module_respones_map */,
-        std::move(interface_provider_info_), BeginFrameProviderParams(),
-        nullptr /* parent_feature_policy */,
+        std::move(interface_provider_info_),
+        mojo::NullRemote() /* TODO(crbug.com/985112) pass a real BIB */,
+        BeginFrameProviderParams(), nullptr /* parent_feature_policy */,
         base::UnguessableToken() /* agent_cluster_id */);
   }
 
diff --git a/third_party/blink/renderer/modules/filesystem/file_system_dispatcher.cc b/third_party/blink/renderer/modules/filesystem/file_system_dispatcher.cc
index e0e7e99..af00b1c 100644
--- a/third_party/blink/renderer/modules/filesystem/file_system_dispatcher.cc
+++ b/third_party/blink/renderer/modules/filesystem/file_system_dispatcher.cc
@@ -9,6 +9,7 @@
 
 #include "build/build_config.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/platform/file_path_conversion.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
@@ -93,20 +94,21 @@
 FileSystemDispatcher::~FileSystemDispatcher() = default;
 
 mojom::blink::FileSystemManager& FileSystemDispatcher::GetFileSystemManager() {
-  if (!file_system_manager_ptr_) {
+  if (!file_system_manager_) {
     // See https://bit.ly/2S0zRAS for task types
-    mojom::blink::FileSystemManagerRequest request = mojo::MakeRequest(
-        &file_system_manager_ptr_,
-        GetSupplementable()->GetTaskRunner(blink::TaskType::kMiscPlatformAPI));
-    // Document::GetInterfaceProvider() can return null if the frame is
-    // detached.
-    if (GetSupplementable()->GetInterfaceProvider()) {
-      GetSupplementable()->GetInterfaceProvider()->GetInterface(
-          std::move(request));
+    mojo::PendingReceiver<mojom::blink::FileSystemManager> receiver =
+        file_system_manager_.BindNewPipeAndPassReceiver(
+            GetSupplementable()->GetTaskRunner(
+                blink::TaskType::kMiscPlatformAPI));
+    // Document::GetBrowserInterfaceBrokerProxy() can return null if the frame
+    // is detached.
+    if (GetSupplementable()->GetBrowserInterfaceBrokerProxy()) {
+      GetSupplementable()->GetBrowserInterfaceBrokerProxy()->GetInterface(
+          std::move(receiver));
     }
   }
-  DCHECK(file_system_manager_ptr_);
-  return *file_system_manager_ptr_;
+  DCHECK(file_system_manager_);
+  return *file_system_manager_.get();
 }
 
 void FileSystemDispatcher::OpenFileSystem(
diff --git a/third_party/blink/renderer/modules/filesystem/file_system_dispatcher.h b/third_party/blink/renderer/modules/filesystem/file_system_dispatcher.h
index 16a1162..ea012ca5 100644
--- a/third_party/blink/renderer/modules/filesystem/file_system_dispatcher.h
+++ b/third_party/blink/renderer/modules/filesystem/file_system_dispatcher.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/strong_binding_set.h"
 #include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
 #include "third_party/blink/renderer/modules/filesystem/file_system_callbacks.h"
@@ -193,7 +194,7 @@
 
   void RemoveOperationPtr(int operation_id);
 
-  mojom::blink::FileSystemManagerPtr file_system_manager_ptr_;
+  mojo::Remote<mojom::blink::FileSystemManager> file_system_manager_;
   using OperationsMap =
       HashMap<int, mojom::blink::FileSystemCancellableOperationPtr>;
   OperationsMap cancellable_operations_;
diff --git a/third_party/blink/renderer/modules/manifest/manifest_parser.h b/third_party/blink/renderer/modules/manifest/manifest_parser.h
index 0c0cdcc..db468b3 100644
--- a/third_party/blink/renderer/modules/manifest/manifest_parser.h
+++ b/third_party/blink/renderer/modules/manifest/manifest_parser.h
@@ -148,7 +148,7 @@
       const JSONObject* object);
 
   // Parses the name field of a share target file, as defined in:
-  // https://github.com/WICG/web-share-target/blob/master/docs/interface.md
+  // https://wicg.github.io/web-share-target/level-2/#sharetargetfiles-and-its-members
   // Returns the parsed string if any, an empty string if the parsing failed.
   String ParseFileFilterName(const JSONObject* file);
 
@@ -171,26 +171,26 @@
                        Vector<mojom::blink::ManifestFileFilterPtr>* files);
 
   // Parses the method field of a Share Target, as defined in:
-  // https://github.com/WICG/web-share-target/blob/master/docs/interface.md
+  // https://wicg.github.io/web-share-target/#sharetarget-and-its-members
   // Returns an optional share target method enum object.
   base::Optional<mojom::blink::ManifestShareTarget::Method>
   ParseShareTargetMethod(const JSONObject* share_target_dict);
 
   // Parses the enctype field of a Share Target, as defined in:
-  // https://github.com/WICG/web-share-target/blob/master/docs/interface.md
+  // https://wicg.github.io/web-share-target/#sharetarget-and-its-members
   // Returns an optional share target enctype enum object.
   base::Optional<mojom::blink::ManifestShareTarget::Enctype>
   ParseShareTargetEnctype(const JSONObject* share_target_dict);
 
   // Parses the 'params' field of a Share Target, as defined in:
-  // https://wicg.github.io/web-share-target/level-2/#sharetargetparams-and-its-members
+  // https://wicg.github.io/web-share-target/#sharetarget-and-its-members
   // Returns a parsed mojom::blink:ManifestShareTargetParamsPtr, not all fields
   // need to be populated.
   mojom::blink::ManifestShareTargetParamsPtr ParseShareTargetParams(
       const JSONObject* share_target_params);
 
   // Parses the 'share_target' field of a Manifest, as defined in:
-  // https://github.com/WICG/web-share-target/blob/master/docs/interface.md
+  // https://wicg.github.io/web-share-target/#share_target-member
   // Returns the parsed Web Share target. The returned Share Target is null if
   // the field didn't exist, parsing failed, or it was empty.
   base::Optional<mojom::blink::ManifestShareTargetPtr> ParseShareTarget(
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index 78cbef8c3..a67c4868 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -297,6 +297,9 @@
           "quota/dom_error.idl",
           "quota/storage_manager.idl",
           "remoteplayback/remote_playback.idl",
+          "scheduler/scheduler.idl",
+          "scheduler/task.idl",
+          "scheduler/task_queue.idl",
           "screen_orientation/screen_orientation.idl",
           "sensor/absolute_orientation_sensor.idl",
           "sensor/accelerometer.idl",
@@ -762,6 +765,8 @@
           "push_messaging/push_subscription_options_init.idl",
           "quota/storage_estimate.idl",
           "quota/storage_usage_details.idl",
+          "scheduler/scheduler_post_task_options.idl",
+          "scheduler/task_queue_post_task_options.idl",
           "sensor/sensor_error_event_init.idl",
           "sensor/sensor_options.idl",
           "sensor/spatial_sensor_options.idl",
@@ -969,6 +974,7 @@
           "quota/window_quota.idl",
           "quota/worker_navigator_storage_quota.idl",
           "remoteplayback/html_media_element_remote_playback.idl",
+          "scheduler/window_scheduler.idl",
           "screen_orientation/screen_screen_orientation.idl",
           "service_worker/navigator_service_worker.idl",
           "sms/navigator_sms.idl",
diff --git a/third_party/blink/renderer/modules/scheduler/BUILD.gn b/third_party/blink/renderer/modules/scheduler/BUILD.gn
new file mode 100644
index 0000000..e7365ab
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/BUILD.gn
@@ -0,0 +1,18 @@
+# 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/blink/renderer/modules/modules.gni")
+
+blink_modules_sources("scheduler") {
+  sources = [
+    "scheduler.cc",
+    "scheduler.h",
+    "task.cc",
+    "task.h",
+    "task_queue.cc",
+    "task_queue.h",
+    "window_scheduler.cc",
+    "window_scheduler.h",
+  ]
+}
diff --git a/third_party/blink/renderer/modules/scheduler/OWNERS b/third_party/blink/renderer/modules/scheduler/OWNERS
new file mode 100644
index 0000000..b7b0866
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/OWNERS
@@ -0,0 +1,4 @@
+file://third_party/blink/renderer/platform/scheduler/OWNERS
+
+japhet@chromium.org
+shaseley@chromium.org
diff --git a/third_party/blink/renderer/modules/scheduler/README.md b/third_party/blink/renderer/modules/scheduler/README.md
new file mode 100644
index 0000000..bd4ac8cd
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/README.md
@@ -0,0 +1,108 @@
+# `renderer/core/scheduler`
+
+This directory contains the [Main Thread Scheduling
+APIs](https://github.com/WICG/main-thread-scheduling) (`window.scheduler`).
+
+**Note:** These APIs are experimental and the details are still in flux.
+
+## APIs Implemented
+
+`scheduler.postTask()`[[explainer](https://github.com/WICG/main-thread-scheduling/blob/master/PrioritizedPostTask.md), [design doc](https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#heading=h.iw2lczs6xwe6)]
+
+## `scheduler.postTask()`
+
+For the full API shape and general design, please refer to the (active)
+[design doc](https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#heading=h.iw2lczs6xwe6).
+
+### API Overview
+
+The `postTask()` API allows developers to schedule tasks with a native scheduler
+(`window.scheduler`), at a specific priority. The API is based on prioritized
+task queues, which is a common scheduling paradigm.
+
+The scheduler maintains a set of global task queues&mdash;one of each
+priority&mdash;which developers can interact with directly via
+`scheduler.getTaskQueue()` or indirectly through `scheduler.postTask()`.
+
+Tasks are created with `scheduler.postTask(foo)` or `taskQueue.postTask(foo)`.
+Initially, tasks are in a "pending" state, and transition to "running",
+"canceled", or "completed" as illustrated in the following diagram:
+
+![Task Lifecycle](images/task_lifecycle.png)
+
+Tasks can be posted with an optional delay, which has similar behavior to
+`setTimeout()`.
+
+Tasks can be moved between task queues with `taskQueue.task(task)`.
+
+### Code Overview and Blink Scheduler Integration
+
+#### Scheduler
+
+The `Scheduler` is per-document, and observes document lifecycle changes via
+`ContextLifecycleObserver`. When the context is destroyed, the `Scheduler` stops
+running tasks.
+
+The `Scheduler`'s primary function is to maintain TaskQueues. When the
+`Scheduler` is created (when first accessed via `window.scheduler`), it creates
+a set of *global* `TaskQueues`, one of each priority.  Global here means
+document-global, meaning all document script can access these via
+`window.scheduler`. This is opposed to *custom* task queues, which developers
+will be able to create via `new TaskQueue()` (WIP).
+
+
+#### TaskQueues
+
+Each `TaskQueue` wraps a `WebSchedulingTaskQueue`, which is the interface to the
+Blink scheduler.
+
+The `WebSchedulingTaskQueue` is created through the document's `FrameScheduler`,
+and is created with a `WebSchedulingPriority`. The latter is used by the Blink
+scheduler to determine the priority of the underlying
+[base::sequence_manager::TaskQueue](https://cs.chromium.org/chromium/src/base/task/sequence_manager/task_queue.h).
+
+The `TaskQueue` owns the `WebSchedulingTaskQueue`, and its lifetime is tied to
+that of the `Document` associated with the `Scheduler`.
+
+The `WebSchedulingTaskQueue` exposes a `TaskRunner`, which the `TaskQueue` uses
+to post tasks to the Blink scheduler.
+
+
+#### Tasks
+
+A `Task` is a wrapper for a callback, its arguments, its return value (for
+`task.result`), and a
+[`TaskHandle`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h?sq=package:chromium&g=0&l=22),
+which allows the `Task` to be canceled with `task.cancel()`.
+
+The Blink scheduler handles the actual task scheduling. A `Task`'s callback is
+scheduled to run by posting it a task to a `TaskRunner`&mdash;just as all tasks
+in Blink are. The `TaskRunner` is obtained through the `WebSchedulingTaskQueue`,
+which is owned by the `TaskQueue` that the `Task` is posted to.
+
+TODO(shaseley): Add a diagram for the relation between all the different task
+queues.
+
+#### Task Run Order
+
+The API uses the Blink scheduler to run its tasks, and each
+`WebSchedulingPriority` maps to a
+[base::sequence_manager::TaskQueue::QueuePriority](https://cs.chromium.org/chromium/src/base/task/sequence_manager/task_queue.h?sq=package:chromium&g=0&l=76).
+
+There is no intervention from the scheduling API to enforce a certain
+order&mdash;the order that tasks run is strictly determined by the Blink
+scheduler based on the `WebSchedulingPriority`.
+
+By default, the Blink scheduler has an anti-starvation mechanism that will
+occasionally prioritize lower priority tasks over higher priority tasks. This
+can be disabled with
+[BlinkSchedulerDisableAntiStarvationForPriorities](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/scheduler/common/features.h?q=BlinkSchedulerDisa&sq=package:chromium&g=0&l=173),
+which we are relying on to achieve a static ordering (highest to lowest
+priority) of tasks.
+
+#### Detached Documents
+
+The API is modeled after setTimeout and setInterval with regards to detached
+documents. When a document is detached, any queued tasks will be prevented from
+running, and future calls to postTask through a cached Scheduler or TaskQueue
+will be no-ops and return null.
diff --git a/third_party/blink/renderer/modules/scheduler/images/task_lifecycle.png b/third_party/blink/renderer/modules/scheduler/images/task_lifecycle.png
new file mode 100644
index 0000000..93aa6f7
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/images/task_lifecycle.png
Binary files differ
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler.cc b/third_party/blink/renderer/modules/scheduler/scheduler.cc
new file mode 100644
index 0000000..7218f134
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/scheduler.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 "third_party/blink/renderer/modules/scheduler/scheduler.h"
+
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.h"
+#include "third_party/blink/renderer/modules/scheduler/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
+
+namespace blink {
+
+const char Scheduler::kSupplementName[] = "Scheduler";
+
+Scheduler* Scheduler::From(Document& document) {
+  Scheduler* scheduler = Supplement<Document>::From<Scheduler>(document);
+  if (!scheduler) {
+    scheduler = MakeGarbageCollected<Scheduler>(&document);
+    Supplement<Document>::ProvideTo(document, scheduler);
+  }
+  return scheduler;
+}
+
+Scheduler::Scheduler(Document* document) : ContextLifecycleObserver(document) {
+  if (document->IsContextDestroyed())
+    return;
+  DCHECK(document->GetScheduler());
+  DCHECK(document->GetScheduler()->ToFrameScheduler());
+  CreateGlobalTaskQueues(document);
+}
+
+void Scheduler::ContextDestroyed(ExecutionContext* context) {
+  global_task_queues_.clear();
+}
+
+void Scheduler::Trace(Visitor* visitor) {
+  visitor->Trace(global_task_queues_);
+  ScriptWrappable::Trace(visitor);
+  ContextLifecycleObserver::Trace(visitor);
+  Supplement<Document>::Trace(visitor);
+}
+
+TaskQueue* Scheduler::getTaskQueue(AtomicString priority) const {
+  if (global_task_queues_.IsEmpty())
+    return nullptr;
+  return global_task_queues_[static_cast<int>(
+      TaskQueue::WebSchedulingPriorityFromString(priority))];
+}
+
+Task* Scheduler::postTask(V8Function* callback_function,
+                          SchedulerPostTaskOptions* options,
+                          const Vector<ScriptValue>& args) {
+  TaskQueue* task_queue = getTaskQueue(AtomicString(options->priority()));
+  if (!task_queue)
+    return nullptr;
+  return task_queue->postTask(callback_function, options, args);
+}
+
+void Scheduler::CreateGlobalTaskQueues(Document* document) {
+  for (size_t i = 0; i < kWebSchedulingPriorityCount; i++) {
+    global_task_queues_.push_back(MakeGarbageCollected<TaskQueue>(
+        document, static_cast<WebSchedulingPriority>(i)));
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler.h b/third_party/blink/renderer/modules/scheduler/scheduler.h
new file mode 100644
index 0000000..340e534
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/scheduler.h
@@ -0,0 +1,72 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_SCHEDULER_H_
+
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
+#include "third_party/blink/renderer/platform/supplementable.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+class ExecutionContext;
+class SchedulerPostTaskOptions;
+class ScriptValue;
+class Task;
+class TaskQueue;
+class V8Function;
+
+// TODO(shaseley): Rename this class along with TaskQueue and Task to better
+// differentiate these classes from the various other scheduler, task queue, and
+// task classes in the codebase.
+class MODULES_EXPORT Scheduler : public ScriptWrappable,
+                                 public ContextLifecycleObserver,
+                                 public Supplement<Document> {
+  DEFINE_WRAPPERTYPEINFO();
+  USING_GARBAGE_COLLECTED_MIXIN(Scheduler);
+
+ public:
+  static const char kSupplementName[];
+
+  static Scheduler* From(Document&);
+
+  explicit Scheduler(Document*);
+
+  // Returns the TaskQueue for the given |priority| or nullptr if the underlying
+  // context is destroyed, e.g. for detached documents.
+  TaskQueue* getTaskQueue(AtomicString priority) const;
+
+  // postTask creates and queues a Task in the |global_task_queues_| entry
+  // corresponding to the priority in the SchedulerPostTaskOptions, and returns
+  // the Task. If the underlying context is destroyed, e.g. for detached
+  // documents, this returns nullptr.
+  Task* postTask(V8Function*,
+                 SchedulerPostTaskOptions*,
+                 const Vector<ScriptValue>& args);
+
+  void ContextDestroyed(ExecutionContext*) override;
+
+  void Trace(Visitor*) override;
+
+ private:
+  static constexpr size_t kWebSchedulingPriorityCount =
+      static_cast<size_t>(WebSchedulingPriority::kLastPriority) + 1;
+
+  void CreateGlobalTaskQueues(Document*);
+
+  // |global_task_queues_| is initialized with one entry per priority, indexed
+  // by priority. This will be empty when the document is detached.
+  HeapVector<Member<TaskQueue>, kWebSchedulingPriorityCount>
+      global_task_queues_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_SCHEDULER_H_
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler.idl b/third_party/blink/renderer/modules/scheduler/scheduler.idl
new file mode 100644
index 0000000..611de37
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/scheduler.idl
@@ -0,0 +1,10 @@
+// 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.
+
+// Experimental Scheduling API Proposal:
+// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
+[RuntimeEnabled=WebScheduler] interface Scheduler {
+    TaskQueue getTaskQueue(TaskQueuePriority priority);
+    Task postTask(Function callback, optional SchedulerPostTaskOptions options, any... arguments);
+};
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl b/third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl
new file mode 100644
index 0000000..d0a92b55
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl
@@ -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.
+
+// Experimental Scheduling API Proposal:
+// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
+dictionary SchedulerPostTaskOptions : TaskQueuePostTaskOptions {
+    TaskQueuePriority priority = "default";
+};
diff --git a/third_party/blink/renderer/modules/scheduler/task.cc b/third_party/blink/renderer/modules/scheduler/task.cc
new file mode 100644
index 0000000..400f574
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/task.cc
@@ -0,0 +1,134 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/scheduler/task.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_function.h"
+#include "third_party/blink/renderer/modules/scheduler/task_queue.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+
+Task::Task(TaskQueue* task_queue,
+           V8Function* callback,
+           const Vector<ScriptValue>& args)
+    : status_(Status::kPending),
+      task_queue_(task_queue),
+      callback_(callback),
+      arguments_(args) {
+  DCHECK(task_queue_);
+  DCHECK(callback_);
+}
+
+void Task::Trace(Visitor* visitor) {
+  visitor->Trace(task_queue_);
+  visitor->Trace(callback_);
+  ScriptWrappable::Trace(visitor);
+}
+
+AtomicString Task::priority() const {
+  DCHECK(task_queue_);
+  return task_queue_->priority();
+}
+
+AtomicString Task::status() const {
+  return TaskStatusToString(status_);
+}
+
+void Task::cancel() {
+  if (!IsPending())
+    return;
+  CancelPendingTask();
+  SetTaskStatus(Status::kCanceled);
+}
+
+void Task::SetTaskHandle(TaskHandle&& task_handle) {
+  task_handle_ = std::move(task_handle);
+}
+
+bool Task::IsPending() const {
+  return status_ == Status::kPending;
+}
+
+void Task::CancelPendingTask() {
+  DCHECK(IsPending());
+  DCHECK(task_handle_.IsActive());
+
+  task_handle_.Cancel();
+}
+
+void Task::Invoke() {
+  DCHECK(IsPending());
+  SetTaskStatus(Status::kRunning);
+  callback_->InvokeAndReportException(nullptr, arguments_);
+  SetTaskStatus(Status::kCompleted);
+}
+
+void Task::SetTaskStatus(Status status) {
+  DCHECK(IsValidStatusChange(status_, status))
+      << "Cannot transition from Task::Status " << TaskStatusToString(status_)
+      << " to " << TaskStatusToString(status);
+  status_ = status;
+}
+
+// static
+AtomicString Task::TaskStatusToString(Status status) {
+  DEFINE_STATIC_LOCAL(const AtomicString, pending, ("pending"));
+  DEFINE_STATIC_LOCAL(const AtomicString, running, ("running"));
+  DEFINE_STATIC_LOCAL(const AtomicString, canceled, ("canceled"));
+  DEFINE_STATIC_LOCAL(const AtomicString, completed, ("completed"));
+
+  switch (status) {
+    case Status::kPending:
+      return pending;
+    case Status::kRunning:
+      return running;
+    case Status::kCanceled:
+      return canceled;
+    case Status::kCompleted:
+      return completed;
+  }
+
+  NOTREACHED();
+  return g_empty_atom;
+}
+
+// static
+bool Task::IsValidStatusChange(Status from, Status to) {
+  // Note: Self transitions are not valid.
+  switch (from) {
+    case Status::kPending: {
+      switch (to) {
+        // The task is invoked by the scheduler.
+        case Status::kRunning:
+        // task.cancel().
+        case Status::kCanceled:
+          return true;
+        default:
+          return false;
+      }
+    }
+    case Status::kRunning: {
+      switch (to) {
+        // The task completes with or without exception.
+        case Status::kCompleted:
+          return true;
+        default:
+          return false;
+      }
+    }
+    // Canceled and Finished are both end states.
+    case Status::kCanceled:
+    case Status::kCompleted:
+      return false;
+  }
+  NOTREACHED();
+  return false;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/scheduler/task.h b/third_party/blink/renderer/modules/scheduler/task.h
new file mode 100644
index 0000000..713723e
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/task.h
@@ -0,0 +1,72 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_TASK_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_TASK_H_
+
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+class ScriptValue;
+class TaskQueue;
+class V8Function;
+
+class MODULES_EXPORT Task : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  explicit Task(TaskQueue*, V8Function*, const Vector<ScriptValue>& args);
+
+  // Task IDL Interface.
+  AtomicString priority() const;
+  AtomicString status() const;
+  void cancel();
+
+  // Set the TaskHandle associated with this task, which is used for
+  // cancellation.
+  void SetTaskHandle(TaskHandle&&);
+
+  // Invoke the |callback_|.
+  void Invoke();
+
+  // Returns true if the Task is pending, i.e. queued and runnable.
+  bool IsPending() const;
+
+  // Cancel a pending task. This will update the |status_| to kCanceled, and the
+  // task will no longer be runnable.
+  void CancelPendingTask();
+
+  void Trace(Visitor*) override;
+
+ private:
+  // The Status associated with the task lifecycle. Tasks are "pending" before
+  // they run, transition to "running" during execution, and end in "completed"
+  // when execution completes.
+  enum class Status {
+    kPending,
+    kRunning,
+    kCanceled,
+    kCompleted,
+  };
+
+  void SetTaskStatus(Status);
+
+  static bool IsValidStatusChange(Status from, Status to);
+  static AtomicString TaskStatusToString(Status);
+
+  Status status_;
+  TaskHandle task_handle_;
+  Member<TaskQueue> task_queue_;
+  Member<V8Function> callback_;
+  Vector<ScriptValue> arguments_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_TASK_H_
diff --git a/third_party/blink/renderer/modules/scheduler/task.idl b/third_party/blink/renderer/modules/scheduler/task.idl
new file mode 100644
index 0000000..62d2a1f
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/task.idl
@@ -0,0 +1,13 @@
+// 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.
+
+// Experimental Scheduling API Proposal:
+// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
+enum TaskStatus { "pending", "running", "canceled", "completed" };
+
+[RuntimeEnabled=WebScheduler] interface Task {
+    readonly attribute TaskStatus status;
+    readonly attribute TaskQueuePriority priority;
+    void cancel();
+};
diff --git a/third_party/blink/renderer/modules/scheduler/task_queue.cc b/third_party/blink/renderer/modules/scheduler/task_queue.cc
new file mode 100644
index 0000000..cfafc006
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/task_queue.cc
@@ -0,0 +1,153 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/scheduler/task_queue.h"
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_function.h"
+#include "third_party/blink/renderer/modules/scheduler/task.h"
+#include "third_party/blink/renderer/modules/scheduler/task_queue_post_task_options.h"
+#include "third_party/blink/renderer/platform/bindings/exception_code.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
+#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+
+namespace {
+
+const AtomicString& ImmediatePriorityKeyword() {
+  DEFINE_STATIC_LOCAL(const AtomicString, immediate_priority, ("immediate"));
+  return immediate_priority;
+}
+
+const AtomicString& HighPriorityKeyword() {
+  DEFINE_STATIC_LOCAL(const AtomicString, high_priority, ("high"));
+  return high_priority;
+}
+
+const AtomicString& DefaultPriorityKeyword() {
+  DEFINE_STATIC_LOCAL(const AtomicString, default_priority, ("default"));
+  return default_priority;
+}
+
+const AtomicString& LowPriorityKeyword() {
+  DEFINE_STATIC_LOCAL(const AtomicString, low_priority, ("low"));
+  return low_priority;
+}
+
+const AtomicString& IdlePriorityKeyword() {
+  DEFINE_STATIC_LOCAL(const AtomicString, idle_priority, ("idle"));
+  return idle_priority;
+}
+
+}  // namespace
+
+TaskQueue::TaskQueue(Document* document, WebSchedulingPriority priority)
+    : ContextLifecycleObserver(document),
+      priority_(priority),
+      web_scheduling_task_queue_(document->GetScheduler()
+                                     ->ToFrameScheduler()
+                                     ->CreateWebSchedulingTaskQueue(priority)),
+      task_runner_(web_scheduling_task_queue_->GetTaskRunner()) {
+  DCHECK(!document->IsContextDestroyed());
+}
+
+void TaskQueue::Trace(Visitor* visitor) {
+  ScriptWrappable::Trace(visitor);
+  ContextLifecycleObserver::Trace(visitor);
+}
+
+void TaskQueue::ContextDestroyed(ExecutionContext* context) {
+  web_scheduling_task_queue_.reset(nullptr);
+  task_runner_.reset();
+}
+
+AtomicString TaskQueue::priority() const {
+  return WebSchedulingPriorityToString(priority_);
+}
+
+Task* TaskQueue::postTask(V8Function* function,
+                          TaskQueuePostTaskOptions* options,
+                          const Vector<ScriptValue>& args) {
+  // |task_runner_| will be nullptr when the context is destroyed, which
+  // prevents us from scheduling tasks for detached documents.
+  if (!task_runner_)
+    return nullptr;
+
+  // For global task queues, we don't need to track the task objects separately;
+  // tracking is handled by the |web_scheduling_task_queue_|.
+  Task* task = MakeGarbageCollected<Task>(this, function, args);
+
+  // TODO(shaseley): We need to figure out the behavior we want for delay. For
+  // now, we use behavior that is very similar to setTimeout: negative delays
+  // are treated as 0, and we use the Blink scheduler's delayed task behavior.
+  // We don't, however, adjust the timeout based on nested calls (yet) or clamp
+  // the value to a minimal delay.
+  base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
+      options->delay() > 0 ? options->delay() : 0);
+  task->SetTaskHandle(PostDelayedCancellableTask(
+      *task_runner_, FROM_HERE,
+      WTF::Bind(&TaskQueue::RunTaskCallback, WrapPersistent(this),
+                WrapPersistent(task)),
+      delay));
+  return task;
+}
+
+void TaskQueue::RunTaskCallback(Task* task) {
+  // If there are pending tasks queued when the underlying frame's document is
+  // swapped, the callbacks are still invoked. This prevents us from running the
+  // tasks in that case.
+  if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed())
+    return;
+  task->Invoke();
+}
+
+// static
+AtomicString TaskQueue::WebSchedulingPriorityToString(
+    WebSchedulingPriority priority) {
+  switch (priority) {
+    case WebSchedulingPriority::kImmediatePriority:
+      return ImmediatePriorityKeyword();
+    case WebSchedulingPriority::kHighPriority:
+      return HighPriorityKeyword();
+    case WebSchedulingPriority::kDefaultPriority:
+      return DefaultPriorityKeyword();
+    case WebSchedulingPriority::kLowPriority:
+      return LowPriorityKeyword();
+    case WebSchedulingPriority::kIdlePriority:
+      return IdlePriorityKeyword();
+  }
+
+  NOTREACHED();
+  return g_empty_atom;
+}
+
+// static
+WebSchedulingPriority TaskQueue::WebSchedulingPriorityFromString(
+    AtomicString priority) {
+  if (priority == ImmediatePriorityKeyword())
+    return WebSchedulingPriority::kImmediatePriority;
+  if (priority == HighPriorityKeyword())
+    return WebSchedulingPriority::kHighPriority;
+  if (priority == DefaultPriorityKeyword())
+    return WebSchedulingPriority::kDefaultPriority;
+  if (priority == LowPriorityKeyword())
+    return WebSchedulingPriority::kLowPriority;
+  if (priority == IdlePriorityKeyword())
+    return WebSchedulingPriority::kIdlePriority;
+
+  NOTREACHED();
+  return WebSchedulingPriority::kDefaultPriority;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/scheduler/task_queue.h b/third_party/blink/renderer/modules/scheduler/task_queue.h
new file mode 100644
index 0000000..52b7e46
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/task_queue.h
@@ -0,0 +1,71 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_TASK_QUEUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_TASK_QUEUE_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace blink {
+
+class Document;
+class ExecutionContext;
+class ScriptValue;
+class Task;
+class TaskQueuePostTaskOptions;
+class V8Function;
+class WebSchedulingTaskQueue;
+
+namespace scheduler {
+class WebSchedulingTaskQueue;
+}  // namespace scheduler
+
+class MODULES_EXPORT TaskQueue : public ScriptWrappable,
+                                 ContextLifecycleObserver {
+  DEFINE_WRAPPERTYPEINFO();
+  USING_GARBAGE_COLLECTED_MIXIN(TaskQueue);
+
+ public:
+  TaskQueue(Document*, WebSchedulingPriority);
+
+  // Returns the priority of the TaskQueue.
+  AtomicString priority() const;
+
+  // postTask creates and queues a Task in this TaskQueue and returns the Task.
+  // If the underlying context is destroyed, e.g. for detached documents, this
+  // returns nullptr.
+  Task* postTask(V8Function*,
+                 TaskQueuePostTaskOptions*,
+                 const Vector<ScriptValue>& args);
+
+  void ContextDestroyed(ExecutionContext*) override;
+
+  void Trace(Visitor*) override;
+
+  static AtomicString WebSchedulingPriorityToString(WebSchedulingPriority);
+  static WebSchedulingPriority WebSchedulingPriorityFromString(AtomicString);
+
+ private:
+  void RunTaskCallback(Task*);
+
+  const WebSchedulingPriority priority_;
+  std::unique_ptr<WebSchedulingTaskQueue> web_scheduling_task_queue_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_TASK_QUEUE_H_
diff --git a/third_party/blink/renderer/modules/scheduler/task_queue.idl b/third_party/blink/renderer/modules/scheduler/task_queue.idl
new file mode 100644
index 0000000..eb4856a
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/task_queue.idl
@@ -0,0 +1,18 @@
+// 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.
+
+// Experimental Scheduling API Proposal:
+// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
+enum TaskQueuePriority {
+    "immediate",
+    "high",
+    "default",
+    "low",
+    "idle"
+};
+
+[RuntimeEnabled=WebScheduler] interface TaskQueue {
+    readonly attribute TaskQueuePriority priority;
+    Task postTask(Function callback, optional TaskQueuePostTaskOptions options, any... arguments);
+};
diff --git a/third_party/blink/renderer/modules/scheduler/task_queue_post_task_options.idl b/third_party/blink/renderer/modules/scheduler/task_queue_post_task_options.idl
new file mode 100644
index 0000000..75661afa
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/task_queue_post_task_options.idl
@@ -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.
+
+// Experimental Scheduling API Proposal:
+// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
+dictionary TaskQueuePostTaskOptions {
+    long delay = 0;
+};
diff --git a/third_party/blink/renderer/modules/scheduler/window_scheduler.cc b/third_party/blink/renderer/modules/scheduler/window_scheduler.cc
new file mode 100644
index 0000000..b2c8e5cb
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/window_scheduler.cc
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/scheduler/window_scheduler.h"
+
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/modules/scheduler/scheduler.h"
+
+namespace blink {
+
+Scheduler* WindowScheduler::scheduler(LocalDOMWindow& window) {
+  if (Document* document = window.document()) {
+    return Scheduler::From(*document);
+  }
+  return nullptr;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/scheduler/window_scheduler.h b/third_party/blink/renderer/modules/scheduler/window_scheduler.h
new file mode 100644
index 0000000..bb82156
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/window_scheduler.h
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_WINDOW_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_WINDOW_SCHEDULER_H_
+
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+
+namespace blink {
+
+class LocalDOMWindow;
+class Scheduler;
+
+class MODULES_EXPORT WindowScheduler {
+  STATIC_ONLY(WindowScheduler);
+
+ public:
+  static Scheduler* scheduler(LocalDOMWindow&);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_WINDOW_SCHEDULER_H_
diff --git a/third_party/blink/renderer/modules/scheduler/window_scheduler.idl b/third_party/blink/renderer/modules/scheduler/window_scheduler.idl
new file mode 100644
index 0000000..7abdda42
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/window_scheduler.idl
@@ -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.
+
+// Experimental Scheduling API Proposal:
+// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
+[
+  ImplementedAs=WindowScheduler
+] partial interface Window {
+  [RuntimeEnabled=WebScheduler, SameObject, Replaceable] readonly attribute Scheduler scheduler;
+};
diff --git a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
index 92b59215..796c222 100644
--- a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
+++ b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
@@ -300,23 +300,6 @@
 }  // namespace
 
 TEST_F(WebEmbeddedWorkerImplTest, TerminateSoonAfterStart) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndDisableFeature(
-      features::kOffMainThreadServiceWorkerScriptFetch);
-
-  worker_->StartWorkerContext(start_data_);
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-
-  EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(1);
-  worker_->TerminateWorkerContext();
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-}
-
-TEST_F(WebEmbeddedWorkerImplTest, TerminateSoonAfterStart_OMT_Fetch) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(
-      features::kOffMainThreadServiceWorkerScriptFetch);
-
   worker_->StartWorkerContext(start_data_);
   testing::Mock::VerifyAndClearExpectations(mock_client_.get());
 
@@ -326,25 +309,6 @@
 }
 
 TEST_F(WebEmbeddedWorkerImplTest, TerminateWhileWaitingForDebugger) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndDisableFeature(
-      features::kOffMainThreadServiceWorkerScriptFetch);
-
-  start_data_.wait_for_debugger_mode =
-      WebEmbeddedWorkerStartData::kWaitForDebugger;
-  worker_->StartWorkerContext(start_data_);
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-
-  EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(1);
-  worker_->TerminateWorkerContext();
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-}
-
-TEST_F(WebEmbeddedWorkerImplTest, TerminateWhileWaitingForDebugger_OMT_Fetch) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(
-      features::kOffMainThreadServiceWorkerScriptFetch);
-
   start_data_.wait_for_debugger_mode =
       WebEmbeddedWorkerStartData::kWaitForDebugger;
   worker_->StartWorkerContext(start_data_);
@@ -355,30 +319,6 @@
 }
 
 TEST_F(WebEmbeddedWorkerImplTest, TerminateWhileLoadingScript) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndDisableFeature(
-      features::kOffMainThreadServiceWorkerScriptFetch);
-
-  // Load the shadow page.
-  EXPECT_CALL(*mock_installed_scripts_manager_,
-              IsScriptInstalled(KURL(start_data_.script_url)))
-      .Times(testing::AtLeast(1))
-      .WillRepeatedly(testing::Return(false));
-  worker_->StartWorkerContext(start_data_);
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-  testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_);
-
-  // Terminate before loading the script.
-  EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(1);
-  worker_->TerminateWorkerContext();
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-}
-
-TEST_F(WebEmbeddedWorkerImplTest, TerminateWhileLoadingScript_OMT_Fetch) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(
-      features::kOffMainThreadServiceWorkerScriptFetch);
-
   // Load the shadow page.
   EXPECT_CALL(*mock_installed_scripts_manager_,
               IsScriptInstalled(KURL(start_data_.script_url)))
@@ -394,71 +334,7 @@
   worker_->WaitForShutdownForTesting();
 }
 
-// Tests terminating worker context between script download and execution.
-// No "OMT_Fetch" variant for this test because "pause after download" doesn't
-// have effect for off-main-thread script fetch. When off-main-thread fetch
-// is on, pausing/resuming is handled in ServiceWorkerGlobalScope.
-TEST_F(WebEmbeddedWorkerImplTest, TerminateWhilePausedAfterDownload) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndDisableFeature(
-      features::kOffMainThreadServiceWorkerScriptFetch);
-
-  // Load the shadow page.
-  start_data_.pause_after_download_mode =
-      WebEmbeddedWorkerStartData::kPauseAfterDownload;
-  EXPECT_CALL(*mock_installed_scripts_manager_,
-              IsScriptInstalled(KURL(start_data_.script_url)))
-      .Times(testing::AtLeast(1))
-      .WillRepeatedly(testing::Return(false));
-  worker_->StartWorkerContext(start_data_);
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-  testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_);
-
-  // Load the script.
-  EXPECT_CALL(*mock_client_, WorkerScriptLoadedOnMainThread()).Times(1);
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-
-  // Terminate before resuming after download.
-  EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(1);
-  worker_->TerminateWorkerContext();
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-}
-
 TEST_F(WebEmbeddedWorkerImplTest, ScriptNotFound) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndDisableFeature(
-      features::kOffMainThreadServiceWorkerScriptFetch);
-
-  // Load the shadow page.
-  WebURL script_url = url_test_helpers::ToKURL(kNotFoundScriptURL);
-  WebURLResponse response;
-  response.SetMimeType("text/javascript");
-  response.SetHttpStatusCode(404);
-  ResourceError error = ResourceError::Failure(script_url);
-  Platform::Current()->GetURLLoaderMockFactory()->RegisterErrorURL(
-      script_url, response, error);
-  start_data_.script_url = script_url;
-  EXPECT_CALL(*mock_installed_scripts_manager_,
-              IsScriptInstalled(KURL(start_data_.script_url)))
-      .Times(testing::AtLeast(1))
-      .WillRepeatedly(testing::Return(false));
-  worker_->StartWorkerContext(start_data_);
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-  testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_);
-
-  // Load the script.
-  EXPECT_CALL(*mock_client_, WorkerScriptLoadedOnMainThread()).Times(0);
-  EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(1);
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-}
-
-TEST_F(WebEmbeddedWorkerImplTest, ScriptNotFound_OMT_Fetch) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(
-      features::kOffMainThreadServiceWorkerScriptFetch);
-
   // Load the shadow page.
   WebURL script_url = url_test_helpers::ToKURL(kNotFoundScriptURL);
   WebURLResponse response;
@@ -485,96 +361,4 @@
   worker_->WaitForShutdownForTesting();
 }
 
-// The running worker is detected as a memory leak. crbug.com/586897 and
-// crbug.com/807754.
-// No "OMT_Fetch" variant. See comments on TerminateWhilePausedAfterDownload.
-#if defined(ADDRESS_SANITIZER)
-#define MAYBE_DontPauseAfterDownload DISABLED_DontPauseAfterDownload
-#else
-#define MAYBE_DontPauseAfterDownload DontPauseAfterDownload
-#endif
-TEST_F(WebEmbeddedWorkerImplTest, MAYBE_DontPauseAfterDownload) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndDisableFeature(
-      features::kOffMainThreadServiceWorkerScriptFetch);
-
-  // Load the shadow page.
-  EXPECT_CALL(*mock_installed_scripts_manager_,
-              IsScriptInstalled(KURL(start_data_.script_url)))
-      .Times(testing::AtLeast(1))
-      .WillRepeatedly(testing::Return(false));
-  worker_->StartWorkerContext(start_data_);
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-  testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_);
-
-  // Load the script.
-  EXPECT_CALL(*mock_client_, WorkerScriptLoadedOnMainThread()).Times(1);
-  EXPECT_CALL(*mock_client_,
-              WorkerReadyForInspectionOnMainThread(testing::_, testing::_))
-      .Times(1);
-  // This is called on the worker thread.
-  EXPECT_CALL(*mock_installed_scripts_manager_,
-              IsScriptInstalled(KURL(start_data_.script_url)))
-      .Times(testing::AtLeast(1))
-      .WillRepeatedly(testing::Return(false));
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-  mock_client_->WaitUntilScriptEvaluated();
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-  testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_);
-
-  // Terminate the running worker thread.
-  EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(0);
-  worker_->TerminateWorkerContext();
-  mock_client_->WaitUntilThreadTermination();
-}
-
-// The running worker is detected as a memory leak. crbug.com/586897 and
-// crbug.com/807754.
-// No "OMT_Fetch" variant. See comments on TerminateWhilePausedAfterDownload.
-#if defined(ADDRESS_SANITIZER)
-#define MAYBE_PauseAfterDownload DISABLED_PauseAfterDownload
-#else
-#define MAYBE_PauseAfterDownload PauseAfterDownload
-#endif
-TEST_F(WebEmbeddedWorkerImplTest, MAYBE_PauseAfterDownload) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndDisableFeature(
-      features::kOffMainThreadServiceWorkerScriptFetch);
-
-  // Load the shadow page.
-  EXPECT_CALL(*mock_installed_scripts_manager_,
-              IsScriptInstalled(KURL(start_data_.script_url)))
-      .Times(testing::AtLeast(1))
-      .WillRepeatedly(testing::Return(false));
-  start_data_.pause_after_download_mode =
-      WebEmbeddedWorkerStartData::kPauseAfterDownload;
-  worker_->StartWorkerContext(start_data_);
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-  testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_);
-
-  // Load the script.
-  EXPECT_CALL(*mock_client_, WorkerScriptLoadedOnMainThread()).Times(1);
-  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-
-  // Resume after download.
-  // This is called on the worker thread.
-  EXPECT_CALL(*mock_installed_scripts_manager_,
-              IsScriptInstalled(KURL(start_data_.script_url)))
-      .Times(testing::AtLeast(1))
-      .WillRepeatedly(testing::Return(false));
-  EXPECT_CALL(*mock_client_,
-              WorkerReadyForInspectionOnMainThread(testing::_, testing::_))
-      .Times(1);
-  worker_->ResumeAfterDownload();
-  mock_client_->WaitUntilScriptEvaluated();
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-  testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_);
-
-  // Terminate the running worker thread.
-  EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(0);
-  worker_->TerminateWorkerContext();
-  mock_client_->WaitUntilThreadTermination();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webshare/navigator_share.cc b/third_party/blink/renderer/modules/webshare/navigator_share.cc
index 7c71107..565d0a0 100644
--- a/third_party/blink/renderer/modules/webshare/navigator_share.cc
+++ b/third_party/blink/renderer/modules/webshare/navigator_share.cc
@@ -55,7 +55,7 @@
 }
 
 // Returns a message for a TypeError if share(share_data) would reject with
-// TypeError. https://wicg.github.io/web-share/level-2/#canshare-method
+// TypeError. https://w3c.github.io/web-share/level-2/#canshare-method
 // Otherwise returns an empty string.
 // Populates full_url with the result of running the URL parser on
 // share_data.url
diff --git a/third_party/blink/renderer/modules/webshare/navigator_share.idl b/third_party/blink/renderer/modules/webshare/navigator_share.idl
index 0c20961e..1a4bf168 100644
--- a/third_party/blink/renderer/modules/webshare/navigator_share.idl
+++ b/third_party/blink/renderer/modules/webshare/navigator_share.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/web-share/
+// https://w3c.github.io/web-share/
 
 [
   ImplementedAs=NavigatorShare,
diff --git a/third_party/blink/renderer/modules/webshare/share_data.idl b/third_party/blink/renderer/modules/webshare/share_data.idl
index d9b8a86..09047e8 100644
--- a/third_party/blink/renderer/modules/webshare/share_data.idl
+++ b/third_party/blink/renderer/modules/webshare/share_data.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/web-share/
+// https://w3c.github.io/web-share/
 
 dictionary ShareData {
   USVString title;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 8aed517..9d57a160 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1728,6 +1728,10 @@
       name: "WebNFC",
       status: "experimental",
     },
+    {
+      name: "WebScheduler",
+      status: "experimental",
+    },
     // WebShare is enabled by default on Android.
     {
       name: "WebShare",
diff --git a/third_party/blink/renderer/platform/testing/url_test_helpers.cc b/third_party/blink/renderer/platform/testing/url_test_helpers.cc
index ff8c9dd..653acd53 100644
--- a/third_party/blink/renderer/platform/testing/url_test_helpers.cc
+++ b/third_party/blink/renderer/platform/testing/url_test_helpers.cc
@@ -102,5 +102,19 @@
   Platform::Current()->GetURLLoaderMockFactory()->UnregisterURL(url);
 }
 
+void UnregisterAllURLsAndClearMemoryCache() {
+  Platform::Current()
+      ->GetURLLoaderMockFactory()
+      ->UnregisterAllURLsAndClearMemoryCache();
+}
+
+void SetLoaderDelegate(WebURLLoaderTestDelegate* delegate) {
+  Platform::Current()->GetURLLoaderMockFactory()->SetLoaderDelegate(delegate);
+}
+
+void ServeAsynchronousRequests() {
+  Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+}
+
 }  // namespace url_test_helpers
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/url_test_helpers.h b/third_party/blink/renderer/platform/testing/url_test_helpers.h
index aaf7c51..2edd24f 100644
--- a/third_party/blink/renderer/platform/testing/url_test_helpers.h
+++ b/third_party/blink/renderer/platform/testing/url_test_helpers.h
@@ -33,6 +33,7 @@
 
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 
@@ -85,6 +86,12 @@
 // Registers a mock URL that returns a 404 error.
 void RegisterMockedErrorURLLoad(const WebURL& full_url);
 
+void UnregisterAllURLsAndClearMemoryCache();
+
+void SetLoaderDelegate(WebURLLoaderTestDelegate* delegate);
+
+void ServeAsynchronousRequests();
+
 }  // namespace url_test_helpers
 }  // namespace blink
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 67d01b9..58b9c23 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6563,3 +6563,10 @@
 crbug.com/991243 [ Linux ] external/wpt/workers/semantics/multiple-workers/003.html [ Pass Timeout ]
 crbug.com/991243 [ Linux ] virtual/omt-worker-fetch/external/wpt/workers/semantics/multiple-workers/003.html [ Pass Timeout ]
 crbug.com/991243 [ Linux ] virtual/not-omt-sw-fetch/external/wpt/workers/semantics/multiple-workers/003.html [ Pass Timeout ]
+
+# Sheriff 2019-08-08
+crbug.com/992148 [ Linux ] virtual/disable-blink-gen-property-trees/animations/stability/animation-iteration-event-destroy-renderer.html [ Pass Timeout ]
+
+# Expected failure for experimental Scheduling API when running without the
+# BlinkSchedulerDisableAntiStarvationForPriorities feature.
+crbug.com/979017 wpt_internal/scheduler/task_order.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 437f062..371517d 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1246,5 +1246,10 @@
     "prefix": "audio-service",
     "base": "external/wpt/mediacapture-streams",
     "args": ["--enable-features=AudioServiceOutOfProcess,AudioServiceSandbox"]
+  },
+  {
+    "prefix": "scheduler",
+    "base": "wpt_internal/scheduler",
+    "args": ["--enable-features=BlinkSchedulerDisableAntiStarvationForPriorities"]
   }
 ]
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 2af7b11..b5e75e7 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
@@ -118149,6 +118149,18 @@
      {}
     ]
    ],
+   "svg/extensibility/foreignObject/filter-repaint.html": [
+    [
+     "svg/extensibility/foreignObject/filter-repaint.html",
+     [
+      [
+       "/svg/extensibility/foreignObject/filter-repaint-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "svg/extensibility/foreignObject/foreign-object-margin-collapsing.html": [
     [
      "svg/extensibility/foreignObject/foreign-object-margin-collapsing.html",
@@ -154007,6 +154019,9 @@
    "fetch/sec-metadata/resources/sharedWorker.js": [
     []
    ],
+   "fetch/sec-metadata/resources/unload-with-beacon.html": [
+    []
+   ],
    "fetch/sec-metadata/resources/xslt-test.sub.xml": [
     []
    ],
@@ -169988,6 +170003,9 @@
    "svg/extensibility/foreignObject/compositing-backface-visibility-ref.html": [
     []
    ],
+   "svg/extensibility/foreignObject/filter-repaint-ref.html": [
+    []
+   ],
    "svg/extensibility/foreignObject/foreign-object-paints-before-rect-ref.html": [
     []
    ],
@@ -208537,9 +208555,15 @@
      {}
     ]
    ],
-   "css/css-sizing/inheritance.html": [
+   "css/css-sizing/inheritance-001.html": [
     [
-     "css/css-sizing/inheritance.html",
+     "css/css-sizing/inheritance-001.html",
+     {}
+    ]
+   ],
+   "css/css-sizing/inheritance-002.html": [
+    [
+     "css/css-sizing/inheritance-002.html",
      {}
     ]
    ],
@@ -235066,6 +235090,14 @@
      {}
     ]
    ],
+   "fetch/sec-metadata/unload.tentative.https.sub.html": [
+    [
+     "fetch/sec-metadata/unload.tentative.https.sub.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
    "fetch/sec-metadata/window-open.tentative.https.sub.html": [
     [
      "fetch/sec-metadata/window-open.tentative.https.sub.html",
@@ -327100,7 +327132,7 @@
    "support"
   ],
   "cookie-store/serviceworker_cookieStore_arguments.tentative.https.html": [
-   "06c950149751ab6e264c3bd12c826d8a60b9baf6",
+   "7d006926dac7883deb5e92b01bcc103ab3e1ea83",
    "testharness"
   ],
   "cookie-store/serviceworker_cookieStore_basic.js": [
@@ -327108,7 +327140,7 @@
    "support"
   ],
   "cookie-store/serviceworker_cookieStore_basic.tentative.https.html": [
-   "d4385d64ea993a7ecd8e92da4b722b7951134c30",
+   "379a8d764216ea7479c0df44f7e8dd1b87f149ab",
    "testharness"
   ],
   "cookie-store/serviceworker_cookieStore_cross_origin.js": [
@@ -327124,7 +327156,7 @@
    "support"
   ],
   "cookie-store/serviceworker_cookieStore_subscriptions.tentative.https.html": [
-   "2f26094aa2e3de355fa5f5778f4ede1c34b59559",
+   "375bce622c348d831c968ae63b90b44ae98de5c2",
    "testharness"
   ],
   "cookie-store/serviceworker_cookieStore_subscriptions_basic.js": [
@@ -327132,7 +327164,7 @@
    "support"
   ],
   "cookie-store/serviceworker_cookieStore_subscriptions_basic.tentative.https.html": [
-   "525c3b3c5c15007b282008eff8cd541e193764b6",
+   "0353bc2c2096f72450ad6f7b6aad5439e9bd0b20",
    "testharness"
   ],
   "cookie-store/serviceworker_cookieStore_subscriptions_empty.js": [
@@ -327140,7 +327172,7 @@
    "support"
   ],
   "cookie-store/serviceworker_cookieStore_subscriptions_empty.tentative.https.html": [
-   "38690fe3c72b58c7ed89d263990a2d0a2a1aa16f",
+   "38ec4048f1944c3f6bfe904592ee0646e28979f2",
    "testharness"
   ],
   "cookie-store/serviceworker_cookieStore_subscriptions_eventhandler_attribute.js": [
@@ -366636,11 +366668,11 @@
    "support"
   ],
   "css/css-inline/inheritance-expected.txt": [
-   "c1974d3f1b2e3bd76c57d2afd2d9de77f05ffaf5",
+   "86e56d979fe0b5966cc654ca163697705bae97d0",
    "support"
   ],
   "css/css-inline/inheritance.html": [
-   "f4c93b5b2531e0c4b32ed99a34ec71abe33c6cd9",
+   "7728f67e159eabbcb9dcc87d1a1db1d14b370cb3",
    "testharness"
   ],
   "css/css-inline/inline-crash-chrome-001.html": [
@@ -375651,8 +375683,12 @@
    "5b1713975eb21e96a558933f81412a7bb0007d19",
    "reftest"
   ],
-  "css/css-sizing/inheritance.html": [
-   "367ad0201daa5a307f8e87432c49f9719fa80191",
+  "css/css-sizing/inheritance-001.html": [
+   "2516288e48685cc22a5b7ca4051d3c605671ea79",
+   "testharness"
+  ],
+  "css/css-sizing/inheritance-002.html": [
+   "2af768b2c49fa973db7f5871e21f9f9c6442f81a",
    "testharness"
   ],
   "css/css-sizing/intrinsic-percent-non-replaced-001-ref.html": [
@@ -422007,6 +422043,10 @@
    "5eb89cb4f68a098154cf3605f53318af2f5e56f1",
    "support"
   ],
+  "fetch/sec-metadata/resources/unload-with-beacon.html": [
+   "b00c9a5776a45ac09a1c8e85b763c6be572fa99f",
+   "support"
+  ],
   "fetch/sec-metadata/resources/xslt-test.sub.xml": [
    "2d4dff5fc3efff235827cc8024a87710c1377972",
    "support"
@@ -422039,6 +422079,10 @@
    "ff0e842d5118cba40b73f09e29cf98be0bcabb12",
    "testharness"
   ],
+  "fetch/sec-metadata/unload.tentative.https.sub.html": [
+   "89ebc050328c03c47db866ca8d6858bc1909b3fa",
+   "testharness"
+  ],
   "fetch/sec-metadata/window-open.tentative.https.sub.html": [
    "df13d55cf1ed6f206c9db7f681f56a3f0f834ef3",
    "testharness"
@@ -476523,6 +476567,14 @@
    "da0728c96b5d0eb81435efad329bdcc3ee4dfb26",
    "testharness"
   ],
+  "svg/extensibility/foreignObject/filter-repaint-ref.html": [
+   "07563a617aa00b6561309b4bff5104b4d4d398e6",
+   "support"
+  ],
+  "svg/extensibility/foreignObject/filter-repaint.html": [
+   "0388977841e9cef08d8784f2aaf463049245ea20",
+   "reftest"
+  ],
   "svg/extensibility/foreignObject/foreign-object-circular-filter-reference-crash.html": [
    "162999402ac96dfc966702c554b33b11cf8a1054",
    "testharness"
@@ -494332,11 +494384,11 @@
    "testharness"
   ],
   "webxr/xrInputSource_gamepad_disconnect.https.html": [
-   "9ec349adeb979975569961382fe47f4c889837dc",
+   "66faf8699640fe3cef81a84d1f290988c35c3e14",
    "testharness"
   ],
   "webxr/xrInputSource_gamepad_input_registered.https.html": [
-   "9213657a367bb0dd8872ff10120d705b08c0f6e6",
+   "cb1ce7780b42eec33f6732efad8619755e899e06",
    "testharness"
   ],
   "webxr/xrInputSource_profiles.https.html": [
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_arguments.tentative.https.html b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_arguments.tentative.https.html
index 06c9501..7d00692 100644
--- a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_arguments.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_arguments.tentative.https.html
@@ -11,11 +11,11 @@
 (async () => {
   const scope = 'does/not/exist';
 
-  let registration = await navigator.serviceWorker.getRegistration(scope);
-  if (registration)
-    await registration.unregister();
-  registration = await navigator.serviceWorker.register(
+  const registration = await navigator.serviceWorker.register(
       'serviceworker_cookieStore_arguments.js', {scope});
+  add_completion_callback(() => {
+    registration.unregister();
+  });
 
   fetch_tests_from_worker(registration.installing);
 })();
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_basic.tentative.https.html b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_basic.tentative.https.html
index d4385d64..379a8d7 100644
--- a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_basic.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_basic.tentative.https.html
@@ -11,11 +11,11 @@
 (async () => {
   const scope = 'does/not/exist';
 
-  let registration = await navigator.serviceWorker.getRegistration(scope);
-  if (registration)
-    await registration.unregister();
-  registration = await navigator.serviceWorker.register(
+  const registration = await navigator.serviceWorker.register(
       'serviceworker_cookieStore_basic.js', {scope});
+  add_completion_callback(() => {
+    registration.unregister();
+  });
 
   fetch_tests_from_worker(registration.installing);
 })();
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions.tentative.https.html b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions.tentative.https.html
index 2f26094a..375bce6 100644
--- a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions.tentative.https.html
@@ -12,11 +12,11 @@
   // Not using an explicit scope here in order for script URL to be in scope,
   // to cover implicit subscription URL construction.
 
-  let registration = await navigator.serviceWorker.getRegistration();
-  if (registration)
-    await registration.unregister();
-  registration = await navigator.serviceWorker.register(
+  const registration = await navigator.serviceWorker.register(
       'serviceworker_cookieStore_subscriptions.js');
+  add_completion_callback(() => {
+    registration.unregister();
+  });
 
   fetch_tests_from_worker(registration.installing);
 })();
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_basic.tentative.https.html b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_basic.tentative.https.html
index 525c3b3..0353bc2 100644
--- a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_basic.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_basic.tentative.https.html
@@ -11,11 +11,11 @@
 (async () => {
   const scope = 'scope';
 
-  let registration = await navigator.serviceWorker.getRegistration(scope);
-  if (registration)
-    await registration.unregister();
-  registration = await navigator.serviceWorker.register(
+  const registration = await navigator.serviceWorker.register(
       'serviceworker_cookieStore_subscriptions_basic.js', {scope});
+  add_completion_callback(() => {
+    registration.unregister();
+  });
 
   fetch_tests_from_worker(registration.installing);
 })();
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_empty.tentative.https.html b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_empty.tentative.https.html
index 38690fe..38ec404 100644
--- a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_empty.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_empty.tentative.https.html
@@ -10,11 +10,11 @@
 (async () => {
   const scope = 'scope';
 
-  let registration = await navigator.serviceWorker.getRegistration(scope);
-  if (registration)
-    await registration.unregister();
-  registration = await navigator.serviceWorker.register(
+  const registration = await navigator.serviceWorker.register(
       'serviceworker_cookieStore_subscriptions_empty.js', {scope});
+  add_completion_callback(() => {
+    registration.unregister();
+  });
 
   fetch_tests_from_worker(registration.installing);
 })();
diff --git a/third_party/blink/web_tests/external/wpt/web-share/META.yml b/third_party/blink/web_tests/external/wpt/web-share/META.yml
index e054ccc..d2eccca 100644
--- a/third_party/blink/web_tests/external/wpt/web-share/META.yml
+++ b/third_party/blink/web_tests/external/wpt/web-share/META.yml
@@ -1,4 +1,4 @@
-spec: https://wicg.github.io/web-share/
+spec: https://w3c.github.io/web-share/
 suggested_reviewers:
   - ewilligers
   - mgiuca
diff --git a/third_party/blink/web_tests/external/wpt/web-share/idlharness.https.window.js b/third_party/blink/web_tests/external/wpt/web-share/idlharness.https.window.js
index cbaf9d72..1520387 100644
--- a/third_party/blink/web_tests/external/wpt/web-share/idlharness.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/web-share/idlharness.https.window.js
@@ -1,7 +1,7 @@
 // META: script=/resources/WebIDLParser.js
 // META: script=/resources/idlharness.js
 
-// https://wicg.github.io/web-share/
+// https://w3c.github.io/web-share/
 
 'use strict';
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/audits/audits-limited-run-expected.txt b/third_party/blink/web_tests/http/tests/devtools/audits/audits-limited-run-expected.txt
index 8e84d0b8..ec70c08 100644
--- a/third_party/blink/web_tests/http/tests/devtools/audits/audits-limited-run-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/audits/audits-limited-run-expected.txt
@@ -1,9 +1,9 @@
-Tests that audits panel works when only performance category is selected.
+Tests that audits panel works when only the pwa category is selected.
 
 
 ========== Audits Start Audit State ==========
-[x] Performance
-[ ] Progressive Web App
+[ ] Performance
+[x] Progressive Web App
 [ ] Best practices
 [ ] Accessibility
 [ ] SEO
@@ -11,45 +11,20 @@
 Run audits: enabled visible
 
 =============== Audits run ===============
-bootup-time
-critical-request-chains
-diagnostics
-dom-size
-efficient-animated-content
-estimated-input-latency
-final-screenshot
-first-contentful-paint
-first-cpu-idle
-first-meaningful-paint
-font-display
-interactive
-main-thread-tasks
-mainthread-work-breakdown
-max-potential-fid
-metrics
-network-requests
-network-rtt
-network-server-latency
-offscreen-images
-performance-budget
-redirects
-render-blocking-resources
-resource-summary
-screenshot-thumbnails
-speed-index
-third-party-summary
-time-to-first-byte
-total-blocking-time
-total-byte-weight
-unminified-css
-unminified-javascript
-unused-css-rules
-user-timings
-uses-long-cache-ttl
-uses-optimized-images
-uses-rel-preconnect
-uses-rel-preload
-uses-responsive-images
-uses-text-compression
-uses-webp-images
+apple-touch-icon
+content-width
+installable-manifest
+is-on-https
+load-fast-enough-for-pwa
+offline-start-url
+pwa-cross-browser
+pwa-each-page-has-url
+pwa-page-transitions
+redirects-http
+service-worker
+splash-screen
+themed-omnibox
+viewport
+without-javascript
+works-offline
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/audits/audits-limited-run.js b/third_party/blink/web_tests/http/tests/devtools/audits/audits-limited-run.js
index d130aa9..e16cb987 100644
--- a/third_party/blink/web_tests/http/tests/devtools/audits/audits-limited-run.js
+++ b/third_party/blink/web_tests/http/tests/devtools/audits/audits-limited-run.js
@@ -3,15 +3,15 @@
 // found in the LICENSE file.
 
 (async function() {
-  TestRunner.addResult('Tests that audits panel works when only performance category is selected.\n');
+  TestRunner.addResult('Tests that audits panel works when only the pwa category is selected.\n');
 
   await TestRunner.loadModule('audits_test_runner');
   await TestRunner.showPanel('audits');
 
-  var dialogElement = AuditsTestRunner.getContainerElement();
-  var checkboxes = dialogElement.querySelectorAll('.checkbox');
-  for (var checkbox of checkboxes) {
-    if (checkbox.textElement.textContent === 'Performance' ||
+  const containerElement = AuditsTestRunner.getContainerElement();
+  const checkboxes = containerElement.querySelectorAll('.checkbox');
+  for (const checkbox of checkboxes) {
+    if (checkbox.textElement.textContent === 'Progressive Web App' ||
         checkbox.textElement.textContent === 'Clear storage')
       continue;
 
@@ -21,7 +21,7 @@
   AuditsTestRunner.dumpStartAuditState();
   AuditsTestRunner.getRunButton().click();
 
-  var results = await AuditsTestRunner.waitForResults();
+  const results = await AuditsTestRunner.waitForResults();
   TestRunner.addResult(`\n=============== Audits run ===============`);
   TestRunner.addResult(Object.keys(results.audits).sort().join('\n'));
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/audits/audits-view-trace-run-expected.txt b/third_party/blink/web_tests/http/tests/devtools/audits/audits-view-trace-run-expected.txt
new file mode 100644
index 0000000..1101a72
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/audits/audits-view-trace-run-expected.txt
@@ -0,0 +1,57 @@
+Tests that audits panel renders View Trace button.
+
+
+========== Audits Start Audit State ==========
+[x] Performance
+[ ] Progressive Web App
+[ ] Best practices
+[ ] Accessibility
+[ ] SEO
+[x] Clear storage
+Run audits: enabled visible
+
+=============== Audits run ===============
+bootup-time
+critical-request-chains
+diagnostics
+dom-size
+efficient-animated-content
+estimated-input-latency
+final-screenshot
+first-contentful-paint
+first-cpu-idle
+first-meaningful-paint
+font-display
+interactive
+main-thread-tasks
+mainthread-work-breakdown
+max-potential-fid
+metrics
+network-requests
+network-rtt
+network-server-latency
+offscreen-images
+performance-budget
+redirects
+render-blocking-resources
+resource-summary
+screenshot-thumbnails
+speed-index
+third-party-summary
+time-to-first-byte
+total-blocking-time
+total-byte-weight
+unminified-css
+unminified-javascript
+unused-css-rules
+user-timings
+uses-long-cache-ttl
+uses-optimized-images
+uses-rel-preconnect
+uses-rel-preload
+uses-responsive-images
+uses-text-compression
+uses-webp-images
+
+Showing view: timeline
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/audits/audits-view-trace-run.js b/third_party/blink/web_tests/http/tests/devtools/audits/audits-view-trace-run.js
new file mode 100644
index 0000000..494f7df
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/audits/audits-view-trace-run.js
@@ -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.
+
+(async function() {
+  TestRunner.addResult('Tests that audits panel renders View Trace button.\n');
+
+  await TestRunner.loadModule('audits_test_runner');
+  await TestRunner.showPanel('audits');
+
+  const containerElement = AuditsTestRunner.getContainerElement();
+  const checkboxes = containerElement.querySelectorAll('.checkbox');
+  for (const checkbox of checkboxes) {
+    if (checkbox.textElement.textContent === 'Performance' ||
+        checkbox.textElement.textContent === 'Clear storage')
+      continue;
+
+    checkbox.checkboxElement.click();
+  }
+
+  AuditsTestRunner.dumpStartAuditState();
+  AuditsTestRunner.getRunButton().click();
+
+  const results = await AuditsTestRunner.waitForResults();
+  TestRunner.addResult(`\n=============== Audits run ===============`);
+  TestRunner.addResult(Object.keys(results.audits).sort().join('\n'));
+
+  const waitForShowView = new Promise(resolve => {
+    TestRunner.addSniffer(UI.ViewManager.prototype, 'showView', resolve);
+  });
+  const viewTraceButton = AuditsTestRunner.getResultsElement().querySelector('.view-trace');
+  viewTraceButton.click();
+  const viewShown = await waitForShowView;
+  TestRunner.addResult(`\nShowing view: ${viewShown}`);
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-expected.txt b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-expected.txt
index e0c3b3f..704660a 100644
--- a/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-expected.txt
@@ -3,6 +3,7 @@
 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.77.34.5 Safari/537.36
 Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
 GoogleChrome/99.77.34.5 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36
+Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.77.34.5 Safari/537.36 Edg/99.0.100.0
 
 Manually setting custom user agent
 foobar with %s inside
diff --git a/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-major-version-expected.txt b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-major-version-expected.txt
new file mode 100644
index 0000000..58adb44
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-major-version-expected.txt
@@ -0,0 +1,50 @@
+Test user agent setting
+
+Detected Chrome user agent version: 99.77.34.5
+Generated app version: 99.0.100.0
+Android (4.0.2) Browser — Galaxy Nexus: PASSED
+Android (2.3) Browser — Nexus S: PASSED
+BlackBerry — BB10: PASSED
+BlackBerry — PlayBook 2.1: PASSED
+BlackBerry — 9900: PASSED
+Chrome — Android Mobile: PASSED
+Chrome — Android Tablet: PASSED
+Chrome — iPhone: PASSED
+Chrome — iPad: PASSED
+Chrome — Chrome OS: PASSED
+Chrome — Mac: PASSED
+Chrome — Windows: PASSED
+Firefox — Android Mobile: PASSED
+Firefox — Android Tablet: PASSED
+Firefox — iPhone: PASSED
+Firefox — iPad: PASSED
+Firefox — Mac: PASSED
+Firefox — Windows: PASSED
+Googlebot: PASSED
+Googlebot Smartphone: PASSED
+Internet Explorer 11: PASSED
+Internet Explorer 10: PASSED
+Internet Explorer 9: PASSED
+Internet Explorer 8: PASSED
+Internet Explorer 7: PASSED
+Microsoft Edge (Chromium) — Windows: PASSED
+Microsoft Edge (Chromium) — Mac: PASSED
+Microsoft Edge — iPhone: PASSED
+Microsoft Edge — iPad: PASSED
+Microsoft Edge — Android Mobile: PASSED
+Microsoft Edge — Android Tablet: PASSED
+Microsoft Edge (EdgeHTML) — Windows: PASSED
+Microsoft Edge (EdgeHTML) — XBox: PASSED
+Opera — Mac: PASSED
+Opera — Windows: PASSED
+Opera (Presto) — Mac: PASSED
+Opera (Presto) — Windows: PASSED
+Opera Mobile — Android Mobile: PASSED
+Opera Mini — iOS: PASSED
+Safari — iPad iOS 9: PASSED
+Safari — iPhone iOS 9: PASSED
+Safari — Mac: PASSED
+UC Browser — Android Mobile: PASSED
+UC Browser — iOS: PASSED
+UC Browser — Windows Phone: PASSED
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-major-version.js b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-major-version.js
new file mode 100644
index 0000000..7804c4d
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-major-version.js
@@ -0,0 +1,60 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(`Test user agent setting\n`);
+  await TestRunner.loadModule('network_test_runner');
+  await TestRunner.showPanel('network');
+
+  const chromeRegex = new RegExp('(?:^|\\W)Chrome/(\\S+)');
+  const chromeUserAgentVersion = navigator.userAgent.match(chromeRegex)[1];
+  const additionalAppVersion = chromeUserAgentVersion.split('.', 1)[0] + '.0.100.0';
+
+  TestRunner.addResult('Detected Chrome user agent version: ' + chromeUserAgentVersion);
+  TestRunner.addResult('Generated app version: ' + additionalAppVersion);
+
+  for (const userAgentDescriptor of Network.NetworkConfigView._userAgentGroups) {
+    for (const userAgentVersion of userAgentDescriptor.values) {
+
+      function failTest(reason) {
+        TestRunner.addResult(userAgentVersion.title + ': FAILED TEST because ' + reason);
+        TestRunner.addResult('=== DO NOT COMMIT THIS INTO -expected.txt ===');
+        TestRunner.completeTest();
+      }
+
+      // Split the original user agent string by %s
+      //  If the returned array has length === 1, then no subsitutions will occur
+      //  If the returned array has length === 2, then Chrome user agent version is substituted
+      //  If the returned array has length === 3, then validate Chrome user agent version, and
+      //                                          the generated app version are subsituted (in that order)
+      //  Otherwise fail the test
+      const splitUserAgentVersion = userAgentVersion.value.split('%s');
+
+      if (splitUserAgentVersion.length > 3)
+        failTest('Too many %s in user agent string.');
+
+      let testPatchedUserAgentVersion = splitUserAgentVersion[0];
+
+      if (splitUserAgentVersion.length >= 2) {
+        // The first split user agent must end with either Chrome/ or CriOS/
+        if (!(testPatchedUserAgentVersion.endsWith('Chrome/') || testPatchedUserAgentVersion.endsWith('CriOS/')))
+          failTest('First %s match was not prefixed with either Chrome/ or CriOS/');
+
+        testPatchedUserAgentVersion += chromeUserAgentVersion + splitUserAgentVersion[1];
+      }
+
+      if (splitUserAgentVersion.length === 3)
+          testPatchedUserAgentVersion += additionalAppVersion + splitUserAgentVersion[2];
+
+      const patchedUserAgentVersion = SDK.MultitargetNetworkManager.patchUserAgentWithChromeVersion(userAgentVersion.value);
+
+      if (patchedUserAgentVersion !== testPatchedUserAgentVersion)
+          failTest('Computed user agent strings are not equal.');
+      else
+        TestRunner.addResult(userAgentVersion.title + ': PASSED');
+    }
+  }
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/user-agent-setting.js b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting.js
index 9fce0c6..f580ea6 100644
--- a/third_party/blink/web_tests/http/tests/devtools/user-agent-setting.js
+++ b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting.js
@@ -10,6 +10,7 @@
     'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36',
     'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0',
     'GoogleChrome/%s Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36',
+    'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36 Edg/%s',
   ];
 
   for (var i = 0; i < cases.length; i++) {
diff --git a/third_party/blink/web_tests/virtual/scheduler/README.md b/third_party/blink/web_tests/virtual/scheduler/README.md
new file mode 100644
index 0000000..b888ae1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scheduler/README.md
@@ -0,0 +1,3 @@
+This test suite is for testing that window.scheduler runs tasks in strict
+priority order, which currently depends on the
+BlinkSchedulerDisableAntiStarvationForPriorities feature.
diff --git a/third_party/blink/web_tests/virtual/scheduler/wpt_internal/scheduler/README.txt b/third_party/blink/web_tests/virtual/scheduler/wpt_internal/scheduler/README.txt
new file mode 100644
index 0000000..6690113
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scheduler/wpt_internal/scheduler/README.txt
@@ -0,0 +1,3 @@
+This tests a scheduler.postTask feature. It runs tests in wpt_internal/scheduler
+directory with the BlinkSchedulerDisableAntiStarvationForPriorities flag, which
+guarantees tasks are run in strict priority order.
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index b534cf5d..4be7677 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -7427,6 +7427,11 @@
     getter zoomAndPan
     method constructor
     setter zoomAndPan
+interface Scheduler
+    attribute @@toStringTag
+    method constructor
+    method getTaskQueue
+    method postTask
 interface Scheduling
     attribute @@toStringTag
     method constructor
@@ -7789,6 +7794,12 @@
     method constructor
     method getTags
     method register
+interface Task
+    attribute @@toStringTag
+    getter priority
+    getter status
+    method cancel
+    method constructor
 interface TaskAttributionTiming : PerformanceEntry
     attribute @@toStringTag
     getter containerId
@@ -7797,6 +7808,11 @@
     getter containerType
     method constructor
     method toJSON
+interface TaskQueue
+    attribute @@toStringTag
+    getter priority
+    method constructor
+    method postTask
 interface Text : CharacterData
     attribute @@toStringTag
     getter assignedSlot
@@ -11254,6 +11270,7 @@
     getter performance
     getter personalbar
     getter portalHost
+    getter scheduler
     getter screen
     getter screenLeft
     getter screenTop
@@ -11449,6 +11466,7 @@
     setter pageYOffset
     setter performance
     setter personalbar
+    setter scheduler
     setter screen
     setter screenLeft
     setter screenTop
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/global_task_queues.html b/third_party/blink/web_tests/wpt_internal/scheduler/global_task_queues.html
new file mode 100644
index 0000000..08e68bbc
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/global_task_queues.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<title>Scheduling API: Global Task Queues</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  function* priorityGenerator() {
+    let priorities = [
+      "immediate", "high", "default", "low", "idle"
+    ];
+    for (let i = 0; i < priorities.length; i++)
+      yield priorities[i];
+  }
+
+  function testTaskQueue(priority) {
+    let tq = scheduler.getTaskQueue(priority);
+    assert_equals(tq.priority, priority);
+
+    let task = tq.postTask(t.step_func(() => {
+      nextTaskQueue();
+    }));
+    assert_equals(task.priority, priority);
+  }
+
+  let nextPriority = priorityGenerator();
+
+  function nextTaskQueue() {
+    let next = nextPriority.next();
+    if (next.done) {
+      t.done();
+      return;
+    }
+    testTaskQueue(next.value);
+  }
+
+  // Schedule a task to kick things off.
+  scheduler.postTask(t.step_func(() => {
+    nextTaskQueue();
+  }));
+}, 'Basic functionality for the global TaskQueues returned by scheduler.taskQueue');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/post_delayed_task.html b/third_party/blink/web_tests/wpt_internal/scheduler/post_delayed_task.html
new file mode 100644
index 0000000..0ec9794
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/post_delayed_task.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Scheduling API: Post Delayed Tasks</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  let now = performance.now();
+    scheduler.postTask(t.step_func(() => {
+      let elapsed = performance.now() - now;
+      assert_greater_than_equal(elapsed, 10);
+      t.done();
+    }), { priority: 'immediate', delay: 10 });
+}, 'Tests basic scheduler.postTask with a delay');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_arguments.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_arguments.html
new file mode 100644
index 0000000..a620676
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_arguments.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>Scheduling API: Task Argument Passing</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  scheduler.postTask(t.step_func((arg1, arg2) => {
+    assert_equals(arg1, 'foo');
+    assert_equals(arg2, 10);
+    t.done();
+  }), { priority: 'default' }, 'foo', 10);
+}, 'Test scheduler.postTask correctly passes arguments');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_cancellation.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_cancellation.html
new file mode 100644
index 0000000..db340643
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_cancellation.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<title>Scheduling API: Task Cancellation</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  let result = '';
+  let tasks = [];
+
+  for (let i = 0; i < 5; i++) {
+    let task = scheduler.postTask(() => {
+      result += i.toString();
+    });
+    tasks.push(task);
+  }
+
+  tasks[2].cancel();
+  assert_equals(tasks[2].status, 'canceled');
+
+  scheduler.postTask(t.step_func(() => {
+    assert_equals(result, '0134');
+  }));
+
+  // Check that canceling running, completed, or canceled tasks is a no-op.
+  let task = scheduler.postTask(t.step_func(() => {
+    assert_equals(task.status, 'running');
+    task.cancel();
+    assert_equals(task.status, 'running');
+
+    assert_equals(tasks[2].status, 'canceled');
+    tasks[2].cancel();
+    assert_equals(tasks[2].status, 'canceled');
+
+    assert_equals(tasks[0].status, 'completed');
+    tasks[0].cancel();
+    assert_equals(tasks[0].status, 'completed');
+
+    t.done();
+  }));
+
+}, 'Test canceling a task');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_lifecycle.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_lifecycle.html
new file mode 100644
index 0000000..a81aaefc
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_lifecycle.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>Scheduling API: Task Lifecycle</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  let task = scheduler.postTask(t.step_func(() => {
+    assert_equals(task.status, 'running');
+    scheduler.postTask(t.step_func(() => {
+      assert_equals(task.status, 'completed');
+      t.done();
+    }));
+  }));
+  assert_equals(task.priority, 'default');
+  assert_equals(task.status, 'pending');
+
+}, 'Test scheduler.postTask Task lifecycle');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_order.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_order.html
new file mode 100644
index 0000000..00ec73ae
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_order.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<title>Scheduling API: Tasks Run in Priority Order</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  let result = '';
+
+  function report(id) {
+    result = result === '' ? id : result + ' ' + id;
+  }
+
+  function scheduleReportTask(id, priority) {
+    scheduler.postTask(() => {
+      report(id);
+    }, { priority });
+  }
+
+  // Post tasks in reverse priority order and expect they are run from highest
+  // to lowest priority.
+  scheduleReportTask('E1', 'idle');
+  scheduleReportTask('E2', 'idle');
+  scheduleReportTask('L1', 'low');
+  scheduleReportTask('L2', 'low');
+  scheduleReportTask('D1', 'default');
+  scheduleReportTask('D2', 'default');
+  scheduleReportTask('H1', 'high');
+  scheduleReportTask('H2', 'high');
+  scheduleReportTask('I1', 'immediate');
+  scheduleReportTask('I2', 'immediate');
+
+  scheduler.postTask(t.step_func(() => {
+    assert_equals(result, 'I1 I2 H1 H2 D1 D2 L1 L2 E1 E2');
+    t.done();
+  }), { priority: 'idle' });
+}, 'Test scheduler.postTask task run in priority order');
+
+</script>
diff --git a/third_party/jacoco/jacoco_instrument.flags b/third_party/jacoco/jacoco_instrument.flags
index ceab228..462f28a0 100644
--- a/third_party/jacoco/jacoco_instrument.flags
+++ b/third_party/jacoco/jacoco_instrument.flags
@@ -1,3 +1,8 @@
 # This package is only used when Java coverage is enabled.
 # JaCoCo libraries contain all dependencies so we can ignore warning here.
 -dontwarn java.lang.instrument.**
+
+# Keep everything for the jacoco classes.
+-keep class com.vladium.** {*;}
+-keep class org.jacoco.** {*;}
+-dontwarn org.jacoco.**
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 34bae93..c87550e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -48137,6 +48137,13 @@
   <int value="2" label="Wrong prediction"/>
 </enum>
 
+<enum name="PrefetchRedirect">
+  <int value="0" label="Prefetch request made"/>
+  <int value="1" label="Prefetch request was redirected normally"/>
+  <int value="2"
+      label="Prefetch request was redirected to Signed Exchange handler"/>
+</enum>
+
 <enum name="PrefetchStatus">
   <int value="0" label="undefined"/>
   <int value="1" label="success from cache"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 472ca2d..6c7bf63 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -24793,6 +24793,61 @@
   </summary>
 </histogram>
 
+<histogram name="CryptAuth.EnrollmentV2.ExecutionTime.ClientAppMetadataFetch"
+    units="ms" expires_after="2020-06-01">
+  <owner>nohle@chromium.org</owner>
+  <owner>better-together-dev@google.com</owner>
+  <summary>
+    Records the execution time of the async ClientAppMetadata retrieval during
+    the CryptAuth v2 Enrollment flow. Recorded when the async callback is
+    invoked or when the call times out.
+  </summary>
+</histogram>
+
+<histogram name="CryptAuth.EnrollmentV2.ExecutionTime.EnrollKeys" units="ms"
+    expires_after="2020-06-01">
+  <owner>nohle@chromium.org</owner>
+  <owner>better-together-dev@google.com</owner>
+  <summary>
+    Records the execution time of the async EnrollKeys API call to CryptAuth
+    during the CryptAuth v2 Enrollment flow. Recorded when the async callback is
+    invoked or when the call times out.
+  </summary>
+</histogram>
+
+<histogram name="CryptAuth.EnrollmentV2.ExecutionTime.GcmRegistration"
+    units="ms" expires_after="2020-06-01">
+  <owner>nohle@chromium.org</owner>
+  <owner>better-together-dev@google.com</owner>
+  <summary>
+    Records the execution time of the async GCM registration call during the
+    CryptAuth v2 Enrollment flow. Recorded when the async callback is invoked or
+    when the call times out.
+  </summary>
+</histogram>
+
+<histogram name="CryptAuth.EnrollmentV2.ExecutionTime.KeyCreation" units="ms"
+    expires_after="2020-06-01">
+  <owner>nohle@chromium.org</owner>
+  <owner>better-together-dev@google.com</owner>
+  <summary>
+    Records the execution time of the async key creation call during the
+    CryptAuth v2 Enrollment flow. Recorded when the async callback is invoked or
+    when the call times out.
+  </summary>
+</histogram>
+
+<histogram name="CryptAuth.EnrollmentV2.ExecutionTime.SyncKeys" units="ms"
+    expires_after="2020-06-01">
+  <owner>nohle@chromium.org</owner>
+  <owner>better-together-dev@google.com</owner>
+  <summary>
+    Records the execution time of the async SyncKeys API call to CryptAuth
+    during the CryptAuth v2 Enrollment flow. Recorded when the async callback is
+    invoked or when the call times out.
+  </summary>
+</histogram>
+
 <histogram name="CryptAuth.EnrollmentV2.InvocationReason"
     enum="CryptAuthV2EnrollmentInvocationReason" expires_after="2020-06-01">
   <owner>nohle@chromium.org</owner>
@@ -68272,6 +68327,26 @@
   </summary>
 </histogram>
 
+<histogram name="Navigation.CommitTimeout.ErrorCode" enum="NetErrorCodes"
+    expires_after="M82">
+  <owner>cduvall@chromium.org</owner>
+  <owner>cmumford@chromium.org</owner>
+  <summary>
+    Logs the error code for each navigation commit timeout. This will be used to
+    debug http://crbug.com/934317.
+  </summary>
+</histogram>
+
+<histogram name="Navigation.CommitTimeout.IsMainFrame" enum="BooleanMainFrame"
+    expires_after="M82">
+  <owner>cduvall@chromium.org</owner>
+  <owner>cmumford@chromium.org</owner>
+  <summary>
+    Logs whether this was a main frame navigation for each navigation commit
+    timeout. This will be used to debug http://crbug.com/934317.
+  </summary>
+</histogram>
+
 <histogram name="Navigation.CommitTimeout.IsRendererProcessReady"
     enum="BooleanReady" expires_after="M82">
   <owner>cduvall@chromium.org</owner>
@@ -82751,6 +82826,37 @@
   </summary>
 </histogram>
 
+<histogram name="NetworkService.TimeToFirstResponse" units="ms"
+    expires_after="M82">
+  <owner>cduvall@chromium.org</owner>
+  <owner>cmumford@chromium.org</owner>
+  <summary>
+    How long it takes the network service to respond to the first mojo call.
+    This will be used to debug http://crbug.com/934317.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.TimeToFirstResponse.AfterCrash" units="ms"
+    expires_after="M82">
+  <owner>cduvall@chromium.org</owner>
+  <owner>cmumford@chromium.org</owner>
+  <summary>
+    How long it takes the network service to respond to the first mojo call
+    after a network service crash. This will be used to debug
+    http://crbug.com/934317.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.TimeToFirstResponse.OnStartup" units="ms"
+    expires_after="M82">
+  <owner>cduvall@chromium.org</owner>
+  <owner>cmumford@chromium.org</owner>
+  <summary>
+    How long it takes the network service to respond to the first mojo call on
+    startup. This will be used to debug http://crbug.com/934317.
+  </summary>
+</histogram>
+
 <histogram
     name="NetworkService.URLLoader.RequestInitiatorOriginLockCompatibility"
     enum="RequestInitiatorOriginLockCompatibility" expires_after="2020-06-30">
@@ -105031,6 +105137,22 @@
   </summary>
 </histogram>
 
+<histogram name="Prefetch.Redirect" enum="PrefetchRedirect" expires_after="M83">
+  <owner>dom@chromium.org</owner>
+  <owner>yhirano@chromium.org</owner>
+  <owner>yoavweiss@chromium.org</owner>
+  <summary>
+    Recorded when a prefetch request is made, and when a prefetch request is
+    redirected. Specifically, PrefetchURLLoader::PrefetchURLLoader records
+    &quot;Prefetch request made&quot;, and PrefetchURLLoader::FollowRedirect
+    records one of the two redirection values. This is to collect data on how
+    often prefetch requests experience redirects. Note that when the
+    PrefetchRedirectError flag is enabled, no events are recorded. We do not
+    want to interact with this histogram when we're experimenting with the
+    redirect mode for prefetch requests.
+  </summary>
+</histogram>
+
 <histogram name="PrefetchedSignedExchangeCache.BodySize" units="bytes"
     expires_after="2019-12-31">
   <owner>horo@chromium.org</owner>
diff --git a/tools/perf/PRESUBMIT.py b/tools/perf/PRESUBMIT.py
index 26dc65c..4984d27e 100644
--- a/tools/perf/PRESUBMIT.py
+++ b/tools/perf/PRESUBMIT.py
@@ -40,6 +40,8 @@
   chromium_src_dir = input_api.os_path.join(perf_dir, '..', '..')
   telemetry_dir = input_api.os_path.join(
       chromium_src_dir, 'third_party', 'catapult', 'telemetry')
+  typ_dir = input_api.os_path.join(
+       chromium_src_dir, 'third_party', 'catapult', 'third_party', 'typ')
   experimental_dir = input_api.os_path.join(
       chromium_src_dir, 'third_party', 'catapult', 'experimental')
   tracing_dir = input_api.os_path.join(
@@ -50,6 +52,7 @@
       chromium_src_dir, 'build', 'android')
   return [
       telemetry_dir,
+      typ_dir,
       input_api.os_path.join(telemetry_dir, 'third_party', 'mock'),
       experimental_dir,
       tracing_dir,
diff --git a/tools/perf/contrib/cros_benchmarks/page_cycler_v2.py b/tools/perf/contrib/cros_benchmarks/page_cycler_v2.py
deleted file mode 100644
index 3df2831..0000000
--- a/tools/perf/contrib/cros_benchmarks/page_cycler_v2.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""The page cycler v2.
-
-For details, see design doc:
-https://docs.google.com/document/d/1EZQX-x3eEphXupiX-Hq7T4Afju5_sIdxPWYetj7ynd0
-"""
-
-from core import perf_benchmark
-import page_sets
-
-from benchmarks import loading_metrics_category
-from telemetry import benchmark
-from telemetry.web_perf import timeline_based_measurement
-
-class _PageCyclerV2(perf_benchmark.PerfBenchmark):
-  options = {'pageset_repeat': 2}
-
-  def CreateCoreTimelineBasedMeasurementOptions(self):
-    tbm_options = timeline_based_measurement.Options()
-    loading_metrics_category.AugmentOptionsForLoadingMetrics(tbm_options)
-    return tbm_options
-
-
-@benchmark.Info(emails=['kouhei@chromium.org', 'ksakamoto@chromium.org'])
-class PageCyclerV2Typical25(_PageCyclerV2):
-  """Page load time benchmark for a 25 typical web pages.
-
-  Designed to represent typical, not highly optimized or highly popular web
-  sites. Runs against pages recorded in June, 2014.
-  """
-
-  @classmethod
-  def Name(cls):
-    return 'page_cycler_v2.typical_25'
-
-  def CreateStorySet(self, options):
-    return page_sets.Typical25PageSet()
-
-
-@benchmark.Info(emails=['kouhei@chromium.org', 'ksakamoto@chromium.org'])
-class PageCyclerV2IntlArFaHe(_PageCyclerV2):
-  """Page load time for a variety of pages in Arabic, Farsi and Hebrew.
-
-  Runs against pages recorded in April, 2013.
-  """
-  page_set = page_sets.IntlArFaHePageSet
-
-  @classmethod
-  def Name(cls):
-    return 'page_cycler_v2.intl_ar_fa_he'
-
-  def CreateStorySet(self, options):
-    return page_sets.IntlArFaHePageSet()
-
-
-@benchmark.Info(emails=['kouhei@chromium.org', 'ksakamoto@chromium.org'])
-class PageCyclerV2IntlEsFrPtBr(_PageCyclerV2):
-  """Page load time for a pages in Spanish, French and Brazilian Portuguese.
-
-  Runs against pages recorded in April, 2013.
-  """
-  page_set = page_sets.IntlEsFrPtBrPageSet
-
-  @classmethod
-  def Name(cls):
-    return 'page_cycler_v2.intl_es_fr_pt-BR'
-
-  def CreateStorySet(self, options):
-    return page_sets.IntlEsFrPtBrPageSet()
-
-
-@benchmark.Info(emails=['kouhei@chromium.org', 'ksakamoto@chromium.org'])
-class PageCyclerV2IntlHiRu(_PageCyclerV2):
-  """Page load time benchmark for a variety of pages in Hindi and Russian.
-
-  Runs against pages recorded in April, 2013.
-  """
-  page_set = page_sets.IntlHiRuPageSet
-
-  @classmethod
-  def Name(cls):
-    return 'page_cycler_v2.intl_hi_ru'
-
-  def CreateStorySet(self, options):
-    return page_sets.IntlHiRuPageSet()
-
-
-@benchmark.Info(emails=['kouhei@chromium.org', 'ksakamoto@chromium.org'])
-class PageCyclerV2IntlJaZh(_PageCyclerV2):
-  """Page load time benchmark for a variety of pages in Japanese and Chinese.
-
-  Runs against pages recorded in April, 2013.
-  """
-
-  @classmethod
-  def Name(cls):
-    return 'page_cycler_v2.intl_ja_zh'
-
-  def CreateStorySet(self, options):
-    return page_sets.IntlJaZhPageSet()
-
-
-@benchmark.Info(emails=['kouhei@chromium.org', 'ksakamoto@chromium.org'])
-class PageCyclerV2IntlKoThVi(_PageCyclerV2):
-  """Page load time for a variety of pages in Korean, Thai and Vietnamese.
-
-  Runs against pages recorded in April, 2013.
-  """
-  page_set = page_sets.IntlKoThViPageSet
-
-  @classmethod
-  def Name(cls):
-    return 'page_cycler_v2.intl_ko_th_vi'
-
-  def CreateStorySet(self, options):
-    return page_sets.IntlKoThViPageSet()
diff --git a/tools/perf/core/benchmark_runner.py b/tools/perf/core/benchmark_runner.py
index dd9eb76..654702b9 100644
--- a/tools/perf/core/benchmark_runner.py
+++ b/tools/perf/core/benchmark_runner.py
@@ -14,9 +14,9 @@
 from telemetry import command_line
 
 
-def main(config):
+def main(config, args=None):
   options = command_line.ParseArgs(
-      environment=config,
+      environment=config, args=args,
       results_arg_parser=results_processor.ArgumentParser())
   results_processor.ProcessOptions(options)
   run_return_code = command_line.RunCommand(options)
diff --git a/tools/perf/core/benchmark_runner_unittest.py b/tools/perf/core/benchmark_runner_unittest.py
new file mode 100644
index 0000000..fc2b2f33
--- /dev/null
+++ b/tools/perf/core/benchmark_runner_unittest.py
@@ -0,0 +1,38 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import mock
+
+from core import benchmark_runner
+
+
+def _FakeParseArgs(environment, args, results_arg_parser):
+  del environment  # Unused.
+  options, _ = results_arg_parser.parse_known_args(args)
+  return options
+
+
+class BenchmarkRunnerUnittest(unittest.TestCase):
+  def testMain_returnCode(self):
+    """Test that benchmark_runner.main() respects return code from Telemetry."""
+    # TODO(crbug.com/985712): Ideally we should write a more "integration" kind
+    # of test, where we don't mock out the Telemetry command line. This is
+    # hard to do now, however, because we need a way to convert the browser
+    # selection done by the test runner, back to a suitable --browser arg for
+    # the command line below. Namely, we need an equivalent of
+    # options_for_unittests.GetCopy() that returns a list of string args
+    # rather than the parsed options object.
+    config = mock.Mock()
+    with mock.patch('core.benchmark_runner.command_line') as telemetry_cli:
+      telemetry_cli.ParseArgs.side_effect = _FakeParseArgs
+      telemetry_cli.RunCommand.return_value = 42
+
+      # Note: For now we pass `--output-format none` and a non-existent output
+      # dir to prevent the results processor from processing any results.
+      return_code = benchmark_runner.main(config, [
+          'run', 'some.benchmark', '--browser', 'stable',
+          '--output-dir', '/does/not/exist', '--output-format', 'none'])
+      self.assertEqual(return_code, 42)
diff --git a/tools/perf/core/story_expectation_validator.py b/tools/perf/core/story_expectation_validator.py
index 66013a7..1d32fc0 100755
--- a/tools/perf/core/story_expectation_validator.py
+++ b/tools/perf/core/story_expectation_validator.py
@@ -6,6 +6,7 @@
 
 import argparse
 import json
+import logging
 import os
 
 from core import benchmark_utils
@@ -14,6 +15,8 @@
 path_util.AddTelemetryToPath()
 path_util.AddAndroidPylibToPath()
 
+from typ import expectations_parser as typ_expectations_parser
+
 
 CLUSTER_TELEMETRY_DIR = os.path.join(
     path_util.GetChromiumSrcDir(), 'tools', 'perf', 'contrib',
@@ -23,16 +26,19 @@
     benchmark_finders.GetBenchmarksInSubDirectory(CLUSTER_TELEMETRY_DIR)
 ]
 
-
-def validate_story_names(benchmarks, raw_expectations_data):
+def validate_story_names(benchmarks, test_expectations):
+  stories = []
   for benchmark in benchmarks:
     if benchmark.Name() in CLUSTER_TELEMETRY_BENCHMARKS:
       continue
-    b = benchmark()
-    b.AugmentExpectationsWithParser(raw_expectations_data)
-    story_set = benchmark_utils.GetBenchmarkStorySet(b)
-    failed_stories = b.GetBrokenExpectations(story_set)
-    assert not failed_stories, 'Incorrect story names: %s' % str(failed_stories)
+    story_set = benchmark_utils.GetBenchmarkStorySet(benchmark())
+    stories.extend([benchmark.Name() + '/' + s.name for s in story_set.stories])
+  broken_expectations = test_expectations.check_for_broken_expectations(stories)
+  unused_patterns = ''
+  for pattern in set([e.test for e in broken_expectations]):
+    unused_patterns += ("Expectations with pattern '%s'"
+                        " do not apply to any stories\n" % pattern)
+  assert not unused_patterns, unused_patterns
 
 
 def GetDisabledStories(benchmarks, raw_expectations_data):
@@ -73,9 +79,14 @@
   benchmarks = benchmark_finders.GetAllBenchmarks()
   with open(path_util.GetExpectationsPath()) as fp:
     raw_expectations_data = fp.read()
+  test_expectations = typ_expectations_parser.TestExpectations()
+  ret, msg = test_expectations.parse_tagged_list(raw_expectations_data)
+  if ret:
+    logging.error(msg)
+    return ret
   if options.list:
     stories = GetDisabledStories(benchmarks, raw_expectations_data)
     print json.dumps(stories, sort_keys=True, indent=4, separators=(',', ': '))
   else:
-    validate_story_names(benchmarks, raw_expectations_data)
+    validate_story_names(benchmarks, test_expectations)
   return 0
diff --git a/tools/perf/core/story_expectation_validator_unittest.py b/tools/perf/core/story_expectation_validator_unittest.py
index 702a3b9..52ece0ea 100644
--- a/tools/perf/core/story_expectation_validator_unittest.py
+++ b/tools/perf/core/story_expectation_validator_unittest.py
@@ -8,6 +8,8 @@
 from telemetry import benchmark
 from telemetry import story
 
+from typ import expectations_parser as typ_expectations_parser
+
 class FakePage(object):
   def __init__(self, name):
     self._name = name
@@ -40,32 +42,26 @@
 
 class StoryExpectationValidatorTest(unittest.TestCase):
   def testValidateStoryInValidName(self):
-    raw_expectations = '# tags: Mac\ncrbug.com/123 [ Mac ] b1/s1 [ Skip ]'
+    raw_expectations = ('# tags: [ Mac ]\n'
+                        '# results: [ Skip ]\n'
+                        'crbug.com/123 [ Mac ] b1/s1 [ Skip ]\n')
+    test_expectations = typ_expectations_parser.TestExpectations()
+    ret, _ = test_expectations.parse_tagged_list(raw_expectations)
+    self.assertFalse(ret)
     benchmarks = [FakeBenchmark]
     with self.assertRaises(AssertionError):
       story_expectation_validator.validate_story_names(
-          benchmarks, raw_expectations)
+          benchmarks, test_expectations)
 
   def testValidateStoryValidName(self):
-    raw_expectations = '# tags: Mac\ncrbug.com/123 [ Mac ] b1/One [ Skip ]'
+    raw_expectations = ('# tags: [ Mac] \n'
+                        '# results: [ Skip ]\n'
+                        'crbug.com/123 [ Mac ] b1/One [ Skip ]\n')
+    test_expectations = typ_expectations_parser.TestExpectations()
+    ret, _ = test_expectations.parse_tagged_list(raw_expectations)
+    self.assertFalse(ret)
     benchmarks = [FakeBenchmark]
     # If a name is invalid, an exception is thrown. If no exception is thrown
     # all story names are valid. That is why there is no assert here.
     story_expectation_validator.validate_story_names(
-        benchmarks, raw_expectations)
-
-  def testGetDisabledStoriesWithExpectationsData(self):
-    raw_expectations = '# tags: Mac\ncrbug.com/123 [ Mac ] b1/One [ Skip ]'
-    benchmarks = [FakeBenchmark]
-    results = story_expectation_validator.GetDisabledStories(
-        benchmarks, raw_expectations)
-    expected = {'b1': {'One': [(['Mac'], 'crbug.com/123')]}}
-    self.assertEqual(expected, results)
-
-  def testGetDisabledStoriesWithoutMatchingExpectationsData(self):
-    raw_expectations = '# tags: Mac\ncrbug.com/123 [ Mac ] b2/One [ Skip ]'
-    benchmarks = [FakeBenchmark]
-    results = story_expectation_validator.GetDisabledStories(
-        benchmarks, raw_expectations)
-    expected = { 'b1': {}}
-    self.assertEqual(expected, results)
+        benchmarks, test_expectations)
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index fd6828b..0526c656 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -2,434 +2,436 @@
 # Instructions of how to use this file:
 # https://chromium.googlesource.com/chromium/src/+/master/docs/speed/bot_health_sheriffing/how_to_disable_a_story.md
 
-# tags: All Android_Go Android_One Android_Svelte Android_Low_End
-# tags: Android_Webview Android_but_not_webview Mac Win Linux
-# tags: ChromeOS Android Desktop Nexus_5 Nexus_5X Nexus_6P
-# tags: Nexus_7 Mac_10.11 Mac_10.12 Nexus6_Webview Nexus5X_Webview
-# tags: Pixel_2 Win_7 Win_10 Android_Go_Webview Pixel2_Webview
+# tags: [ android android-go android-low-end android-nexus-5 android-nexus-5x
+#         android-nexus-6 android-pixel-2 chromeos desktop linux mac mac-10.12
+#         win win10 win7 ]
+# tags: [ android-not-webview android-webview ]
+# results: [ Skip ]
+# conflicts_allowed: True
 
 # Benchmark: blink_perf.bindings
-crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/structured-clone-json-deserialize.html [ Skip ]
-crbug.com/910207 [ Nexus_5X ] blink_perf.bindings/structured-clone-json-deserialize.html [ Skip ]
-crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/structured-clone-json-serialize.html [ Skip ]
-crbug.com/910207 [ Nexus_5X ] blink_perf.bindings/structured-clone-json-serialize.html [ Skip ]
-crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
-crbug.com/910207 [ Nexus_5X ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
-crbug.com/931780 [ Android_Webview ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
-crbug.com/893209 [ Android_Webview ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
-crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
-crbug.com/910207 [ Nexus_5X ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
-crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/worker-structured-clone-json-roundtrip.html [ Skip ]
-crbug.com/910207 [ Nexus_5X ] blink_perf.bindings/worker-structured-clone-json-roundtrip.html [ Skip ]
-crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/worker-structured-clone-json-to-worker.html [ Skip ]
-crbug.com/882881 [ Nexus_5X ] blink_perf.bindings/worker-structured-clone-json-to-worker.html [ Skip ]
-crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/worker-structured-clone-json-from-worker.html [ Skip ]
-crbug.com/882881 [ Nexus_5X ] blink_perf.bindings/worker-structured-clone-json-from-worker.html [ Skip ]
-crbug.com/865400 [ Pixel_2 ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
-crbug.com/865400 [ Pixel_2 ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
+crbug.com/882881 [ android-nexus-5 ] blink_perf.bindings/structured-clone-json-deserialize.html [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] blink_perf.bindings/structured-clone-json-deserialize.html [ Skip ]
+crbug.com/882881 [ android-nexus-5 ] blink_perf.bindings/structured-clone-json-serialize.html [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] blink_perf.bindings/structured-clone-json-serialize.html [ Skip ]
+crbug.com/882881 [ android-nexus-5 ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
+crbug.com/931780 [ android-webview ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
+crbug.com/893209 [ android-webview ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
+crbug.com/882881 [ android-nexus-5 ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
+crbug.com/882881 [ android-nexus-5 ] blink_perf.bindings/worker-structured-clone-json-roundtrip.html [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] blink_perf.bindings/worker-structured-clone-json-roundtrip.html [ Skip ]
+crbug.com/882881 [ android-nexus-5 ] blink_perf.bindings/worker-structured-clone-json-to-worker.html [ Skip ]
+crbug.com/882881 [ android-nexus-5x ] blink_perf.bindings/worker-structured-clone-json-to-worker.html [ Skip ]
+crbug.com/882881 [ android-nexus-5 ] blink_perf.bindings/worker-structured-clone-json-from-worker.html [ Skip ]
+crbug.com/882881 [ android-nexus-5x ] blink_perf.bindings/worker-structured-clone-json-from-worker.html [ Skip ]
+crbug.com/865400 [ android-pixel-2 ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
+crbug.com/865400 [ android-pixel-2 ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
 
 # Benchmark: blink_perf.canvas
-crbug.com/593973 [ Android_Svelte ] blink_perf.canvas/* [ Skip ]
-crbug.com/784540 [ Nexus_5 ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
-crbug.com/784540 [ Nexus_5X ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
-crbug.com/784540 [ Nexus_5 ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
-crbug.com/784540 [ Nexus_5X ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
-crbug.com/978500 [ Pixel2_Webview ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
-crbug.com/978500 [ Nexus5X_Webview ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
-crbug.com/978500 [ Pixel2_Webview ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
-crbug.com/978500 [ Android ] blink_perf.canvas/draw-dynamic-webgl-to-hw-accelerated-canvas-2d.html [ Skip ]
-crbug.com/978500 [ Android_Webview ] blink_perf.canvas/draw-dynamic-webgl-to-hw-accelerated-canvas-2d.html [ Skip ]
-crbug.com/978500 [ Nexus_5 ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
-crbug.com/978500 [ Nexus_5 ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
-crbug.com/978500 [ Nexus_5 ] blink_perf.canvas/draw-static-webgl-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
-crbug.com/978500 [ Nexus5X_Webview ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
-crbug.com/978500 [ Pixel2_Webview ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
-crbug.com/978500 [ Nexus5X_Webview ] blink_perf.canvas/draw-static-webgl-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
-crbug.com/978500 [ Pixel2_Webview ] blink_perf.canvas/draw-static-webgl-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
-crbug.com/853738 [ Android_Webview ] blink_perf.canvas/draw-video-to-hw-accelerated-canvas-2d.html [ Skip ]
-crbug.com/979829 [ Android ] blink_perf.canvas/draw-video-to-hw-accelerated-canvas-2d.html [ Skip ]
-crbug.com/853738 [ Android_Webview ] blink_perf.canvas/upload-video-to-sub-texture.html [ Skip ]
-crbug.com/853738 [ Android_Webview ] blink_perf.canvas/upload-video-to-texture.html [ Skip ]
-crbug.com/967809 [ Android_Webview ] blink_perf.canvas/draw-video-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
-crbug.com/967809 [ Android_Webview ] blink_perf.canvas/upload-video-to-sub-texture_RAF.html?RAF [ Skip ]
-crbug.com/967809 [ Android_Webview ] blink_perf.canvas/upload-video-to-texture_RAF.html?RAF [ Skip ]
-crbug.com/978159 [ Nexus_5X ] blink_perf.canvas/* [ Skip ]
-crbug.com/978159 [ Nexus5X_Webview ] blink_perf.canvas/* [ Skip ]
+crbug.com/593973 [ android-low-end ] blink_perf.canvas/* [ Skip ]
+crbug.com/784540 [ android-nexus-5 ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/784540 [ android-nexus-5x ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/784540 [ android-nexus-5 ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/784540 [ android-nexus-5x ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/978500 [ android-pixel-2 android-webview ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/978500 [ android-nexus-5x android-webview ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ android-pixel-2 android-webview ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ android ] blink_perf.canvas/draw-dynamic-webgl-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/978500 [ android-webview ] blink_perf.canvas/draw-dynamic-webgl-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/978500 [ android-nexus-5 ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ android-nexus-5 ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ android-nexus-5 ] blink_perf.canvas/draw-static-webgl-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ android-nexus-5x android-webview ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ android-pixel-2 android-webview ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ android-nexus-5x android-webview ] blink_perf.canvas/draw-static-webgl-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ android-pixel-2 android-webview ] blink_perf.canvas/draw-static-webgl-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/853738 [ android-webview ] blink_perf.canvas/draw-video-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/979829 [ android ] blink_perf.canvas/draw-video-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/853738 [ android-webview ] blink_perf.canvas/upload-video-to-sub-texture.html [ Skip ]
+crbug.com/853738 [ android-webview ] blink_perf.canvas/upload-video-to-texture.html [ Skip ]
+crbug.com/967809 [ android-webview ] blink_perf.canvas/draw-video-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/967809 [ android-webview ] blink_perf.canvas/upload-video-to-sub-texture_RAF.html?RAF [ Skip ]
+crbug.com/967809 [ android-webview ] blink_perf.canvas/upload-video-to-texture_RAF.html?RAF [ Skip ]
+crbug.com/978159 [ android-nexus-5x ] blink_perf.canvas/* [ Skip ]
+crbug.com/978159 [ android-nexus-5x android-webview ] blink_perf.canvas/* [ Skip ]
 
 # Benchmark: blink_perf.css
-crbug.com/891878 [ Nexus5X_Webview ] blink_perf.css/CustomPropertiesVarAlias.html [ Skip ]
-crbug.com/983728 [ All ] blink_perf.css/ClassDescendantSelector.html [ Skip ]
-crbug.com/983728 [ All ] blink_perf.css/ClassInvalidation.html [ Skip ]
-crbug.com/983728 [ All ] blink_perf.css/CustomPropertiesCascade.html [ Skip ]
-crbug.com/983728 [ All ] blink_perf.css/CustomPropertiesNonRootInheritance.html [ Skip ]
-crbug.com/983728 [ All ] blink_perf.css/CustomPropertiesRootInheritance.html [ Skip ]
-crbug.com/983728 [ All ] blink_perf.css/CustomPropertiesVarAlias.html [ Skip ]
-crbug.com/983728 [ All ] blink_perf.css/FocusUpdate.html [ Skip ]
-crbug.com/983728 [ All ] blink_perf.css/PseudoClassSelectors.html [ Skip ]
-crbug.com/983728 [ All ] blink_perf.css/SelectorCountScaling.html [ Skip ]
+crbug.com/891878 [ android-nexus-5x android-webview ] blink_perf.css/CustomPropertiesVarAlias.html [ Skip ]
+crbug.com/983728 blink_perf.css/ClassDescendantSelector.html [ Skip ]
+crbug.com/983728 blink_perf.css/ClassInvalidation.html [ Skip ]
+crbug.com/983728 blink_perf.css/CustomPropertiesCascade.html [ Skip ]
+crbug.com/983728 blink_perf.css/CustomPropertiesNonRootInheritance.html [ Skip ]
+crbug.com/983728 blink_perf.css/CustomPropertiesRootInheritance.html [ Skip ]
+crbug.com/983728 blink_perf.css/CustomPropertiesVarAlias.html [ Skip ]
+crbug.com/983728 blink_perf.css/FocusUpdate.html [ Skip ]
+crbug.com/983728 blink_perf.css/PseudoClassSelectors.html [ Skip ]
+crbug.com/983728 blink_perf.css/SelectorCountScaling.html [ Skip ]
 
 # Benchmark: blink_perf.events
-crbug.com/986423 [ Nexus5X_Webview ] blink_perf.events/EventsDispatchingInDeeplyNestedV0ShadowTrees.html [ Skip ]
-crbug.com/986423 [ Nexus5X_Webview ] blink_perf.events/EventsDispatchingInDeeplyNestedV1ShadowTrees.html [ Skip ]
-crbug.com/986423 [ Nexus5X_Webview ] blink_perf.events/EventsDispatchingInV0ShadowTrees.html [ Skip ]
-crbug.com/986423 [ Nexus5X_Webview ] blink_perf.events/EventsDispatchingInV1ShadowTrees.html [ Skip ]
+crbug.com/986423 [ android-nexus-5x android-webview ] blink_perf.events/EventsDispatchingInDeeplyNestedV0ShadowTrees.html [ Skip ]
+crbug.com/986423 [ android-nexus-5x android-webview ] blink_perf.events/EventsDispatchingInDeeplyNestedV1ShadowTrees.html [ Skip ]
+crbug.com/986423 [ android-nexus-5x android-webview ] blink_perf.events/EventsDispatchingInV0ShadowTrees.html [ Skip ]
+crbug.com/986423 [ android-nexus-5x android-webview ] blink_perf.events/EventsDispatchingInV1ShadowTrees.html [ Skip ]
 
 # Benchmark: blink_perf.layout
-crbug.com/551950 [ Android_Svelte ] blink_perf.layout/* [ Skip ]
-crbug.com/832686 [ Nexus_5 ] blink_perf.layout/subtree-detaching.html [ Skip ]
-crbug.com/910207 [ Nexus_5X ] blink_perf.layout/subtree-detaching.html [ Skip ]
-crbug.com/966921 [ Android ] blink_perf.layout/line-layout-fit-content.html [ Skip ]
-crbug.com/966921 [ Android ] blink_perf.layout/line-layout-fit-content-break-word.html [ Skip ]
+crbug.com/551950 [ android-low-end ] blink_perf.layout/* [ Skip ]
+crbug.com/832686 [ android-nexus-5 ] blink_perf.layout/subtree-detaching.html [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] blink_perf.layout/subtree-detaching.html [ Skip ]
+crbug.com/966921 [ android ] blink_perf.layout/line-layout-fit-content.html [ Skip ]
+crbug.com/966921 [ android ] blink_perf.layout/line-layout-fit-content-break-word.html [ Skip ]
 
 # Benchmark: blink_perf.paint
-crbug.com/574483 [ Android_Svelte ] blink_perf.paint/* [ Skip ]
-crbug.com/799540 [ Nexus_5 ] blink_perf.paint/* [ Skip ]
-crbug.com/910207 [ Nexus_5X ] blink_perf.paint/* [ Skip ]
-crbug.com/859979 [ Android_Webview ] blink_perf.paint/paint-offset-changes.html [ Skip ]
-crbug.com/901493 [ Nexus6_Webview ] blink_perf.paint/* [ Skip ]
-crbug.com/963967 [ Android ] blink_perf.paint/select-all-words.html [ Skip ]
-crbug.com/966636 [ Pixel2_Webview ] blink_perf.paint/select-all-words.html [ Skip ]
+crbug.com/574483 [ android-low-end ] blink_perf.paint/* [ Skip ]
+crbug.com/799540 [ android-nexus-5 ] blink_perf.paint/* [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] blink_perf.paint/* [ Skip ]
+crbug.com/859979 [ android-webview ] blink_perf.paint/paint-offset-changes.html [ Skip ]
+crbug.com/901493 [ android-nexus-6 android-webview ] blink_perf.paint/* [ Skip ]
+crbug.com/963967 [ android ] blink_perf.paint/select-all-words.html [ Skip ]
+crbug.com/966636 [ android-pixel-2 android-webview ] blink_perf.paint/select-all-words.html [ Skip ]
 
 # Benchmark: blink_perf.parser
-crbug.com/966913 [ Nexus5X_Webview ] blink_perf.parser/query-selector-all-class-deep.html [ Skip ]
-crbug.com/966913 [ Nexus5X_Webview ] blink_perf.parser/query-selector-all-deep.html [ Skip ]
-crbug.com/966913 [ Nexus5X_Webview ] blink_perf.parser/query-selector-all-id-deep.html [ Skip ]
-crbug.com/966613 [ Pixel_2 ] blink_perf.parser/query-selector-all-class-deep.html [ Skip ]
-crbug.com/966613 [ Pixel_2 ] blink_perf.parser/query-selector-all-deep.html [ Skip ]
-crbug.com/966613 [ Pixel_2 ] blink_perf.parser/query-selector-all-id-deep.html [ Skip ]
+crbug.com/966913 [ android-nexus-5x android-webview ] blink_perf.parser/query-selector-all-class-deep.html [ Skip ]
+crbug.com/966913 [ android-nexus-5x android-webview ] blink_perf.parser/query-selector-all-deep.html [ Skip ]
+crbug.com/966913 [ android-nexus-5x android-webview ] blink_perf.parser/query-selector-all-id-deep.html [ Skip ]
+crbug.com/966613 [ android-pixel-2 ] blink_perf.parser/query-selector-all-class-deep.html [ Skip ]
+crbug.com/966613 [ android-pixel-2 ] blink_perf.parser/query-selector-all-deep.html [ Skip ]
+crbug.com/966613 [ android-pixel-2 ] blink_perf.parser/query-selector-all-id-deep.html [ Skip ]
 
 # Benchmark: blink_perf.shadow_dom
-crbug.com/702319 [ Nexus_5X ] blink_perf.shadow_dom/* [ Skip ]
+crbug.com/702319 [ android-nexus-5x ] blink_perf.shadow_dom/* [ Skip ]
 
 # Benchmark: dromaeo
-crbug.com/984578 [ Linux ] dromaeo/http://dromaeo.com?dom-modify [ Skip ]
+crbug.com/984578 [ linux ] dromaeo/http://dromaeo.com?dom-modify [ Skip ]
 
 # Benchmark: dummy_benchmark
-crbug.com/848900 [ Win ] dummy_benchmark.stable_benchmark_1/dummy_page.html [ Skip ]
+crbug.com/848900 [ win ] dummy_benchmark.stable_benchmark_1/dummy_page.html [ Skip ]
 
 # Benchmark: blink_perf.svg
-crbug.com/736817 [ Nexus_5X ] blink_perf.svg/SvgCubics.html [ Skip ]
-crbug.com/736817 [ Nexus_5X ] blink_perf.svg/Debian.html [ Skip ]
-crbug.com/736817 [ Nexus_5X ] blink_perf.svg/HarveyRayner.html [ Skip ]
-crbug.com/736817 [ Nexus_5X ] blink_perf.svg/CrawFishGanson.html [ Skip ]
-crbug.com/736817 [ Nexus_5X ] blink_perf.svg/Worldcup.html [ Skip ]
-crbug.com/736817 [ Nexus_5X ] blink_perf.svg/FlowerFromMyGarden.html [ Skip ]
-crbug.com/736817 [ Nexus_5X ] blink_perf.svg/SvgNestedUse.html [ Skip ]
-crbug.com/846061 [ Win Mac ] blink_perf.svg/SierpinskiCarpet.html [ Skip ]
-crbug.com/850293 [ All ] blink_perf.svg/Cowboy_transform.html [ Skip ]
-crbug.com/894147 [ Nexus_5 ] blink_perf.svg/SierpinskiCarpet.html [ Skip ]
-crbug.com/894147 [ Nexus_5X ] blink_perf.svg/SierpinskiCarpet.html [ Skip ]
+crbug.com/736817 [ android-nexus-5x ] blink_perf.svg/SvgCubics.html [ Skip ]
+crbug.com/736817 [ android-nexus-5x ] blink_perf.svg/Debian.html [ Skip ]
+crbug.com/736817 [ android-nexus-5x ] blink_perf.svg/HarveyRayner.html [ Skip ]
+crbug.com/736817 [ android-nexus-5x ] blink_perf.svg/CrawFishGanson.html [ Skip ]
+crbug.com/736817 [ android-nexus-5x ] blink_perf.svg/Worldcup.html [ Skip ]
+crbug.com/736817 [ android-nexus-5x ] blink_perf.svg/FlowerFromMyGarden.html [ Skip ]
+crbug.com/736817 [ android-nexus-5x ] blink_perf.svg/SvgNestedUse.html [ Skip ]
+crbug.com/846061 [ win ] blink_perf.svg/SierpinskiCarpet.html [ Skip ]
+crbug.com/846061 [ mac ] blink_perf.svg/SierpinskiCarpet.html [ Skip ]
+crbug.com/850293 blink_perf.svg/Cowboy_transform.html [ Skip ]
+crbug.com/894147 [ android-nexus-5 ] blink_perf.svg/SierpinskiCarpet.html [ Skip ]
+crbug.com/894147 [ android-nexus-5x ] blink_perf.svg/SierpinskiCarpet.html [ Skip ]
 
 # Benchmark: jetstream
-crbug.com/830600 [ Nexus5X_Webview ] jetstream/http://browserbench.org/JetStream/ [ Skip ]
+crbug.com/830600 [ android-nexus-5x android-webview ] jetstream/http://browserbench.org/JetStream/ [ Skip ]
 
 # Benchmark: loading.desktop
-crbug.com/723783 [ Win ] loading.desktop/Orange_cold [ Skip ]
-crbug.com/723783 [ Win ] loading.desktop/Orange_warm [ Skip ]
-crbug.com/752611 [ Linux ] loading.desktop/uol.com.br_cold [ Skip ]
-crbug.com/752611 [ Linux ] loading.desktop/uol.com.br_warm [ Skip ]
-crbug.com/867836 [ All ] loading.desktop/Elmundo_warm [ Skip ]
-crbug.com/876636 [ ChromeOS ] loading.desktop/TheOnion_cold [ Skip ]
-crbug.com/876636 [ ChromeOS ] loading.desktop/TheOnion_warm [ Skip ]
-crbug.com/876636 [ Win_10 ] loading.desktop/TheOnion_warm [ Skip ]
-crbug.com/876636 [ Win_7 ] loading.desktop/TheOnion_warm [ Skip ]
-crbug.com/876636 [ ChromeOS ] loading.desktop/AllRecipes_cold [ Skip ]
-crbug.com/876636 [ ChromeOS ] loading.desktop/AllRecipes_warm [ Skip ]
-crbug.com/879833 [ Mac_10.12 ] loading.desktop/AllRecipes_cold [ Skip ]
-crbug.com/879833 [ Win ] loading.desktop/AllRecipes_cold [ Skip ]
-crbug.com/879833 [ All ] loading.desktop/Walgreens_cold [ Skip ]
-crbug.com/879833 [ All ] loading.desktop/Walgreens_warm [ Skip ]
-crbug.com/921428 [ ChromeOS ] loading.desktop/TheVerge_cold [ Skip ]
-crbug.com/921428 [ ChromeOS ] loading.desktop/TheVerge_warm [ Skip ]
+crbug.com/723783 [ win ] loading.desktop/Orange_cold [ Skip ]
+crbug.com/723783 [ win ] loading.desktop/Orange_warm [ Skip ]
+crbug.com/752611 [ linux ] loading.desktop/uol.com.br_cold [ Skip ]
+crbug.com/752611 [ linux ] loading.desktop/uol.com.br_warm [ Skip ]
+crbug.com/867836 loading.desktop/Elmundo_warm [ Skip ]
+crbug.com/876636 [ chromeos ] loading.desktop/TheOnion_cold [ Skip ]
+crbug.com/876636 [ chromeos ] loading.desktop/TheOnion_warm [ Skip ]
+crbug.com/876636 [ win10 ] loading.desktop/TheOnion_warm [ Skip ]
+crbug.com/876636 [ win7 ] loading.desktop/TheOnion_warm [ Skip ]
+crbug.com/876636 [ chromeos ] loading.desktop/AllRecipes_cold [ Skip ]
+crbug.com/876636 [ chromeos ] loading.desktop/AllRecipes_warm [ Skip ]
+crbug.com/879833 [ mac-10.12 ] loading.desktop/AllRecipes_cold [ Skip ]
+crbug.com/879833 [ win ] loading.desktop/AllRecipes_cold [ Skip ]
+crbug.com/879833 loading.desktop/Walgreens_cold [ Skip ]
+crbug.com/879833 loading.desktop/Walgreens_warm [ Skip ]
+crbug.com/921428 [ chromeos ] loading.desktop/TheVerge_cold [ Skip ]
+crbug.com/921428 [ chromeos ] loading.desktop/TheVerge_warm [ Skip ]
 
 # Benchmark: loading.mobile
-crbug.com/656861 [ All ] loading.mobile/G1 [ Skip ]
-crbug.com/857108 [ Nexus_5 ] loading.mobile/G1_3g [ Skip ]
-crbug.com/910207 [ Nexus_5X ] loading.mobile/G1_3g [ Skip ]
-[ Nexus_5X ] loading.mobile/Hongkiat [ Skip ]
-[ Nexus_5X ] loading.mobile/Dramaq [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/Bradesco_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/Dailymotion_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/Dawn_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/FlipKart_cold_3g [ Skip ]
-crbug.com/942631 [ All ] loading.mobile/Facebook_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/G1_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/GSShop_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/GoogleIndia_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/KapanLagi_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/Kaskus_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/LocalMoxie_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/Thairath_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/TheStar_3g [ Skip ]
-crbug.com/859597 [ All ] loading.mobile/YahooNews_3g [ Skip ]
-crbug.com/942631 [ All ] loading.mobile/VoiceMemos_hot [ Skip ]
-crbug.com/942631 [ All ] loading.mobile/VoiceMemos_warm [ Skip ]
-crbug.com/942631 [ All ] loading.mobile/VoiceMemos_hot_3g [ Skip ]
-crbug.com/942631 [ All ] loading.mobile/VoiceMemos_warm_3g [ Skip ]
-crbug.com/859597 [ Nexus_5 ] loading.mobile/FlipBoard_cold_3g [ Skip ]
-crbug.com/862663 [ Nexus_5 ] loading.mobile/GoogleBrazil_3g [ Skip ]
-crbug.com/910207 [ Nexus_5X ] loading.mobile/GoogleBrazil_3g [ Skip ]
-crbug.com/862663 [ Nexus_5 ] loading.mobile/GoogleIndonesia_3g [ Skip ]
-crbug.com/862663 [ Nexus_5 ] loading.mobile/GoogleRedirectToGoogleJapan_3g [ Skip ]
-crbug.com/910207 [ Nexus_5X ] loading.mobile/GoogleRedirectToGoogleJapan_3g [ Skip ]
-crbug.com/859597 [ Nexus_5X ] loading.mobile/FlipBoard_cold_3g [ Skip ]
-crbug.com/859597 [ Nexus_5 ] loading.mobile/Hongkiat_3g [ Skip ]
-crbug.com/859597 [ Nexus_5X ] loading.mobile/Hongkiat_3g [ Skip ]
-crbug.com/859597 [ Nexus5X_Webview ] loading.mobile/Hongkiat_3g [ Skip ]
-crbug.com/896088 [ Nexus_5 ] loading.mobile/TribunNews_3g [ Skip ]
-crbug.com/859597 [ Nexus_5X ] loading.mobile/TribunNews_3g [ Skip ]
-crbug.com/859597 [ Nexus5X_Webview ] loading.mobile/TribunNews_3g [ Skip ]
-crbug.com/859597 [ Nexus_5X ] loading.mobile/GoogleIndonesia_3g [ Skip ]
-crbug.com/859597 [ Nexus_5X ] loading.mobile/QQNews_3g [ Skip ]
-crbug.com/859597 [ Nexus_5X ] loading.mobile/Youtube_3g [ Skip ]
-crbug.com/859597 [ Nexus5X_Webview ] loading.mobile/GoogleBrazil_3g [ Skip ]
-crbug.com/867103 [ Nexus5X_Webview ] loading.mobile/FlipBoard_warm_3g [ Skip ]
-crbug.com/873032 [ Android_Webview ] loading.mobile/GoogleRedirectToGoogleJapan_3g [ Skip ]
-crbug.com/873033 [ Android_but_not_webview ] loading.mobile/FlipKart_warm_3g [ Skip ]
-crbug.com/676612 [ Nexus6_Webview ] loading.mobile/FlipBoard_cold_3g [ Skip ]
-crbug.com/676612 [ Nexus6_Webview ] loading.mobile/FlipBoard_warm_3g [ Skip ]
-crbug.com/676612 [ Nexus6_Webview ] loading.mobile/TribunNews_3g [ Skip ]
-crbug.com/676612 [ Nexus6_Webview ] loading.mobile/FlipKart_warm_3g [ Skip ]
-crbug.com/676612 [ Nexus6_Webview ] loading.mobile/OLX_3g [ Skip ]
-crbug.com/676612 [ Nexus6_Webview ] loading.mobile/Youtube_3g [ Skip ]
-crbug.com/676612 [ Nexus6_Webview ] loading.mobile/Hongkiat_3g [ Skip ]
-crbug.com/676612 [ Nexus6_Webview ] loading.mobile/BOLNoticias_3g [ Skip ]
-crbug.com/676612 [ Nexus6_Webview ] loading.mobile/GoogleBrazil_3g [ Skip ]
-crbug.com/676612 [ Nexus6_Webview ] loading.mobile/GoogleIndonesia_3g [ Skip ]
-crbug.com/676612 [ Nexus6_Webview ] loading.mobile/Kaskus [ Skip ]
-crbug.com/865400 [ Pixel_2 ] loading.mobile/TribunNews_3g [ Skip ]
-crbug.com/865400 [ Pixel_2 ] loading.mobile/Youtube_3g [ Skip ]
-crbug.com/914100 [ Nexus6_Webview ] loading.mobile/Wikipedia_3g [ Skip ]
-crbug.com/865400 [ Pixel2_Webview ] loading.mobile/BOLNoticias_3g [ Skip ]
-crbug.com/865400 [ Pixel2_Webview ] loading.mobile/FlipBoard_cold_3g [ Skip ]
-crbug.com/865400 [ Pixel2_Webview ] loading.mobile/FlipBoard_warm_3g [ Skip ]
-crbug.com/865400 [ Pixel2_Webview ] loading.mobile/FlipKart_warm_3g [ Skip ]
-crbug.com/865400 [ Pixel2_Webview ] loading.mobile/GoogleBrazil_3g [ Skip ]
-crbug.com/865400 [ Pixel2_Webview ] loading.mobile/GoogleIndonesia_3g [ Skip ]
-crbug.com/865400 [ Pixel2_Webview ] loading.mobile/Hongkiat_3g [ Skip ]
-crbug.com/865400 [ Pixel2_Webview ] loading.mobile/OLX_3g [ Skip ]
-crbug.com/865400 [ Pixel2_Webview ] loading.mobile/VoiceMemos_cold_3g [ Skip ]
-crbug.com/919191 [ Nexus5X_Webview ] loading.mobile/OLX_3g [ Skip ]
+crbug.com/656861 loading.mobile/G1 [ Skip ]
+crbug.com/857108 [ android-nexus-5 ] loading.mobile/G1_3g [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] loading.mobile/G1_3g [ Skip ]
+[ android-nexus-5x ] loading.mobile/Hongkiat [ Skip ]
+[ android-nexus-5x ] loading.mobile/Dramaq [ Skip ]
+crbug.com/859597 loading.mobile/Bradesco_3g [ Skip ]
+crbug.com/859597 loading.mobile/Dailymotion_3g [ Skip ]
+crbug.com/859597 loading.mobile/Dawn_3g [ Skip ]
+crbug.com/859597 loading.mobile/FlipKart_cold_3g [ Skip ]
+crbug.com/942631 loading.mobile/Facebook_3g [ Skip ]
+crbug.com/859597 loading.mobile/G1_3g [ Skip ]
+crbug.com/859597 loading.mobile/GSShop_3g [ Skip ]
+crbug.com/859597 loading.mobile/GoogleIndia_3g [ Skip ]
+crbug.com/859597 loading.mobile/KapanLagi_3g [ Skip ]
+crbug.com/859597 loading.mobile/Kaskus_3g [ Skip ]
+crbug.com/859597 loading.mobile/LocalMoxie_3g [ Skip ]
+crbug.com/859597 loading.mobile/Thairath_3g [ Skip ]
+crbug.com/859597 loading.mobile/TheStar_3g [ Skip ]
+crbug.com/859597 loading.mobile/YahooNews_3g [ Skip ]
+crbug.com/942631 loading.mobile/VoiceMemos_hot [ Skip ]
+crbug.com/942631 loading.mobile/VoiceMemos_warm [ Skip ]
+crbug.com/942631 loading.mobile/VoiceMemos_hot_3g [ Skip ]
+crbug.com/942631 loading.mobile/VoiceMemos_warm_3g [ Skip ]
+crbug.com/859597 [ android-nexus-5 ] loading.mobile/FlipBoard_cold_3g [ Skip ]
+crbug.com/862663 [ android-nexus-5 ] loading.mobile/GoogleBrazil_3g [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] loading.mobile/GoogleBrazil_3g [ Skip ]
+crbug.com/862663 [ android-nexus-5 ] loading.mobile/GoogleIndonesia_3g [ Skip ]
+crbug.com/862663 [ android-nexus-5 ] loading.mobile/GoogleRedirectToGoogleJapan_3g [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] loading.mobile/GoogleRedirectToGoogleJapan_3g [ Skip ]
+crbug.com/859597 [ android-nexus-5x ] loading.mobile/FlipBoard_cold_3g [ Skip ]
+crbug.com/859597 [ android-nexus-5 ] loading.mobile/Hongkiat_3g [ Skip ]
+crbug.com/859597 [ android-nexus-5x ] loading.mobile/Hongkiat_3g [ Skip ]
+crbug.com/859597 [ android-nexus-5x android-webview ] loading.mobile/Hongkiat_3g [ Skip ]
+crbug.com/896088 [ android-nexus-5 ] loading.mobile/TribunNews_3g [ Skip ]
+crbug.com/859597 [ android-nexus-5x ] loading.mobile/TribunNews_3g [ Skip ]
+crbug.com/859597 [ android-nexus-5x android-webview ] loading.mobile/TribunNews_3g [ Skip ]
+crbug.com/859597 [ android-nexus-5x ] loading.mobile/GoogleIndonesia_3g [ Skip ]
+crbug.com/859597 [ android-nexus-5x ] loading.mobile/QQNews_3g [ Skip ]
+crbug.com/859597 [ android-nexus-5x ] loading.mobile/Youtube_3g [ Skip ]
+crbug.com/859597 [ android-nexus-5x android-webview ] loading.mobile/GoogleBrazil_3g [ Skip ]
+crbug.com/867103 [ android-nexus-5x android-webview ] loading.mobile/FlipBoard_warm_3g [ Skip ]
+crbug.com/873032 [ android-webview ] loading.mobile/GoogleRedirectToGoogleJapan_3g [ Skip ]
+crbug.com/873033 [ android-not-webview ] loading.mobile/FlipKart_warm_3g [ Skip ]
+crbug.com/676612 [ android-nexus-6 android-webview ] loading.mobile/FlipBoard_cold_3g [ Skip ]
+crbug.com/676612 [ android-nexus-6 android-webview ] loading.mobile/FlipBoard_warm_3g [ Skip ]
+crbug.com/676612 [ android-nexus-6 android-webview ] loading.mobile/TribunNews_3g [ Skip ]
+crbug.com/676612 [ android-nexus-6 android-webview ] loading.mobile/FlipKart_warm_3g [ Skip ]
+crbug.com/676612 [ android-nexus-6 android-webview ] loading.mobile/OLX_3g [ Skip ]
+crbug.com/676612 [ android-nexus-6 android-webview ] loading.mobile/Youtube_3g [ Skip ]
+crbug.com/676612 [ android-nexus-6 android-webview ] loading.mobile/Hongkiat_3g [ Skip ]
+crbug.com/676612 [ android-nexus-6 android-webview ] loading.mobile/BOLNoticias_3g [ Skip ]
+crbug.com/676612 [ android-nexus-6 android-webview ] loading.mobile/GoogleBrazil_3g [ Skip ]
+crbug.com/676612 [ android-nexus-6 android-webview ] loading.mobile/GoogleIndonesia_3g [ Skip ]
+crbug.com/676612 [ android-nexus-6 android-webview ] loading.mobile/Kaskus [ Skip ]
+crbug.com/865400 [ android-pixel-2 ] loading.mobile/TribunNews_3g [ Skip ]
+crbug.com/865400 [ android-pixel-2 ] loading.mobile/Youtube_3g [ Skip ]
+crbug.com/914100 [ android-nexus-6 android-webview ] loading.mobile/Wikipedia_3g [ Skip ]
+crbug.com/865400 [ android-pixel-2 android-webview ] loading.mobile/BOLNoticias_3g [ Skip ]
+crbug.com/865400 [ android-pixel-2 android-webview ] loading.mobile/FlipBoard_cold_3g [ Skip ]
+crbug.com/865400 [ android-pixel-2 android-webview ] loading.mobile/FlipBoard_warm_3g [ Skip ]
+crbug.com/865400 [ android-pixel-2 android-webview ] loading.mobile/FlipKart_warm_3g [ Skip ]
+crbug.com/865400 [ android-pixel-2 android-webview ] loading.mobile/GoogleBrazil_3g [ Skip ]
+crbug.com/865400 [ android-pixel-2 android-webview ] loading.mobile/GoogleIndonesia_3g [ Skip ]
+crbug.com/865400 [ android-pixel-2 android-webview ] loading.mobile/Hongkiat_3g [ Skip ]
+crbug.com/865400 [ android-pixel-2 android-webview ] loading.mobile/OLX_3g [ Skip ]
+crbug.com/865400 [ android-pixel-2 android-webview ] loading.mobile/VoiceMemos_cold_3g [ Skip ]
+crbug.com/919191 [ android-nexus-5x android-webview ] loading.mobile/OLX_3g [ Skip ]
 
 # Benchmark: oilpan_gc_times.key_silk_cases
-crbug.com/446332 [ All ] oilpan_gc_times.key_silk_cases/slide_drawer [ Skip ]
-crbug.com/507865 [ All ] oilpan_gc_times.key_silk_cases/polymer_topeka [ Skip ]
-crbug.com/338838 [ All ] oilpan_gc_times.key_silk_cases/basic_stream [ Skip ]
+crbug.com/446332 oilpan_gc_times.key_silk_cases/slide_drawer [ Skip ]
+crbug.com/507865 oilpan_gc_times.key_silk_cases/polymer_topeka [ Skip ]
+crbug.com/338838 oilpan_gc_times.key_silk_cases/basic_stream [ Skip ]
 
 # Benchmark: oilpan_gc_times.sync_scroll.key_mobile_sites_smooth
-crbug.com/756119 [ All ] oilpan_gc_times.sync_scroll.key_mobile_sites_smooth/digg [ Skip ]
+crbug.com/756119 oilpan_gc_times.sync_scroll.key_mobile_sites_smooth/digg [ Skip ]
 
 # Benchmark: rendering.desktop
-crbug.com/755556 [ Mac ] rendering.desktop/mix_blend_mode_animation_difference [ Skip ]
-crbug.com/755556 [ Mac ] rendering.desktop/mix_blend_mode_animation_hue [ Skip ]
+crbug.com/755556 [ mac ] rendering.desktop/mix_blend_mode_animation_difference [ Skip ]
+crbug.com/755556 [ mac ] rendering.desktop/mix_blend_mode_animation_hue [ Skip ]
 
 # Benchmark: rendering.mobile
-crbug.com/785485 [ Android_Webview ] rendering.mobile/kevs_3d [ Skip ]
-crbug.com/785286 [ Android_Webview ] rendering.mobile/smash_cat [ Skip ]
-crbug.com/785286 [ Android_Webview ] rendering.mobile/effect_games [ Skip ]
-crbug.com/364248 [ Nexus_5 ] rendering.mobile/geo_apis [ Skip ]
-crbug.com/910207 [ Nexus_5X ] rendering.mobile/geo_apis [ Skip ]
-crbug.com/825234 [ Android_Webview ] rendering.mobile/bouncing_balls_shadow [ Skip ]
-crbug.com/755556 [ Android ] rendering.mobile/balls_css_key_frame_animations_composited_transform [ Skip ]
-crbug.com/840964 [ Nexus_5X ] rendering.mobile/web_animations_many_keyframes [ Skip ]
-crbug.com/653993 [ Android_Webview ] rendering.mobile/maps_perf_test [ Skip ]
-[ All ] rendering.mobile/core_scroll_header_panel [ Skip ] # Polymer test, needs to be modernized.
-[ All ] rendering.mobile/paper_button [ Skip ] # Polymer test, needs to be modernized.
-[ All ] rendering.mobile/paper_calculator [ Skip ] # Polymer test, needs to be modernized.
-[ All ] rendering.mobile/paper_checkbox [ Skip ] # Polymer test, needs to be modernized.
-[ All ] rendering.mobile/paper_fab [ Skip ] # Polymer test, needs to be modernized.
-[ All ] rendering.mobile/paper_icon_button [ Skip ] # Polymer test, needs to be modernized.
-[ All ] rendering.mobile/paper_shadow [ Skip ] # Polymer test, needs to be modernized.
-[ All ] rendering.mobile/paper_tabs [ Skip ] # Polymer test, needs to be modernized.
-[ All ] rendering.mobile/paper_toggle_button [ Skip ] # Polymer test, needs to be modernized.
-crbug.com/785473 [ Android_Webview ] rendering.mobile/canvas_20000_pixels_per_second [ Skip ]
-crbug.com/785473 [ Android_Webview ] rendering.mobile/canvas_40000_pixels_per_second [ Skip ]
-crbug.com/785473 [ Android_Webview ] rendering.mobile/canvas_10000_pixels_per_second [ Skip ]
-crbug.com/785473 [ Android_Webview ] rendering.mobile/canvas_75000_pixels_per_second [ Skip ]
-crbug.com/785473 [ Android_Webview ] rendering.mobile/canvas_60000_pixels_per_second [ Skip ]
-crbug.com/785473 [ Android_Webview ] rendering.mobile/canvas_90000_pixels_per_second [ Skip ]
-crbug.com/850295 [ All ] rendering.mobile/aquarium_20k [ Skip ]
-crbug.com/780525 [ All ] rendering.mobile/polymer_topeka [ Skip ]
-crbug.com/461127 [ All ] rendering.mobile/famo_us_twitter_demo [ Skip ]
-crbug.com/850295 [ All ] rendering.mobile/aquarium_20k [ Skip ]
-crbug.com/750876 [ All ] rendering.mobile/paper_calculator_hit_test [ Skip ]
-crbug.com/873013 [ Android_Webview ] rendering.mobile/yahoo_answers_mobile_2018 [ Skip ]
-crbug.com/893197 [ Nexus_5X ] rendering.mobile/yahoo_answers_mobile_2018 [ Skip ]
-crbug.com/865400 [ Pixel_2 ] rendering.mobile/yahoo_answers_mobile_2018 [ Skip ]
-crbug.com/874935 [ Nexus_5 ] rendering.mobile/yahoo_news_2018 [ Skip ]
-crbug.com/910207 [ Nexus_5X ] rendering.mobile/yahoo_news_2018 [ Skip ]
-crbug.com/901526 [ All ] rendering.mobile/microsoft_fireflies [ Skip ]
-crbug.com/924400 [ Nexus6_Webview ] rendering.mobile/canvas_animation_no_clear [ Skip ]
-crbug.com/949366 [ Nexus5X_Webview ] rendering.mobile/google_news_mobile_2018 [ Skip ]
-crbug.com/954948 [ Android_Webview ] rendering.mobile/cnn_mobile_pinch_2018 [ Skip ]
-crbug.com/966637 [ Pixel2_Webview ] rendering.mobile/google_news_mobile_2018 [ Skip ]
-crbug.com/967809 [ Android_Webview ] rendering.mobile/microsoft_video_city [ Skip ]
+crbug.com/785485 [ android-webview ] rendering.mobile/kevs_3d [ Skip ]
+crbug.com/785286 [ android-webview ] rendering.mobile/smash_cat [ Skip ]
+crbug.com/785286 [ android-webview ] rendering.mobile/effect_games [ Skip ]
+crbug.com/364248 [ android-nexus-5 ] rendering.mobile/geo_apis [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] rendering.mobile/geo_apis [ Skip ]
+crbug.com/825234 [ android-webview ] rendering.mobile/bouncing_balls_shadow [ Skip ]
+crbug.com/755556 [ android ] rendering.mobile/balls_css_key_frame_animations_composited_transform [ Skip ]
+crbug.com/840964 [ android-nexus-5x ] rendering.mobile/web_animations_many_keyframes [ Skip ]
+crbug.com/653993 [ android-webview ] rendering.mobile/maps_perf_test [ Skip ]
+rendering.mobile/core_scroll_header_panel [ Skip ] # Polymer test, needs to be modernized.
+rendering.mobile/paper_button [ Skip ] # Polymer test, needs to be modernized.
+rendering.mobile/paper_calculator [ Skip ] # Polymer test, needs to be modernized.
+rendering.mobile/paper_checkbox [ Skip ] # Polymer test, needs to be modernized.
+rendering.mobile/paper_fab [ Skip ] # Polymer test, needs to be modernized.
+rendering.mobile/paper_icon_button [ Skip ] # Polymer test, needs to be modernized.
+rendering.mobile/paper_shadow [ Skip ] # Polymer test, needs to be modernized.
+rendering.mobile/paper_tabs [ Skip ] # Polymer test, needs to be modernized.
+rendering.mobile/paper_toggle_button [ Skip ] # Polymer test, needs to be modernized.
+crbug.com/785473 [ android-webview ] rendering.mobile/canvas_20000_pixels_per_second [ Skip ]
+crbug.com/785473 [ android-webview ] rendering.mobile/canvas_40000_pixels_per_second [ Skip ]
+crbug.com/785473 [ android-webview ] rendering.mobile/canvas_10000_pixels_per_second [ Skip ]
+crbug.com/785473 [ android-webview ] rendering.mobile/canvas_75000_pixels_per_second [ Skip ]
+crbug.com/785473 [ android-webview ] rendering.mobile/canvas_60000_pixels_per_second [ Skip ]
+crbug.com/785473 [ android-webview ] rendering.mobile/canvas_90000_pixels_per_second [ Skip ]
+crbug.com/850295 rendering.mobile/aquarium_20k [ Skip ]
+crbug.com/780525 rendering.mobile/polymer_topeka [ Skip ]
+crbug.com/461127 rendering.mobile/famo_us_twitter_demo [ Skip ]
+crbug.com/850295 rendering.mobile/aquarium_20k [ Skip ]
+crbug.com/750876 rendering.mobile/paper_calculator_hit_test [ Skip ]
+crbug.com/873013 [ android-webview ] rendering.mobile/yahoo_answers_mobile_2018 [ Skip ]
+crbug.com/893197 [ android-nexus-5x ] rendering.mobile/yahoo_answers_mobile_2018 [ Skip ]
+crbug.com/865400 [ android-pixel-2 ] rendering.mobile/yahoo_answers_mobile_2018 [ Skip ]
+crbug.com/874935 [ android-nexus-5 ] rendering.mobile/yahoo_news_2018 [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] rendering.mobile/yahoo_news_2018 [ Skip ]
+crbug.com/901526 rendering.mobile/microsoft_fireflies [ Skip ]
+crbug.com/924400 [ android-nexus-6 android-webview ] rendering.mobile/canvas_animation_no_clear [ Skip ]
+crbug.com/949366 [ android-nexus-5x android-webview ] rendering.mobile/google_news_mobile_2018 [ Skip ]
+crbug.com/954948 [ android-webview ] rendering.mobile/cnn_mobile_pinch_2018 [ Skip ]
+crbug.com/966637 [ android-pixel-2 android-webview ] rendering.mobile/google_news_mobile_2018 [ Skip ]
+crbug.com/967809 [ android-webview ] rendering.mobile/microsoft_video_city [ Skip ]
 
 # Benchmark: rasterize_and_record_micro.top_25
-crbug.com/764543 [ All ] rasterize_and_record_micro.top_25/file://static_top_25/wikipedia.html [ Skip ]
-crbug.com/815193 [ Android ] rasterize_and_record_micro.top_25/file://static_top_25/wikipedia.html [ Skip ]
-crbug.com/873011 [ Android_Webview ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
-crbug.com/865400 [ Pixel_2 ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
-crbug.com/865400 [ Nexus_5 ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
-crbug.com/865400 [ Nexus_5X ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
-crbug.com/892223 [ Nexus_5 ] rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html [ Skip ]
-crbug.com/910207 [ Nexus_5X ] rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html [ Skip ]
-crbug.com/875878 [ Nexus6_Webview ] rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html [ Skip ]
+crbug.com/764543 rasterize_and_record_micro.top_25/file://static_top_25/wikipedia.html [ Skip ]
+crbug.com/815193 [ android ] rasterize_and_record_micro.top_25/file://static_top_25/wikipedia.html [ Skip ]
+crbug.com/873011 [ android-webview ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
+crbug.com/865400 [ android-pixel-2 ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
+crbug.com/865400 [ android-nexus-5 ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
+crbug.com/865400 [ android-nexus-5x ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
+crbug.com/892223 [ android-nexus-5 ] rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html [ Skip ]
+crbug.com/875878 [ android-nexus-6 android-webview ] rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html [ Skip ]
 
 # Benchmark: startup.mobile
-crbug.com/948789 [ Nexus_5 ] startup.mobile/maps_pwa:with_http_cache [ Skip ]
-crbug.com/948789 [ Nexus_5X ] startup.mobile/maps_pwa:with_http_cache [ Skip ]
+crbug.com/948789 [ android-nexus-5 ] startup.mobile/maps_pwa:with_http_cache [ Skip ]
+crbug.com/948789 [ android-nexus-5x ] startup.mobile/maps_pwa:with_http_cache [ Skip ]
 
 # Benchmark: system_health.common_desktop
-crbug.com/984599 [ Linux ] system_health.common_desktop/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
-crbug.com/984599 [ Win_10 ] system_health.common_desktop/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
-crbug.com/773084 [ Mac ] system_health.common_desktop/browse:tools:maps [ Skip ]
-crbug.com/903417 [ Mac ] system_health.common_desktop/long_running:tools:gmail-foreground [ Skip ]
-crbug.com/903417 [ Win ] system_health.common_desktop/long_running:tools:gmail-foreground [ Skip ]
-crbug.com/911214 [ Win ] system_health.common_desktop/multitab:misc:typical24 [ Skip ]
-crbug.com/934270 [ Win ] system_health.common_desktop/multitab:misc:typical24:2018 [ Skip ]
-crbug.com/931185 [ Win_7 ] system_health.common_desktop/browse:media:youtubetv:2019 [ Skip ]
-crbug.com/958422 [ Win_7 ] system_health.common_desktop/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
+crbug.com/984599 [ linux ] system_health.common_desktop/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
+crbug.com/984599 [ win10 ] system_health.common_desktop/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
+crbug.com/773084 [ mac ] system_health.common_desktop/browse:tools:maps [ Skip ]
+crbug.com/903417 [ mac ] system_health.common_desktop/long_running:tools:gmail-foreground [ Skip ]
+crbug.com/903417 [ win ] system_health.common_desktop/long_running:tools:gmail-foreground [ Skip ]
+crbug.com/911214 [ win ] system_health.common_desktop/multitab:misc:typical24 [ Skip ]
+crbug.com/934270 [ win ] system_health.common_desktop/multitab:misc:typical24:2018 [ Skip ]
+crbug.com/931185 [ win7 ] system_health.common_desktop/browse:media:youtubetv:2019 [ Skip ]
+crbug.com/958422 [ win7 ] system_health.common_desktop/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
 
 # Benchmark: system_health.common_mobile
-crbug.com/914390 [ Nexus_5 ] system_health.common_mobile/browse:chrome:newtab [ Skip ]
-crbug.com/714650 [ Android ] system_health.common_mobile/browse:news:globo [ Skip ]
-crbug.com/708300 [ Android ] system_health.common_mobile/browse:shopping:flipkart [ Skip ]
-crbug.com/865400 [ Pixel_2 ] system_health.common_mobile/background:news:nytimes [ Skip ]
-crbug.com/877648 [ Android_Go ] system_health.common_mobile/browse:news:toi [ Skip ]
-crbug.com/929839 [ Android_Go ] system_health.common_mobile/browse:chrome:newtab [ Skip ]
-crbug.com/877648 [ Android ] system_health.common_mobile/long_running:tools:gmail-background [ Skip ]
-crbug.com/877648 [ Android ] system_health.common_mobile/long_running:tools:gmail-foreground [ Skip ]
-crbug.com/896851 [ Android ] system_health.common_mobile/background:tools:gmail [ Skip ]
-crbug.com/896871 [ Android ] system_health.common_mobile/load:tools:gmail [ Skip ]
-crbug.com/923116 [ Android ] system_health.common_mobile/browse:shopping:avito [ Skip ]
-crbug.com/923527 [ Android_Webview ] system_health.common_mobile/load:media:soundcloud:2018 [ Skip ]
-crbug.com/954949 [ Nexus5X_Webview ] system_health.common_mobile/browse:news:washingtonpost [ Skip ]
-crbug.com/961417 [ Android_Go ] system_health.common_mobile/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
-crbug.com/979786 [ Nexus5X_Webview ] system_health.common_mobile/browse:news:cricbuzz [ Skip ]
+crbug.com/914390 [ android-nexus-5 ] system_health.common_mobile/browse:chrome:newtab [ Skip ]
+crbug.com/714650 [ android ] system_health.common_mobile/browse:news:globo [ Skip ]
+crbug.com/708300 [ android ] system_health.common_mobile/browse:shopping:flipkart [ Skip ]
+crbug.com/865400 [ android-pixel-2 ] system_health.common_mobile/background:news:nytimes [ Skip ]
+crbug.com/877648 [ android-go ] system_health.common_mobile/browse:news:toi [ Skip ]
+crbug.com/929839 [ android-go ] system_health.common_mobile/browse:chrome:newtab [ Skip ]
+crbug.com/877648 [ android ] system_health.common_mobile/long_running:tools:gmail-background [ Skip ]
+crbug.com/877648 [ android ] system_health.common_mobile/long_running:tools:gmail-foreground [ Skip ]
+crbug.com/896851 [ android ] system_health.common_mobile/background:tools:gmail [ Skip ]
+crbug.com/896871 [ android ] system_health.common_mobile/load:tools:gmail [ Skip ]
+crbug.com/923116 [ android ] system_health.common_mobile/browse:shopping:avito [ Skip ]
+crbug.com/923527 [ android-webview ] system_health.common_mobile/load:media:soundcloud:2018 [ Skip ]
+crbug.com/954949 [ android-nexus-5x android-webview ] system_health.common_mobile/browse:news:washingtonpost [ Skip ]
+crbug.com/961417 [ android-go ] system_health.common_mobile/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
+crbug.com/979786 [ android-nexus-5x android-webview ] system_health.common_mobile/browse:news:cricbuzz [ Skip ]
 
 # Benchmark: system_health.memory_desktop
-crbug.com/984599 [ Linux ] system_health.memory_desktop/long_running:tools:gmail-foreground [ Skip ]
-crbug.com/984599 [ Mac ] system_health.memory_desktop/long_running:tools:gmail-background [ Skip ]
-crbug.com/984599 [ Mac ] system_health.memory_desktop/long_running:tools:gmail-foreground [ Skip ]
-crbug.com/649392 [ All ] system_health.memory_desktop/play:media:google_play_music [ Skip ]
-crbug.com/742475 [ Mac ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
-crbug.com/946495 [ Mac ] system_health.memory_desktop/multitab:misc:typical24:2018 [ Skip ]
-crbug.com/838504 [ Linux ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
-crbug.com/773084 [ Mac ] system_health.memory_desktop/browse:tools:maps [ Skip ]
-crbug.com/799106 [ Win ] system_health.memory_desktop/browse:media:flickr_infinite_scroll [ Skip ]
-crbug.com/836407 [ Linux ] system_health.memory_desktop/browse:tools:maps [ Skip ]
-crbug.com/934885 [ Linux ] system_health.memory_desktop/load_accessibility:media:wikipedia:2018 [ Skip ]
-crbug.com/869118 [ Linux ] system_health.memory_desktop/long_running:tools:gmail-background [ Skip ]
-crbug.com/836447 [ ChromeOS ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
-crbug.com/899887 [ Linux ] system_health.memory_desktop/browse:social:facebook_infinite_scroll:2018 [ Skip ]
-crbug.com/924330 [ Linux ] system_health.memory_desktop/browse:media:pinterest:2018 [ Skip ]
-crbug.com/874803 [ Win_10 ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
-crbug.com/944978 [ Win_7 ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
-crbug.com/934270 [ Win ] system_health.memory_desktop/multitab:misc:typical24:2018 [ Skip ]
-crbug.com/942952 [ ChromeOS ] system_health.memory_desktop/browse:news:hackernews:2018 [ Skip ]
-crbug.com/959418 [ ChromeOS ] system_health.memory_desktop/long_running:tools:gmail-background [ Skip ]
-crbug.com/959418 [ ChromeOS ] system_health.memory_desktop/load:games:spychase:2018 [ Skip ]
-crbug.com/959418 [ ChromeOS ] system_health.memory_desktop/long_running:tools:gmail-foreground [ Skip ]
+crbug.com/984599 [ linux ] system_health.memory_desktop/long_running:tools:gmail-foreground [ Skip ]
+crbug.com/984599 [ mac ] system_health.memory_desktop/long_running:tools:gmail-background [ Skip ]
+crbug.com/984599 [ mac ] system_health.memory_desktop/long_running:tools:gmail-foreground [ Skip ]
+crbug.com/649392 system_health.memory_desktop/play:media:google_play_music [ Skip ]
+crbug.com/742475 [ mac ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
+crbug.com/946495 [ mac ] system_health.memory_desktop/multitab:misc:typical24:2018 [ Skip ]
+crbug.com/838504 [ linux ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
+crbug.com/773084 [ mac ] system_health.memory_desktop/browse:tools:maps [ Skip ]
+crbug.com/799106 [ win ] system_health.memory_desktop/browse:media:flickr_infinite_scroll [ Skip ]
+crbug.com/836407 [ linux ] system_health.memory_desktop/browse:tools:maps [ Skip ]
+crbug.com/934885 [ linux ] system_health.memory_desktop/load_accessibility:media:wikipedia:2018 [ Skip ]
+crbug.com/869118 [ linux ] system_health.memory_desktop/long_running:tools:gmail-background [ Skip ]
+crbug.com/836447 [ chromeos ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
+crbug.com/899887 [ linux ] system_health.memory_desktop/browse:social:facebook_infinite_scroll:2018 [ Skip ]
+crbug.com/924330 [ linux ] system_health.memory_desktop/browse:media:pinterest:2018 [ Skip ]
+crbug.com/874803 [ win10 ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
+crbug.com/944978 [ win7 ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
+crbug.com/934270 [ win ] system_health.memory_desktop/multitab:misc:typical24:2018 [ Skip ]
+crbug.com/942952 [ chromeos ] system_health.memory_desktop/browse:news:hackernews:2018 [ Skip ]
+crbug.com/959418 [ chromeos ] system_health.memory_desktop/long_running:tools:gmail-background [ Skip ]
+crbug.com/959418 [ chromeos ] system_health.memory_desktop/load:games:spychase:2018 [ Skip ]
+crbug.com/959418 [ chromeos ] system_health.memory_desktop/long_running:tools:gmail-foreground [ Skip ]
 
 # Benchmark: system_health.memory_mobile
-crbug.com/914390 [ Nexus_5 ] system_health.memory_mobile/browse:chrome:newtab [ Skip ]
-crbug.com/714650 [ Android ] system_health.memory_mobile/browse:news:globo [ Skip ]
-crbug.com/819552 [ Nexus_5X ] system_health.memory_mobile/browse:chrome:omnibox [ Skip ]
-crbug.com/867853 [ Nexus_5X ] system_health.memory_mobile/browse:chrome:newtab [ Skip ]
-crbug.com/657433 [ Android ] system_health.memory_mobile/load:tools:gmail [ Skip ]
-crbug.com/708300 [ Android ] system_health.memory_mobile/browse:shopping:flipkart [ Skip ]
-crbug.com/784400 [ Nexus_5 ] system_health.memory_mobile/background:tools:gmail [ Skip ]
-crbug.com/910207 [ Nexus_5X ] system_health.memory_mobile/background:tools:gmail [ Skip ]
-crbug.com/780779 [ Nexus_5 ] system_health.memory_mobile/browse:social:facebook [ Skip ]
-crbug.com/910207 [ Nexus_5X ] system_health.memory_mobile/browse:social:facebook [ Skip ]
-crbug.com/738854 [ Nexus_5X ] system_health.memory_mobile/load:tools:drive [ Skip ]
-crbug.com/738854 [ Android_Webview ] system_health.memory_mobile/load:tools:drive [ Skip ]
-crbug.com/834905 [ Nexus_5 ] system_health.memory_mobile/browse:social:pinterest_infinite_scroll [ Skip ]
-crbug.com/910207 [ Nexus_5X ] system_health.memory_mobile/browse:social:pinterest_infinite_scroll [ Skip ]
-crbug.com/843547 [ Android_Go ] system_health.memory_mobile/background:news:nytimes [ Skip ]
-crbug.com/852888 [ Nexus_5X ] system_health.memory_mobile/background:news:nytimes [ Skip ]
-crbug.com/871708 [ Android_Webview ] system_health.memory_mobile/browse:social:facebook_infinite_scroll [ Skip ]
-crbug.com/865400 [ Pixel_2 ] system_health.memory_mobile/background:news:nytimes [ Skip ]
-crbug.com/877648 [ Android ] system_health.memory_mobile/long_running:tools:gmail-background [ Skip ]
-crbug.com/877648 [ Android ] system_health.memory_mobile/long_running:tools:gmail-foreground [ Skip ]
-crbug.com/883320 [ Android_Go ] system_health.memory_mobile/browse:news:cnn:2018 [ Skip ]
-crbug.com/880652 [ Nexus_5X ] system_health.memory_mobile/browse:shopping:avito [ Skip ]
-crbug.com/883652 [ Nexus_5 ] system_health.memory_mobile/browse:shopping:avito [ Skip ]
-crbug.com/923116 [ Android ] system_health.memory_mobile/browse:shopping:avito [ Skip ]
-crbug.com/893873 [ Nexus_5X ] system_health.memory_mobile/load:news:washingtonpost [ Skip ]
-crbug.com/896851 [ Android ] system_health.memory_mobile/background:tools:gmail [ Skip ]
-crbug.com/892704 [ Android_Webview ] system_health.memory_mobile/browse:tech:discourse_infinite_scroll:2018 [ Skip ]
-crbug.com/923527 [ Android_Webview ] system_health.memory_mobile/load:media:soundcloud:2018 [ Skip ]
-crbug.com/947267 [ Nexus_5X ] system_health.memory_mobile/background:media:imgur [ Skip ]
-crbug.com/954949 [ Nexus5X_Webview ] system_health.memory_mobile/browse:news:washingtonpost [ Skip ]
-crbug.com/961417 [ Android_Go ] system_health.memory_mobile/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
+crbug.com/914390 [ android-nexus-5 ] system_health.memory_mobile/browse:chrome:newtab [ Skip ]
+crbug.com/714650 [ android ] system_health.memory_mobile/browse:news:globo [ Skip ]
+crbug.com/819552 [ android-nexus-5x ] system_health.memory_mobile/browse:chrome:omnibox [ Skip ]
+crbug.com/867853 [ android-nexus-5x ] system_health.memory_mobile/browse:chrome:newtab [ Skip ]
+crbug.com/657433 [ android ] system_health.memory_mobile/load:tools:gmail [ Skip ]
+crbug.com/708300 [ android ] system_health.memory_mobile/browse:shopping:flipkart [ Skip ]
+crbug.com/784400 [ android-nexus-5 ] system_health.memory_mobile/background:tools:gmail [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] system_health.memory_mobile/background:tools:gmail [ Skip ]
+crbug.com/780779 [ android-nexus-5 ] system_health.memory_mobile/browse:social:facebook [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] system_health.memory_mobile/browse:social:facebook [ Skip ]
+crbug.com/738854 [ android-nexus-5x ] system_health.memory_mobile/load:tools:drive [ Skip ]
+crbug.com/738854 [ android-webview ] system_health.memory_mobile/load:tools:drive [ Skip ]
+crbug.com/834905 [ android-nexus-5 ] system_health.memory_mobile/browse:social:pinterest_infinite_scroll [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] system_health.memory_mobile/browse:social:pinterest_infinite_scroll [ Skip ]
+crbug.com/843547 [ android-go ] system_health.memory_mobile/background:news:nytimes [ Skip ]
+crbug.com/852888 [ android-nexus-5x ] system_health.memory_mobile/background:news:nytimes [ Skip ]
+crbug.com/871708 [ android-webview ] system_health.memory_mobile/browse:social:facebook_infinite_scroll [ Skip ]
+crbug.com/865400 [ android-pixel-2 ] system_health.memory_mobile/background:news:nytimes [ Skip ]
+crbug.com/877648 [ android ] system_health.memory_mobile/long_running:tools:gmail-background [ Skip ]
+crbug.com/877648 [ android ] system_health.memory_mobile/long_running:tools:gmail-foreground [ Skip ]
+crbug.com/883320 [ android-go ] system_health.memory_mobile/browse:news:cnn:2018 [ Skip ]
+crbug.com/880652 [ android-nexus-5x ] system_health.memory_mobile/browse:shopping:avito [ Skip ]
+crbug.com/883652 [ android-nexus-5 ] system_health.memory_mobile/browse:shopping:avito [ Skip ]
+crbug.com/923116 [ android ] system_health.memory_mobile/browse:shopping:avito [ Skip ]
+crbug.com/893873 [ android-nexus-5x ] system_health.memory_mobile/load:news:washingtonpost [ Skip ]
+crbug.com/896851 [ android ] system_health.memory_mobile/background:tools:gmail [ Skip ]
+crbug.com/892704 [ android-webview ] system_health.memory_mobile/browse:tech:discourse_infinite_scroll:2018 [ Skip ]
+crbug.com/923527 [ android-webview ] system_health.memory_mobile/load:media:soundcloud:2018 [ Skip ]
+crbug.com/947267 [ android-nexus-5x ] system_health.memory_mobile/background:media:imgur [ Skip ]
+crbug.com/954949 [ android-nexus-5x android-webview ] system_health.memory_mobile/browse:news:washingtonpost [ Skip ]
+crbug.com/961417 [ android-go ] system_health.memory_mobile/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
 
-crbug.com/964960 [ Nexus_5X ] system_health.memory_mobile/* [ Skip ]
+crbug.com/964960 [ android-nexus-5x ] system_health.memory_mobile/* [ Skip ]
 
 # Benchmark: tab_switching.typical_25
-crbug.com/747026 [ Mac ] tab_switching.typical_25/multitab:misc:typical24 [ Skip ]
-crbug.com/883731 [ Win ] tab_switching.typical_25/multitab:misc:typical24 [ Skip ]
+crbug.com/747026 [ mac ] tab_switching.typical_25/multitab:misc:typical24 [ Skip ]
+crbug.com/883731 [ win ] tab_switching.typical_25/multitab:misc:typical24 [ Skip ]
 
 # Benchmark: tracing.tracing_with_background_memory_infra
-crbug.com/914092 [ Win ] tracing.tracing_with_background_memory_infra/http://www.bing.com/ [ Skip ]
-crbug.com/914092 [ Win ] tracing.tracing_with_background_memory_infra/http://www.amazon.com [ Skip ]
+crbug.com/914092 [ win ] tracing.tracing_with_background_memory_infra/http://www.bing.com/ [ Skip ]
+crbug.com/914092 [ win ] tracing.tracing_with_background_memory_infra/http://www.amazon.com [ Skip ]
 
 # Benchmark: v8.browsing_desktop
-crbug.com/773084 [ Mac ] v8.browsing_desktop/browse:tools:maps [ Skip ]
-crbug.com/788796 [ Linux ] v8.browsing_desktop/browse:media:imgur [ Skip ]
-crbug.com/953371 [ Desktop ] v8.browsing_desktop/browse:social:twitter_infinite_scroll:2018 [ Skip ]
-crbug.com/875159 [ Win_10 ] v8.browsing_desktop/browse:media:imgur [ Skip ]
-crbug.com/953371 [ Win ] v8.browsing_desktop/browse:social:twitter_infinite_scroll:2018 [ Skip ]
-crbug.com/954959 [ Linux ] v8.browsing_desktop/browse:media:pinterest:2018 [ Skip ]
-crbug.com/954959 [ Linux ] v8.browsing_desktop/browse:tools:maps [ Skip ]
-crbug.com/958422 [ All ] v8.browsing_desktop/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
-crbug.com/958507 [ Desktop ] v8.browsing_desktop/browse:media:imgur [ Skip ]
+crbug.com/773084 [ mac ] v8.browsing_desktop/browse:tools:maps [ Skip ]
+crbug.com/788796 [ linux ] v8.browsing_desktop/browse:media:imgur [ Skip ]
+crbug.com/953371 [ desktop ] v8.browsing_desktop/browse:social:twitter_infinite_scroll:2018 [ Skip ]
+crbug.com/875159 [ win10 ] v8.browsing_desktop/browse:media:imgur [ Skip ]
+crbug.com/953371 [ win ] v8.browsing_desktop/browse:social:twitter_infinite_scroll:2018 [ Skip ]
+crbug.com/954959 [ linux ] v8.browsing_desktop/browse:media:pinterest:2018 [ Skip ]
+crbug.com/954959 [ linux ] v8.browsing_desktop/browse:tools:maps [ Skip ]
+crbug.com/958422 v8.browsing_desktop/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
+crbug.com/958507 [ desktop ] v8.browsing_desktop/browse:media:imgur [ 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 [ All ] 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 [ All ] 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/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 ]
 
 # Benchmark: v8.browsing_mobile
-crbug.com/958034 [ Android_Go_Webview ] v8.browsing_mobile/* [ Skip ]
-crbug.com/714650 [ Android ] v8.browsing_mobile/browse:news:globo [ Skip ]
-crbug.com/767970 [ Android ] v8.browsing_mobile/browse:shopping:flipkart [ Skip ]
-crbug.com/708300 [ Android ] v8.browsing_mobile/browse:shopping:flipkart [ Skip ]
-crbug.com/815175 [ Nexus_5 ] v8.browsing_mobile/browse:chrome:newtab [ Skip ]
-crbug.com/910207 [ Nexus_5X ] v8.browsing_mobile/browse:chrome:newtab [ Skip ]
-crbug.com/853212 [ Android_Webview ] v8.browsing_mobile/browse:media:youtube [ Skip ]
-crbug.com/877648 [ Android_Go ] v8.browsing_mobile/browse:news:toi [ Skip ]
-crbug.com/954260 [ Android ] v8.browsing_mobile/browse:news:toi [ Skip ]
-crbug.com/950757 [ Android_Go ] v8.browsing_mobile/browse:news:cnn:2018 [ Skip ]
-crbug.com/901967 [ Nexus6_Webview ] v8.browsing_mobile/browse:news:toi [ Skip ]
-crbug.com/923116 [ Android ] v8.browsing_mobile/browse:shopping:avito [ Skip ]
-crbug.com/929839 [ Android_Go ] v8.browsing_mobile/browse:chrome:newtab [ Skip ]
-crbug.com/954949 [ Nexus5X_Webview ] v8.browsing_mobile/browse:news:washingtonpost [ Skip ]
-crbug.com/961417 [ Android_Go ] v8.browsing_mobile/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
-crbug.com/979786 [ Nexus5X_Webview ] v8.browsing_mobile/browse:media:flickr_infinite_scroll [ Skip ]
+crbug.com/958034 [ android-go android-webview ] v8.browsing_mobile/* [ Skip ]
+crbug.com/714650 [ android ] v8.browsing_mobile/browse:news:globo [ Skip ]
+crbug.com/767970 [ android ] v8.browsing_mobile/browse:shopping:flipkart [ Skip ]
+crbug.com/708300 [ android ] v8.browsing_mobile/browse:shopping:flipkart [ Skip ]
+crbug.com/815175 [ android-nexus-5 ] v8.browsing_mobile/browse:chrome:newtab [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] v8.browsing_mobile/browse:chrome:newtab [ Skip ]
+crbug.com/853212 [ android-webview ] v8.browsing_mobile/browse:media:youtube [ Skip ]
+crbug.com/877648 [ android-go ] v8.browsing_mobile/browse:news:toi [ Skip ]
+crbug.com/954260 [ android ] v8.browsing_mobile/browse:news:toi [ Skip ]
+crbug.com/950757 [ android-go ] v8.browsing_mobile/browse:news:cnn:2018 [ Skip ]
+crbug.com/901967 [ android-nexus-6 android-webview ] v8.browsing_mobile/browse:news:toi [ Skip ]
+crbug.com/923116 [ android ] v8.browsing_mobile/browse:shopping:avito [ Skip ]
+crbug.com/929839 [ android-go ] v8.browsing_mobile/browse:chrome:newtab [ Skip ]
+crbug.com/954949 [ android-nexus-5x android-webview ] v8.browsing_mobile/browse:news:washingtonpost [ Skip ]
+crbug.com/961417 [ android-go ] v8.browsing_mobile/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
+crbug.com/979786 [ android-nexus-5x android-webview ] v8.browsing_mobile/browse:media:flickr_infinite_scroll [ Skip ]
 
 # Benchmark: v8.browsing_mobile-future
-crbug.com/714650 [ Android ] v8.browsing_mobile-future/browse:news:globo [ Skip ]
-crbug.com/803465 [ Nexus_5 ] v8.browsing_mobile-future/browse:chrome:newtab [ Skip ]
-crbug.com/910207 [ Nexus_5X ] v8.browsing_mobile-future/browse:chrome:newtab [ Skip ]
-crbug.com/799080 [ Nexus_5X Android_Webview ] v8.browsing_mobile-future/browse:social:facebook [ Skip ]
-crbug.com/799080 [ Nexus_5X Android_Webview ] v8.browsing_mobile-future/browse:social:facebook [ Skip ]
-crbug.com/901534 [ Nexus6_Webview ] v8.browsing_mobile-future/browse:news:toi [ Skip ]
-crbug.com/954260 [ Android ] v8.browsing_mobile-future/browse:news:toi [ Skip ]
-crbug.com/865400 [ Pixel_2 ] v8.browsing_mobile-future/browse:shopping:avito [ Skip ]
-crbug.com/923116 [ Android ] v8.browsing_mobile-future/browse:shopping:avito [ Skip ]
-crbug.com/954949 [ Nexus5X_Webview ] v8.browsing_mobile-future/browse:news:washingtonpost [ Skip ]
-crbug.com/958336 [ Android ] v8.browsing_mobile-future/browse:shopping:flipkart [ Skip ]
+crbug.com/714650 [ android ] v8.browsing_mobile-future/browse:news:globo [ Skip ]
+crbug.com/803465 [ android-nexus-5 ] v8.browsing_mobile-future/browse:chrome:newtab [ Skip ]
+crbug.com/910207 [ android-nexus-5x ] v8.browsing_mobile-future/browse:chrome:newtab [ Skip ]
+crbug.com/799080 [ android-nexus-5x android-webview ] v8.browsing_mobile-future/browse:social:facebook [ Skip ]
+crbug.com/799080 [ android-nexus-5x android-webview ] v8.browsing_mobile-future/browse:social:facebook [ Skip ]
+crbug.com/901534 [ android-nexus-6 android-webview ] v8.browsing_mobile-future/browse:news:toi [ Skip ]
+crbug.com/954260 [ android ] v8.browsing_mobile-future/browse:news:toi [ Skip ]
+crbug.com/865400 [ android-pixel-2 ] v8.browsing_mobile-future/browse:shopping:avito [ Skip ]
+crbug.com/923116 [ android ] v8.browsing_mobile-future/browse:shopping:avito [ Skip ]
+crbug.com/954949 [ android-nexus-5x android-webview ] v8.browsing_mobile-future/browse:news:washingtonpost [ Skip ]
+crbug.com/958336 [ android ] v8.browsing_mobile-future/browse:shopping:flipkart [ Skip ]
 
 # Benchmark: v8.runtime_stats.top_25
-crbug.com/954229 [ Mac ] v8.runtime_stats.top_25/* [ Skip ]
+crbug.com/954229 [ mac ] v8.runtime_stats.top_25/* [ Skip ]
 
 ##### Perf FYI benchmarks go after here #####
 # Benchmark: loading.desktop_layout_ng
-crbug.com/879833 [ Linux ] loading.desktop_layout_ng/Walgreens_cold [ Skip ]
-crbug.com/879833 [ Linux ] loading.desktop_layout_ng/Walgreens_warm [ Skip ]
+crbug.com/879833 [ linux ] loading.desktop_layout_ng/Walgreens_cold [ Skip ]
+crbug.com/879833 [ linux ] loading.desktop_layout_ng/Walgreens_warm [ Skip ]
diff --git a/tools/perf/page_sets/__init__.py b/tools/perf/page_sets/__init__.py
index 14a4e2c..ebbcb6e 100644
--- a/tools/perf/page_sets/__init__.py
+++ b/tools/perf/page_sets/__init__.py
@@ -7,14 +7,13 @@
 import sys
 
 from telemetry import story
-from telemetry.story import expectations
 
 from py_utils import discover
 
 # Import all submodules' PageSet classes.
 start_dir = os.path.dirname(os.path.abspath(__file__))
 top_level_dir = os.path.dirname(start_dir)
-base_classes = [story.StorySet, expectations.StoryExpectations]
+base_classes = [story.StorySet]
 
 for base_class in base_classes:
   for cls in discover.DiscoverClasses(
diff --git a/tools/perf/page_sets/data/typical_25.json b/tools/perf/page_sets/data/typical_25.json
deleted file mode 100644
index 52e9a08..0000000
--- a/tools/perf/page_sets/data/typical_25.json
+++ /dev/null
@@ -1,150 +0,0 @@
-{
-    "archives": {
-        "http://allrecipes.com/Recipe/Pull-Apart-Hot-Cross-Buns/Detail.aspx_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://allrecipes.com/Recipe/Pull-Apart-Hot-Cross-Buns/Detail.aspx_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://arstechnica.com/_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://arstechnica.com/_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://colorado.edu_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://colorado.edu_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://gawker.com/5939683/based-on-a-true-story-is-a-rotten-lie-i-hope-you-never-believe_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://gawker.com/5939683/based-on-a-true-story-is-a-rotten-lie-i-hope-you-never-believe_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://money.cnn.com/_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://money.cnn.com/_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://premierleague.com_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://premierleague.com_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://walgreens.com_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://walgreens.com_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.airbnb.com/_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.airbnb.com/_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.economist.com/news/science-and-technology/21573529-small-models-cosmic-phenomena-are-shedding-light-real-thing-how-build_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.economist.com/news/science-and-technology/21573529-small-models-cosmic-phenomena-are-shedding-light-real-thing-how-build_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.fda.gov_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.fda.gov_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.fifa.com/_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.fifa.com/_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.flickr.com/search/?q=monkeys&f=hp_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.flickr.com/search/?q=monkeys&f=hp_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.gamestop.com/ps3_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.gamestop.com/ps3_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.html5rocks.com/en/_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.html5rocks.com/en/_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.ign.com/_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.ign.com/_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.imdb.com/title/tt0910970/_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.imdb.com/title/tt0910970/_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.mlb.com/_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.mlb.com/_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.nationalgeographic.com/_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.nationalgeographic.com/_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.nick.com/games_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.nick.com/games_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.osubeavers.com/_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.osubeavers.com/_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.rei.com/_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.rei.com/_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.theonion.com_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.theonion.com_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.theverge.com/2013/3/5/4061684/inside-ted-the-smartest-bubble-in-the-world_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.theverge.com/2013/3/5/4061684/inside-ted-the-smartest-bubble-in-the-world_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.ticketmaster.com/JAY-Z-and-Justin-Timberlake-tickets/artist/1837448?brand=none&tm_link=tm_homeA_rc_name2_cold": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }, 
-        "http://www.ticketmaster.com/JAY-Z-and-Justin-Timberlake-tickets/artist/1837448?brand=none&tm_link=tm_homeA_rc_name2_warm": {
-            "DEFAULT": "typical_25_002.wprgo"
-        }
-    }, 
-    "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.", 
-    "platform_specific": true
-}
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/typical_25_002.wprgo.sha1 b/tools/perf/page_sets/data/typical_25_002.wprgo.sha1
deleted file mode 100644
index 9176953..0000000
--- a/tools/perf/page_sets/data/typical_25_002.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-63d6d450e5517411e8fc3d4d095bb5e87e739f37
\ No newline at end of file
diff --git a/tools/perf/page_sets/typical_25.py b/tools/perf/page_sets/typical_25.py
deleted file mode 100644
index 5977a41..0000000
--- a/tools/perf/page_sets/typical_25.py
+++ /dev/null
@@ -1,87 +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.
-
-from telemetry.page import page as page_module
-from telemetry.page import cache_temperature as cache_temperature_module
-from telemetry.page import shared_page_state
-from telemetry import story
-
-
-class Typical25Page(page_module.Page):
-
-  def __init__(self, url, page_set,
-      shared_page_state_class=shared_page_state.SharedDesktopPageState,
-      cache_temperature=None):
-    if cache_temperature == cache_temperature_module.COLD:
-      temp_suffix = '_cold'
-    elif cache_temperature == cache_temperature_module.WARM:
-      temp_suffix = '_warm'
-    else:
-      raise NotImplementedError
-    super(Typical25Page, self).__init__(
-        url=url, page_set=page_set,
-        shared_page_state_class=shared_page_state_class,
-        cache_temperature=cache_temperature, name=url + temp_suffix)
-    if cache_temperature != cache_temperature_module.ANY:
-      self.grouping_keys['cache_temperature'] = cache_temperature
-
-  def RunPageInteractions(self, action_runner):
-    action_runner.WaitForJavaScriptCondition(
-        'performance.timing.loadEventStart > 0')
-    return
-
-
-class Typical25PageSet(story.StorySet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self, cache_temperatures=(cache_temperature_module.COLD,
-                                         cache_temperature_module.WARM)):
-    super(Typical25PageSet, self).__init__(
-      archive_data_file='data/typical_25.json',
-      cloud_storage_bucket=story.PARTNER_BUCKET)
-    if cache_temperatures is None:
-      cache_temperatures = [cache_temperature_module.ANY]
-
-    urls_list = [
-      # Why: Alexa games #48
-      'http://www.nick.com/games',
-      # Why: Alexa sports #45
-      'http://www.rei.com/',
-      # Why: Alexa sports #50
-      'http://www.fifa.com/',
-      # Why: Alexa shopping #41
-      'http://www.gamestop.com/ps3',
-      # Why: Alexa news #55
-      ('http://www.economist.com/news/science-and-technology/21573529-small-'
-       'models-cosmic-phenomena-are-shedding-light-real-thing-how-build'),
-      # Why: Alexa news #67
-      'http://www.theonion.com',
-      'http://arstechnica.com/',
-      # Why: Alexa home #10
-      'http://allrecipes.com/Recipe/Pull-Apart-Hot-Cross-Buns/Detail.aspx',
-      'http://www.html5rocks.com/en/',
-      'http://www.mlb.com/',
-      'http://gawker.com/5939683/based-on-a-true-story-is-a-rotten-lie-i-hope-you-never-believe',
-      'http://www.imdb.com/title/tt0910970/',
-      'http://www.flickr.com/search/?q=monkeys&f=hp',
-      'http://money.cnn.com/',
-      'http://www.nationalgeographic.com/',
-      'http://premierleague.com',
-      'http://www.osubeavers.com/',
-      'http://walgreens.com',
-      'http://colorado.edu',
-      ('http://www.ticketmaster.com/JAY-Z-and-Justin-Timberlake-tickets/artist/'
-       '1837448?brand=none&tm_link=tm_homeA_rc_name2'),
-      # pylint: disable=line-too-long
-      'http://www.theverge.com/2013/3/5/4061684/inside-ted-the-smartest-bubble-in-the-world',
-      'http://www.airbnb.com/',
-      'http://www.ign.com/',
-      # Why: Alexa health #25
-      'http://www.fda.gov',
-    ]
-
-    for url in urls_list:
-      for temp in cache_temperatures:
-        self.AddStory(Typical25Page(url, self, cache_temperature=temp))
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc
index e7e564b..05cb96d 100644
--- a/ui/accessibility/ax_node.cc
+++ b/ui/accessibility/ax_node.cc
@@ -153,8 +153,9 @@
 
 bool AXNode::IsLineBreak() const {
   return data().role == ax::mojom::Role::kLineBreak ||
-         (IsText() && parent() &&
-          parent()->data().role == ax::mojom::Role::kLineBreak);
+         (data().role == ax::mojom::Role::kInlineTextBox &&
+          data().GetBoolAttribute(
+              ax::mojom::BoolAttribute::kIsLineBreakingObject));
 }
 
 void AXNode::SetData(const AXNodeData& src) {
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
index 4409cbe..64c03487 100644
--- a/ui/accessibility/ax_node_position_unittest.cc
+++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -924,6 +924,344 @@
   EXPECT_TRUE(text_position->AtEndOfParagraph());
 }
 
+TEST_F(AXPositionTest, ParagraphEdgesWithPreservedNewLine) {
+  // This test updates the tree structure to test a specific edge case -
+  // At{Start|End}OfParagraph when an ancestor position can resolve to a
+  // preserved newline descendant.
+  // ++1 kRootWebArea isLineBreakingObject
+  // ++++2 kStaticText
+  // ++++++3 kInlineTextBox "some text"
+  // ++++4 kGenericContainer isLineBreakingObject
+  // ++++++5 kStaticText
+  // ++++++++6 kInlineTextBox "\n" isLineBreakingObject
+  // ++++++++7 kInlineTextBox "more text"
+  AXNodePosition::SetTreeForTesting(nullptr);
+
+  ui::AXNodeData root_data;
+  root_data.id = 1;
+  root_data.role = ax::mojom::Role::kRootWebArea;
+  root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
+                             true);
+
+  ui::AXNodeData static_text_data_1;
+  static_text_data_1.id = 2;
+  static_text_data_1.role = ax::mojom::Role::kStaticText;
+  static_text_data_1.SetName("some text");
+
+  ui::AXNodeData some_text_data;
+  some_text_data.id = 3;
+  some_text_data.role = ax::mojom::Role::kInlineTextBox;
+  some_text_data.SetName("some text");
+
+  ui::AXNodeData container_data;
+  container_data.id = 4;
+  container_data.role = ax::mojom::Role::kGenericContainer;
+  container_data.AddBoolAttribute(
+      ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
+
+  ui::AXNodeData static_text_data_2;
+  static_text_data_2.id = 5;
+  static_text_data_2.role = ax::mojom::Role::kStaticText;
+  static_text_data_2.SetName("\nmore text");
+
+  ui::AXNodeData preserved_newline_data;
+  preserved_newline_data.id = 6;
+  preserved_newline_data.role = ax::mojom::Role::kInlineTextBox;
+  preserved_newline_data.SetName("\n");
+  preserved_newline_data.AddBoolAttribute(
+      ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
+
+  ui::AXNodeData more_text_data;
+  more_text_data.id = 7;
+  more_text_data.role = ax::mojom::Role::kInlineTextBox;
+  more_text_data.SetName("more text");
+
+  static_text_data_1.child_ids = {3};
+  container_data.child_ids = {5};
+  static_text_data_2.child_ids = {6, 7};
+  root_data.child_ids = {2, 4};
+
+  ui::AXTreeUpdate update;
+  ui::AXTreeData tree_data;
+  tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
+  update.tree_data = tree_data;
+  update.has_tree_data = true;
+  update.root_id = root_data.id;
+  update.nodes = {root_data,      static_text_data_1, some_text_data,
+                  container_data, static_text_data_2, preserved_newline_data,
+                  more_text_data};
+
+  std::unique_ptr<AXTree> new_tree;
+  new_tree.reset(new AXTree(update));
+  AXNodePosition::SetTreeForTesting(new_tree.get());
+
+  TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, root_data.id, 8 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position1->AtEndOfParagraph());
+  EXPECT_FALSE(text_position1->AtStartOfParagraph());
+
+  TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, root_data.id, 9 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position2->AtEndOfParagraph());
+  EXPECT_FALSE(text_position2->AtStartOfParagraph());
+
+  TestPositionType text_position3 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, root_data.id, 9 /* text_offset */,
+      ax::mojom::TextAffinity::kUpstream);
+  EXPECT_FALSE(text_position3->AtEndOfParagraph());
+  EXPECT_FALSE(text_position3->AtStartOfParagraph());
+
+  TestPositionType text_position4 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, root_data.id, 10 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position4->AtEndOfParagraph());
+  EXPECT_TRUE(text_position4->AtStartOfParagraph());
+
+  TestPositionType text_position5 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, root_data.id, 10 /* text_offset */,
+      ax::mojom::TextAffinity::kUpstream);
+  EXPECT_TRUE(text_position5->AtEndOfParagraph());
+  EXPECT_FALSE(text_position5->AtStartOfParagraph());
+
+  TestPositionType text_position6 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, container_data.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position6->AtEndOfParagraph());
+  EXPECT_FALSE(text_position6->AtStartOfParagraph());
+
+  TestPositionType text_position7 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, container_data.id, 1 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position7->AtEndOfParagraph());
+  EXPECT_TRUE(text_position7->AtStartOfParagraph());
+
+  TestPositionType text_position8 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, container_data.id, 1 /* text_offset */,
+      ax::mojom::TextAffinity::kUpstream);
+  EXPECT_TRUE(text_position8->AtEndOfParagraph());
+  EXPECT_FALSE(text_position8->AtStartOfParagraph());
+
+  TestPositionType text_position9 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, static_text_data_2.id, 1 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position9->AtEndOfParagraph());
+  EXPECT_TRUE(text_position9->AtStartOfParagraph());
+
+  TestPositionType text_position10 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, static_text_data_2.id, 1 /* text_offset */,
+      ax::mojom::TextAffinity::kUpstream);
+  EXPECT_TRUE(text_position10->AtEndOfParagraph());
+  EXPECT_FALSE(text_position10->AtStartOfParagraph());
+
+  TestPositionType text_position11 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, preserved_newline_data.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position11->AtEndOfParagraph());
+  EXPECT_FALSE(text_position11->AtStartOfParagraph());
+
+  TestPositionType text_position12 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, preserved_newline_data.id, 1 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_TRUE(text_position12->AtEndOfParagraph());
+  EXPECT_FALSE(text_position12->AtStartOfParagraph());
+
+  TestPositionType text_position13 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, more_text_data.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position13->AtEndOfParagraph());
+  EXPECT_TRUE(text_position13->AtStartOfParagraph());
+
+  TestPositionType text_position14 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, more_text_data.id, 1 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position14->AtEndOfParagraph());
+  EXPECT_FALSE(text_position14->AtStartOfParagraph());
+}
+
+TEST_F(AXPositionTest,
+       AtStartOrEndOfParagraphWithLeadingAndTrailingDocumentWhitespace) {
+  // This test updates the tree structure to test a specific edge case -
+  // At{Start|End}OfParagraph when an ancestor position can resolve to a
+  // preserved newline descendant.
+  // ++1 kRootWebArea isLineBreakingObject
+  // ++++2 kGenericContainer isLineBreakingObject
+  // ++++++3 kStaticText
+  // ++++++++4 kInlineTextBox "\n" isLineBreakingObject
+  // ++++5 kGenericContainer isLineBreakingObject
+  // ++++++6 kStaticText
+  // ++++++++7 kInlineTextBox "some"
+  // ++++++++8 kInlineTextBox " "
+  // ++++++++9 kInlineTextBox "text"
+  // ++++10 kGenericContainer isLineBreakingObject
+  // ++++++11 kStaticText
+  // ++++++++12 kInlineTextBox "\n" isLineBreakingObject
+  AXNodePosition::SetTreeForTesting(nullptr);
+
+  ui::AXNodeData root_data;
+  root_data.id = 1;
+  root_data.role = ax::mojom::Role::kRootWebArea;
+  root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
+                             true);
+
+  ui::AXNodeData container_data_a;
+  container_data_a.id = 2;
+  container_data_a.role = ax::mojom::Role::kGenericContainer;
+  container_data_a.AddBoolAttribute(
+      ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
+
+  ui::AXNodeData static_text_data_a;
+  static_text_data_a.id = 3;
+  static_text_data_a.role = ax::mojom::Role::kStaticText;
+  static_text_data_a.SetName("\n");
+
+  ui::AXNodeData inline_text_data_a;
+  inline_text_data_a.id = 4;
+  inline_text_data_a.role = ax::mojom::Role::kInlineTextBox;
+  inline_text_data_a.SetName("\n");
+  inline_text_data_a.AddBoolAttribute(
+      ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
+
+  ui::AXNodeData container_data_b;
+  container_data_b.id = 5;
+  container_data_b.role = ax::mojom::Role::kGenericContainer;
+  container_data_b.AddBoolAttribute(
+      ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
+
+  ui::AXNodeData static_text_data_b;
+  static_text_data_b.id = 6;
+  static_text_data_b.role = ax::mojom::Role::kStaticText;
+  static_text_data_b.SetName("some text");
+
+  ui::AXNodeData inline_text_data_b_1;
+  inline_text_data_b_1.id = 7;
+  inline_text_data_b_1.role = ax::mojom::Role::kInlineTextBox;
+  inline_text_data_b_1.SetName("some");
+
+  ui::AXNodeData inline_text_data_b_2;
+  inline_text_data_b_2.id = 8;
+  inline_text_data_b_2.role = ax::mojom::Role::kInlineTextBox;
+  inline_text_data_b_2.SetName(" ");
+
+  ui::AXNodeData inline_text_data_b_3;
+  inline_text_data_b_3.id = 9;
+  inline_text_data_b_3.role = ax::mojom::Role::kInlineTextBox;
+  inline_text_data_b_3.SetName("text");
+
+  ui::AXNodeData container_data_c;
+  container_data_c.id = 10;
+  container_data_c.role = ax::mojom::Role::kGenericContainer;
+  container_data_c.AddBoolAttribute(
+      ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
+
+  ui::AXNodeData static_text_data_c;
+  static_text_data_c.id = 11;
+  static_text_data_c.role = ax::mojom::Role::kStaticText;
+  static_text_data_c.SetName("\n");
+
+  ui::AXNodeData inline_text_data_c;
+  inline_text_data_c.id = 12;
+  inline_text_data_c.role = ax::mojom::Role::kInlineTextBox;
+  inline_text_data_c.SetName("\n");
+  inline_text_data_c.AddBoolAttribute(
+      ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
+
+  root_data.child_ids = {container_data_a.id, container_data_b.id,
+                         container_data_c.id};
+  container_data_a.child_ids = {static_text_data_a.id};
+  static_text_data_a.child_ids = {inline_text_data_a.id};
+  container_data_b.child_ids = {static_text_data_b.id};
+  static_text_data_b.child_ids = {inline_text_data_b_1.id,
+                                  inline_text_data_b_2.id,
+                                  inline_text_data_b_3.id};
+  container_data_c.child_ids = {static_text_data_c.id};
+  static_text_data_c.child_ids = {inline_text_data_c.id};
+
+  ui::AXTreeUpdate update;
+  ui::AXTreeData tree_data;
+  tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
+  update.tree_data = tree_data;
+  update.has_tree_data = true;
+  update.root_id = root_data.id;
+  update.nodes = {root_data,
+                  container_data_a,
+                  container_data_b,
+                  container_data_c,
+                  static_text_data_a,
+                  static_text_data_b,
+                  static_text_data_c,
+                  inline_text_data_a,
+                  inline_text_data_b_1,
+                  inline_text_data_b_2,
+                  inline_text_data_b_3,
+                  inline_text_data_c};
+
+  std::unique_ptr<AXTree> new_tree;
+  new_tree.reset(new AXTree(update));
+  AXNodePosition::SetTreeForTesting(new_tree.get());
+
+  TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, inline_text_data_a.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position1->AtEndOfParagraph());
+  EXPECT_TRUE(text_position1->AtStartOfParagraph());
+
+  TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, inline_text_data_a.id, 1 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_TRUE(text_position2->AtEndOfParagraph());
+  EXPECT_FALSE(text_position2->AtStartOfParagraph());
+
+  TestPositionType text_position3 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, inline_text_data_b_1.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position3->AtEndOfParagraph());
+  EXPECT_TRUE(text_position3->AtStartOfParagraph());
+
+  TestPositionType text_position4 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, inline_text_data_b_1.id, 4 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position4->AtEndOfParagraph());
+  EXPECT_FALSE(text_position4->AtStartOfParagraph());
+
+  TestPositionType text_position5 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, inline_text_data_b_2.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position5->AtEndOfParagraph());
+  EXPECT_FALSE(text_position5->AtStartOfParagraph());
+
+  TestPositionType text_position6 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, inline_text_data_b_2.id, 1 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position6->AtEndOfParagraph());
+  EXPECT_FALSE(text_position6->AtStartOfParagraph());
+
+  TestPositionType text_position7 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, inline_text_data_b_3.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position7->AtEndOfParagraph());
+  EXPECT_FALSE(text_position7->AtStartOfParagraph());
+
+  TestPositionType text_position8 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, inline_text_data_b_3.id, 4 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position8->AtEndOfParagraph());
+  EXPECT_FALSE(text_position8->AtStartOfParagraph());
+
+  TestPositionType text_position9 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, inline_text_data_c.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_FALSE(text_position9->AtEndOfParagraph());
+  EXPECT_FALSE(text_position9->AtStartOfParagraph());
+
+  TestPositionType text_position10 = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, inline_text_data_c.id, 1 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_TRUE(text_position10->AtEndOfParagraph());
+  EXPECT_FALSE(text_position10->AtStartOfParagraph());
+}
+
 TEST_F(AXPositionTest, LowestCommonAncestor) {
   TestPositionType null_position = AXNodePosition::CreateNullPosition();
   ASSERT_NE(nullptr, null_position);
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index 12846a2..9e5d40b1 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -365,6 +365,20 @@
     return false;
   }
 
+  // |AtStartOfParagraph| is asymmetric from |AtEndOfParagraph| because of
+  // trailing whitespace collapse rules.
+  // The start of a paragraph should be a leaf text position (or equivalent),
+  // either at the start of the document, or at the start of the next leaf text
+  // position from the one representing the end of the previous paragraph.
+  // A position |AsLeafTextPosition| is the start of a paragraph if all of the
+  // following are true :
+  // 1. The current leaf text position must be at the start of an anchor.
+  // 2. The current position is not whitespace only, unless it is also
+  //    the first leaf text position within the document.
+  // 3. Either (a) the current leaf text position is the first leaf text
+  //    position in the document, or (b) there are no line breaking
+  //    objects between it and the previous non-whitespace leaf text
+  //    position.
   bool AtStartOfParagraph() const {
     AXPositionInstance text_position = AsLeafTextPosition();
     switch (text_position->kind_) {
@@ -374,24 +388,68 @@
         NOTREACHED();
         return false;
       case AXPositionKind::TEXT_POSITION: {
+        // 1. The current leaf text position must be at the start of an anchor.
         if (!text_position->AtStartOfAnchor())
           return false;
 
+        // 2. The current position is not whitespace only, unless it is also
+        //    the first leaf text position within the document.
+        if (text_position->IsInWhiteSpace()) {
+          return text_position->CreatePreviousLeafTextPosition()
+              ->IsNullPosition();
+        }
+
+        // 3. Either (a) the current leaf text position is the first leaf text
+        //    position in the document, or (b) there are no line breaking
+        //    objects between it and the previous non-whitespace leaf text
+        //    position.
+        //
         // Search for the previous text position within the current paragraph,
         // using the paragraph boundary abort predicate.
         // If a valid position was found, then this position cannot be
         // the start of a paragraph.
         // This will return a null position when an anchor movement would
         // cross a paragraph boundary, or the start of document was reached.
-        auto previous_text_position =
-            text_position->CreatePreviousTextAnchorPosition(
-                base::BindRepeating(&AbortMoveAtParagraphBoundary));
+        bool crossed_potential_boundary_token = false;
+        const AbortMovePredicate abort_move_predicate =
+            base::BindRepeating(&AbortMoveAtParagraphBoundary,
+                                std::ref(crossed_potential_boundary_token));
+        auto previous_text_position = text_position->Clone();
+        do {
+          previous_text_position =
+              previous_text_position->CreatePreviousTextAnchorPosition(
+                  abort_move_predicate);
+          // If the previous position is whitespace, then continue searching
+          // until a non-whitespace leaf text position is found within the
+          // current paragraph because whitespace is supposed to be collapsed.
+          // There's a chance that |CreatePreviousTextAnchorPosition| will
+          // return whitespace that should be appended to a previous paragraph
+          // rather than separating two pieces of the current paragraph.
+        } while (previous_text_position->IsInWhiteSpace());
         return previous_text_position->IsNullPosition();
       }
     }
     return false;
   }
 
+  // |AtEndOfParagraph| is asymmetric from |AtStartOfParagraph| because of
+  // trailing whitespace collapse rules.
+  // The end of a paragraph should be a leaf text position (or equivalent),
+  // either at the end of the document, or at the end of the previous leaf text
+  // position from the one representing the start of the next paragraph.
+  // A position |AsLeafTextPosition| is the end of a paragraph if all of the
+  // following are true :
+  // 1. The current leaf text position must be at the end of an anchor.
+  // 2. Either (a) the current leaf text position is the last leaf text
+  //    position in the document, or (b) there are no line breaking
+  //    objects between it and the next leaf text position except when
+  //    the next leaf text position is whitespace only since whitespace
+  //    must be collapsed.
+  // 3. If there is a next leaf text position then it must not be
+  //    whitespace only.
+  // 4. If there is a next leaf text position and it is not whitespace
+  //    only, it must also be the start of a paragraph for the current
+  //    position to be the end of a paragraph.
   bool AtEndOfParagraph() const {
     AXPositionInstance text_position = AsLeafTextPosition();
     switch (text_position->kind_) {
@@ -401,18 +459,56 @@
         NOTREACHED();
         return false;
       case AXPositionKind::TEXT_POSITION: {
+        // 1. The current leaf text position must be at the end of an anchor.
         if (!text_position->AtEndOfAnchor())
           return false;
 
+        // 2. Either (a) the current leaf text position is the last leaf text
+        //    position in the document, or (b) there are no line breaking
+        //    objects between it and the next leaf text position except when
+        //    the next leaf text position is whitespace only since whitespace
+        //    must be collapsed.
+        //
         // Search for the next text position within the current paragraph,
         // using the paragraph boundary abort predicate.
-        // If a valid position was found, then this position cannot be
-        // the end of a paragraph.
-        // This will return a null position when an anchor movement would
-        // cross a paragraph boundary, or the end of document was reached.
-        auto next_text_position = text_position->CreateNextTextAnchorPosition(
-            base::BindRepeating(&AbortMoveAtParagraphBoundary));
-        return next_text_position->IsNullPosition();
+        // If a null position was found, then this position must be the end of
+        // a paragraph.
+        // |CreateNextTextAnchorPosition| + |AbortMoveAtParagraphBoundary|
+        // will return a null position when an anchor movement would
+        // cross a paragraph boundary and there is no doubt that it is the end
+        // of a paragraph, or the end of document was reached.
+        // There are some fringe cases related to whitespace collapse that
+        // cannot be handled easily with only |AbortMoveAtParagraphBoundary|.
+        bool crossed_potential_boundary_token = false;
+        const AbortMovePredicate abort_move_predicate =
+            base::BindRepeating(&AbortMoveAtParagraphBoundary,
+                                std::ref(crossed_potential_boundary_token));
+        auto next_text_position =
+            text_position->CreateNextTextAnchorPosition(abort_move_predicate);
+        if (next_text_position->IsNullPosition())
+          return true;
+
+        // 3. If there is a next leaf text position then it must not be
+        //    whitespace only.
+        if (next_text_position->IsInWhiteSpace())
+          return false;
+
+        // 4. If there is a next leaf text position and it is not whitespace
+        //    only, it must also be the start of a paragraph for the current
+        //    position to be the end of a paragraph.
+        //
+        // Consider the following example :
+        // ++{1} kStaticText "First Paragraph"
+        // ++++{2} kInlineTextBox "First Paragraph"
+        // ++{3} kStaticText "\n Second Paragraph"
+        // ++++{4} kInlineTextBox "\n" kIsLineBreakingObject
+        // ++++{5} kInlineTextBox " "
+        // ++++{6} kInlineTextBox "Second Paragraph"
+        // A position at the end of {5} is the end of a paragraph, because
+        // the first paragraph must collapse trailing whitespace and contain
+        // leaf text anchors {2, 4, 5}. The second paragraph is only {6}.
+        return next_text_position->CreatePositionAtStartOfAnchor()
+            ->AtStartOfParagraph();
       }
     }
     return false;
@@ -2029,7 +2125,7 @@
 
   // Uses depth-first pre-order traversal.
   AXPositionInstance CreateNextAnchorPosition(
-      const AbortMovePredicate abort_predicate) const {
+      const AbortMovePredicate& abort_predicate) const {
     if (IsNullPosition())
       return CreateNullPosition();
 
@@ -2092,7 +2188,7 @@
 
   // Uses depth-first pre-order traversal.
   AXPositionInstance CreatePreviousAnchorPosition(
-      const AbortMovePredicate abort_predicate) const {
+      const AbortMovePredicate& abort_predicate) const {
     if (IsNullPosition())
       return CreateNullPosition();
 
@@ -2137,7 +2233,7 @@
   // Creates a position using the next text-only node as its anchor.
   // Assumes that text-only nodes are leaf nodes.
   AXPositionInstance CreateNextTextAnchorPosition(
-      const AbortMovePredicate abort_predicate) const {
+      const AbortMovePredicate& abort_predicate) const {
     // If this is an ancestor text position, resolve to its leaf text position.
     if (IsTextPosition() && AnchorChildCount())
       return AsLeafTextPosition();
@@ -2154,7 +2250,7 @@
   // Creates a position using the previous text-only node as its anchor.
   // Assumes that text-only nodes are leaf nodes.
   AXPositionInstance CreatePreviousTextAnchorPosition(
-      const AbortMovePredicate abort_predicate) const {
+      const AbortMovePredicate& abort_predicate) const {
     // If this is an ancestor text position, resolve to its leaf text position.
     if (IsTextPosition() && AnchorChildCount())
       return AsLeafTextPosition();
@@ -2207,6 +2303,7 @@
 
   // AbortMovePredicate function used to detect paragraph boundaries.
   static bool AbortMoveAtParagraphBoundary(
+      bool& crossed_potential_boundary_token,
       const AXPosition<AXPositionType, AXNodeType>& move_from,
       const AXPosition<AXPositionType, AXNodeType>& move_to,
       const AXMoveType move_type,
@@ -2217,48 +2314,34 @@
     const bool move_from_break = move_from.IsInLineBreakingObject();
     const bool move_to_break = move_to.IsInLineBreakingObject();
 
-    bool potential_paragraph_boundary = false;
     switch (move_type) {
       case AXMoveType::kAncestor:
         // For Ancestor moves, only abort when exiting a block descendant.
         // We don't care if the ancestor is a block or not, since the
         // descendant is contained by it.
-        potential_paragraph_boundary = move_from_break;
+        crossed_potential_boundary_token |= move_from_break;
         break;
       case AXMoveType::kDescendant:
         // For Descendant moves, only abort when entering a block descendant.
         // We don't care if the ancestor is a block or not, since the
         // descendant is contained by it.
-        potential_paragraph_boundary = move_to_break;
+        crossed_potential_boundary_token |= move_to_break;
         break;
       case AXMoveType::kSibling:
         // For Sibling moves, abort if at least one of the siblings are a block,
         // because that would mean exiting and/or entering a block.
-        potential_paragraph_boundary = move_from_break || move_to_break;
+        crossed_potential_boundary_token |= (move_from_break || move_to_break);
         break;
     }
 
-    // If there's a sequence of whitespace-only blocks, collapse so only the
-    // trailing whitespace-only block is considered a paragraph boundary.
-    if (potential_paragraph_boundary) {
-      switch (direction) {
-        case AXMoveDirection::kNextInTree: {
-          // Forward navigation, collapse trailing whitespace only nodes.
-          // Move into all whitespace only blocks that are available
-          // before aborting.
-          if (move_to.IsInWhiteSpace())
-            return false;
-          break;
-        }
-        case AXMoveDirection::kPreviousInTree: {
-          // Reverse navigation, skip over all whitespace nodes unless
-          // it is the last trailing whitespace node, signaling the end
-          // of a previous paragraph.
-          if (move_from.IsInWhiteSpace())
-            return false;
-          break;
-        }
+    if (crossed_potential_boundary_token && move_to.IsLeafTextPosition()) {
+      // If there's a sequence of whitespace-only anchors, collapse so only the
+      // last whitespace-only anchor is considered a paragraph boundary.
+      if (direction == AXMoveDirection::kNextInTree &&
+          move_to.IsInWhiteSpace()) {
+        return false;
       }
+
       return true;
     }
 
diff --git a/ui/accessibility/ax_range.h b/ui/accessibility/ax_range.h
index be34712f..612516d1 100644
--- a/ui/accessibility/ax_range.h
+++ b/ui/accessibility/ax_range.h
@@ -226,6 +226,7 @@
                          int max_count = -1) const {
     base::string16 range_text;
     bool should_append_newline = false;
+    bool found_trailing_newline = false;
     for (const AXRange& leaf_text_range : *this) {
       AXPositionType* start = leaf_text_range.anchor();
       AXPositionType* end = leaf_text_range.focus();
@@ -236,7 +237,6 @@
       if (should_append_newline)
         range_text += base::ASCIIToUTF16("\n");
 
-      bool current_leaf_is_line_break = false;
       base::string16 current_anchor_text = start->GetText();
       int current_leaf_text_length = end->text_offset() - start->text_offset();
 
@@ -246,7 +246,11 @@
                                         current_leaf_text_length)
                              : current_leaf_text_length;
 
-        current_leaf_is_line_break = start->IsInLineBreak();
+        // Collapse all whitespace following any line break.
+        found_trailing_newline =
+            start->IsInLineBreak() ||
+            (found_trailing_newline && start->IsInWhiteSpace());
+
         range_text += current_anchor_text.substr(start->text_offset(),
                                                  characters_to_append);
       }
@@ -260,7 +264,7 @@
       // its respective anchor is invisible to the text representation.
       if (concatenation_behavior == AXTextConcatenationBehavior::kAsInnerText)
         should_append_newline = current_anchor_text.length() > 0 &&
-                                !current_leaf_is_line_break &&
+                                !found_trailing_newline &&
                                 end->AtEndOfParagraph();
     }
     return range_text;
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index 250f0ce..7f32ce0 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -397,7 +397,7 @@
   }
 
   const base::string16 tree_for_move_full_text =
-      L"First line of text\n\nStandalone line\n"
+      L"First line of text\nStandalone line\n"
       L"bold text\nParagraph 1\nParagraph 2";
 
   ui::AXTreeUpdate BuildAXTreeForMove() {
@@ -1138,7 +1138,7 @@
   int count;
   ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
       TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -6, &count));
-  EXPECT_EQ(-6, count);
+  EXPECT_EQ(-5, count);
   EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"");
 
   ASSERT_HRESULT_SUCCEEDED(
@@ -1720,7 +1720,7 @@
                   /*expected_count*/ 1);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word,
                   /*count*/ 2,
-                  /*expected_text*/ L"text\n\n",
+                  /*expected_text*/ L"text\n",
                   /*expected_count*/ 2);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word,
                   /*count*/ 2,
@@ -1752,7 +1752,7 @@
                   /*expected_count*/ -3);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word,
                   /*count*/ -2,
-                  /*expected_text*/ L"text\n\n",
+                  /*expected_text*/ L"text\n",
                   /*expected_count*/ -2);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word,
                   /*count*/ -6,
@@ -1889,18 +1889,18 @@
   EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
       text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
       /*count*/ -4,
-      /*expected_text*/ L"First line of text\n\n",
+      /*expected_text*/ L"First line of text\n",
       /*expected_count*/ -4);
 
   // Move forward.
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
                   /*count*/ 1,
-                  /*expected_text*/ L"\n",
+                  /*expected_text*/ L"Standalone line\n",
                   /*expected_count*/ 1);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
-                  /*count*/ 2,
+                  /*count*/ 1,
                   /*expected_text*/ L"bold text",
-                  /*expected_count*/ 2);
+                  /*expected_count*/ 1);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
                   /*count*/ 1,
                   /*expected_text*/ L"Paragraph 1",
@@ -1923,7 +1923,7 @@
                   /*expected_count*/ -3);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
                   /*count*/ -2,
-                  /*expected_text*/ L"First line of text",
+                  /*expected_text*/ L"First line of text\n",
                   /*expected_count*/ -2);
 
   // Moving backward by any number of paragraphs at the start of document
@@ -1931,7 +1931,7 @@
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
                   /*count*/ -1,
                   /*expected_text*/
-                  L"First line of text",
+                  L"First line of text\n",
                   /*expected_count*/ 0);
 
   // Test degenerate range creation at the beginning of the document.
@@ -1943,14 +1943,14 @@
   EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
       text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
       /*count*/ 1,
-      /*expected_text*/ L"First line of text",
+      /*expected_text*/ L"First line of text\n",
       /*expected_count*/ 1);
 
   // Test degenerate range creation at the end of the document.
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
                   /*count*/ 6,
                   /*expected_text*/ L"Paragraph 2",
-                  /*expected_count*/ 5);
+                  /*expected_count*/ 4);
   EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
       text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
       /*count*/ 1,
@@ -1975,8 +1975,8 @@
   // Degenerate range moves.
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
                   /*count*/ -7,
-                  /*expected_text*/ L"First line of text",
-                  /*expected_count*/ -6);
+                  /*expected_text*/ L"First line of text\n",
+                  /*expected_count*/ -5);
   EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
       text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
       /*count*/ -1,
@@ -1989,7 +1989,7 @@
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
                   /*count*/ 70,
                   /*expected_text*/ L"",
-                  /*expected_count*/ 3);
+                  /*expected_count*/ 2);
 
   // Trying to move past the last paragraph should have no effect.
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
diff --git a/ui/ozone/platform/scenic/OWNERS b/ui/ozone/platform/scenic/OWNERS
new file mode 100644
index 0000000..e7034ea
--- /dev/null
+++ b/ui/ozone/platform/scenic/OWNERS
@@ -0,0 +1 @@
+file://build/fuchsia/OWNERS
diff --git a/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.js b/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.js
index 2a3ce9b..f4704956 100644
--- a/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.js
+++ b/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.js
@@ -5,24 +5,25 @@
 cr.define('network_config', function() {
   /** @interface */
   class MojoInterfaceProvider {
-    /** @return {!chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
-    getMojoServiceProxy() {}
+    /** @return {!chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
+    getMojoServiceRemote() {}
   }
 
   /** @implements {network_config.MojoInterfaceProvider} */
   class MojoInterfaceProviderImpl {
     constructor() {
-      /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
-      this.proxy_ = null;
+      /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
+      this.remote_ = null;
     }
 
     /** @override */
-    getMojoServiceProxy() {
-      if (!this.proxy_) {
-        this.proxy_ = chromeos.networkConfig.mojom.CrosNetworkConfig.getProxy();
+    getMojoServiceRemote() {
+      if (!this.remote_) {
+        this.remote_ =
+            chromeos.networkConfig.mojom.CrosNetworkConfig.getRemote();
       }
 
-      return this.proxy_;
+      return this.remote_;
     }
   }
 
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
index ae7c5ad..e957155 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
@@ -118,14 +118,13 @@
   /** @private {boolean} */
   simUnlockSent_: false,
 
-  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
-  networkConfigProxy_: null,
+  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
+  networkConfig_: null,
 
   /** @override */
   created: function() {
-    this.networkConfigProxy_ =
-        network_config.MojoInterfaceProviderImpl.getInstance()
-            .getMojoServiceProxy();
+    this.networkConfig_ = network_config.MojoInterfaceProviderImpl.getInstance()
+                              .getMojoServiceRemote();
     this.getDeviceStateProperties_();
   },
 
@@ -176,7 +175,7 @@
 
   /** @private */
   getDeviceStateProperties_: function() {
-    this.networkConfigProxy_.getDeviceStateList().then(response => {
+    this.networkConfig_.getDeviceStateList().then(response => {
       const devices = response.result;
       const kCellular = chromeos.networkConfig.mojom.NetworkType.kCellular;
       this.deviceStateProperties_ =
@@ -310,18 +309,17 @@
    */
   setCellularSimState_: function(cellularSimState) {
     this.setInProgress_();
-    this.networkConfigProxy_.setCellularSimState(cellularSimState)
-        .then(response => {
-          this.inProgress_ = false;
-          if (!response.success) {
-            this.error_ = ErrorType.INCORRECT_PIN;
-            this.focusDialogInput_();
-          } else {
-            this.error_ = ErrorType.NONE;
-            this.closeDialogs_();
-            this.delayUpdateLockEnabled_();
-          }
-        });
+    this.networkConfig_.setCellularSimState(cellularSimState).then(response => {
+      this.inProgress_ = false;
+      if (!response.success) {
+        this.error_ = ErrorType.INCORRECT_PIN;
+        this.focusDialogInput_();
+      } else {
+        this.error_ = ErrorType.NONE;
+        this.closeDialogs_();
+        this.delayUpdateLockEnabled_();
+      }
+    });
   },
 
   /**
@@ -338,19 +336,17 @@
     if (puk) {
       cellularSimState.newPin = pin;
     }
-    this.networkConfigProxy_.setCellularSimState(cellularSimState)
-        .then(response => {
-          this.inProgress_ = false;
-          if (!response.success) {
-            this.error_ =
-                puk ? ErrorType.INCORRECT_PUK : ErrorType.INCORRECT_PIN;
-            this.focusDialogInput_();
-          } else {
-            this.error_ = ErrorType.NONE;
-            this.closeDialogs_();
-            this.delayUpdateLockEnabled_();
-          }
-        });
+    this.networkConfig_.setCellularSimState(cellularSimState).then(response => {
+      this.inProgress_ = false;
+      if (!response.success) {
+        this.error_ = puk ? ErrorType.INCORRECT_PUK : ErrorType.INCORRECT_PIN;
+        this.focusDialogInput_();
+      } else {
+        this.error_ = ErrorType.NONE;
+        this.closeDialogs_();
+        this.delayUpdateLockEnabled_();
+      }
+    });
   },
 
   /**
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_listener_behavior.js b/ui/webui/resources/cr_elements/chromeos/network/cr_network_listener_behavior.js
index 39052d3..dc56f7b 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_listener_behavior.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_listener_behavior.js
@@ -15,10 +15,11 @@
   /** @override */
   attached: function() {
     this.observer_ =
-        new chromeos.networkConfig.mojom.CrosNetworkConfigObserver(this);
+        new chromeos.networkConfig.mojom.CrosNetworkConfigObserverReceiver(
+            this);
     network_config.MojoInterfaceProviderImpl.getInstance()
-        .getMojoServiceProxy()
-        .addObserver(this.observer_.$.createProxy());
+        .getMojoServiceRemote()
+        .addObserver(this.observer_.$.bindNewPipeAndPassRemote());
   },
 
   // CrosNetworkConfigObserver methods. Override these in the implementation.
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js b/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
index f79b03c5..e8041a2a 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
@@ -78,14 +78,13 @@
   /** @private {number|null} */
   scanIntervalId_: null,
 
-  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
-  networkConfigProxy_: null,
+  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
+  networkConfig_: null,
 
   /** @override */
   created: function() {
-    this.networkConfigProxy_ =
-        network_config.MojoInterfaceProviderImpl.getInstance()
-            .getMojoServiceProxy();
+    this.networkConfig_ = network_config.MojoInterfaceProviderImpl.getInstance()
+                              .getMojoServiceRemote();
   },
 
   /** @override */
@@ -94,9 +93,9 @@
 
     const INTERVAL_MS = 10 * 1000;
     const kAll = chromeos.networkConfig.mojom.NetworkType.kAll;
-    this.networkConfigProxy_.requestNetworkScan(kAll);
+    this.networkConfig_.requestNetworkScan(kAll);
     this.scanIntervalId_ = window.setInterval(function() {
-      this.networkConfigProxy_.requestNetworkScan(kAll);
+      this.networkConfig_.requestNetworkScan(kAll);
     }.bind(this), INTERVAL_MS);
   },
 
@@ -133,7 +132,7 @@
    * refresh and list update (e.g. when the element is shown).
    */
   refreshNetworks: function() {
-    this.networkConfigProxy_.getDeviceStateList().then(response => {
+    this.networkConfig_.getDeviceStateList().then(response => {
       this.onGetDeviceStates_(response.result);
     });
   },
@@ -186,7 +185,7 @@
       networkType: mojom.NetworkType.kAll,
       limit: chromeos.networkConfig.mojom.kNoLimit,
     };
-    this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+    this.networkConfig_.getNetworkStateList(filter).then(response => {
       this.onGetNetworkStateList_(deviceStates, response.result);
     });
   },