diff --git a/DEPS b/DEPS
index a85dd4c..2bef2e1 100644
--- a/DEPS
+++ b/DEPS
@@ -175,11 +175,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '853789cdfe3c518492959458804add4a8d2e664c',
+  'skia_revision': '964aa91580b6aa3f14fe0b61578e5807ae9c92ce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '189353b3360a48f637f7bb228db10268fdc991c9',
+  'v8_revision': '65b70d61b93c092a2cf5c0bcf264c1d594fdc6fb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -187,11 +187,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'c3655c3a7d55049bb9c881dc89df66f69125afb8',
+  'angle_revision': 'f8b28678163603f8fab1c45e19507c7500299021',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '176f3a3dbbc90d7b2dd9822db709505b4ad41b67',
+  'swiftshader_revision': 'abc7a30a06c7c135d76032f1346c5d2d1b73f427',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -246,7 +246,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'd5ae9bf042df46c5eddab0d34d643f4dd7897d28',
+  'devtools_frontend_revision': '683c198380dbee96fad08e689d30cb9911a4712c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -298,7 +298,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.
-  'shaderc_revision': '06b6db68ff7e20d4f097a132dd56a1c6482e8c25',
+  'shaderc_revision': '6e9087162a1cd9b67d66ebd3cda0054765f27ee9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -863,7 +863,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '6566156b03a02a3269ef099feccf40b762aa7668',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '181bb9733f8392b5dbea5fe37545f29251268ea6',
       'condition': 'checkout_linux',
   },
 
@@ -944,7 +944,7 @@
     Var('chromium_git') + '/codecs/libgav1.git' + '@' + '8e8c13b9e821f4590761487c4e0b96f432eaf051',
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '56364b6b602696c021349794a8d39744a1052afc',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '8985fc91089f3117d338f0ebd683596a197b2d92',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1239,7 +1239,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '29f04c8cdb9bb12711a1706b061ec81d82311c5f',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '8ff2fc8fcdb168a9ba4ac17d053d3e8dd7864912',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1440,7 +1440,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'ec18cc3262922e7dcdbe70243c6f40606f979144',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '83762b21dbd2efbcbed81fefbd0218a99c03c18a',
+    Var('webrtc_git') + '/src.git' + '@' + '5e1ea251895c08fbc48cb595b02f9257cedeac75',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1515,7 +1515,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e36af5c1ab9bb1c194e46c956d4e3154eb5caa1b',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f8a16ac6d46764c95d9197be1aab0374b97ae8d9',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 835b045..693253b 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2397,8 +2397,7 @@
            'tikuta+cc@chromium.org',
            'ukai+cc@chromium.org',
            'yyanagisawa+cc@chromium.org'],
-    'cr_elements': ['michaelpg+watch-elements@chromium.org',
-                    'stevenjb+watch-md-settings@chromium.org'],
+    'cr_elements': ['michaelpg+watch-elements@chromium.org'],
     'cros_benchmarks': ['cros-perf-detectives@google.com',
                         'cywang@chromium.org',
                         'vovoy@chromium.org'],
diff --git a/android_webview/browser/BUILD.gn b/android_webview/browser/BUILD.gn
index f69ebd1e..df05873 100644
--- a/android_webview/browser/BUILD.gn
+++ b/android_webview/browser/BUILD.gn
@@ -253,7 +253,6 @@
     "//third_party/crashpad/crashpad/client",
     "//ui/android",
     "//ui/gl",
-    "//ui/native_theme",
     "//ui/resources",
     "//ui/touch_selection",
   ]
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS
index 0175c4b1..bd5acad41 100644
--- a/android_webview/browser/DEPS
+++ b/android_webview/browser/DEPS
@@ -84,7 +84,6 @@
   "+ui/display",
   "+ui/gfx",
   "+ui/gl",
-  "+ui/native_theme",
   "+ui/touch_selection/touch_handle.h",
 
   # Temporary until we bundle our own favicon. See
diff --git a/android_webview/browser/aw_settings.cc b/android_webview/browser/aw_settings.cc
index 56dbf31..b579762 100644
--- a/android_webview/browser/aw_settings.cc
+++ b/android_webview/browser/aw_settings.cc
@@ -26,7 +26,6 @@
 #include "content/public/common/web_preferences.h"
 #include "net/http/http_util.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
-#include "ui/native_theme/native_theme.h"
 
 using base::android::ConvertJavaStringToUTF16;
 using base::android::ConvertUTF8ToJavaString;
@@ -526,19 +525,19 @@
       break;
     }
   }
-  ui::NativeTheme::PreferredColorScheme preferred_color_scheme =
-      is_dark_mode ? ui::NativeTheme::PreferredColorScheme::kDark
-                   : ui::NativeTheme::PreferredColorScheme::kNoPreference;
+  web_prefs->preferred_color_scheme =
+      is_dark_mode ? blink::PreferredColorScheme::kDark
+                   : blink::PreferredColorScheme::kNoPreference;
   if (is_dark_mode) {
     switch (Java_AwSettings_getForceDarkBehaviorLocked(env, obj)) {
       case ForceDarkBehavior::FORCE_DARK_ONLY: {
-        preferred_color_scheme =
-            ui::NativeTheme::PreferredColorScheme::kNoPreference;
+        web_prefs->preferred_color_scheme =
+            blink::PreferredColorScheme::kNoPreference;
         web_prefs->force_dark_mode_enabled = true;
         break;
       }
       case ForceDarkBehavior::MEDIA_QUERY_ONLY: {
-        preferred_color_scheme = ui::NativeTheme::PreferredColorScheme::kDark;
+        web_prefs->preferred_color_scheme = blink::PreferredColorScheme::kDark;
         web_prefs->force_dark_mode_enabled = false;
         break;
       }
@@ -549,19 +548,16 @@
       // dark so that dark themed content will be preferred over force
       // darkening.
       case ForceDarkBehavior::PREFER_MEDIA_QUERY_OVER_FORCE_DARK: {
-        preferred_color_scheme = ui::NativeTheme::PreferredColorScheme::kDark;
+        web_prefs->preferred_color_scheme = blink::PreferredColorScheme::kDark;
         web_prefs->force_dark_mode_enabled = true;
         break;
       }
     }
   } else {
-    preferred_color_scheme =
-        ui::NativeTheme::PreferredColorScheme::kNoPreference;
+    web_prefs->preferred_color_scheme =
+        blink::PreferredColorScheme::kNoPreference;
     web_prefs->force_dark_mode_enabled = false;
   }
-  // Notify NativeTheme of changes to dark mode.
-  ui::NativeTheme::GetInstanceForWeb()->set_preferred_color_scheme(
-      preferred_color_scheme);
 }
 
 bool AwSettings::GetAllowFileAccess() {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/DarkModeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/DarkModeTest.java
new file mode 100644
index 0000000..6fe3f81
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/DarkModeTest.java
@@ -0,0 +1,148 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview.test;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.android_webview.AwContents;
+import org.chromium.android_webview.AwSettings;
+import org.chromium.android_webview.settings.ForceDarkBehavior;
+import org.chromium.android_webview.settings.ForceDarkMode;
+
+/**
+ * Tests dark-mode related data are correctly passed to blink.
+ */
+@RunWith(AwJUnit4ClassRunner.class)
+public class DarkModeTest {
+    @Rule
+    public AwActivityTestRule mRule = new AwActivityTestRule();
+
+    private TestAwContentsClient mContentsClient = new TestAwContentsClient();
+    private AwContents mContents;
+    private AwSettings mSettings;
+
+    @Before
+    public void setUp() {
+        mContents = createAwContentsJsEnabled();
+        mSettings = mContents.getSettings();
+    }
+
+    @Test
+    @SmallTest
+    public void testDarkModeOff() throws Throwable {
+        mRule.loadUrlSync(mContents, mContentsClient.getOnPageFinishedHelper(), "about:blank");
+
+        // Disable dark mode and check that prefers-color-scheme is not set to dark.
+        mSettings.setForceDarkMode(ForceDarkMode.FORCE_DARK_OFF);
+        assertNotDarkScheme(mContents);
+    }
+
+    @Test
+    @SmallTest
+    public void testUAOnlyDarkening() throws Throwable {
+        // If WebView uses UA only darkening strategy prefer-color-scheme should always been set to
+        // NoPreference to avoid double darkening regardless whether web-page supports dark theme.
+        mSettings.setForceDarkMode(ForceDarkMode.FORCE_DARK_ON);
+        mSettings.setForceDarkBehavior(ForceDarkBehavior.FORCE_DARK_ONLY);
+
+        // Load web-page which does not support dark theme and
+        // check prefers-color-scheme is not set to dark
+        mRule.loadUrlSync(mContents, mContentsClient.getOnPageFinishedHelper(), "about:blank");
+        assertNotDarkScheme(mContents);
+
+        // Load web page which supports dark theme and
+        // check prefers-color-scheme is still not set to dark
+        final String supportsDarkScheme =
+                "<html><head><meta name=\"color-scheme\" content=\"dark light\"></head>"
+                + "<body></body></html>";
+        mRule.loadHtmlSync(
+                mContents, mContentsClient.getOnPageFinishedHelper(), supportsDarkScheme);
+        assertNotDarkScheme(mContents);
+    }
+
+    @Test
+    @SmallTest
+    public void testWebThemeOnlyDarkening() throws Throwable {
+        // If WebView uses web theme only darkening strategy
+        // prefer-color-scheme should been set to dark.
+        mRule.loadUrlSync(mContents, mContentsClient.getOnPageFinishedHelper(), "about:blank");
+        mSettings.setForceDarkMode(ForceDarkMode.FORCE_DARK_ON);
+        mSettings.setForceDarkBehavior(ForceDarkBehavior.MEDIA_QUERY_ONLY);
+
+        assertDarkScheme(mContents);
+    }
+
+    @Test
+    @SmallTest
+    public void testPreferWebThemeDarkening() throws Throwable {
+        // If WebView prefers web theme darkening over UA darkening prefer-color-scheme is set
+        // according to whether web page supports dark-theme
+        mRule.loadUrlSync(mContents, mContentsClient.getOnPageFinishedHelper(), "about:blank");
+        mSettings.setForceDarkMode(ForceDarkMode.FORCE_DARK_ON);
+        mSettings.setForceDarkBehavior(ForceDarkBehavior.PREFER_MEDIA_QUERY_OVER_FORCE_DARK);
+
+        // If web page does not support dark theme prefer-color-scheme should be set to NoPreference
+        assertNotDarkScheme(mContents);
+
+        final String supportsDarkScheme =
+                "<html><head><meta name=\"color-scheme\" content=\"dark light\"></head>"
+                + "<body></body></html>";
+        mRule.loadHtmlSync(
+                mContents, mContentsClient.getOnPageFinishedHelper(), supportsDarkScheme);
+
+        // If web page supports dark theme, prefer-color-scheme is set to dark.
+        assertDarkScheme(mContents);
+    }
+
+    @Test
+    @SmallTest
+    public void testDarkThemePerWebView() throws Throwable {
+        // WebView allows to set dark mode preferences per WebView, check that their settings
+        // do not interfere with each other.
+
+        mRule.loadUrlSync(mContents, mContentsClient.getOnPageFinishedHelper(), "about:blank");
+        mSettings.setForceDarkMode(ForceDarkMode.FORCE_DARK_ON);
+        mSettings.setForceDarkBehavior(ForceDarkBehavior.MEDIA_QUERY_ONLY);
+
+        AwContents otherContents = createAwContentsJsEnabled();
+        AwSettings otherSettings = otherContents.getSettings();
+        mRule.loadUrlSync(otherContents, mContentsClient.getOnPageFinishedHelper(), "about:blank");
+        otherSettings.setForceDarkMode(ForceDarkMode.FORCE_DARK_ON);
+        otherSettings.setForceDarkBehavior(ForceDarkBehavior.FORCE_DARK_ONLY);
+
+        assertDarkScheme(mContents);
+        assertNotDarkScheme(otherContents);
+    }
+
+    private boolean prefersDarkTheme(AwContents contents) throws Exception {
+        final String colorSchemeSelector =
+                "window.matchMedia('(prefers-color-scheme: dark)').matches";
+        String result = mRule.executeJavaScriptAndWaitForResult(
+                contents, mContentsClient, colorSchemeSelector);
+
+        return "true".equals(result);
+    }
+
+    private void assertNotDarkScheme(AwContents contents) throws Exception {
+        Assert.assertFalse(prefersDarkTheme(contents));
+    }
+
+    private void assertDarkScheme(AwContents contents) throws Exception {
+        Assert.assertTrue(prefersDarkTheme(contents));
+    }
+
+    private AwContents createAwContentsJsEnabled() {
+        AwTestContainerView view = mRule.createAwTestContainerViewOnMainSync(mContentsClient);
+        AwContents contents = view.getAwContents();
+        AwActivityTestRule.enableJavaScriptOnUiThread(contents);
+        return contents;
+    }
+}
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index cb04470..fedb876b 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -253,6 +253,7 @@
     "../javatests/src/org/chromium/android_webview/test/ContentViewMiscTest.java",
     "../javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java",
     "../javatests/src/org/chromium/android_webview/test/CookieManagerTest.java",
+    "../javatests/src/org/chromium/android_webview/test/DarkModeTest.java",
     "../javatests/src/org/chromium/android_webview/test/DisableHardwareAccelerationForTest.java",
     "../javatests/src/org/chromium/android_webview/test/FullScreenVideoTestAwContentsClient.java",
     "../javatests/src/org/chromium/android_webview/test/GeolocationTest.java",
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc
index 6bd26b9..56066cd 100644
--- a/ash/login/ui/login_auth_user_view.cc
+++ b/ash/login/ui/login_auth_user_view.cc
@@ -75,6 +75,7 @@
 // Distance from the top of the user view to the user icon.
 constexpr int kDistanceFromTopOfBigUserViewToUserIconDp = 24;
 
+constexpr SkColor kChallengeResponseSmartCardIconColor = gfx::kGoogleGrey200;
 constexpr SkColor kChallengeResponseArrowBackgroundColor =
     SkColorSetARGB(0x2B, 0xFF, 0xFF, 0xFF);
 constexpr SkColor kChallengeResponseErrorColor = gfx::kGoogleRed300;
@@ -596,7 +597,7 @@
       case State::kAuthenticating:
         return gfx::CreateVectorIcon(kLockScreenSmartCardIcon,
                                      kChallengeResponseIconSizeDp,
-                                     SK_ColorWHITE);
+                                     kChallengeResponseSmartCardIconColor);
       case State::kFailure:
         return gfx::CreateVectorIcon(kLockScreenSmartCardFailureIcon,
                                      kChallengeResponseIconSizeDp,
diff --git a/ash/public/cpp/shelf_test_api.h b/ash/public/cpp/shelf_test_api.h
index 1e96a4f..3a51126 100644
--- a/ash/public/cpp/shelf_test_api.h
+++ b/ash/public/cpp/shelf_test_api.h
@@ -32,6 +32,10 @@
 
   views::View* GetHomeButton();
 
+  // Whether the shelf has a login shelf gesture handler set up, which would
+  // imply that swipe from shelf gesture detection is active.
+  bool HasLoginShelfGestureHandler() const;
+
   // Returns ui information of scrollable shelf for the given state. If |state|
   // specifies the scroll distance, the target offset, which is the offset value
   // after scrolling by the distance, is also calculated. It is useful if you
diff --git a/ash/shelf/shelf.cc b/ash/shelf/shelf.cc
index 422f6b18..a88e0d7 100644
--- a/ash/shelf/shelf.cc
+++ b/ash/shelf/shelf.cc
@@ -20,6 +20,7 @@
 #include "ash/shelf/shelf_controller.h"
 #include "ash/shelf/shelf_focus_cycler.h"
 #include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shelf/shelf_layout_manager_observer.h"
 #include "ash/shelf/shelf_navigation_widget.h"
 #include "ash/shelf/shelf_observer.h"
 #include "ash/shelf/shelf_tooltip_manager.h"
@@ -104,6 +105,65 @@
   HotseatState target_state_;
 };
 
+// An animation metrics reporter for the shelf navigation widget.
+class ASH_EXPORT NavigationWidgetAnimationMetricsReporter
+    : public ui::AnimationMetricsReporter,
+      public ShelfLayoutManagerObserver {
+ public:
+  explicit NavigationWidgetAnimationMetricsReporter(Shelf* shelf)
+      : shelf_(shelf) {
+    shelf_->shelf_layout_manager()->AddObserver(this);
+  }
+
+  ~NavigationWidgetAnimationMetricsReporter() override {
+    shelf_->shelf_layout_manager()->RemoveObserver(this);
+  }
+
+  NavigationWidgetAnimationMetricsReporter(
+      const NavigationWidgetAnimationMetricsReporter&) = delete;
+  NavigationWidgetAnimationMetricsReporter& operator=(
+      const NavigationWidgetAnimationMetricsReporter&) = delete;
+
+  // ui::AnimationMetricsReporter:
+  void Report(int value) override {
+    switch (target_state_) {
+      case HotseatState::kShownClamshell:
+      case HotseatState::kShownHomeLauncher:
+        UMA_HISTOGRAM_PERCENTAGE(
+            "Ash.NavigationWidget.Widget.AnimationSmoothness."
+            "TransitionToShownHotseat",
+            value);
+        break;
+      case HotseatState::kExtended:
+        UMA_HISTOGRAM_PERCENTAGE(
+            "Ash.NavigationWidget.Widget.AnimationSmoothness."
+            "TransitionToExtendedHotseat",
+            value);
+        break;
+      case HotseatState::kHidden:
+        UMA_HISTOGRAM_PERCENTAGE(
+            "Ash.NavigationWidget.Widget.AnimationSmoothness."
+            "TransitionToHiddenHotseat",
+            value);
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  // ShelfLayoutManagerObserver:
+  void OnHotseatStateChanged(HotseatState old_state,
+                             HotseatState new_state) override {
+    target_state_ = new_state;
+  }
+
+ private:
+  Shelf* shelf_;
+  // The state to which the animation is transitioning.
+  HotseatState target_state_ = HotseatState::kShownHomeLauncher;
+};
+
 // Shelf::AutoHideEventHandler -----------------------------------------------
 
 // Forwards mouse and gesture events to ShelfLayoutManager for auto-hide.
@@ -257,6 +317,8 @@
   navigation_widget_ = std::make_unique<ShelfNavigationWidget>(
       this, hotseat_widget()->GetShelfView());
   navigation_widget_->Initialize(container);
+  navigation_widget_metrics_reporter_ =
+      std::make_unique<NavigationWidgetAnimationMetricsReporter>(this);
   Shell::Get()->focus_cycler()->AddWidget(navigation_widget_.get());
 }
 
@@ -540,10 +602,16 @@
   return hotseat_transition_metrics_reporter_.get();
 }
 
+ui::AnimationMetricsReporter*
+Shelf::GetNavigationWidgetAnimationMetricsReporter() {
+  return navigation_widget_metrics_reporter_.get();
+}
+
 void Shelf::WillDeleteShelfLayoutManager() {
   // Clear event handlers that might forward events to the destroyed instance.
   auto_hide_event_handler_.reset();
   auto_dim_event_handler_.reset();
+  navigation_widget_metrics_reporter_.reset();
 
   DCHECK(shelf_layout_manager_);
   shelf_layout_manager_->RemoveObserver(this);
diff --git a/ash/shelf/shelf.h b/ash/shelf/shelf.h
index a1b3bb1..cd34087 100644
--- a/ash/shelf/shelf.h
+++ b/ash/shelf/shelf.h
@@ -34,6 +34,7 @@
 enum class AnimationChangeType;
 class HotseatWidget;
 class HotseatWidgetAnimationMetricsReporter;
+class NavigationWidgetAnimationMetricsReporter;
 class ShelfFocusCycler;
 class ShelfLayoutManager;
 class ShelfLayoutManagerTest;
@@ -231,6 +232,7 @@
   ShelfTooltipManager* tooltip() { return tooltip_.get(); }
 
   ui::AnimationMetricsReporter* GetHotseatTransitionMetricsReporter();
+  ui::AnimationMetricsReporter* GetNavigationWidgetAnimationMetricsReporter();
 
  protected:
   // ShelfLayoutManagerObserver:
@@ -292,6 +294,11 @@
   std::unique_ptr<HotseatWidgetAnimationMetricsReporter>
       hotseat_transition_metrics_reporter_;
 
+  // Animation metrics reporter for navigation widget animations. Owned by the
+  // Shelf to ensure it outlives the Navigation Widget.
+  std::unique_ptr<NavigationWidgetAnimationMetricsReporter>
+      navigation_widget_metrics_reporter_;
+
   // True while the animation to enter or exit tablet mode is running. Sometimes
   // this value is true when the shelf movements are not actually animating
   // (animation value = 0.0). This is because this is set to true when we
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 3f3c860..3bab8c70 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -115,22 +115,27 @@
 
 void SetupAnimator(ui::ScopedLayerAnimationSettings* animation_setter,
                    base::TimeDelta animation_duration,
-                   gfx::Tween::Type type) {
+                   gfx::Tween::Type type,
+                   ui::AnimationMetricsReporter* animation_metrics_reporter) {
   animation_setter->SetTransitionDuration(animation_duration);
   if (!animation_duration.is_zero()) {
     animation_setter->SetTweenType(type);
     animation_setter->SetPreemptionStrategy(
         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+    if (animation_metrics_reporter)
+      animation_setter->SetAnimationMetricsReporter(animation_metrics_reporter);
   }
 }
 
 void AnimateOpacity(views::Widget* widget,
                     float target_opacity,
                     base::TimeDelta animation_duration,
-                    gfx::Tween::Type type) {
+                    gfx::Tween::Type type,
+                    ui::AnimationMetricsReporter* animation_metrics_reporter) {
   ui::ScopedLayerAnimationSettings animation_setter(
       GetLayer(widget)->GetAnimator());
-  SetupAnimator(&animation_setter, animation_duration, type);
+  SetupAnimator(&animation_setter, animation_duration, type,
+                animation_metrics_reporter);
   GetLayer(widget)->SetOpacity(target_opacity);
 }
 
@@ -1446,14 +1451,17 @@
       ShelfConfig::Get()->DimAnimationTween();
 
   AnimateOpacity(shelf_->navigation_widget(), target_opacity_,
-                 dim_animation_duration, dim_animation_tween);
+                 dim_animation_duration, dim_animation_tween,
+                 shelf_->GetNavigationWidgetAnimationMetricsReporter());
 
   AnimateOpacity(shelf_->hotseat_widget(),
                  shelf_->hotseat_widget()->CalculateOpacity(),
-                 dim_animation_duration, dim_animation_tween);
+                 dim_animation_duration, dim_animation_tween,
+                 /*animation_metrics_reporter=*/nullptr);
 
   AnimateOpacity(shelf_->status_area_widget(), target_opacity_,
-                 dim_animation_duration, dim_animation_tween);
+                 dim_animation_duration, dim_animation_tween,
+                 /*animation_metrics_reporter=*/nullptr);
 
   shelf_widget_->SetLoginShelfButtonOpacity(target_opacity_);
 }
diff --git a/ash/shelf/shelf_navigation_widget.cc b/ash/shelf/shelf_navigation_widget.cc
index d9cc2c2..75031c25 100644
--- a/ash/shelf/shelf_navigation_widget.cc
+++ b/ash/shelf/shelf_navigation_widget.cc
@@ -11,14 +11,17 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_focus_cycler.h"
 #include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shelf/shelf_layout_manager_observer.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "base/metrics/histogram_macros.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/compositor/animation_metrics_reporter.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/views/animation/bounds_animator.h"
@@ -91,6 +94,115 @@
 };
 
 }  // namespace
+// An animation metrics reporter for the shelf navigation buttons.
+class ASH_EXPORT NavigationButtonAnimationMetricsReporter
+    : public ui::AnimationMetricsReporter,
+      public ShelfLayoutManagerObserver {
+ public:
+  // The different kinds of navigation buttons.
+  enum class NavigationButtonType {
+    // The Navigation Widget's back button.
+    kBackButton,
+    // The Navigation Widget's home button.
+    kHomeButton
+  };
+  NavigationButtonAnimationMetricsReporter(
+      Shelf* shelf,
+      NavigationButtonType navigation_button_type)
+      : shelf_(shelf), navigation_button_type_(navigation_button_type) {
+    shelf_->shelf_layout_manager()->AddObserver(this);
+  }
+
+  ~NavigationButtonAnimationMetricsReporter() override {
+    shelf_->shelf_layout_manager()->RemoveObserver(this);
+  }
+
+  NavigationButtonAnimationMetricsReporter(
+      const NavigationButtonAnimationMetricsReporter&) = delete;
+  NavigationButtonAnimationMetricsReporter& operator=(
+      const NavigationButtonAnimationMetricsReporter&) = delete;
+
+  // ui::AnimationMetricsReporter:
+  void Report(int value) override {
+    switch (target_state_) {
+      case HotseatState::kShownClamshell:
+      case HotseatState::kShownHomeLauncher:
+        switch (navigation_button_type_) {
+          case NavigationButtonType::kBackButton:
+            UMA_HISTOGRAM_PERCENTAGE(
+                "Ash.NavigationWidget.BackButton.AnimationSmoothness."
+                "TransitionToShownHotseat",
+                value);
+            break;
+          case NavigationButtonType::kHomeButton:
+            UMA_HISTOGRAM_PERCENTAGE(
+                "Ash.NavigationWidget.HomeButton.AnimationSmoothness."
+                "TransitionToShownHotseat",
+                value);
+            break;
+          default:
+            NOTREACHED();
+            break;
+        }
+        break;
+      case HotseatState::kExtended:
+        switch (navigation_button_type_) {
+          case NavigationButtonType::kBackButton:
+            UMA_HISTOGRAM_PERCENTAGE(
+                "Ash.NavigationWidget.BackButton.AnimationSmoothness."
+                "TransitionToExtendedHotseat",
+                value);
+            break;
+          case NavigationButtonType::kHomeButton:
+            UMA_HISTOGRAM_PERCENTAGE(
+                "Ash.NavigationWidget.HomeButton.AnimationSmoothness."
+                "TransitionToExtendedHotseat",
+                value);
+            break;
+          default:
+            NOTREACHED();
+            break;
+        }
+        break;
+      case HotseatState::kHidden:
+        switch (navigation_button_type_) {
+          case NavigationButtonType::kBackButton:
+            UMA_HISTOGRAM_PERCENTAGE(
+                "Ash.NavigationWidget.BackButton.AnimationSmoothness."
+                "TransitionToHiddenHotseat",
+                value);
+            break;
+          case NavigationButtonType::kHomeButton:
+            UMA_HISTOGRAM_PERCENTAGE(
+                "Ash.NavigationWidget.HomeButton.AnimationSmoothness."
+                "TransitionToHiddenHotseat",
+                value);
+            break;
+          default:
+            NOTREACHED();
+            break;
+        }
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  // ShelfLayoutManagerObserver:
+  void OnHotseatStateChanged(HotseatState old_state,
+                             HotseatState new_state) override {
+    target_state_ = new_state;
+  }
+
+ private:
+  // Owned by RootWindowController
+  Shelf* shelf_;
+  // The state to which the animation is transitioning.
+  HotseatState target_state_ = HotseatState::kShownHomeLauncher;
+  // The type of navigation button that is animated.
+  NavigationButtonType navigation_button_type_;
+};
 
 class ShelfNavigationWidget::Delegate : public views::AccessiblePaneView,
                                         public views::WidgetDelegate {
@@ -283,13 +395,27 @@
                                              ShelfView* shelf_view)
     : shelf_(shelf),
       delegate_(new ShelfNavigationWidget::Delegate(shelf, shelf_view)),
-      bounds_animator_(std::make_unique<views::BoundsAnimator>(delegate_)) {
+      bounds_animator_(std::make_unique<views::BoundsAnimator>(delegate_)),
+      back_button_metrics_reporter_(
+          std::make_unique<NavigationButtonAnimationMetricsReporter>(
+              shelf,
+              NavigationButtonAnimationMetricsReporter::NavigationButtonType::
+                  kBackButton)),
+      home_button_metrics_reporter_(
+          std::make_unique<NavigationButtonAnimationMetricsReporter>(
+              shelf,
+              NavigationButtonAnimationMetricsReporter::NavigationButtonType::
+                  kHomeButton)) {
   DCHECK(shelf_);
   ShelfConfig::Get()->AddObserver(this);
 }
 
 ShelfNavigationWidget::~ShelfNavigationWidget() {
   ShelfConfig::Get()->RemoveObserver(this);
+
+  // Cancel animations now so the BoundsAnimator doesn't outlive the metrics
+  // reporter associated to it.
+  bounds_animator_->Cancel();
 }
 
 void ShelfNavigationWidget::Initialize(aura::Window* container) {
@@ -447,31 +573,40 @@
   const auto animation_duration =
       animate ? ShelfConfig::Get()->shelf_animation_duration()
               : base::TimeDelta::FromMilliseconds(0);
-  bounds_animator_->SetAnimationDuration(animation_duration);
-
   ui::ScopedLayerAnimationSettings nav_animation_setter(
       GetNativeView()->layer()->GetAnimator());
   nav_animation_setter.SetTransitionDuration(animation_duration);
   nav_animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
   nav_animation_setter.SetPreemptionStrategy(
       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  if (animate) {
+    nav_animation_setter.SetAnimationMetricsReporter(
+        shelf_->GetNavigationWidgetAnimationMetricsReporter());
+  }
 
   GetLayer()->SetOpacity(layout_manager->GetOpacity());
   SetBounds(target_bounds_);
 
   views::View* const back_button = delegate_->back_button();
-  UpdateButtonVisibility(back_button, back_button_shown, animate);
+  UpdateButtonVisibility(back_button, back_button_shown, animate,
+                         back_button_metrics_reporter_.get());
 
   views::View* const home_button = delegate_->home_button();
-  UpdateButtonVisibility(home_button, home_button_shown, animate);
+  UpdateButtonVisibility(home_button, home_button_shown, animate,
+                         home_button_metrics_reporter_.get());
 
   gfx::Rect home_button_bounds =
       back_button_shown ? GetSecondButtonBounds()
                         : GetFirstButtonBounds(shelf_->IsHorizontalAlignment());
-  if (animate)
+
+  if (animate) {
+    bounds_animator_->SetAnimationDuration(animation_duration);
+    bounds_animator_->set_animation_metrics_reporter(
+        home_button_metrics_reporter_.get());
     bounds_animator_->AnimateViewTo(home_button, home_button_bounds);
-  else
+  } else {
     home_button->SetBoundsRect(home_button_bounds);
+  }
 
   back_button->SetBoundsRect(
       GetFirstButtonBounds(shelf_->IsHorizontalAlignment()));
@@ -494,9 +629,13 @@
   }
 }
 
-void ShelfNavigationWidget::UpdateButtonVisibility(views::View* button,
-                                                   bool visible,
-                                                   bool animate) {
+void ShelfNavigationWidget::UpdateButtonVisibility(
+    views::View* button,
+    bool visible,
+    bool animate,
+    ui::AnimationMetricsReporter* reporter) {
+  if (button->GetVisible() == visible)
+    return;
   // Update visibility immediately only if making the button visible. When
   // hiding the button, the visibility will be updated when the animations
   // complete (by AnimationObserverToHideView).
@@ -511,6 +650,9 @@
       animate ? kButtonOpacityAnimationDuration : base::TimeDelta());
   opacity_settings.SetPreemptionStrategy(
       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  if (animate) {
+    opacity_settings.SetAnimationMetricsReporter(reporter);
+  }
   if (!visible)
     opacity_settings.AddObserver(new AnimationObserverToHideView(button));
 
diff --git a/ash/shelf/shelf_navigation_widget.h b/ash/shelf/shelf_navigation_widget.h
index 767e191..c0da3283 100644
--- a/ash/shelf/shelf_navigation_widget.h
+++ b/ash/shelf/shelf_navigation_widget.h
@@ -20,9 +20,14 @@
 class BoundsAnimator;
 }
 
+namespace ui {
+class AnimationMetricsReporter;
+}
+
 namespace ash {
 class BackButton;
 class HomeButton;
+class NavigationButtonAnimationMetricsReporter;
 class Shelf;
 class ShelfView;
 
@@ -90,13 +95,27 @@
  private:
   class Delegate;
 
-  void UpdateButtonVisibility(views::View* button, bool visible, bool animate);
+  void UpdateButtonVisibility(
+      views::View* button,
+      bool visible,
+      bool animate,
+      ui::AnimationMetricsReporter* animation_metrics_reporter);
 
   Shelf* shelf_ = nullptr;
   Delegate* delegate_ = nullptr;
   gfx::Rect target_bounds_;
   std::unique_ptr<views::BoundsAnimator> bounds_animator_;
 
+  // Animation metrics reporter for back button animations. Owned by the
+  // Widget to ensure it outlives the BackButton view.
+  std::unique_ptr<NavigationButtonAnimationMetricsReporter>
+      back_button_metrics_reporter_;
+
+  // Animation metrics reporter for home button animations. Owned by the
+  // Widget to ensure it outlives the HomeButton view.
+  std::unique_ptr<NavigationButtonAnimationMetricsReporter>
+      home_button_metrics_reporter_;
+
   DISALLOW_COPY_AND_ASSIGN(ShelfNavigationWidget);
 };
 
diff --git a/ash/shelf/shelf_test_api.cc b/ash/shelf/shelf_test_api.cc
index 4d314e00..79e59c35 100644
--- a/ash/shelf/shelf_test_api.cc
+++ b/ash/shelf/shelf_test_api.cc
@@ -51,6 +51,10 @@
   return GetShelfWidget()->navigation_widget()->GetHomeButton();
 }
 
+bool ShelfTestApi::HasLoginShelfGestureHandler() const {
+  return GetShelfWidget()->login_shelf_gesture_controller_for_testing();
+}
+
 ScrollableShelfInfo ShelfTestApi::GetScrollableShelfInfoForState(
     const ScrollableShelfState& state) {
   const auto* scrollable_shelf_view = GetScrollableShelfView();
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 02fd7af..a65c9151 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -313,7 +313,9 @@
     : model_(model),
       shelf_(shelf),
       view_model_(std::make_unique<views::ViewModel>()),
-      bounds_animator_(std::make_unique<views::BoundsAnimator>(this)),
+      bounds_animator_(
+          std::make_unique<views::BoundsAnimator>(this,
+                                                  /*use_transforms=*/true)),
       focus_search_(std::make_unique<ShelfFocusSearch>(this)),
       drag_and_drop_host_(drag_and_drop_host),
       shelf_button_delegate_(shelf_button_delegate) {
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 43b74d0..65f0b56 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1139,7 +1139,6 @@
       "mac/scoped_nsautorelease_pool.h",
       "mac/scoped_nsautorelease_pool.mm",
       "mac/scoped_nsobject.h",
-      "mac/scoped_nsobject.mm",
       "mac/scoped_objc_class_swizzler.h",
       "mac/scoped_objc_class_swizzler.mm",
       "mac/scoped_sending_event.h",
@@ -1993,7 +1992,6 @@
       "mac/scoped_nsautorelease_pool.h",
       "mac/scoped_nsautorelease_pool.mm",
       "mac/scoped_nsobject.h",
-      "mac/scoped_nsobject.mm",
       "mac/scoped_objc_class_swizzler.h",
       "mac/scoped_objc_class_swizzler.mm",
       "mac/scoped_typeref.h",
@@ -2435,10 +2433,7 @@
 if (is_ios || is_mac) {
   source_set("base_unittests_arc") {
     testonly = true
-    sources = [
-      "mac/bind_objc_block_unittest_arc.mm",
-      "mac/scoped_nsobject_unittest_arc.mm",
-    ]
+    sources = [ "mac/bind_objc_block_unittest_arc.mm" ]
     configs += [ "//build/config/compiler:enable_arc" ]
     deps = [
       ":base",
diff --git a/base/android/java/src/org/chromium/base/task/PostTask.java b/base/android/java/src/org/chromium/base/task/PostTask.java
index c5b3754..fcaa95d3 100644
--- a/base/android/java/src/org/chromium/base/task/PostTask.java
+++ b/base/android/java/src/org/chromium/base/task/PostTask.java
@@ -87,9 +87,9 @@
                 getTaskExecutorForTraits(taskTraits).postDelayedTask(taskTraits, task, delay);
             } else {
                 TaskTraits postedTraits = taskTraits.withExplicitDestination();
-                PostTaskJni.get().postDelayedTask(postedTraits.mPrioritySetExplicitly,
-                        postedTraits.mPriority, postedTraits.mMayBlock, postedTraits.mUseThreadPool,
-                        postedTraits.mExtensionId, postedTraits.mExtensionData, task, delay);
+                PostTaskJni.get().postDelayedTask(postedTraits.mPriority, postedTraits.mMayBlock,
+                        postedTraits.mUseThreadPool, postedTraits.mExtensionId,
+                        postedTraits.mExtensionData, task, delay);
             }
         }
     }
@@ -261,8 +261,7 @@
 
     @NativeMethods
     interface Natives {
-        void postDelayedTask(boolean prioritySetExplicitly, int priority, boolean mayBlock,
-                boolean useThreadPool, byte extensionId, byte[] extensionData, Runnable task,
-                long delay);
+        void postDelayedTask(int priority, boolean mayBlock, boolean useThreadPool,
+                byte extensionId, byte[] extensionData, Runnable task, long delay);
     }
 }
diff --git a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
index d1a750a..7ede72d 100644
--- a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
+++ b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
@@ -163,9 +163,8 @@
     protected void initNativeTaskRunnerInternal() {
         if (mNativeTaskRunnerAndroid == 0) {
             mNativeTaskRunnerAndroid = TaskRunnerImplJni.get().init(mTaskRunnerType,
-                    mTaskTraits.mPrioritySetExplicitly, mTaskTraits.mPriority,
-                    mTaskTraits.mMayBlock, mTaskTraits.mUseThreadPool, mTaskTraits.mExtensionId,
-                    mTaskTraits.mExtensionData);
+                    mTaskTraits.mPriority, mTaskTraits.mMayBlock, mTaskTraits.mUseThreadPool,
+                    mTaskTraits.mExtensionId, mTaskTraits.mExtensionData);
         }
     }
 
@@ -191,8 +190,8 @@
     @NativeMethods
     interface Natives {
         // NB due to Proguard obfuscation it's easiest to pass the traits via arguments.
-        long init(@TaskRunnerType int taskRunnerType, boolean prioritySetExplicitly, int priority,
-                boolean mayBlock, boolean useThreadPool, byte extensionId, byte[] extensionData);
+        long init(@TaskRunnerType int taskRunnerType, int priority, boolean mayBlock,
+                boolean useThreadPool, byte extensionId, byte[] extensionData);
 
         void destroy(long nativeTaskRunnerAndroid);
         void postDelayedTask(long nativeTaskRunnerAndroid, Runnable task, long delay);
diff --git a/base/android/java/src/org/chromium/base/task/TaskTraits.java b/base/android/java/src/org/chromium/base/task/TaskTraits.java
index a4546bda..f967aa9 100644
--- a/base/android/java/src/org/chromium/base/task/TaskTraits.java
+++ b/base/android/java/src/org/chromium/base/task/TaskTraits.java
@@ -79,7 +79,6 @@
 
     // For convenience of the JNI code, we use primitive types only.
     // Note shutdown behavior is not supported on android.
-    boolean mPrioritySetExplicitly;
     int mPriority;
     boolean mMayBlock;
     boolean mUseThreadPool;
@@ -89,11 +88,11 @@
 
     // Derive custom traits from existing trait constants.
     private TaskTraits() {
-        mPriority = TaskPriority.USER_VISIBLE;
+        // Assume USER_BLOCKING by default.
+        mPriority = TaskPriority.USER_BLOCKING;
     }
 
     private TaskTraits(TaskTraits other) {
-        mPrioritySetExplicitly = other.mPrioritySetExplicitly;
         mPriority = other.mPriority;
         mMayBlock = other.mMayBlock;
         mUseThreadPool = other.mUseThreadPool;
@@ -103,7 +102,6 @@
 
     public TaskTraits taskPriority(int taskPriority) {
         TaskTraits taskTraits = new TaskTraits(this);
-        taskTraits.mPrioritySetExplicitly = true;
         taskTraits.mPriority = taskPriority;
         return taskTraits;
     }
@@ -181,10 +179,8 @@
             return true;
         } else if (object instanceof TaskTraits) {
             TaskTraits other = (TaskTraits) object;
-            return mPrioritySetExplicitly == other.mPrioritySetExplicitly
-                    && mPriority == other.mPriority && mMayBlock == other.mMayBlock
-                    && mUseThreadPool == other.mUseThreadPool
-                    && mExtensionId == other.mExtensionId
+            return mPriority == other.mPriority && mMayBlock == other.mMayBlock
+                    && mUseThreadPool == other.mUseThreadPool && mExtensionId == other.mExtensionId
                     && Arrays.equals(mExtensionData, other.mExtensionData)
                     && mIsChoreographerFrame == other.mIsChoreographerFrame;
         } else {
@@ -195,7 +191,6 @@
     @Override
     public int hashCode() {
         int hash = 31;
-        hash = 37 * hash + (mPrioritySetExplicitly ? 0 : 1);
         hash = 37 * hash + mPriority;
         hash = 37 * hash + (mMayBlock ? 0 : 1);
         hash = 37 * hash + (mUseThreadPool ? 0 : 1);
diff --git a/base/android/task_scheduler/post_task_android.cc b/base/android/task_scheduler/post_task_android.cc
index 78f1cf7..5473cac0 100644
--- a/base/android/task_scheduler/post_task_android.cc
+++ b/base/android/task_scheduler/post_task_android.cc
@@ -45,14 +45,12 @@
 // static
 TaskTraits PostTaskAndroid::CreateTaskTraits(
     JNIEnv* env,
-    jboolean priority_set_explicitly,
     jint priority,
     jboolean may_block,
     jboolean use_thread_pool,
     jbyte extension_id,
     const base::android::JavaParamRef<jbyteArray>& extension_data) {
-  return TaskTraits(priority_set_explicitly,
-                    static_cast<TaskPriority>(priority), may_block,
+  return TaskTraits(static_cast<TaskPriority>(priority), may_block,
                     use_thread_pool,
                     TaskTraitsExtensionStorage(
                         extension_id, GetExtensionData(env, extension_data)));
@@ -60,7 +58,6 @@
 
 void JNI_PostTask_PostDelayedTask(
     JNIEnv* env,
-    jboolean priority_set_explicitly,
     jint priority,
     jboolean may_block,
     jboolean use_thread_pool,
@@ -72,8 +69,8 @@
   // BindOnce because JNIEnv is thread specific.
   PostDelayedTask(FROM_HERE,
                   PostTaskAndroid::CreateTaskTraits(
-                      env, priority_set_explicitly, priority, may_block,
-                      use_thread_pool, extension_id, extension_data),
+                      env, priority, may_block, use_thread_pool, extension_id,
+                      extension_data),
                   BindOnce(&PostTaskAndroid::RunJavaTask,
                            base::android::ScopedJavaGlobalRef<jobject>(task)),
                   TimeDelta::FromMilliseconds(delay));
diff --git a/base/android/task_scheduler/post_task_android.h b/base/android/task_scheduler/post_task_android.h
index c15c822..f097196 100644
--- a/base/android/task_scheduler/post_task_android.h
+++ b/base/android/task_scheduler/post_task_android.h
@@ -24,7 +24,6 @@
 
   static TaskTraits CreateTaskTraits(
       JNIEnv* env,
-      jboolean priority_set_explicitly,
       jint priority,
       jboolean may_block,
       jboolean use_thread_pool,
diff --git a/base/android/task_scheduler/task_runner_android.cc b/base/android/task_scheduler/task_runner_android.cc
index d1a5e4e1..ca18d20b 100644
--- a/base/android/task_scheduler/task_runner_android.cc
+++ b/base/android/task_scheduler/task_runner_android.cc
@@ -16,15 +16,13 @@
 jlong JNI_TaskRunnerImpl_Init(
     JNIEnv* env,
     jint task_runner_type,
-    jboolean priority_set_explicitly,
     jint priority,
     jboolean may_block,
     jboolean thread_pool,
     jbyte extension_id,
     const base::android::JavaParamRef<jbyteArray>& extension_data) {
   TaskTraits task_traits = PostTaskAndroid::CreateTaskTraits(
-      env, priority_set_explicitly, priority, may_block, thread_pool,
-      extension_id, extension_data);
+      env, priority, may_block, thread_pool, extension_id, extension_data);
   scoped_refptr<TaskRunner> task_runner;
   switch (static_cast<TaskRunnerType>(task_runner_type)) {
     case TaskRunnerType::BASE:
diff --git a/base/mac/scoped_block.h b/base/mac/scoped_block.h
index 10ab4b4e..4011f9ab 100644
--- a/base/mac/scoped_block.h
+++ b/base/mac/scoped_block.h
@@ -10,9 +10,7 @@
 #include "base/mac/scoped_typeref.h"
 
 #if defined(__has_feature) && __has_feature(objc_arc)
-#define BASE_MAC_BRIDGE_CAST(TYPE, VALUE) (__bridge TYPE)(VALUE)
-#else
-#define BASE_MAC_BRIDGE_CAST(TYPE, VALUE) VALUE
+#error "Cannot include base/mac/scoped_block.h in file built with ARC."
 #endif
 
 namespace base {
@@ -23,13 +21,8 @@
 template <typename B>
 struct ScopedBlockTraits {
   static B InvalidValue() { return nullptr; }
-  static B Retain(B block) {
-    return BASE_MAC_BRIDGE_CAST(
-        B, Block_copy(BASE_MAC_BRIDGE_CAST(const void*, block)));
-  }
-  static void Release(B block) {
-    Block_release(BASE_MAC_BRIDGE_CAST(const void*, block));
-  }
+  static B Retain(B block) { return Block_copy(block); }
+  static void Release(B block) { Block_release(block); }
 };
 
 }  // namespace internal
@@ -37,36 +30,9 @@
 // ScopedBlock<> is patterned after ScopedCFTypeRef<>, but uses Block_copy() and
 // Block_release() instead of CFRetain() and CFRelease().
 template <typename B>
-class ScopedBlock : public ScopedTypeRef<B, internal::ScopedBlockTraits<B>> {
- public:
-  using Traits = internal::ScopedBlockTraits<B>;
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-  explicit ScopedBlock(
-      B block = Traits::InvalidValue(),
-      base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
-      : ScopedTypeRef<B, Traits>(block, policy) {}
-#else
-  explicit ScopedBlock(B block = Traits::InvalidValue())
-      : ScopedTypeRef<B, Traits>(block, base::scoped_policy::RETAIN) {}
-#endif
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-  void reset(B block = Traits::InvalidValue(),
-             base::scoped_policy::OwnershipPolicy policy =
-                 base::scoped_policy::ASSUME) {
-    ScopedTypeRef<B, Traits>::reset(block, policy);
-  }
-#else
-  void reset(B block = Traits::InvalidValue()) {
-    ScopedTypeRef<B, Traits>::reset(block, base::scoped_policy::RETAIN);
-  }
-#endif
-};
+using ScopedBlock = ScopedTypeRef<B, internal::ScopedBlockTraits<B>>;
 
 }  // namespace mac
 }  // namespace base
 
-#undef BASE_MAC_BRIDGE_CAST
-
 #endif  // BASE_MAC_SCOPED_BLOCK_H_
diff --git a/base/mac/scoped_nsobject.h b/base/mac/scoped_nsobject.h
index b7d1195..ddcfa896 100644
--- a/base/mac/scoped_nsobject.h
+++ b/base/mac/scoped_nsobject.h
@@ -16,10 +16,12 @@
 #include "base/compiler_specific.h"
 #include "base/mac/scoped_typeref.h"
 
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-@class NSAutoreleasePool;
+#if defined(__has_feature) && __has_feature(objc_arc)
+#error "Cannot include base/mac/scoped_nsobject.h in file built with ARC."
 #endif
 
+@class NSAutoreleasePool;
+
 namespace base {
 
 // scoped_nsobject<> is patterned after std::unique_ptr<>, but maintains
@@ -40,39 +42,14 @@
 // NSAutoreleasePool; for Objective-C(++) code use @autoreleasepool instead. We
 // check for bad uses of scoped_nsobject and NSAutoreleasePool at compile time
 // with a template specialization (see below).
-//
-// If Automatic Reference Counting (aka ARC) is enabled then the ownership
-// policy is not controllable by the user as ARC make it really difficult to
-// transfer ownership (the reference passed to scoped_nsobject constructor is
-// sunk by ARC and __attribute((ns_consumed)) appears to not work correctly
-// with Objective-C++ see https://llvm.org/bugs/show_bug.cgi?id=27887). Due to
-// that, the policy is always to |RETAIN| when using ARC.
 
 namespace internal {
 
-BASE_EXPORT id ScopedNSProtocolTraitsRetain(__unsafe_unretained id obj)
-    __attribute((ns_returns_not_retained));
-BASE_EXPORT id ScopedNSProtocolTraitsAutoRelease(__unsafe_unretained id obj)
-    __attribute((ns_returns_not_retained));
-BASE_EXPORT void ScopedNSProtocolTraitsRelease(__unsafe_unretained id obj);
-
-// Traits for ScopedTypeRef<>. As this class may be compiled from file with
-// Automatic Reference Counting enable or not all methods have annotation to
-// enforce the same code generation in both case (in particular, the Retain
-// method uses ns_returns_not_retained to prevent ARC to insert a -release
-// call on the returned value and thus defeating the -retain).
 template <typename NST>
 struct ScopedNSProtocolTraits {
-  static NST InvalidValue() __attribute((ns_returns_not_retained)) {
-    return nil;
-  }
-  static NST Retain(__unsafe_unretained NST nst)
-      __attribute((ns_returns_not_retained)) {
-    return ScopedNSProtocolTraitsRetain(nst);
-  }
-  static void Release(__unsafe_unretained NST nst) {
-    ScopedNSProtocolTraitsRelease(nst);
-  }
+  static NST InvalidValue() { return nil; }
+  static NST Retain(NST nst) { return [nst retain]; }
+  static void Release(NST nst) { [nst release]; }
 };
 
 }  // namespace internal
@@ -81,49 +58,11 @@
 class scoped_nsprotocol
     : public ScopedTypeRef<NST, internal::ScopedNSProtocolTraits<NST>> {
  public:
-  using Traits = internal::ScopedNSProtocolTraits<NST>;
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-  explicit constexpr scoped_nsprotocol(
-      NST object = Traits::InvalidValue(),
-      base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
-      : ScopedTypeRef<NST, Traits>(object, policy) {}
-#else
-  explicit constexpr scoped_nsprotocol(NST object = Traits::InvalidValue())
-      : ScopedTypeRef<NST, Traits>(object, base::scoped_policy::RETAIN) {}
-#endif
-
-  scoped_nsprotocol(const scoped_nsprotocol<NST>& that)
-      : ScopedTypeRef<NST, Traits>(that) {}
-
-  template <typename NSR>
-  explicit scoped_nsprotocol(const scoped_nsprotocol<NSR>& that_as_subclass)
-      : ScopedTypeRef<NST, Traits>(that_as_subclass) {}
-
-  scoped_nsprotocol(scoped_nsprotocol<NST>&& that)
-      : ScopedTypeRef<NST, Traits>(std::move(that)) {}
-
-  scoped_nsprotocol& operator=(const scoped_nsprotocol<NST>& that) {
-    ScopedTypeRef<NST, Traits>::operator=(that);
-    return *this;
-  }
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-  void reset(NST object = Traits::InvalidValue(),
-             base::scoped_policy::OwnershipPolicy policy =
-                 base::scoped_policy::ASSUME) {
-    ScopedTypeRef<NST, Traits>::reset(object, policy);
-  }
-#else
-  void reset(NST object = Traits::InvalidValue()) {
-    ScopedTypeRef<NST, Traits>::reset(object, base::scoped_policy::RETAIN);
-  }
-#endif
+  using ScopedTypeRef<NST,
+                      internal::ScopedNSProtocolTraits<NST>>::ScopedTypeRef;
 
   // Shift reference to the autorelease pool to be released later.
-  NST autorelease() __attribute((ns_returns_not_retained)) {
-    return internal::ScopedNSProtocolTraitsAutoRelease(this->release());
-  }
+  NST autorelease() { return [this->release() autorelease]; }
 };
 
 // Free functions
@@ -145,93 +84,17 @@
 template <typename NST>
 class scoped_nsobject : public scoped_nsprotocol<NST*> {
  public:
-  using Traits = typename scoped_nsprotocol<NST*>::Traits;
+  using scoped_nsprotocol<NST*>::scoped_nsprotocol;
 
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-  explicit constexpr scoped_nsobject(
-      NST* object = Traits::InvalidValue(),
-      base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
-      : scoped_nsprotocol<NST*>(object, policy) {}
-#else
-  explicit constexpr scoped_nsobject(NST* object = Traits::InvalidValue())
-      : scoped_nsprotocol<NST*>(object) {}
-#endif
-
-  scoped_nsobject(const scoped_nsobject<NST>& that)
-      : scoped_nsprotocol<NST*>(that) {}
-
-  template <typename NSR>
-  explicit scoped_nsobject(const scoped_nsobject<NSR>& that_as_subclass)
-      : scoped_nsprotocol<NST*>(that_as_subclass) {}
-
-  scoped_nsobject(scoped_nsobject<NST>&& that)
-      : scoped_nsprotocol<NST*>(std::move(that)) {}
-
-  scoped_nsobject& operator=(const scoped_nsobject<NST>& that) {
-    scoped_nsprotocol<NST*>::operator=(that);
-    return *this;
-  }
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-  void reset(NST* object = Traits::InvalidValue(),
-             base::scoped_policy::OwnershipPolicy policy =
-                 base::scoped_policy::ASSUME) {
-    scoped_nsprotocol<NST*>::reset(object, policy);
-  }
-#else
-  void reset(NST* object = Traits::InvalidValue()) {
-    scoped_nsprotocol<NST*>::reset(object);
-  }
-#endif
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
   static_assert(std::is_same<NST, NSAutoreleasePool>::value == false,
                 "Use @autoreleasepool instead");
-#endif
 };
 
 // Specialization to make scoped_nsobject<id> work.
 template<>
 class scoped_nsobject<id> : public scoped_nsprotocol<id> {
  public:
-  using Traits = typename scoped_nsprotocol<id>::Traits;
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-  explicit constexpr scoped_nsobject(
-      id object = Traits::InvalidValue(),
-      base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
-      : scoped_nsprotocol<id>(object, policy) {}
-#else
-  explicit constexpr scoped_nsobject(id object = Traits::InvalidValue())
-      : scoped_nsprotocol<id>(object) {}
-#endif
-
-  scoped_nsobject(const scoped_nsobject<id>& that)
-      : scoped_nsprotocol<id>(that) {}
-
-  template <typename NSR>
-  explicit scoped_nsobject(const scoped_nsobject<NSR>& that_as_subclass)
-      : scoped_nsprotocol<id>(that_as_subclass) {}
-
-  scoped_nsobject(scoped_nsobject<id>&& that)
-      : scoped_nsprotocol<id>(std::move(that)) {}
-
-  scoped_nsobject& operator=(const scoped_nsobject<id>& that) {
-    scoped_nsprotocol<id>::operator=(that);
-    return *this;
-  }
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-  void reset(id object = Traits::InvalidValue(),
-             base::scoped_policy::OwnershipPolicy policy =
-                 base::scoped_policy::ASSUME) {
-    scoped_nsprotocol<id>::reset(object, policy);
-  }
-#else
-  void reset(id object = Traits::InvalidValue()) {
-    scoped_nsprotocol<id>::reset(object);
-  }
-#endif
+  using scoped_nsprotocol<id>::scoped_nsprotocol;
 };
 
 }  // namespace base
diff --git a/base/mac/scoped_nsobject.mm b/base/mac/scoped_nsobject.mm
deleted file mode 100644
index 65b4031..0000000
--- a/base/mac/scoped_nsobject.mm
+++ /dev/null
@@ -1,23 +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.
-
-#import "base/mac/scoped_nsobject.h"
-
-namespace base {
-namespace internal {
-
-id ScopedNSProtocolTraitsRetain(id obj) {
-  return [obj retain];
-}
-
-id ScopedNSProtocolTraitsAutoRelease(id obj) {
-  return [obj autorelease];
-}
-
-void ScopedNSProtocolTraitsRelease(id obj) {
-  return [obj release];
-}
-
-}  // namespace internal
-}  // namespace base
diff --git a/base/mac/scoped_nsobject_unittest_arc.mm b/base/mac/scoped_nsobject_unittest_arc.mm
deleted file mode 100644
index 5cbf3f83..0000000
--- a/base/mac/scoped_nsobject_unittest_arc.mm
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <vector>
-
-#import <CoreFoundation/CoreFoundation.h>
-
-#include "base/logging.h"
-#import "base/mac/scoped_nsobject.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-template <typename NST>
-CFIndex GetRetainCount(const base::scoped_nsobject<NST>& nst) {
-  @autoreleasepool {
-    return CFGetRetainCount((__bridge CFTypeRef)nst.get()) - 1;
-  }
-}
-
-#if __has_feature(objc_arc_weak)
-TEST(ScopedNSObjectTestARC, DefaultPolicyIsRetain) {
-  __weak id o;
-  @autoreleasepool {
-    base::scoped_nsprotocol<id> p([[NSObject alloc] init]);
-    o = p.get();
-    DCHECK_EQ(o, p.get());
-  }
-  DCHECK_EQ(o, nil);
-}
-#endif
-
-TEST(ScopedNSObjectTestARC, ScopedNSObject) {
-  base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
-  @autoreleasepool {
-    EXPECT_TRUE(p1.get());
-    EXPECT_TRUE(p1.get());
-  }
-  EXPECT_EQ(1, GetRetainCount(p1));
-  EXPECT_EQ(1, GetRetainCount(p1));
-  base::scoped_nsobject<NSObject> p2(p1);
-  @autoreleasepool {
-    EXPECT_EQ(p1.get(), p2.get());
-  }
-  EXPECT_EQ(2, GetRetainCount(p1));
-  p2.reset();
-  EXPECT_EQ(nil, p2.get());
-  EXPECT_EQ(1, GetRetainCount(p1));
-  {
-    base::scoped_nsobject<NSObject> p3 = p1;
-    @autoreleasepool {
-      EXPECT_EQ(p1.get(), p3.get());
-    }
-    EXPECT_EQ(2, GetRetainCount(p1));
-    @autoreleasepool {
-      p3 = p1;
-      EXPECT_EQ(p1.get(), p3.get());
-    }
-    EXPECT_EQ(2, GetRetainCount(p1));
-  }
-  EXPECT_EQ(1, GetRetainCount(p1));
-  base::scoped_nsobject<NSObject> p4;
-  @autoreleasepool {
-    p4 = base::scoped_nsobject<NSObject>(p1.get());
-  }
-  EXPECT_EQ(2, GetRetainCount(p1));
-  @autoreleasepool {
-    EXPECT_TRUE(p1 == p1.get());
-    EXPECT_TRUE(p1 == p1);
-    EXPECT_FALSE(p1 != p1);
-    EXPECT_FALSE(p1 != p1.get());
-  }
-  base::scoped_nsobject<NSObject> p5([[NSObject alloc] init]);
-  @autoreleasepool {
-    EXPECT_TRUE(p1 != p5);
-    EXPECT_TRUE(p1 != p5.get());
-    EXPECT_FALSE(p1 == p5);
-    EXPECT_FALSE(p1 == p5.get());
-  }
-
-  base::scoped_nsobject<NSObject> p6 = p1;
-  EXPECT_EQ(3, GetRetainCount(p6));
-  @autoreleasepool {
-    p6.autorelease();
-    EXPECT_EQ(nil, p6.get());
-  }
-  EXPECT_EQ(2, GetRetainCount(p1));
-}
-
-TEST(ScopedNSObjectTestARC, ScopedNSObjectInContainer) {
-  base::scoped_nsobject<id> p([[NSObject alloc] init]);
-  @autoreleasepool {
-    EXPECT_TRUE(p.get());
-  }
-  EXPECT_EQ(1, GetRetainCount(p));
-  @autoreleasepool {
-    std::vector<base::scoped_nsobject<id>> objects;
-    objects.push_back(p);
-    EXPECT_EQ(2, GetRetainCount(p));
-    @autoreleasepool {
-      EXPECT_EQ(p.get(), objects[0].get());
-    }
-    objects.push_back(base::scoped_nsobject<id>([[NSObject alloc] init]));
-    @autoreleasepool {
-      EXPECT_TRUE(objects[1].get());
-    }
-    EXPECT_EQ(1, GetRetainCount(objects[1]));
-  }
-  EXPECT_EQ(1, GetRetainCount(p));
-}
-
-TEST(ScopedNSObjectTestARC, ScopedNSObjectFreeFunctions) {
-  base::scoped_nsobject<id> p1([[NSObject alloc] init]);
-  id o1 = p1.get();
-  EXPECT_TRUE(o1 == p1);
-  EXPECT_FALSE(o1 != p1);
-  base::scoped_nsobject<id> p2([[NSObject alloc] init]);
-  EXPECT_TRUE(o1 != p2);
-  EXPECT_FALSE(o1 == p2);
-  id o2 = p2.get();
-  swap(p1, p2);
-  EXPECT_EQ(o2, p1.get());
-  EXPECT_EQ(o1, p2.get());
-}
-
-}  // namespace
diff --git a/base/mac/scoped_typeref.h b/base/mac/scoped_typeref.h
index dd9841d734..3bec05f 100644
--- a/base/mac/scoped_typeref.h
+++ b/base/mac/scoped_typeref.h
@@ -54,7 +54,7 @@
   typedef T element_type;
 
   explicit constexpr ScopedTypeRef(
-      __unsafe_unretained T object = Traits::InvalidValue(),
+      T object = Traits::InvalidValue(),
       base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
       : object_(object) {
     if (object_ && policy == base::scoped_policy::RETAIN)
@@ -97,7 +97,7 @@
     return &object_;
   }
 
-  void reset(__unsafe_unretained T object = Traits::InvalidValue(),
+  void reset(T object = Traits::InvalidValue(),
              base::scoped_policy::OwnershipPolicy policy =
                  base::scoped_policy::ASSUME) {
     if (object && policy == base::scoped_policy::RETAIN)
@@ -107,16 +107,16 @@
     object_ = object;
   }
 
-  bool operator==(__unsafe_unretained T that) const { return object_ == that; }
+  bool operator==(T that) const { return object_ == that; }
 
-  bool operator!=(__unsafe_unretained T that) const { return object_ != that; }
+  bool operator!=(T that) const { return object_ != that; }
 
-  operator T() const __attribute((ns_returns_not_retained)) { return object_; }
+  operator T() const { return object_; }
 
-  T get() const __attribute((ns_returns_not_retained)) { return object_; }
+  T get() const { return object_; }
 
   void swap(ScopedTypeRef& that) {
-    __unsafe_unretained T temp = that.object_;
+    T temp = that.object_;
     that.object_ = object_;
     object_ = temp;
   }
@@ -124,14 +124,14 @@
   // ScopedTypeRef<>::release() is like std::unique_ptr<>::release.  It is NOT
   // a wrapper for Release().  To force a ScopedTypeRef<> object to call
   // Release(), use ScopedTypeRef<>::reset().
-  T release() __attribute((ns_returns_not_retained)) WARN_UNUSED_RESULT {
-    __unsafe_unretained T temp = object_;
+  T release() WARN_UNUSED_RESULT {
+    T temp = object_;
     object_ = Traits::InvalidValue();
     return temp;
   }
 
  private:
-  __unsafe_unretained T object_;
+  T object_;
 };
 
 }  // namespace base
diff --git a/base/memory/shared_memory_mapping.cc b/base/memory/shared_memory_mapping.cc
index 8426fa8..0c9c0f8a1 100644
--- a/base/memory/shared_memory_mapping.cc
+++ b/base/memory/shared_memory_mapping.cc
@@ -34,21 +34,18 @@
 SharedMemoryMapping::SharedMemoryMapping() = default;
 
 SharedMemoryMapping::SharedMemoryMapping(SharedMemoryMapping&& mapping) noexcept
-    : memory_(mapping.memory_),
+    : memory_(std::exchange(mapping.memory_, nullptr)),
       size_(mapping.size_),
       mapped_size_(mapping.mapped_size_),
-      guid_(mapping.guid_) {
-  mapping.memory_ = nullptr;
-}
+      guid_(mapping.guid_) {}
 
 SharedMemoryMapping& SharedMemoryMapping::operator=(
     SharedMemoryMapping&& mapping) noexcept {
   Unmap();
-  memory_ = mapping.memory_;
+  memory_ = std::exchange(mapping.memory_, nullptr);
   size_ = mapping.size_;
   mapped_size_ = mapping.mapped_size_;
   guid_ = mapping.guid_;
-  mapping.memory_ = nullptr;
   return *this;
 }
 
diff --git a/base/task/post_job.cc b/base/task/post_job.cc
index d1f3f23d..8de9115 100644
--- a/base/task/post_job.cc
+++ b/base/task/post_job.cc
@@ -134,7 +134,7 @@
   DCHECK_GE(internal::GetTaskPriorityForCurrentThread(),
             task_source_->priority_racy())
       << "Join may not be called on Job with higher priority than the current "
-         "one.";
+         "thread.";
   UpdatePriority(internal::GetTaskPriorityForCurrentThread());
   bool must_run = task_source_->WillJoin();
   while (must_run)
@@ -173,10 +173,8 @@
   DCHECK_EQ(traits.extension_id(),
             TaskTraitsExtensionStorage::kInvalidExtensionId);
 
-  TaskTraits adjusted_traits = traits;
-  adjusted_traits.InheritPriority(internal::GetTaskPriorityForCurrentThread());
   auto task_source = base::MakeRefCounted<internal::JobTaskSource>(
-      from_here, adjusted_traits, std::move(worker_task),
+      from_here, traits, std::move(worker_task),
       std::move(max_concurrency_callback),
       static_cast<internal::ThreadPoolImpl*>(ThreadPoolInstance::Get()));
   const bool queued =
diff --git a/base/task/post_task.cc b/base/task/post_task.cc
index 439604a..c1d6d93 100644
--- a/base/task/post_task.cc
+++ b/base/task/post_task.cc
@@ -32,14 +32,6 @@
   const TaskTraits traits_;
 };
 
-// Returns TaskTraits based on |traits|. If TaskPriority hasn't been set
-// explicitly in |traits|, the returned TaskTraits will inherit the current
-// TaskPriority.
-TaskTraits GetTaskTraitsWithExplicitPriority(TaskTraits traits) {
-  traits.InheritPriority(internal::GetTaskPriorityForCurrentThread());
-  return traits;
-}
-
 TaskExecutor* GetTaskExecutorForTraits(const TaskTraits& traits) {
   const bool has_extension =
       traits.extension_id() != TaskTraitsExtensionStorage::kInvalidExtensionId;
@@ -93,9 +85,8 @@
                      const TaskTraits& traits,
                      OnceClosure task,
                      TimeDelta delay) {
-  const TaskTraits adjusted_traits = GetTaskTraitsWithExplicitPriority(traits);
-  return GetTaskExecutorForTraits(adjusted_traits)
-      ->PostDelayedTask(from_here, adjusted_traits, std::move(task), delay);
+  return GetTaskExecutorForTraits(traits)->PostDelayedTask(
+      from_here, traits, std::move(task), delay);
 }
 
 bool PostTaskAndReply(const Location& from_here,
@@ -128,9 +119,8 @@
            TaskTraitsExtensionStorage::kInvalidExtensionId)
       << "Extension traits cannot be used with "
          "CreateUpdateableSequencedTaskRunner().";
-  const TaskTraits adjusted_traits = GetTaskTraitsWithExplicitPriority(traits);
   return static_cast<internal::ThreadPoolImpl*>(ThreadPoolInstance::Get())
-      ->CreateUpdateableSequencedTaskRunner(adjusted_traits);
+      ->CreateUpdateableSequencedTaskRunner(traits);
 }
 
 scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunner(
diff --git a/base/task/post_task_unittest.cc b/base/task/post_task_unittest.cc
index fe141eb1..cbbbe66 100644
--- a/base/task/post_task_unittest.cc
+++ b/base/task/post_task_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
-#include "base/task/scoped_set_task_priority_for_current_thread.h"
 #include "base/task/task_executor.h"
 #include "base/task/test_task_traits_extension.h"
 #include "base/test/bind_test_util.h"
@@ -231,21 +230,6 @@
       RegisterTaskExecutor(TestTaskTraitsExtension::kExtensionId, &executor_));
 }
 
-TEST_F(PostTaskTestWithExecutor, PriorityInherited) {
-  internal::ScopedSetTaskPriorityForCurrentThread scoped_priority(
-      TaskPriority::BEST_EFFORT);
-  TaskTraits traits = {TestExtensionBoolTrait()};
-  TaskTraits traits_with_inherited_priority = traits;
-  traits_with_inherited_priority.InheritPriority(TaskPriority::BEST_EFFORT);
-  EXPECT_FALSE(traits_with_inherited_priority.priority_set_explicitly());
-  EXPECT_CALL(executor_,
-              PostDelayedTaskMock(_, traits_with_inherited_priority, _, _))
-      .Times(1);
-  EXPECT_TRUE(PostTask(FROM_HERE, traits, DoNothing()));
-  EXPECT_TRUE(executor_.runner()->HasPendingTask());
-  executor_.runner()->ClearPendingTasks();
-}
-
 namespace {
 
 class FlagOnDelete {
diff --git a/base/task/scoped_set_task_priority_for_current_thread.h b/base/task/scoped_set_task_priority_for_current_thread.h
index 713775f..b94d27f 100644
--- a/base/task/scoped_set_task_priority_for_current_thread.h
+++ b/base/task/scoped_set_task_priority_for_current_thread.h
@@ -26,7 +26,7 @@
 };
 
 // Returns the priority of the task running on the current thread,
-// or TaskPriority::USER_VISIBLE if none.
+// or TaskPriority::USER_BLOCKING by default if none.
 BASE_EXPORT TaskPriority GetTaskPriorityForCurrentThread();
 
 }  // namespace internal
diff --git a/base/task/task_features.cc b/base/task/task_features.cc
index cda0808..6e6a1d1 100644
--- a/base/task/task_features.cc
+++ b/base/task/task_features.cc
@@ -25,7 +25,4 @@
 const Feature kUseFiveMinutesThreadReclaimTime = {
     "UseFiveMinutesThreadReclaimTime", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const Feature kNoPriorityInheritanceFromThreadPool{
-    "NoPriorityInheritanceFromThreadPool", base::FEATURE_ENABLED_BY_DEFAULT};
-
 }  // namespace base
diff --git a/base/task/task_features.h b/base/task/task_features.h
index 39d5680..6763cab 100644
--- a/base/task/task_features.h
+++ b/base/task/task_features.h
@@ -40,12 +40,6 @@
 // minutes, instead of 30 seconds.
 extern const BASE_EXPORT Feature kUseFiveMinutesThreadReclaimTime;
 
-// Under this feature, the current default of inheriting priority when posting
-// from the ThreadPool is disabled.
-// Details @
-// https://docs.google.com/document/d/13PIBPuSPJbrgHAgyRbY22EWAfH2narnxpa_CgBmZbSY
-extern const BASE_EXPORT Feature kNoPriorityInheritanceFromThreadPool;
-
 }  // namespace base
 
 #endif  // BASE_TASK_TASK_FEATURES_H_
diff --git a/base/task/task_traits.cc b/base/task/task_traits.cc
index 472d437..4ebb87a7 100644
--- a/base/task/task_traits.cc
+++ b/base/task/task_traits.cc
@@ -12,17 +12,6 @@
 
 namespace base {
 
-// The state of |FeatureList::IsEnabled(kNoPriorityInheritanceFromThreadPool)|
-// as controlled by intenal::SetNoPriorityInheritanceFromThreadPool().
-bool g_no_priority_inheritance_from_thread_pool = false;
-
-void TaskTraits::InheritPriority(TaskPriority priority) {
-  if (priority_set_explicitly() || g_no_priority_inheritance_from_thread_pool)
-    return;
-
-  priority_ = static_cast<uint8_t>(priority);
-}
-
 const char* TaskPriorityToString(TaskPriority task_priority) {
   switch (task_priority) {
     case TaskPriority::BEST_EFFORT:
@@ -61,10 +50,4 @@
   return os;
 }
 
-namespace internal {
-void SetNoPriorityInheritanceFromThreadPool() {
-  g_no_priority_inheritance_from_thread_pool = true;
-}
-}  // namespace internal
-
 }  // namespace base
diff --git a/base/task/task_traits.h b/base/task/task_traits.h
index 5755418..04f6343 100644
--- a/base/task/task_traits.h
+++ b/base/task/task_traits.h
@@ -66,6 +66,12 @@
   // Example:
   // - Loading and rendering a web page after the user clicks a link.
   // - Sorting suggestions after the user types a character in the omnibox.
+  //
+  // This is the default TaskPriority in order for tasks to run in order by
+  // default and avoid unintended consequences. The only way to get a task to
+  // run at a higher priority than USER_BLOCKING is to coordinate with a
+  // higher-level scheduler (contact scheduler-dev@chromium.org for such use
+  // cases).
   USER_BLOCKING,
 
   // This will always be equal to the highest priority available.
@@ -198,18 +204,22 @@
     ValidTrait(ThreadPool);
   };
 
-  // Invoking this constructor without arguments produces TaskTraits that are
-  // appropriate for tasks that
+  // Invoking this constructor without arguments produces default TaskTraits
+  // that are appropriate for tasks that
   //     (1) don't block (ref. MayBlock() and WithBaseSyncPrimitives()),
-  //     (2) prefer inheriting the current priority to specifying their own, and
+  //     (2) pertain to user-blocking activity,
+  //         (explicitly or implicitly by having an ordering dependency with a
+  //          component that does)
   //     (3) can either block shutdown or be skipped on shutdown
-  //         (ThreadPoolInstance implementation is free to choose a fitting
-  //         default).
+  //         (the task recipient is free to choose a fitting default).
   //
-  // To get TaskTraits for tasks that require stricter guarantees and/or know
-  // the specific TaskPriority appropriate for them, provide arguments of type
-  // TaskPriority, TaskShutdownBehavior, ThreadPolicy, MayBlock and/or
-  // WithBaseSyncPrimitives in any order to the constructor.
+  // To get TaskTraits for tasks that have more precise traits: provide any
+  // combination of ValidTrait's as arguments to this constructor.
+  //
+  // Note: When posting to well-known threads (e.g. UI/IO), default traits are
+  // almost always what you want unless you know for sure the task being posted
+  // has no explicit/implicit ordering dependency with anything else running at
+  // default (USER_BLOCKING) priority.
   //
   // E.g.
   // constexpr base::TaskTraits default_traits = {};
@@ -230,12 +240,8 @@
             trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>{},
             args...)),
         priority_(
-            static_cast<uint8_t>(
-                trait_helpers::GetEnum<TaskPriority,
-                                       TaskPriority::USER_BLOCKING>(args...)) |
-            (trait_helpers::HasTrait<TaskPriority, ArgTypes...>()
-                 ? kIsExplicitFlag
-                 : 0)),
+            trait_helpers::GetEnum<TaskPriority, TaskPriority::USER_BLOCKING>(
+                args...)),
         shutdown_behavior_(
             static_cast<uint8_t>(
                 trait_helpers::GetEnum<TaskShutdownBehavior,
@@ -273,22 +279,10 @@
   }
 
   // Sets the priority of tasks with these traits to |priority|.
-  void UpdatePriority(TaskPriority priority) {
-    priority_ = static_cast<uint8_t>(priority) | kIsExplicitFlag;
-  }
-
-  // Sets the priority to |priority| if it wasn't explicitly set before.
-  void InheritPriority(TaskPriority priority);
-
-  // Returns true if the priority was set explicitly.
-  constexpr bool priority_set_explicitly() const {
-    return priority_ & kIsExplicitFlag;
-  }
+  void UpdatePriority(TaskPriority priority) { priority_ = priority; }
 
   // Returns the priority of tasks with these traits.
-  constexpr TaskPriority priority() const {
-    return static_cast<TaskPriority>(priority_ & ~kIsExplicitFlag);
-  }
+  constexpr TaskPriority priority() const { return priority_; }
 
   // Returns true if the shutdown behavior was set explicitly.
   constexpr bool shutdown_behavior_set_explicitly() const {
@@ -336,14 +330,12 @@
   friend PostTaskAndroid;
 
   // For use by PostTaskAndroid.
-  TaskTraits(bool priority_set_explicitly,
-             TaskPriority priority,
+  TaskTraits(TaskPriority priority,
              bool may_block,
              bool use_thread_pool,
              TaskTraitsExtensionStorage extension)
       : extension_(extension),
-        priority_(static_cast<uint8_t>(priority) |
-                  (priority_set_explicitly ? kIsExplicitFlag : 0)),
+        priority_(priority),
         shutdown_behavior_(
             static_cast<uint8_t>(TaskShutdownBehavior::SKIP_ON_SHUTDOWN)),
         thread_policy_(static_cast<uint8_t>(ThreadPolicy::PREFER_BACKGROUND)),
@@ -368,7 +360,7 @@
 
   // Ordered for packing.
   TaskTraitsExtensionStorage extension_;
-  uint8_t priority_;
+  TaskPriority priority_;
   uint8_t shutdown_behavior_;
   uint8_t thread_policy_;
   bool may_block_;
@@ -390,13 +382,6 @@
     std::ostream& os,
     const TaskShutdownBehavior& shutdown_behavior);
 
-namespace internal {
-// Enables the kNoPriorityInheritanceFromThreadPool experimental feature. Must
-// be done statically when the ThreadPoolImpl is initialized because it's racy
-// to check the state later.
-void SetNoPriorityInheritanceFromThreadPool();
-}  // namespace internal
-
 }  // namespace base
 
 #endif  // BASE_TASK_TASK_TRAITS_H_
diff --git a/base/task/task_traits_unittest.cc b/base/task/task_traits_unittest.cc
index b695831..7a1ee8d2 100644
--- a/base/task/task_traits_unittest.cc
+++ b/base/task/task_traits_unittest.cc
@@ -10,7 +10,6 @@
 
 TEST(TaskTraitsTest, Default) {
   constexpr TaskTraits traits = {};
-  EXPECT_FALSE(traits.priority_set_explicitly());
   EXPECT_EQ(TaskPriority::USER_BLOCKING, traits.priority());
   EXPECT_FALSE(traits.shutdown_behavior_set_explicitly());
   EXPECT_EQ(TaskShutdownBehavior::SKIP_ON_SHUTDOWN, traits.shutdown_behavior());
@@ -22,7 +21,6 @@
 
 TEST(TaskTraitsTest, TaskPriority) {
   constexpr TaskTraits traits = {TaskPriority::BEST_EFFORT};
-  EXPECT_TRUE(traits.priority_set_explicitly());
   EXPECT_EQ(TaskPriority::BEST_EFFORT, traits.priority());
   EXPECT_FALSE(traits.shutdown_behavior_set_explicitly());
   EXPECT_EQ(TaskShutdownBehavior::SKIP_ON_SHUTDOWN, traits.shutdown_behavior());
@@ -35,7 +33,6 @@
 TEST(TaskTraitsTest, TaskShutdownBehavior) {
   constexpr TaskTraits traits = {ThreadPool(),
                                  TaskShutdownBehavior::BLOCK_SHUTDOWN};
-  EXPECT_FALSE(traits.priority_set_explicitly());
   EXPECT_EQ(TaskPriority::USER_BLOCKING, traits.priority());
   EXPECT_TRUE(traits.shutdown_behavior_set_explicitly());
   EXPECT_EQ(TaskShutdownBehavior::BLOCK_SHUTDOWN, traits.shutdown_behavior());
@@ -47,7 +44,6 @@
 
 TEST(TaskTraitsTest, ThreadPolicy) {
   constexpr TaskTraits traits = {ThreadPolicy::MUST_USE_FOREGROUND};
-  EXPECT_FALSE(traits.priority_set_explicitly());
   EXPECT_EQ(TaskPriority::USER_BLOCKING, traits.priority());
   EXPECT_FALSE(traits.shutdown_behavior_set_explicitly());
   EXPECT_EQ(TaskShutdownBehavior::SKIP_ON_SHUTDOWN, traits.shutdown_behavior());
@@ -59,7 +55,6 @@
 
 TEST(TaskTraitsTest, MayBlock) {
   constexpr TaskTraits traits = {MayBlock()};
-  EXPECT_FALSE(traits.priority_set_explicitly());
   EXPECT_EQ(TaskPriority::USER_BLOCKING, traits.priority());
   EXPECT_FALSE(traits.shutdown_behavior_set_explicitly());
   EXPECT_EQ(TaskShutdownBehavior::SKIP_ON_SHUTDOWN, traits.shutdown_behavior());
@@ -71,7 +66,6 @@
 
 TEST(TaskTraitsTest, WithBaseSyncPrimitives) {
   constexpr TaskTraits traits = {WithBaseSyncPrimitives()};
-  EXPECT_FALSE(traits.priority_set_explicitly());
   EXPECT_EQ(TaskPriority::USER_BLOCKING, traits.priority());
   EXPECT_FALSE(traits.shutdown_behavior_set_explicitly());
   EXPECT_EQ(TaskShutdownBehavior::SKIP_ON_SHUTDOWN, traits.shutdown_behavior());
@@ -84,34 +78,14 @@
 TEST(TaskTraitsTest, UpdatePriority) {
   {
     TaskTraits traits = {};
-    EXPECT_FALSE(traits.priority_set_explicitly());
     traits.UpdatePriority(TaskPriority::BEST_EFFORT);
     EXPECT_EQ(TaskPriority::BEST_EFFORT, traits.priority());
-    EXPECT_TRUE(traits.priority_set_explicitly());
   }
 
   {
     TaskTraits traits = {TaskPriority::USER_VISIBLE};
-    EXPECT_TRUE(traits.priority_set_explicitly());
     traits.UpdatePriority(TaskPriority::BEST_EFFORT);
     EXPECT_EQ(TaskPriority::BEST_EFFORT, traits.priority());
-    EXPECT_TRUE(traits.priority_set_explicitly());
-  }
-}
-
-TEST(TaskTraitsTest, InheritPriority) {
-  {
-    TaskTraits traits = {};
-    traits.InheritPriority(TaskPriority::BEST_EFFORT);
-    EXPECT_EQ(TaskPriority::BEST_EFFORT, traits.priority());
-    EXPECT_FALSE(traits.priority_set_explicitly());
-  }
-
-  {
-    TaskTraits traits = {TaskPriority::USER_VISIBLE};
-    traits.InheritPriority(TaskPriority::BEST_EFFORT);
-    EXPECT_EQ(TaskPriority::USER_VISIBLE, traits.priority());
-    EXPECT_TRUE(traits.priority_set_explicitly());
   }
 }
 
@@ -119,7 +93,6 @@
   constexpr TaskTraits traits = {
       TaskPriority::BEST_EFFORT, TaskShutdownBehavior::BLOCK_SHUTDOWN,
       ThreadPolicy::MUST_USE_FOREGROUND, MayBlock(), WithBaseSyncPrimitives()};
-  EXPECT_TRUE(traits.priority_set_explicitly());
   EXPECT_EQ(TaskPriority::BEST_EFFORT, traits.priority());
   EXPECT_TRUE(traits.shutdown_behavior_set_explicitly());
   EXPECT_EQ(TaskShutdownBehavior::BLOCK_SHUTDOWN, traits.shutdown_behavior());
@@ -137,8 +110,6 @@
 
   EXPECT_EQ(traits, traits_copy);
 
-  EXPECT_EQ(traits.priority_set_explicitly(),
-            traits_copy.priority_set_explicitly());
   EXPECT_EQ(traits.priority(), traits_copy.priority());
   EXPECT_EQ(traits.shutdown_behavior_set_explicitly(),
             traits_copy.shutdown_behavior_set_explicitly());
diff --git a/base/task/thread_pool.cc b/base/task/thread_pool.cc
index 23f6dfd..f5635d0e 100644
--- a/base/task/thread_pool.cc
+++ b/base/task/thread_pool.cc
@@ -30,14 +30,6 @@
   const TaskTraits traits_;
 };
 
-// Returns TaskTraits based on |traits|. If TaskPriority hasn't been set
-// explicitly in |traits|, the returned TaskTraits will inherit the current
-// TaskPriority.
-TaskTraits GetTaskTraitsWithExplicitPriority(TaskTraits traits) {
-  traits.InheritPriority(internal::GetTaskPriorityForCurrentThread());
-  return traits;
-}
-
 internal::ThreadPoolImpl* GetThreadPoolImpl() {
   auto* instance = ThreadPoolInstance::Get();
   DCHECK(instance)
@@ -84,8 +76,7 @@
                                  const TaskTraits& traits,
                                  OnceClosure task,
                                  TimeDelta delay) {
-  const TaskTraits adjusted_traits = GetTaskTraitsWithExplicitPriority(traits);
-  return GetThreadPoolImpl()->PostDelayedTask(from_here, adjusted_traits,
+  return GetThreadPoolImpl()->PostDelayedTask(from_here, traits,
                                               std::move(task), delay);
 }
 
@@ -113,9 +104,7 @@
 // static
 scoped_refptr<UpdateableSequencedTaskRunner>
 ThreadPool::CreateUpdateableSequencedTaskRunner(const TaskTraits& traits) {
-  const TaskTraits adjusted_traits = GetTaskTraitsWithExplicitPriority(traits);
-  return GetThreadPoolImpl()->CreateUpdateableSequencedTaskRunner(
-      adjusted_traits);
+  return GetThreadPoolImpl()->CreateUpdateableSequencedTaskRunner(traits);
 }
 
 // static
diff --git a/base/task/thread_pool/thread_pool_impl.cc b/base/task/thread_pool/thread_pool_impl.cc
index 1a4ea6bc..f24cbe0 100644
--- a/base/task/thread_pool/thread_pool_impl.cc
+++ b/base/task/thread_pool/thread_pool_impl.cc
@@ -118,9 +118,6 @@
 
   internal::InitializeThreadPrioritiesFeature();
 
-  if (FeatureList::IsEnabled(kNoPriorityInheritanceFromThreadPool))
-    internal::SetNoPriorityInheritanceFromThreadPool();
-
   // The max number of concurrent BEST_EFFORT tasks is |kMaxBestEffortTasks|,
   // unless the max number of foreground threads is lower.
   const int max_best_effort_tasks =
diff --git a/base/util/memory_pressure/system_memory_pressure_evaluator_chromeos.cc b/base/util/memory_pressure/system_memory_pressure_evaluator_chromeos.cc
index aeb3506..2cc13d2 100644
--- a/base/util/memory_pressure/system_memory_pressure_evaluator_chromeos.cc
+++ b/base/util/memory_pressure/system_memory_pressure_evaluator_chromeos.cc
@@ -28,7 +28,7 @@
 namespace chromeos {
 
 const base::Feature kCrOSUserSpaceLowMemoryNotification{
-    "kCrOSUserSpaceLowMemoryNotification", base::FEATURE_DISABLED_BY_DEFAULT};
+    "CrOSUserSpaceLowMemoryNotification", base::FEATURE_DISABLED_BY_DEFAULT};
 
 namespace {
 // Pointer to the SystemMemoryPressureEvaluator used by TabManagerDelegate for
diff --git a/build/check_gn_headers_whitelist.txt b/build/check_gn_headers_whitelist.txt
index d65e232..fe4406b 100644
--- a/build/check_gn_headers_whitelist.txt
+++ b/build/check_gn_headers_whitelist.txt
@@ -30,6 +30,7 @@
 chrome/browser/chromeos/certificate_provider/sign_requests.h
 chrome/browser/chromeos/certificate_provider/thread_safe_certificate_map.h
 chrome/browser/chromeos/login/signin/oauth2_login_manager.h
+chrome/browser/chromeos/login/signin/oauth2_login_verifier.h
 chrome/browser/chromeos/login/signin/oauth2_token_fetcher.h
 chrome/browser/chromeos/profiles/profile_helper.h
 chrome/browser/chromeos/settings/cros_settings.h
diff --git a/build/config/ios/ios_test_runner_config.gni b/build/config/ios/ios_test_runner_config.gni
new file mode 100644
index 0000000..f28533b
--- /dev/null
+++ b/build/config/ios/ios_test_runner_config.gni
@@ -0,0 +1,10 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+declare_args() {
+  # Controls what version of xcode to use for iOS testers. Note that this
+  # version should be in alignment with the swarming named caches used by tests,
+  # defined under src/testing/buildbot/ to avoid performance issues.
+  test_runner_xcode_build_version = "11c29"
+}
diff --git a/build/config/ios/ios_test_runner_wrapper.gni b/build/config/ios/ios_test_runner_wrapper.gni
new file mode 100644
index 0000000..572818a
--- /dev/null
+++ b/build/config/ios/ios_test_runner_wrapper.gni
@@ -0,0 +1,124 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ios/ios_sdk.gni")
+import("//build/config/ios/ios_test_runner_config.gni")
+import("//build/util/generate_wrapper.gni")
+
+# Invokes generate_wrapper to create an executable script wrapping iOS'
+# run.py with baked in arguments. Only takes effect when test entry in
+# gn_isolate_map.pyl is updated to type="generated_script" with script
+# set to the wrapper output path.
+#
+# Arguments:
+#
+# data
+#   (optional, default [ "//ios/build/bots/scripts/" ]) list of files or
+#   directories required to run target
+#
+# data_deps
+#   (optional) list of target non-linked labels
+#
+# deps
+#   (optional) list of files or directories required to run target
+#
+# executable_args
+#   (optional) a list of string arguments to pass to run.py
+#
+# retries
+#   (optional, default 3) number of retry attempts
+#
+# shards
+#   (optional, default 1) number of shards to execute tests in parallel. not
+#   the same as swamring shards.
+#
+# wrapper_output_name
+#   (optional, default "run_${target_name}") name of the wrapper script
+#
+template("ios_test_runner_wrapper") {
+  generate_wrapper(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "data",
+                             "data_deps",
+                             "deps",
+                             "executable_args",
+                             "retries",
+                             "shards",
+                             "wrapper_output_name",
+                           ])
+    testonly = true
+
+    # iOS main test runner
+    executable = "//ios/build/bots/scripts/run.py"
+
+    # arguments passed to run.py
+    if (!defined(executable_args)) {
+      executable_args = []
+    }
+
+    _rebased_mac_toolchain = rebase_path("//mac_toolchain", root_build_dir)
+    _rebased_xcode_path = rebase_path("//Xcode.app", root_build_dir)
+
+    # --out-dir argument is specified in gn_isolate_map.pyl because
+    # ${ISOLATED_OUTDIR} doesn't get resolved through this wrapper.
+    executable_args += [
+      "--xcode-path",
+      "@WrappedPath(${_rebased_xcode_path})",
+      "--mac-toolchain-cmd",
+      "@WrappedPath(${_rebased_mac_toolchain})",
+    ]
+
+    executable_args += [
+      "--xcode-build-version",
+      test_runner_xcode_build_version,
+    ]
+
+    # Default retries to 3
+    if (!defined(retries)) {
+      retries = 3
+    }
+    executable_args += [
+      "--retries",
+      "${retries}",
+    ]
+
+    # Default shards to 1
+    if (!defined(shards)) {
+      shards = 1
+    }
+    executable_args += [
+      "--shards",
+      "${shards}",
+    ]
+
+    # test runner relies on iossim if use_ios_simulator (defined in ios_sdk.gni)
+    if (use_ios_simulator) {
+      _rebased_root_build_dir = rebase_path("${root_build_dir}", root_build_dir)
+      if (!defined(data_deps)) {
+        data_deps = []
+      }
+      data_deps += [ "//testing/iossim" ]
+
+      executable_args += [
+        "--iossim",
+        "@WrappedPath(${_rebased_root_build_dir}/iossim)",
+      ]
+    }
+
+    # wrapper script output name and path
+    if (!defined(wrapper_output_name)) {
+      _wrapper_output_name = "run_${target_name}"
+    } else {
+      _wrapper_output_name = wrapper_output_name
+    }
+    wrapper_script = "${root_build_dir}/bin/${_wrapper_output_name}"
+
+    # ios/build/bot/scripts/*.py needs to be present for test runner
+    if (!defined(data)) {
+      data = []
+    }
+    data += [ "//ios/build/bots/scripts/" ]
+  }
+}
diff --git a/build/config/ios/rules.gni b/build/config/ios/rules.gni
index a1d62f8d..6661a41 100644
--- a/build/config/ios/rules.gni
+++ b/build/config/ios/rules.gni
@@ -1781,7 +1781,12 @@
     output_name = _xctest_output
     product_type = _ios_xcode_xctest_bundle_id
     host_target = _host_target
-    xcode_test_application_name = _host_output
+
+    # TODO(crbug.com/1056328) The change in output name results in a mismatch
+    # between this value and the ios_app_bundle target name. To mitigate, this
+    # has been modified to _host_target. It can be reverted to _host_output
+    # once the output_name is reverted.
+    xcode_test_application_name = _host_target
 
     deps = [ _xctest_module_target ]
   }
@@ -1971,10 +1976,14 @@
          "xcode_test_application_name must be defined for $target_name")
 
   _xcuitest_target = target_name
+  if (defined(invoker.output_name)) {
+    _xcuitest_target = invoker.output_name
+  }
+
   _xcuitest_runner_target = _xcuitest_target + "_runner"
   _xcuitest_module_target = _xcuitest_target + "_module"
 
-  group(_xcuitest_target) {
+  group(target_name) {
     testonly = true
 
     deps = [ ":$_xcuitest_runner_target" ]
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 1d11899..2b00bbc 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200303.2.1
\ No newline at end of file
+0.20200303.3.1
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 1d11899..2b00bbc 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200303.2.1
\ No newline at end of file
+0.20200303.3.1
\ No newline at end of file
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index 46c3b656..f3c4640 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -183,14 +183,20 @@
   // accumulated within this ScrollBegin() scope is reported in the return
   // value's |accumulated_overscroll| field. Should only be called if
   // ScrollBegin() returned SCROLL_STARTED.
+  //
+  // Is a no-op if no scroller was latched to in ScrollBegin and returns an
+  // empty-initialized InputHandlerScrollResult.
+  //
   // |delayed_by| is the delay from the event that caused the scroll. This is
   // taken into account when determining the duration of the animation if one
   // is created.
   virtual InputHandlerScrollResult ScrollUpdate(ScrollState* scroll_state,
                                                 base::TimeDelta delayed_by) = 0;
 
-  // Stop scrolling the selected layer. Should only be called if ScrollBegin()
-  // returned SCROLL_STARTED. Snap to a snap position if |should_snap| is true.
+  // Stop scrolling the selected layer. Must be called only if ScrollBegin()
+  // returned SCROLL_STARTED. No-op if ScrollBegin wasn't called or didn't
+  // result in a successful scroll latch. Snap to a snap position if
+  // |should_snap| is true.
   virtual void ScrollEnd(bool should_snap) = 0;
 
   virtual InputHandlerPointerResult MouseMoveAt(
diff --git a/cc/metrics/compositor_timing_history.cc b/cc/metrics/compositor_timing_history.cc
index 91e8fc8..05c36c2 100644
--- a/cc/metrics/compositor_timing_history.cc
+++ b/cc/metrics/compositor_timing_history.cc
@@ -54,9 +54,6 @@
       base::TimeDelta duration) = 0;
   virtual void AddDrawIntervalWithCustomPropertyAnimations(
       base::TimeDelta duration) = 0;
-
-  // Synchronization measurements
-  virtual void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) = 0;
 };
 
 namespace {
@@ -384,11 +381,6 @@
     UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Renderer.SwapToAckLatency",
                                         duration);
   }
-
-  void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) override {
-    UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED(
-        "Scheduling.Renderer.MainAndImplFrameTimeDelta", delta);
-  }
 };
 
 class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter {
@@ -462,8 +454,6 @@
     UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Browser.SwapToAckLatency",
                                         duration);
   }
-
-  void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) override {}
 };
 
 class NullUMAReporter : public CompositorTimingHistory::UMAReporter {
@@ -492,7 +482,6 @@
   void AddActivateDuration(base::TimeDelta duration) override {}
   void AddDrawDuration(base::TimeDelta duration) override {}
   void AddSubmitToAckLatency(base::TimeDelta duration) override {}
-  void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) override {}
 };
 
 }  // namespace
@@ -685,13 +674,11 @@
 void CompositorTimingHistory::WillBeginMainFrame(
     const viz::BeginFrameArgs& args) {
   DCHECK_EQ(base::TimeTicks(), begin_main_frame_sent_time_);
-  DCHECK_EQ(base::TimeTicks(), begin_main_frame_frame_time_);
 
   compositor_frame_reporting_controller_->WillBeginMainFrame(args.frame_id);
 
   begin_main_frame_on_critical_path_ = args.on_critical_path;
   begin_main_frame_sent_time_ = Now();
-  begin_main_frame_frame_time_ = args.frame_time;
 
   did_send_begin_main_frame_ = true;
   SetBeginMainFrameNeededContinuously(true);
@@ -709,7 +696,6 @@
   compositor_frame_reporting_controller_->BeginMainFrameAborted(id);
   base::TimeTicks begin_main_frame_end_time = Now();
   DidBeginMainFrame(begin_main_frame_end_time);
-  begin_main_frame_frame_time_ = base::TimeTicks();
 }
 
 void CompositorTimingHistory::NotifyReadyToCommit(
@@ -728,7 +714,6 @@
 }
 
 void CompositorTimingHistory::DidCommit() {
-  DCHECK_EQ(base::TimeTicks(), pending_tree_main_frame_time_);
   DCHECK_EQ(pending_tree_creation_time_, base::TimeTicks());
   DCHECK_NE(commit_start_time_, base::TimeTicks());
 
@@ -741,8 +726,6 @@
 
   pending_tree_is_impl_side_ = false;
   pending_tree_creation_time_ = begin_main_frame_end_time;
-  pending_tree_main_frame_time_ = begin_main_frame_frame_time_;
-  begin_main_frame_frame_time_ = base::TimeTicks();
 }
 
 void CompositorTimingHistory::DidBeginMainFrame(
@@ -883,13 +866,7 @@
   if (enabled_)
     activate_duration_history_.InsertSample(activate_duration);
 
-  // The synchronous compositor doesn't necessarily draw every new active tree.
-  if (!using_synchronous_renderer_compositor_)
-    DCHECK_EQ(base::TimeTicks(), active_tree_main_frame_time_);
-  active_tree_main_frame_time_ = pending_tree_main_frame_time_;
-
   activate_start_time_ = base::TimeTicks();
-  pending_tree_main_frame_time_ = base::TimeTicks();
 }
 
 void CompositorTimingHistory::WillDraw() {
@@ -897,12 +874,7 @@
   draw_start_time_ = Now();
 }
 
-void CompositorTimingHistory::DrawAborted() {
-  active_tree_main_frame_time_ = base::TimeTicks();
-}
-
 void CompositorTimingHistory::DidDraw(bool used_new_active_tree,
-                                      base::TimeTicks impl_frame_time,
                                       size_t composited_animations_count,
                                       size_t main_thread_animations_count,
                                       bool current_frame_had_raf,
@@ -942,17 +914,6 @@
   draw_end_time_prev_ = draw_end_time;
 
   if (used_new_active_tree) {
-    DCHECK_NE(base::TimeTicks(), active_tree_main_frame_time_);
-    base::TimeDelta main_and_impl_delta =
-        impl_frame_time - active_tree_main_frame_time_;
-    DCHECK_GE(main_and_impl_delta, base::TimeDelta());
-    TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"),
-                 "CompositorTimingHistory::DidDraw",
-                 "active_tree_main_frame_time", active_tree_main_frame_time_,
-                 "impl_frame_time", impl_frame_time);
-    uma_reporter_->AddMainAndImplFrameTimeDelta(main_and_impl_delta);
-    active_tree_main_frame_time_ = base::TimeTicks();
-
     bool current_main_frame_had_visual_update =
         main_thread_animations_count > 0 || current_frame_had_raf;
     bool previous_main_frame_had_visual_update =
diff --git a/cc/metrics/compositor_timing_history.h b/cc/metrics/compositor_timing_history.h
index 613f007..55c5525b 100644
--- a/cc/metrics/compositor_timing_history.h
+++ b/cc/metrics/compositor_timing_history.h
@@ -88,10 +88,8 @@
   void ReadyToActivate();
   void WillActivate();
   void DidActivate();
-  void DrawAborted();
   void WillDraw();
   void DidDraw(bool used_new_active_tree,
-               base::TimeTicks impl_frame_time,
                size_t composited_animations_count,
                size_t main_thread_animations_count,
                bool current_frame_had_raf,
@@ -157,16 +155,13 @@
   RollingTimeDeltaHistory draw_duration_history_;
 
   bool begin_main_frame_on_critical_path_;
-  base::TimeTicks begin_main_frame_frame_time_;
   base::TimeTicks begin_main_frame_sent_time_;
   base::TimeTicks begin_main_frame_start_time_;
   base::TimeTicks commit_start_time_;
-  base::TimeTicks pending_tree_main_frame_time_;
   base::TimeTicks pending_tree_creation_time_;
   base::TimeTicks pending_tree_ready_to_activate_time_;
   base::TimeTicks prepare_tiles_start_time_;
   base::TimeTicks activate_start_time_;
-  base::TimeTicks active_tree_main_frame_time_;
   base::TimeTicks draw_start_time_;
   base::TimeTicks submit_start_time_;
 
diff --git a/cc/metrics/compositor_timing_history_unittest.cc b/cc/metrics/compositor_timing_history_unittest.cc
index 7734463c..b5b7133 100644
--- a/cc/metrics/compositor_timing_history_unittest.cc
+++ b/cc/metrics/compositor_timing_history_unittest.cc
@@ -68,7 +68,7 @@
     timing_history_.DidActivate();
     timing_history_.WillDraw();
     AdvanceNowBy(base::TimeDelta::FromMicroseconds(advance_ms));
-    timing_history_.DidDraw(true, Now(), composited_animations_count,
+    timing_history_.DidDraw(true, composited_animations_count,
                             main_thread_animations_count, current_frame_had_raf,
                             next_frame_has_pending_raf, false);
   }
@@ -85,7 +85,7 @@
     timing_history_.DidActivate();
     timing_history_.WillDraw();
     AdvanceNowBy(base::TimeDelta::FromMicroseconds(advance_ms));
-    timing_history_.DidDraw(false, Now(), composited_animations_count,
+    timing_history_.DidDraw(false, composited_animations_count,
                             main_thread_animations_count, false, false,
                             has_custom_property_animation);
   }
@@ -148,7 +148,7 @@
   AdvanceNowBy(one_second);
   timing_history_.WillDraw();
   AdvanceNowBy(draw_duration);
-  timing_history_.DidDraw(true, Now(), 0, 0, false, false, false);
+  timing_history_.DidDraw(true, 0, 0, false, false, false);
 
   EXPECT_EQ(begin_main_frame_queue_duration,
             timing_history_.BeginMainFrameQueueDurationCriticalEstimate());
@@ -200,7 +200,7 @@
   AdvanceNowBy(one_second);
   timing_history_.WillDraw();
   AdvanceNowBy(draw_duration);
-  timing_history_.DidDraw(false, Now(), 0, 0, false, false, false);
+  timing_history_.DidDraw(false, 0, 0, false, false, false);
 
   EXPECT_EQ(base::TimeDelta(),
             timing_history_.BeginMainFrameQueueDurationCriticalEstimate());
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 326d7d92..5dd2d88 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -632,10 +632,8 @@
     bool has_damage =
         client_->WillBeginImplFrame(begin_impl_frame_tracker_.Current());
 
-    if (!has_damage) {
+    if (!has_damage)
       state_machine_.AbortDraw();
-      compositor_timing_history_->DrawAborted();
-    }
   }
 
   ProcessScheduledActions();
@@ -747,7 +745,6 @@
   state_machine_.DidDraw(result);
   compositor_timing_history_->DidDraw(
       drawing_with_new_active_tree,
-      begin_impl_frame_tracker_.DangerousMethodCurrentOrLast().frame_time,
       client_->CompositedAnimationsCount(),
       client_->MainThreadAnimationsCount(), client_->CurrentFrameHadRAF(),
       client_->NextFrameHasPendingRAF(),
@@ -766,7 +763,6 @@
   state_machine_.DidDraw(result);
   compositor_timing_history_->DidDraw(
       drawing_with_new_active_tree,
-      begin_impl_frame_tracker_.DangerousMethodCurrentOrLast().frame_time,
       client_->CompositedAnimationsCount(),
       client_->MainThreadAnimationsCount(), client_->CurrentFrameHadRAF(),
       client_->NextFrameHasPendingRAF(),
@@ -856,7 +852,6 @@
         // No action is actually performed, but this allows the state machine to
         // drain the pipeline without actually drawing.
         state_machine_.AbortDraw();
-        compositor_timing_history_->DrawAborted();
         break;
       }
       case SchedulerStateMachine::Action::BEGIN_LAYER_TREE_FRAME_SINK_CREATION:
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index c270e01..35a18b6 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2161,7 +2161,7 @@
 
   if (GetDrawMode() == DRAW_MODE_RESOURCELESS_SOFTWARE) {
     metadata.is_resourceless_software_draw_with_scroll_or_animation =
-        IsActivelyScrolling() || mutator_host_->NeedsTickAnimations();
+        IsActivelyPrecisionScrolling() || mutator_host_->NeedsTickAnimations();
   }
 
   const base::flat_set<viz::SurfaceRange>& referenced_surfaces =
@@ -2974,7 +2974,7 @@
   return active_tree()->CurrentlyScrollingNode();
 }
 
-bool LayerTreeHostImpl::IsActivelyScrolling() const {
+bool LayerTreeHostImpl::IsActivelyPrecisionScrolling() const {
   if (!CurrentlyScrollingNode())
     return false;
   // On Android WebView root flings are controlled by the application,
@@ -2982,7 +2982,14 @@
   // are actually animating. So assume there are none.
   if (settings_.ignore_root_layer_flings && IsCurrentlyScrollingViewport())
     return false;
-  return true;
+
+  if (!last_scroll_update_state_)
+    return false;
+
+  bool did_scroll_content =
+      did_scroll_x_for_scroll_gesture_ || did_scroll_y_for_scroll_gesture_;
+  return !ShouldAnimateScroll(last_scroll_update_state_.value()) &&
+         did_scroll_content;
 }
 
 void LayerTreeHostImpl::CreatePendingTree() {
@@ -4615,7 +4622,7 @@
   if (!CurrentlyScrollingNode())
     return InputHandlerScrollResult();
 
-  last_scroll_state_ = *scroll_state;
+  last_scroll_update_state_ = *scroll_state;
 
   bool is_delta_percent_units = scroll_state->delta_granularity() ==
                                 ui::ScrollGranularity::kScrollByPercentage;
@@ -4774,13 +4781,13 @@
   SnapContainerData& data = scroll_node->snap_container_data.value();
   gfx::ScrollOffset current_position = GetVisualScrollOffset(*scroll_node);
 
-  DCHECK(last_scroll_state_);
+  DCHECK(last_scroll_update_state_);
   bool imprecise_wheel_scrolling =
       latched_scroll_type_ == InputHandler::WHEEL &&
-      last_scroll_state_->delta_granularity() !=
+      last_scroll_update_state_->delta_granularity() !=
           ui::ScrollGranularity::kScrollByPrecisePixel;
-  gfx::ScrollOffset last_scroll_delta(last_scroll_state_->delta_x(),
-                                      last_scroll_state_->delta_y());
+  gfx::ScrollOffset last_scroll_delta(last_scroll_update_state_->delta_x(),
+                                      last_scroll_update_state_->delta_y());
 
   std::unique_ptr<SnapSelectionStrategy> strategy;
 
@@ -4905,10 +4912,13 @@
   did_scroll_y_for_scroll_gesture_ = false;
   scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
   latched_scroll_type_.reset();
-  last_scroll_state_.reset();
+  last_scroll_update_state_.reset();
 }
 
 void LayerTreeHostImpl::ScrollEnd(bool should_snap) {
+  if (!CurrentlyScrollingNode())
+    return;
+
   // Note that if we deferred the scroll end then we should not snap. We will
   // snap once we deliver the deferred scroll end.
   if (mutator_host_->IsImplOnlyScrollAnimating()) {
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index b9afa750..bfe9817 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -603,7 +603,18 @@
   void QueueSwapPromiseForMainThreadScrollUpdate(
       std::unique_ptr<SwapPromise> swap_promise);
 
-  bool IsActivelyScrolling() const;
+  // Returns true if there is an active scroll in progress.  "Active" here
+  // means that it's been latched (i.e. we have a CurrentlyScrollingNode()) but
+  // also that some ScrollUpdates have been received and their delta consumed
+  // for scrolling. These can differ significantly e.g. the page allows the
+  // touchstart but preventDefaults all the touchmoves. In that case, we latch
+  // and have a CurrentlyScrollingNode() but will never receive a ScrollUpdate.
+  //
+  // "Precision" means it's a non-animated scroll like a touchscreen or
+  // high-precision touchpad. The latter distinction is important for things
+  // like scheduling decisions which might schedule a wheel and a touch
+  // scrolling differently due to user perception.
+  bool IsActivelyPrecisionScrolling() const;
 
   virtual void SetVisible(bool visible);
   bool visible() const { return visible_; }
@@ -1123,9 +1134,9 @@
   // time a CompositorFrame is generated.
   gfx::Vector2dF scroll_accumulated_this_frame_;
 
-  // Tracks the last scroll state received. At the moment, this is used to infer
-  // the most recent scroll type and direction for scroll snapping purposes.
-  base::Optional<ScrollState> last_scroll_state_;
+  // Tracks the last scroll update state received. Used to infer the most
+  // recent scroll type and direction.
+  base::Optional<ScrollState> last_scroll_update_state_;
 
   std::vector<std::unique_ptr<SwapPromise>>
       swap_promises_for_main_thread_scroll_update_;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 3498a93..325d04d 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -1089,6 +1089,49 @@
             status.main_thread_scrolling_reasons);
 }
 
+// Tests that receiving ScrollUpdate and ScrollEnd calls that don't have a
+// matching ScrollBegin are just dropped and are a no-op. This can happen due
+// to pre-commit input deferral which causes some input events to be dropped
+// before the first commit in a renderer has occurred. See the flag
+// kAllowPreCommitInput and how it's used.
+TEST_F(LayerTreeHostImplTest, ScrollUpdateAndEndNoOpWithoutBegin) {
+  SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(1000, 1000));
+
+  // Simulate receiving a gesture mid-stream so that the Begin wasn't ever
+  // processed. This shouldn't cause any state change but we should crash or
+  // DCHECK.
+  {
+    EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
+    host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
+                                         InputHandler::TOUCHSCREEN)
+                                 .get());
+    host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
+                                         InputHandler::TOUCHSCREEN)
+                                 .get());
+    EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
+    host_impl_->ScrollEnd();
+  }
+
+  // Ensure a new gesture is now able to correctly scroll.
+  {
+    InputHandler::ScrollStatus status =
+        host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
+                                           InputHandler::TOUCHSCREEN)
+                                    .get(),
+                                InputHandler::TOUCHSCREEN);
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+    EXPECT_TRUE(host_impl_->CurrentlyScrollingNode());
+
+    host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
+                                         InputHandler::TOUCHSCREEN)
+                                 .get());
+    EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 10),
+                     CurrentScrollOffset(OuterViewportScrollLayer()));
+
+    host_impl_->ScrollEnd();
+  }
+}
+
 // Test that specifying a scroller to ScrollBegin (i.e. avoid hit testing)
 // returns the correct status if the scroller cannot be scrolled on the
 // compositor thread.
@@ -1149,25 +1192,92 @@
   EXPECT_TRUE(did_request_commit_);
 }
 
-TEST_F(LayerTreeHostImplTest, ScrollActiveOnlyAfterScrollMovement) {
-  SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
+// Ensure correct semantics for the IsActivelyPrecisionScrolling method. This
+// method is used to determine scheduler policy so it wants to report true only
+// when real scrolling is occurring (i.e. the compositor is consuming scroll
+// delta, the page isn't handling the events itself). We also only consider
+// this signal for non-animated scrolls. This is partially historical but also
+// makes some sense since touchscreen/high-precision touchpad scrolling has a
+// physical metaphor (movement sticks to finger) so smoothness should be
+// prioritized.
+TEST_F(LayerTreeHostImplTest, ActivelyTouchScrollingOnlyAfterScrollMovement) {
+  SetupViewportLayersOuterScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
   DrawFrame();
 
-  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
-      BeginState(gfx::Point(), gfx::Vector2dF(0, 10), InputHandler::WHEEL)
-          .get(),
-      InputHandler::WHEEL);
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
-  EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
-            status.main_thread_scrolling_reasons);
+  // Ensure a touch scroll reports true but only after some delta has been
+  // consumed.
+  {
+    InputHandler::ScrollStatus status =
+        host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
+                                           InputHandler::TOUCHSCREEN)
+                                    .get(),
+                                InputHandler::TOUCHSCREEN);
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+    EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+              status.main_thread_scrolling_reasons);
+    EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling());
 
-  EXPECT_TRUE(host_impl_->IsActivelyScrolling());
-  host_impl_->ScrollUpdate(
-      UpdateState(gfx::Point(), gfx::Vector2d(0, 10), InputHandler::WHEEL)
-          .get());
-  EXPECT_TRUE(host_impl_->IsActivelyScrolling());
-  host_impl_->ScrollEnd();
-  EXPECT_FALSE(host_impl_->IsActivelyScrolling());
+    // There is no extent upwards so the scroll won't consume any delta.
+    host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -10),
+                                         InputHandler::TOUCHSCREEN)
+                                 .get());
+    EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling());
+
+    // This should scroll so ensure the bit flips to true.
+    host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
+                                         InputHandler::TOUCHSCREEN)
+                                 .get());
+    EXPECT_TRUE(host_impl_->IsActivelyPrecisionScrolling());
+    host_impl_->ScrollEnd();
+    EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling());
+  }
+
+  ASSERT_EQ(10, CurrentScrollOffset(OuterViewportScrollLayer()).y());
+
+  // Ensure an animated wheel scroll doesn't cause the bit to flip even when
+  // scrolling occurs.
+  {
+    InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+        BeginState(gfx::Point(), gfx::Vector2dF(0, 10), InputHandler::WHEEL)
+            .get(),
+        InputHandler::WHEEL);
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+    EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling());
+
+    host_impl_->ScrollUpdate(
+        AnimatedUpdateState(gfx::Point(), gfx::Vector2dF(0, 10)).get());
+    EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling());
+
+    base::TimeTicks cur_time =
+        base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
+    viz::BeginFrameArgs begin_frame_args =
+        viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
+
+#define ANIMATE(time_ms)                                  \
+  cur_time += base::TimeDelta::FromMilliseconds(time_ms); \
+  begin_frame_args.frame_time = (cur_time);               \
+  begin_frame_args.frame_id.sequence_number++;            \
+  host_impl_->WillBeginImplFrame(begin_frame_args);       \
+  host_impl_->Animate();                                  \
+  host_impl_->UpdateAnimationState(true);                 \
+  host_impl_->DidFinishImplFrame(begin_frame_args);
+
+    // The animation is setup in the first frame so tick at least twice to
+    // actually animate it.
+    ANIMATE(0);
+    EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling());
+    ANIMATE(200);
+    EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling());
+    ANIMATE(1000);
+    EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling());
+
+#undef ANIMATE
+
+    ASSERT_EQ(20, CurrentScrollOffset(OuterViewportScrollLayer()).y());
+
+    host_impl_->ScrollEnd();
+    EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling());
+  }
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) {
@@ -1251,7 +1361,6 @@
       UpdateState(gfx::Point(), gfx::Vector2d(0, 10), InputHandler::WHEEL)
           .get());
   EXPECT_TRUE(host_impl_->active_tree()->CurrentlyScrollingNode());
-  EXPECT_TRUE(host_impl_->IsActivelyScrolling());
 
   // Create the pending tree containing only the root layer.
   CreatePendingTree();
@@ -1264,7 +1373,6 @@
 
   // The scroll should stop.
   EXPECT_FALSE(host_impl_->active_tree()->CurrentlyScrollingNode());
-  EXPECT_FALSE(host_impl_->IsActivelyScrolling());
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) {
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 819e7e44..7327156c 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -2378,10 +2378,6 @@
   host_impl_->UpdateImageDecodingHints(std::move(decoding_mode_map));
 }
 
-bool LayerTreeImpl::IsActivelyScrolling() const {
-  return host_impl_->IsActivelyScrolling();
-}
-
 int LayerTreeImpl::GetMSAASampleCountForRaster(
     const scoped_refptr<DisplayItemList>& display_list) {
   return host_impl_->GetMSAASampleCountForRaster(display_list);
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index f7ee651..40cd78c 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -153,7 +153,6 @@
   void UpdateImageDecodingHints(
       base::flat_map<PaintImage::Id, PaintImage::DecodingMode>
           decoding_mode_map);
-  bool IsActivelyScrolling() const;
   int GetMSAASampleCountForRaster(
       const scoped_refptr<DisplayItemList>& display_list);
 
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc
index fa484a8a..ba19dae 100644
--- a/cc/trees/proxy_impl.cc
+++ b/cc/trees/proxy_impl.cc
@@ -407,7 +407,7 @@
   const bool user_interaction_in_progress =
       host_impl_->pinch_gesture_active() ||
       host_impl_->page_scale_animation_active() ||
-      host_impl_->IsActivelyScrolling();
+      host_impl_->IsActivelyPrecisionScrolling();
 
   if (host_impl_->ukm_manager()) {
     host_impl_->ukm_manager()->SetUserInteractionInProgress(
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index d724f040..4b74fda 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -77,6 +77,7 @@
   "junit/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencerTest.java",
   "junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java",
   "junit/src/org/chromium/chrome/browser/firstrun/ToSAckedReceiverTest.java",
+  "junit/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsAnnotationUnitTest.java",
   "junit/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsUnitTest.java",
   "junit/src/org/chromium/chrome/browser/fullscreen/BrowserStateBrowserControlsVisibilityDelegateTest.java",
   "junit/src/org/chromium/chrome/browser/gcore/GoogleApiClientHelperTest.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
index 03d0d0de..ae4444a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
@@ -44,8 +44,9 @@
     private final Context mContext;
     private final PropertyModel mModel;
     private final ThemeColorProvider mThemeColorProvider;
-    private final PropertyModelChangeProcessor mModelChangeProcessor;
+    private final TabGroupUiToolbarView mToolbarView;
     private final ViewGroup mTabListContainerView;
+    private PropertyModelChangeProcessor mModelChangeProcessor;
     private TabGridDialogCoordinator mTabGridDialogCoordinator;
     private TabListCoordinator mTabStripCoordinator;
     private TabGroupUiMediator mMediator;
@@ -59,13 +60,10 @@
         mContext = parentView.getContext();
         mThemeColorProvider = themeColorProvider;
         mModel = new PropertyModel(TabGroupUiProperties.ALL_KEYS);
-        TabGroupUiToolbarView toolbarView =
-                (TabGroupUiToolbarView) LayoutInflater.from(mContext).inflate(
-                        R.layout.bottom_tab_strip_toolbar, parentView, false);
-        mTabListContainerView = toolbarView.getViewContainer();
-        parentView.addView(toolbarView);
-        mModelChangeProcessor = PropertyModelChangeProcessor.create(
-                mModel, toolbarView, TabGroupUiViewBinder::bind);
+        mToolbarView = (TabGroupUiToolbarView) LayoutInflater.from(mContext).inflate(
+                R.layout.bottom_tab_strip_toolbar, parentView, false);
+        mTabListContainerView = mToolbarView.getViewContainer();
+        parentView.addView(mToolbarView);
     }
 
     /**
@@ -89,6 +87,11 @@
                         tabModelSelector, null, null, false, null, null, TabProperties.UiType.STRIP,
                         null, mTabListContainerView, null, true, COMPONENT_NAME);
 
+        mModelChangeProcessor = PropertyModelChangeProcessor.create(mModel,
+                new TabGroupUiViewBinder.ViewHolder(
+                        mToolbarView, mTabStripCoordinator.getContainerView()),
+                TabGroupUiViewBinder::bind);
+
         // TODO(crbug.com/972217): find a way to enable interactions between grid tab switcher
         //  and the dialog here.
         mTabGridDialogCoordinator = new TabGridDialogCoordinator(mContext, tabModelSelector,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
index dd9eb618..557d8ae 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import android.os.Handler;
 import android.view.View;
 
 import androidx.annotation.Nullable;
@@ -137,6 +138,10 @@
 
             @Override
             public void didAddTab(Tab tab, int type, @TabCreationState int creationState) {
+                if (type == TabLaunchType.FROM_CHROME_UI && mIsTabGroupUiVisible) {
+                    mModel.set(TabGroupUiProperties.INITIAL_SCROLL_INDEX,
+                            getRelatedTabsForId(tab.getId()).size() - 1);
+                }
                 if (type == TabLaunchType.FROM_CHROME_UI || type == TabLaunchType.FROM_RESTORE
                         || type == TabLaunchType.FROM_STARTUP) {
                     return;
@@ -283,6 +288,15 @@
                 && BottomToolbarConfiguration.isBottomToolbarEnabled();
         assert (mVisibilityController == null) == isDuetTabStripIntegrationEnabled;
         if (isDuetTabStripIntegrationEnabled) return;
+        if (mIsTabGroupUiVisible) {
+            // Post to make sure that the recyclerView already knows how many visible items it has.
+            // This is to make sure that we can scroll to a state where the selected tab is in the
+            // middle of the strip.
+            Handler handler = new Handler();
+            handler.post(()
+                                 -> mModel.set(TabGroupUiProperties.INITIAL_SCROLL_INDEX,
+                                         listOfTabs.indexOf(mTabModelSelector.getCurrentTab())));
+        }
         mVisibilityController.setBottomControlsVisible(mIsTabGroupUiVisible);
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java
index 09431441..c22a7b5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java
@@ -28,8 +28,14 @@
             new PropertyModel.WritableObjectPropertyKey<>();
     public static final PropertyModel.WritableIntPropertyKey LEFT_BUTTON_DRAWABLE_ID =
             new PropertyModel.WritableIntPropertyKey();
+    /**
+     * Integer, but not {@link PropertyModel.WritableIntPropertyKey} so that we can force update on
+     * the same value.
+     */
+    public static final PropertyModel.WritableObjectPropertyKey INITIAL_SCROLL_INDEX =
+            new PropertyModel.WritableObjectPropertyKey(true);
 
-    public static final PropertyKey[] ALL_KEYS =
-            new PropertyKey[] {LEFT_BUTTON_ON_CLICK_LISTENER, RIGHT_BUTTON_ON_CLICK_LISTENER,
-                    IS_MAIN_CONTENT_VISIBLE, PRIMARY_COLOR, TINT, LEFT_BUTTON_DRAWABLE_ID};
+    public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {LEFT_BUTTON_ON_CLICK_LISTENER,
+            RIGHT_BUTTON_ON_CLICK_LISTENER, IS_MAIN_CONTENT_VISIBLE, PRIMARY_COLOR, TINT,
+            LEFT_BUTTON_DRAWABLE_ID, INITIAL_SCROLL_INDEX};
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java
index 89ad8a8..4045754 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.INITIAL_SCROLL_INDEX;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.IS_MAIN_CONTENT_VISIBLE;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.LEFT_BUTTON_DRAWABLE_ID;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.LEFT_BUTTON_ON_CLICK_LISTENER;
@@ -11,6 +12,10 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.RIGHT_BUTTON_ON_CLICK_LISTENER;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.TINT;
 
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -19,26 +24,47 @@
  */
 class TabGroupUiViewBinder {
     /**
+     * ViewHolder class to get access to all {@link View}s inside the TabGroupUi.
+     */
+    public static class ViewHolder {
+        public final TabGroupUiToolbarView toolbarView;
+        public final RecyclerView contentView;
+
+        ViewHolder(TabGroupUiToolbarView toolbarView, RecyclerView contentView) {
+            this.toolbarView = toolbarView;
+            this.contentView = contentView;
+        }
+    }
+    /**
      * Binds the given model to the given view, updating the payload in propertyKey.
      *
-     * @param model       The model to use.
-     * @param view        The view to use.
-     * @param propertyKey The key for the property to update for.
+     * @param model             The model to use.
+     * @param viewHolder        The {@link ViewHolder} to use.
+     * @param propertyKey       The key for the property to update for.
      */
-    public static void bind(
-            PropertyModel model, TabGroupUiToolbarView view, PropertyKey propertyKey) {
+    public static void bind(PropertyModel model, ViewHolder viewHolder, PropertyKey propertyKey) {
         if (LEFT_BUTTON_ON_CLICK_LISTENER == propertyKey) {
-            view.setLeftButtonOnClickListener(model.get(LEFT_BUTTON_ON_CLICK_LISTENER));
+            viewHolder.toolbarView.setLeftButtonOnClickListener(
+                    model.get(LEFT_BUTTON_ON_CLICK_LISTENER));
         } else if (RIGHT_BUTTON_ON_CLICK_LISTENER == propertyKey) {
-            view.setRightButtonOnClickListener(model.get(RIGHT_BUTTON_ON_CLICK_LISTENER));
+            viewHolder.toolbarView.setRightButtonOnClickListener(
+                    model.get(RIGHT_BUTTON_ON_CLICK_LISTENER));
         } else if (IS_MAIN_CONTENT_VISIBLE == propertyKey) {
-            view.setMainContentVisibility(model.get(IS_MAIN_CONTENT_VISIBLE));
+            viewHolder.toolbarView.setMainContentVisibility(model.get(IS_MAIN_CONTENT_VISIBLE));
         } else if (PRIMARY_COLOR == propertyKey) {
-            view.setPrimaryColor(model.get(PRIMARY_COLOR));
+            viewHolder.toolbarView.setPrimaryColor(model.get(PRIMARY_COLOR));
         } else if (TINT == propertyKey) {
-            view.setTint(model.get(TINT));
+            viewHolder.toolbarView.setTint(model.get(TINT));
         } else if (LEFT_BUTTON_DRAWABLE_ID == propertyKey) {
-            view.setLeftButtonDrawableId(model.get(LEFT_BUTTON_DRAWABLE_ID));
+            viewHolder.toolbarView.setLeftButtonDrawableId(model.get(LEFT_BUTTON_DRAWABLE_ID));
+        } else if (INITIAL_SCROLL_INDEX == propertyKey) {
+            int index = (Integer) model.get(INITIAL_SCROLL_INDEX);
+            LinearLayoutManager manager =
+                    (LinearLayoutManager) viewHolder.contentView.getLayoutManager();
+            int showingItemsCount =
+                    manager.findLastVisibleItemPosition() - manager.findFirstVisibleItemPosition();
+            // Try to scroll to a state where the selected tab is in the middle of the strip.
+            manager.scrollToPositionWithOffset(index - showingItemsCount / 2, 0);
         }
     }
 }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java
new file mode 100644
index 0000000..e96ce94
--- /dev/null
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java
@@ -0,0 +1,152 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withParent;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.junit.Assert.assertTrue;
+
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstCardFromTabSwitcher;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickNthTabInDialog;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabs;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.enterTabSwitcher;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.mergeAllNormalTabsToAGroup;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyTabSwitcherCardCount;
+
+import android.support.test.filters.MediumTest;
+import android.support.v7.widget.RecyclerView;
+import android.view.ViewGroup;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.compositor.layouts.Layout;
+import org.chromium.chrome.browser.flags.CachedFeatureFlags;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.features.start_surface.StartSurfaceLayout;
+import org.chromium.chrome.tab_ui.R;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.ChromeRenderTestRule;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.ui.test.util.UiRestriction;
+
+import java.io.IOException;
+
+/** End-to-end tests for TabGroupUi component. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+// clang-format off
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
+public class TabGroupUiTest {
+    // clang-format on
+
+    @Rule
+    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+
+    @Rule
+    public TestRule mProcessor = new Features.InstrumentationProcessor();
+
+    @Rule
+    public ChromeRenderTestRule mRenderTestRule = new ChromeRenderTestRule();
+
+    @Before
+    public void setUp() {
+        CachedFeatureFlags.setForTesting(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, true);
+        CachedFeatureFlags.setForTesting(ChromeFeatureList.TAB_GROUPS_ANDROID, true);
+        mActivityTestRule.startMainActivityFromLauncher();
+        Layout layout = mActivityTestRule.getActivity().getLayoutManager().getOverviewLayout();
+        assertTrue(layout instanceof StartSurfaceLayout);
+        CriteriaHelper.pollUiThread(mActivityTestRule.getActivity()
+                                            .getTabModelSelector()
+                                            .getTabModelFilterProvider()
+                                            .getCurrentTabModelFilter()::isTabModelRestored);
+    }
+
+    @After
+    public void tearDown() {
+        CachedFeatureFlags.setForTesting(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, null);
+        CachedFeatureFlags.setForTesting(ChromeFeatureList.TAB_GROUPS_ANDROID, null);
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    public void testRenderStrip_Select5thTabIn10Tabs() throws InterruptedException, IOException {
+        final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
+        createTabs(cta, false, 10);
+        enterTabSwitcher(cta);
+        verifyTabSwitcherCardCount(cta, 10);
+        mergeAllNormalTabsToAGroup(cta);
+        verifyTabSwitcherCardCount(cta, 1);
+
+        // Select the 5th tab in group.
+        clickFirstCardFromTabSwitcher(cta);
+        clickNthTabInDialog(cta, 4);
+
+        ViewGroup bottomToolbar = cta.findViewById(R.id.bottom_controls);
+        RecyclerView stripRecyclerView =
+                (RecyclerView) bottomToolbar.findViewById(R.id.tab_list_view);
+        mRenderTestRule.render(stripRecyclerView, "5th_tab_selected");
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    public void testRenderStrip_Select10thTabIn10Tabs() throws InterruptedException, IOException {
+        final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
+        createTabs(cta, false, 10);
+        enterTabSwitcher(cta);
+        verifyTabSwitcherCardCount(cta, 10);
+        mergeAllNormalTabsToAGroup(cta);
+        verifyTabSwitcherCardCount(cta, 1);
+
+        // Select the 10th tab in group.
+        clickFirstCardFromTabSwitcher(cta);
+        clickNthTabInDialog(cta, 9);
+
+        ViewGroup bottomToolbar = cta.findViewById(R.id.bottom_controls);
+        RecyclerView stripRecyclerView =
+                (RecyclerView) bottomToolbar.findViewById(R.id.tab_list_view);
+        mRenderTestRule.render(stripRecyclerView, "10th_tab_selected");
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    public void testRenderStrip_AddTab() throws InterruptedException, IOException {
+        final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
+        createTabs(cta, false, 10);
+        enterTabSwitcher(cta);
+        verifyTabSwitcherCardCount(cta, 10);
+        mergeAllNormalTabsToAGroup(cta);
+        verifyTabSwitcherCardCount(cta, 1);
+
+        // Select the first tab in group and add one new tab to group.
+        clickFirstCardFromTabSwitcher(cta);
+        clickNthTabInDialog(cta, 0);
+        ViewGroup bottomToolbar = cta.findViewById(R.id.bottom_controls);
+        RecyclerView stripRecyclerView =
+                (RecyclerView) bottomToolbar.findViewById(R.id.tab_list_view);
+        stripRecyclerView.setItemAnimator(null);
+        onView(allOf(withId(R.id.toolbar_right_button), withParent(withId(R.id.main_content))))
+                .perform(click());
+        mRenderTestRule.render(stripRecyclerView, "11th_tab_selected");
+    }
+}
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinderTest.java
index 3dd7e31..24abba5 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinderTest.java
@@ -16,6 +16,8 @@
 import android.os.Build;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -61,9 +63,16 @@
         mRightButton = toolbarView.findViewById(R.id.toolbar_right_button);
         mContainerView = toolbarView.findViewById(R.id.toolbar_container_view);
         mMainContent = toolbarView.findViewById(R.id.main_content);
+        RecyclerView recyclerView =
+                (TabListRecyclerView) LayoutInflater.from(getActivity())
+                        .inflate(R.layout.tab_list_recycler_view_layout, parentView, false);
+        recyclerView.setLayoutManager(
+                new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false));
 
         mModel = new PropertyModel(TabGroupUiProperties.ALL_KEYS);
-        mMCP = PropertyModelChangeProcessor.create(mModel, toolbarView, TabGroupUiViewBinder::bind);
+        mMCP = PropertyModelChangeProcessor.create(mModel,
+                new TabGroupUiViewBinder.ViewHolder(toolbarView, recyclerView),
+                TabGroupUiViewBinder::bind);
     }
 
     @Override
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java
index f9ee363..041cdcfc 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java
@@ -405,6 +405,26 @@
     }
 
     @Test
+    public void tabSelection_ScrollToSelectedIndex() {
+        initAndAssertProperties(mTab1);
+        assertThat(mModel.get(TabGroupUiProperties.INITIAL_SCROLL_INDEX), equalTo(null));
+
+        // Mock that {tab2, tab3} are in the same tab group.
+        List<Tab> tabGroup = mTabGroupModelFilter.getRelatedTabList(TAB2_ID);
+        assertThat(tabGroup.size(), equalTo(2));
+
+        // Mock selecting tab 3, and the last selected tab is tab 1 which is a single tab.
+        doReturn(mTab3).when(mTabModelSelector).getCurrentTab();
+        mTabModelObserverArgumentCaptor.getValue().didSelectTab(
+                mTab3, TabSelectionType.FROM_USER, TAB1_ID);
+
+        // Strip should be showing since we are selecting a group, and it should scroll to the index
+        // of currently selected tab.
+        verifyResetStrip(true, tabGroup);
+        assertThat(mModel.get(TabGroupUiProperties.INITIAL_SCROLL_INDEX), equalTo(1));
+    }
+
+    @Test
     public void tabClosure_NotLastTabInGroup() {
         initAndAssertProperties(mTab2);
 
@@ -501,6 +521,24 @@
     }
 
     @Test
+    public void tabAddition_TabGroup_ScrollToTheLast() {
+        initAndAssertProperties(mTab2);
+        assertThat(mModel.get(TabGroupUiProperties.INITIAL_SCROLL_INDEX), equalTo(0));
+
+        TabImpl newTab = prepareTab(TAB4_ID, TAB4_ID);
+        mTabGroup2.add(newTab);
+        doReturn(mTabGroup2).when(mTabGroupModelFilter).getRelatedTabList(TAB4_ID);
+
+        mTabModelObserverArgumentCaptor.getValue().didAddTab(
+                newTab, TabLaunchType.FROM_CHROME_UI, TabCreationState.LIVE_IN_FOREGROUND);
+
+        // Strip should be not be reset through adding tab from UI.
+        verifyNeverReset();
+        assertThat(mTabGroupModelFilter.getRelatedTabList(TAB4_ID).size(), equalTo(3));
+        assertThat(mModel.get(TabGroupUiProperties.INITIAL_SCROLL_INDEX), equalTo(2));
+    }
+
+    @Test
     public void restoreCompleted_TabModelNoTab() {
         // Simulate no tab in current TabModel.
         initAndAssertProperties(null);
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index cda20f8..dd29ddf 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -40,6 +40,7 @@
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinderTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupPopupUiTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupPopupUiViewBinderTest.java",
+  "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinderTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index ca42c3c..8e54ae60 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -229,7 +229,7 @@
 
     private boolean mPartnerBrowserRefreshNeeded;
 
-    protected IntentHandler mIntentHandler;
+    protected final IntentHandler mIntentHandler;
 
     /** Set if {@link #postDeferredStartupIfNeeded()} is called before native has loaded. */
     private boolean mDeferredStartupQueued;
@@ -312,6 +312,10 @@
     /** Controls tab reparenting for night mode. */
     NightModeReparentingController mNightModeReparentingController;
 
+    protected ChromeActivity() {
+        mIntentHandler = new IntentHandler(this, createIntentHandlerDelegate());
+    }
+
     @Override
     protected ActivityWindowAndroid createWindowAndroid() {
         return new ChromeWindow(this);
@@ -633,8 +637,8 @@
      */
     protected StartupTabPreloader getStartupTabPreloader() {
         if (mStartupTabPreloader == null) {
-            mStartupTabPreloader = new StartupTabPreloader(
-                    this::getIntent, getLifecycleDispatcher(), getWindowAndroid(), this);
+            mStartupTabPreloader = new StartupTabPreloader(this::getIntent,
+                    getLifecycleDispatcher(), getWindowAndroid(), this, mIntentHandler);
         }
         return mStartupTabPreloader;
     }
@@ -731,7 +735,6 @@
 
         IntentHandler.setTestIntentsEnabled(
                 CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_TEST_INTENTS));
-        mIntentHandler = new IntentHandler(createIntentHandlerDelegate(), getPackageName());
     }
 
     @Override
@@ -962,7 +965,7 @@
         }
 
         super.onNewIntentWithNative(intent);
-        if (IntentHandler.shouldIgnoreIntent(intent)) return;
+        if (mIntentHandler.shouldIgnoreIntent(intent)) return;
 
         // We send this intent so that we can enter WebVr presentation mode if needed. This
         // call doesn't consume the intent because it also has the url that we need to load.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 17fbe02..62ebd2d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1082,7 +1082,7 @@
                     this.getLifecycleDispatcher());
             mIntentWithEffect = false;
             if (getSavedInstanceState() == null && intent != null) {
-                if (!IntentHandler.shouldIgnoreIntent(intent)) {
+                if (!mIntentHandler.shouldIgnoreIntent(intent)) {
                     mIntentWithEffect = mIntentHandler.onNewIntent(intent);
                 }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandler.java
index ca81c107..2076ef60 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandler.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser;
 
+import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -12,6 +13,8 @@
 
 import org.chromium.base.ContextUtils;
 
+import java.lang.ref.WeakReference;
+
 /**
  * Intent handler that is specific for the situation when the screen is unlocked from pin, pattern,
  * or password.
@@ -21,15 +24,17 @@
 public class DelayedScreenLockIntentHandler extends BroadcastReceiver {
     private static final int VALID_DEFERRED_PERIOD_MS = 10000;
 
-    private final Handler mTaskHandler;
-    private final Runnable mUnregisterTask;
-
+    private final Handler mTaskHandler = new Handler();
+    private final Runnable mUnregisterTask = () -> updateDeferredIntent(null);
+    // Must be an Activity context for deferred intents without FLAG_NEW_TASK on Android P+.
+    // http://crbug.com/1034440
+    private final WeakReference<Activity> mActivity;
     private Intent mDeferredIntent;
-    private boolean mReceiverRegistered;
+    private boolean mEnabled;
 
-    public DelayedScreenLockIntentHandler() {
-        mTaskHandler = new Handler();
-        mUnregisterTask = () -> updateDeferredIntent(null);
+    public DelayedScreenLockIntentHandler(Activity activity) {
+        // Use a WeakReference so that Activity is not retained by call to postDelayed.
+        mActivity = new WeakReference<>(activity);
     }
 
     @Override
@@ -37,7 +42,10 @@
         assert Intent.ACTION_USER_PRESENT.equals(intent.getAction());
 
         if (Intent.ACTION_USER_PRESENT.equals(intent.getAction()) && mDeferredIntent != null) {
-            context.startActivity(mDeferredIntent);
+            Activity activity = mActivity.get();
+            if (activity != null) {
+                activity.startActivity(mDeferredIntent);
+            }
             // Prevent the broadcast receiver from firing intent unexpectedly.
             updateDeferredIntent(null);
         }
@@ -47,30 +55,12 @@
      * Update the deferred intent with the target intent, also reset the deferred intent's lifecycle
      * @param intent Target intent
      */
-    public void updateDeferredIntent(final Intent intent) {
+    public void updateDeferredIntent(Intent intent) {
         mTaskHandler.removeCallbacks(mUnregisterTask);
-
-        if (intent == null) {
-            unregisterReceiver();
-            mDeferredIntent = null;
-            return;
-        }
-
-        mDeferredIntent = intent;
-        registerReceiver();
         mTaskHandler.postDelayed(mUnregisterTask, VALID_DEFERRED_PERIOD_MS);
-    }
+        mDeferredIntent = intent;
 
-    /**
-     * Register to receive ACTION_USER_PRESENT when the screen is unlocked.
-     * The ACTION_USER_PRESENT is sent by platform to indicates when user is present.
-     */
-    private void registerReceiver() {
-        if (mReceiverRegistered) return;
-
-        ContextUtils.getApplicationContext()
-                .registerReceiver(this, new IntentFilter(Intent.ACTION_USER_PRESENT));
-        mReceiverRegistered = true;
+        setEnabled(intent != null);
     }
 
     /**
@@ -78,11 +68,17 @@
      * - When the deferred intent expires
      * - When updateDeferredIntent(null) called
      * - When the deferred intent has been fired
+     * Register to receive ACTION_USER_PRESENT when the screen is unlocked.
+     * The ACTION_USER_PRESENT is sent by platform to indicates when user is present.
      */
-    private void unregisterReceiver() {
-        if (!mReceiverRegistered) return;
-
-        ContextUtils.getApplicationContext().unregisterReceiver(this);
-        mReceiverRegistered = false;
+    private void setEnabled(boolean value) {
+        if (value == mEnabled) return;
+        mEnabled = value;
+        Context applicationContext = ContextUtils.getApplicationContext();
+        if (value) {
+            applicationContext.registerReceiver(this, new IntentFilter(Intent.ACTION_USER_PRESENT));
+        } else {
+            applicationContext.unregisterReceiver(this);
+        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
index f4fc976..e76b2d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -274,7 +274,7 @@
     private static boolean sTestIntentsEnabled;
 
     private final IntentHandlerDelegate mDelegate;
-    private final String mPackageName;
+    private final Activity mActivity;
 
     /**
      * Receiver for screen unlock broadcast.
@@ -329,9 +329,9 @@
         sTestIntentsEnabled = enabled;
     }
 
-    public IntentHandler(IntentHandlerDelegate delegate, String packageName) {
+    public IntentHandler(Activity activity, IntentHandlerDelegate delegate) {
         mDelegate = delegate;
-        mPackageName = packageName;
+        mActivity = activity;
     }
 
     /**
@@ -420,9 +420,9 @@
         }
     }
 
-    private static void updateDeferredIntent(Intent intent) {
+    private void updateDeferredIntent(Intent intent) {
         if (sDelayedScreenIntentHandler == null && intent != null) {
-            sDelayedScreenIntentHandler = new DelayedScreenLockIntentHandler();
+            sDelayedScreenIntentHandler = new DelayedScreenLockIntentHandler(mActivity);
         }
 
         if (sDelayedScreenIntentHandler != null) {
@@ -850,7 +850,7 @@
      * @param intent Intent to check.
      * @return true if the intent should be ignored.
      */
-    public static boolean shouldIgnoreIntent(Intent intent) {
+    public boolean shouldIgnoreIntent(Intent intent) {
         // Although not documented to, many/most methods that retrieve values from an Intent may
         // throw. Because we can't control what packages might send to us, we should catch any
         // Throwable and then fail closed (safe). This is ugly, but resolves top crashers in the
@@ -1060,8 +1060,9 @@
 
         // Intents from chrome open in the same tab by default, all others only clobber
         // tabs created by the same app.
-        return mPackageName.equals(appId) ? TabOpenType.CLOBBER_CURRENT_TAB
-                                          : TabOpenType.REUSE_APP_ID_MATCHING_TAB_ELSE_NEW_TAB;
+        return mActivity.getPackageName().equals(appId)
+                ? TabOpenType.CLOBBER_CURRENT_TAB
+                : TabOpenType.REUSE_APP_ID_MATCHING_TAB_ELSE_NEW_TAB;
     }
 
     private static boolean isInvalidScheme(String scheme) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index 1326f2f..ed24838e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -161,7 +161,7 @@
                 mIntent.getBooleanExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, false);
 
         // Check if a web search Intent is being handled.
-        IntentHandler intentHandler = new IntentHandler(this, mActivity.getPackageName());
+        IntentHandler intentHandler = new IntentHandler(mActivity, this);
         String url = IntentHandler.getUrlFromIntent(mIntent);
         if (url == null && tabId == Tab.INVALID_TAB_ID && !incognito
                 && intentHandler.handleWebSearchIntent(mIntent)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index da90935..0eb28b1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -358,7 +358,7 @@
             ChromeActivityCommonsModule commonsModule) {
         // mIntentHandler comes from the base class.
         IntentIgnoringCriterion intentIgnoringCriterion =
-                (intent) -> IntentHandler.shouldIgnoreIntent(intent);
+                (intent) -> mIntentHandler.shouldIgnoreIntent(intent);
 
         CustomTabActivityModule customTabsModule = new CustomTabActivityModule(mIntentDataProvider,
                 mNightModeStateController, intentIgnoringCriterion, getStartupTabPreloader());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index b6c2bf74..838b72e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -65,6 +65,8 @@
             put(ChromeFeatureList.TAB_GROUPS_ANDROID, false);
             put(ChromeFeatureList.DUET_TABSTRIP_INTEGRATION_ANDROID, false);
             put(ChromeFeatureList.SHARE_BUTTON_IN_TOP_TOOLBAR, false);
+            put(ChromeFeatureList.TEST_DEFAULT_DISABLED, false);
+            put(ChromeFeatureList.TEST_DEFAULT_ENABLED, true);
         }
     };
 
@@ -182,6 +184,24 @@
     }
 
     /**
+     * Sets the feature flags to use in JUnit and instrumentation tests.
+     */
+    @VisibleForTesting
+    public static void setFeaturesForTesting(Map<String, Boolean> features) {
+        assert features != null;
+
+        for (Map.Entry<String, Boolean> entry : features.entrySet()) {
+            String key = entry.getKey();
+
+            if (!sDefaults.containsKey(key)) {
+                continue;
+            }
+
+            setForTesting(key, entry.getValue());
+        }
+    }
+
+    /**
      * Caches flags that must take effect on startup but are set via native code.
      */
     public static void cacheNativeFlags(List<String> featuresToCache) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java b/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
index 09c725b..97c9311 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
@@ -45,17 +45,19 @@
     private final ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
     private final WindowAndroid mWindowAndroid;
     private final TabCreatorManager mTabCreatorManager;
+    private final IntentHandler mIntentHandler;
     private LoadUrlParams mLoadUrlParams;
     private Tab mTab;
     private StartupTabObserver mObserver;
 
     public StartupTabPreloader(Supplier<Intent> intentSupplier,
             ActivityLifecycleDispatcher activityLifecycleDispatcher, WindowAndroid windowAndroid,
-            TabCreatorManager tabCreatorManager) {
+            TabCreatorManager tabCreatorManager, IntentHandler intentHandler) {
         mIntentSupplier = intentSupplier;
         mActivityLifecycleDispatcher = activityLifecycleDispatcher;
         mWindowAndroid = windowAndroid;
         mTabCreatorManager = tabCreatorManager;
+        mIntentHandler = intentHandler;
 
         mActivityLifecycleDispatcher.register(this);
         ProfileManager.addObserver(this);
@@ -151,7 +153,7 @@
         if (mTab != null) return false;
 
         Intent intent = mIntentSupplier.get();
-        if (IntentHandler.shouldIgnoreIntent(intent)) return false;
+        if (mIntentHandler.shouldIgnoreIntent(intent)) return false;
         if (getUrlFromIntent(intent) == null) return false;
 
         // We don't support incognito tabs because only chrome can send new incognito tab
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactoryParams.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactoryParams.java
index 20fe98c..27ebd78 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactoryParams.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactoryParams.java
@@ -86,4 +86,9 @@
     default PaymentRequestUpdateEventCallback getPaymentRequestUpdateEventCallback() {
         return null;
     }
+
+    /** @return The currency of the total amount. Should not be null. */
+    default String getTotalAmountCurrency() {
+        return null;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index 32b3811..98ac88b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -2561,6 +2561,12 @@
         return this;
     }
 
+    // PaymentAppFactoryParams implementation.
+    @Override
+    public String getTotalAmountCurrency() {
+        return mRawTotal.amount.currency;
+    }
+
     // PaymentAppFactoryDelegate implementation.
     @Override
     public PaymentAppFactoryParams getParams() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
index 8d91a00..d6ca9fe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
@@ -162,10 +162,16 @@
                 }
             }
 
-            fireCanMakePaymentEvent(webContents, registrationId, scope,
-                    mDelegate.getParams().getId(), mDelegate.getParams().getTopLevelOrigin(),
-                    mDelegate.getParams().getPaymentRequestOrigin(), supportedRequestedMethodData,
-                    supportedRequestedModifiers, /*callback=*/this, app);
+            ServiceWorkerPaymentAppBridgeJni.get().fireCanMakePaymentEvent(webContents,
+                    registrationId, scope, mDelegate.getParams().getId(),
+                    mDelegate.getParams().getTopLevelOrigin(),
+                    mDelegate.getParams().getPaymentRequestOrigin(),
+                    supportedRequestedMethodData.toArray(new PaymentMethodData[0]),
+                    supportedRequestedModifiers.toArray(new PaymentDetailsModifier[0]),
+                    ChromeFeatureList.isEnabled(ChromeFeatureList.WEB_PAYMENTS_MINIMAL_UI)
+                            ? mDelegate.getParams().getTotalAmountCurrency()
+                            : null,
+                    /*callback=*/this, app);
         }
 
         /** Called when an installable payment handler is found. */
@@ -285,34 +291,6 @@
     }
 
     /**
-     * Returns whether the app can make a payment.
-     *
-     * @param webContents      The web contents that invoked PaymentRequest.
-     * @param registrationId   The service worker registration ID of the Payment App.
-     * @param swScope          The service worker scope.
-     * @param paymentRequestId The payment request identifier.
-     * @param origin           The origin of this merchant.
-     * @param iframeOrigin     The origin of the iframe that invoked PaymentRequest. Same as origin
-     *                         if PaymentRequest was not invoked from inside an iframe.
-     * @param methodData       The PaymentMethodData objects that are relevant for this payment
-     *                         app.
-     * @param modifiers        Payment method specific modifiers to the payment items and the total.
-     * @param callback         Called after the payment app is finished running.
-     * @param app              The payment handler where the event is being fired.
-     */
-    private static void fireCanMakePaymentEvent(WebContents webContents, long registrationId,
-            String swScope, String paymentRequestId, String origin, String iframeOrigin,
-            Set<PaymentMethodData> methodData, Set<PaymentDetailsModifier> modifiers,
-            PaymentHandlerFinder callback, ServiceWorkerPaymentApp app) {
-        ThreadUtils.assertOnUiThread();
-
-        ServiceWorkerPaymentAppBridgeJni.get().fireCanMakePaymentEvent(webContents, registrationId,
-                swScope, paymentRequestId, origin, iframeOrigin,
-                methodData.toArray(new PaymentMethodData[0]),
-                modifiers.toArray(new PaymentDetailsModifier[0]), callback, app);
-    }
-
-    /**
      * Make canMakePayment() return true always for testing purpose.
      *
      * @param canMakePayment Indicates whether a SW payment app can make payment.
@@ -692,7 +670,7 @@
         void fireCanMakePaymentEvent(WebContents webContents, long registrationId,
                 String serviceWorkerScope, String paymentRequestId, String topOrigin,
                 String paymentRequestOrigin, PaymentMethodData[] methodData,
-                PaymentDetailsModifier[] modifiers, PaymentHandlerFinder callback,
+                PaymentDetailsModifier[] modifiers, String currency, PaymentHandlerFinder callback,
                 ServiceWorkerPaymentApp app);
         void onClosingPaymentAppWindow(WebContents webContents, int reason);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
index e5788bb5..596bb778 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
@@ -182,8 +182,12 @@
                 new SpanInfo("<learnmore>", "</learnmore>", new ClickableSpan() {
                     @Override
                     public void onClick(View view) {
-                        HelpAndFeedback.getInstance().show(
-                                getActivity(), helpContext, Profile.getLastUsedProfile(), null);
+                        // TODO(https://crbug.com/1048632): It works correctly now, but unsafe
+                        // if sync runs in incognito mode ever. Use the current profile (i.e.,
+                        // regular profile or incognito profile) instead of always using regular
+                        // profile.
+                        HelpAndFeedback.getInstance().show(getActivity(), helpContext,
+                                Profile.getLastUsedRegularProfile(), null);
                     }
                 }));
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index a271e2b..3a48354 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -3230,12 +3230,11 @@
 
     @Test
     @SmallTest
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
     @Feature({"ContextualSearch"})
     // clang-format off
     @Features.EnableFeatures("ContextualSearchLongpressResolve")
-    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.M,
-        message = "Flaky < M, https://crbug.com/1048827")
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.P,
+        message = "Flaky < P, https://crbug.com/1048827")
     public void testLongpressExtendinSelectionExactResolve() throws TimeoutException {
         // clang-format on
         // First test regular long-press.  It should not require an exact resolve.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderUnitTest.java
index 5c83408..fc9932b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderUnitTest.java
@@ -159,12 +159,15 @@
 
     private StartupTabPreloader createStartupTabPreloader(
             Intent intent, TabCreatorManager tabCreatorManager) {
-        return new StartupTabPreloader(new Supplier<Intent>() {
-            @Override
-            public Intent get() {
-                return intent;
-            }
-        }, new ActivityLifecycleDispatcherImpl(), null, tabCreatorManager);
+        return new StartupTabPreloader(
+                new Supplier<Intent>() {
+                    @Override
+                    public Intent get() {
+                        return intent;
+                    }
+                },
+                new ActivityLifecycleDispatcherImpl(), null, tabCreatorManager,
+                new IntentHandler(null, null));
     }
 
     private static class ChromeTabCreatorManager implements TabCreatorManager {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandlerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandlerTest.java
index 210ee20..dd72c01 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandlerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandlerTest.java
@@ -9,6 +9,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -29,15 +30,18 @@
 @RunWith(LocalRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class DelayedScreenLockIntentHandlerTest {
-    @Mock private Context mContextMock;
+    @Mock
+    private Context mApplicationContextMock;
+    @Mock
+    private Activity mActivityMock;
 
     private DelayedScreenLockIntentHandler mIntentHandler;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mIntentHandler = new DelayedScreenLockIntentHandler();
-        ContextUtils.initApplicationContextForTests(mContextMock);
+        mIntentHandler = new DelayedScreenLockIntentHandler(mActivityMock);
+        ContextUtils.initApplicationContextForTests(mApplicationContextMock);
     }
 
     @Test
@@ -46,11 +50,12 @@
         final Intent intent = new Intent(Intent.ACTION_USER_PRESENT);
 
         mIntentHandler.updateDeferredIntent(deferredIntent);
-        mIntentHandler.onReceive(mContextMock, intent);
+        mIntentHandler.onReceive(null, intent);
 
-        verify(mContextMock).registerReceiver(eq(mIntentHandler), any(IntentFilter.class));
-        verify(mContextMock).startActivity(deferredIntent);
-        verify(mContextMock).unregisterReceiver(mIntentHandler);
+        verify(mApplicationContextMock)
+                .registerReceiver(eq(mIntentHandler), any(IntentFilter.class));
+        verify(mActivityMock).startActivity(deferredIntent);
+        verify(mApplicationContextMock).unregisterReceiver(mIntentHandler);
     }
 
     @Test
@@ -59,12 +64,13 @@
         final Intent intent = new Intent(Intent.ACTION_USER_PRESENT);
 
         mIntentHandler.updateDeferredIntent(deferredIntent);
-        mIntentHandler.onReceive(mContextMock, intent);
-        mIntentHandler.onReceive(mContextMock, intent);
+        mIntentHandler.onReceive(null, intent);
+        mIntentHandler.onReceive(null, intent);
 
-        verify(mContextMock).registerReceiver(eq(mIntentHandler), any(IntentFilter.class));
-        verify(mContextMock).startActivity(deferredIntent);
-        verify(mContextMock).unregisterReceiver(mIntentHandler);
+        verify(mApplicationContextMock)
+                .registerReceiver(eq(mIntentHandler), any(IntentFilter.class));
+        verify(mActivityMock).startActivity(deferredIntent);
+        verify(mApplicationContextMock).unregisterReceiver(mIntentHandler);
     }
 
     @Test
@@ -75,24 +81,26 @@
 
         mIntentHandler.updateDeferredIntent(deferredIntent1);
         mIntentHandler.updateDeferredIntent(deferredIntent2);
-        mIntentHandler.onReceive(mContextMock, intent);
+        mIntentHandler.onReceive(null, intent);
 
-        verify(mContextMock).registerReceiver(eq(mIntentHandler), any(IntentFilter.class));
-        verify(mContextMock).startActivity(deferredIntent2);
-        verify(mContextMock).unregisterReceiver(mIntentHandler);
+        verify(mApplicationContextMock)
+                .registerReceiver(eq(mIntentHandler), any(IntentFilter.class));
+        verify(mActivityMock).startActivity(deferredIntent2);
+        verify(mApplicationContextMock).unregisterReceiver(mIntentHandler);
     }
 
     @Test
     public void testNonExpectedIntentAction() {
         mIntentHandler.updateDeferredIntent(new Intent());
         try {
-            mIntentHandler.onReceive(mContextMock, new Intent());
+            mIntentHandler.onReceive(null, new Intent());
         } catch (AssertionError assertError) {
             // Ignore AssertErrors
         }
 
-        verify(mContextMock).registerReceiver(eq(mIntentHandler), any(IntentFilter.class));
-        verify(mContextMock, never()).startActivity(any(Intent.class));
-        verify(mContextMock, never()).unregisterReceiver(mIntentHandler);
+        verify(mApplicationContextMock)
+                .registerReceiver(eq(mIntentHandler), any(IntentFilter.class));
+        verify(mActivityMock, never()).startActivity(any(Intent.class));
+        verify(mApplicationContextMock, never()).unregisterReceiver(mIntentHandler);
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsAnnotationUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsAnnotationUnitTest.java
new file mode 100644
index 0000000..1e3868a
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsAnnotationUnitTest.java
@@ -0,0 +1,47 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.flags;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.test.util.browser.Features;
+
+/**
+ * Unit tests to verify @Features.EnableFeatures() and @Features.DisableFeatures() work for
+ * {@link CachedFeatureFlags}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Features.EnableFeatures(ChromeFeatureList.TEST_DEFAULT_DISABLED)
+@Config(manifest = Config.NONE)
+public class CachedFeatureFlagsAnnotationUnitTest {
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+
+    @Test
+    public void testDefaultFeatureValue() {
+        Assert.assertTrue(CachedFeatureFlags.isEnabled(ChromeFeatureList.TEST_DEFAULT_ENABLED));
+    }
+
+    @Test
+    public void testFeatureAnnotationOnTestSuiteClass() {
+        Assert.assertTrue(CachedFeatureFlags.isEnabled(ChromeFeatureList.TEST_DEFAULT_DISABLED));
+    }
+
+    @Test
+    // clang-format off
+    @Features.DisableFeatures({ChromeFeatureList.TEST_DEFAULT_DISABLED,
+            ChromeFeatureList.TEST_DEFAULT_ENABLED})
+    public void testFeatureAnnotationOnMethod() {
+        // clang-format on
+        Assert.assertFalse(CachedFeatureFlags.isEnabled(ChromeFeatureList.TEST_DEFAULT_DISABLED));
+        Assert.assertFalse(CachedFeatureFlags.isEnabled(ChromeFeatureList.TEST_DEFAULT_ENABLED));
+    }
+}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index fd5cff4..c36d9bd 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1184,8 +1184,6 @@
     "performance_monitor/system_monitor.h",
     "permissions/adaptive_quiet_notification_permission_ui_enabler.cc",
     "permissions/adaptive_quiet_notification_permission_ui_enabler.h",
-    "permissions/chooser_context_base.cc",
-    "permissions/chooser_context_base.h",
     "permissions/chrome_permissions_client.cc",
     "permissions/chrome_permissions_client.h",
     "permissions/contextual_notification_permission_ui_selector.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7ab7071..2220cd1 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4662,11 +4662,6 @@
      flag_descriptions::kAndroidSetupSearchEngineName,
      flag_descriptions::kAndroidSetupSearchEngineDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kAndroidSetupSearchEngine)},
-    {"enable-clipboard-provider-text-suggestions",
-     flag_descriptions::kEnableClipboardProviderTextSuggestionsName,
-     flag_descriptions::kEnableClipboardProviderTextSuggestionsDescription,
-     kOsAndroid,
-     FEATURE_VALUE_TYPE(omnibox::kEnableClipboardProviderTextSuggestions)},
     {"omnibox-remove-suggestions-from-clipboard",
      flag_descriptions::kOmniboxRemoveSuggestionsFromClipboardName,
      flag_descriptions::kOmniboxRemoveSuggestionsFromClipboardDescription,
diff --git a/chrome/browser/bluetooth/bluetooth_chooser_context.cc b/chrome/browser/bluetooth/bluetooth_chooser_context.cc
index 45dce28b..04a5b08 100644
--- a/chrome/browser/bluetooth/bluetooth_chooser_context.cc
+++ b/chrome/browser/bluetooth/bluetooth_chooser_context.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/values.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "device/bluetooth/bluetooth_adapter.h"
@@ -90,9 +91,10 @@
 }  // namespace
 
 BluetoothChooserContext::BluetoothChooserContext(Profile* profile)
-    : ChooserContextBase(profile,
-                         ContentSettingsType::BLUETOOTH_GUARD,
-                         ContentSettingsType::BLUETOOTH_CHOOSER_DATA) {}
+    : ChooserContextBase(
+          ContentSettingsType::BLUETOOTH_GUARD,
+          ContentSettingsType::BLUETOOTH_CHOOSER_DATA,
+          HostContentSettingsMapFactory::GetForProfile(profile)) {}
 
 BluetoothChooserContext::~BluetoothChooserContext() = default;
 
@@ -289,6 +291,13 @@
   return *object.FindStringKey(kDeviceNameKey);
 }
 
+// static
+WebBluetoothDeviceId BluetoothChooserContext::GetObjectDeviceId(
+    const base::Value& object) {
+  std::string device_id_str = *object.FindStringKey(kWebBluetoothDeviceIdKey);
+  return WebBluetoothDeviceId(device_id_str);
+}
+
 bool BluetoothChooserContext::IsValidObject(const base::Value& object) {
   return object.FindStringKey(kDeviceAddressKey) &&
          object.FindStringKey(kDeviceNameKey) &&
diff --git a/chrome/browser/bluetooth/bluetooth_chooser_context.h b/chrome/browser/bluetooth/bluetooth_chooser_context.h
index 9ca5793..b29083bf 100644
--- a/chrome/browser/bluetooth/bluetooth_chooser_context.h
+++ b/chrome/browser/bluetooth/bluetooth_chooser_context.h
@@ -10,13 +10,15 @@
 #include <utility>
 
 #include "base/containers/flat_set.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
+#include "components/permissions/chooser_context_base.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
 #include "third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h"
 #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-forward.h"
 
+class Profile;
+
 namespace base {
 class Value;
 }  // namespace base
@@ -77,6 +79,8 @@
 
   // Returns the human readable string representing the given object.
   static std::string GetObjectName(const base::Value& object);
+  static blink::WebBluetoothDeviceId GetObjectDeviceId(
+      const base::Value& object);
 
  protected:
   // ChooserContextBase implementation;
diff --git a/chrome/browser/bluetooth/bluetooth_chooser_context_unittest.cc b/chrome/browser/bluetooth/bluetooth_chooser_context_unittest.cc
index 4d704593..0aa10b09 100644
--- a/chrome/browser/bluetooth/bluetooth_chooser_context_unittest.cc
+++ b/chrome/browser/bluetooth/bluetooth_chooser_context_unittest.cc
@@ -10,10 +10,10 @@
 #include "chrome/browser/bluetooth/bluetooth_chooser_context.h"
 #include "chrome/browser/bluetooth/bluetooth_chooser_context_factory.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/permissions/chooser_context_base_mock_permission_observer.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/permissions/chooser_context_base.h"
 #include "content/public/test/browser_task_environment.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
diff --git a/chrome/browser/browsing_data/browsing_data_file_system_helper.cc b/chrome/browser/browsing_data/browsing_data_file_system_helper.cc
index a20c7a41..bafcb8f 100644
--- a/chrome/browser/browsing_data/browsing_data_file_system_helper.cc
+++ b/chrome/browser/browsing_data/browsing_data_file_system_helper.cc
@@ -79,17 +79,16 @@
     storage::FileSystemQuotaUtil* quota_util =
         filesystem_context_->GetQuotaUtil(type);
     DCHECK(quota_util);
-    std::set<GURL> origins;
+    std::set<url::Origin> origins;
     quota_util->GetOriginsForTypeOnFileTaskRunner(type, &origins);
-    for (const GURL& current : origins) {
-      if (!BrowsingDataHelper::HasWebScheme(current))
+    for (const auto& current : origins) {
+      if (!BrowsingDataHelper::HasWebScheme(current.GetURL()))
         continue;  // Non-websafe state is not considered browsing data.
       int64_t usage = quota_util->GetOriginUsageOnFileTaskRunner(
-          filesystem_context_.get(), url::Origin::Create(current), type);
+          filesystem_context_.get(), current, type);
       auto inserted =
           file_system_info_map
-              .insert(std::make_pair(
-                  current, FileSystemInfo(url::Origin::Create(current))))
+              .insert(std::make_pair(current.GetURL(), FileSystemInfo(current)))
               .first;
       inserted->second.usage_map[type] = usage;
     }
diff --git a/chrome/browser/browsing_data/browsing_data_media_license_helper.cc b/chrome/browser/browsing_data/browsing_data_media_license_helper.cc
index 1f9d7d6..fea36f54 100644
--- a/chrome/browser/browsing_data/browsing_data_media_license_helper.cc
+++ b/chrome/browser/browsing_data/browsing_data_media_license_helper.cc
@@ -99,19 +99,19 @@
           filesystem_context_->GetFileSystemBackend(kType));
 
   // Determine the set of origins used.
-  std::set<GURL> origins;
+  std::set<url::Origin> origins;
   std::list<MediaLicenseInfo> result;
   backend->GetOriginsForTypeOnFileTaskRunner(kType, &origins);
-  for (const GURL& origin : origins) {
-    if (!BrowsingDataHelper::HasWebScheme(origin))
+  for (const auto& origin : origins) {
+    if (!BrowsingDataHelper::HasWebScheme(origin.GetURL()))
       continue;  // Non-websafe state is not considered browsing data.
 
     int64_t size;
     base::Time last_modified_time;
-    backend->GetOriginDetailsOnFileTaskRunner(filesystem_context_.get(),
-                                              url::Origin::Create(origin),
+    backend->GetOriginDetailsOnFileTaskRunner(filesystem_context_.get(), origin,
                                               &size, &last_modified_time);
-    result.push_back(MediaLicenseInfo(origin, size, last_modified_time));
+    result.push_back(
+        MediaLicenseInfo(origin.GetURL(), size, last_modified_time));
   }
 
   base::PostTask(FROM_HERE, {BrowserThread::UI},
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index cc5fdbc..85eb3a2 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3107,7 +3107,6 @@
       base::GetFieldTrialParamByFeatureAsBool(features::kDataSaverHoldback,
                                               "holdback_web", false);
 
-  auto* native_theme = GetWebTheme();
   auto* contents = content::WebContents::FromRenderViewHost(rvh);
   if (contents) {
 #if defined(OS_ANDROID)
@@ -3122,11 +3121,9 @@
       web_prefs->picture_in_picture_enabled =
           delegate->IsPictureInPictureEnabled();
 
-      // Notify NativeTheme of changes to night mode.
-      native_theme->set_preferred_color_scheme(
-          delegate->IsNightModeEnabled()
-              ? ui::NativeTheme::PreferredColorScheme::kDark
-              : ui::NativeTheme::PreferredColorScheme::kLight);
+      web_prefs->preferred_color_scheme =
+          delegate->IsNightModeEnabled() ? blink::PreferredColorScheme::kDark
+                                         : blink::PreferredColorScheme::kLight;
     }
 #endif  // defined(OS_ANDROID)
 
@@ -3260,6 +3257,7 @@
     }
   }
 
+  auto* native_theme = GetWebTheme();
 #if !defined(OS_ANDROID)
   if (IsAutoplayAllowedByPolicy(contents, prefs)) {
     // If autoplay is allowed by policy then force the no user gesture required
@@ -3277,10 +3275,39 @@
             ? content::AutoplayPolicy::kDocumentUserActivationRequired
             : content::AutoplayPolicy::kNoUserGestureRequired;
   }
+
+  switch (native_theme->GetPreferredColorScheme()) {
+    case ui::NativeTheme::PreferredColorScheme::kDark:
+      web_prefs->preferred_color_scheme = blink::PreferredColorScheme::kDark;
+      break;
+    case ui::NativeTheme::PreferredColorScheme::kLight:
+      web_prefs->preferred_color_scheme = blink::PreferredColorScheme::kLight;
+      break;
+    case ui::NativeTheme::PreferredColorScheme::kNoPreference:
+      web_prefs->preferred_color_scheme =
+          blink::PreferredColorScheme::kNoPreference;
+  }
 #endif  // !defined(OS_ANDROID)
 
   web_prefs->translate_service_available = TranslateService::IsAvailable(prefs);
 
+  // Force a light preferred color scheme on certain URLs if kWebUIDarkMode is
+  // disabled; some of the UI is not yet correctly themed. Note: the WebUI CSS
+  // explicitly uses light (instead of not dark), which is why we don't reset
+  // back to no-preference. https://crbug.com/965811
+  if (!base::FeatureList::IsEnabled(features::kWebUIDarkMode)) {
+    const GURL url = rvh->GetSiteInstance()->GetSiteURL();
+    bool force_light = url.SchemeIs(content::kChromeUIScheme);
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+    if (!force_light) {
+      force_light = url.SchemeIs(extensions::kExtensionScheme) &&
+                    url.host_piece() == extension_misc::kPdfExtensionId;
+    }
+#endif
+    if (force_light)
+      web_prefs->preferred_color_scheme = blink::PreferredColorScheme::kLight;
+  }
+
   // Apply native CaptionStyle parameters.
   base::Optional<ui::CaptionStyle> style;
 
@@ -4923,7 +4950,7 @@
          url->host() == chrome::kChromeUISettingsHost;
 }
 
-ui::NativeTheme* ChromeContentBrowserClient::GetWebTheme() const {
+const ui::NativeTheme* ChromeContentBrowserClient::GetWebTheme() const {
   return ui::NativeTheme::GetInstanceForWeb();
 }
 
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 146cd63..ae3e827 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -661,7 +661,7 @@
   static bool HandleWebUI(GURL* url, content::BrowserContext* browser_context);
   static bool HandleWebUIReverse(GURL* url,
                                  content::BrowserContext* browser_context);
-  virtual ui::NativeTheme* GetWebTheme() const;  // For testing.
+  virtual const ui::NativeTheme* GetWebTheme() const;  // For testing.
 
   // Used by subclasses (e.g. implemented by downstream embedders) to add
   // their own extra part objects.
diff --git a/chrome/browser/chrome_content_browser_client_browsertest.cc b/chrome/browser/chrome_content_browser_client_browsertest.cc
index ec88e4a..0ae7635 100644
--- a/chrome/browser/chrome_content_browser_client_browsertest.cc
+++ b/chrome/browser/chrome_content_browser_client_browsertest.cc
@@ -885,6 +885,111 @@
       opened_tab->GetMainFrame()->GetProcess()->GetID()));
 }
 
+class PrefersColorSchemeTest : public testing::WithParamInterface<bool>,
+                               public InProcessBrowserTest {
+ protected:
+  PrefersColorSchemeTest() : theme_client_(&test_theme_) {
+    feature_list_.InitWithFeatureState(features::kWebUIDarkMode, GetParam());
+  }
+
+  ~PrefersColorSchemeTest() override {
+    CHECK_EQ(&theme_client_, SetBrowserClientForTesting(original_client_));
+  }
+
+  const char* ExpectedColorScheme() const {
+    return GetParam() ? "dark" : "light";
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    InProcessBrowserTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
+                                    "MediaQueryPrefersColorScheme");
+  }
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    original_client_ = SetBrowserClientForTesting(&theme_client_);
+  }
+
+ protected:
+  ui::TestNativeTheme test_theme_;
+
+ private:
+  content::ContentBrowserClient* original_client_ = nullptr;
+
+  class ChromeContentBrowserClientWithWebTheme
+      : public ChromeContentBrowserClient {
+   public:
+    explicit ChromeContentBrowserClientWithWebTheme(
+        const ui::NativeTheme* theme)
+        : theme_(theme) {}
+
+   protected:
+    const ui::NativeTheme* GetWebTheme() const override { return theme_; }
+
+   private:
+    const ui::NativeTheme* const theme_;
+  };
+
+  base::test::ScopedFeatureList feature_list_;
+  ChromeContentBrowserClientWithWebTheme theme_client_;
+};
+
+IN_PROC_BROWSER_TEST_P(PrefersColorSchemeTest, PrefersColorScheme) {
+  test_theme_.SetDarkMode(GetParam());
+  browser()
+      ->tab_strip_model()
+      ->GetActiveWebContents()
+      ->GetRenderViewHost()
+      ->OnWebkitPreferencesChanged();
+  ui_test_utils::NavigateToURL(
+      browser(),
+      ui_test_utils::GetTestUrl(
+          base::FilePath(base::FilePath::kCurrentDirectory),
+          base::FilePath(FILE_PATH_LITERAL("prefers-color-scheme.html"))));
+  base::string16 tab_title;
+  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &tab_title));
+  EXPECT_EQ(base::ASCIIToUTF16(ExpectedColorScheme()), tab_title);
+}
+
+IN_PROC_BROWSER_TEST_P(PrefersColorSchemeTest, FeatureOverridesChromeSchemes) {
+  test_theme_.SetDarkMode(true);
+
+  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIDownloadsURL));
+
+  bool matches;
+  ASSERT_TRUE(ExecuteScriptAndExtractBool(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      base::StringPrintf("window.domAutomationController.send(window."
+                         "matchMedia('(prefers-color-scheme: %s)').matches)",
+                         ExpectedColorScheme()),
+      &matches));
+  EXPECT_TRUE(matches);
+}
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+IN_PROC_BROWSER_TEST_P(PrefersColorSchemeTest, FeatureOverridesPdfUI) {
+  test_theme_.SetDarkMode(true);
+
+  std::string pdf_extension_url(extensions::kExtensionScheme);
+  pdf_extension_url.append(url::kStandardSchemeSeparator);
+  pdf_extension_url.append(extension_misc::kPdfExtensionId);
+  GURL pdf_index = GURL(pdf_extension_url).Resolve("/index.html");
+  ui_test_utils::NavigateToURL(browser(), pdf_index);
+
+  bool matches;
+  ASSERT_TRUE(ExecuteScriptAndExtractBool(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      base::StringPrintf("window.domAutomationController.send(window."
+                         "matchMedia('(prefers-color-scheme: %s)').matches)",
+                         ExpectedColorScheme()),
+      &matches));
+  EXPECT_TRUE(matches);
+}
+#endif
+
+INSTANTIATE_TEST_SUITE_P(All, PrefersColorSchemeTest, testing::Bool());
+
 class ProtocolHandlerTest : public InProcessBrowserTest {
  public:
   ProtocolHandlerTest() = default;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 5e2b7aeb0..b4748883 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1531,6 +1531,8 @@
     "login/security_token_pin_dialog_host_ash_impl.h",
     "login/session/chrome_session_manager.cc",
     "login/session/chrome_session_manager.h",
+    "login/session/user_session_initializer.cc",
+    "login/session/user_session_initializer.h",
     "login/session/user_session_manager.cc",
     "login/session/user_session_manager.h",
     "login/signin/auth_error_observer.cc",
@@ -1545,6 +1547,8 @@
     "login/signin/oauth2_login_manager.h",
     "login/signin/oauth2_login_manager_factory.cc",
     "login/signin/oauth2_login_manager_factory.h",
+    "login/signin/oauth2_login_verifier.cc",
+    "login/signin/oauth2_login_verifier.h",
     "login/signin/oauth2_token_fetcher.cc",
     "login/signin/oauth2_token_fetcher.h",
     "login/signin/oauth2_token_initializer.cc",
diff --git a/chrome/browser/chromeos/account_manager/account_manager_util.h b/chrome/browser/chromeos/account_manager/account_manager_util.h
index f26380ac..97cb028a 100644
--- a/chrome/browser/chromeos/account_manager/account_manager_util.h
+++ b/chrome/browser/chromeos/account_manager/account_manager_util.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_CHROMEOS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_UTIL_H_
 #define CHROME_BROWSER_CHROMEOS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_UTIL_H_
 
-#include "base/callback_forward.h"
+#include "base/bind.h"
 #include "base/files/file_path.h"
 
 class Profile;
diff --git a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
index 2edb72f2..95321c4 100644
--- a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
+++ b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
@@ -56,6 +56,17 @@
   permitted_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
   forwardable = true)";
 
+// Backoff policy used to control managed accounts addition retries.
+const net::BackoffEntry::Policy kBackoffPolicyForManagedAccounts = {
+    0,               // Number of initial errors to ignore without backoff.
+    1 * 1000,        // Initial delay for backoff in ms: 1 second.
+    2,               // Factor to multiply for exponential backoff.
+    0,               // Fuzzing percentage.
+    10 * 60 * 1000,  // Maximum time to delay requests in ms: 10 minutes.
+    -1,              // Don't discard entry even if unused.
+    false            // Don't use initial delay unless the last was an error.
+};
+
 // If |principal_name| is "UsEr@realm.com", sets |principal_name| to
 // "user@REALM.COM". Returns false if the given name has no @ or one of the
 // parts is empty.
@@ -93,6 +104,13 @@
   return error == kerberos::ERROR_NONE;
 }
 
+bool ShouldRetry(kerberos::ErrorType error) {
+  // The error types that should trigger a managed accounts addition retry.
+  return error == kerberos::ERROR_NETWORK_PROBLEM ||
+         error == kerberos::ERROR_CONTACTING_KDC_FAILED ||
+         error == kerberos::ERROR_IN_PROGRESS;
+}
+
 }  // namespace
 
 // Encapsulates the steps to add a Kerberos account. Overview of the flow:
@@ -278,7 +296,8 @@
       primary_profile_(primary_profile),
       kerberos_files_handler_(std::make_unique<KerberosFilesHandler>(
           base::BindRepeating(&KerberosCredentialsManager::GetKerberosFiles,
-                              base::Unretained(this)))) {
+                              base::Unretained(this)))),
+      backoff_entry_for_managed_accounts_(&kBackoffPolicyForManagedAccounts) {
   DCHECK(primary_profile_);
   const user_manager::User* primary_user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(primary_profile);
@@ -326,7 +345,7 @@
   pref_change_registrar_->Add(
       prefs::kKerberosAccounts,
       base::BindRepeating(&KerberosCredentialsManager::UpdateAccountsFromPref,
-                          weak_factory_.GetWeakPtr()));
+                          weak_factory_.GetWeakPtr(), false /* is_retry */));
 
   // Update accounts if policy is already available or start observing.
   policy_service_ =
@@ -335,7 +354,7 @@
       policy_service_->IsInitializationComplete(policy::POLICY_DOMAIN_CHROME);
   VLOG(1) << "Policy service initialized at startup: " << policy_initialized;
   if (policy_initialized)
-    UpdateAccountsFromPref();
+    UpdateAccountsFromPref(false /* is_retry */);
   else
     policy_service_->AddObserver(policy::POLICY_DOMAIN_CHROME, this);
 
@@ -398,7 +417,7 @@
   if (policy_service_->IsInitializationComplete(policy::POLICY_DOMAIN_CHROME)) {
     VLOG(1) << "Policy service initialized";
     policy_service_->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
-    UpdateAccountsFromPref();
+    UpdateAccountsFromPref(false /* is_retry */);
   }
 }
 
@@ -472,6 +491,18 @@
 
 void KerberosCredentialsManager::OnAddManagedAccountRunnerDone(
     kerberos::ErrorType error) {
+  if (!managed_accounts_retry_timer_.IsRunning() && ShouldRetry(error)) {
+    backoff_entry_for_managed_accounts_.InformOfRequest(false);
+
+    if (backoff_entry_for_managed_accounts_.failure_count() <
+        kMaxFailureCountForManagedAccounts) {
+      managed_accounts_retry_timer_.Start(
+          FROM_HERE, backoff_entry_for_managed_accounts_.GetTimeUntilRelease(),
+          base::BindOnce(&KerberosCredentialsManager::UpdateAccountsFromPref,
+                         weak_factory_.GetWeakPtr(), true /* is_retry */));
+    }
+  }
+
   if (add_managed_account_callback_for_testing_) {
     add_managed_account_callback_for_testing_.Run(error);
   }
@@ -746,7 +777,7 @@
   if (IsKerberosEnabled()) {
     // Kerberos got enabled, re-populate managed accounts.
     VLOG(1) << "Kerberos got enabled, populating managed accounts";
-    UpdateAccountsFromPref();
+    UpdateAccountsFromPref(false /* is_retry */);
     return;
   }
 
@@ -781,7 +812,14 @@
                               EmptyResultCallback()));
 }
 
-void KerberosCredentialsManager::UpdateAccountsFromPref() {
+void KerberosCredentialsManager::UpdateAccountsFromPref(bool is_retry) {
+  if (is_retry) {
+    VLOG(1) << "Retrying to update KerberosAccounts from Prefs";
+  } else {
+    // Refreshing backoff entry, since this call was triggered by prefs change.
+    backoff_entry_for_managed_accounts_.Reset();
+  }
+
   if (!IsKerberosEnabled()) {
     VLOG(1) << "Kerberos disabled";
     NotifyRequiresLoginPassword(false);
diff --git a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h
index 08fa3d2..34a3fb9 100644
--- a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h
+++ b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h
@@ -14,11 +14,13 @@
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "base/optional.h"
+#include "base/timer/timer.h"
 #include "chrome/browser/chromeos/authpolicy/kerberos_files_handler.h"
 #include "chromeos/dbus/kerberos/kerberos_service.pb.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/policy/core/common/policy_namespace.h"
 #include "components/policy/core/common/policy_service.h"
+#include "net/base/backoff_entry.h"
 
 class PrefRegistrySimple;
 class PrefService;
@@ -56,6 +58,9 @@
     DISALLOW_COPY_AND_ASSIGN(Observer);
   };
 
+  // Maximum number of managed accounts addition retries per prefs change.
+  static constexpr int kMaxFailureCountForManagedAccounts = 10;
+
   KerberosCredentialsManager(PrefService* local_state,
                              Profile* primary_profile);
   ~KerberosCredentialsManager() override;
@@ -236,7 +241,7 @@
   void UpdateEnabledFromPref();
   void UpdateRememberPasswordEnabledFromPref();
   void UpdateAddAccountsAllowedFromPref();
-  void UpdateAccountsFromPref();
+  void UpdateAccountsFromPref(bool is_retry);
 
   // Does the main work for UpdateAccountsFromPref(). To clean up stale managed
   // accounts, an up-to-date accounts list is needed. UpdateAccountsFromPref()
@@ -277,6 +282,12 @@
   // List of objects that observe this instance.
   base::ObserverList<Observer, true /* check_empty */> observers_;
 
+  // Backoff entry used to control managed accounts addition retries.
+  net::BackoffEntry backoff_entry_for_managed_accounts_;
+
+  // Timer for keeping track of managed accounts addition retries.
+  base::OneShotTimer managed_accounts_retry_timer_;
+
   // Callback optionally used for testing.
   base::RepeatingCallback<void(kerberos::ErrorType)>
       add_managed_account_callback_for_testing_;
diff --git a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager_test.cc b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager_test.cc
index eec263e..6fe28ec4 100644
--- a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager_test.cc
+++ b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager_test.cc
@@ -15,6 +15,7 @@
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
 #include "chrome/browser/chromeos/authpolicy/kerberos_files_handler.h"
 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
 #include "chrome/browser/chromeos/login/users/mock_user_manager.h"
@@ -73,6 +74,10 @@
 const int kTwoAccounts = 2;
 const int kThreeAccounts = 3;
 
+const int kOneFailure = 1;
+const int kThreeFailures = 3;
+const int kLotsOfFailures = 1000000;
+
 // Account keys for the kerberos.accounts pref.
 constexpr char kKeyPrincipal[] = "principal";
 constexpr char kKeyPassword[] = "password";
@@ -89,6 +94,11 @@
   permitted_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
   forwardable = true)";
 
+// A long time delta, used to fast forward the task environment until all
+// pending operations are completed. This value should be equal to the maximum
+// time to delay requests on |kBackoffPolicyForManagedAccounts|.
+const base::TimeDelta kLongTimeDelay = base::TimeDelta::FromMinutes(10);
+
 // Fake observer used to test notifications sent by KerberosCredentialsManager
 // on accounts changes.
 class FakeKerberosCredentialsManagerObserver
@@ -192,23 +202,23 @@
     return KerberosClient::Get()->GetTestInterface();
   }
 
-  void SetupResultCallback(int account_count) {
+  void SetupResultCallback(int operation_count) {
     // If this is the first account addition, sets |result_run_loop_|.
-    if (accounts_addition_count_ == 0) {
+    if (remaining_operation_count_ == 0) {
       EXPECT_TRUE(result_errors_.empty());
       EXPECT_FALSE(result_run_loop_);
       result_run_loop_ = std::make_unique<base::RunLoop>();
     }
 
-    accounts_addition_count_ += account_count;
+    remaining_operation_count_ += operation_count;
   }
 
   // Gets a callback that adds the passed-in error to |result_errors_|.
-  // |account_count| is the number of times the callback should be called
+  // |operation_count| is the number of times the callback should be called
   // before stopping |result_run_loop_|.
   base::RepeatingCallback<void(kerberos::ErrorType)> GetRepeatingCallback(
-      int account_count) {
-    SetupResultCallback(account_count);
+      int operation_count) {
+    SetupResultCallback(operation_count);
     return base::BindRepeating(&KerberosCredentialsManagerTest::OnResult,
                                weak_ptr_factory_.GetWeakPtr());
   }
@@ -221,34 +231,45 @@
   }
 
   void OnResult(kerberos::ErrorType error) {
-    DCHECK_LT(0, accounts_addition_count_);
-    accounts_addition_count_--;
+    // Fails if the test tries to execute more operations than expected.
+    ASSERT_LT(0, remaining_operation_count_);
+    remaining_operation_count_--;
     result_errors_.insert(error);
 
     // Stops |result_run_loop_| if all additions are finished.
-    if (accounts_addition_count_ == 0) {
+    if (remaining_operation_count_ == 0) {
       result_run_loop_->Quit();
     }
   }
 
-  void WaitAndVerifyResult(std::multiset<kerberos::ErrorType> expected_errors_,
+  void WaitAndVerifyResult(std::multiset<kerberos::ErrorType> expected_errors,
                            int expected_notifications_count,
                            int expected_accounts_count) {
-    EXPECT_LT(0, accounts_addition_count_);
+    EXPECT_LT(0, remaining_operation_count_);
     ASSERT_TRUE(result_run_loop_);
     result_run_loop_->Run();
 
-    EXPECT_EQ(expected_errors_, result_errors_);
+    EXPECT_EQ(expected_errors, result_errors_);
     EXPECT_EQ(expected_notifications_count, observer_.notifications_count());
     EXPECT_EQ(expected_accounts_count,
               observer_.accounts_count_at_last_notification());
 
-    EXPECT_EQ(0, accounts_addition_count_);
+    EXPECT_EQ(0, remaining_operation_count_);
     result_run_loop_.reset();
     result_errors_.clear();
     observer_.Reset();
   }
 
+  std::multiset<kerberos::ErrorType> GetRepeatedError(kerberos::ErrorType error,
+                                                      int repetitions) {
+    std::multiset<kerberos::ErrorType> result;
+    for (int i = 0; i < repetitions; i++) {
+      result.insert(error);
+    }
+
+    return result;
+  }
+
   // Calls |mgr_->AddAccountAndAuthenticate()| with |principal_name|,
   // |is_managed| and some default parameters, waits for the result and checks
   // expectations.
@@ -338,7 +359,8 @@
     EXPECT_TRUE(user_context.GetPasswordKey()->GetSecret().empty());
   }
 
-  content::BrowserTaskEnvironment task_environment_;
+  content::BrowserTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   user_manager::ScopedUserManager scoped_user_manager_;
   ScopedTestingLocalState local_state_;
   std::unique_ptr<TestingProfile> profile_;
@@ -346,7 +368,7 @@
   std::unique_ptr<KerberosCredentialsManager> mgr_;
   FakeKerberosCredentialsManagerObserver observer_;
 
-  int accounts_addition_count_ = 0;
+  int remaining_operation_count_ = 0;
   std::unique_ptr<base::RunLoop> result_run_loop_;
   std::multiset<kerberos::ErrorType> result_errors_;
 
@@ -587,8 +609,7 @@
                                   kConfig, kAllowExisting, GetResultCallback());
 
   WaitAndVerifyResult(
-      {kerberos::ERROR_BAD_PASSWORD, kerberos::ERROR_BAD_PASSWORD,
-       kerberos::ERROR_BAD_PASSWORD},
+      GetRepeatedError(kerberos::ERROR_BAD_PASSWORD, kThreeAccounts),
       kOneNotification, kThreeAccounts);
   EXPECT_TRUE(mgr_->GetActiveAccount().empty());
 }
@@ -608,9 +629,8 @@
                                   kDontRememberPassword, kConfig,
                                   kAllowExisting, GetResultCallback());
 
-  WaitAndVerifyResult(
-      {kerberos::ERROR_NONE, kerberos::ERROR_NONE, kerberos::ERROR_NONE},
-      kOneNotification, kThreeAccounts);
+  WaitAndVerifyResult(GetRepeatedError(kerberos::ERROR_NONE, kThreeAccounts),
+                      kOneNotification, kThreeAccounts);
   EXPECT_EQ(kNormalizedPrincipal, mgr_->GetActiveAccount());
 }
 
@@ -780,7 +800,7 @@
 
   // Two notifications are expected: one from AddAccountRunner and another from
   // RemoveAllManagedAccountsExcept.
-  WaitAndVerifyResult({kerberos::ERROR_NONE, kerberos::ERROR_NONE},
+  WaitAndVerifyResult(GetRepeatedError(kerberos::ERROR_NONE, kTwoAccounts),
                       kTwoNotifications, kTwoAccounts);
 
   EXPECT_TRUE(mgr_->IsKerberosEnabled());
@@ -813,7 +833,7 @@
                                   kRememberPassword, kConfig, kAllowExisting,
                                   GetResultCallback());
 
-  WaitAndVerifyResult({kerberos::ERROR_NONE, kerberos::ERROR_NONE},
+  WaitAndVerifyResult(GetRepeatedError(kerberos::ERROR_NONE, kTwoAccounts),
                       kOneNotification, kTwoAccounts);
 
   SetPref(prefs::kKerberosRememberPasswordEnabled, base::Value(false));
@@ -966,7 +986,7 @@
 
   // Two notifications are expected: one from AddAccountRunner and another from
   // RemoveAllManagedAccountsExcept().
-  WaitAndVerifyResult({kerberos::ERROR_NONE, kerberos::ERROR_NONE},
+  WaitAndVerifyResult(GetRepeatedError(kerberos::ERROR_NONE, kTwoAccounts),
                       kTwoNotifications, kTwoAccounts);
 
   VerifyVotedForSavingLoginPassword(kDontSaveLoginPassword);
@@ -1005,7 +1025,7 @@
 
   // Two notifications are expected: one from AddAccountRunner and another from
   // RemoveAllManagedAccountsExcept().
-  WaitAndVerifyResult({kerberos::ERROR_NONE, kerberos::ERROR_NONE},
+  WaitAndVerifyResult(GetRepeatedError(kerberos::ERROR_NONE, kTwoAccounts),
                       kTwoNotifications, kTwoAccounts);
 
   VerifyVotedForSavingLoginPassword(kSaveLoginPassword);
@@ -1046,7 +1066,7 @@
 
   // Two notifications are expected: one from AddAccountRunner and another from
   // RemoveAllManagedAccountsExcept().
-  WaitAndVerifyResult({kerberos::ERROR_NONE, kerberos::ERROR_NONE},
+  WaitAndVerifyResult(GetRepeatedError(kerberos::ERROR_NONE, kTwoAccounts),
                       kTwoNotifications, kTwoAccounts);
 
   VerifyVotedForSavingLoginPassword(kSaveLoginPassword);
@@ -1089,7 +1109,7 @@
 
   // Two notifications are expected: one from AddAccountRunner and another from
   // RemoveAllManagedAccountsExcept().
-  WaitAndVerifyResult({kerberos::ERROR_NONE, kerberos::ERROR_NONE},
+  WaitAndVerifyResult(GetRepeatedError(kerberos::ERROR_NONE, kTwoAccounts),
                       kTwoNotifications, kTwoAccounts);
 
   VerifyVotedForSavingLoginPassword(kDontSaveLoginPassword);
@@ -1101,6 +1121,178 @@
   EXPECT_EQ(kNormalizedPrincipal, mgr_->GetActiveAccount());
 }
 
+// UpdateAccountsFromPref retries to add account if addition fails for network
+// related errors.
+TEST_F(KerberosCredentialsManagerTest, UpdateAccountsFromPrefRetry) {
+  // Starting with Kerberos enabled.
+  SetPref(prefs::kKerberosEnabled, base::Value(true));
+
+  client_test_interface()->SetSimulatedNumberOfNetworkFailures(kOneFailure *
+                                                               kOneAccount);
+
+  mgr_->SetAddManagedAccountCallbackForTesting(
+      GetRepeatingCallback((kOneFailure + 1) * kOneAccount));
+
+  base::Value managed_account_1(base::Value::Type::DICTIONARY);
+
+  managed_account_1.SetStringKey(kKeyPrincipal, kPrincipal);
+  managed_account_1.SetStringKey(kKeyPassword, kPassword);
+
+  base::Value managed_accounts(base::Value::Type::LIST);
+  managed_accounts.Append(std::move(managed_account_1));
+
+  SetPref(prefs::kKerberosAccounts, std::move(managed_accounts));
+
+  // Two notifications are expected for each attempt: one from AddAccountRunner
+  // and another from RemoveAllManagedAccountsExcept().
+  WaitAndVerifyResult({kerberos::ERROR_NETWORK_PROBLEM, kerberos::ERROR_NONE},
+                      (kOneFailure + 1) * kTwoNotifications, kOneAccount);
+
+  // Fast forwarding the task environment to force all pending tasks to be
+  // executed. Makes sure no retry is scheduled after a successful attempt.
+  task_environment_.FastForwardBy(kLongTimeDelay);
+
+  Accounts accounts = ListAccounts();
+  ASSERT_EQ(1u, accounts.size());
+  EXPECT_EQ(kNormalizedPrincipal, accounts[0].principal_name());
+  EXPECT_EQ(kNormalizedPrincipal, mgr_->GetActiveAccount());
+}
+
+// UpdateAccountsFromPref retries multiple times to add account if addition
+// fails multiple times for network related errors.
+TEST_F(KerberosCredentialsManagerTest, UpdateAccountsFromPrefMultipleRetries) {
+  // Starting with Kerberos enabled.
+  SetPref(prefs::kKerberosEnabled, base::Value(true));
+
+  client_test_interface()->SetSimulatedNumberOfNetworkFailures(kThreeFailures *
+                                                               kOneAccount);
+
+  mgr_->SetAddManagedAccountCallbackForTesting(
+      GetRepeatingCallback((kThreeFailures + 1) * kOneAccount));
+
+  base::Value managed_account_1(base::Value::Type::DICTIONARY);
+
+  managed_account_1.SetStringKey(kKeyPrincipal, kPrincipal);
+  managed_account_1.SetStringKey(kKeyPassword, kPassword);
+
+  base::Value managed_accounts(base::Value::Type::LIST);
+  managed_accounts.Append(std::move(managed_account_1));
+
+  SetPref(prefs::kKerberosAccounts, std::move(managed_accounts));
+
+  // Two notifications are expected for each attempt: one from AddAccountRunner
+  // and another from RemoveAllManagedAccountsExcept().
+  WaitAndVerifyResult(
+      {kerberos::ERROR_NETWORK_PROBLEM, kerberos::ERROR_NETWORK_PROBLEM,
+       kerberos::ERROR_NETWORK_PROBLEM, kerberos::ERROR_NONE},
+      (kThreeFailures + 1) * kTwoNotifications, kOneAccount);
+
+  // Fast forwarding the task environment to force all pending tasks to be
+  // executed. This will make sure no retry is scheduled after a successful
+  // attempt.
+  task_environment_.FastForwardBy(kLongTimeDelay);
+
+  Accounts accounts = ListAccounts();
+  ASSERT_EQ(1u, accounts.size());
+  EXPECT_EQ(kNormalizedPrincipal, accounts[0].principal_name());
+  EXPECT_EQ(kNormalizedPrincipal, mgr_->GetActiveAccount());
+}
+
+// UpdateAccountsFromPref retries to add multiple accounts if addition fails for
+// network related errors.
+TEST_F(KerberosCredentialsManagerTest,
+       UpdateAccountsFromPrefRetryMultipleAccounts) {
+  // Starting with Kerberos enabled.
+  SetPref(prefs::kKerberosEnabled, base::Value(true));
+
+  client_test_interface()->SetSimulatedNumberOfNetworkFailures(kOneFailure *
+                                                               kTwoAccounts);
+
+  mgr_->SetAddManagedAccountCallbackForTesting(
+      GetRepeatingCallback((kOneFailure + 1) * kTwoAccounts));
+
+  base::Value managed_account_1(base::Value::Type::DICTIONARY);
+  base::Value managed_account_2(base::Value::Type::DICTIONARY);
+
+  managed_account_1.SetStringKey(kKeyPrincipal, kPrincipal);
+  managed_account_1.SetStringKey(kKeyPassword, kPassword);
+  managed_account_2.SetStringKey(kKeyPrincipal, kOtherPrincipal);
+  managed_account_2.SetStringKey(kKeyPassword, kPassword);
+
+  base::Value managed_accounts(base::Value::Type::LIST);
+  managed_accounts.Append(std::move(managed_account_1));
+  managed_accounts.Append(std::move(managed_account_2));
+
+  SetPref(prefs::kKerberosAccounts, std::move(managed_accounts));
+
+  // Two notifications are expected for each attempt: one from AddAccountRunner
+  // and another from RemoveAllManagedAccountsExcept().
+  WaitAndVerifyResult(
+      {kerberos::ERROR_NETWORK_PROBLEM, kerberos::ERROR_NETWORK_PROBLEM,
+       kerberos::ERROR_NONE, kerberos::ERROR_NONE},
+      (kOneFailure + 1) * kTwoNotifications, kTwoAccounts);
+
+  // Fast forwarding the task environment to force all pending tasks to be
+  // executed. This will make sure no retry is scheduled after a successful
+  // attempt.
+  task_environment_.FastForwardBy(kLongTimeDelay);
+
+  Accounts accounts = ListAccounts();
+  ASSERT_EQ(2u, accounts.size());
+  EXPECT_EQ(kNormalizedPrincipal, accounts[0].principal_name());
+  EXPECT_EQ(kNormalizedOtherPrincipal, accounts[1].principal_name());
+  EXPECT_EQ(kNormalizedPrincipal, mgr_->GetActiveAccount());
+}
+
+// UpdateAccountsFromPref stops retrying after a certain number of network
+// related errors.
+TEST_F(KerberosCredentialsManagerTest, UpdateAccountsFromPrefStopsRetrying) {
+  // Starting with Kerberos enabled.
+  SetPref(prefs::kKerberosEnabled, base::Value(true));
+
+  client_test_interface()->SetSimulatedNumberOfNetworkFailures(kLotsOfFailures);
+
+  mgr_->SetAddManagedAccountCallbackForTesting(GetRepeatingCallback(
+      KerberosCredentialsManager::kMaxFailureCountForManagedAccounts *
+      kTwoAccounts));
+
+  base::Value managed_account_1(base::Value::Type::DICTIONARY);
+  base::Value managed_account_2(base::Value::Type::DICTIONARY);
+
+  managed_account_1.SetStringKey(kKeyPrincipal, kPrincipal);
+  managed_account_1.SetStringKey(kKeyPassword, kPassword);
+  managed_account_2.SetStringKey(kKeyPrincipal, kOtherPrincipal);
+  managed_account_2.SetStringKey(kKeyPassword, kPassword);
+
+  base::Value managed_accounts(base::Value::Type::LIST);
+  managed_accounts.Append(std::move(managed_account_1));
+  managed_accounts.Append(std::move(managed_account_2));
+
+  SetPref(prefs::kKerberosAccounts, std::move(managed_accounts));
+
+  // Two notifications are expected for each attempt: one from AddAccountRunner
+  // and another from RemoveAllManagedAccountsExcept().
+  WaitAndVerifyResult(
+      GetRepeatedError(
+          kerberos::ERROR_NETWORK_PROBLEM,
+          KerberosCredentialsManager::kMaxFailureCountForManagedAccounts *
+              kTwoAccounts),
+      KerberosCredentialsManager::kMaxFailureCountForManagedAccounts *
+          kTwoNotifications,
+      kTwoAccounts);
+
+  // Fast forwarding the task environment to force all pending tasks to be
+  // executed. This will make sure no retry is scheduled after a certain number
+  // of network related errors.
+  task_environment_.FastForwardBy(kLongTimeDelay);
+
+  Accounts accounts = ListAccounts();
+  ASSERT_EQ(2u, accounts.size());
+  EXPECT_EQ(kNormalizedPrincipal, accounts[0].principal_name());
+  EXPECT_EQ(kNormalizedOtherPrincipal, accounts[1].principal_name());
+  EXPECT_EQ(kNormalizedPrincipal, mgr_->GetActiveAccount());
+}
+
 // TODO(https://crbug.com/952251): Add more tests
 // - ClearAccounts
 //     + Normalization like in AddAccountAndAuthenticate
diff --git a/chrome/browser/chromeos/login/screens/marketing_opt_in_screen.h b/chrome/browser/chromeos/login/screens/marketing_opt_in_screen.h
index c55a5358..60024ec 100644
--- a/chrome/browser/chromeos/login/screens/marketing_opt_in_screen.h
+++ b/chrome/browser/chromeos/login/screens/marketing_opt_in_screen.h
@@ -32,6 +32,11 @@
   // ash::ShelfCondif::Observer:
   void OnShelfConfigUpdated() override;
 
+  void set_exit_callback_for_testing(
+      const base::RepeatingClosure& exit_callback) {
+    exit_callback_ = exit_callback;
+  }
+
  protected:
   // BaseScreen:
   void ShowImpl() override;
diff --git a/chrome/browser/chromeos/login/screens/marketing_opt_in_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/marketing_opt_in_screen_browsertest.cc
new file mode 100644
index 0000000..2fe4902
--- /dev/null
+++ b/chrome/browser/chromeos/login/screens/marketing_opt_in_screen_browsertest.cc
@@ -0,0 +1,219 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/screens/marketing_opt_in_screen.h"
+
+#include <string>
+#include <vector>
+
+#include "ash/public/cpp/ash_features.h"
+#include "ash/public/cpp/ash_pref_names.h"
+#include "ash/public/cpp/shelf_test_api.h"
+#include "ash/public/cpp/test/shell_test_api.h"
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/login/screen_manager.h"
+#include "chrome/browser/chromeos/login/test/js_checker.h"
+#include "chrome/browser/chromeos/login/test/oobe_base_test.h"
+#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chrome/browser/chromeos/login/wizard_controller.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/aura/window.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/vector2d.h"
+
+namespace chromeos {
+
+class MarketingOptInScreenTest : public OobeBaseTest {
+ public:
+  MarketingOptInScreenTest() {
+    feature_list_.InitAndEnableFeature(
+        ash::features::kHideShelfControlsInTabletMode);
+  }
+  ~MarketingOptInScreenTest() override = default;
+
+  // OobeBaseTest:
+  void SetUpOnMainThread() override {
+    ash::ShellTestApi().SetTabletModeEnabledForTest(true);
+
+    MarketingOptInScreen* marketing_screen = static_cast<MarketingOptInScreen*>(
+        WizardController::default_controller()->screen_manager()->GetScreen(
+            MarketingOptInScreenView::kScreenId));
+    marketing_screen->set_exit_callback_for_testing(base::BindRepeating(
+        &MarketingOptInScreenTest::HandleScreenExit, base::Unretained(this)));
+
+    OobeBaseTest::SetUpOnMainThread();
+  }
+
+  // Shows the gesture navigation screen.
+  void ShowMarketingOptInScreen() {
+    WizardController::default_controller()->AdvanceToScreen(
+        MarketingOptInScreenView::kScreenId);
+  }
+
+  void WaitForScreenExit() {
+    if (screen_exited_)
+      return;
+
+    base::RunLoop run_loop;
+    screen_exit_callback_ = run_loop.QuitClosure();
+    run_loop.Run();
+  }
+
+  void SimulateFlingFromShelf() {
+    aura::Window* oobe_window = LoginDisplayHost::default_host()
+                                    ->GetOobeWebContents()
+                                    ->GetTopLevelNativeWindow();
+    display::Screen* const screen = display::Screen::GetScreen();
+    const display::Display display =
+        screen->GetDisplayNearestWindow(oobe_window);
+
+    // Start at the center of the expected shelf bounds.
+    const int shelf_size = ash::ShelfConfig::Get()->shelf_size();
+    const gfx::Point start =
+        gfx::Point(display.bounds().x() + display.bounds().width() / 2,
+                   display.bounds().bottom() - shelf_size / 2);
+    // Swipe upwards.
+    const gfx::Point end = start + gfx::Vector2d(0, -shelf_size);
+    const base::TimeDelta kTimeDelta = base::TimeDelta::FromMilliseconds(10);
+    const int kNumScrollSteps = 4;
+    ui::test::EventGenerator event_generator(oobe_window->GetRootWindow());
+    event_generator.GestureScrollSequence(start, end, kTimeDelta,
+                                          kNumScrollSteps);
+  }
+
+ private:
+  void HandleScreenExit() {
+    ASSERT_FALSE(screen_exited_);
+    screen_exited_ = true;
+    if (screen_exit_callback_)
+      std::move(screen_exit_callback_).Run();
+  }
+
+  bool screen_exited_ = false;
+  base::RepeatingClosure screen_exit_callback_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Tests that marketing opt in toggles are hidden by default (as the command
+// line switch to show marketing opt in is not set).
+IN_PROC_BROWSER_TEST_F(MarketingOptInScreenTest, MarketingTogglesHidden) {
+  ShowMarketingOptInScreen();
+  OobeScreenWaiter(MarketingOptInScreenView::kScreenId).Wait();
+  test::OobeJS().ExpectHiddenPath(
+      {"marketing-opt-in", "marketing-opt-in-subtitle"});
+  test::OobeJS().ExpectHiddenPath(
+      {"marketing-opt-in", "marketing-opt-in-toggles"});
+
+  ash::ShellTestApi().SetTabletModeEnabledForTest(false);
+  test::OobeJS().ExpectHiddenPath(
+      {"marketing-opt-in", "marketing-opt-in-subtitle"});
+  test::OobeJS().ExpectHiddenPath(
+      {"marketing-opt-in", "marketing-opt-in-toggles"});
+
+  ash::ShellTestApi().SetTabletModeEnabledForTest(true);
+  test::OobeJS().ExpectHiddenPath(
+      {"marketing-opt-in", "marketing-opt-in-subtitle"});
+  test::OobeJS().ExpectHiddenPath(
+      {"marketing-opt-in", "marketing-opt-in-toggles"});
+}
+
+// Tests that fling from shelf exits the screen in tablet mode.
+IN_PROC_BROWSER_TEST_F(MarketingOptInScreenTest, FlingFromShelfInTabletMode) {
+  ShowMarketingOptInScreen();
+  OobeScreenWaiter(MarketingOptInScreenView::kScreenId).Wait();
+
+  ASSERT_TRUE(ash::ShelfTestApi().HasLoginShelfGestureHandler());
+  SimulateFlingFromShelf();
+
+  WaitForScreenExit();
+  EXPECT_FALSE(ash::ShelfTestApi().HasLoginShelfGestureHandler());
+}
+
+// Tests that fling from shelf is not enabled in tablet mode if shelf
+// navigation buttons are forced by the accessibility setting to show the
+// buttons.
+IN_PROC_BROWSER_TEST_F(MarketingOptInScreenTest,
+                       ShelfButtonsEnabledInTabletMode) {
+  ShowMarketingOptInScreen();
+  OobeScreenWaiter(MarketingOptInScreenView::kScreenId).Wait();
+
+  ASSERT_TRUE(ash::ShelfTestApi().HasLoginShelfGestureHandler());
+
+  // If the setting to always show shelf navigation buttons is enabled, the
+  // shelf gesture detection should be disabled on the screen, and the user
+  // should be able to use "next" button to exit the screen.
+  ProfileManager::GetActiveUserProfile()->GetPrefs()->SetBoolean(
+      ash::prefs::kAccessibilityTabletModeShelfNavigationButtonsEnabled, true);
+  EXPECT_FALSE(ash::ShelfTestApi().HasLoginShelfGestureHandler());
+
+  test::OobeJS()
+      .CreateVisibilityWaiter(
+          true, {"marketing-opt-in", "marketing-opt-in-next-button"})
+      ->Wait();
+  test::OobeJS().TapOnPath(
+      {"marketing-opt-in", "marketing-opt-in-next-button"});
+
+  WaitForScreenExit();
+  EXPECT_FALSE(ash::ShelfTestApi().HasLoginShelfGestureHandler());
+}
+
+// Tests that login shelf does not have fling handler in clamshell, and that
+// the user can exit the screen using a button in the OOBE screen to exit the
+// screen.
+IN_PROC_BROWSER_TEST_F(MarketingOptInScreenTest,
+                       ExitScreenUsingButtonInClamshell) {
+  ShowMarketingOptInScreen();
+  OobeScreenWaiter(MarketingOptInScreenView::kScreenId).Wait();
+  ash::ShellTestApi().SetTabletModeEnabledForTest(false);
+
+  // When not in tablet mode, the shelf gesture detection should be disabled,
+  // and the user should be able to exit the screen using "next" button in the
+  // screen.
+  EXPECT_FALSE(ash::ShelfTestApi().HasLoginShelfGestureHandler());
+  test::OobeJS()
+      .CreateVisibilityWaiter(
+          true, {"marketing-opt-in", "marketing-opt-in-next-button"})
+      ->Wait();
+  test::OobeJS().TapOnPath(
+      {"marketing-opt-in", "marketing-opt-in-next-button"});
+
+  WaitForScreenExit();
+  EXPECT_FALSE(ash::ShelfTestApi().HasLoginShelfGestureHandler());
+}
+
+// Tests that enabling tablet mode while on the screen will enable login shelf
+// gestures as well.
+IN_PROC_BROWSER_TEST_F(MarketingOptInScreenTest,
+                       FlingFromGestureEnabledOnTabletModeEnter) {
+  ShowMarketingOptInScreen();
+  OobeScreenWaiter(MarketingOptInScreenView::kScreenId).Wait();
+  ash::ShellTestApi().SetTabletModeEnabledForTest(false);
+
+  EXPECT_FALSE(ash::ShelfTestApi().HasLoginShelfGestureHandler());
+  test::OobeJS()
+      .CreateVisibilityWaiter(
+          true, {"marketing-opt-in", "marketing-opt-in-next-button"})
+      ->Wait();
+
+  // Enter tablet mode and verify shelf gesture detection gets re-enabled.
+  ash::ShellTestApi().SetTabletModeEnabledForTest(true);
+  ASSERT_TRUE(ash::ShelfTestApi().HasLoginShelfGestureHandler());
+
+  SimulateFlingFromShelf();
+  WaitForScreenExit();
+  EXPECT_FALSE(ash::ShelfTestApi().HasLoginShelfGestureHandler());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/session/chrome_session_manager.cc b/chrome/browser/chromeos/login/session/chrome_session_manager.cc
index 430b63d..2925e8f 100644
--- a/chrome/browser/chromeos/login/session/chrome_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/chrome_session_manager.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/chromeos/login/login_wizard.h"
 #include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h"
 #include "chrome/browser/chromeos/login/screens/sync_consent_screen.h"
+#include "chrome/browser/chromeos/login/session/user_session_initializer.h"
 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
@@ -145,19 +146,8 @@
       return;
     }
 
-    user_session_mgr->InitRlz(user_profile);
-    user_session_mgr->InitializeCerts(user_profile);
-    user_session_mgr->InitializeCRLSetFetcher(user);
-    user_session_mgr->InitializeCertificateTransparencyComponents(user);
-
     ProfileHelper::Get()->ProfileStartup(user_profile);
 
-    user_session_mgr->InitializePrimaryProfileServices(user_profile, user);
-
-    if (user->GetType() == user_manager::USER_TYPE_CHILD) {
-      user_session_mgr->InitializeChildUserServices(user_profile);
-    }
-
     user_session_mgr->NotifyUserProfileLoaded(user_profile, user);
 
     // This call will set session state to SESSION_STATE_ACTIVE (same one).
@@ -196,8 +186,14 @@
 }  // namespace
 
 ChromeSessionManager::ChromeSessionManager()
-    : oobe_configuration_(std::make_unique<OobeConfiguration>()) {}
-ChromeSessionManager::~ChromeSessionManager() {}
+    : oobe_configuration_(std::make_unique<OobeConfiguration>()),
+      user_session_initializer_(std::make_unique<UserSessionInitializer>()) {
+  AddObserver(user_session_initializer_.get());
+}
+
+ChromeSessionManager::~ChromeSessionManager() {
+  RemoveObserver(user_session_initializer_.get());
+}
 
 void ChromeSessionManager::Initialize(
     const base::CommandLine& parsed_command_line,
diff --git a/chrome/browser/chromeos/login/session/chrome_session_manager.h b/chrome/browser/chromeos/login/session/chrome_session_manager.h
index bb3fe5fb..386fd1a 100644
--- a/chrome/browser/chromeos/login/session/chrome_session_manager.h
+++ b/chrome/browser/chromeos/login/session/chrome_session_manager.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/chromeos/login/oobe_configuration.h"
+#include "chrome/browser/chromeos/login/session/user_session_initializer.h"
 #include "components/session_manager/core/session_manager.h"
 
 namespace base {
@@ -43,6 +44,7 @@
 
  private:
   std::unique_ptr<chromeos::OobeConfiguration> oobe_configuration_;
+  std::unique_ptr<UserSessionInitializer> user_session_initializer_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeSessionManager);
 };
diff --git a/chrome/browser/chromeos/login/session/user_session_initializer.cc b/chrome/browser/chromeos/login/session/user_session_initializer.cc
new file mode 100644
index 0000000..7f0d74d
--- /dev/null
+++ b/chrome/browser/chromeos/login/session/user_session_initializer.cc
@@ -0,0 +1,245 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/session/user_session_initializer.h"
+
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/system/sys_info.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part_chromeos.h"
+#include "chrome/browser/chromeos/arc/session/arc_service_launcher.h"
+#include "chrome/browser/chromeos/child_accounts/child_status_reporting_service_factory.h"
+#include "chrome/browser/chromeos/child_accounts/child_user_service_factory.h"
+#include "chrome/browser/chromeos/child_accounts/screen_time_controller_factory.h"
+#include "chrome/browser/chromeos/crostini/crostini_manager.h"
+#include "chrome/browser/chromeos/lock_screen_apps/state_controller.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/policy/app_install_event_log_manager_wrapper.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/component_updater/crl_set_component_installer.h"
+#include "chrome/browser/component_updater/sth_set_component_remover.h"
+#include "chrome/browser/google/google_brand_chromeos.h"
+#include "chrome/browser/net/nss_context.h"
+#include "chrome/common/pref_names.h"
+#include "chromeos/network/network_cert_loader.h"
+#include "chromeos/tpm/install_attributes.h"
+#include "components/prefs/pref_service.h"
+
+#if BUILDFLAG(ENABLE_RLZ)
+#include "chrome/browser/rlz/chrome_rlz_tracker_delegate.h"
+#include "components/rlz/rlz_tracker.h"
+#endif
+
+namespace chromeos {
+
+namespace {
+UserSessionInitializer* g_instance = nullptr;
+
+#if BUILDFLAG(ENABLE_RLZ)
+// Flag file that disables RLZ tracking, when present.
+const base::FilePath::CharType kRLZDisabledFlagName[] =
+    FILE_PATH_LITERAL(".rlz_disabled");
+
+base::FilePath GetRlzDisabledFlagPath() {
+  base::FilePath homedir;
+  base::PathService::Get(base::DIR_HOME, &homedir);
+  return homedir.Append(kRLZDisabledFlagName);
+}
+
+UserSessionInitializer::RlzInitParams CollectRlzParams() {
+  UserSessionInitializer::RlzInitParams params;
+  params.disabled = base::PathExists(GetRlzDisabledFlagPath());
+  params.time_since_oobe_completion =
+      chromeos::StartupUtils::GetTimeSinceOobeFlagFileCreation();
+  return params;
+}
+#endif
+
+// Callback to GetNSSCertDatabaseForProfile. It passes the user-specific NSS
+// database to NetworkCertLoader. It must be called for primary user only.
+void OnGetNSSCertDatabaseForUser(net::NSSCertDatabase* database) {
+  if (!NetworkCertLoader::IsInitialized())
+    return;
+
+  NetworkCertLoader::Get()->SetUserNSSDB(database);
+}
+
+}  // namespace
+
+UserSessionInitializer::UserSessionInitializer() {
+  DCHECK(!g_instance);
+  g_instance = this;
+}
+
+UserSessionInitializer::~UserSessionInitializer() {
+  DCHECK(g_instance);
+  g_instance = nullptr;
+}
+
+// static
+UserSessionInitializer* UserSessionInitializer::Get() {
+  return g_instance;
+}
+
+void UserSessionInitializer::OnUserProfileLoaded(const AccountId& account_id) {
+  Profile* profile = ProfileHelper::Get()->GetProfileByAccountId(account_id);
+  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
+
+  if (user_manager::UserManager::Get()->GetPrimaryUser() == user) {
+    InitRlz(profile);
+    InitializeCerts(profile);
+    InitializeCRLSetFetcher(user);
+    InitializeCertificateTransparencyComponents(user);
+    InitializePrimaryProfileServices(profile, user);
+  }
+
+  if (user->GetType() == user_manager::USER_TYPE_CHILD)
+    InitializeChildUserServices(profile);
+}
+
+void UserSessionInitializer::InitializeChildUserServices(Profile* profile) {
+  ChildStatusReportingServiceFactory::GetForBrowserContext(profile);
+  ChildUserServiceFactory::GetForBrowserContext(profile);
+  ScreenTimeControllerFactory::GetForBrowserContext(profile);
+}
+
+void UserSessionInitializer::InitRlz(Profile* profile) {
+#if BUILDFLAG(ENABLE_RLZ)
+  // Initialize the brand code in the local prefs if it does not exist yet or
+  // if it is empty.  The latter is to correct a problem in older builds where
+  // an empty brand code would be persisted if the first login after OOBE was
+  // a guest session.
+  if (!g_browser_process->local_state()->HasPrefPath(prefs::kRLZBrand) ||
+      g_browser_process->local_state()
+          ->Get(prefs::kRLZBrand)
+          ->GetString()
+          .empty()) {
+    // Read brand code asynchronously from an OEM data and repost ourselves.
+    google_brand::chromeos::InitBrand(base::Bind(
+        &UserSessionInitializer::InitRlz, weak_factory_.GetWeakPtr(), profile));
+    return;
+  }
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(&CollectRlzParams),
+      base::BindOnce(&UserSessionInitializer::InitRlzImpl,
+                     weak_factory_.GetWeakPtr(), profile));
+#endif
+}
+
+void UserSessionInitializer::InitializeCerts(Profile* profile) {
+  // Now that the user profile has been initialized
+  // |GetNSSCertDatabaseForProfile| is safe to be used.
+  if (NetworkCertLoader::IsInitialized() &&
+      base::SysInfo::IsRunningOnChromeOS()) {
+    GetNSSCertDatabaseForProfile(profile,
+                                 base::Bind(&OnGetNSSCertDatabaseForUser));
+  }
+}
+
+void UserSessionInitializer::InitializeCRLSetFetcher(
+    const user_manager::User* user) {
+  const std::string username_hash = user->username_hash();
+  if (!username_hash.empty()) {
+    base::FilePath path =
+        ProfileHelper::GetProfilePathByUserIdHash(username_hash);
+    component_updater::ComponentUpdateService* cus =
+        g_browser_process->component_updater();
+    if (cus)
+      component_updater::RegisterCRLSetComponent(cus, path);
+  }
+}
+
+void UserSessionInitializer::InitializeCertificateTransparencyComponents(
+    const user_manager::User* user) {
+  const std::string username_hash = user->username_hash();
+  if (!username_hash.empty()) {
+    base::FilePath path =
+        ProfileHelper::GetProfilePathByUserIdHash(username_hash);
+    component_updater::DeleteLegacySTHSet(path);
+  }
+}
+
+void UserSessionInitializer::InitializePrimaryProfileServices(
+    Profile* profile,
+    const user_manager::User* user) {
+  lock_screen_apps::StateController::Get()->SetPrimaryProfile(profile);
+
+  if (user->GetType() == user_manager::USER_TYPE_REGULAR) {
+    // App install logs are uploaded via the user's communication channel with
+    // the management server. This channel exists for regular users only.
+    // The |AppInstallEventLogManagerWrapper| manages its own lifetime and
+    // self-destructs on logout.
+    policy::AppInstallEventLogManagerWrapper::CreateForProfile(profile);
+  }
+  arc::ArcServiceLauncher::Get()->OnPrimaryUserProfilePrepared(profile);
+
+  crostini::CrostiniManager* crostini_manager =
+      crostini::CrostiniManager::GetForProfile(profile);
+  if (crostini_manager)
+    crostini_manager->MaybeUpgradeCrostini();
+
+  g_browser_process->platform_part()->InitializePrimaryProfileServices(profile);
+}
+
+void UserSessionInitializer::InitRlzImpl(Profile* profile,
+                                         const RlzInitParams& params) {
+#if BUILDFLAG(ENABLE_RLZ)
+  // If RLZ is disabled then clear the brand for the session.
+  //
+  // RLZ is disabled if disabled explicitly OR if the device's enrollment
+  // state is not yet known. The device's enrollment state is definitively
+  // known once the device is locked. Note that for enrolled devices, the
+  // enrollment login locks the device.
+  //
+  // There the following cases to consider when a session starts:
+  //
+  // 1) This is a regular session.
+  // 1a) The device is LOCKED. Thus, the enrollment state is KNOWN.
+  // 1b) The device is NOT LOCKED. This should only happen on the first
+  //     regular login (due to lock race condition with this code) if the
+  //     device is NOT enrolled; thus, the enrollment state is also KNOWN.
+  //
+  // 2) This is a guest session.
+  // 2a) The device is LOCKED. Thus, the enrollment state is KNOWN.
+  // 2b) The device is NOT locked. This should happen if ONLY Guest mode
+  //     sessions have ever been used on this device. This is the only
+  //     situation where the enrollment state is NOT KNOWN at this point.
+
+  PrefService* local_state = g_browser_process->local_state();
+  if (params.disabled || (profile->IsGuestSession() &&
+                          !InstallAttributes::Get()->IsDeviceLocked())) {
+    // Empty brand code means an organic install (no RLZ pings are sent).
+    google_brand::chromeos::ClearBrandForCurrentSession();
+  }
+  if (params.disabled != local_state->GetBoolean(prefs::kRLZDisabled)) {
+    // When switching to RLZ enabled/disabled state, clear all recorded events.
+    rlz::RLZTracker::ClearRlzState();
+    local_state->SetBoolean(prefs::kRLZDisabled, params.disabled);
+  }
+  // Init the RLZ library.
+  int ping_delay = profile->GetPrefs()->GetInteger(prefs::kRlzPingDelaySeconds);
+  // Negative ping delay means to send ping immediately after a first search is
+  // recorded.
+  bool send_ping_immediately = ping_delay < 0;
+  base::TimeDelta delay = base::TimeDelta::FromSeconds(abs(ping_delay)) -
+                          params.time_since_oobe_completion;
+  rlz::RLZTracker::SetRlzDelegate(
+      base::WrapUnique(new ChromeRLZTrackerDelegate));
+  rlz::RLZTracker::InitRlzDelayed(
+      user_manager::UserManager::Get()->IsCurrentUserNew(),
+      send_ping_immediately, delay,
+      ChromeRLZTrackerDelegate::IsGoogleDefaultSearch(profile),
+      ChromeRLZTrackerDelegate::IsGoogleHomepage(profile),
+      ChromeRLZTrackerDelegate::IsGoogleInStartpages(profile));
+#endif
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/session/user_session_initializer.h b/chrome/browser/chromeos/login/session/user_session_initializer.h
new file mode 100644
index 0000000..cbe2ed0
--- /dev/null
+++ b/chrome/browser/chromeos/login/session/user_session_initializer.h
@@ -0,0 +1,74 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SESSION_USER_SESSION_INITIALIZER_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_SESSION_USER_SESSION_INITIALIZER_H_
+
+#include "components/session_manager/core/session_manager_observer.h"
+#include "components/user_manager/user.h"
+
+class Profile;
+
+namespace user_manager {
+class User;
+}
+
+namespace chromeos {
+
+class UserSessionInitializer : public session_manager::SessionManagerObserver {
+ public:
+  // Parameters to use when initializing the RLZ library.  These fields need
+  // to be retrieved from a blocking task and this structure is used to pass
+  // the data.
+  struct RlzInitParams {
+    // Set to true if RLZ is disabled.
+    bool disabled;
+
+    // The elapsed time since the device went through the OOBE.  This can
+    // be a very long time.
+    base::TimeDelta time_since_oobe_completion;
+  };
+
+  UserSessionInitializer();
+  UserSessionInitializer(const UserSessionInitializer&) = delete;
+  UserSessionInitializer& operator=(const UserSessionInitializer&) = delete;
+  ~UserSessionInitializer() override;
+
+  // Returns UserSessionInitializer instance.
+  static UserSessionInitializer* Get();
+
+  // session_manager::SessionManagerObserver:
+  void OnUserProfileLoaded(const AccountId& account_id) override;
+
+  // Initialize child user profile services that depend on the policy.
+  void InitializeChildUserServices(Profile* profile);
+
+ private:
+  // Initialize RLZ.
+  void InitRlz(Profile* profile);
+
+  // Get the NSS cert database for the user represented with |profile|
+  // and start certificate loader with it.
+  void InitializeCerts(Profile* profile);
+
+  // Starts loading CRL set.
+  void InitializeCRLSetFetcher(const user_manager::User* user);
+
+  // Initializes Certificate Transparency-related components.
+  void InitializeCertificateTransparencyComponents(
+      const user_manager::User* user);
+
+  // Initialize all services that need the primary profile.
+  void InitializePrimaryProfileServices(Profile* profile,
+                                        const user_manager::User* user);
+
+  // Initializes RLZ. If |disabled| is true, RLZ pings are disabled.
+  void InitRlzImpl(Profile* profile, const RlzInitParams& params);
+
+  base::WeakPtrFactory<UserSessionInitializer> weak_factory_{this};
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_SESSION_USER_SESSION_INITIALIZER_H_
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 5b80bf9e..1e68513 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -20,7 +20,6 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
-#include "base/files/file_util.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -31,7 +30,6 @@
 #include "base/strings/string16.h"
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
-#include "base/task/thread_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/about_flags.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
@@ -42,17 +40,11 @@
 #include "chrome/browser/chromeos/account_manager/account_manager_util.h"
 #include "chrome/browser/chromeos/arc/arc_migration_guide_notification.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
-#include "chrome/browser/chromeos/arc/session/arc_service_launcher.h"
 #include "chrome/browser/chromeos/base/locale_util.h"
 #include "chrome/browser/chromeos/boot_times_recorder.h"
 #include "chrome/browser/chromeos/child_accounts/child_policy_observer.h"
-#include "chrome/browser/chromeos/child_accounts/child_status_reporting_service_factory.h"
-#include "chrome/browser/chromeos/child_accounts/child_user_service_factory.h"
-#include "chrome/browser/chromeos/child_accounts/screen_time_controller_factory.h"
-#include "chrome/browser/chromeos/crostini/crostini_manager.h"
 #include "chrome/browser/chromeos/first_run/first_run.h"
 #include "chrome/browser/chromeos/first_run/goodies_displayer.h"
-#include "chrome/browser/chromeos/lock_screen_apps/state_controller.h"
 #include "chrome/browser/chromeos/logging.h"
 #include "chrome/browser/chromeos/login/auth/chrome_cryptohome_authenticator.h"
 #include "chrome/browser/chromeos/login/chrome_restart_request.h"
@@ -69,6 +61,7 @@
 #include "chrome/browser/chromeos/login/saml/saml_offline_signin_limiter_factory.h"
 #include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h"
 #include "chrome/browser/chromeos/login/screens/sync_consent_screen.h"
+#include "chrome/browser/chromeos/login/session/user_session_initializer.h"
 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.h"
 #include "chrome/browser/chromeos/login/signin/token_handle_fetcher.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
@@ -78,7 +71,6 @@
 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
 #include "chrome/browser/chromeos/login/users/supervised_user_manager.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
-#include "chrome/browser/chromeos/policy/app_install_event_log_manager_wrapper.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/policy/tpm_auto_update_mode_policy_handler.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -87,13 +79,9 @@
 #include "chrome/browser/chromeos/tether/tether_service.h"
 #include "chrome/browser/chromeos/tpm_firmware_update_notification.h"
 #include "chrome/browser/chromeos/u2f_notification.h"
-#include "chrome/browser/component_updater/crl_set_component_installer.h"
-#include "chrome/browser/component_updater/sth_set_component_remover.h"
 #include "chrome/browser/first_run/first_run.h"
-#include "chrome/browser/google/google_brand_chromeos.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
-#include "chrome/browser/net/nss_context.h"
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
@@ -131,11 +119,9 @@
 #include "chromeos/login/auth/challenge_response/known_user_pref_utils.h"
 #include "chromeos/login/auth/stub_authenticator_builder.h"
 #include "chromeos/login/session/session_termination_manager.h"
-#include "chromeos/network/network_cert_loader.h"
 #include "chromeos/network/portal_detector/network_portal_detector.h"
 #include "chromeos/network/portal_detector/network_portal_detector_strategy.h"
 #include "chromeos/settings/cros_settings_names.h"
-#include "chromeos/tpm/install_attributes.h"
 #include "components/account_id/account_id.h"
 #include "components/component_updater/component_updater_service.h"
 #include "components/flags_ui/pref_service_flags_storage.h"
@@ -174,11 +160,6 @@
 #include "ui/message_center/public/cpp/notifier_id.h"
 #include "url/gurl.h"
 
-#if BUILDFLAG(ENABLE_RLZ)
-#include "chrome/browser/rlz/chrome_rlz_tracker_delegate.h"
-#include "components/rlz/rlz_tracker.h"
-#endif
-
 using signin::ConsentLevel;
 
 namespace chromeos {
@@ -298,27 +279,6 @@
   prefs->SetBoolean(prefs::kLanguageShouldMergeInputMethods, true);
 }
 
-#if BUILDFLAG(ENABLE_RLZ)
-// Flag file that disables RLZ tracking, when present.
-const base::FilePath::CharType kRLZDisabledFlagName[] =
-    FILE_PATH_LITERAL(".rlz_disabled");
-
-base::FilePath GetRlzDisabledFlagPath() {
-  base::FilePath homedir;
-  base::PathService::Get(base::DIR_HOME, &homedir);
-  return homedir.Append(kRLZDisabledFlagName);
-}
-#endif
-
-// Callback to GetNSSCertDatabaseForProfile. It passes the user-specific NSS
-// database to NetworkCertLoader. It must be called for primary user only.
-void OnGetNSSCertDatabaseForUser(net::NSSCertDatabase* database) {
-  if (!NetworkCertLoader::IsInitialized())
-    return;
-
-  NetworkCertLoader::Get()->SetUserNSSDB(database);
-}
-
 // Returns new CommandLine with per-user flags.
 base::CommandLine CreatePerSessionCommandLine(Profile* profile) {
   base::CommandLine user_flags(base::CommandLine::NO_PROGRAM);
@@ -402,16 +362,6 @@
              ::switches::kTestType);
 }
 
-#if BUILDFLAG(ENABLE_RLZ)
-UserSessionManager::RlzInitParams CollectRlzParams() {
-  UserSessionManager::RlzInitParams params;
-  params.disabled = base::PathExists(GetRlzDisabledFlagPath());
-  params.time_since_oobe_completion =
-      chromeos::StartupUtils::GetTimeSinceOobeFlagFileCreation();
-  return params;
-}
-#endif
-
 bool IsOnlineSignin(const UserContext& user_context) {
   return user_context.GetAuthFlow() == UserContext::AUTH_FLOW_GAIA_WITH_SAML ||
          user_context.GetAuthFlow() == UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML;
@@ -695,31 +645,6 @@
   return user_sessions_restore_in_progress_;
 }
 
-void UserSessionManager::InitRlz(Profile* profile) {
-#if BUILDFLAG(ENABLE_RLZ)
-  // Initialize the brand code in the local prefs if it does not exist yet or
-  // if it is empty.  The latter is to correct a problem in older builds where
-  // an empty brand code would be persisted if the first login after OOBE was
-  // a guest session.
-  if (!g_browser_process->local_state()->HasPrefPath(prefs::kRLZBrand) ||
-      g_browser_process->local_state()
-          ->Get(prefs::kRLZBrand)
-          ->GetString()
-          .empty()) {
-    // Read brand code asynchronously from an OEM data and repost ourselves.
-    google_brand::chromeos::InitBrand(
-        base::Bind(&UserSessionManager::InitRlz, AsWeakPtr(), profile));
-    return;
-  }
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE,
-      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-      base::BindOnce(&CollectRlzParams),
-      base::BindOnce(&UserSessionManager::InitRlzImpl, AsWeakPtr(), profile));
-#endif
-}
-
 void UserSessionManager::InitNonKioskExtensionFeaturesSessionType(
     const user_manager::User* user) {
   // Kiosk session should be set as part of kiosk user session initialization
@@ -963,16 +888,19 @@
       IdentityManagerFactory::GetForProfile(user_profile);
   switch (state) {
     case OAuth2LoginManager::SESSION_RESTORE_DONE:
-      if (identity_manager) {
-        // Now SESSION_RESTORE_DONE state means that primary account has a valid
-        // token.
-        DCHECK(
-            !identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
-                identity_manager
-                    ->GetPrimaryAccountInfo(ConsentLevel::kNotRequired)
-                    .account_id));
-      }
-      user_status = user_manager::User::OAUTH2_TOKEN_STATUS_VALID;
+      // Session restore done does not always mean valid token because the
+      // merge session operation could be skipped when the first account in
+      // Gaia cookies matches the primary account in TokenService. However
+      // the token could still be invalid in some edge cases. See
+      // http://crbug.com/760610
+      user_status =
+          (identity_manager &&
+           identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
+               identity_manager
+                   ->GetPrimaryAccountInfo(ConsentLevel::kNotRequired)
+                   .account_id))
+              ? user_manager::User::OAUTH2_TOKEN_STATUS_INVALID
+              : user_manager::User::OAUTH2_TOKEN_STATUS_VALID;
       break;
     case OAuth2LoginManager::SESSION_RESTORE_FAILED:
       user_status = user_manager::User::OAUTH2_TOKEN_STATUS_INVALID;
@@ -1688,11 +1616,6 @@
 
   // Initialize various services only for primary user.
   if (user_manager->GetPrimaryUser() == user) {
-    InitRlz(profile);
-    InitializeCerts(profile);
-    InitializeCRLSetFetcher(user);
-    InitializeCertificateTransparencyComponents(user);
-    InitializePrimaryProfileServices(profile, user);
     StartTetherServiceIfPossible(profile);
 
     // PrefService is ready, check whether we need to force a VPN connection.
@@ -1738,7 +1661,6 @@
           kWaitForChildPolicyTimeout);
       return;
     }
-    InitializeChildUserServices(profile);
   }
 
   InitializeBrowser(profile);
@@ -1900,120 +1822,6 @@
                                 user_context_.GetAccessToken());
 }
 
-void UserSessionManager::InitRlzImpl(Profile* profile,
-                                     const RlzInitParams& params) {
-#if BUILDFLAG(ENABLE_RLZ)
-  // If RLZ is disabled then clear the brand for the session.
-  //
-  // RLZ is disabled if disabled explicitly OR if the device's enrollment
-  // state is not yet known. The device's enrollment state is definitively
-  // known once the device is locked. Note that for enrolled devices, the
-  // enrollment login locks the device.
-  //
-  // There the following cases to consider when a session starts:
-  //
-  // 1) This is a regular session.
-  // 1a) The device is LOCKED. Thus, the enrollment state is KNOWN.
-  // 1b) The device is NOT LOCKED. This should only happen on the first
-  //     regular login (due to lock race condition with this code) if the
-  //     device is NOT enrolled; thus, the enrollment state is also KNOWN.
-  //
-  // 2) This is a guest session.
-  // 2a) The device is LOCKED. Thus, the enrollment state is KNOWN.
-  // 2b) The device is NOT locked. This should happen if ONLY Guest mode
-  //     sessions have ever been used on this device. This is the only
-  //     situation where the enrollment state is NOT KNOWN at this point.
-
-  PrefService* local_state = g_browser_process->local_state();
-  if (params.disabled || (profile->IsGuestSession() &&
-                          !InstallAttributes::Get()->IsDeviceLocked())) {
-    // Empty brand code means an organic install (no RLZ pings are sent).
-    google_brand::chromeos::ClearBrandForCurrentSession();
-  }
-  if (params.disabled != local_state->GetBoolean(prefs::kRLZDisabled)) {
-    // When switching to RLZ enabled/disabled state, clear all recorded events.
-    rlz::RLZTracker::ClearRlzState();
-    local_state->SetBoolean(prefs::kRLZDisabled, params.disabled);
-  }
-  // Init the RLZ library.
-  int ping_delay = profile->GetPrefs()->GetInteger(prefs::kRlzPingDelaySeconds);
-  // Negative ping delay means to send ping immediately after a first search is
-  // recorded.
-  bool send_ping_immediately = ping_delay < 0;
-  base::TimeDelta delay = base::TimeDelta::FromSeconds(abs(ping_delay)) -
-                          params.time_since_oobe_completion;
-  rlz::RLZTracker::SetRlzDelegate(
-      base::WrapUnique(new ChromeRLZTrackerDelegate));
-  rlz::RLZTracker::InitRlzDelayed(
-      user_manager::UserManager::Get()->IsCurrentUserNew(),
-      send_ping_immediately, delay,
-      ChromeRLZTrackerDelegate::IsGoogleDefaultSearch(profile),
-      ChromeRLZTrackerDelegate::IsGoogleHomepage(profile),
-      ChromeRLZTrackerDelegate::IsGoogleInStartpages(profile));
-#endif
-}
-
-void UserSessionManager::InitializeCerts(Profile* profile) {
-  // Now that the user profile has been initialized
-  // |GetNSSCertDatabaseForProfile| is safe to be used.
-  if (NetworkCertLoader::IsInitialized() &&
-      base::SysInfo::IsRunningOnChromeOS()) {
-    GetNSSCertDatabaseForProfile(profile,
-                                 base::Bind(&OnGetNSSCertDatabaseForUser));
-  }
-}
-
-void UserSessionManager::InitializeCRLSetFetcher(
-    const user_manager::User* user) {
-  const std::string username_hash = user->username_hash();
-  if (!username_hash.empty()) {
-    base::FilePath path =
-        ProfileHelper::GetProfilePathByUserIdHash(username_hash);
-    component_updater::ComponentUpdateService* cus =
-        g_browser_process->component_updater();
-    if (cus)
-      component_updater::RegisterCRLSetComponent(cus, path);
-  }
-}
-
-void UserSessionManager::InitializeCertificateTransparencyComponents(
-    const user_manager::User* user) {
-  const std::string username_hash = user->username_hash();
-  if (!username_hash.empty()) {
-    base::FilePath path =
-        ProfileHelper::GetProfilePathByUserIdHash(username_hash);
-    component_updater::DeleteLegacySTHSet(path);
-  }
-}
-
-void UserSessionManager::InitializeChildUserServices(Profile* profile) {
-  ChildStatusReportingServiceFactory::GetForBrowserContext(profile);
-  ChildUserServiceFactory::GetForBrowserContext(profile);
-  ScreenTimeControllerFactory::GetForBrowserContext(profile);
-}
-
-void UserSessionManager::InitializePrimaryProfileServices(
-    Profile* profile,
-    const user_manager::User* user) {
-  lock_screen_apps::StateController::Get()->SetPrimaryProfile(profile);
-
-  if (user->GetType() == user_manager::USER_TYPE_REGULAR) {
-    // App install logs are uploaded via the user's communication channel with
-    // the management server. This channel exists for regular users only.
-    // The |AppInstallEventLogManagerWrapper| manages its own lifetime and
-    // self-destructs on logout.
-    policy::AppInstallEventLogManagerWrapper::CreateForProfile(profile);
-  }
-  arc::ArcServiceLauncher::Get()->OnPrimaryUserProfilePrepared(profile);
-
-  crostini::CrostiniManager* crostini_manager =
-      crostini::CrostiniManager::GetForProfile(profile);
-  if (crostini_manager)
-    crostini_manager->MaybeUpgradeCrostini();
-
-  g_browser_process->platform_part()->InitializePrimaryProfileServices(profile);
-}
-
 void UserSessionManager::NotifyUserProfileLoaded(
     Profile* profile,
     const user_manager::User* user) {
@@ -2228,7 +2036,8 @@
 
   child_policy_observer_.reset();
 
-  InitializeChildUserServices(profile);
+  UserSessionInitializer::Get()->InitializeChildUserServices(profile);
+
   InitializeBrowser(profile);
 }
 
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.h b/chrome/browser/chromeos/login/session/user_session_manager.h
index ea3b713..a7751c7 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.h
+++ b/chrome/browser/chromeos/login/session/user_session_manager.h
@@ -132,18 +132,6 @@
     kPolicyAndFlagsAndKioskControl
   };
 
-  // Parameters to use when initializing the RLZ library.  These fields need
-  // to be retrieved from a blocking task and this structure is used to pass
-  // the data.
-  struct RlzInitParams {
-    // Set to true if RLZ is disabled.
-    bool disabled;
-
-    // The elapsed time since the device went through the OOBE.  This can
-    // be a very long time.
-    base::TimeDelta time_since_oobe_completion;
-  };
-
   // To keep track of which systems need the login password to be stored in the
   // kernel keyring.
   enum class PasswordConsumingService {
@@ -219,27 +207,6 @@
   // user sessions restoration is in progress.
   bool UserSessionsRestoreInProgress() const;
 
-  // Initialize RLZ.
-  void InitRlz(Profile* profile);
-
-  // Get the NSS cert database for the user represented with |profile|
-  // and start certificate loader with it.
-  void InitializeCerts(Profile* profile);
-
-  // Starts loading CRL set.
-  void InitializeCRLSetFetcher(const user_manager::User* user);
-
-  // Initializes Certificate Transparency-related components.
-  void InitializeCertificateTransparencyComponents(
-      const user_manager::User* user);
-
-  // Initialize child user profile services that depend on the policy.
-  void InitializeChildUserServices(Profile* profile);
-
-  // Initialize all services that need the primary profile.
-  void InitializePrimaryProfileServices(Profile* profile,
-                                        const user_manager::User* user);
-
   // Send the notification before creating the browser so additional objects
   // that need the profile (e.g. the launcher) can be created first.
   void NotifyUserProfileLoaded(Profile* profile,
@@ -481,9 +448,6 @@
   // Restores GAIA auth cookies for the created user profile from OAuth2 token.
   void RestoreAuthSessionImpl(Profile* profile, bool restore_from_auth_cookies);
 
-  // Initializes RLZ. If |disabled| is true, RLZ pings are disabled.
-  void InitRlzImpl(Profile* profile, const RlzInitParams& params);
-
   // If |user| is not a kiosk app, sets session type as seen by extensions
   // feature system according to |user|'s type.
   // The value should eventually be set for kiosk users, too - that's done as
diff --git a/chrome/browser/chromeos/login/signin/merge_session_navigation_throttle.cc b/chrome/browser/chromeos/login/signin/merge_session_navigation_throttle.cc
index 57ff3cb..7e8ce168 100644
--- a/chrome/browser/chromeos/login/signin/merge_session_navigation_throttle.cc
+++ b/chrome/browser/chromeos/login/signin/merge_session_navigation_throttle.cc
@@ -7,7 +7,6 @@
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/login/signin/merge_session_throttling_utils.h"
 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.h"
-#include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.cc b/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.cc
index 2cd43e5..0643dc6 100644
--- a/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.cc
+++ b/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.cc
@@ -14,7 +14,6 @@
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h"
 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/common/url_constants.h"
 #include "components/google/core/common/google_util.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
index 3fd1836..1d992f7e 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
@@ -701,6 +701,57 @@
           ->is_under_advanced_protection);
 }
 
+// Sets up a new user with stored refresh token.
+IN_PROC_BROWSER_TEST_F(OAuth2Test, PRE_SetInvalidTokenStatus) {
+  StartNewUserSession(/*wait_for_merge=*/true,
+                      /*is_under_advanced_protection=*/false);
+}
+
+// Tests that an auth error marks invalid auth token status despite
+// OAuth2LoginManager thinks merge session is done successfully
+IN_PROC_BROWSER_TEST_F(OAuth2Test, SetInvalidTokenStatus) {
+  RequestDeferrer list_accounts_request_deferer;
+  AddRequestDeferer("/ListAccounts", &list_accounts_request_deferer);
+
+  SetupGaiaServerForUnexpiredAccount();
+  SimulateNetworkOnline();
+
+  // Signs in as the existing user created in pre test.
+  ExistingUserController* const controller =
+      ExistingUserController::current_controller();
+  UserContext user_context(
+      user_manager::USER_TYPE_REGULAR,
+      AccountId::FromUserEmailGaiaId(kTestEmail, kTestGaiaId));
+  user_context.SetKey(Key(kTestAccountPassword));
+  controller->Login(user_context, SigninSpecifics());
+
+  // Wait until /ListAccounts request happens so that an auth error can be
+  // generated after user profile is available but before merge session
+  // finishes.
+  list_accounts_request_deferer.WaitForRequestToStart();
+
+  // Make sure that merge session is not finished.
+  OAuth2LoginManager* const login_manager =
+      OAuth2LoginManagerFactory::GetInstance()->GetForProfile(GetProfile());
+  ASSERT_NE(OAuth2LoginManager::SESSION_RESTORE_DONE, login_manager->state());
+
+  // Generate an auth error.
+  signin::SetInvalidRefreshTokenForAccount(
+      IdentityManagerFactory::GetInstance()->GetForProfile(GetProfile()),
+      PickAccountId(GetProfile(), kTestGaiaId, kTestEmail));
+
+  // Let go /ListAccounts request.
+  list_accounts_request_deferer.UnblockRequest();
+
+  // Wait for the session merge to finish with success.
+  WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE);
+
+  // User oauth2 token status should be marked as invalid because of auth error
+  // and regardless of the merge session outcome.
+  EXPECT_EQ(GetOAuthStatusFromLocalState(kTestEmail),
+            user_manager::User::OAUTH2_TOKEN_STATUS_INVALID);
+}
+
 constexpr char kGooglePageContent[] =
     "<html><title>Hello!</title><script>alert('hello');</script>"
     "<body>Hello Google!</body></html>";
@@ -709,7 +760,6 @@
 constexpr char kHelloPagePath[] = "/hello_google";
 constexpr char kRandomPagePath[] = "/non_google_page";
 constexpr char kMergeSessionPath[] = "/MergeSession";
-constexpr char kMultiLoginPath[] = "/oauth/multilogin";
 
 // FakeGoogle serves content of http://www.google.com/hello_google page for
 // merge session tests.
@@ -742,8 +792,7 @@
       http_response->set_code(net::HTTP_OK);
       http_response->set_content_type("text/html");
       http_response->set_content(kRandomPageContent);
-    } else if ((hang_merge_session_ && request_path == kMergeSessionPath) ||
-               (hang_merge_session_ && request_path == kMultiLoginPath)) {
+    } else if (hang_merge_session_ && request_path == kMergeSessionPath) {
       merge_session_event_.Signal();
       base::PostTask(FROM_HERE, {content::BrowserThread::UI},
                      base::BindOnce(&FakeGoogle::QuitMergeRunnerOnUIThread,
@@ -827,7 +876,6 @@
   void RegisterAdditionalRequestHandlers() override {
     OAuth2Test::RegisterAdditionalRequestHandlers();
     AddRequestDeferer("/MergeSession", &merge_session_deferer_);
-    AddRequestDeferer("/oauth/multilogin", &merge_session_deferer_);
 
     embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
         &FakeGoogle::HandleRequest, base::Unretained(&fake_google_)));
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc b/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
index a3034d4..038a994 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
@@ -9,13 +9,9 @@
 
 #include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/account_manager/account_manager_migrator.h"
-#include "chrome/browser/chromeos/account_manager/account_migration_runner.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/account_id_from_account_info.h"
-#include "chrome/browser/signin/account_reconcilor_factory.h"
 #include "chrome/browser/signin/chrome_device_id_helper.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chromeos/constants/chromeos_switches.h"
@@ -71,12 +67,6 @@
       << "Exchanging cookies for OAuth 2.0 tokens is no longer supported";
 
   SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_PREPARING);
-  account_reconcilor_observer_.Add(GetAccountReconcilor());
-
-  // AccountReconcilor may be disabled while migrations are running.
-  if (GetAccountReconcilor()->IsReconcileEnabled()) {
-    OnStateChanged(GetAccountReconcilor()->GetState());
-  }
 
   // Save passed OAuth2 refresh token.
   if (restore_strategy_ == RESTORE_FROM_PASSED_OAUTH2_REFRESH_TOKEN) {
@@ -91,10 +81,12 @@
 }
 
 void OAuth2LoginManager::RestoreSessionFromSavedTokens() {
-  SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_IN_PROGRESS);
   signin::IdentityManager* identity_manager = GetIdentityManager();
-  if (!identity_manager->HasAccountWithRefreshToken(
+  if (identity_manager->HasAccountWithRefreshToken(
           GetUnconsentedPrimaryAccountId())) {
+    VLOG(1) << "OAuth2 refresh token is already loaded.";
+    VerifySessionCookies();
+  } else {
     VLOG(1) << "Waiting for OAuth2 refresh token being loaded from database.";
 
     // Flag user with unknown token status in case there are no saved tokens
@@ -110,6 +102,10 @@
   }
 }
 
+void OAuth2LoginManager::Stop() {
+  login_verifier_.reset();
+}
+
 bool OAuth2LoginManager::SessionRestoreIsRunning() const {
   return state_ == SESSION_RESTORE_PREPARING ||
          state_ == SESSION_RESTORE_IN_PROGRESS;
@@ -137,29 +133,17 @@
   }
   // Only restore session cookies for the primary account in the profile.
   if (GetUnconsentedPrimaryAccountId() == account_info.account_id) {
+    // The refresh token has changed, so stop any ongoing actions that were
+    // based on the old refresh token.
+    Stop();
+
     // Token is loaded. Undo the flagging before token loading.
     DCHECK(!account_info.gaia.empty());
     user_manager::UserManager::Get()->SaveUserOAuthStatus(
         AccountIdFromAccountInfo(account_info),
         user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
-  }
-}
 
-void OAuth2LoginManager::OnStateChanged(
-    signin_metrics::AccountReconcilorState state) {
-  if (state == signin_metrics::AccountReconcilorState::ACCOUNT_RECONCILOR_OK) {
-    RecordSessionRestoreOutcome(SESSION_RESTORE_SUCCESS, SESSION_RESTORE_DONE);
-    GetAccountReconcilor()->RemoveObserver(this);
-  } else if (state ==
-             signin_metrics::AccountReconcilorState::ACCOUNT_RECONCILOR_ERROR) {
-    if (GetAccountReconcilor()->GetReconcileError().IsTransientError()) {
-      RecordSessionRestoreOutcome(SESSION_RESTORE_MERGE_SESSION_FAILED,
-                                  SESSION_RESTORE_CONNECTION_FAILED);
-    } else {
-      RecordSessionRestoreOutcome(SESSION_RESTORE_MERGE_SESSION_FAILED,
-                                  SESSION_RESTORE_FAILED);
-    }
-    GetAccountReconcilor()->RemoveObserver(this);
+    VerifySessionCookies();
   }
 }
 
@@ -167,10 +151,6 @@
   return IdentityManagerFactory::GetForProfile(user_profile_);
 }
 
-AccountReconcilor* OAuth2LoginManager::GetAccountReconcilor() {
-  return AccountReconcilorFactory::GetForProfile(user_profile_);
-}
-
 CoreAccountId OAuth2LoginManager::GetUnconsentedPrimaryAccountId() {
   // Use the primary ID whether or not the user has consented to browser sync.
   const CoreAccountId primary_account_id =
@@ -200,9 +180,103 @@
   OnRefreshTokenUpdatedForAccount(primary_account_info);
 }
 
+void OAuth2LoginManager::VerifySessionCookies() {
+  DCHECK(!login_verifier_.get());
+  login_verifier_ = std::make_unique<OAuth2LoginVerifier>(
+      this, GetIdentityManager(), GetUnconsentedPrimaryAccountId(),
+      oauthlogin_access_token_);
+
+  if (restore_strategy_ == RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN) {
+    login_verifier_->VerifyUserCookies();
+    return;
+  }
+
+  RestoreSessionCookies();
+}
+
+void OAuth2LoginManager::RestoreSessionCookies() {
+  SetSessionRestoreState(SESSION_RESTORE_IN_PROGRESS);
+  login_verifier_->VerifyProfileTokens();
+}
+
 void OAuth2LoginManager::Shutdown() {
   GetIdentityManager()->RemoveObserver(this);
-  account_reconcilor_observer_.RemoveAll();
+  login_verifier_.reset();
+}
+
+void OAuth2LoginManager::OnSessionMergeSuccess() {
+  VLOG(1) << "OAuth2 refresh and/or GAIA token verification succeeded.";
+  RecordSessionRestoreOutcome(SESSION_RESTORE_SUCCESS, SESSION_RESTORE_DONE);
+}
+
+void OAuth2LoginManager::OnSessionMergeFailure(bool connection_error) {
+  LOG(ERROR) << "OAuth2 refresh and GAIA token verification failed!"
+             << " connection_error: " << connection_error;
+  RecordSessionRestoreOutcome(SESSION_RESTORE_MERGE_SESSION_FAILED,
+                              connection_error
+                                  ? SESSION_RESTORE_CONNECTION_FAILED
+                                  : SESSION_RESTORE_FAILED);
+}
+
+void OAuth2LoginManager::OnListAccountsSuccess(
+    const std::vector<gaia::ListedAccount>& accounts) {
+  MergeVerificationOutcome outcome = POST_MERGE_SUCCESS;
+  // Let's analyze which accounts we see logged in here:
+  CoreAccountId user_account_id = GetUnconsentedPrimaryAccountId();
+  if (!accounts.empty()) {
+    bool found = false;
+    bool first = true;
+    for (std::vector<gaia::ListedAccount>::const_iterator iter =
+             accounts.begin();
+         iter != accounts.end(); ++iter) {
+      if (iter->id == user_account_id) {
+        found = iter->valid;
+        break;
+      }
+
+      first = false;
+    }
+
+    if (!found)
+      outcome = POST_MERGE_MISSING_PRIMARY_ACCOUNT;
+    else if (!first)
+      outcome = POST_MERGE_PRIMARY_NOT_FIRST_ACCOUNT;
+
+  } else {
+    outcome = POST_MERGE_NO_ACCOUNTS;
+  }
+
+  bool is_pre_merge = (state_ == SESSION_RESTORE_PREPARING);
+  RecordCookiesCheckOutcome(is_pre_merge, outcome);
+  // If the primary account is missing during the initial cookie freshness
+  // check, try to restore GAIA session cookies form the OAuth2 tokens.
+  if (is_pre_merge) {
+    if (outcome != POST_MERGE_SUCCESS &&
+        outcome != POST_MERGE_PRIMARY_NOT_FIRST_ACCOUNT) {
+      RestoreSessionCookies();
+    } else {
+      // We are done with this account, it's GAIA cookies are legit.
+      RecordSessionRestoreOutcome(SESSION_RESTORE_NOT_NEEDED,
+                                  SESSION_RESTORE_DONE);
+    }
+  }
+}
+
+void OAuth2LoginManager::OnListAccountsFailure(bool connection_error) {
+  bool is_pre_merge = (state_ == SESSION_RESTORE_PREPARING);
+  RecordCookiesCheckOutcome(is_pre_merge, connection_error
+                                              ? POST_MERGE_CONNECTION_FAILED
+                                              : POST_MERGE_VERIFICATION_FAILED);
+  if (is_pre_merge) {
+    if (!connection_error) {
+      // If we failed to get account list, our cookies might be stale so we
+      // need to attempt to restore them.
+      RestoreSessionCookies();
+    } else {
+      RecordSessionRestoreOutcome(SESSION_RESTORE_LISTACCOUNTS_FAILED,
+                                  SESSION_RESTORE_CONNECTION_FAILED);
+    }
+  }
 }
 
 void OAuth2LoginManager::RecordSessionRestoreOutcome(
@@ -213,6 +287,19 @@
   SetSessionRestoreState(state);
 }
 
+// static
+void OAuth2LoginManager::RecordCookiesCheckOutcome(
+    bool is_pre_merge,
+    MergeVerificationOutcome outcome) {
+  if (is_pre_merge) {
+    UMA_HISTOGRAM_ENUMERATION("OAuth2Login.PreMergeVerification", outcome,
+                              POST_MERGE_COUNT);
+  } else {
+    UMA_HISTOGRAM_ENUMERATION("OAuth2Login.PostMergeVerification", outcome,
+                              POST_MERGE_COUNT);
+  }
+}
+
 void OAuth2LoginManager::SetSessionRestoreState(
     OAuth2LoginManager::SessionRestoreState state) {
   if (state_ == state)
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_manager.h b/chrome/browser/chromeos/login/signin/oauth2_login_manager.h
index 37c4833..52947fd 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_login_manager.h
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_manager.h
@@ -13,8 +13,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
+#include "chrome/browser/chromeos/login/signin/oauth2_login_verifier.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/signin/core/browser/account_reconcilor.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 
 class GoogleServiceAuthError;
@@ -25,8 +25,8 @@
 // This class is responsible for restoring authenticated web sessions out of
 // OAuth2 refresh tokens or pre-authenticated cookie jar.
 class OAuth2LoginManager : public KeyedService,
-                           public signin::IdentityManager::Observer,
-                           public AccountReconcilor::Observer {
+                           public OAuth2LoginVerifier::Delegate,
+                           public signin::IdentityManager::Observer {
  public:
   // Session restore states.
   enum SessionRestoreState {
@@ -143,13 +143,17 @@
   // KeyedService implementation.
   void Shutdown() override;
 
+  // OAuth2LoginVerifier::Delegate overrides.
+  void OnSessionMergeSuccess() override;
+  void OnSessionMergeFailure(bool connection_error) override;
+  void OnListAccountsSuccess(
+      const std::vector<gaia::ListedAccount>& accounts) override;
+  void OnListAccountsFailure(bool connection_error) override;
+
   // signin::IdentityManager::Observer implementation:
   void OnRefreshTokenUpdatedForAccount(
       const CoreAccountInfo& account_info) override;
 
-  // AccountReconcilor::Observer implementation:
-  void OnStateChanged(signin_metrics::AccountReconcilorState state) override;
-
   // Signals delegate that authentication is completed, kicks off token fetching
   // process.
   void CompleteAuthentication();
@@ -157,10 +161,7 @@
   // Retrieves IdentityManager for |user_profile_|.
   signin::IdentityManager* GetIdentityManager();
 
-  // Retrieves AccountReconcilor for |user_profile_|.
-  AccountReconcilor* GetAccountReconcilor();
-
-  // Retrieves the primary account for |user_profile_|.
+  // Retrieves the primary account ID for |user_profile_|.
   CoreAccountId GetUnconsentedPrimaryAccountId();
 
   // Records |refresh_token_| to token service. The associated account id is
@@ -169,6 +170,10 @@
   // retrieve the associated account info.
   void StoreOAuth2Token();
 
+  // Checks if primary account sessions cookies are stale and restores them
+  // if needed.
+  void VerifySessionCookies();
+
   // Issue GAIA cookie recovery (MergeSession) from |refresh_token_|.
   void RestoreSessionCookies();
 
@@ -198,6 +203,8 @@
   // Whether there is pending TokenService::LoadCredentials call.
   bool pending_token_service_load_ = false;
 
+  std::unique_ptr<OAuth2LoginVerifier> login_verifier_;
+
   // OAuth2 refresh token.
   std::string refresh_token_;
 
@@ -213,9 +220,6 @@
   // can change the line below to base::ObserverList<Observer, true>.
   base::ObserverList<Observer, false>::Unchecked observer_list_;
 
-  ScopedObserver<AccountReconcilor, AccountReconcilor::Observer>
-      account_reconcilor_observer_{this};
-
   DISALLOW_COPY_AND_ASSIGN(OAuth2LoginManager);
 };
 
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.cc b/chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.cc
index 073e9182..a188264a 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.cc
@@ -6,7 +6,6 @@
 
 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/account_reconcilor_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 
@@ -17,7 +16,6 @@
           "OAuth2LoginManager",
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(IdentityManagerFactory::GetInstance());
-  DependsOn(AccountReconcilorFactory::GetInstance());
 }
 
 OAuth2LoginManagerFactory::~OAuth2LoginManagerFactory() {}
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc b/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc
new file mode 100644
index 0000000..eea35da
--- /dev/null
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc
@@ -0,0 +1,100 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/signin/oauth2_login_verifier.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "components/signin/public/identity_manager/accounts_cookie_mutator.h"
+#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+
+using content::BrowserThread;
+
+namespace chromeos {
+
+OAuth2LoginVerifier::OAuth2LoginVerifier(
+    OAuth2LoginVerifier::Delegate* delegate,
+    signin::IdentityManager* identity_manager,
+    const CoreAccountId& primary_account_id,
+    const std::string& oauthlogin_access_token)
+    : delegate_(delegate),
+      identity_manager_(identity_manager),
+      primary_account_id_(primary_account_id),
+      access_token_(oauthlogin_access_token) {
+  DCHECK(delegate);
+  identity_manager_->AddObserver(this);
+}
+
+OAuth2LoginVerifier::~OAuth2LoginVerifier() {
+  identity_manager_->RemoveObserver(this);
+}
+
+void OAuth2LoginVerifier::VerifyUserCookies() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  std::vector<gaia::ListedAccount> accounts;
+  std::vector<gaia::ListedAccount> signed_out_accounts;
+  signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info =
+      identity_manager_->GetAccountsInCookieJar();
+  if (accounts_in_cookie_jar_info.accounts_are_fresh) {
+    OnAccountsInCookieUpdated(
+        accounts_in_cookie_jar_info,
+        GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+  }
+}
+
+void OAuth2LoginVerifier::VerifyProfileTokens() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  signin::AccountsCookieMutator::AddAccountToCookieCompletedCallback
+      completion_callback =
+          base::BindOnce(&OAuth2LoginVerifier::OnAddAccountToCookieCompleted,
+                         weak_ptr_factory_.GetWeakPtr());
+  if (access_token_.empty()) {
+    identity_manager_->GetAccountsCookieMutator()->AddAccountToCookie(
+        primary_account_id_, gaia::GaiaSource::kOAuth2LoginVerifier,
+        std::move(completion_callback));
+  } else {
+    identity_manager_->GetAccountsCookieMutator()->AddAccountToCookieWithToken(
+        primary_account_id_, access_token_,
+        gaia::GaiaSource::kOAuth2LoginVerifier, std::move(completion_callback));
+  }
+}
+
+void OAuth2LoginVerifier::OnAddAccountToCookieCompleted(
+    const CoreAccountId& account_id,
+    const GoogleServiceAuthError& error) {
+  if (account_id != primary_account_id_)
+    return;
+
+  if (error.state() == GoogleServiceAuthError::State::NONE) {
+    VLOG(1) << "MergeSession successful.";
+    delegate_->OnSessionMergeSuccess();
+    return;
+  }
+
+  LOG(WARNING) << "Failed MergeSession request,"
+               << " error: " << error.state();
+  delegate_->OnSessionMergeFailure(error.IsTransientError());
+}
+
+void OAuth2LoginVerifier::OnAccountsInCookieUpdated(
+    const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
+    const GoogleServiceAuthError& error) {
+  if (error.state() == GoogleServiceAuthError::State::NONE) {
+    VLOG(1) << "ListAccounts successful.";
+    delegate_->OnListAccountsSuccess(
+        accounts_in_cookie_jar_info.signed_in_accounts);
+    return;
+  }
+
+  LOG(WARNING) << "Failed to get list of session accounts, "
+               << " error: " << error.state();
+  delegate_->OnListAccountsFailure(error.IsTransientError());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_verifier.h b/chrome/browser/chromeos/login/signin/oauth2_login_verifier.h
new file mode 100644
index 0000000..c009f6a0
--- /dev/null
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_verifier.h
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SIGNIN_OAUTH2_LOGIN_VERIFIER_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_SIGNIN_OAUTH2_LOGIN_VERIFIER_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+
+namespace chromeos {
+
+// Given GCMS and primary account id, this class verifies GAIA credentials
+// (SAPISID) and rebuild current session's cookie jar for the primary account.
+class OAuth2LoginVerifier : public signin::IdentityManager::Observer {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+    // Invoked when cookie session is successfully merged.
+    virtual void OnSessionMergeSuccess() = 0;
+
+    // Invoked when cookie session can not be merged.
+    virtual void OnSessionMergeFailure(bool connection_error) = 0;
+
+    // Invoked when account list is retrieved during post-merge session
+    // verification.
+    virtual void OnListAccountsSuccess(
+        const std::vector<gaia::ListedAccount>& accounts) = 0;
+
+    // Invoked when post-merge session verification fails.
+    virtual void OnListAccountsFailure(bool connection_error) = 0;
+  };
+
+  OAuth2LoginVerifier(OAuth2LoginVerifier::Delegate* delegate,
+                      signin::IdentityManager* identity_manager,
+                      const CoreAccountId& primary_account_id,
+                      const std::string& oauthlogin_access_token);
+  ~OAuth2LoginVerifier() override;
+
+  // Initiates verification of GAIA cookies in the cookie jar.
+  void VerifyUserCookies();
+
+  // Attempts to restore session from OAuth2 refresh token minting all necessary
+  // tokens along the way (OAuth2 access token, SID/LSID, GAIA service token).
+  void VerifyProfileTokens();
+
+ private:
+  // IdentityManager::Observer
+  void OnAccountsInCookieUpdated(
+      const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
+      const GoogleServiceAuthError& error) override;
+
+  void OnAddAccountToCookieCompleted(const CoreAccountId& account_id,
+                                     const GoogleServiceAuthError& error);
+
+  OAuth2LoginVerifier::Delegate* delegate_;
+  signin::IdentityManager* identity_manager_;
+  const CoreAccountId primary_account_id_;
+  const std::string access_token_;
+
+  base::WeakPtrFactory<OAuth2LoginVerifier> weak_ptr_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(OAuth2LoginVerifier);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_SIGNIN_OAUTH2_LOGIN_VERIFIER_H_
diff --git a/chrome/browser/enterprise_reporting/browser_report_generator.cc b/chrome/browser/enterprise_reporting/browser_report_generator.cc
index 3cda14f..063221f1 100644
--- a/chrome/browser/enterprise_reporting/browser_report_generator.cc
+++ b/chrome/browser/enterprise_reporting/browser_report_generator.cc
@@ -10,6 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
@@ -45,18 +46,16 @@
 BrowserReportGenerator::~BrowserReportGenerator() = default;
 
 void BrowserReportGenerator::Generate(ReportCallback callback) {
-  DCHECK(!callback_);
-  callback_ = std::move(callback);
-
   auto report = std::make_unique<em::BrowserReport>();
   GenerateBasicInfos(report.get());
   GenerateProfileInfos(report.get());
 
   // std::move is required here because the function completes the report
   // asynchronously.
-  GeneratePluginsIfNeeded(std::move(report));
+  GeneratePluginsIfNeeded(std::move(callback), std::move(report));
 }
 
+// static
 void BrowserReportGenerator::GenerateBasicInfos(em::BrowserReport* report) {
 #if !defined(OS_CHROMEOS)
   report->set_browser_version(version_info::GetVersionNumber());
@@ -66,6 +65,7 @@
   report->set_executable_path(GetExecutablePath());
 }
 
+// static
 void BrowserReportGenerator::GenerateProfileInfos(em::BrowserReport* report) {
   for (auto* entry : g_browser_process->profile_manager()
                          ->GetProfileAttributesStorage()
@@ -86,18 +86,20 @@
 }
 
 void BrowserReportGenerator::GeneratePluginsIfNeeded(
+    ReportCallback callback,
     std::unique_ptr<em::BrowserReport> report) {
 #if defined(OS_CHROMEOS) || !BUILDFLAG(ENABLE_PLUGINS)
-  std::move(callback_).Run(std::move(report));
+  std::move(callback).Run(std::move(report));
 #else
-  content::PluginService::GetInstance()->GetPlugins(
-      base::BindOnce(&BrowserReportGenerator::OnPluginsReady,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(report)));
+  content::PluginService::GetInstance()->GetPlugins(base::BindOnce(
+      &BrowserReportGenerator::OnPluginsReady, weak_ptr_factory_.GetWeakPtr(),
+      std::move(callback), std::move(report)));
 #endif
 }
 
 #if BUILDFLAG(ENABLE_PLUGINS)
 void BrowserReportGenerator::OnPluginsReady(
+    ReportCallback callback,
     std::unique_ptr<em::BrowserReport> report,
     const std::vector<content::WebPluginInfo>& plugins) {
   for (content::WebPluginInfo plugin : plugins) {
@@ -108,7 +110,7 @@
     plugin_info->set_description(base::UTF16ToUTF8(plugin.desc));
   }
 
-  std::move(callback_).Run(std::move(report));
+  std::move(callback).Run(std::move(report));
 }
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
 
diff --git a/chrome/browser/enterprise_reporting/browser_report_generator.h b/chrome/browser/enterprise_reporting/browser_report_generator.h
index c1c08fd..abe7c33 100644
--- a/chrome/browser/enterprise_reporting/browser_report_generator.h
+++ b/chrome/browser/enterprise_reporting/browser_report_generator.h
@@ -21,9 +21,7 @@
 
 namespace enterprise_reporting {
 
-/**
- * A report generator that collects Browser related information.
- */
+// A report generator that collects Browser related information.
 class BrowserReportGenerator {
  public:
   using ReportCallback =
@@ -32,31 +30,34 @@
   BrowserReportGenerator();
   ~BrowserReportGenerator();
 
-  // Generate a BrowserReport with following fields.
+  // Generates a BrowserReport with the following fields:
   // - browser_version, channel, executable_path
   // - user profiles: id, name, is_full_report (always be false).
   // - plugins: name, version, filename, description.
   void Generate(ReportCallback callback);
 
  private:
-  // Generate browser_version, channel, executable_path info in the given
+  // Generates browser_version, channel, executable_path info in the given
   // report instance.
-  void GenerateBasicInfos(em::BrowserReport* report);
+  static void GenerateBasicInfos(em::BrowserReport* report);
 
-  // Generate user profiles info in the given report instance.
-  void GenerateProfileInfos(em::BrowserReport* report);
+  // Generates user profiles info in the given report instance.
+  static void GenerateProfileInfos(em::BrowserReport* report);
 
-  // Generate plugin info in the given report instance, if needed. It requires
-  // the ownership of report instance to pass into ReportCallback method.
-  void GeneratePluginsIfNeeded(std::unique_ptr<em::BrowserReport> report);
+  // Generates plugin info in the given report instance, if needed. Passes
+  // |report| to |callback| either asynchronously when the plugin info is
+  // available, or synchronously otherwise.
+  void GeneratePluginsIfNeeded(ReportCallback callback,
+                               std::unique_ptr<em::BrowserReport> report);
 
 #if BUILDFLAG(ENABLE_PLUGINS)
-  void OnPluginsReady(std::unique_ptr<em::BrowserReport> report,
+  // Populates |report| with the plugin info in |plugins|, then passes the
+  // report to |callback|.
+  void OnPluginsReady(ReportCallback callback,
+                      std::unique_ptr<em::BrowserReport> report,
                       const std::vector<content::WebPluginInfo>& plugins);
 #endif
 
-  ReportCallback callback_;
-
   base::WeakPtrFactory<BrowserReportGenerator> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(BrowserReportGenerator);
diff --git a/chrome/browser/enterprise_reporting/report_generator.cc b/chrome/browser/enterprise_reporting/report_generator.cc
index b396877..ab19826 100644
--- a/chrome/browser/enterprise_reporting/report_generator.cc
+++ b/chrome/browser/enterprise_reporting/report_generator.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/files/file_path.h"
+#include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
@@ -33,28 +33,31 @@
 
 ReportGenerator::~ReportGenerator() = default;
 
-void ReportGenerator::Generate(ReportCallback callback) {
-  DCHECK(!callback_);
-  callback_ = std::move(callback);
-  CreateBasicRequest();
+void ReportGenerator::Generate(bool with_profiles, ReportCallback callback) {
+  CreateBasicRequest(std::make_unique<ReportRequest>(), with_profiles,
+                     std::move(callback));
 }
 
 void ReportGenerator::SetMaximumReportSizeForTesting(size_t size) {
   report_request_queue_generator_.SetMaximumReportSizeForTesting(size);
 }
 
-void ReportGenerator::CreateBasicRequest() {
+void ReportGenerator::CreateBasicRequest(
+    std::unique_ptr<ReportRequest> basic_request,
+    bool with_profiles,
+    ReportCallback callback) {
 #if defined(OS_CHROMEOS)
-  SetAndroidAppInfos();
+  SetAndroidAppInfos(basic_request.get());
 #else
-  basic_request_.set_computer_name(this->GetMachineName());
-  basic_request_.set_os_user_name(GetOSUserName());
-  basic_request_.set_serial_number(GetSerialNumber());
-  basic_request_.set_allocated_os_report(GetOSReport().release());
+  basic_request->set_computer_name(this->GetMachineName());
+  basic_request->set_os_user_name(GetOSUserName());
+  basic_request->set_serial_number(GetSerialNumber());
+  basic_request->set_allocated_os_report(GetOSReport().release());
 #endif
 
   browser_report_generator_.Generate(base::BindOnce(
-      &ReportGenerator::OnBrowserReportReady, weak_ptr_factory_.GetWeakPtr()));
+      &ReportGenerator::OnBrowserReportReady, weak_ptr_factory_.GetWeakPtr(),
+      with_profiles, std::move(callback), std::move(basic_request)));
 }
 
 std::unique_ptr<em::OSReport> ReportGenerator::GetOSReport() {
@@ -84,8 +87,9 @@
 
 #if defined(OS_CHROMEOS)
 
-void ReportGenerator::SetAndroidAppInfos() {
-  basic_request_.clear_android_app_infos();
+void ReportGenerator::SetAndroidAppInfos(ReportRequest* basic_request) {
+  DCHECK(basic_request);
+  basic_request->clear_android_app_infos();
 
   // Android application is only supported for primary profile.
   Profile* primary_profile =
@@ -106,7 +110,7 @@
 
   AndroidAppInfoGenerator generator;
   for (std::string app_id : prefs->GetAppIds()) {
-    em::AndroidAppInfo* app_info = basic_request_.add_android_app_infos();
+    em::AndroidAppInfo* app_info = basic_request->add_android_app_infos();
     generator.Generate(prefs, app_id)->Swap(app_info);
   }
 }
@@ -114,11 +118,24 @@
 #endif
 
 void ReportGenerator::OnBrowserReportReady(
+    bool with_profiles,
+    ReportCallback callback,
+    std::unique_ptr<ReportRequest> basic_request,
     std::unique_ptr<em::BrowserReport> browser_report) {
-  basic_request_.set_allocated_browser_report(browser_report.release());
-  ReportRequests requests =
-      report_request_queue_generator_.Generate(basic_request_);
-  std::move(callback_).Run(std::move(requests));
+  basic_request->set_allocated_browser_report(browser_report.release());
+
+  if (with_profiles) {
+    // Generate a queue of requests containing detailed profile information.
+    std::move(callback).Run(
+        report_request_queue_generator_.Generate(*basic_request));
+    return;
+  }
+
+  // Return a queue containing only the basic request and browser report without
+  // detailed profile information.
+  ReportRequests requests;
+  requests.push(std::move(basic_request));
+  std::move(callback).Run(std::move(requests));
 }
 
 }  // namespace enterprise_reporting
diff --git a/chrome/browser/enterprise_reporting/report_generator.h b/chrome/browser/enterprise_reporting/report_generator.h
index a224705..2b62a6c 100644
--- a/chrome/browser/enterprise_reporting/report_generator.h
+++ b/chrome/browser/enterprise_reporting/report_generator.h
@@ -30,13 +30,19 @@
   ReportGenerator();
   virtual ~ReportGenerator();
 
-  virtual void Generate(ReportCallback callback);
+  // Asynchronously generates a queue of report requests, providing them to
+  // |callback| when ready. If |with_profiles| is true, full details are
+  // included for all loaded profiles; otherwise, only profile name and path
+  // are included.
+  virtual void Generate(bool with_profiles, ReportCallback callback);
 
   void SetMaximumReportSizeForTesting(size_t size);
 
  protected:
   // Creates a basic request that will be used by all Profiles.
-  void CreateBasicRequest();
+  void CreateBasicRequest(std::unique_ptr<ReportRequest> basic_request,
+                          bool with_profiles,
+                          ReportCallback callback);
 
   // Returns an OS report contains basic OS information includes OS name, OS
   // architecture and OS version.
@@ -55,17 +61,17 @@
 #if defined(OS_CHROMEOS)
   // Collect the Android application information installed on primary profile,
   // and set it to |basic_request_|.
-  virtual void SetAndroidAppInfos();
+  virtual void SetAndroidAppInfos(ReportRequest* basic_request);
 #endif
 
  private:
-  void OnBrowserReportReady(std::unique_ptr<em::BrowserReport> browser_report);
+  void OnBrowserReportReady(bool with_profiles,
+                            ReportCallback callback,
+                            std::unique_ptr<ReportRequest> basic_request,
+                            std::unique_ptr<em::BrowserReport> browser_report);
 
   ReportRequestQueueGenerator report_request_queue_generator_;
   BrowserReportGenerator browser_report_generator_;
-  ReportCallback callback_;
-  // Basic information that is shared among requests.
-  ReportRequest basic_request_;
 
   base::WeakPtrFactory<ReportGenerator> weak_ptr_factory_{this};
 
diff --git a/chrome/browser/enterprise_reporting/report_generator_unittest.cc b/chrome/browser/enterprise_reporting/report_generator_unittest.cc
index b9e6202..7c54c0d9f 100644
--- a/chrome/browser/enterprise_reporting/report_generator_unittest.cc
+++ b/chrome/browser/enterprise_reporting/report_generator_unittest.cc
@@ -15,6 +15,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/enterprise_reporting/report_request_definition.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/account_id/account_id.h"
@@ -194,20 +195,24 @@
     plugin_service->RefreshPlugins();
   }
 
-  std::vector<std::unique_ptr<ReportRequest>> GenerateRequests() {
+  std::vector<std::unique_ptr<ReportRequest>> GenerateRequests(
+      bool with_profiles) {
     histogram_tester_ = std::make_unique<base::HistogramTester>();
     base::RunLoop run_loop;
     std::vector<std::unique_ptr<ReportRequest>> rets;
-    generator_.Generate(base::BindLambdaForTesting(
-        [&run_loop, &rets](ReportGenerator::ReportRequests requests) {
-          while (!requests.empty()) {
-            rets.push_back(std::move(requests.front()));
-            requests.pop();
-          }
-          run_loop.Quit();
-        }));
+    generator_.Generate(
+        with_profiles,
+        base::BindLambdaForTesting(
+            [&run_loop, &rets](ReportGenerator::ReportRequests requests) {
+              while (!requests.empty()) {
+                rets.push_back(std::move(requests.front()));
+                requests.pop();
+              }
+              run_loop.Quit();
+            }));
     run_loop.Run();
-    VerifyMetrics(rets);
+    if (with_profiles)
+      VerifyMetrics(rets);  // Only generated for reports with profiles.
     return rets;
   }
 
@@ -228,10 +233,7 @@
       std::string actual_profile_name = actual_profile_info.name();
 
       // Verify that the profile id is set as profile path.
-      EXPECT_EQ(profile_manager_.profiles_dir()
-                    .AppendASCII(actual_profile_name)
-                    .AsUTF8Unsafe(),
-                actual_profile_info.id());
+      EXPECT_EQ(GetProfilePath(actual_profile_name), actual_profile_info.id());
 
       EXPECT_TRUE(actual_profile_info.has_is_full_report());
 
@@ -258,6 +260,23 @@
   base::HistogramTester* histogram_tester() { return histogram_tester_.get(); }
 
  private:
+  // Returns a Profile's path, preferring the path of the active profile for
+  // user |profile_name| and falling back to a generated path based on it.
+  std::string GetProfilePath(const std::string& profile_name) {
+    // Find active Profile and return its path.
+    for (auto* profile :
+         profile_manager_.profile_manager()->GetLoadedProfiles()) {
+      if (profile->GetProfileUserName() == profile_name)
+        return profile->GetPath().AsUTF8Unsafe();
+    }
+
+    // No active profile, profile path must be generated by us, return path
+    // with same generator.
+    return profile_manager_.profiles_dir()
+        .AppendASCII(profile_name)
+        .AsUTF8Unsafe();
+  }
+
   ReportGenerator generator_;
 
   content::BrowserTaskEnvironment task_environment_;
@@ -271,7 +290,57 @@
   auto profile_names = CreateProfiles(/*number*/ 2, kIdle);
   CreatePlugin();
 
-  auto requests = GenerateRequests();
+  auto requests = GenerateRequests(/*with_profiles=*/true);
+  EXPECT_EQ(1u, requests.size());
+
+  auto* basic_request = requests[0].get();
+
+  // In the ChromeOsUserReportRequest for Chrome OS, these fields are not
+  // existing. Therefore, they are skipped according to current environment.
+#if !defined(OS_CHROMEOS)
+  EXPECT_NE(std::string(), basic_request->computer_name());
+  EXPECT_NE(std::string(), basic_request->os_user_name());
+  VerifySerialNumber(basic_request->serial_number());
+
+  EXPECT_TRUE(basic_request->has_os_report());
+  auto& os_report = basic_request->os_report();
+  EXPECT_NE(std::string(), os_report.name());
+  EXPECT_NE(std::string(), os_report.arch());
+  EXPECT_NE(std::string(), os_report.version());
+#endif
+
+  EXPECT_TRUE(basic_request->has_browser_report());
+  auto& browser_report = basic_request->browser_report();
+#if defined(OS_CHROMEOS)
+  EXPECT_FALSE(browser_report.has_browser_version());
+  EXPECT_FALSE(browser_report.has_channel());
+#else
+  EXPECT_NE(std::string(), browser_report.browser_version());
+  EXPECT_TRUE(browser_report.has_channel());
+#endif
+  EXPECT_NE(std::string(), browser_report.executable_path());
+
+#if defined(OS_CHROMEOS)
+  EXPECT_EQ(0, browser_report.plugins_size());
+#else
+  // There might be other plugins like PDF plugin, however, our fake plugin
+  // should be the first one in the report.
+  EXPECT_LE(1, browser_report.plugins_size());
+  EXPECT_EQ(kPluginName, browser_report.plugins(0).name());
+  EXPECT_EQ(kPluginVersion, browser_report.plugins(0).version());
+  EXPECT_EQ(kPluginDescription, browser_report.plugins(0).description());
+  EXPECT_EQ(kPluginFileName, browser_report.plugins(0).filename());
+#endif
+
+  VerifyProfileReport(/*active_profile_names*/ std::set<std::string>(),
+                      profile_names, browser_report);
+}
+
+TEST_F(ReportGeneratorTest, GenerateWithoutProfiles) {
+  auto profile_names = CreateProfiles(/*number*/ 2, kActive);
+  CreatePlugin();
+
+  auto requests = GenerateRequests(/*with_profiles=*/false);
   EXPECT_EQ(1u, requests.size());
 
   auto* basic_request = requests[0].get();
@@ -334,7 +403,7 @@
 
   // Verify the Arc application information in the report is same as the test
   // data.
-  auto requests = GenerateRequests();
+  auto requests = GenerateRequests(/*with_profiles=*/true);
   EXPECT_EQ(1u, requests.size());
 
   ReportRequest* request = requests.front().get();
@@ -346,7 +415,7 @@
 
   // Generate the Arc application information again and make sure the report
   // remains the same.
-  requests = GenerateRequests();
+  requests = GenerateRequests(/*with_profiles=*/true);
   EXPECT_EQ(1u, requests.size());
 
   request = requests.front().get();
@@ -375,7 +444,7 @@
   // No Arc application information is reported after the Arc Play Store
   // support for given profile is disabled.
   primary_profile.GetPrefs()->SetBoolean(arc::prefs::kArcEnabled, false);
-  auto requests = GenerateRequests();
+  auto requests = GenerateRequests(/*with_profiles=*/true);
   EXPECT_EQ(1u, requests.size());
 
   ReportRequest* request = requests.front().get();
diff --git a/chrome/browser/enterprise_reporting/report_scheduler.cc b/chrome/browser/enterprise_reporting/report_scheduler.cc
index 954209b5..58013678 100644
--- a/chrome/browser/enterprise_reporting/report_scheduler.cc
+++ b/chrome/browser/enterprise_reporting/report_scheduler.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/policy/browser_dm_token_storage.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/upgrade_detector/build_state.h"
 #include "chrome/common/pref_names.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
@@ -40,6 +41,16 @@
       prefs::kCloudReportingEnabled);
 }
 
+// Returns true if this build should generate basic reports when an update is
+// detected.
+constexpr bool ShouldReportUpdates() {
+#if defined(OS_CHROMEOS)
+  return false;
+#else
+  return true;
+#endif
+}
+
 }  // namespace
 
 ReportScheduler::ReportScheduler(
@@ -59,6 +70,8 @@
                                   stale_profiles_->size(),
                                   kMaximumTrackedProfiles);
   }
+  if (ShouldReportUpdates())
+    g_browser_process->GetBuildState()->RemoveObserver(this);
 }
 
 bool ReportScheduler::IsNextReportScheduledForTesting() const {
@@ -74,6 +87,14 @@
   OnReportEnabledPrefChanged();
 }
 
+void ReportScheduler::OnUpdate(const BuildState* build_state) {
+  DCHECK(ShouldReportUpdates());
+  // A new version has been detected on the machine and a restart is now needed
+  // for it to take effect. Send a basic report (without profile info)
+  // immediately.
+  GenerateAndUploadReport(kTriggerUpdate);
+}
+
 void ReportScheduler::RegisterPrefObserver() {
   pref_change_registrar_.Init(g_browser_process->local_state());
   pref_change_registrar_.Add(
@@ -86,7 +107,7 @@
 
 void ReportScheduler::OnReportEnabledPrefChanged() {
   if (!IsReportingEnabled()) {
-    StopRequestTimer();
+    Stop();
     return;
   }
 
@@ -95,16 +116,26 @@
   // initialized, and will keep valid during whole life-cycle.
 #if !defined(OS_CHROMEOS)
   if (!SetupBrowserPolicyClientRegistration()) {
-    StopRequestTimer();
+    Stop();
     return;
   }
 #endif
 
-  Start();
+  // Start the periodic report timer.
+  Start(g_browser_process->local_state()->GetTime(kLastUploadTimestamp));
+
+  if (ShouldReportUpdates()) {
+    // Watch for browser updates if not already doing so.
+    auto* build_state = g_browser_process->GetBuildState();
+    if (!build_state->HasObserver(this))
+      build_state->AddObserver(this);
+  }
 }
 
-void ReportScheduler::StopRequestTimer() {
+void ReportScheduler::Stop() {
   request_timer_.Stop();
+  if (ShouldReportUpdates())
+    g_browser_process->GetBuildState()->RemoveObserver(this);
 }
 
 bool ReportScheduler::SetupBrowserPolicyClientRegistration() {
@@ -127,36 +158,58 @@
   return true;
 }
 
-void ReportScheduler::Start() {
-  // The |next_upload_time| is based on the |lastUploadTimestamp| in the
-  // |local_state|, after that, it's 24 hours for each succeeded upload.
-  base::Time next_upload_time =
-      g_browser_process->local_state()->GetTime(kLastUploadTimestamp) +
-      kDefaultUploadInterval;
+void ReportScheduler::Start(base::Time last_upload_time) {
+  // The next report is triggered 24h after the previous was uploaded.
+  const base::Time next_upload_time = last_upload_time + kDefaultUploadInterval;
   if (VLOG_IS_ON(1)) {
     base::TimeDelta first_request_delay = next_upload_time - base::Time::Now();
     VLOG(1) << "Schedule the first report in about "
             << first_request_delay.InHours() << " hour(s) and "
             << first_request_delay.InMinutes() % 60 << " minute(s).";
   }
-  request_timer_.Start(
-      FROM_HERE, next_upload_time,
-      base::BindRepeating(&ReportScheduler::GenerateAndUploadReport,
-                          base::Unretained(this)));
+  request_timer_.Start(FROM_HERE, next_upload_time,
+                       base::BindOnce(&ReportScheduler::GenerateAndUploadReport,
+                                      base::Unretained(this), kTriggerTimer));
 }
 
-void ReportScheduler::GenerateAndUploadReport() {
-  VLOG(1) << "Generating enterprise report.";
-  report_generator_->Generate(base::BindOnce(
-      &ReportScheduler::OnReportGenerated, base::Unretained(this)));
+void ReportScheduler::GenerateAndUploadReport(ReportTrigger trigger) {
+  if (active_trigger_ != kTriggerNone) {
+    // A report is already being generated. Remember this trigger to be handled
+    // once the current report completes.
+    pending_triggers_ |= trigger;
+    return;
+  }
+
+  active_trigger_ = trigger;
+  bool with_profiles = true;
+  switch (trigger) {
+    case kTriggerNone:
+      NOTREACHED();
+      FALLTHROUGH;
+    case kTriggerTimer:
+      VLOG(1) << "Generating enterprise report.";
+      break;
+    case kTriggerUpdate:
+      VLOG(1) << "Generating basic enterprise report upon update.";
+      with_profiles = false;
+      break;
+  }
+
+  report_generator_->Generate(
+      with_profiles, base::BindOnce(&ReportScheduler::OnReportGenerated,
+                                    base::Unretained(this)));
 }
 
 void ReportScheduler::OnReportGenerated(
     ReportGenerator::ReportRequests requests) {
+  DCHECK_NE(active_trigger_, kTriggerNone);
   if (requests.empty()) {
     SYSLOG(ERROR)
         << "No cloud report can be generated. Likely the report is too large.";
-    // We can't generate any report, stop the reporting.
+    // Do not restart the periodic report timer, as it's likely that subsequent
+    // attempts to generate full reports would also fail.
+    active_trigger_ = kTriggerNone;
+    RunPendingTriggers();
     return;
   }
   VLOG(1) << "Uploading enterprise report.";
@@ -170,6 +223,7 @@
 }
 
 void ReportScheduler::OnReportUploaded(ReportUploader::ReportStatus status) {
+  DCHECK_NE(active_trigger_, kTriggerNone);
   VLOG(1) << "The enterprise report upload result " << status << ".";
   switch (status) {
     case ReportUploader::kSuccess:
@@ -182,21 +236,32 @@
     case ReportUploader::kTransientError:
       // Stop retrying and schedule the next report to avoid stale report.
       // Failure count is not reset so retry delay remains.
-      {
-        base::Time now = base::Time::Now();
+      if (active_trigger_ == kTriggerTimer) {
+        const base::Time now = base::Time::Now();
         g_browser_process->local_state()->SetTime(kLastUploadTimestamp, now);
-        if (IsReportingEnabled()) {
-          request_timer_.Start(
-              FROM_HERE, now + kDefaultUploadInterval,
-              base::BindRepeating(&ReportScheduler::GenerateAndUploadReport,
-                                  base::Unretained(this)));
-        }
+        if (IsReportingEnabled())
+          Start(now);
       }
       break;
     case ReportUploader::kPersistentError:
       // No future upload until Chrome relaunch or pref change event.
       break;
   }
+
+  active_trigger_ = kTriggerNone;
+  RunPendingTriggers();
+}
+
+void ReportScheduler::RunPendingTriggers() {
+  DCHECK_EQ(active_trigger_, kTriggerNone);
+  if (!pending_triggers_)
+    return;
+
+  // Timer-triggered reports are a superset of those triggered by an update, so
+  // favor them and consider that they serve both purposes.
+  uint32_t pending_triggers = std::exchange(pending_triggers_, 0);
+  GenerateAndUploadReport(
+      (pending_triggers & kTriggerTimer) != 0 ? kTriggerTimer : kTriggerUpdate);
 }
 
 void ReportScheduler::TrackStaleProfiles() {
diff --git a/chrome/browser/enterprise_reporting/report_scheduler.h b/chrome/browser/enterprise_reporting/report_scheduler.h
index 60fcf15..b6a5819 100644
--- a/chrome/browser/enterprise_reporting/report_scheduler.h
+++ b/chrome/browser/enterprise_reporting/report_scheduler.h
@@ -5,18 +5,19 @@
 #ifndef CHROME_BROWSER_ENTERPRISE_REPORTING_REPORT_SCHEDULER_H_
 #define CHROME_BROWSER_ENTERPRISE_REPORTING_REPORT_SCHEDULER_H_
 
+#include <stdint.h>
 #include <memory>
 #include <queue>
 #include <string>
 
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
-#include "build/build_config.h"
 #include "chrome/browser/enterprise_reporting/notification/extension_request_observer_factory.h"
 #include "chrome/browser/enterprise_reporting/report_generator.h"
 #include "chrome/browser/enterprise_reporting/report_uploader.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
 #include "chrome/browser/ui/views/relaunch_notification/wall_clock_timer.h"
+#include "chrome/browser/upgrade_detector/build_state_observer.h"
 #include "components/prefs/pref_change_registrar.h"
 
 namespace policy {
@@ -25,9 +26,12 @@
 
 namespace enterprise_reporting {
 
-// Schedules the next report and handles retry in case of error. It also cancels
-// all pending uploads if the report policy is turned off.
-class ReportScheduler : public ProfileManagerObserver {
+// Schedules report generation and upload every 24 hours and upon browser update
+// for desktop Chrome while cloud reporting is enabled via administrative
+// policy. If either of these triggers fires while a report is being generated,
+// processing is deferred until the existing processing completes.
+class ReportScheduler : public ProfileManagerObserver,
+                        public BuildStateObserver {
  public:
   ReportScheduler(policy::CloudPolicyClient* client,
                   std::unique_ptr<ReportGenerator> report_generator);
@@ -43,7 +47,18 @@
 
   void OnDMTokenUpdated();
 
+  // BuildStateObserver:
+  void OnUpdate(const BuildState* build_state) override;
+
  private:
+  // The trigger leading to report generation. Values are bitmasks in the
+  // |pending_triggers_| bitfield.
+  enum ReportTrigger : uint32_t {
+    kTriggerNone = 0,          // No trigger.
+    kTriggerTimer = 1U << 0,   // The periodic timer expired.
+    kTriggerUpdate = 1U << 1,  // An update was detected.
+  };
+
   // Observes CloudReportingEnabled policy.
   void RegisterPrefObserver();
 
@@ -51,25 +66,31 @@
   // policy value check during startup.
   void OnReportEnabledPrefChanged();
 
-  // Stop |request_timer_| if it is existing.
-  void StopRequestTimer();
+  // Stops the periodic timer and the update observer.
+  void Stop();
 
   // Register |cloud_policy_client_| with dm token and client id for desktop
   // browser only. (Chrome OS doesn't need this step here.)
   bool SetupBrowserPolicyClientRegistration();
 
-  // Schedules the first update request.
-  void Start();
+  // Starts the periodic timer based on the last time a report was uploaded.
+  void Start(base::Time last_upload_time);
 
-  // Generates a report and uploads it.
-  void GenerateAndUploadReport();
+  // Starts report generation in response to |trigger|.
+  void GenerateAndUploadReport(ReportTrigger trigger);
 
-  // Callback once report is generated.
+  // Continues processing a report (contained in the |requests| collection) by
+  // sending it to the uploader.
   void OnReportGenerated(ReportGenerator::ReportRequests requests);
 
-  // Callback once report upload request is finished.
+  // Finishes processing following report upload. |status| indicates the result
+  // of the attempted upload.
   void OnReportUploaded(ReportUploader::ReportStatus status);
 
+  // Initiates report generation for any triggers that arrived during generation
+  // of another report.
+  void RunPendingTriggers();
+
   // Tracks profiles that miss at least one report.
   void TrackStaleProfiles();
 
@@ -92,6 +113,14 @@
 
   ExtensionRequestObserverFactory extension_request_observer_factory_;
 
+  // The trigger responsible for initiating active report generation.
+  ReportTrigger active_trigger_ = kTriggerNone;
+
+  // The set of triggers that have fired while processing a report (a bitfield
+  // of ReportTrigger values). They will be handled following completion of the
+  // in-process report.
+  uint32_t pending_triggers_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(ReportScheduler);
 };
 
diff --git a/chrome/browser/enterprise_reporting/report_scheduler_unittest.cc b/chrome/browser/enterprise_reporting/report_scheduler_unittest.cc
index 20b4776..5088c13fd 100644
--- a/chrome/browser/enterprise_reporting/report_scheduler_unittest.cc
+++ b/chrome/browser/enterprise_reporting/report_scheduler_unittest.cc
@@ -10,22 +10,27 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise_reporting/prefs.h"
 #include "chrome/browser/policy/fake_browser_dm_token_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/upgrade_detector/build_state.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
+#include "components/version_info/version_info.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::base::test::RunOnceCallback;
 using ::testing::_;
+using ::testing::ByMove;
 using ::testing::Invoke;
+using ::testing::Return;
 using ::testing::WithArgs;
 
 namespace em = enterprise_management;
@@ -50,20 +55,18 @@
 
 class MockReportGenerator : public ReportGenerator {
  public:
-  void Generate(ReportCallback callback) override { OnGenerate(callback); }
-  MOCK_METHOD1(OnGenerate, void(ReportCallback& callback));
+  void Generate(bool with_profiles, ReportCallback callback) override {
+    OnGenerate(with_profiles, callback);
+  }
+  MOCK_METHOD2(OnGenerate, void(bool with_profiles, ReportCallback& callback));
+  MOCK_METHOD0(GenerateBasic, ReportRequests());
 };
 
 class MockReportUploader : public ReportUploader {
  public:
   MockReportUploader() : ReportUploader(nullptr, 0) {}
   ~MockReportUploader() override = default;
-  void SetRequestAndUpload(ReportRequests requests,
-                           ReportCallback callback) override {
-    OnSetRequestAndUpload(requests, callback);
-  }
-  MOCK_METHOD2(OnSetRequestAndUpload,
-               void(ReportRequests& requests, ReportCallback& callback));
+  MOCK_METHOD2(SetRequestAndUpload, void(ReportRequests, ReportCallback));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockReportUploader);
@@ -196,9 +199,9 @@
 
 TEST_F(ReportSchedulerTest, UploadReportSucceeded) {
   EXPECT_CALL_SetupRegistration();
-  EXPECT_CALL(*generator_, OnGenerate(_))
-      .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1)));
-  EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _))
+  EXPECT_CALL(*generator_, OnGenerate(true, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(1)));
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
       .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess));
 
   CreateScheduler();
@@ -217,9 +220,9 @@
 
 TEST_F(ReportSchedulerTest, UploadReportTransientError) {
   EXPECT_CALL_SetupRegistration();
-  EXPECT_CALL(*generator_, OnGenerate(_))
-      .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1)));
-  EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _))
+  EXPECT_CALL(*generator_, OnGenerate(true, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(1)));
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
       .WillOnce(RunOnceCallback<1>(ReportUploader::kTransientError));
 
   CreateScheduler();
@@ -238,9 +241,9 @@
 
 TEST_F(ReportSchedulerTest, UploadReportPersistentError) {
   EXPECT_CALL_SetupRegistrationWithSetDMToken();
-  EXPECT_CALL(*generator_, OnGenerate(_))
-      .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1)));
-  EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _))
+  EXPECT_CALL(*generator_, OnGenerate(true, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(1)));
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
       .WillOnce(RunOnceCallback<1>(ReportUploader::kPersistentError));
 
   CreateScheduler();
@@ -264,9 +267,9 @@
 
 TEST_F(ReportSchedulerTest, NoReportGenerate) {
   EXPECT_CALL_SetupRegistrationWithSetDMToken();
-  EXPECT_CALL(*generator_, OnGenerate(_))
-      .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(0)));
-  EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _)).Times(0);
+  EXPECT_CALL(*generator_, OnGenerate(true, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(0)));
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _)).Times(0);
 
   CreateScheduler();
   EXPECT_TRUE(scheduler_->IsNextReportScheduledForTesting());
@@ -292,9 +295,9 @@
   SetLastUploadInHour(gap);
 
   EXPECT_CALL_SetupRegistration();
-  EXPECT_CALL(*generator_, OnGenerate(_))
-      .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1)));
-  EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _))
+  EXPECT_CALL(*generator_, OnGenerate(true, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(1)));
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
       .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess));
 
   CreateScheduler();
@@ -313,9 +316,9 @@
 
 TEST_F(ReportSchedulerTest, TimerDelayWithoutLastUploadTimestamp) {
   EXPECT_CALL_SetupRegistration();
-  EXPECT_CALL(*generator_, OnGenerate(_))
-      .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1)));
-  EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _))
+  EXPECT_CALL(*generator_, OnGenerate(true, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(1)));
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
       .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess));
 
   CreateScheduler();
@@ -350,9 +353,9 @@
 
 TEST_F(ReportSchedulerTest, ReportingIsDisabledWhileNewReportIsPosted) {
   EXPECT_CALL_SetupRegistration();
-  EXPECT_CALL(*generator_, OnGenerate(_))
-      .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1)));
-  EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _))
+  EXPECT_CALL(*generator_, OnGenerate(true, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(1)));
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
       .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess));
 
   CreateScheduler();
@@ -394,9 +397,9 @@
 
 TEST_F(ReportSchedulerTest, StaleProfileMetricsForProfileAdded) {
   EXPECT_CALL_SetupRegistration();
-  EXPECT_CALL(*generator_, OnGenerate(_))
-      .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1)));
-  EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _))
+  EXPECT_CALL(*generator_, OnGenerate(true, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(1)));
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
       .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess));
 
   CreateScheduler();
@@ -410,9 +413,9 @@
 
 TEST_F(ReportSchedulerTest, StaleProfileMetricsForProfileRemoved) {
   EXPECT_CALL_SetupRegistration();
-  EXPECT_CALL(*generator_, OnGenerate(_))
-      .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1)));
-  EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _))
+  EXPECT_CALL(*generator_, OnGenerate(true, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(1)));
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
       .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess));
 
   // Create a profile a head of time to prevent default profile creation during
@@ -432,9 +435,9 @@
 
 TEST_F(ReportSchedulerTest, StaleProfileMetricsResetAfterNewUpload) {
   EXPECT_CALL_SetupRegistration();
-  EXPECT_CALL(*generator_, OnGenerate(_))
-      .WillRepeatedly(WithArgs<0>(ScheduleGeneratorCallback(1)));
-  EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _))
+  EXPECT_CALL(*generator_, OnGenerate(true, _))
+      .WillRepeatedly(WithArgs<1>(ScheduleGeneratorCallback(1)));
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
       .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess));
 
   CreateScheduler();
@@ -444,7 +447,7 @@
   auto new_uploader = std::make_unique<MockReportUploader>();
   uploader_ = new_uploader.get();
   scheduler_->SetReportUploaderForTesting(std::move(new_uploader));
-  EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _))
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
       .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess));
 
   profile_manager_.CreateTestingProfile("profile1");
@@ -455,4 +458,80 @@
   histogram_tester_.ExpectUniqueSample(kStaleProfileCountMetricsName, 0, 1);
 }
 
+#if !defined(OS_CHROMEOS)
+
+// Tests that a basic report is generated and uploaded when a browser update is
+// detected.
+TEST_F(ReportSchedulerTest, OnUpdate) {
+  // Pretend that a periodic report was generated recently so that one isn't
+  // kicked off during startup.
+  SetLastUploadInHour(base::TimeDelta::FromHours(1));
+  EXPECT_CALL_SetupRegistration();
+  EXPECT_CALL(*generator_, OnGenerate(false, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(1)));
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
+      .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess));
+
+  CreateScheduler();
+  g_browser_process->GetBuildState()->SetUpdate(
+      BuildState::UpdateType::kNormalUpdate,
+      base::Version("1" + version_info::GetVersionNumber()), base::nullopt);
+  task_environment_.RunUntilIdle();
+
+  // The timestamp should not have been updated, since a periodic report was not
+  // generated/uploaded.
+  ExpectLastUploadTimestampUpdated(false);
+}
+
+// Tests that a full report is generated and uploaded following a basic report
+// if the timer fires while the basic report is being uploaded.
+TEST_F(ReportSchedulerTest, DeferredTimer) {
+  EXPECT_CALL_SetupRegistration();
+  CreateScheduler();
+
+  // An update arrives, triggering report generation and upload (sans profiles).
+  EXPECT_CALL(*generator_, OnGenerate(false, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(1)));
+
+  // Hang on to the uploader's ReportCallback.
+  ReportUploader::ReportCallback saved_callback;
+  EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
+      .WillOnce([&saved_callback](ReportUploader::ReportRequests requests,
+                                  ReportUploader::ReportCallback callback) {
+        saved_callback = std::move(callback);
+      });
+
+  g_browser_process->GetBuildState()->SetUpdate(
+      BuildState::UpdateType::kNormalUpdate,
+      base::Version("1" + version_info::GetVersionNumber()), base::nullopt);
+  task_environment_.RunUntilIdle();
+  ::testing::Mock::VerifyAndClearExpectations(generator_);
+  ::testing::Mock::VerifyAndClearExpectations(uploader_);
+
+  // Now the timer fires before the upload completes. No new report should be
+  // generated yet.
+  task_environment_.RunUntilIdle();
+  ::testing::Mock::VerifyAndClearExpectations(generator_);
+
+  // Once the previous upload completes, a new report should be generated
+  // forthwith.
+  EXPECT_CALL(*generator_, OnGenerate(true, _))
+      .WillOnce(WithArgs<1>(ScheduleGeneratorCallback(1)));
+  auto new_uploader = std::make_unique<MockReportUploader>();
+  EXPECT_CALL(*new_uploader, SetRequestAndUpload(_, _))
+      .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess));
+  std::move(saved_callback).Run(ReportUploader::kSuccess);
+  ExpectLastUploadTimestampUpdated(false);
+  ::testing::Mock::VerifyAndClearExpectations(generator_);
+
+  this->uploader_ = new_uploader.get();
+  this->scheduler_->SetReportUploaderForTesting(std::move(new_uploader));
+
+  task_environment_.RunUntilIdle();
+  ::testing::Mock::VerifyAndClearExpectations(uploader_);
+  ExpectLastUploadTimestampUpdated(true);
+}
+
+#endif  // !defined(OS_CHROMEOS)
+
 }  // namespace enterprise_reporting
diff --git a/chrome/browser/extensions/background_header_browsertest.cc b/chrome/browser/extensions/background_header_browsertest.cc
index 2c87bc5..bd333ac 100644
--- a/chrome/browser/extensions/background_header_browsertest.cc
+++ b/chrome/browser/extensions/background_header_browsertest.cc
@@ -27,12 +27,7 @@
 class BackgroundHeaderTest : public ExtensionBrowserTest {
  public:
   BackgroundHeaderTest()
-      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
-    feature_list_.InitWithFeatures(
-        {network::features::kFetchMetadata,
-         network::features::kFetchMetadataDestination},
-        {});
-  }
+      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
 
   BackgroundHeaderTest(const BackgroundHeaderTest& other) = delete;
   BackgroundHeaderTest& operator=(const BackgroundHeaderTest& other) = delete;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index c810cfe..25df656c 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1079,11 +1079,6 @@
     "expiry_milestone": 79
   },
   {
-    "name": "enable-clipboard-provider-text-suggestions",
-    "owners": [ "gangwu", "chrome-omnibox-team@google.com"],
-    "expiry_milestone": 84
-  },
-  {
     "name": "enable-close-tab-suggestions",
     "owners": [ "memex-team@google.com" ],
     "expiry_milestone": 82
@@ -1374,7 +1369,7 @@
   {
     "name": "enable-heavy-ad-intervention",
     "owners": [ "johnidel", "jkarlin" ],
-    "expiry_milestone": 82
+    "expiry_milestone": 85
   },
   {
     "name": "enable-heuristic-stylus-palm-rejection",
@@ -2258,7 +2253,7 @@
   {
     "name": "forced-colors",
     "owners": [ "weblayout@microsoft.com" ],
-    "expiry_milestone": 82
+    "expiry_milestone": 85
   },
   {
     "name": "form-controls-refresh",
@@ -2352,7 +2347,7 @@
   {
     "name": "heavy-ad-privacy-mitigations-opt-out",
     "owners": [ "johnidel", "jkarlin" ],
-    "expiry_milestone": 82
+    "expiry_milestone": 85
   },
   {
     "name": "help-app",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 4cb4aa8b..954ae61d 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -69,11 +69,6 @@
     "Allows synchronous XHR requests during page dismissal when the page is "
     "being navigated away or closed by the user.";
 
-const char kEnableClipboardProviderTextSuggestionsName[] =
-    "Omnibox clipboard text search suggestions";
-const char kEnableClipboardProviderTextSuggestionsDescription[] =
-    "Enables search suggestions in omnibox";
-
 const char kEnableFtpName[] = "Enable support for FTP URLs";
 const char kEnableFtpDescription[] =
     "When enabled, the browser will handle navigations to ftp:// URLs by "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index e0279bd8..2d778875 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -74,9 +74,6 @@
 extern const char kAllowSyncXHRInPageDismissalName[];
 extern const char kAllowSyncXHRInPageDismissalDescription[];
 
-extern const char kEnableClipboardProviderTextSuggestionsName[];
-extern const char kEnableClipboardProviderTextSuggestionsDescription[];
-
 extern const char kEnableFtpName[];
 extern const char kEnableFtpDescription[];
 
diff --git a/chrome/browser/hid/hid_chooser_context.cc b/chrome/browser/hid/hid_chooser_context.cc
index dbf8db1c..5b43a54e0 100644
--- a/chrome/browser/hid/hid_chooser_context.cc
+++ b/chrome/browser/hid/hid_chooser_context.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/values.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/browser/device_service.h"
@@ -31,9 +32,9 @@
 }  // namespace
 
 HidChooserContext::HidChooserContext(Profile* profile)
-    : ChooserContextBase(profile,
-                         ContentSettingsType::HID_GUARD,
-                         ContentSettingsType::HID_CHOOSER_DATA),
+    : ChooserContextBase(ContentSettingsType::HID_GUARD,
+                         ContentSettingsType::HID_CHOOSER_DATA,
+                         HostContentSettingsMapFactory::GetForProfile(profile)),
       is_incognito_(profile->IsOffTheRecord()) {}
 
 HidChooserContext::~HidChooserContext() = default;
diff --git a/chrome/browser/hid/hid_chooser_context.h b/chrome/browser/hid/hid_chooser_context.h
index bf6ecbc4..90bb8c1 100644
--- a/chrome/browser/hid/hid_chooser_context.h
+++ b/chrome/browser/hid/hid_chooser_context.h
@@ -14,12 +14,14 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/unguessable_token.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
+#include "components/permissions/chooser_context_base.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "url/origin.h"
 
+class Profile;
+
 namespace base {
 class Value;
 }
diff --git a/chrome/browser/payments/android/service_worker_payment_app_bridge.cc b/chrome/browser/payments/android/service_worker_payment_app_bridge.cc
index ddfa89f..3f5cc9b1 100644
--- a/chrome/browser/payments/android/service_worker_payment_app_bridge.cc
+++ b/chrome/browser/payments/android/service_worker_payment_app_bridge.cc
@@ -467,6 +467,7 @@
     const JavaParamRef<jstring>& jpayment_request_origin,
     const JavaParamRef<jobjectArray>& jmethod_data,
     const JavaParamRef<jobjectArray>& jmodifiers,
+    const JavaParamRef<jstring>& jcurrency,
     const JavaParamRef<jobject>& jcallback,
     const JavaParamRef<jobject>& japp) {
   content::WebContents* web_contents =
@@ -479,6 +480,8 @@
       GURL(ConvertJavaStringToUTF8(env, jpayment_request_origin));
   event_data->method_data =
       ConvertPaymentMethodDataFromJavaToNative(env, jmethod_data);
+  if (!jcurrency.is_null())
+    event_data->currency = ConvertJavaStringToUTF8(env, jcurrency);
 
   for (auto jmodifier : jmodifiers.ReadElements<jobject>()) {
     PaymentDetailsModifierPtr modifier = PaymentDetailsModifier::New();
diff --git a/chrome/browser/permissions/chooser_context_base_mock_permission_observer.h b/chrome/browser/permissions/chooser_context_base_mock_permission_observer.h
index 3dca0d5d..877ba676d 100644
--- a/chrome/browser/permissions/chooser_context_base_mock_permission_observer.h
+++ b/chrome/browser/permissions/chooser_context_base_mock_permission_observer.h
@@ -5,8 +5,8 @@
 #ifndef CHROME_BROWSER_PERMISSIONS_CHOOSER_CONTEXT_BASE_MOCK_PERMISSION_OBSERVER_H_
 #define CHROME_BROWSER_PERMISSIONS_CHOOSER_CONTEXT_BASE_MOCK_PERMISSION_OBSERVER_H_
 
-#include "chrome/browser/permissions/chooser_context_base.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "components/permissions/chooser_context_base.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/permissions/chooser_context_base_unittest.cc b/chrome/browser/permissions/chooser_context_base_unittest.cc
index 1c7d91b..3a9b10f 100644
--- a/chrome/browser/permissions/chooser_context_base_unittest.cc
+++ b/chrome/browser/permissions/chooser_context_base_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/permissions/chooser_context_base.h"
+#include "components/permissions/chooser_context_base.h"
 
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/permissions/chooser_context_base_mock_permission_observer.h"
@@ -22,9 +22,10 @@
  public:
   // This class uses the USB content settings type for testing purposes only.
   explicit TestChooserContext(Profile* profile)
-      : ChooserContextBase(profile,
-                           ContentSettingsType::USB_GUARD,
-                           ContentSettingsType::USB_CHOOSER_DATA) {}
+      : ChooserContextBase(
+            ContentSettingsType::USB_GUARD,
+            ContentSettingsType::USB_CHOOSER_DATA,
+            HostContentSettingsMapFactory::GetForProfile(profile)) {}
   ~TestChooserContext() override {}
 
   bool IsValidObject(const base::Value& object) override {
diff --git a/chrome/browser/prerender/prerender_tab_helper.cc b/chrome/browser/prerender/prerender_tab_helper.cc
index 7301289..a61b3a2 100644
--- a/chrome/browser/prerender/prerender_tab_helper.cc
+++ b/chrome/browser/prerender/prerender_tab_helper.cc
@@ -20,10 +20,9 @@
 namespace prerender {
 
 PrerenderTabHelper::PrerenderTabHelper(content::WebContents* web_contents)
-    : content::WebContentsObserver(web_contents), origin_(ORIGIN_NONE) {}
+    : content::WebContentsObserver(web_contents) {}
 
-PrerenderTabHelper::~PrerenderTabHelper() {
-}
+PrerenderTabHelper::~PrerenderTabHelper() = default;
 
 void PrerenderTabHelper::DidFinishNavigation(
       content::NavigationHandle* navigation_handle) {
@@ -33,40 +32,12 @@
     return;
   }
 
-  url_ = navigation_handle->GetURL();
   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
   if (!prerender_manager)
     return;
-  if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL))
+  if (prerender_manager->IsWebContentsPrerendering(web_contents(), nullptr))
     return;
-  prerender_manager->RecordNavigation(url_);
-}
-
-void PrerenderTabHelper::DidStartNavigation(
-    content::NavigationHandle* navigation_handle) {
-  // Determine the origin.
-  PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
-  if (prerender_manager)
-    prerender_manager->IsWebContentsPrerendering(web_contents(), &origin_);
-
-  if (navigation_handle->IsSameDocument())
-    return;
-
-  if (!navigation_handle->IsInMainFrame())
-    return;
-
-  MainFrameUrlDidChange(navigation_handle->GetURL());
-}
-
-void PrerenderTabHelper::DidRedirectNavigation(
-    content::NavigationHandle* navigation_handle) {
-  if (!navigation_handle->IsInMainFrame())
-    return;
-  MainFrameUrlDidChange(navigation_handle->GetURL());
-}
-
-void PrerenderTabHelper::MainFrameUrlDidChange(const GURL& url) {
-  url_ = url;
+  prerender_manager->RecordNavigation(navigation_handle->GetURL());
 }
 
 PrerenderManager* PrerenderTabHelper::MaybeGetPrerenderManager() const {
@@ -90,7 +61,7 @@
   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
   if (!prerender_manager)
     return false;
-  return prerender_manager->IsWebContentsPrerendering(web_contents(), NULL);
+  return prerender_manager->IsWebContentsPrerendering(web_contents(), nullptr);
 }
 
 void PrerenderTabHelper::PrerenderSwappedIn() {
diff --git a/chrome/browser/prerender/prerender_tab_helper.h b/chrome/browser/prerender/prerender_tab_helper.h
index f7127c3..622cb91 100644
--- a/chrome/browser/prerender/prerender_tab_helper.h
+++ b/chrome/browser/prerender/prerender_tab_helper.h
@@ -29,24 +29,14 @@
   ~PrerenderTabHelper() override;
 
   // content::WebContentsObserver implementation.
-  void DidStartNavigation(
-      content::NavigationHandle* navigation_handle) override;
-  void DidRedirectNavigation(
-      content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
 
-  // Called when the URL of the main frame changed, either when the load
-  // commits, or a redirect happens.
-  void MainFrameUrlDidChange(const GURL& url);
-
   // Called when this prerendered WebContents has just been swapped in.
   void PrerenderSwappedIn();
 
   base::TimeTicks swap_ticks() const { return swap_ticks_; }
 
-  Origin origin() const { return origin_; }
-
  private:
   explicit PrerenderTabHelper(content::WebContents* web_contents);
   friend class content::WebContentsUserData<PrerenderTabHelper>;
@@ -62,18 +52,9 @@
   // Returns whether the WebContents being observed is currently prerendering.
   bool IsPrerendering();
 
-  // The origin of the relevant prerender or ORIGIN_NONE if there is no
-  // prerender associated with the WebContents.
-  Origin origin_;
-
   // Record the most recent swap time.
   base::TimeTicks swap_ticks_;
 
-  // Current URL being loaded.
-  GURL url_;
-
-  base::WeakPtrFactory<PrerenderTabHelper> weak_factory_{this};
-
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
   DISALLOW_COPY_AND_ASSIGN(PrerenderTabHelper);
diff --git a/chrome/browser/resources/chromeos/login/marketing_opt_in.html b/chrome/browser/resources/chromeos/login/marketing_opt_in.html
index 483d7c7..be6037a16 100644
--- a/chrome/browser/resources/chromeos/login/marketing_opt_in.html
+++ b/chrome/browser/resources/chromeos/login/marketing_opt_in.html
@@ -15,10 +15,12 @@
       <hd-iron-icon slot="oobe-icon" icon1x="oobe-32:checkmark"
           icon2x="oobe-32:checkmark">
       </hd-iron-icon>
-      <div slot="subtitle">
+      <div slot="subtitle" id="marketing-opt-in-subtitle"
+          hidden="[[!marketingOptInEnabled_]]">
         [[i18nRecursive(locale, 'marketingOptInScreenSubtitle', 'productName')]]
       </div>
-      <div slot="footer" class="layout vertical">
+      <div slot="footer" class="layout vertical" id="marketing-opt-in-toggles"
+          hidden="[[!marketingOptInEnabled_]]">
         <div class="marketing-option layout horizontal center">
           <hd-iron-icon icon1x="oobe-32:checkmark" icon2x="oobe-64:checkmark">
           </hd-iron-icon>
diff --git a/chrome/browser/resources/chromeos/login/marketing_opt_in.js b/chrome/browser/resources/chromeos/login/marketing_opt_in.js
index b37242c..07ecf3d 100644
--- a/chrome/browser/resources/chromeos/login/marketing_opt_in.js
+++ b/chrome/browser/resources/chromeos/login/marketing_opt_in.js
@@ -15,6 +15,20 @@
       type: Boolean,
       value: true,
     },
+
+    /**
+     * Whether the marketing opt in toggles should be shown, which will be the
+     * case only if marketing opt in feature is enabled.
+     * When this is false, the screen will only contain UI related to the
+     * tablet mode gestural navigation settings.
+     */
+    marketingOptInEnabled_: {
+      type: Boolean,
+      readOnly: true,
+      value() {
+        return loadTimeData.getBoolean('enableMarketingOptIn');
+      },
+    },
   },
 
   behaviors: [OobeI18nBehavior, OobeDialogHostBehavior, LoginScreenBehavior],
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin.css b/chrome/browser/resources/chromeos/login/security_token_pin.css
index 0f00bf9..0c4ca7ba 100644
--- a/chrome/browser/resources/chromeos/login/security_token_pin.css
+++ b/chrome/browser/resources/chromeos/login/security_token_pin.css
@@ -11,8 +11,8 @@
   --pin-keyboard-pin-input-style: {
     width: 192px;
   };
+  --pin-keyboard-input-letter-spacing: 14px;
   --pin-keyboard-number-color: var(--google-grey-900);
-  --cr-input-letter-spacing: 14px;
   --cr-icon-button-margin-start: 5px;
 }
 
diff --git a/chrome/browser/resources/settings/site_settings/edit_exception_dialog.js b/chrome/browser/resources/settings/site_settings/edit_exception_dialog.js
index 1288667..60dafcb 100644
--- a/chrome/browser/resources/settings/site_settings/edit_exception_dialog.js
+++ b/chrome/browser/resources/settings/site_settings/edit_exception_dialog.js
@@ -63,8 +63,8 @@
           this.model.incognito);
 
       this.browserProxy_.setCategoryPermissionForPattern(
-          this.origin_, this.origin_, this.model.category, this.model.setting,
-          this.model.incognito);
+          this.origin_, settings.SITE_EXCEPTION_WILDCARD, this.model.category,
+          this.model.setting, this.model.incognito);
     }
 
     this.$.dialog.close();
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
index e58ebd7..c46687c 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
@@ -192,7 +192,7 @@
         route: 'SITE_SETTINGS_PAYMENT_HANDLER',
         id: Id.PAYMENT_HANDLER,
         label: 'siteSettingsPaymentHandler',
-        icon: 'settings:bluetooth-scanning',
+        icon: 'settings:payment-handler',
         enabledLabel: 'siteSettingsPaymentHandlerAllow',
         disabledLabel: 'siteSettingsPaymentHandlerBlock',
         shouldShow: () =>
diff --git a/chrome/browser/resources/tab_strip/tab.js b/chrome/browser/resources/tab_strip/tab.js
index 467fcd1..7e23975d 100644
--- a/chrome/browser/resources/tab_strip/tab.js
+++ b/chrome/browser/resources/tab_strip/tab.js
@@ -236,8 +236,6 @@
 
   /** @private */
   onSwipe_() {
-    // Prevent slideOut animation from playing.
-    this.remove();
     this.tabsApi_.closeTab(this.tab_.id, CloseTabAction.SWIPED_TO_CLOSE);
   }
 
diff --git a/chrome/browser/resources/tab_strip/tab_swiper.js b/chrome/browser/resources/tab_strip/tab_swiper.js
index d02f130..7c6588a7 100644
--- a/chrome/browser/resources/tab_strip/tab_swiper.js
+++ b/chrome/browser/resources/tab_strip/tab_swiper.js
@@ -161,10 +161,6 @@
     }
 
     const yDiff = this.currentPointerDownEvent_.clientY - event.clientY;
-    const animationTime = yDiff;
-    this.animation_.currentTime =
-        Math.max(0, Math.min(SWIPE_FINISH_THRESHOLD_PX, animationTime));
-
     if (!this.animationInitiated_ &&
         Math.abs(yDiff) > TRANSLATE_ANIMATION_THRESHOLD_PX) {
       this.animationInitiated_ = true;
@@ -181,22 +177,17 @@
       return;
     }
 
-    const pixelsSwiped = this.animation_.currentTime;
+    const yDiff = this.currentPointerDownEvent_.clientY - event.clientY;
+    const pixelsSwiped =
+        Math.max(0, Math.min(SWIPE_FINISH_THRESHOLD_PX, yDiff));
     const swipedEnoughToClose = pixelsSwiped > SWIPE_START_THRESHOLD_PX;
     const wasHighVelocity = pixelsSwiped /
             (event.timeStamp - this.currentPointerDownEvent_.timeStamp) >
         SWIPE_VELOCITY_THRESHOLD;
 
-    if (pixelsSwiped === SWIPE_FINISH_THRESHOLD_PX) {
-      // The user has swiped the max amount of pixels to swipe and the animation
-      // has already completed all its keyframes, so just fire the onfinish
-      // events on the animation.
-      this.animation_.finish();
-    } else if (swipedEnoughToClose || wasHighVelocity) {
-      this.animation_.play();
-    } else {
-      this.animation_.cancel();
-      this.animation_.currentTime = 0;
+    if (pixelsSwiped === SWIPE_FINISH_THRESHOLD_PX || swipedEnoughToClose ||
+        wasHighVelocity) {
+      this.element_.dispatchEvent(new CustomEvent('swipe'));
     }
 
     this.clearPointerEvents_();
diff --git a/chrome/browser/serial/serial_chooser_context.cc b/chrome/browser/serial/serial_chooser_context.cc
index 1ca4b4f..60521e0d 100644
--- a/chrome/browser/serial/serial_chooser_context.cc
+++ b/chrome/browser/serial/serial_chooser_context.cc
@@ -8,6 +8,7 @@
 
 #include "base/base64.h"
 #include "base/values.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/device_service.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -51,9 +52,9 @@
 }  // namespace
 
 SerialChooserContext::SerialChooserContext(Profile* profile)
-    : ChooserContextBase(profile,
-                         ContentSettingsType::SERIAL_GUARD,
-                         ContentSettingsType::SERIAL_CHOOSER_DATA),
+    : ChooserContextBase(ContentSettingsType::SERIAL_GUARD,
+                         ContentSettingsType::SERIAL_CHOOSER_DATA,
+                         HostContentSettingsMapFactory::GetForProfile(profile)),
       is_incognito_(profile->IsOffTheRecord()) {}
 
 SerialChooserContext::~SerialChooserContext() = default;
diff --git a/chrome/browser/serial/serial_chooser_context.h b/chrome/browser/serial/serial_chooser_context.h
index d416eb9..7402b74d 100644
--- a/chrome/browser/serial/serial_chooser_context.h
+++ b/chrome/browser/serial/serial_chooser_context.h
@@ -14,7 +14,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/unguessable_token.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
+#include "components/permissions/chooser_context_base.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/serial.mojom-forward.h"
@@ -22,6 +22,8 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
+class Profile;
+
 namespace base {
 class Value;
 }
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index e95674f..ee2ac02e 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -2045,12 +2045,7 @@
 class MultiOriginSessionRestoreTest : public SessionRestoreTest {
  public:
   MultiOriginSessionRestoreTest()
-      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
-    feature_list_.InitWithFeatures(
-        {network::features::kFetchMetadata,
-         network::features::kFetchMetadataDestination},
-        {});
-  }
+      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
 
   void SetUpOnMainThread() override {
     SessionRestoreTest::SetUpOnMainThread();
diff --git a/chrome/browser/signin/account_reconcilor_factory.cc b/chrome/browser/signin/account_reconcilor_factory.cc
index f1b858de..3921787 100644
--- a/chrome/browser/signin/account_reconcilor_factory.cc
+++ b/chrome/browser/signin/account_reconcilor_factory.cc
@@ -44,15 +44,36 @@
 namespace {
 
 #if defined(OS_CHROMEOS)
-class ChromeOSChildAccountReconcilorDelegate
+class ChromeOSLimitedAccessAccountReconcilorDelegate
     : public signin::MirrorAccountReconcilorDelegate {
  public:
-  explicit ChromeOSChildAccountReconcilorDelegate(
+  enum class ReconcilorBehavior {
+    kChild,
+    kEnterprise,
+  };
+
+  ChromeOSLimitedAccessAccountReconcilorDelegate(
+      ReconcilorBehavior reconcilor_behavior,
       signin::IdentityManager* identity_manager)
-      : signin::MirrorAccountReconcilorDelegate(identity_manager) {}
+      : signin::MirrorAccountReconcilorDelegate(identity_manager),
+        reconcilor_behavior_(reconcilor_behavior) {}
+
+  ChromeOSLimitedAccessAccountReconcilorDelegate(
+      const ChromeOSLimitedAccessAccountReconcilorDelegate&) = delete;
+  ChromeOSLimitedAccessAccountReconcilorDelegate& operator=(
+      const ChromeOSLimitedAccessAccountReconcilorDelegate&) = delete;
 
   base::TimeDelta GetReconcileTimeout() const override {
-    return base::TimeDelta::FromSeconds(10);
+    switch (reconcilor_behavior_) {
+      case ReconcilorBehavior::kChild:
+        return base::TimeDelta::FromSeconds(10);
+      case ReconcilorBehavior::kEnterprise:
+        // 60 seconds is enough to cover about 99% of all reconcile cases.
+        return base::TimeDelta::FromSeconds(60);
+      default:
+        NOTREACHED();
+        return MirrorAccountReconcilorDelegate::GetReconcileTimeout();
+    }
   }
 
   void OnReconcileError(const GoogleServiceAuthError& error) override {
@@ -74,14 +95,16 @@
     user_manager::UserManager::Get()->SaveForceOnlineSignin(
         primary_user->GetAccountId(), true /* force_online_signin */);
 
+    if (reconcilor_behavior_ == ReconcilorBehavior::kChild) {
+      UMA_HISTOGRAM_BOOLEAN(
+          "ChildAccountReconcilor.ForcedUserExitOnReconcileError", true);
+    }
     // Force a logout.
-    UMA_HISTOGRAM_BOOLEAN(
-        "ChildAccountReconcilor.ForcedUserExitOnReconcileError", true);
     chrome::AttemptUserExit();
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(ChromeOSChildAccountReconcilorDelegate);
+  const ReconcilorBehavior reconcilor_behavior_;
 };
 
 // An |AccountReconcilorDelegate| for Chrome OS that is exactly the same as
@@ -176,7 +199,9 @@
       // Only for child accounts on Chrome OS, use the specialized Mirror
       // delegate.
       if (profile->IsChild()) {
-        return std::make_unique<ChromeOSChildAccountReconcilorDelegate>(
+        return std::make_unique<ChromeOSLimitedAccessAccountReconcilorDelegate>(
+            ChromeOSLimitedAccessAccountReconcilorDelegate::ReconcilorBehavior::
+                kChild,
             IdentityManagerFactory::GetForProfile(profile));
       }
 
@@ -190,6 +215,14 @@
             signin::ActiveDirectoryAccountReconcilorDelegate>();
       }
 
+      if (profile->GetPrefs()->GetBoolean(
+              prefs::kForceLogoutUnauthenticatedUserEnabled)) {
+        return std::make_unique<ChromeOSLimitedAccessAccountReconcilorDelegate>(
+            ChromeOSLimitedAccessAccountReconcilorDelegate::ReconcilorBehavior::
+                kEnterprise,
+            IdentityManagerFactory::GetForProfile(profile));
+      }
+
       // TODO(sinhak): Use |MirrorAccountReconcilorDelegate|) when all Chrome OS
       // users have been migrated to Account Manager.
       return std::make_unique<ChromeOSAccountReconcilorDelegate>(
diff --git a/chrome/browser/sync/test/integration/cookie_helper.cc b/chrome/browser/sync/test/integration/cookie_helper.cc
new file mode 100644
index 0000000..5db66ff
--- /dev/null
+++ b/chrome/browser/sync/test/integration/cookie_helper.cc
@@ -0,0 +1,65 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/integration/cookie_helper.h"
+
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "net/cookies/canonical_cookie.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
+
+namespace cookie_helper {
+
+namespace {
+
+// Name of the GAIA cookie that is being observed to detect when available
+// accounts have changed in the content-area.
+const char kSigninCookieName[] = "SAPISID";
+
+}  // namespace
+
+void AddSigninCookie(Profile* profile) {
+  DCHECK(profile);
+  net::CanonicalCookie cookie(
+      kSigninCookieName, std::string(), ".google.com", "/", base::Time(),
+      base::Time(), base::Time(), /*secure=*/true, false,
+      net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT);
+
+  network::mojom::CookieManager* cookie_manager =
+      content::BrowserContext::GetDefaultStoragePartition(profile)
+          ->GetCookieManagerForBrowserProcess();
+  DCHECK(cookie_manager);
+
+  base::RunLoop run_loop;
+  cookie_manager->SetCanonicalCookie(
+      cookie, "https", net::CookieOptions(),
+      base::BindLambdaForTesting(
+          [&run_loop](net::CanonicalCookie::CookieInclusionStatus) {
+            run_loop.Quit();
+          }));
+  run_loop.Run();
+}
+
+void DeleteSigninCookies(Profile* profile) {
+  DCHECK(profile);
+  network::mojom::CookieManager* cookie_manager =
+      content::BrowserContext::GetDefaultStoragePartition(profile)
+          ->GetCookieManagerForBrowserProcess();
+  DCHECK(cookie_manager);
+
+  base::RunLoop run_loop;
+  network::mojom::CookieDeletionFilterPtr filter =
+      network::mojom::CookieDeletionFilter::New();
+  filter->cookie_name = kSigninCookieName;
+
+  cookie_manager->DeleteCookies(
+      std::move(filter),
+      base::BindLambdaForTesting([&run_loop](uint32_t) { run_loop.Quit(); }));
+  run_loop.Run();
+}
+
+}  // namespace cookie_helper
diff --git a/chrome/browser/sync/test/integration/cookie_helper.h b/chrome/browser/sync/test/integration/cookie_helper.h
new file mode 100644
index 0000000..a439dbe0
--- /dev/null
+++ b/chrome/browser/sync/test/integration/cookie_helper.h
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_INTEGRATION_COOKIE_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_INTEGRATION_COOKIE_HELPER_H_
+
+class Profile;
+
+namespace cookie_helper {
+
+// Adds new signin cookie (aka SAPISID cookie) directly to the profile's
+// CookieManager. |profile| must not be nullptr and network must be already
+// initialized.
+void AddSigninCookie(Profile* profile);
+
+// Removes all signin cookie (aka SAPISID cookie) directly from the profile's
+// CookieManager. |profile| must not be nullptr and network must be already
+// initialized.
+void DeleteSigninCookies(Profile* profile);
+
+}  // namespace cookie_helper
+
+#endif  // CHROME_BROWSER_SYNC_TEST_INTEGRATION_COOKIE_HELPER_H_
diff --git a/chrome/browser/sync/test/integration/encryption_helper.cc b/chrome/browser/sync/test/integration/encryption_helper.cc
index 25b667e..a8336d1a 100644
--- a/chrome/browser/sync/test/integration/encryption_helper.cc
+++ b/chrome/browser/sync/test/integration/encryption_helper.cc
@@ -6,10 +6,12 @@
 #include <vector>
 
 #include "base/base64.h"
+#include "base/bind.h"
 #include "chrome/browser/sync/test/integration/encryption_helper.h"
 #include "components/sync/base/passphrase_enums.h"
 #include "components/sync/base/sync_base_switches.h"
 #include "components/sync/driver/profile_sync_service.h"
+#include "components/sync/driver/sync_client.h"
 #include "components/sync/engine/sync_engine_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -212,6 +214,31 @@
          desired_state_;
 }
 
+TrustedVaultKeysChangedStateChecker::TrustedVaultKeysChangedStateChecker(
+    syncer::ProfileSyncService* service)
+    : keys_changed_(false) {
+  // base::Unretained() is safe here, because callback won't be called once
+  // |subscription_| is destroyed.
+  subscription_ = service->GetSyncClientForTest()
+                      ->GetTrustedVaultClient()
+                      ->AddKeysChangedObserver(base::BindRepeating(
+                          &TrustedVaultKeysChangedStateChecker::OnKeysChanged,
+                          base::Unretained(this)));
+}
+
+TrustedVaultKeysChangedStateChecker::~TrustedVaultKeysChangedStateChecker() =
+    default;
+
+bool TrustedVaultKeysChangedStateChecker::IsExitConditionSatisfied(
+    std::ostream* os) {
+  *os << "Waiting for trusted vault keys change";
+  return keys_changed_;
+}
+
+void TrustedVaultKeysChangedStateChecker::OnKeysChanged() {
+  keys_changed_ = true;
+}
+
 ScopedScryptFeatureToggler::ScopedScryptFeatureToggler(
     bool force_disabled,
     bool use_for_new_passphrases) {
diff --git a/chrome/browser/sync/test/integration/encryption_helper.h b/chrome/browser/sync/test/integration/encryption_helper.h
index 02ed85e0..5809d1af 100644
--- a/chrome/browser/sync/test/integration/encryption_helper.h
+++ b/chrome/browser/sync/test/integration/encryption_helper.h
@@ -10,6 +10,8 @@
 
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
+#include "chrome/browser/sync/test/integration/status_change_checker.h"
+#include "components/sync/driver/trusted_vault_client.h"
 #include "components/sync/protocol/nigori_specifics.pb.h"
 #include "components/sync/syncable/directory_cryptographer.h"
 #include "components/sync/test/fake_server/fake_server.h"
@@ -105,6 +107,22 @@
   const bool desired_state_;
 };
 
+// Checker used to block until trusted vault keys are changed.
+class TrustedVaultKeysChangedStateChecker : public StatusChangeChecker {
+ public:
+  explicit TrustedVaultKeysChangedStateChecker(
+      syncer::ProfileSyncService* service);
+  ~TrustedVaultKeysChangedStateChecker() override;
+
+  bool IsExitConditionSatisfied(std::ostream* os) override;
+
+ private:
+  void OnKeysChanged();
+
+  bool keys_changed_;
+  std::unique_ptr<syncer::TrustedVaultClient::Subscription> subscription_;
+};
+
 // Helper for setting scrypt-related feature flags.
 // NOTE: DO NOT INSTANTIATE THIS CLASS IN THE TEST BODY FOR INTEGRATION TESTS!
 // That causes data races, see crbug.com/915219. Instead, instantiate it in the
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
index 468404a4..015d6de 100644
--- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -14,6 +14,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "chrome/browser/sync/sync_ui_util.h"
+#include "chrome/browser/sync/test/integration/cookie_helper.h"
 #include "chrome/browser/sync/test/integration/encryption_helper.h"
 #include "chrome/browser/sync/test/integration/passwords_helper.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
@@ -677,6 +678,60 @@
   EXPECT_FALSE(sync_ui_util::ShouldShowSyncKeysMissingError(GetSyncService(0)));
 }
 
+IN_PROC_BROWSER_TEST_F(
+    SingleClientNigoriWithWebApiTest,
+    PRE_ShouldClearEncryptionKeysFromTheWebWhenSigninCookiesCleared) {
+  const std::string kTestEncryptionKey = "testpassphrase1";
+  const GURL retrieval_url =
+      GetTrustedVaultRetrievalURL(*embedded_test_server(), kTestEncryptionKey);
+
+  ASSERT_TRUE(SetupClients());
+
+  // Explicitly add signin cookie (normally it would be done during the keys
+  // retrieval or before it).
+  cookie_helper::AddSigninCookie(GetProfile(0));
+
+  TrustedVaultKeysChangedStateChecker keys_fetched_checker(GetSyncService(0));
+  // Mimic opening a web page where the user can interact with the retrieval
+  // flow, while the user is signed out.
+  sync_ui_util::OpenTabForSyncKeyRetrievalWithURLForTesting(GetBrowser(0),
+                                                            retrieval_url);
+  ASSERT_THAT(GetBrowser(0)->tab_strip_model()->GetActiveWebContents(),
+              NotNull());
+
+  // Wait until the title changes to "OK" via Javascript, which indicates
+  // completion.
+  PageTitleChecker title_checker(
+      /*expected_title=*/"OK",
+      GetBrowser(0)->tab_strip_model()->GetActiveWebContents());
+  EXPECT_TRUE(title_checker.Wait());
+  EXPECT_TRUE(keys_fetched_checker.Wait());
+
+  // Mimic signin cookie clearing.
+  TrustedVaultKeysChangedStateChecker keys_cleared_checker(GetSyncService(0));
+  cookie_helper::DeleteSigninCookies(GetProfile(0));
+  EXPECT_TRUE(keys_cleared_checker.Wait());
+}
+
+IN_PROC_BROWSER_TEST_F(
+    SingleClientNigoriWithWebApiTest,
+    ShouldClearEncryptionKeysFromTheWebWhenSigninCookiesCleared) {
+  const std::string kTestEncryptionKey = "testpassphrase1";
+
+  // Mimic the account being already using a trusted vault passphrase.
+  encryption_helper::SetNigoriInFakeServer(
+      GetFakeServer(), BuildTrustedVaultNigoriSpecifics({kTestEncryptionKey}));
+
+  // Sign in and start sync.
+  ASSERT_TRUE(SetupSync());
+
+  EXPECT_TRUE(GetSyncService(0)
+                  ->GetUserSettings()
+                  ->IsTrustedVaultKeyRequiredForPreferredDataTypes());
+  EXPECT_FALSE(GetSyncService(0)->GetActiveDataTypes().Has(syncer::PASSWORDS));
+  EXPECT_TRUE(sync_ui_util::ShouldShowSyncKeysMissingError(GetSyncService(0)));
+}
+
 // Same as SingleClientNigoriWithWebApiTest but does NOT override
 // switches::kGaiaUrl, which means the embedded test server gets treated as
 // untrusted origin.
diff --git a/chrome/browser/sync/trusted_vault_client_android.cc b/chrome/browser/sync/trusted_vault_client_android.cc
index a8a068d..b778dc4b 100644
--- a/chrome/browser/sync/trusted_vault_client_android.cc
+++ b/chrome/browser/sync/trusted_vault_client_android.cc
@@ -103,6 +103,10 @@
   NOTREACHED();
 }
 
+void TrustedVaultClientAndroid::RemoveAllStoredKeys() {
+  // StoreKeys() not supported on Android, nothing to remove.
+}
+
 void TrustedVaultClientAndroid::MarkKeysAsStale(
     const CoreAccountInfo& account_info,
     base::OnceCallback<void(bool)> cb) {
diff --git a/chrome/browser/sync/trusted_vault_client_android.h b/chrome/browser/sync/trusted_vault_client_android.h
index 9ca98fea..a3101045 100644
--- a/chrome/browser/sync/trusted_vault_client_android.h
+++ b/chrome/browser/sync/trusted_vault_client_android.h
@@ -55,6 +55,7 @@
   void StoreKeys(const std::string& gaia_id,
                  const std::vector<std::vector<uint8_t>>& keys,
                  int last_key_version) override;
+  void RemoveAllStoredKeys() override;
   void MarkKeysAsStale(const CoreAccountInfo& account_info,
                        base::OnceCallback<void(bool)> cb) override;
 
diff --git a/chrome/browser/task_manager/providers/render_process_host_task_provider.cc b/chrome/browser/task_manager/providers/render_process_host_task_provider.cc
index 8629b0e..e24aa460 100644
--- a/chrome/browser/task_manager/providers/render_process_host_task_provider.cc
+++ b/chrome/browser/task_manager/providers/render_process_host_task_provider.cc
@@ -25,16 +25,9 @@
 
 namespace task_manager {
 
-RenderProcessHostTaskProvider::RenderProcessHostTaskProvider() {
-  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
-                 content::NotificationService::AllBrowserContextsAndSources());
-  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
-                 content::NotificationService::AllBrowserContextsAndSources());
-  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
-                 content::NotificationService::AllBrowserContextsAndSources());
-}
+RenderProcessHostTaskProvider::RenderProcessHostTaskProvider() = default;
 
-RenderProcessHostTaskProvider::~RenderProcessHostTaskProvider() {}
+RenderProcessHostTaskProvider::~RenderProcessHostTaskProvider() = default;
 
 Task* RenderProcessHostTaskProvider::GetTaskOfUrlRequest(int child_id,
                                                          int route_id) {
@@ -59,11 +52,20 @@
       // from the notification service.
     }
   }
+
+  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
+                 content::NotificationService::AllBrowserContextsAndSources());
+  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
+                 content::NotificationService::AllBrowserContextsAndSources());
+  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
+                 content::NotificationService::AllBrowserContextsAndSources());
 }
 
 void RenderProcessHostTaskProvider::StopUpdating() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+  registrar_.RemoveAll();
+
   // Then delete all tasks (if any).
   tasks_by_rph_id_.clear();
 }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 62846310..bb7698b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3676,8 +3676,6 @@
       "app_list/arc/arc_app_icon.h",
       "app_list/arc/arc_app_icon_descriptor.cc",
       "app_list/arc/arc_app_icon_descriptor.h",
-      "app_list/arc/arc_app_icon_loader.cc",
-      "app_list/arc/arc_app_icon_loader.h",
       "app_list/arc/arc_app_launcher.cc",
       "app_list/arc/arc_app_launcher.h",
       "app_list/arc/arc_app_list_prefs.cc",
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
deleted file mode 100644
index bde46f8..0000000
--- a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 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.
-
-#include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "ui/gfx/image/image_skia_operations.h"
-
-ArcAppIconLoader::ArcAppIconLoader(Profile* profile,
-                                   int icon_size_in_dip,
-                                   AppIconLoaderDelegate* delegate)
-    : AppIconLoader(profile, icon_size_in_dip, delegate),
-      arc_prefs_(ArcAppListPrefs::Get(profile)) {
-  DCHECK(arc_prefs_);
-  arc_prefs_->AddObserver(this);
-}
-
-ArcAppIconLoader::~ArcAppIconLoader() {
-  arc_prefs_->RemoveObserver(this);
-}
-
-bool ArcAppIconLoader::CanLoadImageForApp(const std::string& app_id) {
-  if (icon_map_.find(app_id) != icon_map_.end())
-    return true;
-  return arc::IsArcItem(profile(), app_id);
-}
-
-void ArcAppIconLoader::FetchImage(const std::string& app_id) {
-  if (icon_map_.find(app_id) != icon_map_.end())
-    return;  // Already loading the image.
-
-  // Note, ARC icon is available only for 48x48 dips. In case
-  // |icon_size_in_dip_| differs from this size, re-scale is required.
-  std::unique_ptr<ArcAppIcon> icon =
-      std::make_unique<ArcAppIcon>(profile(), app_id, icon_size_in_dip(), this);
-  icon->LoadSupportedScaleFactors();
-  icon_map_[app_id] = std::move(icon);
-  UpdateImage(app_id);
-}
-
-void ArcAppIconLoader::ClearImage(const std::string& app_id) {
-  icon_map_.erase(app_id);
-}
-
-void ArcAppIconLoader::UpdateImage(const std::string& app_id) {
-  AppIDToIconMap::iterator it = icon_map_.find(app_id);
-  if (it == icon_map_.end())
-    return;
-
-  gfx::ImageSkia image = it->second->image_skia();
-  DCHECK_EQ(icon_size_in_dip(), image.width());
-  DCHECK_EQ(icon_size_in_dip(), image.height());
-
-  std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
-      arc_prefs_->GetApp(app_id);
-  if (app_info && app_info->suspended) {
-    image =
-        gfx::ImageSkiaOperations::CreateHSLShiftedImage(image, {-1, 0, 0.6});
-  }
-
-  delegate()->OnAppImageUpdated(app_id, image);
-}
-
-void ArcAppIconLoader::OnIconUpdated(ArcAppIcon* icon) {
-  UpdateImage(icon->app_id());
-}
-
-void ArcAppIconLoader::OnAppStatesChanged(
-    const std::string& app_id,
-    const ArcAppListPrefs::AppInfo& app_info) {
-  AppIDToIconMap::const_iterator it = icon_map_.find(app_id);
-  if (it == icon_map_.end())
-    return;
-
-  UpdateImage(app_id);
-}
-
-void ArcAppIconLoader::OnAppIconUpdated(
-    const std::string& app_id,
-    const ArcAppIconDescriptor& descriptor) {
-  if (descriptor.dip_size != icon_size_in_dip())
-    return;
-  AppIDToIconMap::const_iterator it = icon_map_.find(app_id);
-  if (it == icon_map_.end())
-    return;
-  it->second->LoadForScaleFactor(descriptor.scale_factor);
-}
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.h b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.h
deleted file mode 100644
index f87a3ef..0000000
--- a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 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.
-
-#ifndef CHROME_BROWSER_UI_APP_LIST_ARC_ARC_APP_ICON_LOADER_H_
-#define CHROME_BROWSER_UI_APP_LIST_ARC_ARC_APP_ICON_LOADER_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "chrome/browser/ui/app_icon_loader.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
-
-class Profile;
-
-// ARC++ icon provider for the apps. It can support multiple ARC++ apps. This
-// observes apps changes and updates icons accordingly.
-class ArcAppIconLoader : public AppIconLoader,
-                         public ArcAppListPrefs::Observer,
-                         public ArcAppIcon::Observer {
- public:
-  ArcAppIconLoader(Profile* profile,
-                   int icon_size_in_dip,
-                   AppIconLoaderDelegate* delegate);
-  ~ArcAppIconLoader() override;
-
-  // Overrides AppIconLoader:
-  bool CanLoadImageForApp(const std::string& app_id) override;
-  void FetchImage(const std::string& id) override;
-  void ClearImage(const std::string& id) override;
-  void UpdateImage(const std::string& id) override;
-
-  // Overrides ArcAppListPrefs::Observer:
-  void OnAppStatesChanged(const std::string& app_id,
-                          const ArcAppListPrefs::AppInfo& app_info) override;
-  void OnAppIconUpdated(const std::string& id,
-                        const ArcAppIconDescriptor& descriptor) override;
-
-  // Overrides ArcAppIcon::Observer:
-  void OnIconUpdated(ArcAppIcon* icon) override;
-
- private:
-  using AppIDToIconMap = std::map<std::string, std::unique_ptr<ArcAppIcon>>;
-
-  // Unowned pointer.
-  ArcAppListPrefs* const arc_prefs_;
-
-  AppIDToIconMap icon_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(ArcAppIconLoader);
-};
-
-#endif  // CHROME_BROWSER_UI_APP_LIST_ARC_ARC_APP_ICON_LOADER_H_
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index 7f73f67..ec092be 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -44,7 +44,6 @@
 #include "chrome/browser/ui/app_list/app_service/app_service_app_item.h"
 #include "chrome/browser/ui/app_list/app_service/app_service_app_model_builder.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_launcher.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h"
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index 90e4972..14fab3a6 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -37,7 +37,6 @@
 #include "chrome/browser/ui/app_list/app_list_client_impl.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
 #include "chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/app_list/md_icon_normalizer.h"
 #include "chrome/browser/ui/apps/app_info_dialog.h"
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index de37567..895d919 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -35,7 +35,6 @@
 #include "chrome/browser/content_settings/local_shared_objects_container.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
 #include "chrome/browser/permissions/permission_manager.h"
 #include "chrome/browser/profiles/profile.h"
@@ -56,6 +55,7 @@
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/permissions/chooser_context_base.h"
 #include "components/permissions/permission_decision_auto_blocker.h"
 #include "components/permissions/permission_result.h"
 #include "components/permissions/permission_uma_util.h"
diff --git a/chrome/browser/ui/page_info/page_info_ui.h b/chrome/browser/ui/page_info/page_info_ui.h
index 90619d77..5b1dc497 100644
--- a/chrome/browser/ui/page_info/page_info_ui.h
+++ b/chrome/browser/ui/page_info/page_info_ui.h
@@ -11,10 +11,10 @@
 
 #include "base/strings/string16.h"
 #include "build/build_config.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/ui/page_info/page_info.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "components/permissions/chooser_context_base.h"
 #include "components/safe_browsing/buildflags.h"
 #include "ui/gfx/native_widget_types.h"
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
index c261dc1..94d7f2b 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
@@ -50,10 +50,6 @@
       : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
 
   void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {network::features::kFetchMetadata,
-         network::features::kFetchMetadataDestination},
-        {});
     InProcessBrowserTest::SetUp();
   }
 
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc b/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
index ae89330e..8e9bd6a 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
@@ -52,6 +52,11 @@
   }
 
   void ShowUi(const std::string& name) override {
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+    // The extensions menu can appear offscreen on Linux, so verifying bounds
+    // makes the tests flaky.
+    set_should_verify_dialog_bounds(false);
+#endif
     ui_test_name_ = name;
 
     if (name == "ReloadPageBubble") {
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc
index 7d1a3d0..b6e4718 100644
--- a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc
+++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc
@@ -133,9 +133,9 @@
 }
 
 gfx::Size PermissionPromptBubbleView::CalculatePreferredSize() const {
-  const int width = std::min(views::View::CalculatePreferredSize().width(),
-                             ChromeLayoutProvider::Get()->GetDistanceMetric(
-                                 DISTANCE_BUBBLE_PREFERRED_WIDTH));
+  const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
+                        DISTANCE_BUBBLE_PREFERRED_WIDTH) -
+                    margins().width();
   return gfx::Size(width, GetHeightForWidth(width));
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 4638f862..fc3f52cf 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -577,6 +577,8 @@
   }
   public_saml_url_fetcher_.reset();
 
+  was_security_token_pin_canceled_ = false;
+
   frame_state_ = FRAME_STATE_LOADING;
   CallJS("login.GaiaSigninScreen.loadAuthExtension", params);
 }
@@ -782,11 +784,14 @@
                  << net::ErrorToShortString(error_code);
     return;
   }
-  if (error_code == net::ERR_TIMED_OUT &&
-      is_security_token_pin_dialog_running()) {
-    // Timeout errors are expected when the security token PIN is not entered by
-    // the user on time. In that case, return the user back to the first sign-in
-    // step instead of showing the network error screen.
+  if ((was_security_token_pin_canceled_ && error_code == net::ERR_FAILED) ||
+      (is_security_token_pin_dialog_running() &&
+       error_code == net::ERR_TIMED_OUT)) {
+    // Specific errors are expected when the security token PIN is aborted
+    // (either with a generic failure if the user canceled the dialog, or with a
+    // timeout error if the user didn't enter it on time). In that case, return
+    // the user back to the first sign-in step instead of showing the network
+    // error screen.
     ReloadGaia(/*force_reload=*/true);
     return;
   }
@@ -1116,6 +1121,7 @@
     return;
   }
 
+  was_security_token_pin_canceled_ = user_input.empty();
   if (user_input.empty()) {
     security_token_pin_entered_callback_.Reset();
     std::move(security_token_pin_dialog_closed_callback_).Run();
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
index 42d1e9f0..ff64497 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -446,6 +446,9 @@
   // Is non-empty iff the dialog is active.
   SecurityTokenPinDialogClosedCallback
       security_token_pin_dialog_closed_callback_;
+  // Whether the PIN dialog shown during the current authentication attempt was
+  // canceled by the user.
+  bool was_security_token_pin_canceled_ = false;
 
   // Handler for |samlChallengeMachineKey| request.
   std::unique_ptr<SamlChallengeKeyHandler> saml_challenge_key_handler_;
diff --git a/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.cc
index 2f06f0d..9100315 100644
--- a/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.cc
@@ -4,8 +4,10 @@
 
 #include "chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.h"
 
+#include "base/command_line.h"
 #include "chrome/browser/chromeos/login/screens/marketing_opt_in_screen.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/constants/chromeos_switches.h"
 #include "components/login/localized_values_builder.h"
 
 namespace chromeos {
@@ -55,6 +57,14 @@
               &MarketingOptInScreenHandler::HandleAllSet);
 }
 
+void MarketingOptInScreenHandler::GetAdditionalParameters(
+    base::DictionaryValue* parameters) {
+  parameters->SetBoolean("enableMarketingOptIn",
+                         base::CommandLine::ForCurrentProcess()->HasSwitch(
+                             chromeos::switches::kEnableMarketingOptInScreen));
+  BaseScreenHandler::GetAdditionalParameters(parameters);
+}
+
 void MarketingOptInScreenHandler::HandleAllSet(
     bool play_communications_opt_in,
     bool tips_communications_opt_in) {
diff --git a/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.h
index 8038b22..50ae413 100644
--- a/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.h
@@ -56,6 +56,7 @@
   // BaseScreenHandler:
   void Initialize() override;
   void RegisterMessages() override;
+  void GetAdditionalParameters(base::DictionaryValue* parameters) override;
 
   // WebUI event handler.
   void HandleAllSet(bool play_communications_opt_in,
diff --git a/chrome/browser/ui/webui/policy_ui_handler.cc b/chrome/browser/ui/webui/policy_ui_handler.cc
index 943ebee..31439866 100644
--- a/chrome/browser/ui/webui/policy_ui_handler.cc
+++ b/chrome/browser/ui/webui/policy_ui_handler.cc
@@ -918,7 +918,7 @@
 #if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
   if (updater_policies_) {
     base::Value updater_policies(base::Value::Type::DICTIONARY);
-    updater_policies.SetStringKey("name", "Updater Policies");
+    updater_policies.SetStringKey("name", "Google Update Policies");
     updater_policies.SetKey("policyNames", GetGoogleUpdatePolicyNames());
     names.SetKey("updater", std::move(updater_policies));
   }
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index b5d43d9..bad12e3 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/hid/hid_chooser_context_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/media/unified_autoplay_config.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
 #include "chrome/browser/permissions/permission_manager.h"
 #include "chrome/browser/serial/serial_chooser_context.h"
@@ -46,6 +45,7 @@
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "components/crx_file/id_util.h"
+#include "components/permissions/chooser_context_base.h"
 #include "components/permissions/permission_decision_auto_blocker.h"
 #include "components/permissions/permission_uma_util.h"
 #include "components/permissions/permission_util.h"
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.h b/chrome/browser/ui/webui/settings/site_settings_handler.h
index f665a452..63cb145 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.h
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -13,7 +13,6 @@
 #include "base/containers/flat_set.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/browsing_data/cookies_tree_model.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_observer.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
@@ -21,6 +20,7 @@
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/permissions/chooser_context_base.h"
 #include "content/public/browser/host_zoom_map.h"
 #include "ppapi/buildflags/buildflags.h"
 
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index f7d8670..14d5a4b 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/permissions/chooser_context_base_mock_permission_observer.h"
 #include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -45,6 +44,7 @@
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/infobars/core/infobar.h"
+#include "components/permissions/chooser_context_base.h"
 #include "components/permissions/permission_decision_auto_blocker.h"
 #include "components/permissions/permission_uma_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
diff --git a/chrome/browser/ui/webui/site_settings_helper.cc b/chrome/browser/ui/webui/site_settings_helper.cc
index d81d8f1..c5ccbfe 100644
--- a/chrome/browser/ui/webui/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/site_settings_helper.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/hid/hid_chooser_context.h"
 #include "chrome/browser/hid/hid_chooser_context_factory.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/permissions/permission_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/serial/serial_chooser_context.h"
@@ -28,6 +27,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
+#include "components/permissions/chooser_context_base.h"
 #include "components/permissions/permission_result.h"
 #include "components/prefs/pref_service.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
diff --git a/chrome/browser/ui/webui/site_settings_helper_unittest.cc b/chrome/browser/ui/webui/site_settings_helper_unittest.cc
index 7f23d9b..18ddbba 100644
--- a/chrome/browser/ui/webui/site_settings_helper_unittest.cc
+++ b/chrome/browser/ui/webui/site_settings_helper_unittest.cc
@@ -8,7 +8,6 @@
 #include "base/guid.h"
 #include "base/json/json_reader.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
 #include "chrome/common/pref_names.h"
@@ -19,6 +18,7 @@
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/content_settings/core/test/content_settings_mock_provider.h"
 #include "components/content_settings/core/test/content_settings_test_utils.h"
+#include "components/permissions/chooser_context_base.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/upgrade_detector/build_state.cc b/chrome/browser/upgrade_detector/build_state.cc
index 70af1de..d6d3ca5 100644
--- a/chrome/browser/upgrade_detector/build_state.cc
+++ b/chrome/browser/upgrade_detector/build_state.cc
@@ -47,6 +47,11 @@
   observers_.RemoveObserver(observer);
 }
 
+bool BuildState::HasObserver(const BuildStateObserver* observer) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return observers_.HasObserver(observer);
+}
+
 void BuildState::NotifyObserversOnUpdate() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   for (auto& observer : observers_)
diff --git a/chrome/browser/upgrade_detector/build_state.h b/chrome/browser/upgrade_detector/build_state.h
index 35b3ce6a..23666d52 100644
--- a/chrome/browser/upgrade_detector/build_state.h
+++ b/chrome/browser/upgrade_detector/build_state.h
@@ -70,6 +70,7 @@
 
   void AddObserver(BuildStateObserver* observer);
   void RemoveObserver(const BuildStateObserver* observer);
+  bool HasObserver(const BuildStateObserver* observer) const;
 
  private:
   void NotifyObserversOnUpdate();
diff --git a/chrome/browser/usb/usb_chooser_context.cc b/chrome/browser/usb/usb_chooser_context.cc
index bb0cfc86..8bba3f2 100644
--- a/chrome/browser/usb/usb_chooser_context.cc
+++ b/chrome/browser/usb/usb_chooser_context.cc
@@ -16,6 +16,7 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/usb/usb_blocklist.h"
 #include "chrome/common/pref_names.h"
@@ -131,9 +132,9 @@
 void UsbChooserContext::DeviceObserver::OnDeviceManagerConnectionError() {}
 
 UsbChooserContext::UsbChooserContext(Profile* profile)
-    : ChooserContextBase(profile,
-                         ContentSettingsType::USB_GUARD,
-                         ContentSettingsType::USB_CHOOSER_DATA),
+    : ChooserContextBase(ContentSettingsType::USB_GUARD,
+                         ContentSettingsType::USB_CHOOSER_DATA,
+                         HostContentSettingsMapFactory::GetForProfile(profile)),
       is_incognito_(profile->IsOffTheRecord()) {
 #if defined(OS_CHROMEOS)
   bool is_signin_profile = chromeos::ProfileHelper::IsSigninProfile(profile);
diff --git a/chrome/browser/usb/usb_chooser_context.h b/chrome/browser/usb/usb_chooser_context.h
index 505b24e..519c7d0 100644
--- a/chrome/browser/usb/usb_chooser_context.h
+++ b/chrome/browser/usb/usb_chooser_context.h
@@ -17,8 +17,8 @@
 #include "base/observer_list.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/usb/usb_policy_allowed_devices.h"
+#include "components/permissions/chooser_context_base.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -27,6 +27,8 @@
 #include "services/device/public/mojom/usb_manager_client.mojom.h"
 #include "url/origin.h"
 
+class Profile;
+
 class UsbChooserContext : public ChooserContextBase,
                           public device::mojom::UsbDeviceManagerClient {
  public:
diff --git a/chrome/common/extensions/docs/templates/articles/xhr.html b/chrome/common/extensions/docs/templates/articles/xhr.html
index 80a77543..c1ee41b 100644
--- a/chrome/common/extensions/docs/templates/articles/xhr.html
+++ b/chrome/common/extensions/docs/templates/articles/xhr.html
@@ -243,19 +243,3 @@
 adding either the <code>connect-src</code> or <code>default-src</code>
 directives.
 </p>
-
-<h2 id="origin-neither-GET-nor-HEAD">Origin header for requests with neither
-GET nor HEAD methods</h2>
-
-<p>A request with an HTTP method that is neither GET nor HEAD (such as POST)
-always has an Origin header, regardless of whether the request is cross-origin.
-<span class="availability">Starting from Chrome 80</span>, the origin of
-the destination URL, not the extension's security origin, is attached to the
-header if all of the following conditions are met:
-<ul>
-<li>The request is made in a frame with the extension's security origin, such as
-the background page, the popup, or an extension tab.</li>
-<li>The request has a method that is neither GET nor HEAD.</li>
-<li>The extension has cross-origin permission to the destination URL.</li>
-</ul>
-</p>
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index e2e4951..7a48e12 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -171,9 +171,9 @@
 #include "extensions/renderer/dispatcher.h"
 #include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h"
 #include "extensions/renderer/renderer_extension_registry.h"
+#include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/public/web/web_settings.h"
 #include "third_party/blink/public/web/web_view.h"
-#include "ui/native_theme/native_theme.h"
 #endif
 
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -900,8 +900,12 @@
         if (GURL(frame->GetDocument().Url()).host_piece() ==
             extension_misc::kPdfExtensionId) {
           if (!base::FeatureList::IsEnabled(features::kWebUIDarkMode)) {
-            ui::NativeTheme::GetInstanceForWeb()->set_preferred_color_scheme(
-                ui::NativeTheme::PreferredColorScheme::kLight);
+            auto* render_view = render_frame->GetRenderView();
+            auto* web_view = render_view ? render_view->GetWebView() : nullptr;
+            if (web_view) {
+              web_view->GetSettings()->SetPreferredColorScheme(
+                  blink::PreferredColorScheme::kLight);
+            }
           }
         } else if (info.name ==
                    ASCIIToUTF16(ChromeContentClient::kPDFExtensionPluginName)) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 78a90d1..d1ca426 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2274,6 +2274,7 @@
         "../browser/chromeos/login/screens/fingerprint_setup_browsertest.cc",
         "../browser/chromeos/login/screens/gesture_navigation_screen_browsertest.cc",
         "../browser/chromeos/login/screens/hid_detection_screen_browsertest.cc",
+        "../browser/chromeos/login/screens/marketing_opt_in_screen_browsertest.cc",
         "../browser/chromeos/login/screens/mock_arc_terms_of_service_screen.cc",
         "../browser/chromeos/login/screens/mock_arc_terms_of_service_screen.h",
         "../browser/chromeos/login/screens/mock_demo_preferences_screen.cc",
@@ -6205,6 +6206,8 @@
         "../browser/sync/test/integration/await_match_status_change_checker.h",
         "../browser/sync/test/integration/bookmarks_helper.cc",
         "../browser/sync/test/integration/bookmarks_helper.h",
+        "../browser/sync/test/integration/cookie_helper.cc",
+        "../browser/sync/test/integration/cookie_helper.h",
         "../browser/sync/test/integration/dictionary_helper.cc",
         "../browser/sync/test/integration/dictionary_helper.h",
         "../browser/sync/test/integration/dictionary_load_observer.cc",
@@ -6277,8 +6280,10 @@
       "//components/sync",
       "//components/sync:test_support_model",
       "//components/sync/test/fake_server",
+      "//content/public/browser",
       "//content/test:test_support",
       "//net",
+      "//services/network/public/mojom",
       "//skia",
     ]
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/Features.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/Features.java
index 459ccab9..471ffb46 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/Features.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/Features.java
@@ -10,6 +10,7 @@
 
 import org.chromium.base.CommandLine;
 import org.chromium.base.test.util.AnnotationRule;
+import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 
 import java.lang.annotation.Annotation;
@@ -88,12 +89,14 @@
 
     private void applyForJUnit() {
         ChromeFeatureList.setTestFeatures(mRegisteredState);
+        CachedFeatureFlags.setFeaturesForTesting(mRegisteredState);
     }
 
     private void applyForInstrumentation() {
         ChromeFeatureList.setTestCanUseDefaultsForTesting();
         mergeFeatureLists("enable-features", true);
         mergeFeatureLists("disable-features", false);
+        CachedFeatureFlags.setFeaturesForTesting(mRegisteredState);
     }
 
     /**
@@ -123,6 +126,7 @@
         sInstance = null;
         ChromeFeatureList.setTestFeatures(null);
         ChromeFeatureList.resetTestCanUseDefaultsForTesting();
+        CachedFeatureFlags.resetFlagsForTesting();
     }
 
     /**
diff --git a/chrome/test/data/android/render_tests/TabGroupUiTest.10th_tab_selected.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/TabGroupUiTest.10th_tab_selected.Nexus_5-19.png.sha1
new file mode 100644
index 0000000..056c61a
--- /dev/null
+++ b/chrome/test/data/android/render_tests/TabGroupUiTest.10th_tab_selected.Nexus_5-19.png.sha1
@@ -0,0 +1 @@
+661913928237b92a6e9b775bc6b25e9a9bfd70d8
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/TabGroupUiTest.10th_tab_selected.Nexus_5X-23.png.sha1 b/chrome/test/data/android/render_tests/TabGroupUiTest.10th_tab_selected.Nexus_5X-23.png.sha1
new file mode 100644
index 0000000..3e95b19b
--- /dev/null
+++ b/chrome/test/data/android/render_tests/TabGroupUiTest.10th_tab_selected.Nexus_5X-23.png.sha1
@@ -0,0 +1 @@
+df99ffecab9d4e5a9f14aa251a0c4d34d71a8abe
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/TabGroupUiTest.11th_tab_selected.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/TabGroupUiTest.11th_tab_selected.Nexus_5-19.png.sha1
new file mode 100644
index 0000000..056c61a
--- /dev/null
+++ b/chrome/test/data/android/render_tests/TabGroupUiTest.11th_tab_selected.Nexus_5-19.png.sha1
@@ -0,0 +1 @@
+661913928237b92a6e9b775bc6b25e9a9bfd70d8
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/TabGroupUiTest.11th_tab_selected.Nexus_5X-23.png.sha1 b/chrome/test/data/android/render_tests/TabGroupUiTest.11th_tab_selected.Nexus_5X-23.png.sha1
new file mode 100644
index 0000000..3e95b19b
--- /dev/null
+++ b/chrome/test/data/android/render_tests/TabGroupUiTest.11th_tab_selected.Nexus_5X-23.png.sha1
@@ -0,0 +1 @@
+df99ffecab9d4e5a9f14aa251a0c4d34d71a8abe
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/TabGroupUiTest.5th_tab_selected.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/TabGroupUiTest.5th_tab_selected.Nexus_5-19.png.sha1
new file mode 100644
index 0000000..0fa0e96
--- /dev/null
+++ b/chrome/test/data/android/render_tests/TabGroupUiTest.5th_tab_selected.Nexus_5-19.png.sha1
@@ -0,0 +1 @@
+edb4497eafe8220b91051e45f71bc8710c0cadcb
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/TabGroupUiTest.5th_tab_selected.Nexus_5X-23.png.sha1 b/chrome/test/data/android/render_tests/TabGroupUiTest.5th_tab_selected.Nexus_5X-23.png.sha1
new file mode 100644
index 0000000..3a2d652
--- /dev/null
+++ b/chrome/test/data/android/render_tests/TabGroupUiTest.5th_tab_selected.Nexus_5X-23.png.sha1
@@ -0,0 +1 @@
+fb7e128ac8cf0a030f4e80fb50cd5500f6cbb3ab
\ No newline at end of file
diff --git a/chrome/test/data/prefers-color-scheme.html b/chrome/test/data/prefers-color-scheme.html
new file mode 100644
index 0000000..f8913c1
--- /dev/null
+++ b/chrome/test/data/prefers-color-scheme.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>FAIL</title>
+<script>
+  if (window.matchMedia("(prefers-color-scheme: light)").matches) {
+    document.title = "light";
+  } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+    document.title = "dark";
+  } else if (window.matchMedia("(prefers-color-scheme: no-preference)").matches) {
+    document.title = "no-preference";
+  }
+</script>
diff --git a/chrome/test/data/webui/settings/site_list_tests.js b/chrome/test/data/webui/settings/site_list_tests.js
index dd2cf78..68359bc2 100644
--- a/chrome/test/data/webui/settings/site_list_tests.js
+++ b/chrome/test/data/webui/settings/site_list_tests.js
@@ -1044,7 +1044,7 @@
   setup(function() {
     cookieException = {
       category: settings.ContentSettingsTypes.COOKIES,
-      embeddingOrigin: 'http://foo.com',
+      embeddingOrigin: settings.SITE_EXCEPTION_WILDCARD,
       incognito: false,
       setting: settings.ContentSetting.BLOCK,
       enforcement: '',
@@ -1116,7 +1116,7 @@
         })
         .then(function(args) {
           assertEquals(newValue, args[0]);
-          assertEquals(newValue, args[1]);
+          assertEquals(settings.SITE_EXCEPTION_WILDCARD, args[1]);
           assertEquals(settings.ContentSettingsTypes.COOKIES, args[2]);
           assertEquals(cookieException.setting, args[3]);
           assertEquals(cookieException.incognito, args[4]);
diff --git a/chrome/test/data/webui/tab_strip/tab_swiper_test.js b/chrome/test/data/webui/tab_strip/tab_swiper_test.js
index f22c99f..5f971563 100644
--- a/chrome/test/data/webui/tab_strip/tab_swiper_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_swiper_test.js
@@ -25,7 +25,7 @@
     tabSwiper.startObserving();
   });
 
-  test('swiping progresses the animation', () => {
+  test('swiping does not progress the animation', () => {
     // Set margin top 0 to avoid offsetting the bounding client rect.
     document.body.style.margin = 0;
 
@@ -47,30 +47,24 @@
     let startTop = tabElement.getBoundingClientRect().top;
     assertEquals(startTop, 0);
 
-    // Swipe was enough to start animating the position.
+    // Swiping did not start the animation.
     pointerState.clientY = startY - (TRANSLATE_ANIMATION_THRESHOLD_PX + 1);
     tabElement.dispatchEvent(new PointerEvent('pointermove', pointerState));
     assertEquals(tabElStyle.maxWidth, `${tabWidth}px`);
-    let top = tabElement.getBoundingClientRect().top;
-    assertTrue(top < startTop && top > -1 * SWIPE_FINISH_THRESHOLD_PX);
+    assertEquals(startTop, tabElement.getBoundingClientRect().top);
 
-    // Swipe was enough to start animating max width and opacity.
+    // Swipe was enough to close but did not yet animate.
     pointerState.clientY = startY - (SWIPE_START_THRESHOLD_PX + 1);
     tabElement.dispatchEvent(new PointerEvent('pointermove', pointerState));
-    assertTrue(
-        parseInt(tabElStyle.maxWidth) > 0 &&
-        parseInt(tabElStyle.maxWidth) < tabWidth);
-    assertTrue(
-        parseFloat(tabElStyle.opacity) > 0 &&
-        parseFloat(tabElStyle.opacity) < 1);
+    assertEquals(tabWidth, parseInt(tabElStyle.maxWidth, 10));
+    assertEquals('1', tabElStyle.opacity);
 
-    // Swipe was enough to finish animating.
+    // Verify animation still not progressed.
     pointerState.clientY = startY - (SWIPE_FINISH_THRESHOLD_PX + 1);
     tabElement.dispatchEvent(new PointerEvent('pointermove', pointerState));
-    assertEquals(tabElStyle.maxWidth, '0px');
-    assertEquals(tabElStyle.opacity, '0');
-    assertEquals(
-        tabElement.getBoundingClientRect().top, -SWIPE_FINISH_THRESHOLD_PX);
+    assertEquals(tabWidth, parseInt(tabElStyle.maxWidth, 10));
+    assertEquals('1', tabElStyle.opacity);
+    assertEquals(startTop, tabElement.getBoundingClientRect().top);
   });
 
   test('finishing the swipe animation fires an event', async () => {
@@ -86,24 +80,6 @@
     await firedEventPromise;
   });
 
-  test('swiping enough and releasing finishes the animation', async () => {
-    const firedEventPromise = eventToPromise('swipe', tabElement);
-
-    const tabElStyle = window.getComputedStyle(tabElement);
-    const startY = 50;
-
-    const pointerState = {clientY: 50, pointerId: 1};
-    tabElement.dispatchEvent(new PointerEvent('pointerdown', pointerState));
-
-    pointerState.clientY = startY - (SWIPE_START_THRESHOLD_PX + 1);
-    pointerState.movementY = 1; /* Any non-0 value here is fine. */
-    tabElement.dispatchEvent(new PointerEvent('pointermove', pointerState));
-    tabElement.dispatchEvent(new PointerEvent('pointerup', pointerState));
-    await firedEventPromise;
-    assertEquals(tabElStyle.maxWidth, '0px');
-    assertEquals(tabElStyle.opacity, '0');
-  });
-
   test('swiping and letting go before resets animation', () => {
     tabElement.style.setProperty('--tabstrip-tab-width', '100px');
     const tabElStyle = window.getComputedStyle(tabElement);
@@ -121,7 +97,7 @@
     assertEquals(tabElStyle.opacity, '1');
   });
 
-  test('swiping fast enough finishes playing the animation', async () => {
+  test('swiping fast enough fires an event', async () => {
     const tabElStyle = window.getComputedStyle(tabElement);
     const firedEventPromise = eventToPromise('swipe', tabElement);
     const startY = 50;
@@ -136,8 +112,6 @@
     tabElement.dispatchEvent(new PointerEvent('pointerup', pointerState));
 
     await firedEventPromise;
-    assertEquals(tabElStyle.maxWidth, '0px');
-    assertEquals(tabElStyle.opacity, '0');
   });
 
   test('pointerdown should reset the animation time', async () => {
diff --git a/chromeos/dbus/kerberos/fake_kerberos_client.cc b/chromeos/dbus/kerberos/fake_kerberos_client.cc
index 04938c42..4b60b9f 100644
--- a/chromeos/dbus/kerberos/fake_kerberos_client.cc
+++ b/chromeos/dbus/kerberos/fake_kerberos_client.cc
@@ -121,14 +121,14 @@
   if (it != accounts_.end()) {
     it->is_managed |= request.is_managed();
     PostResponse(std::move(callback), kerberos::ERROR_DUPLICATE_PRINCIPAL_NAME,
-                 mTaskDelay);
+                 task_delay_);
     return;
   }
 
   AccountData data(request.principal_name());
   data.is_managed = request.is_managed();
   accounts_.push_back(data);
-  PostResponse(std::move(callback), kerberos::ERROR_NONE, mTaskDelay);
+  PostResponse(std::move(callback), kerberos::ERROR_NONE, task_delay_);
 }
 
 void FakeKerberosClient::RemoveAccount(
@@ -146,7 +146,7 @@
   }
 
   MapAccountData(response.mutable_accounts());
-  PostProtoResponse(std::move(callback), response, mTaskDelay);
+  PostProtoResponse(std::move(callback), response, task_delay_);
 }
 
 void FakeKerberosClient::ClearAccounts(
@@ -183,7 +183,7 @@
   kerberos::ClearAccountsResponse response;
   MapAccountData(response.mutable_accounts());
   response.set_error(kerberos::ERROR_NONE);
-  PostProtoResponse(std::move(callback), response, mTaskDelay);
+  PostProtoResponse(std::move(callback), response, task_delay_);
 }
 
 void FakeKerberosClient::ListAccounts(
@@ -193,7 +193,7 @@
   kerberos::ListAccountsResponse response;
   MapAccountData(response.mutable_accounts());
   response.set_error(kerberos::ERROR_NONE);
-  PostProtoResponse(std::move(callback), response, mTaskDelay);
+  PostProtoResponse(std::move(callback), response, task_delay_);
 }
 
 void FakeKerberosClient::SetConfig(const kerberos::SetConfigRequest& request,
@@ -202,19 +202,19 @@
   AccountData* data = GetAccountData(request.principal_name());
   if (!data) {
     PostResponse(std::move(callback), kerberos::ERROR_UNKNOWN_PRINCIPAL_NAME,
-                 mTaskDelay);
+                 task_delay_);
     return;
   }
 
   kerberos::ConfigErrorInfo error_info =
       ValidateConfigLines(request.krb5conf());
   if (error_info.code() != kerberos::CONFIG_ERROR_NONE) {
-    PostResponse(std::move(callback), kerberos::ERROR_BAD_CONFIG, mTaskDelay);
+    PostResponse(std::move(callback), kerberos::ERROR_BAD_CONFIG, task_delay_);
     return;
   }
 
   data->krb5conf = request.krb5conf();
-  PostResponse(std::move(callback), kerberos::ERROR_NONE, mTaskDelay);
+  PostResponse(std::move(callback), kerberos::ERROR_NONE, task_delay_);
 }
 
 void FakeKerberosClient::ValidateConfig(
@@ -229,7 +229,7 @@
                          ? kerberos::ERROR_BAD_CONFIG
                          : kerberos::ERROR_NONE);
   *response.mutable_error_info() = std::move(error_info);
-  PostProtoResponse(std::move(callback), response, mTaskDelay);
+  PostProtoResponse(std::move(callback), response, task_delay_);
 }
 
 void FakeKerberosClient::AcquireKerberosTgt(
@@ -240,7 +240,7 @@
   AccountData* data = GetAccountData(request.principal_name());
   if (!data) {
     PostResponse(std::move(callback), kerberos::ERROR_UNKNOWN_PRINCIPAL_NAME,
-                 mTaskDelay);
+                 task_delay_);
     return;
   }
 
@@ -271,13 +271,21 @@
 
   // Reject empty passwords.
   if (password.empty()) {
-    PostResponse(std::move(callback), kerberos::ERROR_BAD_PASSWORD, mTaskDelay);
+    PostResponse(std::move(callback), kerberos::ERROR_BAD_PASSWORD,
+                 task_delay_);
+    return;
+  }
+
+  if (simulated_number_of_network_failures_ > 0) {
+    simulated_number_of_network_failures_--;
+    PostResponse(std::move(callback), kerberos::ERROR_NETWORK_PROBLEM,
+                 task_delay_);
     return;
   }
 
   // It worked! Magic!
   data->has_tgt = true;
-  PostResponse(std::move(callback), kerberos::ERROR_NONE, mTaskDelay);
+  PostResponse(std::move(callback), kerberos::ERROR_NONE, task_delay_);
 }
 
 void FakeKerberosClient::GetKerberosFiles(
@@ -287,7 +295,7 @@
   AccountData* data = GetAccountData(request.principal_name());
   if (!data) {
     PostResponse(std::move(callback), kerberos::ERROR_UNKNOWN_PRINCIPAL_NAME,
-                 mTaskDelay);
+                 task_delay_);
     return;
   }
 
@@ -297,7 +305,7 @@
     response.mutable_files()->set_krb5conf("Fake Kerberos configuration");
   }
   response.set_error(kerberos::ERROR_NONE);
-  PostProtoResponse(std::move(callback), response, mTaskDelay);
+  PostProtoResponse(std::move(callback), response, task_delay_);
 }
 
 void FakeKerberosClient::ConnectToKerberosFileChangedSignal(
@@ -315,7 +323,7 @@
 }
 
 void FakeKerberosClient::SetTaskDelay(base::TimeDelta delay) {
-  mTaskDelay = delay;
+  task_delay_ = delay;
 }
 
 void FakeKerberosClient::StartRecordingFunctionCalls() {
@@ -335,6 +343,11 @@
   return accounts_.size();
 }
 
+void FakeKerberosClient::SetSimulatedNumberOfNetworkFailures(
+    int number_of_failures) {
+  simulated_number_of_network_failures_ = number_of_failures;
+}
+
 void FakeKerberosClient::MaybeRecordFunctionCallForTesting(
     const char* function_name) {
   if (!recorded_function_calls_)
diff --git a/chromeos/dbus/kerberos/fake_kerberos_client.h b/chromeos/dbus/kerberos/fake_kerberos_client.h
index 0fd8a512..860e8f7 100644
--- a/chromeos/dbus/kerberos/fake_kerberos_client.h
+++ b/chromeos/dbus/kerberos/fake_kerberos_client.h
@@ -53,6 +53,7 @@
   void StartRecordingFunctionCalls() override;
   std::string StopRecordingAndGetRecordedFunctionCalls() override;
   std::size_t GetNumberOfAccounts() const override;
+  void SetSimulatedNumberOfNetworkFailures(int number_of_failures) override;
 
  private:
   using RepeatedAccountField =
@@ -108,7 +109,11 @@
   base::Optional<std::string> recorded_function_calls_;
 
   // Fake delay for any asynchronous operation.
-  base::TimeDelta mTaskDelay = base::TimeDelta::FromMilliseconds(100);
+  base::TimeDelta task_delay_ = base::TimeDelta::FromMilliseconds(100);
+
+  // The simulated number of network failures on |AcquireKerberosTgt()| (for
+  // testing).
+  int simulated_number_of_network_failures_ = 0;
 
   KerberosFilesChangedCallback kerberos_files_changed_callback_;
   KerberosTicketExpiringCallback kerberos_ticket_expiring_callback_;
diff --git a/chromeos/dbus/kerberos/kerberos_client.h b/chromeos/dbus/kerberos/kerberos_client.h
index 54b7523..bccf68d 100644
--- a/chromeos/dbus/kerberos/kerberos_client.h
+++ b/chromeos/dbus/kerberos/kerberos_client.h
@@ -61,6 +61,12 @@
     // Returns the number of accounts currently saved.
     virtual std::size_t GetNumberOfAccounts() const = 0;
 
+    // Sets the simulated number of network failures for |AcquireKerberosTgt()|.
+    // The default value is zero. This value should be set when testing the
+    // exponential backoff retry for adding managed accounts.
+    virtual void SetSimulatedNumberOfNetworkFailures(
+        int number_of_failures) = 0;
+
    protected:
     virtual ~TestInterface() {}
   };
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
index d3c17e23..6b96be9 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
@@ -259,12 +259,18 @@
     return;
   }
 
-  if (features::ShouldUseV2DeviceSync()) {
+  if (features::ShouldUseV1DeviceSync()) {
+    local_instance_id_ = local_device_metadata->instance_id.empty()
+                             ? base::nullopt
+                             : base::make_optional<std::string>(
+                                   local_device_metadata->instance_id);
+    local_legacy_device_id_ = local_device_metadata->GetDeviceId().empty()
+                                  ? base::nullopt
+                                  : base::make_optional<std::string>(
+                                        local_device_metadata->GetDeviceId());
+  } else {
     DCHECK(!local_device_metadata->instance_id.empty());
     local_instance_id_ = local_device_metadata->instance_id;
-  } else {
-    DCHECK(!local_device_metadata->GetDeviceId().empty());
-    local_legacy_device_id_ = local_device_metadata->GetDeviceId();
   }
 
   expiring_device_cache_->UpdateRemoteDevice(*local_device_metadata);
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
index 281fb4a..239a831 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
@@ -125,9 +125,13 @@
 
   base::Optional<std::string> local_instance_id_;
 
-  // TODO(https://crbug.com/951969): Only track local Instance ID after v2
-  // DeviceSync is launched, when the local device is guaranteed to have an
-  // Instance ID.
+  // TODO(https://crbug.com/1019206): Track only the local Instance ID after v1
+  // DeviceSync is disabled, when the local device is guaranteed to have an
+  // Instance ID. Note: When v1 and v2 DeviceSync are running in parallel, if we
+  // are still waiting for the first v2 DeviceSync to successfully complete, it
+  // is possible that only v1 device data--which does not contain Instance
+  // IDs--is loaded by the RemoteDeviceProvider. In that case, the local device
+  // will not have an Instance ID until the very first v2 DeviceSync succeeds.
   base::Optional<std::string> local_legacy_device_id_;
 
   base::WeakPtrFactory<DeviceSyncClientImpl> weak_ptr_factory_{this};
diff --git a/components/bookmarks/browser/bookmark_codec.cc b/components/bookmarks/browser/bookmark_codec.cc
index bbf771a..6d6aa53 100644
--- a/components/bookmarks/browser/bookmark_codec.cc
+++ b/components/bookmarks/browser/bookmark_codec.cc
@@ -42,8 +42,6 @@
 const char BookmarkCodec::kDateModifiedKey[] = "date_modified";
 const char BookmarkCodec::kChildrenKey[] = "children";
 const char BookmarkCodec::kMetaInfo[] = "meta_info";
-const char BookmarkCodec::kSyncTransactionVersion[] =
-    "sync_transaction_version";
 const char BookmarkCodec::kTypeURL[] = "url";
 const char BookmarkCodec::kTypeFolder[] = "folder";
 const char BookmarkCodec::kSyncMetadata[] = "sync_metadata";
@@ -55,9 +53,7 @@
     : ids_reassigned_(false),
       guids_reassigned_(false),
       ids_valid_(true),
-      maximum_id_(0),
-      model_sync_transaction_version_(
-          BookmarkNode::kInvalidSyncTransactionVersion) {}
+      maximum_id_(0) {}
 
 BookmarkCodec::~BookmarkCodec() = default;
 
@@ -66,7 +62,6 @@
     const std::string& sync_metadata_str) {
   return Encode(model->bookmark_bar_node(), model->other_node(),
                 model->mobile_node(), model->root_node()->GetMetaInfoMap(),
-                model->root_node()->sync_transaction_version(),
                 sync_metadata_str);
 }
 
@@ -75,7 +70,6 @@
     const BookmarkNode* other_folder_node,
     const BookmarkNode* mobile_folder_node,
     const BookmarkNode::MetaInfoMap* model_meta_info_map,
-    int64_t sync_transaction_version,
     const std::string& sync_metadata_str) {
   ids_reassigned_ = false;
   guids_reassigned_ = false;
@@ -86,11 +80,6 @@
   roots->Set(kMobileBookmarkFolderNameKey, EncodeNode(mobile_folder_node));
   if (model_meta_info_map)
     roots->Set(kMetaInfo, EncodeMetaInfo(*model_meta_info_map));
-  if (sync_transaction_version !=
-      BookmarkNode::kInvalidSyncTransactionVersion) {
-    roots->SetString(kSyncTransactionVersion,
-                     base::NumberToString(sync_transaction_version));
-  }
   auto main = std::make_unique<base::DictionaryValue>();
   main->SetInteger(kVersionKey, kCurrentVersion);
   FinalizeChecksum();
@@ -167,11 +156,6 @@
   const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap();
   if (meta_info_map)
     value->Set(kMetaInfo, EncodeMetaInfo(*meta_info_map));
-  if (node->sync_transaction_version() !=
-      BookmarkNode::kInvalidSyncTransactionVersion) {
-    value->SetString(kSyncTransactionVersion,
-                     base::NumberToString(node->sync_transaction_version()));
-  }
   return std::move(value);
 }
 
@@ -244,15 +228,7 @@
       ReassignIDsHelper(mobile_folder_node);
   }
 
-  if (!DecodeMetaInfo(*roots_d_value, &model_meta_info_map_,
-                      &model_sync_transaction_version_))
-    return false;
-
-  std::string sync_transaction_version_str;
-  if (roots_d_value->GetString(kSyncTransactionVersion,
-                               &sync_transaction_version_str) &&
-      !base::StringToInt64(sync_transaction_version_str,
-                           &model_sync_transaction_version_))
+  if (!DecodeMetaInfo(*roots_d_value, &model_meta_info_map_))
     return false;
 
   std::string sync_metadata_str_base64;
@@ -415,28 +391,17 @@
   node->SetTitle(title);
   node->set_date_added(Time::FromInternalValue(internal_time));
 
-  int64_t sync_transaction_version = node->sync_transaction_version();
   BookmarkNode::MetaInfoMap meta_info_map;
-  if (!DecodeMetaInfo(value, &meta_info_map, &sync_transaction_version))
+  if (!DecodeMetaInfo(value, &meta_info_map))
     return false;
   node->SetMetaInfoMap(meta_info_map);
 
-  std::string sync_transaction_version_str;
-  if (value.GetString(kSyncTransactionVersion, &sync_transaction_version_str) &&
-      !base::StringToInt64(sync_transaction_version_str,
-                           &sync_transaction_version))
-    return false;
-
-  node->set_sync_transaction_version(sync_transaction_version);
-
   return true;
 }
 
 bool BookmarkCodec::DecodeMetaInfo(const base::DictionaryValue& value,
-                                   BookmarkNode::MetaInfoMap* meta_info_map,
-                                   int64_t* sync_transaction_version) {
+                                   BookmarkNode::MetaInfoMap* meta_info_map) {
   DCHECK(meta_info_map);
-  DCHECK(sync_transaction_version);
   meta_info_map->clear();
 
   const base::Value* meta_info;
@@ -464,18 +429,6 @@
     return false;
   DecodeMetaInfoHelper(*meta_info_dict, std::string(), meta_info_map);
 
-  // Previously sync transaction version was stored in the meta info field
-  // using this key. If the key is present when decoding, set the sync
-  // transaction version to its value, then delete the field.
-  if (deserialized_holder) {
-    const char kBookmarkTransactionVersionKey[] = "sync.transaction_version";
-    auto it = meta_info_map->find(kBookmarkTransactionVersionKey);
-    if (it != meta_info_map->end()) {
-      base::StringToInt64(it->second, sync_transaction_version);
-      meta_info_map->erase(it);
-    }
-  }
-
   return true;
 }
 
diff --git a/components/bookmarks/browser/bookmark_codec.h b/components/bookmarks/browser/bookmark_codec.h
index 279528d..017ca98 100644
--- a/components/bookmarks/browser/bookmark_codec.h
+++ b/components/bookmarks/browser/bookmark_codec.h
@@ -51,7 +51,6 @@
       const BookmarkNode* other_folder_node,
       const BookmarkNode* mobile_folder_node,
       const BookmarkNode::MetaInfoMap* model_meta_info_map,
-      int64_t sync_transaction_version,
       const std::string& sync_metadata_str);
 
   // Decodes the previously encoded value to the specified nodes as well as
@@ -81,11 +80,6 @@
     return model_meta_info_map_;
   }
 
-  // Return the sync transaction version of the bookmark model root.
-  int64_t model_sync_transaction_version() const {
-    return model_sync_transaction_version_;
-  }
-
   // Returns whether the IDs were reassigned during decoding. Always returns
   // false after encoding.
   bool ids_reassigned() const { return ids_reassigned_; }
@@ -110,7 +104,6 @@
   static const char kDateModifiedKey[];
   static const char kChildrenKey[];
   static const char kMetaInfo[];
-  static const char kSyncTransactionVersion[];
   // Allows the BookmarkClient to read and a write a string blob from the JSON
   // file. That string captures the bookmarks sync metadata.
   static const char kSyncMetadata[];
@@ -154,14 +147,10 @@
                   BookmarkNode* parent,
                   BookmarkNode* node);
 
-  // Decodes the meta info from the supplied value. If the meta info contains
-  // a "sync.transaction_version" key, the value of that field will be stored
-  // in the sync_transaction_version variable, then deleted. This is for
-  // backward-compatibility reasons.
-  // meta_info_map and sync_transaction_version must not be NULL.
+  // Decodes the meta info from the supplied value. meta_info_map must not be
+  // nullptr.
   bool DecodeMetaInfo(const base::DictionaryValue& value,
-                      BookmarkNode::MetaInfoMap* meta_info_map,
-                      int64_t* sync_transaction_version);
+                      BookmarkNode::MetaInfoMap* meta_info_map);
 
   // Decodes the meta info from the supplied sub-node dictionary. The values
   // found will be inserted in meta_info_map with the given prefix added to the
@@ -221,9 +210,6 @@
   // Meta info set on bookmark model root.
   BookmarkNode::MetaInfoMap model_meta_info_map_;
 
-  // Sync transaction version set on bookmark model root.
-  int64_t model_sync_transaction_version_;
-
   DISALLOW_COPY_AND_ASSIGN(BookmarkCodec);
 };
 
diff --git a/components/bookmarks/browser/bookmark_codec_unittest.cc b/components/bookmarks/browser/bookmark_codec_unittest.cc
index 3b3f298..9e2fe1fe 100644
--- a/components/bookmarks/browser/bookmark_codec_unittest.cc
+++ b/components/bookmarks/browser/bookmark_codec_unittest.cc
@@ -184,8 +184,6 @@
                                 sync_metadata_str);
     model->set_next_node_id(max_id);
     AsMutable(model->root_node())->SetMetaInfoMap(codec->model_meta_info_map());
-    AsMutable(model->root_node())
-        ->set_sync_transaction_version(codec->model_sync_transaction_version());
 
     return result;
   }
@@ -452,28 +450,6 @@
   EXPECT_FALSE(child->GetMetaInfo("other_key", &meta_value));
 }
 
-TEST_F(BookmarkCodecTest, EncodeAndDecodeSyncTransactionVersion) {
-  // Add sync transaction version and encode.
-  std::unique_ptr<BookmarkModel> model(CreateTestModel2());
-  model->SetNodeSyncTransactionVersion(model->root_node(), 1);
-  const BookmarkNode* bbn = model->bookmark_bar_node();
-  model->SetNodeSyncTransactionVersion(bbn->children()[1].get(), 42);
-
-  std::string checksum;
-  std::unique_ptr<base::Value> value =
-      EncodeHelper(model.get(), /*sync_metadata_str=*/std::string(), &checksum);
-  ASSERT_TRUE(value.get() != nullptr);
-
-  // Decode and verify.
-  model = DecodeHelper(*value, checksum, &checksum, false,
-                       /*sync_metadata_str=*/nullptr);
-  EXPECT_EQ(1, model->root_node()->sync_transaction_version());
-  bbn = model->bookmark_bar_node();
-  EXPECT_EQ(42, bbn->children()[1]->sync_transaction_version());
-  EXPECT_EQ(BookmarkNode::kInvalidSyncTransactionVersion,
-            bbn->children()[0]->sync_transaction_version());
-}
-
 // Verifies that we can still decode the old codec format after changing the
 // way meta info is stored.
 TEST_F(BookmarkCodecTest, CanDecodeMetaInfoAsString) {
@@ -490,20 +466,11 @@
   ASSERT_TRUE(Decode(&decoder, *root.get(), model.get(),
                      /*sync_metadata_str=*/nullptr));
 
-  EXPECT_EQ(1, model->root_node()->sync_transaction_version());
   const BookmarkNode* bbn = model->bookmark_bar_node();
-  EXPECT_EQ(BookmarkNode::kInvalidSyncTransactionVersion,
-            bbn->children()[0]->sync_transaction_version());
-  EXPECT_EQ(42, bbn->children()[1]->sync_transaction_version());
 
-  const char kSyncTransactionVersionKey[] = "sync.transaction_version";
   const char kNormalKey[] = "key";
   const char kNestedKey[] = "nested.key";
   std::string meta_value;
-  EXPECT_FALSE(
-      model->root_node()->GetMetaInfo(kSyncTransactionVersionKey, &meta_value));
-  EXPECT_FALSE(
-      bbn->children()[1]->GetMetaInfo(kSyncTransactionVersionKey, &meta_value));
   EXPECT_TRUE(bbn->children()[0]->GetMetaInfo(kNormalKey, &meta_value));
   EXPECT_EQ("value", meta_value);
   EXPECT_TRUE(bbn->children()[1]->GetMetaInfo(kNormalKey, &meta_value));
diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc
index ffbcd66..b1825f4c 100644
--- a/components/bookmarks/browser/bookmark_model.cc
+++ b/components/bookmarks/browser/bookmark_model.cc
@@ -502,20 +502,6 @@
   non_cloned_keys_.insert(key);
 }
 
-void BookmarkModel::SetNodeSyncTransactionVersion(
-    const BookmarkNode* node,
-    int64_t sync_transaction_version) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(client_->CanSyncNode(node));
-
-  if (sync_transaction_version == node->sync_transaction_version())
-    return;
-
-  AsMutable(node)->set_sync_transaction_version(sync_transaction_version);
-  if (store_)
-    store_->ScheduleSave();
-}
-
 void BookmarkModel::OnFaviconsChanged(const std::set<GURL>& page_urls,
                                       const GURL& icon_url) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -895,8 +881,6 @@
                    VisibilityComparator(client_.get()));
 
   root_->SetMetaInfoMap(details->model_meta_info_map());
-  root_->set_sync_transaction_version(
-      details->model_sync_transaction_version());
 
   loaded_ = true;
   client_->DecodeBookmarkSyncMetadata(
diff --git a/components/bookmarks/browser/bookmark_model.h b/components/bookmarks/browser/bookmark_model.h
index 60845af..672e75e 100644
--- a/components/bookmarks/browser/bookmark_model.h
+++ b/components/bookmarks/browser/bookmark_model.h
@@ -304,10 +304,6 @@
     return non_cloned_keys_;
   }
 
-  // Sets the sync transaction version of |node|.
-  void SetNodeSyncTransactionVersion(const BookmarkNode* node,
-                                     int64_t sync_transaction_version);
-
   // Notify BookmarkModel that the favicons for the given page URLs (e.g.
   // http://www.google.com) and the given icon URL (e.g.
   // http://www.google.com/favicon.ico) have changed. It is valid to call
diff --git a/components/bookmarks/browser/bookmark_node.cc b/components/bookmarks/browser/bookmark_node.cc
index f7928c82..c2c1dc68 100644
--- a/components/bookmarks/browser/bookmark_node.cc
+++ b/components/bookmarks/browser/bookmark_node.cc
@@ -46,7 +46,6 @@
 // BookmarkNode ---------------------------------------------------------------
 
 // static
-const int64_t BookmarkNode::kInvalidSyncTransactionVersion = -1;
 const char BookmarkNode::kRootNodeGuid[] =
     "00000000-0000-4000-a000-000000000001";
 const char BookmarkNode::kBookmarkBarNodeGuid[] =
diff --git a/components/bookmarks/browser/bookmark_node.h b/components/bookmarks/browser/bookmark_node.h
index 0f2a600..6366123 100644
--- a/components/bookmarks/browser/bookmark_node.h
+++ b/components/bookmarks/browser/bookmark_node.h
@@ -44,7 +44,6 @@
 
   typedef std::map<std::string, std::string> MetaInfoMap;
 
-  static const int64_t kInvalidSyncTransactionVersion;
   static const char kRootNodeGuid[];
   static const char kBookmarkBarNodeGuid[];
   static const char kOtherBookmarksNodeGuid[];
@@ -125,11 +124,6 @@
   // Returns NULL if there are no values in the map.
   const MetaInfoMap* GetMetaInfoMap() const;
 
-  void set_sync_transaction_version(int64_t sync_transaction_version) {
-    sync_transaction_version_ = sync_transaction_version;
-  }
-  int64_t sync_transaction_version() const { return sync_transaction_version_; }
-
   // TitledUrlNode interface methods.
   const base::string16& GetTitledUrlNodeTitle() const override;
   const GURL& GetTitledUrlNodeUrl() const override;
@@ -218,9 +212,6 @@
   // A map that stores arbitrary meta information about the node.
   std::unique_ptr<MetaInfoMap> meta_info_map_;
 
-  // The sync transaction version.
-  int64_t sync_transaction_version_ = kInvalidSyncTransactionVersion;
-
   const bool is_permanent_node_;
 
   DISALLOW_COPY_AND_ASSIGN(BookmarkNode);
diff --git a/components/bookmarks/browser/bookmark_storage.cc b/components/bookmarks/browser/bookmark_storage.cc
index 5611c16a..9b8ba38 100644
--- a/components/bookmarks/browser/bookmark_storage.cc
+++ b/components/bookmarks/browser/bookmark_storage.cc
@@ -141,8 +141,6 @@
       details->set_ids_reassigned(codec.ids_reassigned());
       details->set_guids_reassigned(codec.guids_reassigned());
       details->set_model_meta_info_map(codec.model_meta_info_map());
-      details->set_model_sync_transaction_version(
-          codec.model_sync_transaction_version());
       UMA_HISTOGRAM_TIMES("Bookmarks.DecodeTime",
                           TimeTicks::Now() - start_time);
       int64_t size = 0;
@@ -202,9 +200,7 @@
 
 BookmarkLoadDetails::BookmarkLoadDetails(BookmarkClient* client)
     : load_managed_node_callback_(client->GetLoadManagedNodeCallback()),
-      index_(std::make_unique<TitledUrlIndex>()),
-      model_sync_transaction_version_(
-          BookmarkNode::kInvalidSyncTransactionVersion) {
+      index_(std::make_unique<TitledUrlIndex>()) {
   // WARNING: do NOT add |client| as a member. Much of this code runs on another
   // thread, and |client_| is not thread safe, and/or may be destroyed before
   // this.
diff --git a/components/bookmarks/browser/bookmark_storage.h b/components/bookmarks/browser/bookmark_storage.h
index 3f18b59d..41b4d2ec 100644
--- a/components/bookmarks/browser/bookmark_storage.h
+++ b/components/bookmarks/browser/bookmark_storage.h
@@ -70,13 +70,6 @@
     model_meta_info_map_ = meta_info_map;
   }
 
-  int64_t model_sync_transaction_version() const {
-    return model_sync_transaction_version_;
-  }
-  void set_model_sync_transaction_version(int64_t sync_transaction_version) {
-    model_sync_transaction_version_ = sync_transaction_version;
-  }
-
   // Max id of the nodes.
   void set_max_id(int64_t max_id) { max_id_ = max_id; }
   int64_t max_id() const { return max_id_; }
@@ -128,7 +121,6 @@
   LoadManagedNodeCallback load_managed_node_callback_;
   std::unique_ptr<TitledUrlIndex> index_;
   BookmarkNode::MetaInfoMap model_meta_info_map_;
-  int64_t model_sync_transaction_version_;
   int64_t max_id_ = 1;
   std::string computed_checksum_;
   std::string stored_checksum_;
diff --git a/components/invalidation/impl/BUILD.gn b/components/invalidation/impl/BUILD.gn
index 81090093..a9d5aae 100644
--- a/components/invalidation/impl/BUILD.gn
+++ b/components/invalidation/impl/BUILD.gn
@@ -115,8 +115,8 @@
   if (!is_android) {
     # Non-Android tests.
     sources += [
-      "object_id_invalidation_map_unittest.cc",
       "single_object_invalidation_set_unittest.cc",
+      "topic_invalidation_map_unittest.cc",
       "unacked_invalidation_set_unittest.cc",
     ]
   }
diff --git a/components/invalidation/impl/object_id_invalidation_map_unittest.cc b/components/invalidation/impl/object_id_invalidation_map_unittest.cc
deleted file mode 100644
index 0021d2c8..0000000
--- a/components/invalidation/impl/object_id_invalidation_map_unittest.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/invalidation/public/object_id_invalidation_map.h"
-
-#include <memory>
-
-#include "google/cacheinvalidation/types.pb.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace syncer {
-
-namespace {
-
-class ObjectIdInvalidationMapTest : public testing::Test {
- public:
-  ObjectIdInvalidationMapTest()
-      : kIdOne(ipc::invalidation::ObjectSource::TEST, "one"),
-        kIdTwo(ipc::invalidation::ObjectSource::TEST, "two"),
-        kInv1(Invalidation::Init(kIdOne, 10, "ten")) {
-    set1.insert(kIdOne);
-    set2.insert(kIdTwo);
-    all_set.insert(kIdOne);
-    all_set.insert(kIdTwo);
-
-    one_invalidation.Insert(kInv1);
-    invalidate_all = ObjectIdInvalidationMap::InvalidateAll(all_set);
-  }
-
- protected:
-  const invalidation::ObjectId kIdOne;
-  const invalidation::ObjectId kIdTwo;
-  const Invalidation kInv1;
-
-  ObjectIdSet set1;
-  ObjectIdSet set2;
-  ObjectIdSet all_set;
-  ObjectIdInvalidationMap empty;
-  ObjectIdInvalidationMap one_invalidation;
-  ObjectIdInvalidationMap invalidate_all;
-};
-
-TEST_F(ObjectIdInvalidationMapTest, Empty) {
-  EXPECT_TRUE(empty.Empty());
-  EXPECT_FALSE(one_invalidation.Empty());
-  EXPECT_FALSE(invalidate_all.Empty());
-}
-
-TEST_F(ObjectIdInvalidationMapTest, Equality) {
-  ObjectIdInvalidationMap empty2;
-  EXPECT_TRUE(empty == empty2);
-
-  ObjectIdInvalidationMap one_invalidation2;
-  one_invalidation2.Insert(kInv1);
-  EXPECT_TRUE(one_invalidation == one_invalidation2);
-
-  EXPECT_FALSE(empty == invalidate_all);
-}
-
-TEST_F(ObjectIdInvalidationMapTest, GetObjectIds) {
-  EXPECT_EQ(ObjectIdSet(), empty.GetObjectIds());
-  EXPECT_EQ(set1, one_invalidation.GetObjectIds());
-  EXPECT_EQ(all_set, invalidate_all.GetObjectIds());
-}
-
-TEST_F(ObjectIdInvalidationMapTest, GetSubsetWithObjectIds) {
-  EXPECT_TRUE(empty.GetSubsetWithObjectIds(set1).Empty());
-
-  EXPECT_TRUE(one_invalidation.GetSubsetWithObjectIds(set1) ==
-              one_invalidation);
-  EXPECT_TRUE(one_invalidation.GetSubsetWithObjectIds(all_set) ==
-              one_invalidation);
-  EXPECT_TRUE(one_invalidation.GetSubsetWithObjectIds(set2).Empty());
-
-  EXPECT_TRUE(invalidate_all.GetSubsetWithObjectIds(ObjectIdSet()).Empty());
-}
-
-TEST_F(ObjectIdInvalidationMapTest, SerializeEmpty) {
-  std::unique_ptr<base::ListValue> value = empty.ToValue();
-  ASSERT_TRUE(value.get());
-  ObjectIdInvalidationMap deserialized;
-  deserialized.ResetFromValue(*value);
-  EXPECT_TRUE(empty == deserialized);
-}
-
-TEST_F(ObjectIdInvalidationMapTest, SerializeOneInvalidation) {
-  std::unique_ptr<base::ListValue> value = one_invalidation.ToValue();
-  ASSERT_TRUE(value.get());
-  ObjectIdInvalidationMap deserialized;
-  deserialized.ResetFromValue(*value);
-  EXPECT_TRUE(one_invalidation == deserialized);
-}
-
-TEST_F(ObjectIdInvalidationMapTest, SerializeInvalidateAll) {
-  std::unique_ptr<base::ListValue> value = invalidate_all.ToValue();
-  ASSERT_TRUE(value.get());
-  ObjectIdInvalidationMap deserialized;
-  deserialized.ResetFromValue(*value);
-  EXPECT_TRUE(invalidate_all == deserialized);
-}
-
-}  // namespace
-
-}  // namespace syncer
diff --git a/components/invalidation/impl/topic_invalidation_map_unittest.cc b/components/invalidation/impl/topic_invalidation_map_unittest.cc
new file mode 100644
index 0000000..11173a4
--- /dev/null
+++ b/components/invalidation/impl/topic_invalidation_map_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/invalidation/public/topic_invalidation_map.h"
+
+#include <memory>
+
+#include "google/cacheinvalidation/types.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+
+namespace {
+
+// TODO(crbug.com/1056651): some methods aren't covered by tests, it's likely
+// worth adding them (especially for ToValue(), which is passed to js code).
+class TopicInvalidationMapTest : public testing::Test {
+ public:
+  TopicInvalidationMapTest()
+      : kTopicOne("one"),
+        kTopicTwo("two"),
+        kInv1(Invalidation::Init(kTopicOne, 10, "ten")) {
+    set1_.insert(kTopicOne);
+    set2_.insert(kTopicTwo);
+    all_set_.insert(kTopicOne);
+    all_set_.insert(kTopicTwo);
+
+    one_invalidation_.Insert(kInv1);
+    invalidate_all_.Insert(Invalidation::InitUnknownVersion(kTopicOne));
+    invalidate_all_.Insert(Invalidation::InitUnknownVersion(kTopicTwo));
+  }
+
+ protected:
+  const Topic kTopicOne;
+  const Topic kTopicTwo;
+  const Invalidation kInv1;
+
+  TopicSet set1_;
+  TopicSet set2_;
+  TopicSet all_set_;
+  TopicInvalidationMap empty_;
+  TopicInvalidationMap one_invalidation_;
+  TopicInvalidationMap invalidate_all_;
+};
+
+TEST_F(TopicInvalidationMapTest, Empty) {
+  EXPECT_TRUE(empty_.Empty());
+  EXPECT_FALSE(one_invalidation_.Empty());
+  EXPECT_FALSE(invalidate_all_.Empty());
+}
+
+TEST_F(TopicInvalidationMapTest, Equality) {
+  // TODO(crbug.com/1056651): equality operator is only used in tests, so maybe
+  // factor it away from the TopicInvalidationMap.
+  TopicInvalidationMap empty2;
+  EXPECT_EQ(empty_, empty2);
+
+  TopicInvalidationMap one_invalidation_2;
+  one_invalidation_2.Insert(kInv1);
+  EXPECT_EQ(one_invalidation_, one_invalidation_2);
+
+  EXPECT_FALSE(empty_ == invalidate_all_);
+}
+
+TEST_F(TopicInvalidationMapTest, GetTopics) {
+  EXPECT_EQ(TopicSet(), empty_.GetTopics());
+  EXPECT_EQ(set1_, one_invalidation_.GetTopics());
+  EXPECT_EQ(all_set_, invalidate_all_.GetTopics());
+}
+
+TEST_F(TopicInvalidationMapTest, GetSubsetWithTopics) {
+  EXPECT_TRUE(empty_.GetSubsetWithTopics(set1_).Empty());
+
+  EXPECT_EQ(one_invalidation_.GetSubsetWithTopics(set1_), one_invalidation_);
+  EXPECT_EQ(one_invalidation_.GetSubsetWithTopics(all_set_), one_invalidation_);
+  EXPECT_TRUE(one_invalidation_.GetSubsetWithTopics(set2_).Empty());
+
+  EXPECT_TRUE(invalidate_all_.GetSubsetWithTopics(TopicSet()).Empty());
+}
+
+}  // namespace
+
+}  // namespace syncer
diff --git a/components/invalidation/public/BUILD.gn b/components/invalidation/public/BUILD.gn
index fafda47..0089d81 100644
--- a/components/invalidation/public/BUILD.gn
+++ b/components/invalidation/public/BUILD.gn
@@ -20,8 +20,6 @@
     "invalidation_util.h",
     "invalidator_state.cc",
     "invalidator_state.h",
-    "object_id_invalidation_map.cc",
-    "object_id_invalidation_map.h",
     "single_object_invalidation_set.cc",
     "single_object_invalidation_set.h",
     "topic_invalidation_map.cc",
diff --git a/components/invalidation/public/object_id_invalidation_map.cc b/components/invalidation/public/object_id_invalidation_map.cc
deleted file mode 100644
index 253c8c4..0000000
--- a/components/invalidation/public/object_id_invalidation_map.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/invalidation/public/object_id_invalidation_map.h"
-
-#include <stddef.h>
-
-#include "base/json/json_string_value_serializer.h"
-
-namespace syncer {
-
-// static
-ObjectIdInvalidationMap ObjectIdInvalidationMap::InvalidateAll(
-    const ObjectIdSet& ids) {
-  ObjectIdInvalidationMap invalidate_all;
-  for (auto it = ids.begin(); it != ids.end(); ++it) {
-    invalidate_all.Insert(Invalidation::InitUnknownVersion(*it));
-  }
-  return invalidate_all;
-}
-
-ObjectIdInvalidationMap::ObjectIdInvalidationMap() {}
-
-ObjectIdInvalidationMap::ObjectIdInvalidationMap(
-    const ObjectIdInvalidationMap& other) = default;
-
-ObjectIdInvalidationMap::~ObjectIdInvalidationMap() {}
-
-ObjectIdSet ObjectIdInvalidationMap::GetObjectIds() const {
-  ObjectIdSet ret;
-  for (auto it = map_.begin(); it != map_.end(); ++it) {
-    ret.insert(it->first);
-  }
-  return ret;
-}
-
-bool ObjectIdInvalidationMap::Empty() const {
-  return map_.empty();
-}
-
-void ObjectIdInvalidationMap::Insert(const Invalidation& invalidation) {
-  map_[invalidation.object_id()].Insert(invalidation);
-}
-
-ObjectIdInvalidationMap ObjectIdInvalidationMap::GetSubsetWithObjectIds(
-    const ObjectIdSet& ids) const {
-  IdToListMap new_map;
-  for (auto it = ids.begin(); it != ids.end(); ++it) {
-    auto lookup = map_.find(*it);
-    if (lookup != map_.end()) {
-      new_map[*it] = lookup->second;
-    }
-  }
-  return ObjectIdInvalidationMap(new_map);
-}
-
-const SingleObjectInvalidationSet& ObjectIdInvalidationMap::ForObject(
-    invalidation::ObjectId id) const {
-  auto lookup = map_.find(id);
-  DCHECK(lookup != map_.end());
-  DCHECK(!lookup->second.IsEmpty());
-  return lookup->second;
-}
-
-void ObjectIdInvalidationMap::GetAllInvalidations(
-    std::vector<syncer::Invalidation>* out) const {
-  for (auto it = map_.begin(); it != map_.end(); ++it) {
-    out->insert(out->begin(), it->second.begin(), it->second.end());
-  }
-}
-void ObjectIdInvalidationMap::AcknowledgeAll() const {
-  for (auto it1 = map_.begin(); it1 != map_.end(); ++it1) {
-    for (auto it2 = it1->second.begin(); it2 != it1->second.end(); ++it2) {
-      it2->Acknowledge();
-    }
-  }
-}
-
-bool ObjectIdInvalidationMap::operator==(
-    const ObjectIdInvalidationMap& other) const {
-  return map_ == other.map_;
-}
-
-std::unique_ptr<base::ListValue> ObjectIdInvalidationMap::ToValue() const {
-  std::unique_ptr<base::ListValue> value(new base::ListValue());
-  for (auto it1 = map_.begin(); it1 != map_.end(); ++it1) {
-    for (auto it2 = it1->second.begin(); it2 != it1->second.end(); ++it2) {
-      value->Append(it2->ToValue());
-    }
-  }
-  return value;
-}
-
-bool ObjectIdInvalidationMap::ResetFromValue(const base::ListValue& value) {
-  map_.clear();
-  for (size_t i = 0; i < value.GetSize(); ++i) {
-    const base::DictionaryValue* dict;
-    if (!value.GetDictionary(i, &dict)) {
-      return false;
-    }
-    std::unique_ptr<Invalidation> invalidation =
-        Invalidation::InitFromValue(*dict);
-    if (!invalidation) {
-      return false;
-    }
-    Insert(*invalidation);
-  }
-  return true;
-}
-
-std::string ObjectIdInvalidationMap::ToString() const {
-  std::string output;
-  JSONStringValueSerializer serializer(&output);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(*ToValue());
-  return output;
-}
-
-ObjectIdInvalidationMap::ObjectIdInvalidationMap(const IdToListMap& map)
-  : map_(map) {}
-
-}  // namespace syncer
diff --git a/components/invalidation/public/object_id_invalidation_map.h b/components/invalidation/public/object_id_invalidation_map.h
deleted file mode 100644
index 140b330..0000000
--- a/components/invalidation/public/object_id_invalidation_map.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_INVALIDATION_PUBLIC_OBJECT_ID_INVALIDATION_MAP_H_
-#define COMPONENTS_INVALIDATION_PUBLIC_OBJECT_ID_INVALIDATION_MAP_H_
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "components/invalidation/public/invalidation.h"
-#include "components/invalidation/public/invalidation_export.h"
-#include "components/invalidation/public/invalidation_util.h"
-#include "components/invalidation/public/single_object_invalidation_set.h"
-
-namespace syncer {
-
-// A set of notifications with some helper methods to organize them by object ID
-// and version number.
-class INVALIDATION_EXPORT ObjectIdInvalidationMap {
-  public:
-   // Creates an invalidation map that includes an 'unknown version'
-   // invalidation for each specified ID in |ids|.
-   static ObjectIdInvalidationMap InvalidateAll(const ObjectIdSet& ids);
-
-   ObjectIdInvalidationMap();
-   ObjectIdInvalidationMap(const ObjectIdInvalidationMap& other);
-   ~ObjectIdInvalidationMap();
-
-   // Returns set of ObjectIds for which at least one invalidation is present.
-   ObjectIdSet GetObjectIds() const;
-
-   // Returns true if this map contains no invalidations.
-   bool Empty() const;
-
-   // Returns true if both maps contain the same set of invalidations.
-   bool operator==(const ObjectIdInvalidationMap& other) const;
-
-   // Inserts a new invalidation into this map.
-   void Insert(const Invalidation& invalidation);
-
-   // Returns a new map containing the subset of invaliations from this map
-   // whose IDs were in the specified |ids| set.
-   ObjectIdInvalidationMap GetSubsetWithObjectIds(const ObjectIdSet& ids) const;
-
-   // Returns the subset of invalidations with IDs matching |id|.
-   const SingleObjectInvalidationSet& ForObject(
-       invalidation::ObjectId id) const;
-
-   // Returns the contents of this map in a single vector.
-   void GetAllInvalidations(std::vector<syncer::Invalidation>* out) const;
-
-   // Call Acknowledge() on all contained Invalidations.
-   void AcknowledgeAll() const;
-
-   // Serialize this map to a value.
-   std::unique_ptr<base::ListValue> ToValue() const;
-
-   // Deserialize the value into a map and use it to re-initialize this object.
-   bool ResetFromValue(const base::ListValue& value);
-
-   // Prints the contentes of this map as a human-readable string.
-   std::string ToString() const;
-
-  private:
-   typedef std::map<invalidation::ObjectId,
-                    SingleObjectInvalidationSet,
-                    ObjectIdLessThan> IdToListMap;
-
-   ObjectIdInvalidationMap(const IdToListMap& map);
-
-   IdToListMap map_;
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_INVALIDATION_PUBLIC_OBJECT_ID_INVALIDATION_MAP_H_
diff --git a/components/invalidation/public/topic_invalidation_map.cc b/components/invalidation/public/topic_invalidation_map.cc
index 70c9c95f..05938d2 100644
--- a/components/invalidation/public/topic_invalidation_map.cc
+++ b/components/invalidation/public/topic_invalidation_map.cc
@@ -6,8 +6,7 @@
 
 #include <stddef.h>
 
-#include "base/json/json_string_value_serializer.h"
-#include "components/invalidation/public/object_id_invalidation_map.h"
+#include "base/values.h"
 
 namespace syncer {
 
@@ -35,7 +34,7 @@
 
 TopicInvalidationMap TopicInvalidationMap::GetSubsetWithTopics(
     const Topics& topics) const {
-  TopicToListMap new_map;
+  std::map<Topic, SingleObjectInvalidationSet> new_map;
   for (const auto& topic : topics) {
     auto lookup = map_.find(topic.first);
     if (lookup != map_.end()) {
@@ -45,6 +44,18 @@
   return TopicInvalidationMap(new_map);
 }
 
+TopicInvalidationMap TopicInvalidationMap::GetSubsetWithTopics(
+    const TopicSet& topics) const {
+  std::map<Topic, SingleObjectInvalidationSet> new_map;
+  for (const auto& topic : topics) {
+    auto lookup = map_.find(topic);
+    if (lookup != map_.end()) {
+      new_map[topic] = lookup->second;
+    }
+  }
+  return TopicInvalidationMap(new_map);
+}
+
 const SingleObjectInvalidationSet& TopicInvalidationMap::ForTopic(
     Topic topic) const {
   auto lookup = map_.find(topic);
@@ -82,54 +93,8 @@
   return value;
 }
 
-bool TopicInvalidationMap::ResetFromValue(const base::ListValue& value) {
-  map_.clear();
-  for (size_t i = 0; i < value.GetSize(); ++i) {
-    const base::DictionaryValue* dict;
-    if (!value.GetDictionary(i, &dict)) {
-      return false;
-    }
-    std::unique_ptr<Invalidation> invalidation =
-        Invalidation::InitFromValue(*dict);
-    if (!invalidation) {
-      return false;
-    }
-    Insert(*invalidation);
-  }
-  return true;
-}
-
-std::string TopicInvalidationMap::ToString() const {
-  std::string output;
-  JSONStringValueSerializer serializer(&output);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(*ToValue());
-  return output;
-}
-
-TopicInvalidationMap::TopicInvalidationMap(const TopicToListMap& map)
+TopicInvalidationMap::TopicInvalidationMap(
+    const std::map<Topic, SingleObjectInvalidationSet>& map)
     : map_(map) {}
 
-TopicInvalidationMap ConvertObjectIdInvalidationMapToTopicInvalidationMap(
-    ObjectIdInvalidationMap object_ids_map) {
-  TopicInvalidationMap topics_map;
-  std::vector<Invalidation> invalidations;
-  object_ids_map.GetAllInvalidations(&invalidations);
-  for (const auto& invalidation : invalidations) {
-    topics_map.Insert(invalidation);
-  }
-  return topics_map;
-}
-
-ObjectIdInvalidationMap ConvertTopicInvalidationMapToObjectIdInvalidationMap(
-    const TopicInvalidationMap& topics_map) {
-  ObjectIdInvalidationMap object_ids_map;
-  std::vector<Invalidation> invalidations;
-  topics_map.GetAllInvalidations(&invalidations);
-  for (const auto& invalidation : invalidations) {
-    object_ids_map.Insert(invalidation);
-  }
-  return object_ids_map;
-}
-
 }  // namespace syncer
diff --git a/components/invalidation/public/topic_invalidation_map.h b/components/invalidation/public/topic_invalidation_map.h
index e72ae32..3999311 100644
--- a/components/invalidation/public/topic_invalidation_map.h
+++ b/components/invalidation/public/topic_invalidation_map.h
@@ -14,8 +14,11 @@
 #include "components/invalidation/public/invalidation_util.h"
 #include "components/invalidation/public/single_object_invalidation_set.h"
 
+namespace base {
+class ListValue;
+}  // namespace base
+
 namespace syncer {
-class ObjectIdInvalidationMap;
 
 // A set of notifications with some helper methods to organize them by object ID
 // and version number.
@@ -38,11 +41,17 @@
   void Insert(const Invalidation& invalidation);
 
   // Returns a new map containing the subset of invaliations from this map
-  // whose IDs were in the specified |ids| set.
-  TopicInvalidationMap GetSubsetWithTopics(const Topics& ids) const;
+  // whose topic were in the specified |topics|.
+  // TODO(crbug.com/1029698): replace all usages with the version below and
+  // remove this method.
+  TopicInvalidationMap GetSubsetWithTopics(const Topics& topics) const;
 
-  // Returns the subset of invalidations with IDs matching |id|.
-  const SingleObjectInvalidationSet& ForTopic(Topic id) const;
+  // Returns a new map containing the subset of invaliations from this map
+  // whose topic were in the specified |topics|.
+  TopicInvalidationMap GetSubsetWithTopics(const TopicSet& topics) const;
+
+  // Returns the subset of invalidations with Topic matching |topic|.
+  const SingleObjectInvalidationSet& ForTopic(Topic topic) const;
 
   // Returns the contents of this map in a single vector.
   void GetAllInvalidations(std::vector<syncer::Invalidation>* out) const;
@@ -50,29 +59,16 @@
   // Call Acknowledge() on all contained Invalidations.
   void AcknowledgeAll() const;
 
-  // Serialize this map to a value.
+  // Serialize this map to a value. Used to expose value on
+  // chrome://invalidations page.
   std::unique_ptr<base::ListValue> ToValue() const;
 
-  // Deserialize the value into a map and use it to re-initialize this object.
-  bool ResetFromValue(const base::ListValue& value);
-
-  // Prints the contentes of this map as a human-readable string.
-  std::string ToString() const;
-
  private:
-  typedef std::map<Topic, SingleObjectInvalidationSet> TopicToListMap;
+  TopicInvalidationMap(const std::map<Topic, SingleObjectInvalidationSet>& map);
 
-  TopicInvalidationMap(const TopicToListMap& map);
-
-  TopicToListMap map_;
+  std::map<Topic, SingleObjectInvalidationSet> map_;
 };
 
-TopicInvalidationMap ConvertObjectIdInvalidationMapToTopicInvalidationMap(
-    ObjectIdInvalidationMap object_ids_map);
-
-ObjectIdInvalidationMap ConvertTopicInvalidationMapToObjectIdInvalidationMap(
-    const TopicInvalidationMap& topics_map);
-
 }  // namespace syncer
 
 #endif  // COMPONENTS_INVALIDATION_PUBLIC_TOPIC_INVALIDATION_MAP_H_
diff --git a/components/offline_pages/core/background/change_requests_state_task.h b/components/offline_pages/core/background/change_requests_state_task.h
index 5ce3c74..4251f6d 100644
--- a/components/offline_pages/core/background/change_requests_state_task.h
+++ b/components/offline_pages/core/background/change_requests_state_task.h
@@ -26,10 +26,9 @@
                           RequestQueueStore::UpdateCallback callback);
   ~ChangeRequestsStateTask() override;
 
+ private:
   // TaskQueue::Task implementation.
   void Run() override;
-
- private:
   // Step 1. Reading the requests.
   void ReadRequests();
   // Step 2. Updates available requests.
diff --git a/components/offline_pages/core/background/change_requests_state_task_unittest.cc b/components/offline_pages/core/background/change_requests_state_task_unittest.cc
index 85f4e96e..f5b977c2 100644
--- a/components/offline_pages/core/background/change_requests_state_task_unittest.cc
+++ b/components/offline_pages/core/background/change_requests_state_task_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
@@ -85,7 +86,7 @@
       &store_, request_ids, SavePageRequest::RequestState::PAUSED,
       base::BindOnce(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
@@ -104,7 +105,7 @@
       &store_, request_ids, SavePageRequest::RequestState::PAUSED,
       base::BindOnce(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
@@ -125,7 +126,7 @@
       &store_, request_ids, SavePageRequest::RequestState::PAUSED,
       base::BindOnce(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   ASSERT_EQ(2UL, last_result()->item_statuses.size());
@@ -163,7 +164,7 @@
       &store_, request_ids, SavePageRequest::RequestState::PAUSED,
       base::BindOnce(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(0UL, last_result()->item_statuses.size());
@@ -179,7 +180,7 @@
       &store_, request_ids, SavePageRequest::RequestState::PAUSED,
       base::BindOnce(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   ASSERT_EQ(2UL, last_result()->item_statuses.size());
diff --git a/components/offline_pages/core/background/cleanup_task.h b/components/offline_pages/core/background/cleanup_task.h
index 3a900112..9aa6891 100644
--- a/components/offline_pages/core/background/cleanup_task.h
+++ b/components/offline_pages/core/background/cleanup_task.h
@@ -30,10 +30,9 @@
               RequestCoordinatorEventLogger* logger);
   ~CleanupTask() override;
 
+ private:
   // TaskQueue::Task implementation, starts the async chain
   void Run() override;
-
- private:
   // Step 1. get results from the store
   void GetRequests();
 
diff --git a/components/offline_pages/core/background/cleanup_task_unittest.cc b/components/offline_pages/core/background/cleanup_task_unittest.cc
index 2a6260ed..5857a3b2 100644
--- a/components/offline_pages/core/background/cleanup_task_unittest.cc
+++ b/components/offline_pages/core/background/cleanup_task_unittest.cc
@@ -8,6 +8,7 @@
 #include <set>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/core/background/offliner_policy.h"
@@ -177,7 +178,7 @@
   QueueRequests(request1, request2);
 
   // Initiate cleanup.
-  task()->Run();
+  task()->Execute(base::DoNothing());
   PumpLoop();
 
   // See what is left in the queue, should be just the other request.
@@ -199,7 +200,7 @@
   QueueRequests(request1, request2);
 
   // Initiate cleanup.
-  task()->Run();
+  task()->Execute(base::DoNothing());
   PumpLoop();
 
   // See what is left in the queue, should be just the other request.
@@ -221,7 +222,7 @@
   QueueRequests(request1, request2);
 
   // Initiate cleanup.
-  task()->Run();
+  task()->Execute(base::DoNothing());
   PumpLoop();
 
   // See what is left in the queue, should be just the other request.
@@ -246,7 +247,7 @@
   QueueRequests(request1, request2);
 
   // Initiate cleanup.
-  task()->Run();
+  task()->Execute(base::DoNothing());
   PumpLoop();
 
   // See what is left in the queue, request1 should be left in the queue even
diff --git a/components/offline_pages/core/background/get_requests_task.h b/components/offline_pages/core/background/get_requests_task.h
index e5b079e..edf6800 100644
--- a/components/offline_pages/core/background/get_requests_task.h
+++ b/components/offline_pages/core/background/get_requests_task.h
@@ -20,10 +20,9 @@
                   RequestQueueStore::GetRequestsCallback callback);
   ~GetRequestsTask() override;
 
+ private:
   // Task implementation:
   void Run() override;
-
- private:
   // Step 1: Read the requests from he store.
   void ReadRequest();
   // Step 2: Calls the callback with result, completes the task.
diff --git a/components/offline_pages/core/background/get_requests_task_unittest.cc b/components/offline_pages/core/background/get_requests_task_unittest.cc
index 13b8e17d..6355f9307 100644
--- a/components/offline_pages/core/background/get_requests_task_unittest.cc
+++ b/components/offline_pages/core/background/get_requests_task_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
@@ -87,7 +88,7 @@
   GetRequestsTask task(&store_,
                        base::BindOnce(&GetRequestsTaskTest::GetRequestsCallback,
                                       base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   EXPECT_TRUE(callback_called());
   EXPECT_TRUE(last_call_successful());
@@ -101,7 +102,7 @@
   GetRequestsTask task(&store_,
                        base::BindOnce(&GetRequestsTaskTest::GetRequestsCallback,
                                       base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   EXPECT_TRUE(callback_called());
   EXPECT_TRUE(last_call_successful());
diff --git a/components/offline_pages/core/background/initialize_store_task.h b/components/offline_pages/core/background/initialize_store_task.h
index 772dc63..9b155a3 100644
--- a/components/offline_pages/core/background/initialize_store_task.h
+++ b/components/offline_pages/core/background/initialize_store_task.h
@@ -28,10 +28,9 @@
                       RequestQueueStore::InitializeCallback callback);
   ~InitializeStoreTask() override;
 
+ private:
   // TaskQueue::Task implementation.
   void Run() override;
-
- private:
   // Step 1. Initialize store.
   void InitializeStore();
   // Step 2a. Completes initialization if successful or tries to reset if there
diff --git a/components/offline_pages/core/background/initialize_store_task_unittest.cc b/components/offline_pages/core/background/initialize_store_task_unittest.cc
index 289637d7..a62a228 100644
--- a/components/offline_pages/core/background/initialize_store_task_unittest.cc
+++ b/components/offline_pages/core/background/initialize_store_task_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
@@ -41,7 +42,7 @@
   InitializeStoreTask task(
       &store_, base::BindOnce(&InitializeStoreTaskTest::InitializeCallback,
                               base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   EXPECT_TRUE(callback_called());
   EXPECT_TRUE(last_call_successful());
@@ -54,7 +55,7 @@
   InitializeStoreTask task(
       &store_, base::BindOnce(&InitializeStoreTaskTest::InitializeCallback,
                               base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
 
   PumpLoop();
 
@@ -71,7 +72,7 @@
   InitializeStoreTask task(
       &store_, base::BindOnce(&InitializeStoreTaskTest::InitializeCallback,
                               base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   EXPECT_TRUE(callback_called());
   EXPECT_FALSE(last_call_successful());
diff --git a/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc b/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc
index 63b23b4..b0a2e5c 100644
--- a/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc
+++ b/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/core/background/change_requests_state_task.h"
@@ -84,7 +85,7 @@
       &store_, kRequestId1,
       base::BindOnce(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
@@ -103,7 +104,7 @@
       &store_, kRequestId1,
       base::BindOnce(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  start_request_task.Run();
+  start_request_task.Execute(base::DoNothing());
   PumpLoop();
   ClearResults();
 
@@ -112,7 +113,7 @@
       base::BindOnce(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
 
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
@@ -132,7 +133,7 @@
       &store_, kRequestId2,
       base::BindOnce(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
@@ -151,7 +152,7 @@
       &store_, kRequestId1,
       base::BindOnce(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  start_request_task.Run();
+  start_request_task.Execute(base::DoNothing());
   PumpLoop();
   ClearResults();
 
@@ -162,7 +163,7 @@
       &store_, requests, SavePageRequest::RequestState::PAUSED,
       base::BindOnce(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  pauseTask.Run();
+  pauseTask.Execute(base::DoNothing());
   PumpLoop();
 
   // Abort the task, the state should not change from PAUSED.
@@ -171,7 +172,7 @@
       base::BindOnce(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
 
-  abortTask.Run();
+  abortTask.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
diff --git a/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc b/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc
index 2bfe026..a66e68d 100644
--- a/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc
+++ b/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
 #include "components/offline_pages/core/background/request_queue_task_test_base.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
@@ -71,7 +72,7 @@
       base::BindOnce(&MarkAttemptCompletedTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
 
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
@@ -93,7 +94,7 @@
       &store_, kRequestId2, FailState::CANNOT_DOWNLOAD,
       base::BindOnce(&MarkAttemptCompletedTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
diff --git a/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc b/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc
index ee1c8d8..eac1ad1e 100644
--- a/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc
+++ b/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
@@ -68,7 +69,7 @@
       &store_, kRequestId1,
       base::BindOnce(&MarkAttemptStartedTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
@@ -89,7 +90,7 @@
 
   // Current time for verification.
   base::Time before_time = OfflineTimeNow();
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
@@ -114,7 +115,7 @@
       &store_, kRequestId2,
       base::BindOnce(&MarkAttemptStartedTaskTest::ChangeRequestsStateCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
diff --git a/components/offline_pages/core/background/pick_request_task.h b/components/offline_pages/core/background/pick_request_task.h
index 7332891..e44e205 100644
--- a/components/offline_pages/core/background/pick_request_task.h
+++ b/components/offline_pages/core/background/pick_request_task.h
@@ -57,10 +57,10 @@
 
   ~PickRequestTask() override;
 
+ private:
   // TaskQueue::Task implementation, starts the async chain
   void Run() override;
 
- private:
   // Step 1. get the requests
   void GetRequests();
 
diff --git a/components/offline_pages/core/background/pick_request_task_unittest.cc b/components/offline_pages/core/background/pick_request_task_unittest.cc
index d923e28..831f091 100644
--- a/components/offline_pages/core/background/pick_request_task_unittest.cc
+++ b/components/offline_pages/core/background/pick_request_task_unittest.cc
@@ -122,9 +122,14 @@
 
   PickRequestTask* task() { return task_.get(); }
 
-  void TaskCompletionCallback(Task* completed_task);
+  base::OnceClosure TaskCompletionCallback() {
+    return base::BindOnce(&PickRequestTaskTest::OnTaskComplete,
+                          base::Unretained(this));
+  }
 
  protected:
+  void OnTaskComplete() { task_complete_called_ = true; }
+
   std::unique_ptr<RequestNotifierStub> notifier_;
   std::unique_ptr<SavePageRequest> last_picked_;
   std::unique_ptr<OfflinerPolicy> policy_;
@@ -155,10 +160,6 @@
   PumpLoop();
 }
 
-void PickRequestTaskTest::TaskCompletionCallback(Task* completed_task) {
-  task_complete_called_ = true;
-}
-
 void PickRequestTaskTest::RequestPicked(
     const SavePageRequest& request,
     std::unique_ptr<std::vector<SavePageRequest>> available_requests,
@@ -204,13 +205,10 @@
       base::BindOnce(&PickRequestTaskTest::RequestCountCallback,
                      base::Unretained(this)),
       conditions, disabled_requests_, &prioritized_requests_));
-  task_->SetTaskCompletionCallbackForTesting(
-      base::BindRepeating(&PickRequestTaskTest::TaskCompletionCallback,
-                          base::Unretained(this)));
 }
 
 TEST_F(PickRequestTaskTest, PickFromEmptyQueue) {
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   // Pump the loop again to give the async queue the opportunity to return
@@ -240,7 +238,7 @@
 
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   EXPECT_EQ(kRequestId2, last_picked_->request_id());
@@ -261,7 +259,7 @@
 
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   EXPECT_EQ(kRequestId1, last_picked_->request_id());
@@ -287,7 +285,7 @@
 
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   EXPECT_EQ(kRequestId1, last_picked_->request_id());
@@ -311,7 +309,7 @@
 
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   EXPECT_EQ(kRequestId2, last_picked_->request_id());
@@ -335,7 +333,7 @@
 
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   EXPECT_EQ(kRequestId1, last_picked_->request_id());
@@ -360,7 +358,7 @@
 
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   EXPECT_EQ(kRequestId2, last_picked_->request_id());
@@ -380,7 +378,7 @@
 
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   EXPECT_EQ(kRequestId1, last_picked_->request_id());
@@ -406,7 +404,7 @@
 
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   EXPECT_EQ(kRequestId2, last_picked_->request_id());
@@ -432,7 +430,7 @@
 
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   EXPECT_EQ(kRequestId2, last_picked_->request_id());
@@ -461,7 +459,7 @@
   // Add test requests on the Queue.
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   // Pump the loop again to give the async queue the opportunity to return
@@ -493,7 +491,7 @@
   // Add test requests on the Queue.
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   // Pump the loop again to give the async queue the opportunity to return
@@ -531,7 +529,7 @@
   // Add test requests on the Queue.
   QueueRequests(request1, request2);
 
-  task()->Run();
+  task()->Execute(TaskCompletionCallback());
   PumpLoop();
 
   // Pump the loop again to give the async queue the opportunity to return
diff --git a/components/offline_pages/core/background/reconcile_task.h b/components/offline_pages/core/background/reconcile_task.h
index 100d1c05..0a2b5203 100644
--- a/components/offline_pages/core/background/reconcile_task.h
+++ b/components/offline_pages/core/background/reconcile_task.h
@@ -24,11 +24,11 @@
                 RequestQueueStore::UpdateCallback callback);
   ~ReconcileTask() override;
 
+ private:
   // TaskQueue::Task implementation:
   // Starts the async chain.
   void Run() override;
 
- private:
   // Step 1. Get results from the store.
   void GetRequests();
 
diff --git a/components/offline_pages/core/background/reconcile_task_unittest.cc b/components/offline_pages/core/background/reconcile_task_unittest.cc
index 6a94916..47308aa 100644
--- a/components/offline_pages/core/background/reconcile_task_unittest.cc
+++ b/components/offline_pages/core/background/reconcile_task_unittest.cc
@@ -8,6 +8,7 @@
 #include <set>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/core/background/request_coordinator.h"
@@ -126,7 +127,7 @@
   QueueRequests(request1, request2);
 
   // Initiate cleanup.
-  task()->Run();
+  task()->Execute(base::DoNothing());
   PumpLoop();
 
   // See what is left in the queue, should be just the other request.
@@ -161,7 +162,7 @@
   QueueRequests(request1, request2);
 
   // Initiate cleanup.
-  task()->Run();
+  task()->Execute(base::DoNothing());
   PumpLoop();
 
   // See what is left in the queue, should be just the other request.
diff --git a/components/offline_pages/core/background/remove_requests_task.h b/components/offline_pages/core/background/remove_requests_task.h
index 85229ea48..e9ced3e 100644
--- a/components/offline_pages/core/background/remove_requests_task.h
+++ b/components/offline_pages/core/background/remove_requests_task.h
@@ -24,10 +24,9 @@
                      RequestQueueStore::UpdateCallback callback);
   ~RemoveRequestsTask() override;
 
+ private:
   // TaskQueue::Task implementation.
   void Run() override;
-
- private:
   // Step 1. Removes requests from the store.
   void RemoveRequests();
   // Step for early termination, that builds failure result.
diff --git a/components/offline_pages/core/background/remove_requests_task_unittest.cc b/components/offline_pages/core/background/remove_requests_task_unittest.cc
index bf2c6c8..de815d1 100644
--- a/components/offline_pages/core/background/remove_requests_task_unittest.cc
+++ b/components/offline_pages/core/background/remove_requests_task_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
@@ -83,7 +84,7 @@
       &store_, request_ids,
       base::BindOnce(&RemoveRequestsTaskTest::RemoveRequestsCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
@@ -102,7 +103,7 @@
       &store_, request_ids,
       base::BindOnce(&RemoveRequestsTaskTest::RemoveRequestsCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(1UL, last_result()->item_statuses.size());
@@ -122,7 +123,7 @@
       &store_, request_ids,
       base::BindOnce(&RemoveRequestsTaskTest::RemoveRequestsCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(2UL, last_result()->item_statuses.size());
@@ -145,7 +146,7 @@
       &store_, request_ids,
       base::BindOnce(&RemoveRequestsTaskTest::RemoveRequestsCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(0UL, last_result()->item_statuses.size());
@@ -161,7 +162,7 @@
       &store_, request_ids,
       base::BindOnce(&RemoveRequestsTaskTest::RemoveRequestsCallback,
                      base::Unretained(this)));
-  task.Run();
+  task.Execute(base::DoNothing());
   PumpLoop();
   ASSERT_TRUE(last_result());
   EXPECT_EQ(2UL, last_result()->item_statuses.size());
diff --git a/components/offline_pages/core/background/update_request_task.h b/components/offline_pages/core/background/update_request_task.h
index 7871ec1..d1e44f14 100644
--- a/components/offline_pages/core/background/update_request_task.h
+++ b/components/offline_pages/core/background/update_request_task.h
@@ -25,10 +25,9 @@
                     RequestQueueStore::UpdateCallback callback);
   ~UpdateRequestTask() override;
 
+ protected:
   // TaskQueue::Task implementation.
   void Run() override;
-
- protected:
   // Step 1. Reading the requests.
   void ReadRequest();
   // Step 2. Work is done in the implementation step.
diff --git a/components/offline_pages/core/model/add_page_task.h b/components/offline_pages/core/model/add_page_task.h
index 70bb11a..3ce6adec 100644
--- a/components/offline_pages/core/model/add_page_task.h
+++ b/components/offline_pages/core/model/add_page_task.h
@@ -30,10 +30,10 @@
               AddPageTaskCallback callback);
   ~AddPageTask() override;
 
+ private:
   // Task implementation.
   void Run() override;
 
- private:
   void OnAddPageDone(ItemActionStatus status);
   void InformAddPageDone(AddPageResult result);
 
diff --git a/components/offline_pages/core/model/cleanup_visuals_task.h b/components/offline_pages/core/model/cleanup_visuals_task.h
index 56ae19b..83ddd26d 100644
--- a/components/offline_pages/core/model/cleanup_visuals_task.h
+++ b/components/offline_pages/core/model/cleanup_visuals_task.h
@@ -30,10 +30,10 @@
                      CleanupVisualsCallback complete_callback);
   ~CleanupVisualsTask() override;
 
+ private:
   // Task implementation:
   void Run() override;
 
- private:
   void Complete(Result result);
   OfflinePageMetadataStore* store_;
   base::Time now_;
diff --git a/components/offline_pages/core/model/clear_storage_task.h b/components/offline_pages/core/model/clear_storage_task.h
index f67f6f9..927c038 100644
--- a/components/offline_pages/core/model/clear_storage_task.h
+++ b/components/offline_pages/core/model/clear_storage_task.h
@@ -50,10 +50,10 @@
                    ClearStorageCallback callback);
   ~ClearStorageTask() override;
 
+ private:
   // Task implementation.
   void Run() override;
 
- private:
   void OnGetStorageStatsDone(const ArchiveManager::StorageStats& stats);
   void OnClearPagesDone(std::pair<size_t, DeletePageResult> result);
   void InformClearStorageDone(size_t pages_cleared, ClearStorageResult result);
diff --git a/components/offline_pages/core/model/delete_page_task.h b/components/offline_pages/core/model/delete_page_task.h
index 0ff39b4..5bb209d9 100644
--- a/components/offline_pages/core/model/delete_page_task.h
+++ b/components/offline_pages/core/model/delete_page_task.h
@@ -60,9 +60,6 @@
 
   ~DeletePageTask() override;
 
-  // Task implementation.
-  void Run() override;
-
   // Deletes a single page from the database. This function reads
   // from the database and should be called from within an
   // |SqlStoreBase::Execute()| call.
@@ -78,6 +75,9 @@
   using DeleteFunction =
       base::OnceCallback<DeletePageTaskResult(sql::Database*)>;
 
+  // Task implementation.
+  void Run() override;
+
   // Making the constructor private, in order to use static methods to create
   // tasks.
   DeletePageTask(OfflinePageMetadataStore* store,
diff --git a/components/offline_pages/core/model/get_pages_task.h b/components/offline_pages/core/model/get_pages_task.h
index 4a49bb00..efb64633 100644
--- a/components/offline_pages/core/model/get_pages_task.h
+++ b/components/offline_pages/core/model/get_pages_task.h
@@ -37,9 +37,6 @@
 
   ~GetPagesTask() override;
 
-  // Task implementation:
-  void Run() override;
-
   // Reads and returns all pages matching |criteria|. This function reads
   // from the database and should be called from within an
   // |SqlStoreBase::Execute()| call.
@@ -48,6 +45,9 @@
       sql::Database* db);
 
  private:
+  // Task implementation:
+  void Run() override;
+
   void CompleteWithResult(ReadResult result);
 
   OfflinePageMetadataStore* store_;
diff --git a/components/offline_pages/core/model/get_visuals_task.h b/components/offline_pages/core/model/get_visuals_task.h
index 2d8a984..6c71169 100644
--- a/components/offline_pages/core/model/get_visuals_task.h
+++ b/components/offline_pages/core/model/get_visuals_task.h
@@ -27,12 +27,12 @@
                  CompleteCallback complete_callback);
   ~GetVisualsTask() override;
 
-  // Task implementation:
-  void Run() override;
-
  private:
   typedef std::unique_ptr<OfflinePageVisuals> Result;
 
+  // Task implementation:
+  void Run() override;
+
   void Complete(Result result);
 
   OfflinePageMetadataStore* store_;
diff --git a/components/offline_pages/core/model/mark_page_accessed_task.h b/components/offline_pages/core/model/mark_page_accessed_task.h
index 0efaf782..c93e7f2 100644
--- a/components/offline_pages/core/model/mark_page_accessed_task.h
+++ b/components/offline_pages/core/model/mark_page_accessed_task.h
@@ -27,10 +27,10 @@
                        const base::Time& access_time);
   ~MarkPageAccessedTask() override;
 
+ private:
   // Task implementation.
   void Run() override;
 
- private:
   void OnMarkPageAccessedDone(bool result);
 
   // The metadata store used to update the page. Not owned.
diff --git a/components/offline_pages/core/model/persistent_page_consistency_check_task.h b/components/offline_pages/core/model/persistent_page_consistency_check_task.h
index 051fca15..9997ec0 100644
--- a/components/offline_pages/core/model/persistent_page_consistency_check_task.h
+++ b/components/offline_pages/core/model/persistent_page_consistency_check_task.h
@@ -46,10 +46,10 @@
       PersistentPageConsistencyCheckCallback callback);
   ~PersistentPageConsistencyCheckTask() override;
 
+ private:
   // Task implementation:
   void Run() override;
 
- private:
   void OnPersistentPageConsistencyCheckDone(CheckResult result);
 
   // The store containing the offline pages. Not owned.
diff --git a/components/offline_pages/core/model/startup_maintenance_task.h b/components/offline_pages/core/model/startup_maintenance_task.h
index bab8ca6..dc197756 100644
--- a/components/offline_pages/core/model/startup_maintenance_task.h
+++ b/components/offline_pages/core/model/startup_maintenance_task.h
@@ -23,10 +23,10 @@
                          ArchiveManager* archive_manager);
   ~StartupMaintenanceTask() override;
 
+ private:
   // Task implementation:
   void Run() override;
 
- private:
   void OnStartupMaintenanceDone(bool result);
 
   // The store containing the offline pages. Not owned.
diff --git a/components/offline_pages/core/model/store_visuals_task.h b/components/offline_pages/core/model/store_visuals_task.h
index 4909ec9..8674c3d 100644
--- a/components/offline_pages/core/model/store_visuals_task.h
+++ b/components/offline_pages/core/model/store_visuals_task.h
@@ -40,10 +40,10 @@
       std::string favicon,
       CompleteCallback callback);
 
+ private:
   // Task implementation:
   void Run() override;
 
- private:
   StoreVisualsTask(OfflinePageMetadataStore* store,
                    int64_t offline_id,
                    std::string thumbnail,
diff --git a/components/offline_pages/core/model/update_publish_id_task.h b/components/offline_pages/core/model/update_publish_id_task.h
index 338735bf..75b1d09 100644
--- a/components/offline_pages/core/model/update_publish_id_task.h
+++ b/components/offline_pages/core/model/update_publish_id_task.h
@@ -31,10 +31,10 @@
                       base::OnceCallback<void(bool)> callback);
   ~UpdatePublishIdTask() override;
 
+ private:
   // Task implementation.
   void Run() override;
 
- private:
   void OnUpdatePublishIdDone(bool result);
 
   // The metadata store used to update the page. Not owned.
diff --git a/components/offline_pages/core/model/visuals_availability_task.h b/components/offline_pages/core/model/visuals_availability_task.h
index 5fa290c..a9a8089 100644
--- a/components/offline_pages/core/model/visuals_availability_task.h
+++ b/components/offline_pages/core/model/visuals_availability_task.h
@@ -25,10 +25,10 @@
                           VisualsAvailableCallback exists_callback);
   ~VisualsAvailabilityTask() override;
 
+ private:
   // Task implementation:
   void Run() override;
 
- private:
   void OnVisualsAvailable(VisualsAvailability availability);
 
   OfflinePageMetadataStore* store_;
diff --git a/components/offline_pages/core/offline_page_metadata_store_test_util.cc b/components/offline_pages/core/offline_page_metadata_store_test_util.cc
index b31d621..aedaaf5 100644
--- a/components/offline_pages/core/offline_page_metadata_store_test_util.cc
+++ b/components/offline_pages/core/offline_page_metadata_store_test_util.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/callback_forward.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
@@ -74,7 +75,7 @@
         result = cb_result;
         run_loop.Quit();
       }));
-  task->Run();
+  task->Execute(base::DoNothing());
   run_loop.Run();
   EXPECT_EQ(AddPageResult::SUCCESS, result);
 }
@@ -107,7 +108,7 @@
               page = new OfflinePageItem(cb_pages[0]);
             run_loop.Quit();
           })));
-  task->Run();
+  task->Execute(base::DoNothing());
   run_loop.Run();
   return base::WrapUnique<OfflinePageItem>(page);
 }
diff --git a/components/offline_pages/core/prefetch/tasks/add_unique_urls_task.h b/components/offline_pages/core/prefetch/tasks/add_unique_urls_task.h
index 0764f6b..a5f9682 100644
--- a/components/offline_pages/core/prefetch/tasks/add_unique_urls_task.h
+++ b/components/offline_pages/core/prefetch/tasks/add_unique_urls_task.h
@@ -38,10 +38,8 @@
                     const std::string& name_space,
                     const std::vector<PrefetchURL>& prefetch_urls);
   ~AddUniqueUrlsTask() override;
-
-  void Run() override;
-
  private:
+  void Run() override;
   void OnUrlsAdded(Result result);
 
   // Dispatcher to call back to with results. Not owned.
diff --git a/components/offline_pages/core/prefetch/tasks/download_archives_task.h b/components/offline_pages/core/prefetch/tasks/download_archives_task.h
index 5e41e87..6cab857 100644
--- a/components/offline_pages/core/prefetch/tasks/download_archives_task.h
+++ b/components/offline_pages/core/prefetch/tasks/download_archives_task.h
@@ -47,9 +47,8 @@
                        PrefService* prefs);
   ~DownloadArchivesTask() override;
 
-  void Run() override;
-
  private:
+  void Run() override;
   void SendItemsToPrefetchDownloader(
       std::unique_ptr<ItemsToDownload> items_to_download);
 
diff --git a/components/offline_pages/core/prefetch/tasks/download_cleanup_task.h b/components/offline_pages/core/prefetch/tasks/download_cleanup_task.h
index 65276a1..599512892 100644
--- a/components/offline_pages/core/prefetch/tasks/download_cleanup_task.h
+++ b/components/offline_pages/core/prefetch/tasks/download_cleanup_task.h
@@ -35,9 +35,8 @@
           success_downloads);
   ~DownloadCleanupTask() override;
 
-  void Run() override;
-
  private:
+  void Run() override;
   void OnFinished(bool success);
 
   PrefetchDispatcher* prefetch_dispatcher_;  // Outlives this class.
diff --git a/components/offline_pages/core/prefetch/tasks/download_completed_task.h b/components/offline_pages/core/prefetch/tasks/download_completed_task.h
index 2b0505b7..0b770e1 100644
--- a/components/offline_pages/core/prefetch/tasks/download_completed_task.h
+++ b/components/offline_pages/core/prefetch/tasks/download_completed_task.h
@@ -22,8 +22,6 @@
                         const PrefetchDownloadResult& download_result);
   ~DownloadCompletedTask() override;
 
-  void Run() override;
-
   struct UpdateInfo {
     // True if the row was updated.
     bool success = false;
@@ -32,6 +30,7 @@
   };
 
  private:
+  void Run() override;
   void OnPrefetchItemUpdated(bool successful_download, UpdateInfo update_info);
 
   PrefetchDispatcher* prefetch_dispatcher_;  // Outlives this class.
diff --git a/components/offline_pages/core/prefetch/tasks/finalize_dismissed_url_suggestion_task.h b/components/offline_pages/core/prefetch/tasks/finalize_dismissed_url_suggestion_task.h
index 789c3f0e..2ccdc98 100644
--- a/components/offline_pages/core/prefetch/tasks/finalize_dismissed_url_suggestion_task.h
+++ b/components/offline_pages/core/prefetch/tasks/finalize_dismissed_url_suggestion_task.h
@@ -34,9 +34,8 @@
                                      const ClientId& client_id);
   ~FinalizeDismissedUrlSuggestionTask() override;
 
-  void Run() override;
-
  private:
+  void Run() override;
   void OnComplete(bool removed);
 
   PrefetchStore* prefetch_store_;
diff --git a/components/offline_pages/core/prefetch/tasks/generate_page_bundle_reconcile_task.h b/components/offline_pages/core/prefetch/tasks/generate_page_bundle_reconcile_task.h
index 6189937..e5dd7134 100644
--- a/components/offline_pages/core/prefetch/tasks/generate_page_bundle_reconcile_task.h
+++ b/components/offline_pages/core/prefetch/tasks/generate_page_bundle_reconcile_task.h
@@ -25,10 +25,9 @@
       PrefetchNetworkRequestFactory* request_factory);
   ~GeneratePageBundleReconcileTask() override;
 
+ private:
   // Task implementation.
   void Run() override;
-
- private:
   void FinishedUpdate(bool success);
 
   PrefetchStore* prefetch_store_;
diff --git a/components/offline_pages/core/prefetch/tasks/generate_page_bundle_task.h b/components/offline_pages/core/prefetch/tasks/generate_page_bundle_task.h
index 766af07e..0669805 100644
--- a/components/offline_pages/core/prefetch/tasks/generate_page_bundle_task.h
+++ b/components/offline_pages/core/prefetch/tasks/generate_page_bundle_task.h
@@ -31,10 +31,9 @@
                          PrefetchRequestFinishedCallback callback);
   ~GeneratePageBundleTask() override;
 
+ private:
   // Task implementation.
   void Run() override;
-
- private:
   void StartGeneratePageBundle(std::unique_ptr<UrlAndIds> url_and_ids);
 
   PrefetchDispatcher* prefetch_dispatcher_;
diff --git a/components/offline_pages/core/prefetch/tasks/get_operation_task.h b/components/offline_pages/core/prefetch/tasks/get_operation_task.h
index 9ca54a4..0bbe745 100644
--- a/components/offline_pages/core/prefetch/tasks/get_operation_task.h
+++ b/components/offline_pages/core/prefetch/tasks/get_operation_task.h
@@ -36,10 +36,9 @@
                    GetOperationFinishedCallback callback);
   ~GetOperationTask() override;
 
+ private:
   // Task implementation.
   void Run() override;
-
- private:
   void StartGetOperationRequests(OperationResultList list);
 
   PrefetchStore* prefetch_store_;
diff --git a/components/offline_pages/core/prefetch/tasks/get_visuals_info_task.h b/components/offline_pages/core/prefetch/tasks/get_visuals_info_task.h
index 8febe50..09fbdbb 100644
--- a/components/offline_pages/core/prefetch/tasks/get_visuals_info_task.h
+++ b/components/offline_pages/core/prefetch/tasks/get_visuals_info_task.h
@@ -34,10 +34,9 @@
                      ResultCallback callback);
   ~GetVisualsInfoTask() override;
 
+ private:
   // Task implementation.
   void Run() override;
-
- private:
   void CompleteTaskAndForwardResult(Result result);
   PrefetchStore* prefetch_store_;
   int64_t offline_id_;
diff --git a/components/offline_pages/core/prefetch/tasks/import_archives_task.h b/components/offline_pages/core/prefetch/tasks/import_archives_task.h
index ddbe7b2a4..88f124e 100644
--- a/components/offline_pages/core/prefetch/tasks/import_archives_task.h
+++ b/components/offline_pages/core/prefetch/tasks/import_archives_task.h
@@ -23,9 +23,8 @@
                      PrefetchImporter* prefetch_importer);
   ~ImportArchivesTask() override;
 
-  void Run() override;
-
  private:
+  void Run() override;
   void OnArchivesRetrieved(
       std::unique_ptr<std::vector<PrefetchArchiveInfo>> archive);
 
diff --git a/components/offline_pages/core/prefetch/tasks/import_cleanup_task.h b/components/offline_pages/core/prefetch/tasks/import_cleanup_task.h
index d41b874..4d60844a 100644
--- a/components/offline_pages/core/prefetch/tasks/import_cleanup_task.h
+++ b/components/offline_pages/core/prefetch/tasks/import_cleanup_task.h
@@ -22,9 +22,8 @@
                     PrefetchImporter* prefetch_importer);
   ~ImportCleanupTask() override;
 
-  void Run() override;
-
  private:
+  void Run() override;
   void OnPrefetchItemUpdated(bool row_was_updated);
 
   PrefetchStore* prefetch_store_;        // Outlives this class.
diff --git a/components/offline_pages/core/prefetch/tasks/import_completed_task.h b/components/offline_pages/core/prefetch/tasks/import_completed_task.h
index 27468f6..3b548e5 100644
--- a/components/offline_pages/core/prefetch/tasks/import_completed_task.h
+++ b/components/offline_pages/core/prefetch/tasks/import_completed_task.h
@@ -25,9 +25,8 @@
                       bool success);
   ~ImportCompletedTask() override;
 
-  void Run() override;
-
  private:
+  void Run() override;
   void OnStateUpdatedToFinished(bool success);
 
   PrefetchDispatcher* prefetch_dispatcher_;  // Outlives this class.
diff --git a/components/offline_pages/core/prefetch/tasks/mark_operation_done_task.h b/components/offline_pages/core/prefetch/tasks/mark_operation_done_task.h
index b37c3b6..8b6b830 100644
--- a/components/offline_pages/core/prefetch/tasks/mark_operation_done_task.h
+++ b/components/offline_pages/core/prefetch/tasks/mark_operation_done_task.h
@@ -40,9 +40,6 @@
                         const std::string& operation_name);
   ~MarkOperationDoneTask() override;
 
-  // Task implementation.
-  void Run() override;
-
   StoreResult store_result() const { return std::get<0>(result_); }
 
   // Number of rows changed, or -1 if there was a store error (or not yet
@@ -50,6 +47,8 @@
   int64_t change_count() const { return std::get<1>(result_); }
 
  private:
+  // Task implementation.
+  void Run() override;
   void MarkOperationDone(int updated_entry_count);
   void Done(TaskResult result);
 
diff --git a/components/offline_pages/core/prefetch/tasks/metrics_finalization_task.h b/components/offline_pages/core/prefetch/tasks/metrics_finalization_task.h
index dc57d6b..5ce7536 100644
--- a/components/offline_pages/core/prefetch/tasks/metrics_finalization_task.h
+++ b/components/offline_pages/core/prefetch/tasks/metrics_finalization_task.h
@@ -23,10 +23,9 @@
   explicit MetricsFinalizationTask(PrefetchStore* prefetch_store);
   ~MetricsFinalizationTask() override;
 
+ private:
   // Task implementation.
   void Run() override;
-
- private:
   void MetricsFinalized(bool result);
 
   PrefetchStore* prefetch_store_;
diff --git a/components/offline_pages/core/prefetch/tasks/page_bundle_update_task.h b/components/offline_pages/core/prefetch/tasks/page_bundle_update_task.h
index 2d096863..af1e26a4 100644
--- a/components/offline_pages/core/prefetch/tasks/page_bundle_update_task.h
+++ b/components/offline_pages/core/prefetch/tasks/page_bundle_update_task.h
@@ -43,10 +43,9 @@
                        const std::vector<RenderPageInfo>& pages);
   ~PageBundleUpdateTask() override;
 
+ private:
   // Task implementation.
   void Run() override;
-
- private:
   void FinishedWork(PageBundleUpdateResult result);
 
   // Owned by PrefetchService which also transitively owns |this|, so raw
diff --git a/components/offline_pages/core/prefetch/tasks/sent_get_operation_cleanup_task.h b/components/offline_pages/core/prefetch/tasks/sent_get_operation_cleanup_task.h
index 039718d..844a429d 100644
--- a/components/offline_pages/core/prefetch/tasks/sent_get_operation_cleanup_task.h
+++ b/components/offline_pages/core/prefetch/tasks/sent_get_operation_cleanup_task.h
@@ -24,9 +24,8 @@
                               PrefetchNetworkRequestFactory* request_factory);
   ~SentGetOperationCleanupTask() override;
 
-  void Run() override;
-
  private:
+  void Run() override;
   void OnFinished(bool success);
 
   PrefetchStore* prefetch_store_;                   // Outlives this class.
diff --git a/components/offline_pages/core/prefetch/tasks/stale_entry_finalizer_task.h b/components/offline_pages/core/prefetch/tasks/stale_entry_finalizer_task.h
index e441e4fd..dfded34 100644
--- a/components/offline_pages/core/prefetch/tasks/stale_entry_finalizer_task.h
+++ b/components/offline_pages/core/prefetch/tasks/stale_entry_finalizer_task.h
@@ -34,12 +34,11 @@
                           PrefetchStore* prefetch_store);
   ~StaleEntryFinalizerTask() override;
 
-  void Run() override;
-
   // Will be set to true upon after an error-free run.
   Result final_status() const { return final_status_; }
 
  private:
+  void Run() override;
   void OnFinished(Result result);
 
   // Not owned.
diff --git a/components/offline_pages/task/closure_task.h b/components/offline_pages/task/closure_task.h
index ae5ede5..013f2141 100644
--- a/components/offline_pages/task/closure_task.h
+++ b/components/offline_pages/task/closure_task.h
@@ -16,9 +16,10 @@
  public:
   ClosureTask(base::OnceClosure closure);
   ~ClosureTask() override;
-  void Run() override;
 
  private:
+  void Run() override;
+
   base::OnceClosure closure_;
 };
 
diff --git a/components/offline_pages/task/task.cc b/components/offline_pages/task/task.cc
index c662a9b..4198454 100644
--- a/components/offline_pages/task/task.cc
+++ b/components/offline_pages/task/task.cc
@@ -6,28 +6,30 @@
 
 #include <utility>
 
+#include "base/logging.h"
+
 namespace offline_pages {
 
-Task::Task() {}
-
-Task::~Task() {}
-
-void Task::SetTaskCompletionCallbackForTesting(
-    TaskCompletionCallback task_completion_callback) {
-  SetTaskCompletionCallback(std::move(task_completion_callback));
+Task::Task() = default;
+Task::~Task() {
+  // This may happen when tearing-down the |TaskQueue|.
+  DLOG_IF(WARNING, started_ && !completed_)
+      << "Task being destroyed before completion";
 }
 
-void Task::SetTaskCompletionCallback(
-    TaskCompletionCallback task_completion_callback) {
-  // Attempts to enforce that SetTaskCompletionCallback is at most called once.
-  DCHECK(task_completion_callback_.is_null());
-  DCHECK(!task_completion_callback.is_null());
-  task_completion_callback_ = std::move(task_completion_callback);
+void Task::Execute(base::OnceClosure complete_callback) {
+  DCHECK(!started_);
+  started_ = true;
+  task_completion_callback_ = std::move(complete_callback);
+  Run();
 }
 
 void Task::TaskComplete() {
+  DCHECK(started_);
+  DCHECK(!completed_);
+  completed_ = true;
   if (!task_completion_callback_.is_null())
-    std::move(task_completion_callback_).Run(this);
+    std::move(task_completion_callback_).Run();
 }
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/task/task.h b/components/offline_pages/task/task.h
index a071ffa..2f1d8cf 100644
--- a/components/offline_pages/task/task.h
+++ b/components/offline_pages/task/task.h
@@ -6,68 +6,32 @@
 #define COMPONENTS_OFFLINE_PAGES_TASK_TASK_H_
 
 #include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
 
 namespace offline_pages {
 
-// Task interface for consumers of the TaskQueue. Implements a mechanism for
-// task completion.
-//
-// To create your own Task:
-// * Derive your new task from offline_pages::Task.
-// * Implement your task with as many async operations on the controlled
-//   resource as is required. (In general the smaller the task the better.)
-// * Whenever the task is terminated, call |Task::TaskComplete|. This step is
-//   mandatory to ensure |TaskQueue| can pick another task. It should be called
-//   once, but in every situation when task is exited.
-// * To schedule task for execution call |TaskQueue::AddTask|.
-//
-// If there is a chance that a task callback will come after the task is
-// destroyed, it is up to the task to actually implement mechanism to deal with
-// that, such as using a |base::WeakPtrFactory|.
+// A task which may run asynchronous steps. Its primary purpose is to implement
+// operations to be inserted into a |TaskQueue|, however, tasks can also be run
+// outside of a |TaskQueue|.
 class Task {
  public:
-  // Signature of the method to be called by a task, when its work is done.
-  typedef base::OnceCallback<void(Task*)> TaskCompletionCallback;
-
   Task();
   virtual ~Task();
+  Task(const Task&) = delete;
+  Task& operator=(const Task&) = delete;
 
-  // Entry point to the task. This is used by the queue to start the task, and
-  // first step of the task should be implemented by overloading this method.
-  // The task should define an additional method for every asynchronous step,
-  // with each step setting up the next step as a callback.
-  virtual void Run() = 0;
-
-  // Sets the callback normally used by |TaskQueue| for testing. See
-  // |SetTaskCompletionCallback| for details.
-  void SetTaskCompletionCallbackForTesting(
-      TaskCompletionCallback task_completion_callback);
+  void Execute(base::OnceClosure complete_callback);
 
  protected:
-  // Tasks must call |TaskComplete| as their last step. This will cause
-  // |TaskQueue| to schedule the task's destruction and start another task if
-  // one is available.
+  // Entry point to the task. Called by |Execute()| to perform the task.
+  // Must call |TaskComplete()| as the final step.
+  virtual void Run() = 0;
+
+  // Tasks must call |TaskComplete()| as their last step.
   void TaskComplete();
 
- private:
-  friend class TaskQueue;
-
-  // Allows |TaskQueue| to set the |task_completion_callback| that will be used
-  // to inform it when the task is done. If the task is run outside of the
-  // |TaskQueue| and completion callback is not set, it will also work.
-  //
-  // Note: The callback implementation is responsible for scheduling work with
-  // the appropriate task runner and not to cause side effects to the |Task|
-  // calling into |TaskComplete|.
-  void SetTaskCompletionCallback(
-      TaskCompletionCallback task_completion_callback);
-
-  // Completion callback for this task set by |SetTaskCompletionCallback|.
-  TaskCompletionCallback task_completion_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(Task);
+  base::OnceClosure task_completion_callback_;
+  bool completed_ = false;
+  bool started_ = false;
 };
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/task/task_queue.cc b/components/offline_pages/task/task_queue.cc
index 13e5790..12173f8 100644
--- a/components/offline_pages/task/task_queue.cc
+++ b/components/offline_pages/task/task_queue.cc
@@ -23,8 +23,6 @@
 
 void TaskQueue::AddTask(std::unique_ptr<Task> task) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  task->SetTaskCompletionCallback(base::BindOnce(
-      &TaskCompletedCallback, task_runner_, weak_ptr_factory_.GetWeakPtr()));
   tasks_.push(std::move(task));
   StartTaskIfAvailable();
 }
@@ -61,7 +59,9 @@
 }
 
 void TaskQueue::RunCurrentTask() {
-  current_task_->Run();
+  current_task_->Execute(base::BindOnce(&TaskCompletedCallback, task_runner_,
+                                        weak_ptr_factory_.GetWeakPtr(),
+                                        current_task_.get()));
 }
 
 // static
diff --git a/components/offline_pages/task/task_unittest.cc b/components/offline_pages/task/task_unittest.cc
index 6bdb771..3bc87c1 100644
--- a/components/offline_pages/task/task_unittest.cc
+++ b/components/offline_pages/task/task_unittest.cc
@@ -7,53 +7,78 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/task/test_task.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
-
+namespace {
 using TaskState = TestTask::TaskState;
 
+class ClosureStub {
+ public:
+  base::OnceClosure Bind() {
+    return base::BindOnce(&ClosureStub::Done, base::Unretained(this));
+  }
+  bool called() const { return called_; }
+
+ private:
+  void Done() { called_ = true; }
+  bool called_ = false;
+};
+
+class NestingTask : public Task {
+ public:
+  explicit NestingTask(NestingTask* child) : child_(child) {}
+  void Run() override {
+    if (child_) {
+      child_->Execute(
+          base::BindOnce(&NestingTask::NestedTaskDone, base::Unretained(this)));
+    } else {
+      completed_ = true;
+      TaskComplete();
+    }
+  }
+
+  bool completed() const { return completed_; }
+
+ private:
+  void NestedTaskDone() {
+    completed_ = true;
+    TaskComplete();
+  }
+
+  NestingTask* child_ = nullptr;
+  bool completed_ = false;
+};
+
 class OfflineTaskTest : public testing::Test {
  public:
   OfflineTaskTest();
 
-  void TaskCompleted(Task* task);
   void PumpLoop();
-
-  Task* completed_task() const { return completed_task_; }
-
  private:
-  Task* completed_task_;
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle task_runner_handle_;
 };
 
 OfflineTaskTest::OfflineTaskTest()
-    : completed_task_(nullptr),
-      task_runner_(new base::TestSimpleTaskRunner),
+    : task_runner_(new base::TestSimpleTaskRunner),
       task_runner_handle_(task_runner_) {}
 
 void OfflineTaskTest::PumpLoop() {
   task_runner_->RunUntilIdle();
 }
 
-void OfflineTaskTest::TaskCompleted(Task* task) {
-  auto set_task_callback = [](Task** t_ptr, Task* t) { *t_ptr = t; };
-  task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(set_task_callback, &completed_task_, task));
-}
-
 TEST_F(OfflineTaskTest, RunTaskStepByStep) {
   ConsumedResource resource;
   TestTask task(&resource);
-  task.SetTaskCompletionCallbackForTesting(
-      base::BindOnce(&OfflineTaskTest::TaskCompleted, base::Unretained(this)));
 
   EXPECT_EQ(TaskState::NOT_STARTED, task.state());
-  task.Run();
+  ClosureStub complete;
+  task.Execute(complete.Bind());
   EXPECT_EQ(TaskState::STEP_1, task.state());
   EXPECT_TRUE(resource.HasNextStep());
   resource.CompleteStep();
@@ -62,14 +87,14 @@
   resource.CompleteStep();
   EXPECT_EQ(TaskState::COMPLETED, task.state());
   PumpLoop();
-  EXPECT_EQ(completed_task(), &task);
+  EXPECT_TRUE(complete.called());
 }
 
 TEST_F(OfflineTaskTest, LeaveEarly) {
   ConsumedResource resource;
   TestTask task(&resource, true /* leave early */);
   EXPECT_EQ(TaskState::NOT_STARTED, task.state());
-  task.Run();
+  task.Execute(base::DoNothing());
   EXPECT_EQ(TaskState::STEP_1, task.state());
   EXPECT_TRUE(resource.HasNextStep());
   resource.CompleteStep();
@@ -79,4 +104,14 @@
   EXPECT_FALSE(resource.HasNextStep());
 }
 
+TEST_F(OfflineTaskTest, RunNestedTask) {
+  NestingTask child(nullptr);
+  NestingTask parent(&child);
+
+  parent.Execute(base::DoNothing());
+  EXPECT_TRUE(child.completed());
+  EXPECT_TRUE(parent.completed());
+}
+
+}  // namespace
 }  // namespace offline_pages
diff --git a/components/offline_pages/task/test_task_runner.cc b/components/offline_pages/task/test_task_runner.cc
index 5ebdc31..59a1251 100644
--- a/components/offline_pages/task/test_task_runner.cc
+++ b/components/offline_pages/task/test_task_runner.cc
@@ -18,9 +18,8 @@
 void TestTaskRunner::RunTask(Task* task) {
   DCHECK(task);
   base::RunLoop run_loop;
-  task->SetTaskCompletionCallbackForTesting(base::BindOnce(
-      [](base::RunLoop* run_loop, Task*) { run_loop->Quit(); }, &run_loop));
-  task->Run();
+  task->Execute(base::BindOnce(
+      [](base::RunLoop* run_loop) { run_loop->Quit(); }, &run_loop));
   run_loop.Run();
 }
 
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 1af2f68a..34e2ea45e 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -131,9 +131,10 @@
     "OmniboxSuggestionTransparencyOptions", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Feature to enable clipboard provider to suggest copied text.
+// TODO(gangwu): Remove this feature flag after full launched in Android.
 const base::Feature kEnableClipboardProviderTextSuggestions{
   "OmniboxEnableClipboardProviderTextSuggestions",
-#if defined(OS_IOS)
+#if defined(OS_IOS) || defined(OS_ANDROID)
       base::FEATURE_ENABLED_BY_DEFAULT
 #else
       base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/components/password_manager/core/browser/multi_store_password_save_manager.cc b/components/password_manager/core/browser/multi_store_password_save_manager.cc
index a864d20..0c9d481 100644
--- a/components/password_manager/core/browser/multi_store_password_save_manager.cc
+++ b/components/password_manager/core/browser/multi_store_password_save_manager.cc
@@ -51,7 +51,7 @@
 MultiStorePasswordSaveManager::~MultiStorePasswordSaveManager() = default;
 
 FormSaver* MultiStorePasswordSaveManager::GetFormSaverForGeneration() {
-  return IsAccountStoreActive() && account_store_form_saver_
+  return IsAccountStoreEnabled() && account_store_form_saver_
              ? account_store_form_saver_.get()
              : form_saver_.get();
 }
@@ -69,21 +69,20 @@
 
   switch (pending_credentials_.in_store) {
     case PasswordForm::Store::kAccountStore:
-      if (account_store_form_saver_ && IsAccountStoreActive())
+      if (account_store_form_saver_ && IsAccountStoreEnabled()) {
         account_store_form_saver_->Save(
             pending_credentials_, AccountStoreMatches(matches), old_password);
+      }
+      // TODO(crbug.com/1012203): Record UMA for how many passwords get dropped
+      // here. In rare cases it could happen that the user *was* opted in when
+      // the save dialog was shown, but now isn't anymore.
       break;
     case PasswordForm::Store::kProfileStore:
       form_saver_->Save(pending_credentials_, ProfileStoreMatches(matches),
                         old_password);
       break;
     case PasswordForm::Store::kNotSet:
-      if (account_store_form_saver_ && IsAccountStoreActive())
-        account_store_form_saver_->Save(
-            pending_credentials_, AccountStoreMatches(matches), old_password);
-      else
-        form_saver_->Save(pending_credentials_, ProfileStoreMatches(matches),
-                          old_password);
+      NOTREACHED();
       break;
   }
 }
@@ -95,7 +94,7 @@
   // update operation is no-op.
   form_saver_->Update(pending_credentials_, ProfileStoreMatches(matches),
                       old_password);
-  if (account_store_form_saver_ && IsAccountStoreActive()) {
+  if (account_store_form_saver_ && IsAccountStoreEnabled()) {
     account_store_form_saver_->Update(
         pending_credentials_, AccountStoreMatches(matches), old_password);
   }
@@ -104,7 +103,7 @@
 void MultiStorePasswordSaveManager::PermanentlyBlacklist(
     const PasswordStore::FormDigest& form_digest) {
   DCHECK(!client_->IsIncognito());
-  if (account_store_form_saver_ && IsAccountStoreActive() &&
+  if (account_store_form_saver_ && IsAccountStoreEnabled() &&
       client_->GetPasswordFeatureManager()->GetDefaultPasswordStore() ==
           PasswordForm::Store::kAccountStore) {
     account_store_form_saver_->PermanentlyBlacklist(form_digest);
@@ -118,7 +117,7 @@
   // Try to unblacklist in both stores anyway because if credentials don't
   // exist, the unblacklist operation is no-op.
   form_saver_->Unblacklist(form_digest);
-  if (account_store_form_saver_ && IsAccountStoreActive()) {
+  if (account_store_form_saver_ && IsAccountStoreEnabled()) {
     account_store_form_saver_->Unblacklist(form_digest);
   }
 }
@@ -130,9 +129,8 @@
   return result;
 }
 
-bool MultiStorePasswordSaveManager::IsAccountStoreActive() {
-  return client_->GetPasswordSyncState() ==
-         password_manager::ACCOUNT_PASSWORDS_ACTIVE_NORMAL_ENCRYPTION;
+bool MultiStorePasswordSaveManager::IsAccountStoreEnabled() {
+  return client_->GetPasswordFeatureManager()->IsOptedInForAccountStorage();
 }
 
 void MultiStorePasswordSaveManager::MoveCredentialsToAccountStore() {
diff --git a/components/password_manager/core/browser/multi_store_password_save_manager.h b/components/password_manager/core/browser/multi_store_password_save_manager.h
index 1dfac4c3..a7bceef 100644
--- a/components/password_manager/core/browser/multi_store_password_save_manager.h
+++ b/components/password_manager/core/browser/multi_store_password_save_manager.h
@@ -45,7 +45,7 @@
   FormSaver* GetFormSaverForGeneration() override;
 
  private:
-  bool IsAccountStoreActive();
+  bool IsAccountStoreEnabled();
 
   const std::unique_ptr<FormSaver> account_store_form_saver_;
   DISALLOW_COPY_AND_ASSIGN(MultiStorePasswordSaveManager);
diff --git a/components/password_manager/core/browser/multi_store_password_save_manager_unittest.cc b/components/password_manager/core/browser/multi_store_password_save_manager_unittest.cc
index b1dfc6ca..a3a59507 100644
--- a/components/password_manager/core/browser/multi_store_password_save_manager_unittest.cc
+++ b/components/password_manager/core/browser/multi_store_password_save_manager_unittest.cc
@@ -67,8 +67,6 @@
   MockPasswordManagerClient() = default;
   ~MockPasswordManagerClient() override = default;
 
-  MOCK_CONST_METHOD0(GetPasswordSyncState, SyncState());
-
   MOCK_CONST_METHOD0(IsMainFrameSecure, bool());
 
  private:
@@ -171,15 +169,10 @@
     fetcher_->NotifyFetchCompleted();
   }
 
-  void SetAccountStoreActive(bool is_active) {
-    if (is_active) {
-      ON_CALL(*client(), GetPasswordSyncState())
-          .WillByDefault(Return(
-              password_manager::ACCOUNT_PASSWORDS_ACTIVE_NORMAL_ENCRYPTION));
-      return;
-    }
-    ON_CALL(*client(), GetPasswordSyncState())
-        .WillByDefault(Return(password_manager::SYNCING_NORMAL_ENCRYPTION));
+  void SetAccountStoreEnabled(bool is_enabled) {
+    ON_CALL(*client()->GetPasswordFeatureManager(),
+            IsOptedInForAccountStorage())
+        .WillByDefault(Return(is_enabled));
   }
 
   void SetDefaultPasswordStore(const autofill::PasswordForm::Store& store) {
@@ -218,8 +211,8 @@
 };
 
 TEST_F(MultiStorePasswordSaveManagerTest,
-       SaveInAccountStoreWhenAccountStoreActive) {
-  SetAccountStoreActive(/*is_active=*/true);
+       SaveInAccountStoreWhenAccountStoreEnabled) {
+  SetAccountStoreEnabled(/*is_enabled=*/true);
 
   fetcher()->NotifyFetchCompleted();
 
@@ -240,8 +233,8 @@
 }
 
 TEST_F(MultiStorePasswordSaveManagerTest,
-       DoNotSaveInAccountStoreWhenAccountStoreInactive) {
-  SetAccountStoreActive(/*is_active=*/false);
+       DoNotSaveInAccountStoreWhenAccountStoreDisabled) {
+  SetAccountStoreEnabled(/*is_enabled=*/false);
 
   fetcher()->NotifyFetchCompleted();
 
@@ -262,7 +255,7 @@
 }
 
 TEST_F(MultiStorePasswordSaveManagerTest, SaveInProfileStore) {
-  SetAccountStoreActive(/*is_active=*/true);
+  SetAccountStoreEnabled(/*is_enabled=*/true);
 
   fetcher()->NotifyFetchCompleted();
 
@@ -283,50 +276,8 @@
 }
 
 TEST_F(MultiStorePasswordSaveManagerTest,
-       SaveInAccountStoreByDefaultIfAccountStoreIsActive) {
-  SetAccountStoreActive(/*is_active=*/true);
-
-  fetcher()->NotifyFetchCompleted();
-
-  PasswordForm parsed_submitted_form(parsed_submitted_form_);
-
-  password_save_manager()->CreatePendingCredentials(
-      parsed_submitted_form, observed_form_, submitted_form_,
-      /*is_http_auth=*/false,
-      /*is_credential_api_save=*/false);
-
-  EXPECT_TRUE(password_save_manager()->IsNewLogin());
-
-  EXPECT_CALL(*mock_profile_form_saver(), Save(_, _, _)).Times(0);
-  EXPECT_CALL(*mock_account_form_saver(), Save(_, _, _));
-
-  password_save_manager()->Save(observed_form_, parsed_submitted_form);
-}
-
-TEST_F(MultiStorePasswordSaveManagerTest,
-       SaveInProfileStoreByDefaultIfAccountStoreIsInactive) {
-  SetAccountStoreActive(/*is_active=*/false);
-
-  fetcher()->NotifyFetchCompleted();
-
-  PasswordForm parsed_submitted_form(parsed_submitted_form_);
-
-  password_save_manager()->CreatePendingCredentials(
-      parsed_submitted_form, observed_form_, submitted_form_,
-      /*is_http_auth=*/false,
-      /*is_credential_api_save=*/false);
-
-  EXPECT_TRUE(password_save_manager()->IsNewLogin());
-
-  EXPECT_CALL(*mock_profile_form_saver(), Save(_, _, _));
-  EXPECT_CALL(*mock_account_form_saver(), Save(_, _, _)).Times(0);
-
-  password_save_manager()->Save(observed_form_, parsed_submitted_form);
-}
-
-TEST_F(MultiStorePasswordSaveManagerTest,
        UpdateBothStoresIfCredentialsExistInAccountStoreOnly) {
-  SetAccountStoreActive(/*is_active=*/true);
+  SetAccountStoreEnabled(/*is_enabled=*/true);
 
   PasswordForm saved_match_in_account_store(saved_match_);
   saved_match_in_account_store.username_value =
@@ -349,7 +300,7 @@
 
 TEST_F(MultiStorePasswordSaveManagerTest,
        UpdateBothStoresIfCredentialsExistInProfileStoreOnly) {
-  SetAccountStoreActive(/*is_active=*/true);
+  SetAccountStoreEnabled(/*is_enabled=*/true);
 
   PasswordForm saved_match_in_profile_store(saved_match_);
   saved_match_in_profile_store.username_value =
@@ -372,7 +323,7 @@
 
 TEST_F(MultiStorePasswordSaveManagerTest,
        UpdateBothStoresIfCredentialsExistInBothStoreOnly) {
-  SetAccountStoreActive(/*is_active=*/true);
+  SetAccountStoreEnabled(/*is_enabled=*/true);
 
   PasswordForm saved_match_in_profile_store(saved_match_);
   saved_match_in_profile_store.username_value =
@@ -397,8 +348,8 @@
 }
 
 TEST_F(MultiStorePasswordSaveManagerTest,
-       PresaveGeneratedPasswordInAccountStoreIfAccountStoreActive) {
-  SetAccountStoreActive(/*is_active=*/true);
+       PresaveGeneratedPasswordInAccountStoreIfAccountStoreEnabled) {
+  SetAccountStoreEnabled(/*is_enabled=*/true);
   fetcher()->NotifyFetchCompleted();
 
   EXPECT_CALL(*mock_profile_form_saver(), Save(_, _, _)).Times(0);
@@ -408,8 +359,8 @@
 }
 
 TEST_F(MultiStorePasswordSaveManagerTest,
-       PresaveGeneratedPasswordInProfileStoreIfAccountStoreInactive) {
-  SetAccountStoreActive(/*is_active=*/false);
+       PresaveGeneratedPasswordInProfileStoreIfAccountStoreDisabled) {
+  SetAccountStoreEnabled(/*is_enabled=*/false);
   fetcher()->NotifyFetchCompleted();
 
   EXPECT_CALL(*mock_profile_form_saver(), Save(_, _, _));
@@ -420,7 +371,7 @@
 
 TEST_F(MultiStorePasswordSaveManagerTest,
        SaveInAccountStoreWhenPSLMatchExistsInTheAccountStore) {
-  SetAccountStoreActive(/*is_active=*/true);
+  SetAccountStoreEnabled(/*is_enabled=*/true);
   PasswordForm psl_saved_match(psl_saved_match_);
   psl_saved_match.in_store = PasswordForm::Store::kAccountStore;
   SetNonFederatedAndNotifyFetchCompleted({&psl_saved_match});
@@ -438,7 +389,7 @@
 
 TEST_F(MultiStorePasswordSaveManagerTest,
        SaveInProfileStoreWhenPSLMatchExistsInTheProfileStore) {
-  SetAccountStoreActive(/*is_active=*/true);
+  SetAccountStoreEnabled(/*is_enabled=*/true);
   PasswordForm psl_saved_match(psl_saved_match_);
   psl_saved_match.in_store = PasswordForm::Store::kProfileStore;
   SetNonFederatedAndNotifyFetchCompleted({&psl_saved_match});
@@ -455,7 +406,7 @@
 }
 
 TEST_F(MultiStorePasswordSaveManagerTest, UnblacklistInBothStores) {
-  SetAccountStoreActive(/*is_active=*/true);
+  SetAccountStoreEnabled(/*is_enabled=*/true);
   const PasswordStore::FormDigest form_digest(saved_match_);
 
   EXPECT_CALL(*mock_profile_form_saver(), Unblacklist(form_digest));
@@ -465,8 +416,8 @@
 }
 
 TEST_F(MultiStorePasswordSaveManagerTest,
-       BlacklistInAccountStoreWhenAccountStoreActive) {
-  SetAccountStoreActive(/*is_active=*/true);
+       BlacklistInAccountStoreWhenAccountStoreEnabled) {
+  SetAccountStoreEnabled(/*is_enabled=*/true);
   const PasswordStore::FormDigest form_digest(saved_match_);
   SetDefaultPasswordStore(PasswordForm::Store::kAccountStore);
 
@@ -477,8 +428,8 @@
 }
 
 TEST_F(MultiStorePasswordSaveManagerTest,
-       BlacklistInProfileStoreAlthoughAccountStoreActive) {
-  SetAccountStoreActive(/*is_active=*/true);
+       BlacklistInProfileStoreAlthoughAccountStoreEnabled) {
+  SetAccountStoreEnabled(/*is_enabled=*/true);
   const PasswordStore::FormDigest form_digest(saved_match_);
   SetDefaultPasswordStore(PasswordForm::Store::kProfileStore);
 
@@ -489,8 +440,8 @@
 }
 
 TEST_F(MultiStorePasswordSaveManagerTest,
-       BlacklistInProfileStoreWhenAccountStoreInactive) {
-  SetAccountStoreActive(/*is_active=*/false);
+       BlacklistInProfileStoreWhenAccountStoreDisabled) {
+  SetAccountStoreEnabled(/*is_enabled=*/false);
   const PasswordStore::FormDigest form_digest(saved_match_);
   SetDefaultPasswordStore(PasswordForm::Store::kAccountStore);
 
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index f0a621a..2182a834 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -380,6 +380,8 @@
     ON_CALL(mock_autofill_download_manager_,
             StartUploadRequest(_, _, _, _, _, _))
         .WillByDefault(Return(true));
+    ON_CALL(*client_.GetPasswordFeatureManager(), GetDefaultPasswordStore)
+        .WillByDefault(Return(PasswordForm::Store::kProfileStore));
 
     fetcher_.reset(new FakeFormFetcher());
     fetcher_->Fetch();
diff --git a/components/password_manager/core/browser/password_save_manager_impl_unittest.cc b/components/password_manager/core/browser/password_save_manager_impl_unittest.cc
index 4ecabb0e..6140e80 100644
--- a/components/password_manager/core/browser/password_save_manager_impl_unittest.cc
+++ b/components/password_manager/core/browser/password_save_manager_impl_unittest.cc
@@ -317,6 +317,8 @@
     ON_CALL(mock_autofill_download_manager_,
             StartUploadRequest(_, _, _, _, _, _))
         .WillByDefault(Return(true));
+    ON_CALL(*client_.GetPasswordFeatureManager(), GetDefaultPasswordStore)
+        .WillByDefault(Return(PasswordForm::Store::kProfileStore));
   }
 
   PasswordForm Parse(const FormData& form_data) {
diff --git a/components/permissions/BUILD.gn b/components/permissions/BUILD.gn
index 4067cac..590b4c39 100644
--- a/components/permissions/BUILD.gn
+++ b/components/permissions/BUILD.gn
@@ -4,6 +4,8 @@
 
 source_set("permissions") {
   sources = [
+    "chooser_context_base.cc",
+    "chooser_context_base.h",
     "features.cc",
     "features.h",
     "notification_permission_ui_selector.h",
diff --git a/chrome/browser/permissions/chooser_context_base.cc b/components/permissions/chooser_context_base.cc
similarity index 95%
rename from chrome/browser/permissions/chooser_context_base.cc
rename to components/permissions/chooser_context_base.cc
index 7355eee9..68831a6 100644
--- a/chrome/browser/permissions/chooser_context_base.cc
+++ b/components/permissions/chooser_context_base.cc
@@ -2,26 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/permissions/chooser_context_base.h"
+#include "components/permissions/chooser_context_base.h"
 
 #include <utility>
 
 #include "base/values.h"
-#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/profiles/profile.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "url/origin.h"
 
 const char kObjectListKey[] = "chosen-objects";
 
 ChooserContextBase::ChooserContextBase(
-    Profile* profile,
     const ContentSettingsType guard_content_settings_type,
-    const ContentSettingsType data_content_settings_type)
+    const ContentSettingsType data_content_settings_type,
+    HostContentSettingsMap* host_content_settings_map)
     : guard_content_settings_type_(guard_content_settings_type),
       data_content_settings_type_(data_content_settings_type),
-      host_content_settings_map_(
-          HostContentSettingsMapFactory::GetForProfile(profile)) {
+      host_content_settings_map_(host_content_settings_map) {
   DCHECK(host_content_settings_map_);
 }
 
diff --git a/chrome/browser/permissions/chooser_context_base.h b/components/permissions/chooser_context_base.h
similarity index 94%
rename from chrome/browser/permissions/chooser_context_base.h
rename to components/permissions/chooser_context_base.h
index 1f322b0..ba22419 100644
--- a/chrome/browser/permissions/chooser_context_base.h
+++ b/components/permissions/chooser_context_base.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_PERMISSIONS_CHOOSER_CONTEXT_BASE_H_
-#define CHROME_BROWSER_PERMISSIONS_CHOOSER_CONTEXT_BASE_H_
+#ifndef COMPONENTS_PERMISSIONS_CHOOSER_CONTEXT_BASE_H_
+#define COMPONENTS_PERMISSIONS_CHOOSER_CONTEXT_BASE_H_
 
 #include <memory>
 #include <string>
@@ -19,7 +19,6 @@
 #include "url/gurl.h"
 
 class HostContentSettingsMap;
-class Profile;
 
 namespace url {
 class Origin;
@@ -64,9 +63,9 @@
   void AddObserver(PermissionObserver* observer);
   void RemoveObserver(PermissionObserver* observer);
 
-  ChooserContextBase(Profile* profile,
-                     ContentSettingsType guard_content_settings_type,
-                     ContentSettingsType data_content_settings_type);
+  ChooserContextBase(ContentSettingsType guard_content_settings_type,
+                     ContentSettingsType data_content_settings_type,
+                     HostContentSettingsMap* host_content_settings_map);
   ~ChooserContextBase() override;
 
   // Checks whether |requesting_origin| can request permission to access objects
@@ -143,4 +142,4 @@
   HostContentSettingsMap* const host_content_settings_map_;
 };
 
-#endif  // CHROME_BROWSER_PERMISSIONS_CHOOSER_CONTEXT_BASE_H_
+#endif  // COMPONENTS_PERMISSIONS_CHOOSER_CONTEXT_BASE_H_
diff --git a/components/policy/core/browser/policy_conversions.cc b/components/policy/core/browser/policy_conversions.cc
index f31846d..abc3f77e 100644
--- a/components/policy/core/browser/policy_conversions.cc
+++ b/components/policy/core/browser/policy_conversions.cc
@@ -195,7 +195,7 @@
 #if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
 Value ArrayPolicyConversions::GetUpdaterPolicies() {
   Value chrome_policies_data(Value::Type::DICTIONARY);
-  chrome_policies_data.SetKey("name", Value("Updater Policies"));
+  chrome_policies_data.SetKey("name", Value("Google Update Policies"));
   chrome_policies_data.SetKey("id", Value("updater"));
   chrome_policies_data.SetKey("policies", client()->GetUpdaterPolicies());
   return chrome_policies_data;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 9712543..b1e6c03 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -19833,7 +19833,7 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
-      'example_value': True,
+      'example_value': 0,
       'id': 650,
       'caption': '''Enable Ambient Authentication for profile types.''',
       'tags': [],
@@ -19851,7 +19851,7 @@
 
       Note that, ambient authentication is always allowed on regular profiles.
 
-      If the policy is left not set, then the Incognito and Guest sessions will not be able to ambiently authenticate in the future releases of <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>, as they will be disallowed.'''
+      In <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> version 81 and later, if the policy is left not set, ambient authentication will be enabled in regular sessions only.'''
     },
     {
       'name': 'PaymentMethodQueryEnabled',
diff --git a/components/signin/core/browser/account_reconcilor.cc b/components/signin/core/browser/account_reconcilor.cc
index bf5e60fe..c39d430 100644
--- a/components/signin/core/browser/account_reconcilor.cc
+++ b/components/signin/core/browser/account_reconcilor.cc
@@ -351,14 +351,6 @@
   return base::WrapUnique(new ScopedSyncedDataDeletion(this));
 }
 
-GoogleServiceAuthError AccountReconcilor::GetReconcileError() const {
-  return error_during_last_reconcile_;
-}
-
-bool AccountReconcilor::IsReconcileEnabled() const {
-  return delegate_->IsReconcileEnabled();
-}
-
 void AccountReconcilor::AddObserver(Observer* observer) {
   observer_list_.AddObserver(observer);
 }
diff --git a/components/signin/core/browser/account_reconcilor.h b/components/signin/core/browser/account_reconcilor.h
index a190eee..5bf5fdfc 100644
--- a/components/signin/core/browser/account_reconcilor.h
+++ b/components/signin/core/browser/account_reconcilor.h
@@ -123,16 +123,6 @@
   // from being invalidated during the deletion.
   std::unique_ptr<ScopedSyncedDataDeletion> GetScopedSyncDataDeletion();
 
-  // Returns the 'Most severe' error encountered during the last attempt to
-  // reconcile (after the state is already set to ACCOUNT_RECONCILOR_OK or
-  // ACCOUNT_RECONCILOR_ERROR).
-  // If the last reconciliation attempt was successful, this will be
-  // |GoogleServiceAuthError::State::NONE|.
-  GoogleServiceAuthError GetReconcileError() const;
-
-  // Returns true if the reconcilor is enabled.
-  bool IsReconcileEnabled() const;
-
  private:
   friend class AccountReconcilorTest;
   friend class DiceBrowserTest;
diff --git a/components/sync/driver/file_based_trusted_vault_client.cc b/components/sync/driver/file_based_trusted_vault_client.cc
index f7f0210..c5b1b33 100644
--- a/components/sync/driver/file_based_trusted_vault_client.cc
+++ b/components/sync/driver/file_based_trusted_vault_client.cc
@@ -98,6 +98,11 @@
     WriteToDisk(data_, file_path_);
   }
 
+  void RemoveAllStoredKeys() {
+    base::DeleteFile(file_path_, /*recursive=*/false);
+    data_.Clear();
+  }
+
  private:
   friend class base::RefCountedThreadSafe<Backend>;
 
@@ -156,6 +161,13 @@
   observer_list_.Notify();
 }
 
+void FileBasedTrustedVaultClient::RemoveAllStoredKeys() {
+  TriggerLazyInitializationIfNeeded();
+  backend_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&Backend::RemoveAllStoredKeys, backend_));
+  observer_list_.Notify();
+}
+
 void FileBasedTrustedVaultClient::MarkKeysAsStale(
     const CoreAccountInfo& account_info,
     base::OnceCallback<void(bool)> cb) {
diff --git a/components/sync/driver/file_based_trusted_vault_client.h b/components/sync/driver/file_based_trusted_vault_client.h
index edab496..6550494d 100644
--- a/components/sync/driver/file_based_trusted_vault_client.h
+++ b/components/sync/driver/file_based_trusted_vault_client.h
@@ -38,6 +38,7 @@
   void StoreKeys(const std::string& gaia_id,
                  const std::vector<std::vector<uint8_t>>& keys,
                  int last_key_version) override;
+  void RemoveAllStoredKeys() override;
   void MarkKeysAsStale(const CoreAccountInfo& account_info,
                        base::OnceCallback<void(bool)> cb) override;
 
diff --git a/components/sync/driver/file_based_trusted_vault_client_unittest.cc b/components/sync/driver/file_based_trusted_vault_client_unittest.cc
index cfa297c..5721bb1 100644
--- a/components/sync/driver/file_based_trusted_vault_client_unittest.cc
+++ b/components/sync/driver/file_based_trusted_vault_client_unittest.cc
@@ -182,6 +182,29 @@
               ElementsAre(kKey2, kKey3));
 }
 
+TEST_F(FileBasedTrustedVaultClientTest, ShouldRemoveAllStoredKeys) {
+  const std::string kGaiaId1 = "user1";
+  const std::string kGaiaId2 = "user2";
+  const std::vector<uint8_t> kKey1 = {0, 1, 2, 3, 4};
+  const std::vector<uint8_t> kKey2 = {1, 2, 3, 4};
+  const std::vector<uint8_t> kKey3 = {2, 3, 4};
+
+  client_.StoreKeys(kGaiaId1, {kKey1}, /*last_key_version=*/0);
+  client_.StoreKeys(kGaiaId2, {kKey2, kKey3}, /*last_key_version=*/1);
+
+  // Wait until the last write completes.
+  WaitForFlush();
+  client_.RemoveAllStoredKeys();
+
+  // Wait until the last write completes.
+  WaitForFlush();
+
+  // Keys should be removed from both in-memory and disk storages.
+  EXPECT_THAT(FetchKeysAndWait(kGaiaId1), IsEmpty());
+  EXPECT_THAT(FetchKeysAndWait(kGaiaId2), IsEmpty());
+  EXPECT_FALSE(base::PathExists(file_path_));
+}
+
 }  // namespace
 
 }  // namespace syncer
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index fbeafe7..ad41ba7 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -1652,6 +1652,10 @@
       accounts_in_cookie_jar_info.signed_in_accounts, base::NullCallback());
 }
 
+void ProfileSyncService::OnAccountsCookieDeletedByUserAction() {
+  sync_client_->GetTrustedVaultClient()->RemoveAllStoredKeys();
+}
+
 void ProfileSyncService::OnAccountsInCookieUpdatedWithCallback(
     const std::vector<gaia::ListedAccount>& signed_in_accounts,
     base::OnceClosure callback) {
diff --git a/components/sync/driver/profile_sync_service.h b/components/sync/driver/profile_sync_service.h
index db80d76..7fa5db1 100644
--- a/components/sync/driver/profile_sync_service.h
+++ b/components/sync/driver/profile_sync_service.h
@@ -197,6 +197,7 @@
   void OnAccountsInCookieUpdated(
       const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
       const GoogleServiceAuthError& error) override;
+  void OnAccountsCookieDeletedByUserAction() override;
 
   // Similar to above but with a callback that will be invoked on completion.
   void OnAccountsInCookieUpdatedWithCallback(
diff --git a/components/sync/driver/sync_service_crypto.cc b/components/sync/driver/sync_service_crypto.cc
index 0665630..7928b31 100644
--- a/components/sync/driver/sync_service_crypto.cc
+++ b/components/sync/driver/sync_service_crypto.cc
@@ -50,6 +50,11 @@
     NOTREACHED();
   }
 
+  void RemoveAllStoredKeys() override {
+    // Never invoked by SyncServiceCrypto.
+    NOTREACHED();
+  }
+
   void MarkKeysAsStale(const CoreAccountInfo& account_info,
                        base::OnceCallback<void(bool)> cb) override {
     std::move(cb).Run(false);
diff --git a/components/sync/driver/sync_service_crypto_unittest.cc b/components/sync/driver/sync_service_crypto_unittest.cc
index 18b0ccd5..bd09de0 100644
--- a/components/sync/driver/sync_service_crypto_unittest.cc
+++ b/components/sync/driver/sync_service_crypto_unittest.cc
@@ -193,6 +193,11 @@
     observer_list_.Notify();
   }
 
+  void RemoveAllStoredKeys() override {
+    gaia_id_to_cached_keys_.clear();
+    observer_list_.Notify();
+  }
+
   void MarkKeysAsStale(const CoreAccountInfo& account_info,
                        base::OnceCallback<void(bool)> cb) override {
     const std::string& gaia_id = account_info.gaia;
diff --git a/components/sync/driver/trusted_vault_client.h b/components/sync/driver/trusted_vault_client.h
index bc10282..28b2178 100644
--- a/components/sync/driver/trusted_vault_client.h
+++ b/components/sync/driver/trusted_vault_client.h
@@ -61,6 +61,11 @@
                          const std::vector<std::vector<uint8_t>>& keys,
                          int last_key_version) = 0;
 
+  // Allows implementation to remove all previously stored keys.
+  // Implementations must erase all keys saved during StoreKeys() call. Used
+  // when accounts cookies deleted by the user action.
+  virtual void RemoveAllStoredKeys() = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(TrustedVaultClient);
 };
diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
index 231a1e46..9835513 100644
--- a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
+++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
@@ -43,7 +43,14 @@
   Image(gpu::SharedImageFactory* factory,
         gpu::SharedImageRepresentationFactory* representation_factory)
       : factory_(factory), representation_factory_(representation_factory) {}
-  ~Image() = default;
+  ~Image() {
+    // TODO(vasilyt): As we are going to delete image anyway we should be able
+    // to abort write to avoid unnecessary flush to submit semaphores.
+    if (scoped_skia_write_access_) {
+      EndWriteSkia();
+    }
+    DCHECK(!scoped_skia_write_access_);
+  }
 
   bool Initialize(const gfx::Size& size,
                   const gfx::ColorSpace& color_space,
diff --git a/components/viz/service/display_embedder/skia_output_device_dawn.cc b/components/viz/service/display_embedder/skia_output_device_dawn.cc
index b5b1513..8b41500 100644
--- a/components/viz/service/display_embedder/skia_output_device_dawn.cc
+++ b/components/viz/service/display_embedder/skia_output_device_dawn.cc
@@ -87,7 +87,7 @@
   DCHECK(backend_target.isValid());
   sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
       context_provider_->GetGrContext(), backend_target,
-      capabilities_.output_surface_origin == SurfaceOrigin::kTopLeft
+      capabilities_.output_surface_origin == gfx::SurfaceOrigin::kTopLeft
           ? kTopLeft_GrSurfaceOrigin
           : kBottomLeft_GrSurfaceOrigin,
       kSurfaceColorType, sk_color_space_, /*surfaceProps=*/nullptr);
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 55f2a0d..5f77dab 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -288,6 +288,11 @@
   RunCSSTest(FILE_PATH_LITERAL("table-display-other.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityCSSDataTableDisplayOther) {
+  RunCSSTest(FILE_PATH_LITERAL("table-data-display-other.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityCSSTransform) {
   RunCSSTest(FILE_PATH_LITERAL("transform.html"));
 }
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc
index 85796696..b9941e3 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -288,10 +288,12 @@
 
 bool WebBluetoothServiceImpl::IsDevicePaired(
     const std::string& device_address) {
-  BluetoothDelegate* delegate =
-      GetContentClient()->browser()->GetBluetoothDelegate();
-  if (delegate && base::FeatureList::IsEnabled(
-                      features::kWebBluetoothNewPermissionsBackend)) {
+  if (base::FeatureList::IsEnabled(
+          features::kWebBluetoothNewPermissionsBackend)) {
+    BluetoothDelegate* delegate =
+        GetContentClient()->browser()->GetBluetoothDelegate();
+    if (!delegate)
+      return false;
     return delegate->GetWebBluetoothDeviceId(render_frame_host_, device_address)
         .IsValid();
   }
@@ -536,10 +538,12 @@
   auto client = scanning_clients_.begin();
   while (client != scanning_clients_.end()) {
     auto device = blink::mojom::WebBluetoothDevice::New();
-    BluetoothDelegate* delegate =
-        GetContentClient()->browser()->GetBluetoothDelegate();
-    if (delegate && base::FeatureList::IsEnabled(
-                        features::kWebBluetoothNewPermissionsBackend)) {
+    if (base::FeatureList::IsEnabled(
+            features::kWebBluetoothNewPermissionsBackend)) {
+      BluetoothDelegate* delegate =
+          GetContentClient()->browser()->GetBluetoothDelegate();
+      if (!delegate)
+        return;
       device->id =
           delegate->AddScannedDevice(render_frame_host_, device_address);
     } else {
@@ -707,13 +711,15 @@
     RemoteServerConnectCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  bool is_connect_allowed;
-  BluetoothDelegate* delegate =
-      GetContentClient()->browser()->GetBluetoothDelegate();
-  if (delegate && base::FeatureList::IsEnabled(
-                      features::kWebBluetoothNewPermissionsBackend)) {
-    is_connect_allowed =
-        delegate->HasDevicePermission(render_frame_host_, device_id);
+  bool is_connect_allowed = false;
+  if (base::FeatureList::IsEnabled(
+          features::kWebBluetoothNewPermissionsBackend)) {
+    BluetoothDelegate* delegate =
+        GetContentClient()->browser()->GetBluetoothDelegate();
+    if (delegate) {
+      is_connect_allowed =
+          delegate->HasDevicePermission(render_frame_host_, device_id);
+    }
   } else {
     is_connect_allowed = allowed_devices().IsAllowedToGATTConnect(device_id);
   }
@@ -1502,10 +1508,16 @@
   DVLOG(1) << "Device: " << device->GetNameForDisplay();
 
   auto web_bluetooth_device = blink::mojom::WebBluetoothDevice::New();
-  BluetoothDelegate* delegate =
-      GetContentClient()->browser()->GetBluetoothDelegate();
-  if (delegate && base::FeatureList::IsEnabled(
-                      features::kWebBluetoothNewPermissionsBackend)) {
+  if (base::FeatureList::IsEnabled(
+          features::kWebBluetoothNewPermissionsBackend)) {
+    BluetoothDelegate* delegate =
+        GetContentClient()->browser()->GetBluetoothDelegate();
+    if (!delegate) {
+      std::move(callback).Run(
+          blink::mojom::WebBluetoothResult::WEB_BLUETOOTH_NOT_SUPPORTED,
+          /*device=*/nullptr);
+      return;
+    }
     web_bluetooth_device->id = delegate->GrantServiceAccessPermission(
         render_frame_host_, device, options.get());
   } else {
@@ -1660,12 +1672,15 @@
 
 CacheQueryResult WebBluetoothServiceImpl::QueryCacheForDevice(
     const blink::WebBluetoothDeviceId& device_id) {
-  std::string device_address;
-  BluetoothDelegate* delegate =
-      GetContentClient()->browser()->GetBluetoothDelegate();
-  if (delegate && base::FeatureList::IsEnabled(
-                      features::kWebBluetoothNewPermissionsBackend)) {
-    device_address = delegate->GetDeviceAddress(render_frame_host_, device_id);
+  std::string device_address = "";
+  if (base::FeatureList::IsEnabled(
+          features::kWebBluetoothNewPermissionsBackend)) {
+    BluetoothDelegate* delegate =
+        GetContentClient()->browser()->GetBluetoothDelegate();
+    if (delegate) {
+      device_address =
+          delegate->GetDeviceAddress(render_frame_host_, device_id);
+    }
   } else {
     device_address = allowed_devices().GetDeviceAddress(device_id);
   }
@@ -1698,12 +1713,14 @@
   }
 
   blink::WebBluetoothDeviceId device_id;
-  BluetoothDelegate* delegate =
-      GetContentClient()->browser()->GetBluetoothDelegate();
-  if (delegate && base::FeatureList::IsEnabled(
-                      features::kWebBluetoothNewPermissionsBackend)) {
-    device_id = delegate->GetWebBluetoothDeviceId(render_frame_host_,
-                                                  device_iter->second);
+  if (base::FeatureList::IsEnabled(
+          features::kWebBluetoothNewPermissionsBackend)) {
+    BluetoothDelegate* delegate =
+        GetContentClient()->browser()->GetBluetoothDelegate();
+    if (delegate) {
+      device_id = delegate->GetWebBluetoothDeviceId(render_frame_host_,
+                                                    device_iter->second);
+    }
   } else {
     const blink::WebBluetoothDeviceId* device_id_ptr =
         allowed_devices().GetDeviceId(device_iter->second);
@@ -1900,10 +1917,12 @@
 
 bool WebBluetoothServiceImpl::IsAllowedToAccessAtLeastOneService(
     const blink::WebBluetoothDeviceId& device_id) {
-  BluetoothDelegate* delegate =
-      GetContentClient()->browser()->GetBluetoothDelegate();
-  if (delegate && base::FeatureList::IsEnabled(
-                      features::kWebBluetoothNewPermissionsBackend)) {
+  if (base::FeatureList::IsEnabled(
+          features::kWebBluetoothNewPermissionsBackend)) {
+    BluetoothDelegate* delegate =
+        GetContentClient()->browser()->GetBluetoothDelegate();
+    if (!delegate)
+      return false;
     return delegate->IsAllowedToAccessAtLeastOneService(render_frame_host_,
                                                         device_id);
   } else {
@@ -1914,10 +1933,12 @@
 bool WebBluetoothServiceImpl::IsAllowedToAccessService(
     const blink::WebBluetoothDeviceId& device_id,
     const device::BluetoothUUID& service) {
-  BluetoothDelegate* delegate =
-      GetContentClient()->browser()->GetBluetoothDelegate();
-  if (delegate && base::FeatureList::IsEnabled(
-                      features::kWebBluetoothNewPermissionsBackend)) {
+  if (base::FeatureList::IsEnabled(
+          features::kWebBluetoothNewPermissionsBackend)) {
+    BluetoothDelegate* delegate =
+        GetContentClient()->browser()->GetBluetoothDelegate();
+    if (!delegate)
+      return false;
     return delegate->IsAllowedToAccessService(render_frame_host_, device_id,
                                               service);
   } else {
diff --git a/content/browser/frame_host/sec_fetch_browsertest.cc b/content/browser/frame_host/sec_fetch_browsertest.cc
index b966f10..11a96411 100644
--- a/content/browser/frame_host/sec_fetch_browsertest.cc
+++ b/content/browser/frame_host/sec_fetch_browsertest.cc
@@ -31,12 +31,7 @@
 class SecFetchBrowserTest : public ContentBrowserTest {
  public:
   SecFetchBrowserTest()
-      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
-    feature_list_.InitWithFeatures(
-        {network::features::kFetchMetadata,
-         network::features::kFetchMetadataDestination},
-        {});
-  }
+      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
 
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
diff --git a/content/browser/loader/browser_initiated_resource_request.cc b/content/browser/loader/browser_initiated_resource_request.cc
index e88d4aa..d9cbc45 100644
--- a/content/browser/loader/browser_initiated_resource_request.cc
+++ b/content/browser/loader/browser_initiated_resource_request.cc
@@ -22,19 +22,6 @@
 
 namespace content {
 
-bool IsFetchMetadataEnabled() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-             switches::kEnableExperimentalWebPlatformFeatures) ||
-         base::FeatureList::IsEnabled(network::features::kFetchMetadata);
-}
-
-bool IsFetchMetadataDestinationEnabled() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-             switches::kEnableExperimentalWebPlatformFeatures) ||
-         base::FeatureList::IsEnabled(
-             network::features::kFetchMetadataDestination);
-}
-
 void UpdateAdditionalHeadersForBrowserInitiatedRequest(
     net::HttpRequestHeaders* headers,
     BrowserContext* browser_context,
diff --git a/content/browser/loader/browser_initiated_resource_request.h b/content/browser/loader/browser_initiated_resource_request.h
index 89df8474..f18f548e 100644
--- a/content/browser/loader/browser_initiated_resource_request.h
+++ b/content/browser/loader/browser_initiated_resource_request.h
@@ -19,14 +19,6 @@
 
 class BrowserContext;
 
-// Returns true if either of FetchMetadata or experimental-web-platform-features
-// is enabled.
-bool IsFetchMetadataEnabled();
-
-// Returns true if either of FetchMetadataDestination or
-// experimental-web-platform-features is enabled.
-bool IsFetchMetadataDestinationEnabled();
-
 // Sets request headers appropriate for browser-initiated resource requests,
 // i.e., requests for navigations and dedicated/shared/service worker
 // scripts.
diff --git a/content/browser/plugin_private_storage_helper.cc b/content/browser/plugin_private_storage_helper.cc
index 015731fb8..5210bea 100644
--- a/content/browser/plugin_private_storage_helper.cc
+++ b/content/browser/plugin_private_storage_helper.cc
@@ -267,9 +267,9 @@
         begin_(begin),
         end_(end),
         callback_(std::move(callback)) {}
-  ~PluginPrivateDataDeletionHelper() {}
+  ~PluginPrivateDataDeletionHelper() = default;
 
-  void CheckOriginsOnFileTaskRunner(const std::set<GURL>& origins);
+  void CheckOriginsOnFileTaskRunner(const std::set<url::Origin>& origins);
 
  private:
   // Keeps track of the pending work. When |task_count_| goes to 0 then
@@ -287,7 +287,7 @@
 };
 
 void PluginPrivateDataDeletionHelper::CheckOriginsOnFileTaskRunner(
-    const std::set<GURL>& origins) {
+    const std::set<url::Origin>& origins) {
   DCHECK(filesystem_context_->default_file_task_runner()
              ->RunsTasksInCurrentSequence());
   IncrementTaskCount();
@@ -306,7 +306,7 @@
     // for this origin.
     base::File::Error error;
     base::FilePath path = obfuscated_file_util->GetDirectoryForOriginAndType(
-        url::Origin::Create(origin), "", false, &error);
+        origin, "", false, &error);
     if (error != base::File::FILE_OK) {
       DLOG(ERROR) << "Unable to read directory for " << origin;
       continue;
@@ -326,7 +326,7 @@
       IncrementTaskCount();
       PluginPrivateDataByOriginChecker* helper =
           new PluginPrivateDataByOriginChecker(
-              filesystem_context_.get(), origin.GetOrigin(),
+              filesystem_context_.get(), origin.GetURL().GetOrigin(),
               plugin_path.BaseName().MaybeAsASCII(), begin_, end_,
               decrement_callback);
       base::PostTask(
@@ -389,7 +389,7 @@
 
 void ClearPluginPrivateDataOnFileTaskRunner(
     scoped_refptr<storage::FileSystemContext> filesystem_context,
-    const GURL& storage_origin,
+    const GURL& storage_origin_url,
     StoragePartition::OriginMatcherFunction origin_matcher,
     const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
     const base::Time begin,
@@ -397,7 +397,7 @@
     base::OnceClosure callback) {
   DCHECK(filesystem_context->default_file_task_runner()
              ->RunsTasksInCurrentSequence());
-  DVLOG(3) << "Clearing plugin data for origin: " << storage_origin;
+  DVLOG(3) << "Clearing plugin data for origin: " << storage_origin_url;
 
   storage::FileSystemBackend* backend =
       filesystem_context->GetFileSystemBackend(
@@ -405,7 +405,7 @@
   storage::FileSystemQuotaUtil* quota_util = backend->GetQuotaUtil();
 
   // Determine the set of origins used.
-  std::set<GURL> origins;
+  std::set<url::Origin> origins;
   quota_util->GetOriginsForTypeOnFileTaskRunner(
       storage::kFileSystemTypePluginPrivate, &origins);
 
@@ -417,9 +417,10 @@
 
   // If a specific origin is provided, then check that it is in the list
   // returned and remove all the other origins.
-  if (!storage_origin.is_empty()) {
-    DCHECK(!origin_matcher) << "Only 1 of |storage_origin| and "
+  if (!storage_origin_url.is_empty()) {
+    DCHECK(!origin_matcher) << "Only 1 of |storage_origin_url| and "
                                "|origin_matcher| should be specified.";
+    url::Origin storage_origin = url::Origin::Create(storage_origin_url);
     if (!base::Contains(origins, storage_origin)) {
       // Nothing matches, so nothing to do.
       std::move(callback).Run();
@@ -433,14 +434,13 @@
 
   // If a filter is provided, determine which origins match.
   if (origin_matcher) {
-    DCHECK(storage_origin.is_empty())
-        << "Only 1 of |storage_origin| and |origin_matcher| should be "
+    DCHECK(storage_origin_url.is_empty())
+        << "Only 1 of |storage_origin_url| and |origin_matcher| should be "
            "specified.";
-    std::set<GURL> origins_to_check;
+    std::set<url::Origin> origins_to_check;
     origins_to_check.swap(origins);
     for (const auto& origin : origins_to_check) {
-      if (origin_matcher.Run(url::Origin::Create(origin),
-                             special_storage_policy.get()))
+      if (origin_matcher.Run(origin, special_storage_policy.get()))
         origins.insert(origin);
     }
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 0fcd5010..9a4917a 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3238,7 +3238,6 @@
     switches::kEnableWebGLDraftExtensions,
     switches::kEnableWebGLImageChromium,
     switches::kFileUrlPathAlias,
-    switches::kForceDarkMode,
     switches::kForceDeviceScaleFactor,
     switches::kForceDisableWebRtcApmInAudioService,
     switches::kForceDisplayColorProfile,
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index 0baf858..eb751d2 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -307,6 +307,9 @@
 }
 
 gfx::NativeView RenderWidgetHostViewChildFrame::GetNativeView() {
+  if (!frame_connector_)
+    return nullptr;
+
   RenderWidgetHostView* parent_view =
       frame_connector_->GetParentRenderWidgetHostView();
   return parent_view ? parent_view->GetNativeView() : nullptr;
diff --git a/content/browser/scheduler/OWNERS b/content/browser/scheduler/OWNERS
index 23e7c0a8..17d283a 100644
--- a/content/browser/scheduler/OWNERS
+++ b/content/browser/scheduler/OWNERS
@@ -2,3 +2,6 @@
 carlscab@chromium.org
 eseckler@chromium.org
 skyostil@chromium.org
+
+# TEAM: scheduler-dev@chromium.org
+# COMPONENT: Internals>TaskScheduling
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_unittest.cc
index 4b23c04e..9677816 100644
--- a/content/browser/storage_partition_impl_unittest.cc
+++ b/content/browser/storage_partition_impl_unittest.cc
@@ -560,10 +560,10 @@
     storage::FileSystemQuotaUtil* quota_util = backend->GetQuotaUtil();
 
     // Determine the set of origins used.
-    std::set<GURL> origins;
+    std::set<url::Origin> origins;
     quota_util->GetOriginsForTypeOnFileTaskRunner(
         storage::kFileSystemTypePluginPrivate, &origins);
-    *data_exists_for_origin = origins.find(origin.GetURL()) != origins.end();
+    *data_exists_for_origin = origins.find(origin) != origins.end();
 
     // AwaitCompletionHelper and MessageLoop don't work on a
     // SequencedTaskRunner, so post a task on the IO thread.
diff --git a/content/browser/theme_helper.cc b/content/browser/theme_helper.cc
index 817f085..adc7df0 100644
--- a/content/browser/theme_helper.cc
+++ b/content/browser/theme_helper.cc
@@ -27,7 +27,6 @@
       mojom::UpdateSystemColorInfoParams::New();
   params->is_dark_mode = native_theme->ShouldUseDarkColors();
   params->is_high_contrast = native_theme->UsesHighContrastColors();
-  params->preferred_color_scheme = native_theme->GetPreferredColorScheme();
   const auto& colors = native_theme->GetSystemColors();
   params->colors.insert(colors.begin(), colors.end());
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index e6abd33d..749dca3 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -592,6 +592,7 @@
       is_overlay_content_(false),
       showing_context_menu_(false),
       text_autosizer_page_info_({0, 0, 1.f}),
+      native_theme_observer_(this),
       had_inner_webcontents_(false) {
   frame_tree_.SetFrameRemoveListener(base::BindRepeating(
       &WebContentsImpl::OnFrameRemoved, base::Unretained(this)));
@@ -603,6 +604,11 @@
   display_cutout_host_impl_ = std::make_unique<DisplayCutoutHostImpl>(this);
 #endif
 
+  ui::NativeTheme* native_theme = ui::NativeTheme::GetInstanceForWeb();
+  native_theme_observer_.Add(native_theme);
+  using_dark_colors_ = native_theme->ShouldUseDarkColors();
+  preferred_color_scheme_ = native_theme->GetPreferredColorScheme();
+
   // ConversionHost takes a weak ref on |this|, so it must be created outside of
   // the initializer list.
   conversion_host_ = std::make_unique<ConversionHost>(this);
@@ -7484,6 +7490,27 @@
   GetMainFrame()->SetVisibilityForChildViews(visible);
 }
 
+void WebContentsImpl::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) {
+  DCHECK(native_theme_observer_.IsObserving(observed_theme));
+
+  bool using_dark_colors = observed_theme->ShouldUseDarkColors();
+  ui::NativeTheme::PreferredColorScheme preferred_color_scheme =
+      observed_theme->GetPreferredColorScheme();
+  bool preferences_changed = false;
+
+  if (using_dark_colors_ != using_dark_colors) {
+    using_dark_colors_ = using_dark_colors;
+    preferences_changed = true;
+  }
+  if (preferred_color_scheme_ != preferred_color_scheme) {
+    preferred_color_scheme_ = preferred_color_scheme;
+    preferences_changed = true;
+  }
+
+  if (preferences_changed)
+    NotifyPreferencesChanged();
+}
+
 mojom::FrameInputHandler* WebContentsImpl::GetFocusedFrameInputHandler() {
   auto* focused_frame = GetFocusedFrame();
   if (!focused_frame)
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index d2a7473..47e0887d 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -22,6 +22,7 @@
 #include "base/observer_list.h"
 #include "base/optional.h"
 #include "base/process/process.h"
+#include "base/scoped_observer.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "build/build_config.h"
@@ -75,6 +76,7 @@
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/native_theme/native_theme.h"
+#include "ui/native_theme/native_theme_observer.h"
 
 #if defined(OS_ANDROID)
 #include "content/public/browser/android/child_process_importance.h"
@@ -155,7 +157,8 @@
                                        public blink::mojom::ColorChooserFactory,
                                        public NotificationObserver,
                                        public NavigationControllerDelegate,
-                                       public NavigatorDelegate {
+                                       public NavigatorDelegate,
+                                       public ui::NativeThemeObserver {
  public:
   class FriendWrapper;
 
@@ -1583,6 +1586,9 @@
   // |current_fullscreen_frame_| and notify observers whenever it changes.
   void FullscreenFrameSetUpdated();
 
+  // ui::NativeThemeObserver:
+  void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
+
   // Sets the visibility to |new_visibility| and propagates this to the
   // renderer side, taking into account the current capture state. This
   // can be called with the current visibility to effect capturing
@@ -1973,6 +1979,15 @@
   // with OOPIF renderers.
   blink::WebTextAutosizerPageInfo text_autosizer_page_info_;
 
+  // Observe native theme for changes to dark mode, and preferred color scheme.
+  // Used to notify the renderer of preferred color scheme changes.
+  ScopedObserver<ui::NativeTheme, ui::NativeThemeObserver>
+      native_theme_observer_;
+
+  bool using_dark_colors_ = false;
+  ui::NativeTheme::PreferredColorScheme preferred_color_scheme_ =
+      ui::NativeTheme::PreferredColorScheme::kNoPreference;
+
   // TODO(crbug.com/934637): Remove this field when pdf/any inner web contents
   // user gesture is properly propagated. This is a temporary fix for history
   // intervention to be disabled for pdfs (crbug.com/965434).
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 68dc281c..97e66822 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -181,10 +181,6 @@
           {wf::EnableWebXRHitTest, features::kWebXrHitTest, kUseFeatureState},
           {wf::EnableWebXRIncubations, features::kWebXrIncubations,
            kEnableOnly},
-          {wf::EnableFetchMetadata, network::features::kFetchMetadata,
-           kUseFeatureState},
-          {wf::EnableFetchMetadataDestination,
-           network::features::kFetchMetadataDestination, kUseFeatureState},
           {wf::EnableUserActivationPostMessageTransfer,
            features::kUserActivationPostMessageTransfer, kUseFeatureState},
           {wf::EnableUserActivationSameOriginVisibility,
@@ -546,8 +542,6 @@
   // these features.
   if (enable_experimental_web_platform_features) {
     WebRuntimeFeatures::EnableNetInfoDownlinkMax(true);
-    WebRuntimeFeatures::EnableFetchMetadata(true);
-    WebRuntimeFeatures::EnableFetchMetadataDestination(true);
   }
 
   WebRuntimeFeatures::EnableBackForwardCache(
diff --git a/content/child/webthemeengine_impl_android.cc b/content/child/webthemeengine_impl_android.cc
index fe90e3f..db48cdcd 100644
--- a/content/child/webthemeengine_impl_android.cc
+++ b/content/child/webthemeengine_impl_android.cc
@@ -177,17 +177,4 @@
   ui::NativeTheme::GetInstanceForWeb()->set_high_contrast(
       forced_colors == blink::ForcedColors::kActive);
 }
-
-blink::PreferredColorScheme WebThemeEngineAndroid::PreferredColorScheme()
-    const {
-  ui::NativeTheme::PreferredColorScheme preferred_color_scheme =
-      ui::NativeTheme::GetInstanceForWeb()->GetPreferredColorScheme();
-  return WebPreferredColorScheme(preferred_color_scheme);
-}
-
-void WebThemeEngineAndroid::SetPreferredColorScheme(
-    const blink::PreferredColorScheme preferred_color_scheme) {
-  ui::NativeTheme::GetInstanceForWeb()->set_preferred_color_scheme(
-      NativePreferredColorScheme(preferred_color_scheme));
-}
 }  // namespace content
diff --git a/content/child/webthemeengine_impl_android.h b/content/child/webthemeengine_impl_android.h
index 95e2744..b8c713e 100644
--- a/content/child/webthemeengine_impl_android.h
+++ b/content/child/webthemeengine_impl_android.h
@@ -24,9 +24,6 @@
              blink::WebColorScheme color_scheme) override;
   blink::ForcedColors GetForcedColors() const override;
   void SetForcedColors(const blink::ForcedColors forced_colors) override;
-  blink::PreferredColorScheme PreferredColorScheme() const override;
-  void SetPreferredColorScheme(
-      const blink::PreferredColorScheme preferred_color_scheme) override;
 };
 
 }  // namespace content
diff --git a/content/child/webthemeengine_impl_conversions.cc b/content/child/webthemeengine_impl_conversions.cc
index 47ebce82..ce5773a6 100644
--- a/content/child/webthemeengine_impl_conversions.cc
+++ b/content/child/webthemeengine_impl_conversions.cc
@@ -117,28 +117,5 @@
   }
 }
 
-ui::NativeTheme::PreferredColorScheme NativePreferredColorScheme(
-    blink::PreferredColorScheme preferred_color_scheme) {
-  switch (preferred_color_scheme) {
-    case blink::PreferredColorScheme::kDark:
-      return ui::NativeTheme::PreferredColorScheme::kDark;
-    case blink::PreferredColorScheme::kLight:
-      return ui::NativeTheme::PreferredColorScheme::kLight;
-    case blink::PreferredColorScheme::kNoPreference:
-      return ui::NativeTheme::PreferredColorScheme::kNoPreference;
-  }
-}
-
-blink::PreferredColorScheme WebPreferredColorScheme(
-    ui::NativeTheme::PreferredColorScheme preferred_color_scheme) {
-  switch (preferred_color_scheme) {
-    case ui::NativeTheme::PreferredColorScheme::kDark:
-      return blink::PreferredColorScheme::kDark;
-    case ui::NativeTheme::PreferredColorScheme::kLight:
-      return blink::PreferredColorScheme::kLight;
-    case ui::NativeTheme::PreferredColorScheme::kNoPreference:
-      return blink::PreferredColorScheme::kNoPreference;
-  }
-}
 
 }  // namespace content
diff --git a/content/child/webthemeengine_impl_conversions.h b/content/child/webthemeengine_impl_conversions.h
index 4d7dbba..fdd2e56 100644
--- a/content/child/webthemeengine_impl_conversions.h
+++ b/content/child/webthemeengine_impl_conversions.h
@@ -27,12 +27,6 @@
 CONTENT_EXPORT ui::NativeTheme::SystemThemeColor NativeSystemThemeColor(
     blink::WebThemeEngine::SystemThemeColor theme_color);
 
-CONTENT_EXPORT ui::NativeTheme::PreferredColorScheme NativePreferredColorScheme(
-    blink::PreferredColorScheme preferred_color_scheme);
-
-CONTENT_EXPORT blink::PreferredColorScheme WebPreferredColorScheme(
-    ui::NativeTheme::PreferredColorScheme preferred_color_scheme);
-
 }  // namespace content
 
 #endif  // CONTENT_CHILD_WEBTHEMEENGINE_IMPL_CONVERSIONS_H_
diff --git a/content/child/webthemeengine_impl_default.cc b/content/child/webthemeengine_impl_default.cc
index e3d3ef5..0691820 100644
--- a/content/child/webthemeengine_impl_default.cc
+++ b/content/child/webthemeengine_impl_default.cc
@@ -250,17 +250,4 @@
       forced_colors == blink::ForcedColors::kActive);
 }
 
-blink::PreferredColorScheme WebThemeEngineDefault::PreferredColorScheme()
-    const {
-  ui::NativeTheme::PreferredColorScheme preferred_color_scheme =
-      ui::NativeTheme::GetInstanceForWeb()->GetPreferredColorScheme();
-  return WebPreferredColorScheme(preferred_color_scheme);
-}
-
-void WebThemeEngineDefault::SetPreferredColorScheme(
-    const blink::PreferredColorScheme preferred_color_scheme) {
-  ui::NativeTheme::GetInstanceForWeb()->set_preferred_color_scheme(
-      NativePreferredColorScheme(preferred_color_scheme));
-}
-
 }  // namespace content
diff --git a/content/child/webthemeengine_impl_default.h b/content/child/webthemeengine_impl_default.h
index 1f08f67..755a6d8a 100644
--- a/content/child/webthemeengine_impl_default.h
+++ b/content/child/webthemeengine_impl_default.h
@@ -41,9 +41,6 @@
 #endif
   blink::ForcedColors GetForcedColors() const override;
   void SetForcedColors(const blink::ForcedColors forced_colors) override;
-  blink::PreferredColorScheme PreferredColorScheme() const override;
-  void SetPreferredColorScheme(
-      const blink::PreferredColorScheme preferred_color_scheme) override;
 };
 
 }  // namespace content
diff --git a/content/child/webthemeengine_impl_mac.cc b/content/child/webthemeengine_impl_mac.cc
index 5ac4e20a..dbd8f0b 100644
--- a/content/child/webthemeengine_impl_mac.cc
+++ b/content/child/webthemeengine_impl_mac.cc
@@ -18,16 +18,4 @@
   forced_colors_ = forced_colors;
 }
 
-blink::PreferredColorScheme WebThemeEngineMac::PreferredColorScheme() const {
-  ui::NativeTheme::PreferredColorScheme preferred_color_scheme =
-      ui::NativeTheme::GetInstanceForWeb()->GetPreferredColorScheme();
-  return WebPreferredColorScheme(preferred_color_scheme);
-}
-
-void WebThemeEngineMac::SetPreferredColorScheme(
-    const blink::PreferredColorScheme preferred_color_scheme) {
-  ui::NativeTheme::GetInstanceForWeb()->set_preferred_color_scheme(
-      NativePreferredColorScheme(preferred_color_scheme));
-}
-
 }  // namespace content
diff --git a/content/child/webthemeengine_impl_mac.h b/content/child/webthemeengine_impl_mac.h
index adfd539..81f1d06 100644
--- a/content/child/webthemeengine_impl_mac.h
+++ b/content/child/webthemeengine_impl_mac.h
@@ -15,9 +15,6 @@
 
   blink::ForcedColors GetForcedColors() const override;
   void SetForcedColors(const blink::ForcedColors forced_colors) override;
-  blink::PreferredColorScheme PreferredColorScheme() const override;
-  void SetPreferredColorScheme(
-      const blink::PreferredColorScheme preferred_color_scheme) override;
 
  private:
   blink::ForcedColors forced_colors_ = blink::ForcedColors::kNone;
diff --git a/content/common/native_types.mojom b/content/common/native_types.mojom
index 6d7f52c..e8015a1 100644
--- a/content/common/native_types.mojom
+++ b/content/common/native_types.mojom
@@ -89,7 +89,4 @@
 struct SyntheticPointerAction;
 
 [Native]
-enum PreferredColorScheme;
-
-[Native]
 enum SystemThemeColor;
diff --git a/content/common/native_types.typemap b/content/common/native_types.typemap
index 6f11569..20a1fb2 100644
--- a/content/common/native_types.typemap
+++ b/content/common/native_types.typemap
@@ -78,7 +78,6 @@
   "content.mojom.InputEventAckState=::content::InputEventAckState",
   "content.mojom.NetworkConnectionType=::net::NetworkChangeNotifier::ConnectionType",
   "content.mojom.PointerType=::blink::WebPointerProperties::PointerType",
-  "content.mojom.PreferredColorScheme=::ui::NativeTheme::PreferredColorScheme",
   "content.mojom.ScrollGranularity=::ui::ScrollGranularity",
   "content.mojom.SyntheticPinch=::content::SyntheticPinchGestureParams",
   "content.mojom.SyntheticPointerAction=::content::SyntheticPointerActionListParams",
diff --git a/content/common/renderer.mojom b/content/common/renderer.mojom
index f6a1f8c..87d7a5b 100644
--- a/content/common/renderer.mojom
+++ b/content/common/renderer.mojom
@@ -180,7 +180,6 @@
 struct UpdateSystemColorInfoParams {
   bool is_dark_mode;
   bool is_high_contrast;
-  PreferredColorScheme preferred_color_scheme;
   // uint32 represents a SkColor in the map.
   map<SystemThemeColor, uint32> colors;
 };
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index 72594b5..7aa8ff0 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -78,8 +78,6 @@
 IPC_ENUM_TRAITS_MAX_VALUE(blink::ScrollerStyle, blink::kScrollerStyleOverlay)
 #endif
 
-IPC_ENUM_TRAITS_MAX_VALUE(ui::NativeTheme::PreferredColorScheme,
-                          ui::NativeTheme::PreferredColorScheme::kMaxValue)
 IPC_ENUM_TRAITS_MAX_VALUE(ui::NativeTheme::SystemThemeColor,
                           ui::NativeTheme::SystemThemeColor::kMaxValue)
 
diff --git a/content/public/browser/browser_task_traits.h b/content/public/browser/browser_task_traits.h
index cb0637e..7645fd6 100644
--- a/content/public/browser/browser_task_traits.h
+++ b/content/public/browser/browser_task_traits.h
@@ -23,10 +23,14 @@
 
 // Semantic annotations which tell the scheduler what type of task it's dealing
 // with. This will be used by the scheduler for dynamic prioritization and for
-// attribution in traces, etc...
+// attribution in traces, etc... In general, BrowserTaskType::kDefault is what
+// you want (it's implicit if you don't specify this trait). Only explicitly
+// specify this trait if you carefully isolated a set of tasks that have no
+// ordering requirements with anything else (in doubt, consult with
+// scheduler-dev@chromium.org).
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.content_public.browser
 enum class BrowserTaskType {
-  // A catch all tasks that don't fit the types below.
+  // A catch all for tasks that don't fit the types below.
   kDefault,
 
   // Critical startup tasks.
diff --git a/content/public/common/common_param_traits_macros.h b/content/public/common/common_param_traits_macros.h
index 7a3c0c0c..1df0ec45 100644
--- a/content/public/common/common_param_traits_macros.h
+++ b/content/public/common/common_param_traits_macros.h
@@ -76,6 +76,9 @@
     content::AutoplayPolicy,
     content::AutoplayPolicy::kNoUserGestureRequired,
     content::AutoplayPolicy::kDocumentUserActivationRequired)
+IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::PreferredColorScheme,
+                              blink::PreferredColorScheme::kNoPreference,
+                              blink::PreferredColorScheme::kMaxValue)
 
 IPC_STRUCT_TRAITS_BEGIN(blink::WebRect)
   IPC_STRUCT_TRAITS_MEMBER(x)
@@ -233,6 +236,7 @@
   IPC_STRUCT_TRAITS_MEMBER(media_controls_enabled)
   IPC_STRUCT_TRAITS_MEMBER(do_not_update_selection_on_mutating_selection_range)
   IPC_STRUCT_TRAITS_MEMBER(autoplay_policy)
+  IPC_STRUCT_TRAITS_MEMBER(preferred_color_scheme)
   IPC_STRUCT_TRAITS_MEMBER(low_priority_iframes_threshold)
   IPC_STRUCT_TRAITS_MEMBER(picture_in_picture_enabled)
   IPC_STRUCT_TRAITS_MEMBER(translate_service_available)
diff --git a/content/public/common/web_preferences.cc b/content/public/common/web_preferences.cc
index 60b12f8..8d1b50f 100644
--- a/content/public/common/web_preferences.cc
+++ b/content/public/common/web_preferences.cc
@@ -223,6 +223,7 @@
       media_controls_enabled(true),
       do_not_update_selection_on_mutating_selection_range(false),
       autoplay_policy(AutoplayPolicy::kDocumentUserActivationRequired),
+      preferred_color_scheme(blink::PreferredColorScheme::kNoPreference),
       low_priority_iframes_threshold(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
       picture_in_picture_enabled(true),
       translate_service_available(false),
diff --git a/content/public/common/web_preferences.h b/content/public/common/web_preferences.h
index 062d138..76cb05a1 100644
--- a/content/public/common/web_preferences.h
+++ b/content/public/common/web_preferences.h
@@ -14,6 +14,7 @@
 #include "build/build_config.h"
 #include "content/common/content_export.h"
 #include "net/nqe/effective_connection_type.h"
+#include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/public/mojom/v8_cache_options.mojom.h"
 #include "ui/base/pointer/pointer_device.h"
 #include "url/gurl.h"
@@ -311,6 +312,12 @@
   // Defines the current autoplay policy.
   AutoplayPolicy autoplay_policy;
 
+  // The preferred color scheme for the web content. The scheme is used to
+  // evaluate the prefers-color-scheme media query and resolve UA color scheme
+  // to be used based on the supported-color-schemes META tag and CSS property.
+  blink::PreferredColorScheme preferred_color_scheme =
+      blink::PreferredColorScheme::kNoPreference;
+
   // Network quality threshold below which resources from iframes are assigned
   // either kVeryLow or kVeryLow Blink priority.
   net::EffectiveConnectionType low_priority_iframes_threshold;
diff --git a/content/renderer/input/render_widget_input_handler.cc b/content/renderer/input/render_widget_input_handler.cc
index 3564a38..dd0de3f 100644
--- a/content/renderer/input/render_widget_input_handler.cc
+++ b/content/renderer/input/render_widget_input_handler.cc
@@ -682,8 +682,8 @@
           widget_->layer_tree_host()->GetSwapPromiseManager(), nullptr);
       auto scoped_event_metrics_monitor =
           widget_->layer_tree_host()->GetScopedEventMetricsMonitor(
-              {ui::WebEventTypeToEventType(input_event.GetType()),
-               input_event.TimeStamp()});
+              {ui::WebEventTypeToEventType(gesture_event->GetType()),
+               gesture_event->TimeStamp()});
       widget_->GetWebWidget()->HandleInputEvent(
           blink::WebCoalescedInputEvent(*gesture_event.get()));
     }
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 9aa93fb..8c0fe45 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1992,8 +1992,7 @@
     mojom::UpdateSystemColorInfoParamsPtr params) {
   bool did_system_color_info_change =
       ui::NativeTheme::GetInstanceForWeb()->UpdateSystemColorInfo(
-          params->is_dark_mode, params->is_high_contrast,
-          params->preferred_color_scheme, params->colors);
+          params->is_dark_mode, params->is_high_contrast, params->colors);
   if (did_system_color_info_change) {
     blink::SystemColorsChanged();
     blink::ColorSchemeChanged();
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 2235e7b..8d235493 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -860,6 +860,7 @@
       prefs.data_saver_holdback_web_api_enabled);
 
   settings->SetLazyLoadEnabled(prefs.lazy_load_enabled);
+  settings->SetPreferredColorScheme(prefs.preferred_color_scheme);
 
   for (const auto& ect_distance_pair :
        prefs.lazy_frame_loading_distance_thresholds_px) {
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index c7bfe84..778c599 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -291,9 +291,6 @@
       GetContentClient()->renderer()->OverrideThemeEngine();
   if (!theme_engine)
     theme_engine = BlinkPlatformImpl::ThemeEngine();
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kForceDarkMode))
-    theme_engine->SetPreferredColorScheme(blink::PreferredColorScheme::kDark);
   return theme_engine;
 }
 
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 450b90d4..92c49392 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -158,6 +158,8 @@
     "browser/web_test/fake_bluetooth_chooser.h",
     "browser/web_test/fake_bluetooth_chooser_factory.cc",
     "browser/web_test/fake_bluetooth_chooser_factory.h",
+    "browser/web_test/fake_bluetooth_delegate.cc",
+    "browser/web_test/fake_bluetooth_delegate.h",
     "browser/web_test/fake_bluetooth_scanning_prompt.cc",
     "browser/web_test/fake_bluetooth_scanning_prompt.h",
     "browser/web_test/leak_detector.cc",
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 9ec9470..5a8689e 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -298,6 +298,17 @@
   return new ShellSpeechRecognitionManagerDelegate();
 }
 
+void ShellContentBrowserClient::OverrideWebkitPrefs(
+    RenderViewHost* render_view_host,
+    WebPreferences* prefs) {
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kForceDarkMode)) {
+    prefs->preferred_color_scheme = blink::PreferredColorScheme::kDark;
+  } else {
+    prefs->preferred_color_scheme = blink::PreferredColorScheme::kLight;
+  }
+}
+
 base::FilePath ShellContentBrowserClient::GetFontLookupTableCacheDir() {
   return browser_context()->GetPath().Append(
       FILE_PATH_LITERAL("FontLookupTableCache"));
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index 618eb3b..92c77f2 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -58,6 +58,8 @@
       std::unique_ptr<ClientCertificateDelegate> delegate) override;
   SpeechRecognitionManagerDelegate* CreateSpeechRecognitionManagerDelegate()
       override;
+  void OverrideWebkitPrefs(RenderViewHost* render_view_host,
+                           WebPreferences* prefs) override;
   base::FilePath GetFontLookupTableCacheDir() override;
   DevToolsManagerDelegate* GetDevToolsManagerDelegate() override;
   mojo::Remote<::media::mojom::MediaService> RunSecondaryMediaService()
diff --git a/content/shell/browser/web_test/blink_test_controller.cc b/content/shell/browser/web_test/blink_test_controller.cc
index 546b4cc..c898b1d1 100644
--- a/content/shell/browser/web_test/blink_test_controller.cc
+++ b/content/shell/browser/web_test/blink_test_controller.cc
@@ -10,6 +10,7 @@
 #include <algorithm>
 #include <iostream>
 #include <memory>
+#include <queue>
 #include <set>
 #include <utility>
 #include <vector>
@@ -575,6 +576,7 @@
   WebTestContentBrowserClient::Get()->SetPopupBlockingEnabled(false);
   WebTestContentBrowserClient::Get()->ResetMockClipboardHost();
   WebTestContentBrowserClient::Get()->SetScreenOrientationChanged(false);
+  WebTestContentBrowserClient::Get()->ResetFakeBluetoothDelegate();
   navigation_history_dump_ = "";
   pixel_dump_.reset();
   actual_pixel_hash_ = "";
@@ -614,6 +616,12 @@
   } else {
     ApplyWebTestDefaultPreferences(prefs);
   }
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kForceDarkMode)) {
+    prefs->preferred_color_scheme = blink::PreferredColorScheme::kDark;
+  } else {
+    prefs->preferred_color_scheme = blink::PreferredColorScheme::kLight;
+  }
 }
 
 void BlinkTestController::OpenURL(const GURL& url) {
diff --git a/content/shell/browser/web_test/fake_bluetooth_delegate.cc b/content/shell/browser/web_test/fake_bluetooth_delegate.cc
new file mode 100644
index 0000000..4f02cca
--- /dev/null
+++ b/content/shell/browser/web_test/fake_bluetooth_delegate.cc
@@ -0,0 +1,139 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/shell/browser/web_test/fake_bluetooth_delegate.h"
+
+#include "content/public/browser/web_contents.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h"
+#include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom.h"
+#include "url/origin.h"
+
+using blink::WebBluetoothDeviceId;
+using device::BluetoothDevice;
+using device::BluetoothUUID;
+
+namespace content {
+
+// public
+FakeBluetoothDelegate::FakeBluetoothDelegate() = default;
+FakeBluetoothDelegate::~FakeBluetoothDelegate() = default;
+
+WebBluetoothDeviceId FakeBluetoothDelegate::GetWebBluetoothDeviceId(
+    RenderFrameHost* frame,
+    const std::string& device_address) {
+  auto& device_address_to_id_map = GetAddressToIdMapForOrigin(frame);
+  auto it = device_address_to_id_map.find(device_address);
+  if (it != device_address_to_id_map.end())
+    return it->second;
+  return {};
+}
+
+std::string FakeBluetoothDelegate::GetDeviceAddress(
+    RenderFrameHost* frame,
+    const WebBluetoothDeviceId& device_id) {
+  auto& device_address_to_id_map = GetAddressToIdMapForOrigin(frame);
+  for (auto& entry : device_address_to_id_map) {
+    if (entry.second == device_id)
+      return entry.first;
+  }
+  return std::string();
+}
+
+blink::WebBluetoothDeviceId FakeBluetoothDelegate::AddScannedDevice(
+    RenderFrameHost* frame,
+    const std::string& device_address) {
+  return GetOrCreateDeviceIdForDeviceAddress(frame, device_address);
+}
+
+WebBluetoothDeviceId FakeBluetoothDelegate::GrantServiceAccessPermission(
+    RenderFrameHost* frame,
+    const BluetoothDevice* device,
+    const blink::mojom::WebBluetoothRequestDeviceOptions* options) {
+  WebBluetoothDeviceId device_id =
+      GetOrCreateDeviceIdForDeviceAddress(frame, device->GetAddress());
+  device_id_to_name_map_[device_id] =
+      device->GetName() ? *device->GetName() : std::string();
+  GrantUnionOfServicesForDevice(device_id, options);
+  return device_id;
+}
+
+bool FakeBluetoothDelegate::HasDevicePermission(
+    RenderFrameHost* frame,
+    const WebBluetoothDeviceId& device_id) {
+  return base::Contains(device_id_to_services_map_, device_id);
+}
+
+bool FakeBluetoothDelegate::IsAllowedToAccessService(
+    RenderFrameHost* frame,
+    const WebBluetoothDeviceId& device_id,
+    const BluetoothUUID& service) {
+  auto id_to_services_it = device_id_to_services_map_.find(device_id);
+  if (id_to_services_it == device_id_to_services_map_.end())
+    return false;
+
+  return base::Contains(id_to_services_it->second, service);
+}
+
+bool FakeBluetoothDelegate::IsAllowedToAccessAtLeastOneService(
+    RenderFrameHost* frame,
+    const WebBluetoothDeviceId& device_id) {
+  auto id_to_services_it = device_id_to_services_map_.find(device_id);
+  if (id_to_services_it == device_id_to_services_map_.end())
+    return false;
+
+  return !id_to_services_it->second.empty();
+}
+
+WebBluetoothDeviceId FakeBluetoothDelegate::GetOrCreateDeviceIdForDeviceAddress(
+    RenderFrameHost* frame,
+    const std::string& device_address) {
+  WebBluetoothDeviceId device_id;
+  auto& device_address_to_id_map = GetAddressToIdMapForOrigin(frame);
+  auto it = device_address_to_id_map.find(device_address);
+  if (it != device_address_to_id_map.end()) {
+    device_id = it->second;
+  } else {
+    device_id = WebBluetoothDeviceId::Create();
+    device_address_to_id_map[device_address] = device_id;
+  }
+  return device_id;
+}
+
+void FakeBluetoothDelegate::GrantUnionOfServicesForDevice(
+    const WebBluetoothDeviceId& device_id,
+    const blink::mojom::WebBluetoothRequestDeviceOptions* options) {
+  if (!options)
+    return;
+
+  // Create an entry for |device_id| in |device_id_to_services_map_| to indicate
+  // that the site can attempt to GATT connect even if |options| does not
+  // contain any services.
+  base::flat_set<BluetoothUUID>& granted_services =
+      device_id_to_services_map_[device_id];
+  if (options->filters) {
+    for (const blink::mojom::WebBluetoothLeScanFilterPtr& filter :
+         options->filters.value()) {
+      if (!filter->services)
+        continue;
+
+      for (const BluetoothUUID& uuid : filter->services.value())
+        granted_services.insert(uuid);
+    }
+  }
+
+  for (const BluetoothUUID& uuid : options->optional_services)
+    granted_services.insert(uuid);
+}
+
+FakeBluetoothDelegate::AddressToIdMap&
+FakeBluetoothDelegate::GetAddressToIdMapForOrigin(RenderFrameHost* frame) {
+  auto* web_contents = WebContents::FromRenderFrameHost(frame);
+  auto origin_pair =
+      std::make_pair(frame->GetLastCommittedOrigin(),
+                     web_contents->GetMainFrame()->GetLastCommittedOrigin());
+  return device_address_to_id_map_for_origin_[origin_pair];
+}
+
+}  // namespace content
diff --git a/content/shell/browser/web_test/fake_bluetooth_delegate.h b/content/shell/browser/web_test/fake_bluetooth_delegate.h
new file mode 100644
index 0000000..1b80b79f
--- /dev/null
+++ b/content/shell/browser/web_test/fake_bluetooth_delegate.h
@@ -0,0 +1,103 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_SHELL_BROWSER_WEB_TEST_FAKE_BLUETOOTH_DELEGATE_H_
+#define CONTENT_SHELL_BROWSER_WEB_TEST_FAKE_BLUETOOTH_DELEGATE_H_
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "content/public/browser/bluetooth_delegate.h"
+#include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-forward.h"
+
+namespace blink {
+class WebBluetoothDeviceId;
+}  // namespace blink
+
+namespace device {
+class BluetoothDevice;
+class BluetoothUUID;
+}  // namespace device
+
+namespace url {
+class Origin;
+}  // namespace url
+
+namespace content {
+
+class RenderFrameHost;
+
+// Fakes Web Bluetooth permissions for web tests by emulating Chrome's
+// implementation.
+class FakeBluetoothDelegate : public BluetoothDelegate {
+ public:
+  FakeBluetoothDelegate();
+  ~FakeBluetoothDelegate() override;
+
+  // Move-only class.
+  FakeBluetoothDelegate(const FakeBluetoothDelegate&) = delete;
+  FakeBluetoothDelegate& operator=(const FakeBluetoothDelegate&) = delete;
+
+  // BluetoothDelegate implementation:
+  blink::WebBluetoothDeviceId GetWebBluetoothDeviceId(
+      RenderFrameHost* frame,
+      const std::string& device_address) override;
+  std::string GetDeviceAddress(RenderFrameHost* frame,
+                               const blink::WebBluetoothDeviceId&) override;
+  blink::WebBluetoothDeviceId AddScannedDevice(
+      RenderFrameHost* frame,
+      const std::string& device_address) override;
+  blink::WebBluetoothDeviceId GrantServiceAccessPermission(
+      RenderFrameHost* frame,
+      const device::BluetoothDevice* device,
+      const blink::mojom::WebBluetoothRequestDeviceOptions* options) override;
+  bool HasDevicePermission(
+      RenderFrameHost* frame,
+      const blink::WebBluetoothDeviceId& device_id) override;
+  bool IsAllowedToAccessService(RenderFrameHost* frame,
+                                const blink::WebBluetoothDeviceId& device_id,
+                                const device::BluetoothUUID& service) override;
+  bool IsAllowedToAccessAtLeastOneService(
+      RenderFrameHost* frame,
+      const blink::WebBluetoothDeviceId& device_id) override;
+
+ private:
+  using AddressToIdMap = std::map<std::string, blink::WebBluetoothDeviceId>;
+  using OriginPair = std::pair<url::Origin, url::Origin>;
+  using IdToServicesMap = std::map<blink::WebBluetoothDeviceId,
+                                   base::flat_set<device::BluetoothUUID>>;
+  using IdToNameMap = std::map<blink::WebBluetoothDeviceId, std::string>;
+
+  // Finds an existing WebBluetoothDeviceId for |device_address| for |frame| or
+  // creates a new ID for the Bluetooth device on the current frame.
+  blink::WebBluetoothDeviceId GetOrCreateDeviceIdForDeviceAddress(
+      RenderFrameHost* frame,
+      const std::string& device_address);
+
+  // Adds the union of |options->filters->services| and
+  // |options->optional_services| to the allowed services for |device_id|.
+  void GrantUnionOfServicesForDevice(
+      const blink::WebBluetoothDeviceId& device_id,
+      const blink::mojom::WebBluetoothRequestDeviceOptions* options);
+  AddressToIdMap& GetAddressToIdMapForOrigin(RenderFrameHost* frame);
+
+  // Maps origins to their own maps of device address to device ID.
+  // If a given origin and device address does not have an associated device ID,
+  // then the origin does not have permission to access the device.
+  std::map<OriginPair, AddressToIdMap> device_address_to_id_map_for_origin_;
+
+  // These map device IDs to their set of allowed services and device names.
+  // Since devices IDs are randomly generated, it is very unlikely that two
+  // unique devices will share the same ID. Therefore, these maps contain all of
+  // the service permissions and device names from all of the origins.
+  IdToServicesMap device_id_to_services_map_;
+  IdToNameMap device_id_to_name_map_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_SHELL_BROWSER_WEB_TEST_FAKE_BLUETOOTH_DELEGATE_H_
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.cc b/content/shell/browser/web_test/web_test_content_browser_client.cc
index b899d61..730790e 100644
--- a/content/shell/browser/web_test/web_test_content_browser_client.cc
+++ b/content/shell/browser/web_test/web_test_content_browser_client.cc
@@ -30,6 +30,7 @@
 #include "content/shell/browser/web_test/blink_test_controller.h"
 #include "content/shell/browser/web_test/fake_bluetooth_chooser.h"
 #include "content/shell/browser/web_test/fake_bluetooth_chooser_factory.h"
+#include "content/shell/browser/web_test/fake_bluetooth_delegate.h"
 #include "content/shell/browser/web_test/mojo_web_test_helper.h"
 #include "content/shell/browser/web_test/web_test_bluetooth_fake_adapter_setter_impl.h"
 #include "content/shell/browser/web_test/web_test_browser_context.h"
@@ -355,6 +356,16 @@
       switches::kRunWebTests);
 }
 
+BluetoothDelegate* WebTestContentBrowserClient::GetBluetoothDelegate() {
+  if (!fake_bluetooth_delegate_)
+    fake_bluetooth_delegate_ = std::make_unique<FakeBluetoothDelegate>();
+  return fake_bluetooth_delegate_.get();
+}
+
+void WebTestContentBrowserClient::ResetFakeBluetoothDelegate() {
+  fake_bluetooth_delegate_.reset();
+}
+
 content::TtsControllerDelegate*
 WebTestContentBrowserClient::GetTtsControllerDelegate() {
   return WebTestTtsControllerDelegate::GetInstance();
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.h b/content/shell/browser/web_test/web_test_content_browser_client.h
index 0992408..aaf3b8fe 100644
--- a/content/shell/browser/web_test/web_test_content_browser_client.h
+++ b/content/shell/browser/web_test/web_test_content_browser_client.h
@@ -6,6 +6,8 @@
 #define CONTENT_SHELL_BROWSER_WEB_TEST_WEB_TEST_CONTENT_BROWSER_CLIENT_H_
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "content/public/common/client_hints.mojom.h"
 #include "content/shell/browser/shell_content_browser_client.h"
@@ -20,6 +22,7 @@
 
 class FakeBluetoothChooser;
 class FakeBluetoothChooserFactory;
+class FakeBluetoothDelegate;
 class MockClipboardHost;
 class MockPlatformNotificationService;
 class WebTestBrowserContext;
@@ -39,6 +42,7 @@
 
   // Retrieves the last created FakeBluetoothChooser instance.
   std::unique_ptr<FakeBluetoothChooser> GetNextFakeBluetoothChooser();
+  void ResetFakeBluetoothDelegate();
 
   // ContentBrowserClient overrides.
   void RenderProcessWillLaunch(RenderProcessHost* host) override;
@@ -75,7 +79,7 @@
       service_manager::BinderMapWithContext<content::RenderFrameHost*>* map)
       override;
   bool CanAcceptUntrustedExchangesIfNeeded() override;
-
+  BluetoothDelegate* GetBluetoothDelegate() override;
   content::TtsControllerDelegate* GetTtsControllerDelegate() override;
   content::TtsPlatform* GetTtsPlatform() override;
   bool CanEnterFullscreenWithoutUserActivation() override;
@@ -112,6 +116,7 @@
 
   // Stores the FakeBluetoothChooserFactory that produces FakeBluetoothChoosers.
   std::unique_ptr<FakeBluetoothChooserFactory> fake_bluetooth_chooser_factory_;
+  std::unique_ptr<FakeBluetoothDelegate> fake_bluetooth_delegate_;
   std::unique_ptr<MockClipboardHost> mock_clipboard_host_;
 };
 
diff --git a/content/test/data/accessibility/css/table-data-display-other-expected-blink.txt b/content/test/data/accessibility/css/table-data-display-other-expected-blink.txt
new file mode 100644
index 0000000..a6bd31b
--- /dev/null
+++ b/content/test/data/accessibility/css/table-data-display-other-expected-blink.txt
@@ -0,0 +1,25 @@
+rootWebArea
+++genericContainer ignored
+++++table tableRowCount=2 tableColumnCount=3
+++++++rowGroup ignored
+++++++++row tableRowIndex=0
+++++++++++columnHeader name='1' tableCellColumnIndex=0 tableCellRowIndex=0
+++++++++++++staticText name='1'
+++++++++++++++inlineTextBox name='1'
+++++++++++columnHeader name='2' tableCellColumnIndex=1 tableCellRowIndex=0
+++++++++++++staticText name='2'
+++++++++++++++inlineTextBox name='2'
+++++++++++columnHeader name='3' tableCellColumnIndex=2 tableCellRowIndex=0
+++++++++++++staticText name='3'
+++++++++++++++inlineTextBox name='3'
+++++++rowGroup ignored
+++++++++row tableRowIndex=1
+++++++++++cell name='a' tableCellColumnIndex=0 tableCellRowIndex=1
+++++++++++++staticText name='a'
+++++++++++++++inlineTextBox name='a'
+++++++++++cell name='b' tableCellColumnIndex=1 tableCellRowIndex=1
+++++++++++++staticText name='b'
+++++++++++++++inlineTextBox name='b'
+++++++++++cell name='c' tableCellColumnIndex=2 tableCellRowIndex=1
+++++++++++++staticText name='c'
+++++++++++++++inlineTextBox name='c'
diff --git a/content/test/data/accessibility/css/table-data-display-other.html b/content/test/data/accessibility/css/table-data-display-other.html
new file mode 100644
index 0000000..948b224
--- /dev/null
+++ b/content/test/data/accessibility/css/table-data-display-other.html
@@ -0,0 +1,26 @@
+<!--
+@BLINK-ALLOW:tableColumnCount*
+@BLINK-ALLOW:tableRowCount*
+@BLINK-ALLOW:table*Index*
+-->
+<!-- Ensure that a table with display styles and other semantic table markup
+  is still exposed as a data table.. -->
+<style>
+table, thead, tbody, tr, td { display: flex; }
+</style>
+<table>
+  <thead>
+    <tr>
+      <th>1</th>
+      <th>2</th>
+      <th>3</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>a</td>
+      <td>b</td>
+      <td>c</td>
+    </tr>
+  </tbody>
+</table>
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 4de85b3..0a00b8c 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -201,6 +201,7 @@
 
 # Win / NVIDIA GeForce GTX 1660 / D3D11 flaky failures
 crbug.com/angleproject/4377 [ d3d11 win nvidia-0x2184 ] conformance/attribs/gl-vertex-attrib-unconsumed-out-of-bounds.html [ RetryOnFailure ]
+crbug.com/1057635 [ d3d11 win nvidia-0x2184 ] deqp/functional/gles3/framebufferblit/depth_stencil.html [ RetryOnFailure ]
 
 # WIN / OpenGL / NVIDIA failures
 crbug.com/963205 [ win nvidia opengl passthrough ] conformance/extensions/webgl-compressed-texture-s3tc.html [ RetryOnFailure ]
diff --git a/docs/threading_and_tasks.md b/docs/threading_and_tasks.md
index 5ace1f2..5c8f0b3 100644
--- a/docs/threading_and_tasks.md
+++ b/docs/threading_and_tasks.md
@@ -455,8 +455,10 @@
 
 Methods that take `base::TaskTraits` can be be passed `{}` when default traits
 are sufficient. Default traits are appropriate for tasks that:
-- Don’t block (ref. MayBlock and WithBaseSyncPrimitives).
-- Prefer inheriting the current priority to specifying their own.
+- Don’t block (ref. MayBlock and WithBaseSyncPrimitives);
+- Pertain to user-blocking activity;
+  (explicitly or implicitly by having an ordering dependency with a component
+   that does)
 - Can either block shutdown or be skipped on shutdown (thread pool is free to
   choose a fitting default).
 Tasks that don’t match this description must be posted with explicit TaskTraits.
@@ -470,14 +472,12 @@
 Below are some examples of how to specify `base::TaskTraits`.
 
 ```cpp
-// This task has no explicit TaskTraits. It cannot block. Its priority
-// is inherited from the calling context (e.g. if it is posted from
-// a BEST_EFFORT task, it will have a BEST_EFFORT priority). It will either
-// block shutdown or be skipped on shutdown.
+// This task has no explicit TaskTraits. It cannot block. Its priority is
+// USER_BLOCKING. It will either block shutdown or be skipped on shutdown.
 base::ThreadPool::PostTask(FROM_HERE, base::BindOnce(...));
 
-// This task has the highest priority. The thread pool will try to
-// run it before USER_VISIBLE and BEST_EFFORT tasks.
+// This task has the highest priority. The thread pool will schedule it before
+// USER_VISIBLE and BEST_EFFORT tasks.
 base::ThreadPool::PostTask(
     FROM_HERE, {base::TaskPriority::USER_BLOCKING},
     base::BindOnce(...));
diff --git a/infra/config/buckets/ci.star b/infra/config/buckets/ci.star
index d64145e..7594814 100644
--- a/infra/config/buckets/ci.star
+++ b/infra/config/buckets/ci.star
@@ -1186,7 +1186,7 @@
 ci.fyi_mac_builder(
     name = 'Mac Builder Next',
     cores = None,
-    os = os.MAC_10_14,
+    os = None,
 )
 
 ci.fyi_mac_builder(
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 8effa41..e31660a 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -3452,7 +3452,6 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "builder:Mac Builder Next"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Mac-10.14"
       recipe: <
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
diff --git a/ios/build/bots/OWNERS b/ios/build/bots/OWNERS
index e39778a..1ed7b5b 100644
--- a/ios/build/bots/OWNERS
+++ b/ios/build/bots/OWNERS
@@ -1,5 +1,4 @@
 justincohen@chromium.org
-kkhorimoto@chromium.org
 rohitrao@chromium.org
 sdefresne@chromium.org
 sergeyberezin@chromium.org
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 2e45cee..fe0ecaa 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -133,7 +133,6 @@
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h"
-#import "ios/chrome/browser/url_loading/app_url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
@@ -417,7 +416,6 @@
 
 @implementation MainController
 // Defined by MainControllerGuts.
-@synthesize appURLLoadingService;
 @synthesize dismissingTabSwitcher = _dismissingTabSwitcher;
 @synthesize restoreHelper = _restoreHelper;
 
@@ -605,16 +603,14 @@
   // This is per-window code.
 
   DCHECK(!self.browserViewWrangler);
-
-  self.appURLLoadingService = new AppUrlLoadingService();
-  self.appURLLoadingService->SetDelegate(self.sceneController);
+  DCHECK(self.sceneController.appURLLoadingService);
 
   self.browserViewWrangler = [[BrowserViewWrangler alloc]
              initWithBrowserState:self.mainBrowserState
              webStateListObserver:self.sceneController
        applicationCommandEndpoint:self.sceneController
       browsingDataCommandEndpoint:self
-             appURLLoadingService:self.appURLLoadingService];
+             appURLLoadingService:self.sceneController.appURLLoadingService];
 
   // Ensure the main tab model is created. This also creates the BVC.
   [self.browserViewWrangler createMainBrowser];
@@ -780,12 +776,21 @@
 }
 
 - (void)stopChromeMain {
+  // This code is per-window.
+
   // Teardown UI state that is associated with scenes.
   [self.sceneController teardownUI];
 
   [_mainCoordinator stop];
   _mainCoordinator = nil;
 
+  // Invariant: The UI is stopped before the model is shutdown.
+  DCHECK(!_mainCoordinator);
+  [self.browserViewWrangler shutdown];
+  self.browserViewWrangler = nil;
+
+  // End of per-window code.
+
   [_spotlightManager shutdown];
   _spotlightManager = nil;
 
@@ -800,11 +805,6 @@
             self.mainBrowserState));
   }
 
-  // Invariant: The UI is stopped before the model is shutdown.
-  DCHECK(!_mainCoordinator);
-  [self.browserViewWrangler shutdown];
-  self.browserViewWrangler = nil;
-
   _extensionSearchEngineDataUpdater = nullptr;
 
   ios::GetChromeBrowserProvider()
diff --git a/ios/chrome/app/main_controller_guts.h b/ios/chrome/app/main_controller_guts.h
index 3c09eae..91c67f7 100644
--- a/ios/chrome/app/main_controller_guts.h
+++ b/ios/chrome/app/main_controller_guts.h
@@ -19,17 +19,12 @@
 @class TabGridCoordinator;
 @protocol BrowserInterfaceProvider;
 @protocol TabSwitcher;
-class AppUrlLoadingService;
 
 // TODO(crbug.com/1012697): Remove this protocol when SceneController is
 // operational. Move the private internals back into MainController, and pass
 // ownership of Scene-related objects to SceneController.
 @protocol MainControllerGuts
 
-// The application level component for url loading. Is passed down to
-// browser state level UrlLoadingService instances.
-@property(nonatomic, assign) AppUrlLoadingService* appURLLoadingService;
-
 // If YES, the tab switcher is currently active.
 @property(nonatomic, assign, getter=isTabSwitcherActive)
     BOOL tabSwitcherIsActive;
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
index 2ac5bbc..e0e48ef 100644
--- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
+++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -36,7 +36,6 @@
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #include "ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_factory.h"
-#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h"
 #include "ios/chrome/browser/signin/about_signin_internals_factory.h"
 #include "ios/chrome/browser/signin/account_consistency_service_factory.h"
 #include "ios/chrome/browser/signin/account_reconcilor_factory.h"
@@ -121,7 +120,6 @@
   SigninClientFactory::GetInstance();
   SnapshotCacheFactory::GetInstance();
   SyncSetupServiceFactory::GetInstance();
-  TabRestoreServiceDelegateImplIOSFactory::GetInstance();
   TextToSpeechPlaybackControllerFactory::GetInstance();
   TranslateAcceptLanguagesFactory::GetInstance();
   UnifiedConsentServiceFactory::GetInstance();
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.mm b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.mm
index 6783519b..702ba68 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.mm
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.mm
@@ -25,7 +25,8 @@
 // Returns true if navigation URL repesents Chrome's New Tab Page.
 bool IsNptUrl(const GURL& url) {
   return url.GetOrigin() == kChromeUINewTabURL ||
-         (url.SchemeIs(url::kAboutScheme) && url.path() == "//newtab");
+         (url.SchemeIs(url::kAboutScheme) &&
+          (url.path() == "//newtab" || url.path() == "//newtab/"));
 }
 }
 
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper_unittest.mm b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper_unittest.mm
index ba200c40..757f48b 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper_unittest.mm
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper_unittest.mm
@@ -146,6 +146,25 @@
       << events.front();
 }
 
+// Tests metadata for about://newtab/ NTP navigation.
+TEST_F(BreadcrumbManagerTabHelperTest, AboutNewTabNavigationStart2) {
+  BreadcrumbManagerTabHelper::CreateForWebState(&first_web_state_);
+  ASSERT_EQ(0ul, breadcrumb_service_->GetEvents(0).size());
+
+  web::FakeNavigationContext context;
+  context.SetUrl(GURL("about://newtab/"));
+  first_web_state_.OnNavigationStarted(&context);
+  std::list<std::string> events = breadcrumb_service_->GetEvents(0);
+  ASSERT_EQ(1ul, events.size());
+
+  EXPECT_NE(std::string::npos, events.front().find(base::StringPrintf(
+                                   "%s%lld", kBreadcrumbDidStartNavigation,
+                                   context.GetNavigationId())))
+      << events.front();
+  EXPECT_NE(std::string::npos, events.front().find(kBreadcrumbNtpNavigation))
+      << events.front();
+}
+
 // Tests unique ID in DidStartNavigation and DidStartNavigation.
 TEST_F(BreadcrumbManagerTabHelperTest, NavigationUniqueId) {
   BreadcrumbManagerTabHelper::CreateForWebState(&first_web_state_);
diff --git a/ios/chrome/browser/main/BUILD.gn b/ios/chrome/browser/main/BUILD.gn
index 48c9fde05..a97707c 100644
--- a/ios/chrome/browser/main/BUILD.gn
+++ b/ios/chrome/browser/main/BUILD.gn
@@ -43,6 +43,7 @@
     "//ios/chrome/browser/crash_report/breadcrumbs:feature_flags",
     "//ios/chrome/browser/infobars/overlays/browser_agent:browser_agent_util",
     "//ios/chrome/browser/metrics:metrics_browser_agent",
+    "//ios/chrome/browser/sessions",
     "//ios/chrome/browser/sessions:restoration_agent",
     "//ios/chrome/browser/sessions:serialisation",
     "//ios/chrome/browser/sessions:session_service",
diff --git a/ios/chrome/browser/main/browser_agent_util.mm b/ios/chrome/browser/main/browser_agent_util.mm
index 1b9e8df..618c022d 100644
--- a/ios/chrome/browser/main/browser_agent_util.mm
+++ b/ios/chrome/browser/main/browser_agent_util.mm
@@ -10,6 +10,7 @@
 #include "ios/chrome/browser/crash_report/breadcrumbs/features.h"
 #include "ios/chrome/browser/infobars/overlays/browser_agent/infobar_overlay_browser_agent_util.h"
 #import "ios/chrome/browser/metrics/tab_usage_recorder_browser_agent.h"
+#import "ios/chrome/browser/sessions/live_tab_context_browser_agent.h"
 #import "ios/chrome/browser/sessions/session_restoration_browser_agent.h"
 #import "ios/chrome/browser/sessions/session_service_ios.h"
 #include "ios/chrome/browser/tabs/synced_window_delegate_browser_agent.h"
@@ -25,6 +26,7 @@
   if (base::FeatureList::IsEnabled(kLogBreadcrumbs)) {
     BreadcrumbManagerBrowserAgent::CreateForBrowser(browser);
   }
+  LiveTabContextBrowserAgent::CreateForBrowser(browser);
   TabInsertionBrowserAgent::CreateForBrowser(browser);
   AttachInfobarOverlayBrowserAgent(browser);
   SyncedWindowDelegateBrowserAgent::CreateForBrowser(browser);
diff --git a/ios/chrome/browser/sessions/BUILD.gn b/ios/chrome/browser/sessions/BUILD.gn
index fc3ea07..6086716e 100644
--- a/ios/chrome/browser/sessions/BUILD.gn
+++ b/ios/chrome/browser/sessions/BUILD.gn
@@ -11,20 +11,19 @@
     "ios_chrome_tab_restore_service_client.mm",
     "ios_chrome_tab_restore_service_factory.cc",
     "ios_chrome_tab_restore_service_factory.h",
-    "tab_restore_service_delegate_impl_ios.h",
-    "tab_restore_service_delegate_impl_ios.mm",
-    "tab_restore_service_delegate_impl_ios_factory.h",
-    "tab_restore_service_delegate_impl_ios_factory.mm",
+    "live_tab_context_browser_agent.h",
+    "live_tab_context_browser_agent.mm",
   ]
+  public_deps = [ "//components/sessions" ]
   deps = [
     ":serialisation",
     "//base",
     "//components/keyed_service/core",
     "//components/keyed_service/ios",
-    "//components/sessions",
     "//components/tab_groups",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/web_state_list",
     "//ios/public/provider/chrome/browser",
diff --git a/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_client.mm b/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_client.mm
index 8e72ac4..d2c3a35 100644
--- a/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_client.mm
+++ b/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_client.mm
@@ -11,11 +11,11 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
-#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h"
-#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h"
+#include "ios/chrome/browser/main/browser.h"
+#include "ios/chrome/browser/main/browser_list.h"
+#include "ios/chrome/browser/main/browser_list_factory.h"
+#include "ios/chrome/browser/sessions/live_tab_context_browser_agent.h"
 #include "ios/chrome/browser/tabs/synced_window_delegate_browser_agent.h"
-#import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/browser/tabs/tab_model_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "url/gurl.h"
@@ -26,7 +26,7 @@
 
 namespace {
 sessions::LiveTabContext* FindLiveTabContextWithCondition(
-    base::RepeatingCallback<bool(TabModel*)> condition) {
+    base::RepeatingCallback<bool(Browser*)> condition) {
   std::vector<ChromeBrowserState*> browser_states =
       GetApplicationContext()
           ->GetChromeBrowserStateManager()
@@ -34,28 +34,17 @@
 
   for (ChromeBrowserState* browser_state : browser_states) {
     DCHECK(!browser_state->IsOffTheRecord());
-    NSArray<TabModel*>* tab_models;
-
-    tab_models = TabModelList::GetTabModelsForChromeBrowserState(browser_state);
-    for (TabModel* tab_model : tab_models) {
-      if (condition.Run(tab_model)) {
-        return TabRestoreServiceDelegateImplIOSFactory::GetForBrowserState(
-            browser_state);
+    BrowserList* browsers =
+        BrowserListFactory::GetForBrowserState(browser_state);
+    for (Browser* browser : browsers->AllRegularBrowsers()) {
+      if (condition.Run(browser)) {
+        return LiveTabContextBrowserAgent::FromBrowser(browser);
       }
     }
 
-    if (!browser_state->HasOffTheRecordChromeBrowserState())
-      continue;
-
-    ChromeBrowserState* otr_browser_state =
-        browser_state->GetOffTheRecordChromeBrowserState();
-
-    tab_models =
-        TabModelList::GetTabModelsForChromeBrowserState(otr_browser_state);
-    for (TabModel* tab_model : tab_models) {
-      if (condition.Run(tab_model)) {
-        return TabRestoreServiceDelegateImplIOSFactory::GetForBrowserState(
-            browser_state);
+    for (Browser* browser : browsers->AllIncognitoBrowsers()) {
+      if (condition.Run(browser)) {
+        return LiveTabContextBrowserAgent::FromBrowser(browser);
       }
     }
   }
@@ -76,8 +65,8 @@
     const gfx::Rect& /* bounds */,
     ui::WindowShowState /* show_state */,
     const std::string& /* workspace */) {
-  return TabRestoreServiceDelegateImplIOSFactory::GetForBrowserState(
-      browser_state_);
+  NOTREACHED() << "Tab restore service attempting to create a new window.";
+  return nullptr;
 }
 
 sessions::LiveTabContext*
@@ -87,8 +76,8 @@
       static_cast<const sessions::IOSLiveTab*>(tab);
 
   return FindLiveTabContextWithCondition(base::Bind(
-      [](const web::WebState* web_state, TabModel* tab_model) {
-        WebStateList* web_state_list = tab_model.webStateList;
+      [](const web::WebState* web_state, Browser* browser) {
+        WebStateList* web_state_list = browser->GetWebStateList();
         const int index = web_state_list->GetIndexOfWebState(web_state);
         return index != WebStateList::kInvalidIndex;
       },
@@ -99,9 +88,10 @@
 IOSChromeTabRestoreServiceClient::FindLiveTabContextWithID(
     SessionID desired_id) {
   return FindLiveTabContextWithCondition(base::Bind(
-      [](SessionID desired_id, TabModel* tab_model) {
-        DCHECK(tab_model.syncedWindowDelegate);
-        return tab_model.syncedWindowDelegate->GetSessionId() == desired_id;
+      [](SessionID desired_id, Browser* browser) {
+        SyncedWindowDelegateBrowserAgent* syncedWindowDelegate =
+            SyncedWindowDelegateBrowserAgent::FromBrowser(browser);
+        return syncedWindowDelegate->GetSessionId() == desired_id;
       },
       desired_id));
 }
diff --git a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h b/ios/chrome/browser/sessions/live_tab_context_browser_agent.h
similarity index 69%
rename from ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h
rename to ios/chrome/browser/sessions/live_tab_context_browser_agent.h
index 6504043..792ffd3 100644
--- a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h
+++ b/ios/chrome/browser/sessions/live_tab_context_browser_agent.h
@@ -2,33 +2,32 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_DELEGATE_IMPL_IOS_H_
-#define IOS_CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_DELEGATE_IMPL_IOS_H_
+#ifndef IOS_CHROME_BROWSER_SESSIONS_LIVE_TAB_CONTEXT_BROWSER_AGENT_H_
+#define IOS_CHROME_BROWSER_SESSIONS_LIVE_TAB_CONTEXT_BROWSER_AGENT_H_
 
 #include <string>
 #include <vector>
 
-#include "base/macros.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/sessions/core/live_tab_context.h"
-#include "components/tab_groups/tab_group_id.h"
-#include "components/tab_groups/tab_group_visual_data.h"
+#include "ios/chrome/browser/main/browser_observer.h"
+#include "ios/chrome/browser/main/browser_user_data.h"
 
-class ChromeBrowserState;
 class WebStateList;
 
 // Implementation of sessions::LiveTabContext which uses an instance
 // of TabModel in order to fulfil its duties.
-class TabRestoreServiceDelegateImplIOS : public sessions::LiveTabContext,
-                                         public KeyedService {
+class LiveTabContextBrowserAgent
+    : public sessions::LiveTabContext,
+      public BrowserUserData<LiveTabContextBrowserAgent> {
  public:
-  explicit TabRestoreServiceDelegateImplIOS(ChromeBrowserState* browser_state);
-  ~TabRestoreServiceDelegateImplIOS() override;
+  // Not copiable or movable.
+  LiveTabContextBrowserAgent(const LiveTabContextBrowserAgent&) = delete;
+  LiveTabContextBrowserAgent& operator=(const LiveTabContextBrowserAgent&) =
+      delete;
+  ~LiveTabContextBrowserAgent() override;
 
-  // Overridden from KeyedService:
-  void Shutdown() override {}
-
-  // Overridden from sessions::LiveTabContext:
+  // Sessions::LiveTabContext:
   void ShowBrowserWindow() override;
   SessionID GetSessionID() const override;
   int GetTabCount() const override;
@@ -70,13 +69,13 @@
   void CloseTab() override;
 
  private:
-  // Retrieves the current |WebStateList| corresponding to |browser_state_|;
-  WebStateList* GetWebStateList() const;
+  explicit LiveTabContextBrowserAgent(Browser* browser);
+  friend class BrowserUserData<LiveTabContextBrowserAgent>;
+  BROWSER_USER_DATA_KEY_DECL();
 
-  ChromeBrowserState* browser_state_;  // weak
+  ChromeBrowserState* browser_state_;
+  WebStateList* web_state_list_;
   SessionID session_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(TabRestoreServiceDelegateImplIOS);
 };
 
-#endif  // IOS_CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_DELEGATE_IMPL_IOS_H_
+#endif  // IOS_CHROME_BROWSER_SESSIONS_LIVE_TAB_CONTEXT_BROWSER_AGENT_H_
diff --git a/ios/chrome/browser/sessions/live_tab_context_browser_agent.mm b/ios/chrome/browser/sessions/live_tab_context_browser_agent.mm
new file mode 100644
index 0000000..4123db6
--- /dev/null
+++ b/ios/chrome/browser/sessions/live_tab_context_browser_agent.mm
@@ -0,0 +1,146 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/sessions/live_tab_context_browser_agent.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/optional.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/sessions/core/session_types.h"
+#include "components/tab_groups/tab_group_id.h"
+#include "components/tab_groups/tab_group_visual_data.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/sessions/session_util.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/web_state_list/web_state_opener.h"
+#import "ios/web/public/web_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+BROWSER_USER_DATA_KEY_IMPL(LiveTabContextBrowserAgent)
+
+LiveTabContextBrowserAgent::LiveTabContextBrowserAgent(Browser* browser)
+    : browser_state_(browser->GetBrowserState()),
+      web_state_list_(browser->GetWebStateList()),
+      session_id_(SessionID::NewUnique()) {}
+
+LiveTabContextBrowserAgent::~LiveTabContextBrowserAgent() {}
+
+void LiveTabContextBrowserAgent::ShowBrowserWindow() {
+  // No need to do anything here, as the singleton browser "window" is already
+  // shown.
+}
+
+SessionID LiveTabContextBrowserAgent::GetSessionID() const {
+  return session_id_;
+}
+
+int LiveTabContextBrowserAgent::GetTabCount() const {
+  return web_state_list_->count();
+}
+
+int LiveTabContextBrowserAgent::GetSelectedIndex() const {
+  return web_state_list_->active_index();
+}
+
+std::string LiveTabContextBrowserAgent::GetAppName() const {
+  return std::string();
+}
+
+sessions::LiveTab* LiveTabContextBrowserAgent::GetLiveTabAt(int index) const {
+  return nullptr;
+}
+
+sessions::LiveTab* LiveTabContextBrowserAgent::GetActiveLiveTab() const {
+  return nullptr;
+}
+
+bool LiveTabContextBrowserAgent::IsTabPinned(int index) const {
+  // Not supported by iOS.
+  return false;
+}
+
+base::Optional<tab_groups::TabGroupId>
+LiveTabContextBrowserAgent::GetTabGroupForTab(int index) const {
+  // Not supported by iOS.
+  return base::nullopt;
+}
+
+const tab_groups::TabGroupVisualData*
+LiveTabContextBrowserAgent::GetVisualDataForGroup(
+    const tab_groups::TabGroupId& group) const {
+  // Since we never return a group from GetTabGroupForTab(), this should never
+  // be called.
+  NOTREACHED();
+  return nullptr;
+}
+
+void LiveTabContextBrowserAgent::SetVisualDataForGroup(
+    const tab_groups::TabGroupId& group,
+    const tab_groups::TabGroupVisualData& visual_data) {
+  // Not supported on iOS.
+}
+
+const gfx::Rect LiveTabContextBrowserAgent::GetRestoredBounds() const {
+  // Not supported by iOS.
+  return gfx::Rect();
+}
+
+ui::WindowShowState LiveTabContextBrowserAgent::GetRestoredState() const {
+  // Not supported by iOS.
+  return ui::SHOW_STATE_NORMAL;
+}
+
+std::string LiveTabContextBrowserAgent::GetWorkspace() const {
+  // Not supported by iOS.
+  return std::string();
+}
+
+sessions::LiveTab* LiveTabContextBrowserAgent::AddRestoredTab(
+    const std::vector<sessions::SerializedNavigationEntry>& navigations,
+    int tab_index,
+    int selected_navigation,
+    const std::string& extension_app_id,
+    base::Optional<tab_groups::TabGroupId> group,
+    const tab_groups::TabGroupVisualData& group_visual_data,
+    bool select,
+    bool pin,
+    bool from_last_session,
+    const sessions::PlatformSpecificTabData* tab_platform_data,
+    const std::string& user_agent_override) {
+  // TODO(crbug.com/661636): Handle tab-switch animation somehow...
+  web_state_list_->InsertWebState(
+      tab_index,
+      session_util::CreateWebStateWithNavigationEntries(
+          browser_state_, selected_navigation, navigations),
+      WebStateList::INSERT_FORCE_INDEX | WebStateList::INSERT_ACTIVATE,
+      WebStateOpener());
+  return nullptr;
+}
+
+sessions::LiveTab* LiveTabContextBrowserAgent::ReplaceRestoredTab(
+    const std::vector<sessions::SerializedNavigationEntry>& navigations,
+    base::Optional<tab_groups::TabGroupId> group,
+    int selected_navigation,
+    bool from_last_session,
+    const std::string& extension_app_id,
+    const sessions::PlatformSpecificTabData* tab_platform_data,
+    const std::string& user_agent_override) {
+  web_state_list_->ReplaceWebStateAt(
+      web_state_list_->active_index(),
+      session_util::CreateWebStateWithNavigationEntries(
+          browser_state_, selected_navigation, navigations));
+
+  return nullptr;
+}
+
+void LiveTabContextBrowserAgent::CloseTab() {
+  web_state_list_->CloseWebStateAt(web_state_list_->active_index(),
+                                   WebStateList::CLOSE_USER_ACTION);
+}
diff --git a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.mm b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.mm
deleted file mode 100644
index a3eee23..0000000
--- a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.mm
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/logging.h"
-#include "base/optional.h"
-#include "base/strings/sys_string_conversions.h"
-#include "components/sessions/core/session_types.h"
-#include "components/tab_groups/tab_group_id.h"
-#include "components/tab_groups/tab_group_visual_data.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/sessions/session_util.h"
-#import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/browser/tabs/tab_model_list.h"
-#import "ios/chrome/browser/web_state_list/web_state_list.h"
-#import "ios/chrome/browser/web_state_list/web_state_opener.h"
-#import "ios/web/public/web_state.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-TabRestoreServiceDelegateImplIOS::TabRestoreServiceDelegateImplIOS(
-    ChromeBrowserState* browser_state)
-    : browser_state_(browser_state), session_id_(SessionID::NewUnique()) {}
-
-TabRestoreServiceDelegateImplIOS::~TabRestoreServiceDelegateImplIOS() {}
-
-WebStateList* TabRestoreServiceDelegateImplIOS::GetWebStateList() const {
-  TabModel* tab_model =
-      TabModelList::GetLastActiveTabModelForChromeBrowserState(browser_state_);
-  DCHECK([tab_model webStateList]);
-  return [tab_model webStateList];
-}
-
-void TabRestoreServiceDelegateImplIOS::ShowBrowserWindow() {
-  // No need to do anything here, as the singleton browser "window" is already
-  // shown.
-}
-
-SessionID TabRestoreServiceDelegateImplIOS::GetSessionID() const {
-  return session_id_;
-}
-
-int TabRestoreServiceDelegateImplIOS::GetTabCount() const {
-  return GetWebStateList()->count();
-}
-
-int TabRestoreServiceDelegateImplIOS::GetSelectedIndex() const {
-  return GetWebStateList()->active_index();
-}
-
-std::string TabRestoreServiceDelegateImplIOS::GetAppName() const {
-  return std::string();
-}
-
-sessions::LiveTab* TabRestoreServiceDelegateImplIOS::GetLiveTabAt(
-    int index) const {
-  return nullptr;
-}
-
-sessions::LiveTab* TabRestoreServiceDelegateImplIOS::GetActiveLiveTab() const {
-  return nullptr;
-}
-
-bool TabRestoreServiceDelegateImplIOS::IsTabPinned(int index) const {
-  // Not supported by iOS.
-  return false;
-}
-
-base::Optional<tab_groups::TabGroupId>
-TabRestoreServiceDelegateImplIOS::GetTabGroupForTab(int index) const {
-  // Not supported by iOS.
-  return base::nullopt;
-}
-
-const tab_groups::TabGroupVisualData*
-TabRestoreServiceDelegateImplIOS::GetVisualDataForGroup(
-    const tab_groups::TabGroupId& group) const {
-  // Since we never return a group from GetTabGroupForTab(), this should never
-  // be called.
-  NOTREACHED();
-  return nullptr;
-}
-
-void TabRestoreServiceDelegateImplIOS::SetVisualDataForGroup(
-    const tab_groups::TabGroupId& group,
-    const tab_groups::TabGroupVisualData& visual_data) {
-  // Not supported on iOS.
-}
-
-const gfx::Rect TabRestoreServiceDelegateImplIOS::GetRestoredBounds() const {
-  // Not supported by iOS.
-  return gfx::Rect();
-}
-
-ui::WindowShowState TabRestoreServiceDelegateImplIOS::GetRestoredState() const {
-  // Not supported by iOS.
-  return ui::SHOW_STATE_NORMAL;
-}
-
-std::string TabRestoreServiceDelegateImplIOS::GetWorkspace() const {
-  // Not supported by iOS.
-  return std::string();
-}
-
-sessions::LiveTab* TabRestoreServiceDelegateImplIOS::AddRestoredTab(
-    const std::vector<sessions::SerializedNavigationEntry>& navigations,
-    int tab_index,
-    int selected_navigation,
-    const std::string& extension_app_id,
-    base::Optional<tab_groups::TabGroupId> group,
-    const tab_groups::TabGroupVisualData& group_visual_data,
-    bool select,
-    bool pin,
-    bool from_last_session,
-    const sessions::PlatformSpecificTabData* tab_platform_data,
-    const std::string& user_agent_override) {
-  // TODO(crbug.com/661636): Handle tab-switch animation somehow...
-  WebStateList* web_state_list = GetWebStateList();
-  web_state_list->InsertWebState(
-      tab_index,
-      session_util::CreateWebStateWithNavigationEntries(
-          browser_state_, selected_navigation, navigations),
-      WebStateList::INSERT_FORCE_INDEX | WebStateList::INSERT_ACTIVATE,
-      WebStateOpener());
-  return nullptr;
-}
-
-sessions::LiveTab* TabRestoreServiceDelegateImplIOS::ReplaceRestoredTab(
-    const std::vector<sessions::SerializedNavigationEntry>& navigations,
-    base::Optional<tab_groups::TabGroupId> group,
-    int selected_navigation,
-    bool from_last_session,
-    const std::string& extension_app_id,
-    const sessions::PlatformSpecificTabData* tab_platform_data,
-    const std::string& user_agent_override) {
-  WebStateList* web_state_list = GetWebStateList();
-  web_state_list->ReplaceWebStateAt(
-      web_state_list->active_index(),
-      session_util::CreateWebStateWithNavigationEntries(
-          browser_state_, selected_navigation, navigations));
-
-  return nullptr;
-}
-
-void TabRestoreServiceDelegateImplIOS::CloseTab() {
-  WebStateList* web_state_list = GetWebStateList();
-  web_state_list->CloseWebStateAt(web_state_list->active_index(),
-                                  WebStateList::CLOSE_USER_ACTION);
-}
diff --git a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h
deleted file mode 100644
index 4f38c54..0000000
--- a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_DELEGATE_IMPL_IOS_FACTORY_H_
-#define IOS_CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_DELEGATE_IMPL_IOS_FACTORY_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/no_destructor.h"
-#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-class ChromeBrowserState;
-class TabRestoreServiceDelegateImplIOS;
-
-class TabRestoreServiceDelegateImplIOSFactory
-    : public BrowserStateKeyedServiceFactory {
- public:
-  static TabRestoreServiceDelegateImplIOS* GetForBrowserState(
-      ChromeBrowserState* browser_state);
-
-  static TabRestoreServiceDelegateImplIOSFactory* GetInstance();
-
- private:
-  friend class base::NoDestructor<TabRestoreServiceDelegateImplIOSFactory>;
-
-  TabRestoreServiceDelegateImplIOSFactory();
-  ~TabRestoreServiceDelegateImplIOSFactory() override;
-
-  // BrowserStateKeyedServiceFactory implementation.
-  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
-      web::BrowserState* context) const override;
-
-  DISALLOW_COPY_AND_ASSIGN(TabRestoreServiceDelegateImplIOSFactory);
-};
-
-#endif  // IOS_CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_DELEGATE_IMPL_IOS_FACTORY_H_
diff --git a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.mm b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.mm
deleted file mode 100644
index 1bfe79f..0000000
--- a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.mm
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h"
-
-#include <memory>
-
-#include "base/no_destructor.h"
-#include "components/keyed_service/ios/browser_state_dependency_manager.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-// static
-TabRestoreServiceDelegateImplIOS*
-TabRestoreServiceDelegateImplIOSFactory::GetForBrowserState(
-    ChromeBrowserState* browser_state) {
-  return static_cast<TabRestoreServiceDelegateImplIOS*>(
-      GetInstance()->GetServiceForBrowserState(browser_state, true));
-}
-
-// static
-TabRestoreServiceDelegateImplIOSFactory*
-TabRestoreServiceDelegateImplIOSFactory::GetInstance() {
-  static base::NoDestructor<TabRestoreServiceDelegateImplIOSFactory> instance;
-  return instance.get();
-}
-
-TabRestoreServiceDelegateImplIOSFactory::
-    TabRestoreServiceDelegateImplIOSFactory()
-    : BrowserStateKeyedServiceFactory(
-          "TabRestoreServiceDelegateImplIOS",
-          BrowserStateDependencyManager::GetInstance()) {}
-
-TabRestoreServiceDelegateImplIOSFactory::
-    ~TabRestoreServiceDelegateImplIOSFactory() {}
-
-std::unique_ptr<KeyedService>
-TabRestoreServiceDelegateImplIOSFactory::BuildServiceInstanceFor(
-    web::BrowserState* context) const {
-  return std::make_unique<TabRestoreServiceDelegateImplIOS>(
-      ChromeBrowserState::FromBrowserState(context));
-}
diff --git a/ios/chrome/browser/sync/profile_sync_service_factory.cc b/ios/chrome/browser/sync/profile_sync_service_factory.cc
index 488a2bed..e5457e6c 100644
--- a/ios/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/ios/chrome/browser/sync/profile_sync_service_factory.cc
@@ -34,7 +34,6 @@
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
-#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
 #include "ios/chrome/browser/signin/about_signin_internals_factory.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/chrome/browser/sync/consent_auditor_factory.h"
diff --git a/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/BUILD.gn b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/BUILD.gn
index 9be29c9..a3fd4159 100644
--- a/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/BUILD.gn
@@ -9,7 +9,34 @@
   sources = [
     "advanced_settings_signin_coordinator.h",
     "advanced_settings_signin_coordinator.mm",
+    "advanced_settings_signin_mediator.h",
+    "advanced_settings_signin_mediator.mm",
+    "advanced_settings_signin_navigation_controller.h",
+    "advanced_settings_signin_navigation_controller.mm",
   ]
   public_deps =
       [ "//ios/chrome/browser/ui/authentication/signin:signin_headers" ]
+  deps = [
+    ":constants",
+    "//components/sync",
+    "//components/unified_consent",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/signin",
+    "//ios/chrome/browser/sync",
+    "//ios/chrome/browser/ui/alert_coordinator",
+    "//ios/chrome/browser/ui/authentication/signin:signin_protected",
+    "//ios/chrome/browser/ui/settings:settings_controller_protocol",
+    "//ios/chrome/browser/ui/settings/google_services:constants",
+    "//ios/web/public",
+    "//ui/base",
+  ]
+}
+
+source_set("constants") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "advanced_settings_signin_constants.h",
+    "advanced_settings_signin_constants.mm",
+  ]
 }
diff --git a/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_constants.h b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_constants.h
new file mode 100644
index 0000000..9f7ad185
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_constants.h
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_ADVANCED_SETTINGS_SIGNIN_ADVANCED_SETTINGS_SIGNIN_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_ADVANCED_SETTINGS_SIGNIN_ADVANCED_SETTINGS_SIGNIN_CONSTANTS_H_
+
+#import <Foundation/Foundation.h>
+
+// The accessibility identifier for the navigation "Confirm" button.
+extern NSString* const kSyncSettingsConfirmButtonId;
+
+// The accessibility identifier for the navigation "Cancel" button.
+extern NSString* const kSyncSettingsCancelButtonId;
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_ADVANCED_SETTINGS_SIGNIN_ADVANCED_SETTINGS_SIGNIN_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_constants.mm b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_constants.mm
similarity index 79%
rename from ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_constants.mm
rename to ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_constants.mm
index cbda4646..4c66df3 100644
--- a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_constants.mm
+++ b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_constants.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_constants.h"
+#import "ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_constants.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_coordinator.mm
index e7fa086..5eef2a7ec 100644
--- a/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_coordinator.mm
@@ -4,20 +4,241 @@
 
 #import "ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_coordinator.h"
 
+#import "base/metrics/user_metrics.h"
+#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/signin/authentication_service.h"
+#import "ios/chrome/browser/signin/authentication_service_factory.h"
+#import "ios/chrome/browser/sync/profile_sync_service_factory.h"
+#import "ios/chrome/browser/sync/sync_setup_service.h"
+#import "ios/chrome/browser/sync/sync_setup_service_factory.h"
+#import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
+#import "ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_mediator.h"
+#import "ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_navigation_controller.h"
+#import "ios/chrome/browser/ui/authentication/signin/signin_coordinator+protected.h"
+#import "ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.h"
+#import "ios/chrome/browser/ui/settings/google_services/google_services_settings_mode.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/l10n/l10n_util_mac.h"
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+using base::RecordAction;
+using base::UserMetricsAction;
+using l10n_util::GetNSString;
+
+@interface AdvancedSettingsSigninCoordinator () <
+    AdvancedSettingsSigninNavigationControllerNavigationDelegate,
+    UIAdaptivePresentationControllerDelegate>
+
+// Advanced settings sign-in mediator.
+@property(nonatomic, strong)
+    AdvancedSettingsSigninMediator* advancedSettingsSigninMediator;
+// View controller presented by this coordinator.
+@property(nonatomic, strong) AdvancedSettingsSigninNavigationController*
+    advancedSettingsSigninNavigationController;
+// Google services settings coordinator.
+@property(nonatomic, strong)
+    GoogleServicesSettingsCoordinator* googleServicesSettingsCoordinator;
+// Confirm cancel sign-in/sync dialog.
+@property(nonatomic, strong)
+    AlertCoordinator* cancelConfirmationAlertCoordinator;
+
+@end
+
 @implementation AdvancedSettingsSigninCoordinator
 
-- (instancetype)initWithBaseViewController:(UIViewController*)viewController
-                                   browser:(Browser*)browser {
-  self = [super initWithBaseViewController:viewController browser:browser];
-  if (self) {
-    // TODO(crbug.com/971989): Needs implementation.
-    NOTIMPLEMENTED();
+#pragma mark - SigninCoordinator
+
+- (void)start {
+  [super start];
+  self.advancedSettingsSigninNavigationController =
+      [[AdvancedSettingsSigninNavigationController alloc] init];
+  self.advancedSettingsSigninNavigationController.modalPresentationStyle =
+      UIModalPresentationFormSheet;
+  self.advancedSettingsSigninNavigationController.navigationDelegate = self;
+
+  // Init and start Google settings coordinator.
+  GoogleServicesSettingsMode mode =
+      GoogleServicesSettingsModeAdvancedSigninSettings;
+  self.googleServicesSettingsCoordinator = [[GoogleServicesSettingsCoordinator
+      alloc]
+      initWithBaseViewController:self.advancedSettingsSigninNavigationController
+                         browser:self.browser
+                            mode:mode];
+  self.googleServicesSettingsCoordinator.navigationController =
+      self.advancedSettingsSigninNavigationController;
+  [self.googleServicesSettingsCoordinator start];
+
+  // Create the mediator.
+  SyncSetupService* syncSetupService =
+      SyncSetupServiceFactory::GetForBrowserState(self.browserState);
+  AuthenticationService* authenticationService =
+      AuthenticationServiceFactory::GetForBrowserState(self.browserState);
+  syncer::SyncService* syncService =
+      ProfileSyncServiceFactory::GetForBrowserState(self.browserState);
+  self.advancedSettingsSigninMediator = [[AdvancedSettingsSigninMediator alloc]
+      initWithSyncSetupService:syncSetupService
+         authenticationService:authenticationService
+                   syncService:syncService
+                   prefService:self.browserState->GetPrefs()];
+  self.advancedSettingsSigninNavigationController.presentationController
+      .delegate = self;
+
+  // Present the navigation controller that now contains the Google settings
+  // view controller.
+  [self.baseViewController
+      presentViewController:self.advancedSettingsSigninNavigationController
+                   animated:YES
+                 completion:nil];
+}
+
+- (void)interruptWithAction:(SigninCoordinatorInterruptAction)action
+                 completion:(ProceduralBlock)completion {
+  DCHECK(self.advancedSettingsSigninNavigationController);
+  [self.googleServicesSettingsCoordinator stop];
+  self.googleServicesSettingsCoordinator = nil;
+  switch (action) {
+    case SigninCoordinatorInterruptActionNoDismiss:
+      [self finishedWithSigninResult:SigninCoordinatorResultInterrupted];
+      if (completion) {
+        completion();
+      }
+      break;
+    case SigninCoordinatorInterruptActionDismissWithoutAnimation:
+      [self dismissViewControllerAndFinishWithResult:
+                SigninCoordinatorResultInterrupted
+                                            animated:NO
+                                          completion:completion];
+      break;
+    case SigninCoordinatorInterruptActionDismissWithAnimation:
+      [self dismissViewControllerAndFinishWithResult:
+                SigninCoordinatorResultInterrupted
+                                            animated:YES
+                                          completion:completion];
+      break;
   }
-  return self;
+}
+
+- (BOOL)isSettingsViewPresented {
+  // TODO(crbug.com/971989): Remove this method.
+  return YES;
+}
+
+#pragma mark - Private
+
+// Called when a button of |self.cancelConfirmationAlertCoordinator| is pressed.
+- (void)cancelConfirmationWithShouldCancelSignin:(BOOL)shouldCancelSignin {
+  DCHECK(self.cancelConfirmationAlertCoordinator);
+  [self.cancelConfirmationAlertCoordinator stop];
+  self.cancelConfirmationAlertCoordinator = nil;
+  if (shouldCancelSignin) {
+    [self dismissViewControllerAndFinishWithResult:
+              SigninCoordinatorResultCanceledByUser
+                                          animated:YES
+                                        completion:nil];
+  } else {
+    RecordAction(
+        UserMetricsAction("Signin_Signin_CancelCancelAdvancedSyncSettings"));
+  }
+}
+
+// Dismisses the current view controller with |animated|, triggers the
+// coordinator cleanup and then calls |completion|.
+- (void)dismissViewControllerAndFinishWithResult:(SigninCoordinatorResult)result
+                                        animated:(BOOL)animated
+                                      completion:(ProceduralBlock)completion {
+  DCHECK_EQ(self.advancedSettingsSigninNavigationController,
+            self.baseViewController.presentedViewController);
+  __weak __typeof(self) weakSelf = self;
+  ProceduralBlock dismissCompletion = ^() {
+    [weakSelf finishedWithSigninResult:result];
+    if (completion) {
+      completion();
+    }
+  };
+  [self.baseViewController dismissViewControllerAnimated:animated
+                                              completion:dismissCompletion];
+}
+
+// Does the cleanup once the view has been dismissed, calls the metrics and
+// calls |runCompletionCallbackWithSigninResult:identity:| to finish the
+// sign-in.
+- (void)finishedWithSigninResult:(SigninCoordinatorResult)signinResult {
+  DCHECK(self.advancedSettingsSigninNavigationController);
+  DCHECK(self.advancedSettingsSigninMediator);
+  [self.advancedSettingsSigninMediator
+      saveUserPreferenceForSigninResult:signinResult];
+  self.advancedSettingsSigninNavigationController = nil;
+  self.advancedSettingsSigninMediator = nil;
+  [self.googleServicesSettingsCoordinator stop];
+  self.googleServicesSettingsCoordinator = nil;
+  SyncSetupService* syncSetupService =
+      SyncSetupServiceFactory::GetForBrowserState(self.browserState);
+  DCHECK(!syncSetupService->HasUncommittedChanges())
+      << "-[GoogleServicesSettingsCoordinator stop] should commit sync "
+         "changes.";
+  AuthenticationService* authService =
+      AuthenticationServiceFactory::GetForBrowserState(self.browserState);
+  ChromeIdentity* identity = authService->GetAuthenticatedIdentity();
+  [self runCompletionCallbackWithSigninResult:signinResult identity:identity];
+}
+
+- (void)showCancelConfirmationAlert {
+  DCHECK(!self.cancelConfirmationAlertCoordinator);
+  RecordAction(UserMetricsAction("Signin_Signin_CancelAdvancedSyncSettings"));
+  self.cancelConfirmationAlertCoordinator = [[AlertCoordinator alloc]
+      initWithBaseViewController:self.advancedSettingsSigninNavigationController
+                           title:
+                               GetNSString(
+                                   IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CANCEL_SYNC_ALERT_TITLE)
+                         message:
+                             GetNSString(
+                                 IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CANCEL_SYNC_ALERT_MESSAGE)];
+  __weak __typeof(self) weakSelf = self;
+  [self.cancelConfirmationAlertCoordinator
+      addItemWithTitle:
+          GetNSString(
+              IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CANCEL_SYNC_ALERT_BACK_BUTTON)
+                action:^{
+                  [weakSelf cancelConfirmationWithShouldCancelSignin:NO];
+                }
+                 style:UIAlertActionStyleCancel];
+  [self.cancelConfirmationAlertCoordinator
+      addItemWithTitle:
+          GetNSString(
+              IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CANCEL_SYNC_ALERT_CANCEL_SYNC_BUTTON)
+                action:^{
+                  [weakSelf cancelConfirmationWithShouldCancelSignin:YES];
+                }
+                 style:UIAlertActionStyleDefault];
+  [self.cancelConfirmationAlertCoordinator start];
+}
+
+#pragma mark - AdvancedSettingsSigninNavigationControllerNavigationDelegate
+
+- (void)navigationCancelButtonWasTapped {
+  [self showCancelConfirmationAlert];
+}
+
+- (void)navigationConfirmButtonWasTapped {
+  DCHECK(!self.cancelConfirmationAlertCoordinator);
+  [self dismissViewControllerAndFinishWithResult:SigninCoordinatorResultSuccess
+                                        animated:YES
+                                      completion:nil];
+}
+
+#pragma mark - UIAdaptivePresentationControllerDelegate
+
+- (BOOL)presentationControllerShouldDismiss:
+    (UIPresentationController*)presentationController {
+  return NO;
+}
+
+- (void)presentationControllerDidAttemptToDismiss:
+    (UIPresentationController*)presentationController {
+  [self showCancelConfirmationAlert];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_mediator.h b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_mediator.h
new file mode 100644
index 0000000..74713f5
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_mediator.h
@@ -0,0 +1,38 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_ADVANCED_SETTINGS_SIGNIN_ADVANCED_SETTINGS_SIGNIN_MEDIATOR_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_ADVANCED_SETTINGS_SIGNIN_ADVANCED_SETTINGS_SIGNIN_MEDIATOR_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/authentication/signin/signin_enums.h"
+
+@class AdvancedSettingsSigninMediator;
+@class AdvancedSettingsSigninNavigationController;
+class AuthenticationService;
+class PrefService;
+class SyncSetupService;
+namespace syncer {
+class SyncService;
+}
+
+// Mediator for the advanced settings sign-in flow.
+@interface AdvancedSettingsSigninMediator : NSObject
+
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithSyncSetupService:(SyncSetupService*)syncSetupService
+                   authenticationService:
+                       (AuthenticationService*)authenticationService
+                             syncService:(syncer::SyncService*)syncService
+                             prefService:(PrefService*)prefService
+    NS_DESIGNATED_INITIALIZER;
+
+// Saves the user sync preferences.
+- (void)saveUserPreferenceForSigninResult:(SigninCoordinatorResult)signinResult;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_ADVANCED_SETTINGS_SIGNIN_ADVANCED_SETTINGS_SIGNIN_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_mediator.mm b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_mediator.mm
new file mode 100644
index 0000000..5d667daa
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_mediator.mm
@@ -0,0 +1,89 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_mediator.h"
+
+#import "base/metrics/user_metrics.h"
+#import "components/sync/driver/sync_service.h"
+#import "components/unified_consent/unified_consent_metrics.h"
+#import "ios/chrome/browser/signin/authentication_service.h"
+#import "ios/chrome/browser/sync/sync_setup_service.h"
+#import "ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_navigation_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using base::RecordAction;
+using base::UserMetricsAction;
+using syncer::SyncFirstSetupCompleteSource;
+using unified_consent::metrics::RecordSyncSetupDataTypesHistrogam;
+
+@interface AdvancedSettingsSigninMediator ()
+
+@property(nonatomic, assign, readonly) SyncSetupService* syncSetupService;
+@property(nonatomic, assign, readonly)
+    AuthenticationService* authenticationService;
+@property(nonatomic, assign, readonly) syncer::SyncService* syncService;
+// Browser state preference service.
+@property(nonatomic, assign, readonly) PrefService* prefService;
+
+@end
+
+@implementation AdvancedSettingsSigninMediator
+
+#pragma mark - Public
+
+- (instancetype)initWithSyncSetupService:(SyncSetupService*)syncSetupService
+                   authenticationService:
+                       (AuthenticationService*)authenticationService
+                             syncService:(syncer::SyncService*)syncService
+                             prefService:(PrefService*)prefService {
+  self = [super init];
+  if (self) {
+    DCHECK(syncSetupService);
+    DCHECK(authenticationService);
+    DCHECK(syncService);
+    DCHECK(prefService);
+    _syncSetupService = syncSetupService;
+    _authenticationService = authenticationService;
+    _syncService = syncService;
+    _prefService = prefService;
+  }
+  return self;
+}
+
+- (void)saveUserPreferenceForSigninResult:
+    (SigninCoordinatorResult)signinResult {
+  switch (signinResult) {
+    case SigninCoordinatorResultSuccess: {
+      RecordAction(
+          UserMetricsAction("Signin_Signin_ConfirmAdvancedSyncSettings"));
+      RecordSyncSetupDataTypesHistrogam(self.syncService->GetUserSettings(),
+                                        self.prefService);
+      if (self.syncSetupService->IsSyncEnabled()) {
+        // FirstSetupComplete flag should be only turned on when the user agrees
+        // to start Sync.
+        self.syncSetupService->PrepareForFirstSyncSetup();
+        self.syncSetupService->SetFirstSetupComplete(
+            SyncFirstSetupCompleteSource::ADVANCED_FLOW_CONFIRM);
+      }
+      break;
+    }
+    case SigninCoordinatorResultCanceledByUser:
+      RecordAction(
+          UserMetricsAction("Signin_Signin_ConfirmCancelAdvancedSyncSettings"));
+      self.syncSetupService->CommitSyncChanges();
+      self.authenticationService->SignOut(signin_metrics::ABORT_SIGNIN,
+                                          /*force_clear_browsing_data=*/false,
+                                          nil);
+      break;
+    case SigninCoordinatorResultInterrupted:
+      RecordAction(
+          UserMetricsAction("Signin_Signin_AbortAdvancedSyncSettings"));
+      break;
+  }
+}
+
+@end
diff --git a/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_navigation_controller.h b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_navigation_controller.h
new file mode 100644
index 0000000..dde8bf7
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_navigation_controller.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_ADVANCED_SETTINGS_SIGNIN_ADVANCED_SETTINGS_SIGNIN_NAVIGATION_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_ADVANCED_SETTINGS_SIGNIN_ADVANCED_SETTINGS_SIGNIN_NAVIGATION_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+// Delegate for AdvancedSettingsSigninNavigationController to receive navigation
+// button events.
+@protocol
+    AdvancedSettingsSigninNavigationControllerNavigationDelegate <NSObject>
+
+// Called when the navigation canceled button was tapped.
+- (void)navigationCancelButtonWasTapped;
+
+// Called when the navigation confirm button was tapped.
+- (void)navigationConfirmButtonWasTapped;
+
+@end
+
+// View controller to present the Google services settings.
+@interface AdvancedSettingsSigninNavigationController : UINavigationController
+
+@property(nonatomic, weak)
+    id<AdvancedSettingsSigninNavigationControllerNavigationDelegate>
+        navigationDelegate;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_ADVANCED_SETTINGS_SIGNIN_ADVANCED_SETTINGS_SIGNIN_NAVIGATION_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_navigation_controller.mm b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_navigation_controller.mm
new file mode 100644
index 0000000..a3e5524
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_navigation_controller.mm
@@ -0,0 +1,83 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_navigation_controller.h"
+
+#import "ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_constants.h"
+#import "ios/chrome/browser/ui/settings/settings_controller_protocol.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/l10n/l10n_util_mac.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using l10n_util::GetNSString;
+
+@implementation AdvancedSettingsSigninNavigationController
+
+#pragma mark - UINavigationController
+
+// Calls |viewControllerWasPopped| for the popped view controller.
+- (UIViewController*)popViewControllerAnimated:(BOOL)animated {
+  UIViewController* poppedViewController =
+      [super popViewControllerAnimated:animated];
+  if ([poppedViewController
+          respondsToSelector:@selector(viewControllerWasPopped)]) {
+    [poppedViewController performSelector:@selector(viewControllerWasPopped)];
+  }
+  return poppedViewController;
+}
+
+- (void)pushViewController:(UIViewController*)viewController
+                  animated:(BOOL)animated {
+  [super pushViewController:viewController animated:animated];
+  if (self.viewControllers.count == 1) {
+    viewController.navigationItem.leftBarButtonItem =
+        [self navigationCancelButton];
+    viewController.navigationItem.rightBarButtonItem =
+        [self navigationConfirmButton];
+  }
+}
+
+#pragma mark - Private
+
+// Creates a cancel button for the navigation item.
+- (UIBarButtonItem*)navigationCancelButton {
+  UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc]
+      initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
+                           target:self
+                           action:@selector(onNavigationCancelButton)];
+  cancelButton.accessibilityIdentifier = kSyncSettingsCancelButtonId;
+  return cancelButton;
+}
+
+// Creates a confirmation button for the navigation item.
+- (UIBarButtonItem*)navigationConfirmButton {
+  UIBarButtonItem* confirmButton = [[UIBarButtonItem alloc]
+      initWithTitle:GetNSString(
+                        IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CONFIRM_MAIN_BUTTON)
+              style:UIBarButtonItemStyleDone
+             target:self
+             action:@selector(onNavigationConfirmButton)];
+  confirmButton.accessibilityIdentifier = kSyncSettingsConfirmButtonId;
+  return confirmButton;
+}
+
+#pragma mark - Button events
+
+// Called by the cancel button from the navigation controller. Shows the cancel
+// alert dialog.
+- (void)onNavigationCancelButton {
+  [self.navigationDelegate navigationCancelButtonWasTapped];
+}
+
+// Called by the confirm button from tne navigation controller. Validates the
+// sync preferences chosen by the user, starts the sync, close the completion
+// callback and closes the advanced sign-in settings.
+- (void)onNavigationConfirmButton {
+  [self.navigationDelegate navigationConfirmButtonWasTapped];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_coordinator.h b/ios/chrome/browser/ui/authentication/signin/signin_coordinator.h
index 8699b24..99bf9a2 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_coordinator.h
+++ b/ios/chrome/browser/ui/authentication/signin/signin_coordinator.h
@@ -32,6 +32,12 @@
 // This completion needs to be set before calling -[SigninCoordinator start].
 @property(nonatomic, copy) SigninCoordinatorCompletionCallback signinCompletion;
 
+// Returns YES if the Google services settings view is presented.
+// TODO(crbug.com/971989): This property exists for the implementation
+// transition.
+@property(nonatomic, assign, readonly, getter=isSettingsViewPresented)
+    BOOL settingsViewPresented;
+
 // Returns a coordinator for user sign-in workflow.
 // |viewController| presents the sign-in.
 // |identity| is the identity preselected with the sign-in opens.
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/signin_coordinator.mm
index 5cc26d0..4c4b11c3 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/signin_coordinator.mm
@@ -118,6 +118,13 @@
   DCHECK(!self.signinCompletion);
 }
 
+#pragma mark - Properties
+
+- (BOOL)isSettingsViewPresented {
+  // TODO(crbug.com/971989): Remove this method.
+  return NO;
+}
+
 #pragma mark - Private
 
 - (void)runCompletionCallbackWithSigninResult:
diff --git a/ios/chrome/browser/ui/authentication/signin/user_signin/BUILD.gn b/ios/chrome/browser/ui/authentication/signin/user_signin/BUILD.gn
index aff9e18..453d0fb 100644
--- a/ios/chrome/browser/ui/authentication/signin/user_signin/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/signin/user_signin/BUILD.gn
@@ -7,6 +7,8 @@
 source_set("user_signin") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
+    "gradient_view.h",
+    "gradient_view.mm",
     "user_signin_coordinator.h",
     "user_signin_coordinator.mm",
     "user_signin_mediator.h",
diff --git a/ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.h b/ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.h
new file mode 100644
index 0000000..984c1f0
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.h
@@ -0,0 +1,21 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_USER_SIGNIN_GRADIENT_VIEW_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_USER_SIGNIN_GRADIENT_VIEW_H_
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+// A UIView showing a vertical gradient from transparent to opaque using the
+// system background color.
+@interface GradientView : UIView
+
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
+- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_USER_SIGNIN_GRADIENT_VIEW_H_
diff --git a/ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.mm b/ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.mm
new file mode 100644
index 0000000..831d0d94
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.mm
@@ -0,0 +1,62 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.h"
+
+#import "base/mac/foundation_util.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation GradientView
+
+#pragma mark - Public
+
++ (Class)layerClass {
+  return [CAGradientLayer class];
+}
+
+- (instancetype)init {
+  self = [super initWithFrame:CGRectZero];
+  if (self) {
+    self.userInteractionEnabled = NO;
+  }
+  return self;
+}
+
+- (CAGradientLayer*)gradientLayer {
+  return base::mac::ObjCCastStrict<CAGradientLayer>(self.layer);
+}
+
+- (void)traitCollectionDidChange:
+    (nullable UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  if (@available(iOS 13, *)) {
+    if ([self.traitCollection
+            hasDifferentColorAppearanceComparedToTraitCollection:
+                previousTraitCollection]) {
+      [self updateColors];
+    }
+  }
+}
+
+#pragma mark - Private
+
+- (void)updateColors {
+  [CATransaction begin];
+  // If this isn't set, the changes here are automatically animated. The other
+  // color changes for dark mode don't animate, however, so there ends up being
+  // visual desyncing.
+  [CATransaction setDisableActions:YES];
+
+  self.gradientLayer.colors = @[
+    (id)[UIColor.cr_systemBackgroundColor colorWithAlphaComponent:0].CGColor,
+    (id)UIColor.cr_systemBackgroundColor.CGColor
+  ];
+  [CATransaction commit];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm
index d666bea..47c2a13 100644
--- a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm
@@ -180,9 +180,9 @@
       ^(SigninCoordinatorResult signinResult, ChromeIdentity* identity) {
         if (signinResult == SigninCoordinatorResultSuccess) {
           weakSelf.unifiedConsentCoordinator.selectedIdentity = identity;
-          [weakSelf.addAccountSigninCoordinator stop];
-          weakSelf.addAccountSigninCoordinator = nil;
         }
+        [weakSelf.addAccountSigninCoordinator stop];
+        weakSelf.addAccountSigninCoordinator = nil;
       };
   [self.addAccountSigninCoordinator start];
 }
diff --git a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm
index e8ec4e4..e1f9406b 100644
--- a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.h"
 
 #import "base/logging.h"
+#import "ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.h"
 #import "ios/chrome/browser/ui/util/rtl_geometry.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
@@ -25,6 +26,7 @@
 
 // Layout constants for buttons.
 struct AuthenticationViewConstants {
+  CGFloat GradientHeight;
   CGFloat ButtonHeight;
   CGFloat ButtonHorizontalPadding;
   CGFloat ButtonTopPadding;
@@ -32,6 +34,7 @@
 };
 
 const AuthenticationViewConstants kCompactConstants = {
+    40,  // GradientHeight
     36,  // ButtonHeight
     16,  // ButtonHorizontalPadding
     16,  // ButtonTopPadding
@@ -39,6 +42,7 @@
 };
 
 const AuthenticationViewConstants kRegularConstants = {
+    kCompactConstants.GradientHeight,
     1.5 * kCompactConstants.ButtonHeight,
     32,  // ButtonHorizontalPadding
     32,  // ButtonTopPadding
@@ -66,6 +70,9 @@
 // Property that denotes whether the unified consent screen reached bottom has
 // triggered.
 @property(nonatomic, assign) BOOL hasUnifiedConsentScreenReachedBottom;
+// Gradient used to hide text that is close to the bottom of the screen. This
+// gives users the hint that there is more to scroll through.
+@property(nonatomic, strong) GradientView* gradientView;
 
 @end
 
@@ -126,28 +133,57 @@
 
   [self addConfirmationButtonToView];
   [self embedUserConsentView];
+  [self addActivityIndicatorToView];
   [self addSkipSigninButtonToView];
 
+  [self.view addSubview:self.gradientView];
+
   // The layout constraints should be added at the end once all of the views
   // have been created.
   AuthenticationViewConstants constants = self.authenticationViewConstants;
+
+  // Embedded view constraints.
   AddSameConstraintsWithInsets(
       self.unifiedConsentViewController.view, self.view,
       ChromeDirectionalEdgeInsetsMake(0, 0,
-                                      constants.ButtonHeight +
+                                      2 * constants.ButtonHeight +
                                           constants.ButtonBottomPadding +
                                           constants.ButtonTopPadding,
                                       0));
+
+  // Skip sign-in button constraints.
   AddSameConstraintsToSidesWithInsets(
       self.skipSigninButton, self.view,
       LayoutSides::kBottom | LayoutSides::kLeading,
-      ChromeDirectionalEdgeInsetsMake(0, constants.ButtonHorizontalPadding,
-                                      constants.ButtonBottomPadding, 0));
+      ChromeDirectionalEdgeInsetsMake(
+          0, constants.ButtonHorizontalPadding,
+          constants.ButtonBottomPadding + constants.ButtonHeight, 0));
+
+  // Activity indicator constraints.
+  AddSameCenterConstraints(self.view, self.activityIndicator);
+
+  // Confirmation button constraints.
   AddSameConstraintsToSidesWithInsets(
       self.confirmationButton, self.view,
       LayoutSides::kBottom | LayoutSides::kTrailing,
-      ChromeDirectionalEdgeInsetsMake(0, 0, constants.ButtonBottomPadding,
-                                      constants.ButtonHorizontalPadding));
+      ChromeDirectionalEdgeInsetsMake(
+          0, 0, constants.ButtonBottomPadding + constants.ButtonHeight,
+          constants.ButtonHorizontalPadding));
+
+  // Gradient layer constraints.
+  self.gradientView.translatesAutoresizingMaskIntoConstraints = NO;
+  [NSLayoutConstraint activateConstraints:@[
+    [self.gradientView.bottomAnchor
+        constraintEqualToAnchor:self.unifiedConsentViewController.view
+                                    .bottomAnchor],
+    [self.gradientView.leadingAnchor
+        constraintEqualToAnchor:self.view.leadingAnchor],
+    [self.gradientView.trailingAnchor
+        constraintEqualToAnchor:self.view.trailingAnchor],
+
+    [self.gradientView.heightAnchor
+        constraintEqualToConstant:constants.GradientHeight],
+  ]];
 }
 
 #pragma mark - Properties
@@ -181,6 +217,16 @@
   return isRegularSizeClass ? kRegularConstants : kCompactConstants;
 }
 
+// Sets up gradient that masks text when the iOS device size is a compact size.
+// This is to hint to the user that there is additional text below the end of
+// the screen.
+- (UIView*)gradientView {
+  if (!_gradientView) {
+    _gradientView = [[GradientView alloc] init];
+  }
+  return _gradientView;
+}
+
 #pragma mark - Subviews
 
 // Sets up activity indicator properties and adds it to the view.
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 75ccad7..fbe077cc 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -3058,6 +3058,7 @@
 
   _contextMenuCoordinator = [[ContextMenuCoordinator alloc]
       initWithBaseViewController:self
+                         browser:self.browser
                            title:params.menu_title
                           inView:params.view
                       atLocation:params.location];
@@ -3875,11 +3876,12 @@
   const std::unique_ptr<sessions::TabRestoreService::Entry>& entry =
       tabRestoreService->entries().front();
   // Only handle the TAB type.
+  // TODO(crbug.com/1056596) : Support WINDOW restoration under multi-window.
   if (entry->type != sessions::TabRestoreService::TAB)
     return;
 
   [self.dispatcher openURLInNewTab:[OpenNewTabCommand command]];
-  RestoreTab(entry->id, WindowOpenDisposition::CURRENT_TAB, self.browserState);
+  RestoreTab(entry->id, WindowOpenDisposition::CURRENT_TAB, self.browser);
 }
 
 #pragma mark - MainContentUI
@@ -3912,7 +3914,13 @@
   [[OmniboxGeolocationController sharedInstance]
       locationBarDidBecomeFirstResponder:self.browserState];
 
-  [self.primaryToolbarCoordinator transitionToLocationBarFocusedState:YES];
+  // On the non-incognito NTP, the primaryToolbarCoordinator focuses the
+  // location bar itself when focusing the fakebox (while animating the
+  // fakebox). On all other pages, the fakebox is focused here after the
+  // location bar becomes first responder.
+  if (!IsVisibleURLNewTabPage(self.currentWebState) || self.isOffTheRecord) {
+    [self.primaryToolbarCoordinator transitionToLocationBarFocusedState:YES];
+  }
 }
 
 - (void)locationBarDidResignFirstResponder {
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
index 91c8406..1603476 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
@@ -450,12 +450,19 @@
   if (self.disableScrollAnimation)
     return;
 
+  // Focus the location bar if the omnibox is not already focused.
+  if (!self.omniboxFocused) {
+    // Only animated the location bar focus if the header is scrolled to the
+    // top.
+    BOOL animated = [self.delegate isScrolledToTop];
+    [self.dispatcher focusLocationBarWithAnimation:animated];
+  }
+
   void (^animations)() = nil;
   if (![self.delegate isScrolledToTop]) {
     // Only trigger the fake omnibox animation if the header isn't scrolled to
     // the top. Otherwise just rely on the normal animation.
     self.disableScrollAnimation = YES;
-    [self.dispatcher focusOmniboxNoAnimation];
     NamedGuide* omniboxGuide = [NamedGuide guideWithName:kOmniboxGuide
                                                     view:self.headerView];
     // Layout the owning view to make sure that the constrains are applied.
diff --git a/ios/chrome/browser/ui/context_menu/BUILD.gn b/ios/chrome/browser/ui/context_menu/BUILD.gn
index d45a02a..d02973ff 100644
--- a/ios/chrome/browser/ui/context_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/context_menu/BUILD.gn
@@ -25,6 +25,8 @@
   deps = [
     ":context_menu",
     "//base",
+    "//base/test:test_support",
+    "//ios/chrome/browser/main:test_support",
     "//testing/gtest",
   ]
 }
diff --git a/ios/chrome/browser/ui/context_menu/context_menu_coordinator.h b/ios/chrome/browser/ui/context_menu/context_menu_coordinator.h
index 165fe61f..2d11d71a 100644
--- a/ios/chrome/browser/ui/context_menu/context_menu_coordinator.h
+++ b/ios/chrome/browser/ui/context_menu/context_menu_coordinator.h
@@ -25,6 +25,7 @@
 // location, the context menu will originate from |location| in |view|.
 // Context menu will be presented from |viewController|.
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
                                      title:(NSString*)title
                                     inView:(UIView*)view
                                 atLocation:(CGPoint)location
diff --git a/ios/chrome/browser/ui/context_menu/context_menu_coordinator.mm b/ios/chrome/browser/ui/context_menu/context_menu_coordinator.mm
index fbf0a14..4a55d5c2 100644
--- a/ios/chrome/browser/ui/context_menu/context_menu_coordinator.mm
+++ b/ios/chrome/browser/ui/context_menu/context_menu_coordinator.mm
@@ -24,10 +24,11 @@
 @implementation ContextMenuCoordinator
 
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
                                      title:(NSString*)title
                                     inView:(UIView*)view
                                 atLocation:(CGPoint)location {
-  self = [super initWithBaseViewController:viewController browserState:nullptr];
+  self = [super initWithBaseViewController:viewController browser:browser];
   if (self) {
     _alertCoordinator = [[ActionSheetCoordinator alloc]
         initWithBaseViewController:viewController
diff --git a/ios/chrome/browser/ui/context_menu/context_menu_coordinator_unittest.mm b/ios/chrome/browser/ui/context_menu/context_menu_coordinator_unittest.mm
index ff9c7a5..4cc2fc1 100644
--- a/ios/chrome/browser/ui/context_menu/context_menu_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/context_menu/context_menu_coordinator_unittest.mm
@@ -7,6 +7,8 @@
 #import <UIKit/UIKit.h>
 
 #import "base/mac/foundation_util.h"
+#import "base/test/task_environment.h"
+#import "ios/chrome/browser/main/test_browser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
@@ -24,6 +26,7 @@
     [window_ makeKeyAndVisible];
     view_controller_ = [[UIViewController alloc] init];
     [window_ setRootViewController:view_controller_];
+    browser_ = std::make_unique<TestBrowser>();
   }
 
   ~ContextMenuCoordinatorTest() override {
@@ -31,16 +34,20 @@
   }
 
  protected:
+  base::test::TaskEnvironment task_environment_;
+
   UIWindow* previous_key_window_;
   ContextMenuCoordinator* menu_coordinator_;
   UIWindow* window_;
   UIViewController* view_controller_;
+  std::unique_ptr<Browser> browser_;
 };
 
 // Tests the context menu reports as visible after presenting.
 TEST_F(ContextMenuCoordinatorTest, ValidateIsVisible) {
   menu_coordinator_ = [[ContextMenuCoordinator alloc]
       initWithBaseViewController:view_controller_
+                         browser:browser_.get()
                            title:@"Context Menu"
                           inView:view_controller_.view
                       atLocation:CGPointZero];
@@ -52,6 +59,7 @@
 TEST_F(ContextMenuCoordinatorTest, ValidateDismissalOnStop) {
   menu_coordinator_ = [[ContextMenuCoordinator alloc]
       initWithBaseViewController:view_controller_
+                         browser:browser_.get()
                            title:nil
                           inView:view_controller_.view
                       atLocation:CGPointZero];
@@ -64,6 +72,7 @@
 TEST_F(ContextMenuCoordinatorTest, ValidateActions) {
   menu_coordinator_ = [[ContextMenuCoordinator alloc]
       initWithBaseViewController:view_controller_
+                         browser:browser_.get()
                            title:nil
                           inView:view_controller_.view
                       atLocation:CGPointZero];
@@ -98,6 +107,7 @@
 TEST_F(ContextMenuCoordinatorTest, CancelButtonExists) {
   menu_coordinator_ = [[ContextMenuCoordinator alloc]
       initWithBaseViewController:view_controller_
+                         browser:browser_.get()
                            title:nil
                           inView:view_controller_.view
                       atLocation:CGPointZero];
@@ -121,6 +131,7 @@
   NSString* title = @"Context Menu Title";
   menu_coordinator_ = [[ContextMenuCoordinator alloc]
       initWithBaseViewController:view_controller_
+                         browser:browser_.get()
                            title:title
                           inView:view_controller_.view
                       atLocation:location];
diff --git a/ios/chrome/browser/ui/history/BUILD.gn b/ios/chrome/browser/ui/history/BUILD.gn
index a8a1bff..ae9f0c5 100644
--- a/ios/chrome/browser/ui/history/BUILD.gn
+++ b/ios/chrome/browser/ui/history/BUILD.gn
@@ -81,6 +81,7 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/metrics:metrics_internal",
     "//ios/chrome/browser/sync",
     "//ios/chrome/browser/ui/collection_view/cells",
diff --git a/ios/chrome/browser/ui/history/history_coordinator.mm b/ios/chrome/browser/ui/history/history_coordinator.mm
index 8abcdfa..c5ca7a0 100644
--- a/ios/chrome/browser/ui/history/history_coordinator.mm
+++ b/ios/chrome/browser/ui/history/history_coordinator.mm
@@ -61,8 +61,7 @@
 - (void)start {
   // Initialize and configure HistoryTableViewController.
   self.historyTableViewController = [[HistoryTableViewController alloc] init];
-  self.historyTableViewController.browserState =
-      self.browser->GetBrowserState();
+  self.historyTableViewController.browser = self.browser;
   self.historyTableViewController.loadStrategy = self.loadStrategy;
 
   // Initialize and set HistoryMediator
diff --git a/ios/chrome/browser/ui/history/history_table_view_controller.h b/ios/chrome/browser/ui/history/history_table_view_controller.h
index f401ba9..965014d9 100644
--- a/ios/chrome/browser/ui/history/history_table_view_controller.h
+++ b/ios/chrome/browser/ui/history/history_table_view_controller.h
@@ -9,7 +9,7 @@
 
 #include "ios/chrome/browser/ui/history/history_consumer.h"
 
-class ChromeBrowserState;
+class Browser;
 enum class UrlLoadStrategy;
 
 @class ContextMenuCoordinator;
@@ -21,8 +21,8 @@
 @interface HistoryTableViewController
     : ChromeTableViewController <HistoryConsumer,
                                  UIAdaptivePresentationControllerDelegate>
-// The ViewController's BrowserState.
-@property(nonatomic, assign) ChromeBrowserState* browserState;
+// The ViewController's Browser.
+@property(nonatomic, assign) Browser* browser;
 // Abstraction to communicate with HistoryService and WebHistoryService.
 // Not owned by HistoryTableViewController.
 @property(nonatomic, assign) history::BrowsingHistoryService* historyService;
diff --git a/ios/chrome/browser/ui/history/history_table_view_controller.mm b/ios/chrome/browser/ui/history/history_table_view_controller.mm
index beb01f7..c3321ca 100644
--- a/ios/chrome/browser/ui/history/history_table_view_controller.mm
+++ b/ios/chrome/browser/ui/history/history_table_view_controller.mm
@@ -13,6 +13,7 @@
 #include "components/url_formatter/url_formatter.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/metrics/new_tab_page_uma.h"
 #include "ios/chrome/browser/sync/sync_setup_service.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
@@ -253,7 +254,7 @@
   // If history sync is enabled and there hasn't been a response from synced
   // history, try fetching again.
   SyncSetupService* syncSetupService =
-      SyncSetupServiceFactory::GetForBrowserState(_browserState);
+      SyncSetupServiceFactory::GetForBrowserState(_browser->GetBrowserState());
   if (syncSetupService->IsSyncEnabled() &&
       syncSetupService->IsDataTypeActive(syncer::HISTORY_DELETE_DIRECTIVES) &&
       queryResultsInfo.sync_timed_out) {
@@ -998,6 +999,7 @@
       base::SysUTF16ToNSString(url_formatter::FormatUrl(entry.URL));
   self.contextMenuCoordinator = [[ContextMenuCoordinator alloc]
       initWithBaseViewController:self.navigationController
+                         browser:_browser
                            title:menuTitle
                           inView:self.tableView
                       atLocation:touchLocation];
@@ -1037,7 +1039,8 @@
       base::UserMetricsAction("MobileHistoryPage_EntryLinkOpenNewTab"));
   UrlLoadParams params = UrlLoadParams::InNewTab(URL);
   [self.delegate dismissHistoryWithCompletion:^{
-    UrlLoadingServiceFactory::GetForBrowserState(_browserState)->Load(params);
+    UrlLoadingServiceFactory::GetForBrowserState(_browser->GetBrowserState())
+        ->Load(params);
     [self.presentationDelegate showActiveRegularTabFromHistory];
   }];
 }
@@ -1049,7 +1052,8 @@
   UrlLoadParams params = UrlLoadParams::InNewTab(URL);
   params.in_incognito = YES;
   [self.delegate dismissHistoryWithCompletion:^{
-    UrlLoadingServiceFactory::GetForBrowserState(_browserState)->Load(params);
+    UrlLoadingServiceFactory::GetForBrowserState(_browser->GetBrowserState())
+        ->Load(params);
     [self.presentationDelegate showActiveIncognitoTabFromHistory];
   }];
 }
@@ -1059,13 +1063,14 @@
 // Opens URL in the current tab and dismisses the history view.
 - (void)openURL:(const GURL&)URL {
   // TODO(crbug.com/1032550) : Update this call to pass in the current WebState.
-  new_tab_page_uma::RecordAction(_browserState,
+  new_tab_page_uma::RecordAction(_browser->GetBrowserState(),
                                  new_tab_page_uma::ACTION_OPENED_HISTORY_ENTRY);
   UrlLoadParams params = UrlLoadParams::InCurrentTab(URL);
   params.web_params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
   params.load_strategy = self.loadStrategy;
   [self.delegate dismissHistoryWithCompletion:^{
-    UrlLoadingServiceFactory::GetForBrowserState(_browserState)->Load(params);
+    UrlLoadingServiceFactory::GetForBrowserState(_browser->GetBrowserState())
+        ->Load(params);
     [self.presentationDelegate showActiveRegularTabFromHistory];
   }];
 }
diff --git a/ios/chrome/browser/ui/location_bar/BUILD.gn b/ios/chrome/browser/ui/location_bar/BUILD.gn
index 18bcb57..56797e6 100644
--- a/ios/chrome/browser/ui/location_bar/BUILD.gn
+++ b/ios/chrome/browser/ui/location_bar/BUILD.gn
@@ -51,6 +51,7 @@
     "//ios/chrome/browser/ui/badges",
     "//ios/chrome/browser/ui/badges:public",
     "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/elements",
     "//ios/chrome/browser/ui/fullscreen",
     "//ios/chrome/browser/ui/fullscreen:ui",
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h
index ed2e84a..fb32c215 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h
@@ -5,16 +5,12 @@
 #ifndef IOS_CHROME_BROWSER_UI_LOCATION_BAR_LOCATION_BAR_COORDINATOR_H_
 #define IOS_CHROME_BROWSER_UI_LOCATION_BAR_LOCATION_BAR_COORDINATOR_H_
 
-#import <UIKit/UIKit.h>
+#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
 
 #import "ios/chrome/browser/ui/location_bar/location_bar_url_loader.h"
 #import "ios/chrome/browser/ui/omnibox/location_bar_delegate.h"
 #import "ios/chrome/browser/ui/toolbar/public/omnibox_focuser.h"
 
-@class CommandDispatcher;
-@protocol ApplicationCommands;
-class Browser;
-@protocol BrowserCommands;
 @protocol EditViewAnimatee;
 @protocol LocationBarAnimatee;
 @protocol OmniboxPopupPresenterDelegate;
@@ -22,17 +18,20 @@
 
 // Location bar coordinator.
 @interface LocationBarCoordinator
-    : NSObject<LocationBarURLLoader, OmniboxFocuser>
+    : ChromeCoordinator <LocationBarURLLoader, OmniboxFocuser>
 
-// Command dispatcher.
-@property(nonatomic, strong) CommandDispatcher* commandDispatcher;
+// Unavailable, use -initWithBaseViewController:browser:.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+    NS_UNAVAILABLE;
+
+// Unavailable, use -initWithBaseViewController:browser:.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                              browserState:(ChromeBrowserState*)browserState
+    NS_UNAVAILABLE;
+
 // View controller containing the omnibox.
 @property(nonatomic, strong, readonly)
     UIViewController* locationBarViewController;
-// The location bar's Browser.
-@property(nonatomic, assign) Browser* browser;
-// The dispatcher for this view controller.
-@property(nonatomic, weak) CommandDispatcher* dispatcher;
 // Delegate for this coordinator.
 // TODO(crbug.com/799446): Change this.
 @property(nonatomic, weak) id<ToolbarCoordinatorDelegate> delegate;
@@ -40,11 +39,6 @@
 @property(nonatomic, weak) id<OmniboxPopupPresenterDelegate>
     popupPresenterDelegate;
 
-// Start this coordinator.
-- (void)start;
-// Stop this coordinator.
-- (void)stop;
-
 // Indicates whether the popup has results to show or not.
 - (BOOL)omniboxPopupHasAutocompleteResults;
 
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index a97cac924..d2a9e42 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -123,15 +123,15 @@
 }
 
 - (void)start {
-  DCHECK(self.commandDispatcher);
   DCHECK(self.browser);
 
   if (self.started)
     return;
 
-  [self.commandDispatcher startDispatchingToTarget:self
-                                       forProtocol:@protocol(OmniboxFocuser)];
-  [self.commandDispatcher
+  [self.browser->GetCommandDispatcher()
+      startDispatchingToTarget:self
+                   forProtocol:@protocol(OmniboxFocuser)];
+  [self.browser->GetCommandDispatcher()
       startDispatchingToTarget:self
                    forProtocol:@protocol(LoadQueryCommands)];
 
@@ -140,9 +140,12 @@
   self.viewController = [[LocationBarViewController alloc] init];
   self.viewController.incognito = isIncognito;
   self.viewController.delegate = self;
+  // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol
+  // clean up.
   self.viewController.dispatcher =
       static_cast<id<ActivityServiceCommands, BrowserCommands,
-                     ApplicationCommands, LoadQueryCommands>>(self.dispatcher);
+                     ApplicationCommands, LoadQueryCommands>>(
+          self.browser->GetCommandDispatcher());
   self.viewController.voiceSearchEnabled = ios::GetChromeBrowserProvider()
                                                ->GetVoiceSearchProvider()
                                                ->IsVoiceSearchEnabled();
@@ -182,9 +185,11 @@
   // Create BadgeMediator and set the viewController as its consumer.
   self.badgeMediator = [[BadgeMediator alloc] initWithBrowser:self.browser];
   self.badgeMediator.consumer = self.badgeViewController;
+  // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol
+  // clean up.
   self.badgeMediator.dispatcher =
       static_cast<id<InfobarCommands, BrowserCoordinatorCommands>>(
-          self.dispatcher);
+          self.browser->GetCommandDispatcher());
   buttonFactory.delegate = self.badgeMediator;
   FullscreenController* fullscreenController =
       FullscreenController::FromBrowserState(self.browserState);
@@ -209,7 +214,7 @@
 - (void)stop {
   if (!self.started)
     return;
-  [self.commandDispatcher stopDispatchingToTarget:self];
+  [self.browser->GetCommandDispatcher() stopDispatchingToTarget:self];
   // The popup has to be destroyed before the location bar.
   [self.omniboxPopupCoordinator stop];
   [self.omniboxCoordinator stop];
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
index 3c6cff3..b6ad669 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
@@ -18,7 +18,6 @@
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
 #import "ios/chrome/browser/main/test_browser.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
-#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h"
 #include "ios/chrome/browser/url_loading/test_url_loading_service.h"
 #include "ios/chrome/browser/url_loading/url_loading_params.h"
@@ -109,10 +108,10 @@
 
     delegate_ = [[TestToolbarCoordinatorDelegate alloc] init];
 
-    coordinator_ = [[LocationBarCoordinator alloc] init];
-    coordinator_.browser = browser_.get();
+    coordinator_ = [[LocationBarCoordinator alloc]
+        initWithBaseViewController:nil
+                           browser:browser_.get()];
     coordinator_.delegate = delegate_;
-    coordinator_.commandDispatcher = [[CommandDispatcher alloc] init];
   }
 
   void TearDown() override {
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index a6a9495..9519759 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -149,6 +149,8 @@
 
 @implementation SceneController
 @synthesize settingsNavigationController;  //< From AppNavigation protocol.
+@synthesize appURLLoadingService =
+    _appURLLoadingService;  //< From SceneControllerGuts
 
 - (instancetype)initWithSceneState:(SceneState*)sceneState {
   self = [super init];
@@ -162,6 +164,9 @@
           << "The window must be created by the scene delegate";
       self.sceneState.window = [[ChromeOverlayWindow alloc]
           initWithFrame:[[UIScreen mainScreen] bounds]];
+
+      _appURLLoadingService = new AppUrlLoadingService();
+      _appURLLoadingService->SetDelegate(self);
     }
   }
   return self;
@@ -339,7 +344,7 @@
   params.from_chrome = command.fromChrome;
   params.user_initiated = command.userInitiated;
   params.should_focus_omnibox = command.shouldFocusOmnibox;
-  self.mainController.appURLLoadingService->LoadUrlInNewTab(params);
+  self.appURLLoadingService->LoadUrlInNewTab(params);
 }
 
 // TODO(crbug.com/779791) : Do not pass |baseViewController| through dispatcher.
diff --git a/ios/chrome/browser/ui/main/scene_controller_guts.h b/ios/chrome/browser/ui/main/scene_controller_guts.h
index a8053f3..f444b52 100644
--- a/ios/chrome/browser/ui/main/scene_controller_guts.h
+++ b/ios/chrome/browser/ui/main/scene_controller_guts.h
@@ -10,6 +10,7 @@
 #include "ios/chrome/app/application_delegate/startup_information.h"
 #import "ios/chrome/app/application_delegate/tab_opening.h"
 #import "ios/chrome/browser/procedural_block_types.h"
+#import "ios/chrome/browser/url_loading/app_url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
 
@@ -18,6 +19,10 @@
 
 @protocol SceneControllerGuts <WebStateListObserving>
 
+// The scene level component for url loading. Is passed down to
+// browser state level UrlLoadingService instances.
+@property(nonatomic, assign) AppUrlLoadingService* appURLLoadingService;
+
 - (void)dismissModalDialogsWithCompletion:(ProceduralBlock)completion
                            dismissOmnibox:(BOOL)dismissOmnibox;
 
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
index 79269c47..615d6f9 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -18,9 +18,8 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/metrics/new_tab_page_uma.h"
+#include "ios/chrome/browser/sessions/live_tab_context_browser_agent.h"
 #include "ios/chrome/browser/sessions/session_util.h"
-#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h"
-#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h"
 #include "ios/chrome/browser/sync/session_sync_service_factory.h"
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.h"
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_consumer.h"
@@ -943,6 +942,7 @@
     return;
 
   // Only TAB type is handled.
+  // TODO(crbug.com/1056596) : Support WINDOW restoration under multi-window.
   DCHECK_EQ(entry->type, sessions::TabRestoreService::TAB);
   base::RecordAction(
       base::UserMetricsAction("MobileRecentTabManagerRecentTabOpened"));
@@ -956,7 +956,7 @@
   WindowOpenDisposition disposition =
       self.isIncognito ? WindowOpenDisposition::NEW_FOREGROUND_TAB
                        : self.restoredTabDisposition;
-  RestoreTab(entry->id, disposition, self.browserState);
+  RestoreTab(entry->id, disposition, self.browser);
   [self.presentationDelegate showActiveRegularTabFromRecentTabs];
 }
 
@@ -1060,6 +1060,7 @@
   // Present sheet/popover using controller that is added to view hierarchy.
   self.contextMenuCoordinator = [[ContextMenuCoordinator alloc]
       initWithBaseViewController:self
+                         browser:self.browser
                            title:nil
                           inView:self.tableView
                       atLocation:viewCoordinate];
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 50b34cf8..c721e6bc 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -29,6 +29,7 @@
     "//ios/public/provider/chrome/browser/user_feedback",
     "//ui/base",
   ]
+  public_deps = [ ":settings_controller_protocol" ]
 }
 
 source_set("settings_root_constants") {
@@ -193,6 +194,12 @@
   ]
 }
 
+source_set("settings_controller_protocol") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [ "settings_controller_protocol.h" ]
+  frameworks = [ "UIKit.framework" ]
+}
+
 source_set("constants") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
diff --git a/ios/chrome/browser/ui/settings/google_services/BUILD.gn b/ios/chrome/browser/ui/settings/google_services/BUILD.gn
index 4d738b5..fd4130ad 100644
--- a/ios/chrome/browser/ui/settings/google_services/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/google_services/BUILD.gn
@@ -7,10 +7,6 @@
   sources = [
     "accounts_table_view_controller.h",
     "accounts_table_view_controller.mm",
-    "advanced_signin_settings_coordinator.h",
-    "advanced_signin_settings_coordinator.mm",
-    "advanced_signin_settings_navigation_controller.h",
-    "advanced_signin_settings_navigation_controller.mm",
     "google_services_settings_command_handler.h",
     "google_services_settings_consumer.h",
     "google_services_settings_coordinator.h",
@@ -60,6 +56,7 @@
     "//ios/chrome/browser/ui/alert_coordinator",
     "//ios/chrome/browser/ui/authentication",
     "//ios/chrome/browser/ui/authentication/cells",
+    "//ios/chrome/browser/ui/authentication/signin/advanced_settings_signin",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/icons",
@@ -79,8 +76,10 @@
     "//ios/public/provider/chrome/browser/signin",
     "//ui/base",
   ]
-  allow_circular_includes_from =
-      [ "//ios/chrome/browser/ui/signin_interaction" ]
+  allow_circular_includes_from = [
+    "//ios/chrome/browser/ui/signin_interaction",
+    "//ios/chrome/browser/ui/authentication/signin/advanced_settings_signin",
+  ]
 }
 
 source_set("constants") {
@@ -88,8 +87,6 @@
   sources = [
     "accounts_table_view_controller_constants.h",
     "accounts_table_view_controller_constants.mm",
-    "advanced_signin_settings_constants.h",
-    "advanced_signin_settings_constants.mm",
     "google_services_settings_constants.h",
     "google_services_settings_constants.mm",
     "manage_sync_settings_constants.h",
diff --git a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_constants.h b/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_constants.h
deleted file mode 100644
index 3522162..0000000
--- a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_constants.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_GOOGLE_SERVICES_ADVANCED_SIGNIN_SETTINGS_CONSTANTS_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_GOOGLE_SERVICES_ADVANCED_SIGNIN_SETTINGS_CONSTANTS_H_
-
-#import <Foundation/Foundation.h>
-
-// The accessibility identifier for the navigation "Confirm" button.
-extern NSString* const kSyncSettingsConfirmButtonId;
-
-// The accessibility identifier for the navigation "Cancel" button.
-extern NSString* const kSyncSettingsCancelButtonId;
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_GOOGLE_SERVICES_ADVANCED_SIGNIN_SETTINGS_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_coordinator.h b/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_coordinator.h
deleted file mode 100644
index 9fbc1fa7..0000000
--- a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_coordinator.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_GOOGLE_SERVICES_ADVANCED_SIGNIN_SETTINGS_COORDINATOR_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_GOOGLE_SERVICES_ADVANCED_SIGNIN_SETTINGS_COORDINATOR_H_
-
-#import "base/ios/block_types.h"
-#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
-
-@class AdvancedSigninSettingsCoordinator;
-
-// AdvancedSigninSettingsCoordinator delegate.
-@protocol AdvancedSigninSettingsCoordinatorDelegate <NSObject>
-
-// Called when the user closes AdvancedSigninSettingsCoordinator.
-// |signedin|, YES if the view is confirmed or aborted, and NO if the view is
-// canceled.
-- (void)advancedSigninSettingsCoordinatorDidClose:
-            (AdvancedSigninSettingsCoordinator*)coordinator
-                                         signedin:(BOOL)signedin;
-
-@end
-
-// Shows the Google services settings in a navigation controller, so the user
-// can update their sync settings before sync starts (or sign-out).
-@interface AdvancedSigninSettingsCoordinator : ChromeCoordinator
-
-// Delegate.
-@property(nonatomic, weak) id<AdvancedSigninSettingsCoordinatorDelegate>
-    delegate;
-
-// Aborts the sign-in flow, and calls the delegate. Aborting the Advanced
-// sync settings doesn't sign out the user. The sync is left unsetup and doesn't
-// start. This method does nothing if called twice.
-// |dismiss|, dismisses the view controller if YES.
-// |animated|, the view is animated if YES.
-// |completion|, this completion block is called after the view is dismissed.
-//               It will be called even the view is already dismissed. The value
-//               can be nil.
-- (void)abortWithDismiss:(BOOL)dismiss
-                animated:(BOOL)animated
-              completion:(ProceduralBlock)completion;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_GOOGLE_SERVICES_ADVANCED_SIGNIN_SETTINGS_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_coordinator.mm b/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_coordinator.mm
deleted file mode 100644
index 46b8d09c4..0000000
--- a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_coordinator.mm
+++ /dev/null
@@ -1,293 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_coordinator.h"
-
-#import <UIKit/UIKit.h>
-
-#include "base/logging.h"
-#include "base/metrics/user_metrics.h"
-#include "components/signin/public/base/signin_metrics.h"
-#include "components/strings/grit/components_strings.h"
-#include "components/sync/driver/sync_service.h"
-#include "components/unified_consent/unified_consent_metrics.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/main/browser.h"
-#import "ios/chrome/browser/signin/authentication_service.h"
-#import "ios/chrome/browser/signin/authentication_service_factory.h"
-#include "ios/chrome/browser/sync/profile_sync_service_factory.h"
-#include "ios/chrome/browser/sync/sync_setup_service.h"
-#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
-#import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
-#import "ios/chrome/browser/ui/icons/chrome_icon.h"
-#import "ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_constants.h"
-#import "ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_navigation_controller.h"
-#import "ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.h"
-#import "ios/chrome/browser/ui/settings/google_services/google_services_settings_mode.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using l10n_util::GetNSString;
-
-// Advanced sign-in settings result.
-typedef NS_ENUM(NSInteger, AdvancedSigninSettingsCoordinatorResult) {
-  // The user confirmed the advanced sync settings.
-  AdvancedSyncSettingsCoordinatorResultConfirm,
-  // The user canceled the advanced sync settings.
-  AdvancedSigninSettingsCoordinatorResultCancel,
-  // Chrome aborted the advanced sync settings.
-  AdvancedSigninSettingsCoordinatorResultInterrupted,
-};
-
-@interface AdvancedSigninSettingsCoordinator () <
-    UIAdaptivePresentationControllerDelegate>
-
-// Google services settings coordinator.
-@property(nonatomic, strong)
-    GoogleServicesSettingsCoordinator* googleServicesSettingsCoordinator;
-// Advanced signin settings navigation controller.
-@property(nonatomic, strong) AdvancedSigninSettingsNavigationController*
-    advancedSigninSettingsNavigationController;
-// Confirm cancel sign-in/sync dialog.
-@property(nonatomic, strong)
-    AlertCoordinator* cancelConfirmationAlertCoordinator;
-
-@end
-
-@implementation AdvancedSigninSettingsCoordinator
-
-- (void)start {
-  // Create the navigation controller.
-  self.advancedSigninSettingsNavigationController =
-      [[AdvancedSigninSettingsNavigationController alloc] init];
-
-  // Shorter name to make line wrapping better.
-  AdvancedSigninSettingsNavigationController* controller =
-      self.advancedSigninSettingsNavigationController;
-  controller.modalPresentationStyle = UIModalPresentationFormSheet;
-  controller.presentationController.delegate = self;
-
-  // Init and start Google settings coordinator.
-  GoogleServicesSettingsMode mode =
-      GoogleServicesSettingsModeAdvancedSigninSettings;
-  self.googleServicesSettingsCoordinator =
-      [[GoogleServicesSettingsCoordinator alloc]
-          initWithBaseViewController:controller
-                             browser:self.browser
-                                mode:mode];
-  self.googleServicesSettingsCoordinator.navigationController = controller;
-  // Starting the coordinator will add its view controller to the navigation
-  // controller.
-  [self.googleServicesSettingsCoordinator start];
-
-  // Set navigation items for settings coordinator.
-  self.googleServicesSettingsCoordinator.viewController.navigationItem
-      .leftBarButtonItem = [self navigationCancelButton];
-  self.googleServicesSettingsCoordinator.viewController.navigationItem
-      .rightBarButtonItem = [self navigationConfirmButton];
-
-  // Present the navigation controller that now contains the Google settings
-  // view controller.
-  [self.baseViewController presentViewController:controller
-                                        animated:YES
-                                      completion:nil];
-}
-
-- (void)abortWithDismiss:(BOOL)dismiss
-                animated:(BOOL)animated
-              completion:(ProceduralBlock)completion {
-  if (!self.advancedSigninSettingsNavigationController) {
-    if (completion) {
-      completion();
-    }
-    return;
-  }
-  DCHECK_EQ(self.advancedSigninSettingsNavigationController,
-            self.baseViewController.presentedViewController);
-  if (dismiss) {
-    [self dismissViewControllerAndFinishWithResult:
-              AdvancedSigninSettingsCoordinatorResultInterrupted
-                                          animated:animated
-                                        completion:completion];
-  } else {
-    [self
-        finishedWithResult:AdvancedSigninSettingsCoordinatorResultInterrupted];
-    if (completion) {
-      completion();
-    }
-  }
-}
-
-#pragma mark - UIAdaptivePresentationControllerDelegate
-
-- (BOOL)presentationControllerShouldDismiss:
-    (UIPresentationController*)presentationController {
-  return NO;
-}
-
-- (void)presentationControllerDidAttemptToDismiss:
-    (UIPresentationController*)presentationController {
-  [self showDismissAlertDialog];
-}
-
-#pragma mark - Private
-
-// Called once the view controller has been removed (if needed).
-// |result|, YES if the user accepts to sync.
-- (void)finishedWithResult:(AdvancedSigninSettingsCoordinatorResult)result {
-  DCHECK(self.advancedSigninSettingsNavigationController);
-  SyncSetupService* syncSetupService =
-      SyncSetupServiceFactory::GetForBrowserState(
-          self.browser->GetBrowserState());
-  switch (result) {
-    case AdvancedSyncSettingsCoordinatorResultConfirm: {
-      base::RecordAction(
-          base::UserMetricsAction("Signin_Signin_ConfirmAdvancedSyncSettings"));
-      syncer::SyncService* syncService =
-          ProfileSyncServiceFactory::GetForBrowserState(
-              self.browser->GetBrowserState());
-      unified_consent::metrics::RecordSyncSetupDataTypesHistrogam(
-          syncService->GetUserSettings(),
-          self.browser->GetBrowserState()->GetPrefs());
-      if (syncSetupService->IsSyncEnabled()) {
-        // FirstSetupComplete flag should be only turned on when the user agrees
-        // to start Sync.
-        syncSetupService->PrepareForFirstSyncSetup();
-        syncSetupService->SetFirstSetupComplete(
-            syncer::SyncFirstSetupCompleteSource::ADVANCED_FLOW_CONFIRM);
-      }
-      break;
-    }
-    case AdvancedSigninSettingsCoordinatorResultCancel:
-      base::RecordAction(base::UserMetricsAction(
-          "Signin_Signin_ConfirmCancelAdvancedSyncSettings"));
-      syncSetupService->CommitSyncChanges();
-      AuthenticationServiceFactory::GetForBrowserState(
-          self.browser->GetBrowserState())
-          ->SignOut(signin_metrics::ABORT_SIGNIN,
-                    /*force_clear_browsing_data=*/false, nil);
-      break;
-    case AdvancedSigninSettingsCoordinatorResultInterrupted:
-      base::RecordAction(
-          base::UserMetricsAction("Signin_Signin_AbortAdvancedSyncSettings"));
-      break;
-  }
-  [self.googleServicesSettingsCoordinator stop];
-  self.googleServicesSettingsCoordinator.delegate = nil;
-  self.googleServicesSettingsCoordinator = nil;
-  DCHECK(!syncSetupService->HasUncommittedChanges())
-      << "-[GoogleServicesSettingsCoordinator stop] should commit sync "
-         "changes.";
-  BOOL signedin = result != AdvancedSigninSettingsCoordinatorResultCancel;
-  [self.delegate advancedSigninSettingsCoordinatorDidClose:self
-                                                  signedin:signedin];
-  self.advancedSigninSettingsNavigationController = nil;
-}
-
-// This method should be moved to the view controller.
-- (UIBarButtonItem*)navigationCancelButton {
-  UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc]
-      initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
-                           target:self
-                           action:@selector(showDismissAlertDialog)];
-  cancelButton.accessibilityIdentifier = kSyncSettingsCancelButtonId;
-  return cancelButton;
-}
-
-// This method should be moved to the view controller.
-- (UIBarButtonItem*)navigationConfirmButton {
-  UIBarButtonItem* confirmButton = [[UIBarButtonItem alloc]
-      initWithTitle:GetNSString(
-                        IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CONFIRM_MAIN_BUTTON)
-              style:UIBarButtonItemStyleDone
-             target:self
-             action:@selector(navigationConfirmButtonAction)];
-  confirmButton.accessibilityIdentifier = kSyncSettingsConfirmButtonId;
-  return confirmButton;
-}
-
-// Presents an UIAlert to ask the user if wants to cancel the sign-in.
-- (void)showDismissAlertDialog {
-  base::RecordAction(
-      base::UserMetricsAction("Signin_Signin_CancelAdvancedSyncSettings"));
-  self.cancelConfirmationAlertCoordinator = [[AlertCoordinator alloc]
-      initWithBaseViewController:self.advancedSigninSettingsNavigationController
-                           title:
-                               GetNSString(
-                                   IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CANCEL_SYNC_ALERT_TITLE)
-                         message:
-                             GetNSString(
-                                 IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CANCEL_SYNC_ALERT_MESSAGE)];
-  __weak __typeof(self) weakSelf = self;
-  [self.cancelConfirmationAlertCoordinator
-      addItemWithTitle:
-          GetNSString(
-              IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CANCEL_SYNC_ALERT_BACK_BUTTON)
-                action:^{
-                  [weakSelf alertButtonActionWithCancelSync:NO];
-                }
-                 style:UIAlertActionStyleCancel];
-  [self.cancelConfirmationAlertCoordinator
-      addItemWithTitle:
-          GetNSString(
-              IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CANCEL_SYNC_ALERT_CANCEL_SYNC_BUTTON)
-                action:^{
-                  [weakSelf alertButtonActionWithCancelSync:YES];
-                }
-                 style:UIAlertActionStyleDefault];
-  [self.cancelConfirmationAlertCoordinator start];
-}
-
-// Called by the confirm button from tne navigation controller. Validates the
-// sync preferences chosen by the user, starts the sync, close the completion
-// callback and closes the advanced sign-in settings.
-- (void)navigationConfirmButtonAction {
-  DCHECK_EQ(self.advancedSigninSettingsNavigationController,
-            self.baseViewController.presentedViewController);
-  [self dismissViewControllerAndFinishWithResult:
-            AdvancedSyncSettingsCoordinatorResultConfirm
-                                        animated:YES
-                                      completion:nil];
-}
-
-// Called when a button of |self.cancelConfirmationAlertCoordinator| is pressed.
-- (void)alertButtonActionWithCancelSync:(BOOL)cancelSync {
-  DCHECK(self.cancelConfirmationAlertCoordinator);
-  [self.cancelConfirmationAlertCoordinator stop];
-  self.cancelConfirmationAlertCoordinator = nil;
-  if (cancelSync) {
-    [self dismissViewControllerAndFinishWithResult:
-              AdvancedSigninSettingsCoordinatorResultCancel
-                                          animated:YES
-                                        completion:nil];
-  } else {
-    base::RecordAction(base::UserMetricsAction(
-        "Signin_Signin_CancelCancelAdvancedSyncSettings"));
-  }
-}
-
-// Dismisses the current view controller with animation, and calls
-// -[self finishedWithResult:] with |result|.
-- (void)dismissViewControllerAndFinishWithResult:
-            (AdvancedSigninSettingsCoordinatorResult)result
-                                        animated:(BOOL)animated
-                                      completion:(ProceduralBlock)completion {
-  DCHECK_EQ(self.advancedSigninSettingsNavigationController,
-            self.baseViewController.presentedViewController);
-  __weak __typeof(self) weakSelf = self;
-  ProceduralBlock dismissCompletion = ^() {
-    [weakSelf finishedWithResult:result];
-    if (completion) {
-      completion();
-    }
-  };
-  [self.baseViewController dismissViewControllerAnimated:animated
-                                              completion:dismissCompletion];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_navigation_controller.h b/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_navigation_controller.h
deleted file mode 100644
index 8e520b0..0000000
--- a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_navigation_controller.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_GOOGLE_SERVICES_ADVANCED_SIGNIN_SETTINGS_NAVIGATION_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_GOOGLE_SERVICES_ADVANCED_SIGNIN_SETTINGS_NAVIGATION_CONTROLLER_H_
-
-#import <UIKit/UIKit.h>
-
-#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
-
-// View controller to related to AdvancedSigninSettingsCoordinator.
-@interface AdvancedSigninSettingsNavigationController
-    : SettingsNavigationController
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_GOOGLE_SERVICES_ADVANCED_SIGNIN_SETTINGS_NAVIGATION_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_navigation_controller.mm
deleted file mode 100644
index dbad3c6..0000000
--- a/ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_navigation_controller.mm
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_navigation_controller.h"
-
-#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation AdvancedSigninSettingsNavigationController
-
-- (UIViewController*)popViewControllerAnimated:(BOOL)animated {
-  UIViewController* poppedViewController =
-      [super popViewControllerAnimated:animated];
-  if ([poppedViewController
-          respondsToSelector:@selector(viewControllerWasPopped)]) {
-    [poppedViewController performSelector:@selector(viewControllerWasPopped)];
-  }
-  return poppedViewController;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.mm
index 038415b..7362c7a 100644
--- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.mm
@@ -272,6 +272,7 @@
 - (void)manageSyncSettingsCoordinatorWasPopped:
     (ManageSyncSettingsCoordinator*)coordinator {
   DCHECK_EQ(self.manageSyncSettingsCoordinator, coordinator);
+  [self.manageSyncSettingsCoordinator stop];
   self.manageSyncSettingsCoordinator = nil;
 }
 
diff --git a/ios/chrome/browser/ui/settings/settings_controller_protocol.h b/ios/chrome/browser/ui/settings/settings_controller_protocol.h
new file mode 100644
index 0000000..22ec1e10
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/settings_controller_protocol.h
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_CONTROLLER_PROTOCOL_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_CONTROLLER_PROTOCOL_H_
+
+#import <UIKit/UIKit.h>
+
+// Protocol for settings view controllers.
+@protocol SettingsControllerProtocol <NSObject>
+
+@optional
+
+// Notifies the controller that the settings screen is being dismissed.
+- (void)settingsWillBeDismissed;
+
+// Notifies the controller that is popped out from the settings navigation
+// controller.
+- (void)viewControllerWasPopped;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_CONTROLLER_PROTOCOL_H_
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.h b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
index da9bb71..baf9feb 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.h
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
@@ -8,6 +8,7 @@
 #import <UIKit/UIKit.h>
 
 #import "ios/chrome/browser/ui/commands/application_commands.h"
+#import "ios/chrome/browser/ui/settings/settings_controller_protocol.h"
 
 class Browser;
 @protocol BrowserCommands;
@@ -18,19 +19,6 @@
 // The accessibility identifier for the settings' "Done" button.
 extern NSString* const kSettingsDoneButtonId;
 
-@protocol SettingsControllerProtocol<NSObject>
-
-@optional
-
-// Notifies the controller that the settings screen is being dismissed.
-- (void)settingsWillBeDismissed;
-
-// Notifies the controller that is popped out from the settings navigation
-// controller.
-- (void)viewControllerWasPopped;
-
-@end
-
 @protocol SettingsNavigationControllerDelegate<NSObject>
 
 // Informs the delegate that the settings navigation controller should be
diff --git a/ios/chrome/browser/ui/signin_interaction/signin_interaction_coordinator.mm b/ios/chrome/browser/ui/signin_interaction/signin_interaction_coordinator.mm
index e8a8ea7..8c06a003 100644
--- a/ios/chrome/browser/ui/signin_interaction/signin_interaction_coordinator.mm
+++ b/ios/chrome/browser/ui/signin_interaction/signin_interaction_coordinator.mm
@@ -11,7 +11,6 @@
 #import "ios/chrome/browser/ui/authentication/authentication_ui_util.h"
 #import "ios/chrome/browser/ui/authentication/signin/signin_coordinator.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
-#import "ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_coordinator.h"
 #import "ios/chrome/browser/ui/signin_interaction/signin_interaction_controller.h"
 #import "ios/chrome/browser/ui/signin_interaction/signin_interaction_presenting.h"
 
@@ -19,9 +18,7 @@
 #error "This file requires ARC support."
 #endif
 
-@interface SigninInteractionCoordinator () <
-    AdvancedSigninSettingsCoordinatorDelegate,
-    SigninInteractionPresenting>
+@interface SigninInteractionCoordinator () <SigninInteractionPresenting>
 
 // Coordinator to present alerts.
 @property(nonatomic, strong) AlertCoordinator* alertCoordinator;
@@ -43,10 +40,6 @@
 // Sign-in completion.
 @property(nonatomic, copy) signin_ui::CompletionCallback signinCompletion;
 
-// Advanced sign-in settings coordinator.
-@property(nonatomic, strong)
-    AdvancedSigninSettingsCoordinator* advancedSigninSettingsCoordinator;
-
 @end
 
 @implementation SigninInteractionCoordinator
@@ -130,28 +123,24 @@
 - (void)cancel {
   [self.controller cancel];
   [self interrupSigninCoordinatorWithAction:
-            SigninCoordinatorInterruptActionNoDismiss];
-  [self.advancedSigninSettingsCoordinator abortWithDismiss:NO
-                                                  animated:YES
-                                                completion:nil];
+            SigninCoordinatorInterruptActionNoDismiss
+                                 completion:nil];
 }
 
 - (void)cancelAndDismiss {
   [self.controller cancelAndDismiss];
   [self interrupSigninCoordinatorWithAction:
-            SigninCoordinatorInterruptActionDismissWithAnimation];
-  [self.advancedSigninSettingsCoordinator abortWithDismiss:YES
-                                                  animated:YES
-                                                completion:nil];
+            SigninCoordinatorInterruptActionDismissWithAnimation
+                                 completion:nil];
 }
 
 - (void)abortAndDismissSettingsViewAnimated:(BOOL)animated
                                  completion:(ProceduralBlock)completion {
   DCHECK(!self.controller);
-  DCHECK(self.advancedSigninSettingsCoordinator);
-  [self.advancedSigninSettingsCoordinator abortWithDismiss:YES
-                                                  animated:animated
-                                                completion:completion];
+  SigninCoordinatorInterruptAction action =
+      animated ? SigninCoordinatorInterruptActionDismissWithAnimation
+               : SigninCoordinatorInterruptActionDismissWithoutAnimation;
+  [self interrupSigninCoordinatorWithAction:action completion:completion];
 }
 
 #pragma mark - Properties
@@ -161,17 +150,7 @@
 }
 
 - (BOOL)isSettingsViewPresented {
-  return self.advancedSigninSettingsCoordinator != nil;
-}
-
-#pragma mark - AdvancedSigninSettingsCoordinatorDelegate
-
-- (void)advancedSigninSettingsCoordinatorDidClose:
-            (AdvancedSigninSettingsCoordinator*)coordinator
-                                         signedin:(BOOL)signedin {
-  DCHECK_EQ(self.advancedSigninSettingsCoordinator, coordinator);
-  self.advancedSigninSettingsCoordinator = nil;
-  [self signinDoneWithSuccess:signedin];
+  return self.coordinator.isSettingsViewPresented;
 }
 
 #pragma mark - SigninInteractionPresenting
@@ -280,14 +259,25 @@
 
 // Shows the advanced sign-in settings UI.
 - (void)showAdvancedSigninSettings {
-  DCHECK(!self.advancedSigninSettingsCoordinator);
+  DCHECK(!self.coordinator);
   DCHECK(self.presentingViewController);
-  self.advancedSigninSettingsCoordinator =
-      [[AdvancedSigninSettingsCoordinator alloc]
-          initWithBaseViewController:self.presentingViewController
-                             browser:self.browser];
-  self.advancedSigninSettingsCoordinator.delegate = self;
-  [self.advancedSigninSettingsCoordinator start];
+  self.coordinator = [SigninCoordinator
+      advancedSettingsSigninCoordinatorWithBaseViewController:
+          self.presentingViewController
+                                                      browser:self.browser];
+  __weak SigninInteractionCoordinator* weakSelf = self;
+  self.coordinator.signinCompletion =
+      ^(SigninCoordinatorResult signinResult, ChromeIdentity* identity) {
+        [weakSelf advancedSigninDoneWithSigninResult:signinResult];
+      };
+  [self.coordinator start];
+}
+
+- (void)advancedSigninDoneWithSigninResult:
+    (SigninCoordinatorResult)signinResult {
+  [self.coordinator stop];
+  self.coordinator = nil;
+  [self signinDoneWithSuccess:signinResult == SigninCoordinatorResultSuccess];
 }
 
 // Called when the sign-in is done.
@@ -299,18 +289,19 @@
     self.signinCompletion(success);
     self.signinCompletion = nil;
   }
-  [self.advancedSigninSettingsCoordinator stop];
-  self.advancedSigninSettingsCoordinator = nil;
-  self.presentingViewController = nil;
 }
 
 - (void)interrupSigninCoordinatorWithAction:
-    (SigninCoordinatorInterruptAction)action {
+            (SigninCoordinatorInterruptAction)action
+                                 completion:(ProceduralBlock)completion {
   __weak __typeof(self) weakSelf = self;
   ProceduralBlock interruptCompletion = ^() {
     // |weakSelf.coordinator.signinCompletion| is called before this interrupt
     // block. The signin completion has to set |coordinator| to nil.
     DCHECK(!weakSelf.coordinator);
+    if (completion) {
+      completion();
+    }
   };
   [self.coordinator interruptWithAction:action completion:interruptCompletion];
 }
diff --git a/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
index ef0ad8b..b2adc68 100644
--- a/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
@@ -159,16 +159,10 @@
 
 #pragma mark - FakeboxFocuser
 
-- (void)focusOmniboxNoAnimation {
-  self.enableAnimationsForOmniboxFocus = NO;
-  [self fakeboxFocused];
+- (void)focusLocationBarWithAnimation:(BOOL)animated {
+  self.enableAnimationsForOmniboxFocus = animated;
+  [self transitionToLocationBarFocusedState:YES];
   self.enableAnimationsForOmniboxFocus = YES;
-  // If the pasteboard is containing a URL, the omnibox popup suggestions are
-  // displayed as soon as the omnibox is focused.
-  // If the fake omnibox animation is triggered at the same time, it is possible
-  // to see the NTP going up where the real omnibox should be displayed.
-  if ([self.locationBarCoordinator omniboxPopupHasAutocompleteResults])
-    [self onFakeboxAnimationComplete];
 }
 
 - (void)fakeboxFocused {
@@ -214,12 +208,9 @@
 
 // Sets the location bar up.
 - (void)setUpLocationBar {
-  self.locationBarCoordinator = [[LocationBarCoordinator alloc] init];
-
-  self.locationBarCoordinator.browser = self.browser;
-  self.locationBarCoordinator.dispatcher = self.browser->GetCommandDispatcher();
-  self.locationBarCoordinator.commandDispatcher =
-      self.browser->GetCommandDispatcher();
+  self.locationBarCoordinator =
+      [[LocationBarCoordinator alloc] initWithBaseViewController:nil
+                                                         browser:self.browser];
   self.locationBarCoordinator.delegate = self.delegate;
   self.locationBarCoordinator.popupPresenterDelegate =
       self.popupPresenterDelegate;
diff --git a/ios/chrome/browser/ui/toolbar/public/fakebox_focuser.h b/ios/chrome/browser/ui/toolbar/public/fakebox_focuser.h
index 59808d55..998d9711 100644
--- a/ios/chrome/browser/ui/toolbar/public/fakebox_focuser.h
+++ b/ios/chrome/browser/ui/toolbar/public/fakebox_focuser.h
@@ -10,8 +10,8 @@
 // This protocol provides callbacks for focusing and blurring the fake omnibox
 // on NTP.
 @protocol FakeboxFocuser
-// Focuses the omnibox without animations.
-- (void)focusOmniboxNoAnimation;
+// Focuses the location bar.
+- (void)focusLocationBarWithAnimation:(BOOL)animated;
 // Give focus to the omnibox, but indicate that the focus event was initiated
 // from the fakebox on the Google landing page.
 - (void)fakeboxFocused;
diff --git a/ios/chrome/browser/url_loading/url_loading_util.h b/ios/chrome/browser/url_loading/url_loading_util.h
index 05f373a..cfdc1592 100644
--- a/ios/chrome/browser/url_loading/url_loading_util.h
+++ b/ios/chrome/browser/url_loading/url_loading_util.h
@@ -10,6 +10,7 @@
 #include "components/sessions/core/session_id.h"
 #include "ui/base/window_open_disposition.h"
 
+class Browser;
 class ChromeBrowserState;
 class GURL;
 
@@ -31,6 +32,6 @@
 // into |browser_state|.
 void RestoreTab(const SessionID session_id,
                 WindowOpenDisposition disposition,
-                ChromeBrowserState* browser_state);
+                Browser* browser);
 
 #endif  // IOS_CHROME_BROWSER_URL_LOADING_URL_LOADING_UTIL_H_
diff --git a/ios/chrome/browser/url_loading/url_loading_util.mm b/ios/chrome/browser/url_loading/url_loading_util.mm
index dda8de5d7..6d4c40cf 100644
--- a/ios/chrome/browser/url_loading/url_loading_util.mm
+++ b/ios/chrome/browser/url_loading/url_loading_util.mm
@@ -12,8 +12,7 @@
 #import "ios/chrome/browser/prerender/prerender_service.h"
 #import "ios/chrome/browser/prerender/prerender_service_factory.h"
 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
-#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h"
-#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h"
+#include "ios/chrome/browser/sessions/live_tab_context_browser_agent.h"
 #import "ios/chrome/browser/web/load_timing_tab_helper.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/web/public/web_state.h"
@@ -47,12 +46,19 @@
 
 void RestoreTab(const SessionID session_id,
                 WindowOpenDisposition disposition,
-                ChromeBrowserState* browser_state) {
-  TabRestoreServiceDelegateImplIOS* delegate =
-      TabRestoreServiceDelegateImplIOSFactory::GetForBrowserState(
-          browser_state);
+                Browser* browser) {
+  // iOS Chrome doesn't yet support restoring tabs to new windows.
+  // TODO(crbug.com/1056596) : Support WINDOW restoration under multi-window.
+  DCHECK(disposition != WindowOpenDisposition::NEW_WINDOW);
+  LiveTabContextBrowserAgent* context =
+      LiveTabContextBrowserAgent::FromBrowser(browser);
+  // Passing a nil context into RestoreEntryById can result in the restore
+  // service requesting a new window. This is unsupported on iOS (see above
+  // TODO).
+  DCHECK(context);
+  ChromeBrowserState* browser_state =
+      browser->GetBrowserState()->GetOriginalChromeBrowserState();
   sessions::TabRestoreService* restoreService =
-      IOSChromeTabRestoreServiceFactory::GetForBrowserState(
-          browser_state->GetOriginalChromeBrowserState());
-  restoreService->RestoreEntryById(delegate, session_id, disposition);
+      IOSChromeTabRestoreServiceFactory::GetForBrowserState(browser_state);
+  restoreService->RestoreEntryById(context, session_id, disposition);
 }
diff --git a/ios/chrome/browser/web/navigation_egtest.mm b/ios/chrome/browser/web/navigation_egtest.mm
index 2ef84eef..cab4855 100644
--- a/ios/chrome/browser/web/navigation_egtest.mm
+++ b/ios/chrome/browser/web/navigation_egtest.mm
@@ -562,8 +562,7 @@
 
 // Tests that navigating forward from a WebUI URL works when resuming from
 // session restore. This is a regression test for https://crbug.com/814790.
-// Flaky: https://crbug.com/1054372
-- (void)DISABLED_testRestoreHistoryToWebUIAndNavigateForward {
+- (void)testRestoreHistoryToWebUIAndNavigateForward {
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
   const GURL destinationURL = self.testServer->GetURL(kSimpleFileBasedTestURL);
   [ChromeEarlGrey loadURL:GURL("chrome://version")];
@@ -603,8 +602,7 @@
 
 // Tests that restoring a placeholder URL is correctly restored.  This is a
 // regression test from http://crbug.com/1011758.
-// Flaky: https://crbug.com/1054372
-- (void)DISABLED_testRestoreHistoryToPlaceholderURL {
+- (void)testRestoreHistoryToPlaceholderURL {
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
   const GURL destinationURL("chrome://crash");
   [ChromeEarlGrey loadURL:destinationURL];
diff --git a/ios/chrome/credential_provider_extension/credential_provider_extension_localize_strings_config.plist b/ios/chrome/credential_provider_extension/credential_provider_extension_localize_strings_config.plist
index 6a72e8e..c79a398 100644
--- a/ios/chrome/credential_provider_extension/credential_provider_extension_localize_strings_config.plist
+++ b/ios/chrome/credential_provider_extension/credential_provider_extension_localize_strings_config.plist
@@ -16,6 +16,8 @@
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_CONSENT_ENABLE_BUTTON_TITLE</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_CONSENT_SUBTITLE</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_CONSENT_TITLE</string>
+				<string>IDS_IOS_CREDENTIAL_PROVIDER_EMPTY_CREDENTIALS_SUBTITLE</string>
+				<string>IDS_IOS_CREDENTIAL_PROVIDER_EMPTY_CREDENTIALS_TITLE</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_EXTENSION_CANCEL</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_STALE_CREDENTIALS_SUBTITLE</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_STALE_CREDENTIALS_TITLE</string>
diff --git a/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd
index 7e8b504..d9b6c6d 100644
--- a/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd
+++ b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd
@@ -166,6 +166,12 @@
       <message name="IDS_IOS_CREDENTIAL_PROVIDER_CONSENT_TITLE" desc="Title for consent screen. 'AutoFill' matches what Apple shows in settings. For other languages see: Settings  > Safari > AutoFill." meaning="Title to show when enabling the extension">
         AutoFill Chrome Passwords
       </message>
+      <message name="IDS_IOS_CREDENTIAL_PROVIDER_EMPTY_CREDENTIALS_SUBTITLE" desc="Subtitle for empty credentials screen" meaning="Subtitle to show when a user has no credentials available.">
+        Save some passwords in Chrome to get started. If you already have passwords stored in your Google account, sign in to Chrome and turn on sync to see them here.
+      </message>
+      <message name="IDS_IOS_CREDENTIAL_PROVIDER_EMPTY_CREDENTIALS_TITLE" desc="Title for empty credentials screen." meaning="Title to show when a user has no credentials available.">
+        No Chrome Passwords
+      </message>
       <message name="IDS_IOS_CREDENTIAL_PROVIDER_EXTENSION_CANCEL" desc="Title for cancel buttons" meaning="Title for buttons meant to cancel an action [Length: 10 chars]">
         Cancel
       </message>
diff --git a/ios/chrome/credential_provider_extension/ui/BUILD.gn b/ios/chrome/credential_provider_extension/ui/BUILD.gn
index d2ee457..dec1cfb 100644
--- a/ios/chrome/credential_provider_extension/ui/BUILD.gn
+++ b/ios/chrome/credential_provider_extension/ui/BUILD.gn
@@ -6,6 +6,8 @@
   sources = [
     "consent_view_controller.h",
     "consent_view_controller.mm",
+    "empty_credentials_view_controller.h",
+    "empty_credentials_view_controller.mm",
     "stale_credentials_view_controller.h",
     "stale_credentials_view_controller.mm",
   ]
diff --git a/ios/chrome/credential_provider_extension/ui/empty_credentials_view_controller.h b/ios/chrome/credential_provider_extension/ui/empty_credentials_view_controller.h
new file mode 100644
index 0000000..9d798c42
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/empty_credentials_view_controller.h
@@ -0,0 +1,13 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_EMPTY_CREDENTIALS_VIEW_CONTROLLER_H_
+#define IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_EMPTY_CREDENTIALS_VIEW_CONTROLLER_H_
+
+#import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h"
+
+@interface EmptyCredentialsViewController : ConfirmationAlertViewController
+@end
+
+#endif  // IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_EMPTY_CREDENTIALS_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/credential_provider_extension/ui/empty_credentials_view_controller.mm b/ios/chrome/credential_provider_extension/ui/empty_credentials_view_controller.mm
new file mode 100644
index 0000000..0351e54e
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/empty_credentials_view_controller.mm
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/credential_provider_extension/ui/empty_credentials_view_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation EmptyCredentialsViewController
+
+#pragma mark - Public
+
+- (void)loadView {
+  self.imageName = @"empty_credentials_illustration";
+  self.helpButtonAvailable = NO;
+  self.primaryActionAvailable = NO;
+  NSString* titleString =
+      NSLocalizedString(@"IDS_IOS_CREDENTIAL_PROVIDER_EMPTY_CREDENTIALS_TITLE",
+                        @"The title in the empty credentials screen.");
+  NSString* subtitleString = NSLocalizedString(
+      @"IDS_IOS_CREDENTIAL_PROVIDER_EMPTY_CREDENTIALS_SUBTITLE",
+      @"The subtitle in the empty credentials screen.");
+  self.titleString = titleString;
+  self.subtitleString = subtitleString;
+  [super loadView];
+}
+
+@end
diff --git a/ios/chrome/credential_provider_extension/ui/resources/BUILD.gn b/ios/chrome/credential_provider_extension/ui/resources/BUILD.gn
index 81dfaa9..13e48c5 100644
--- a/ios/chrome/credential_provider_extension/ui/resources/BUILD.gn
+++ b/ios/chrome/credential_provider_extension/ui/resources/BUILD.gn
@@ -8,6 +8,7 @@
 group("resources") {
   deps = [
     ":consent_illustration",
+    ":empty_credentials_illustration",
     ":stale_credentials_illustration",
   ]
 }
@@ -20,6 +21,14 @@
   ]
 }
 
+imageset("empty_credentials_illustration") {
+  sources = [
+    "empty_credentials_illustration.imageset/Contents.json",
+    "empty_credentials_illustration.imageset/illustration_dark.png",
+    "empty_credentials_illustration.imageset/illustration_light.png",
+  ]
+}
+
 imageset("stale_credentials_illustration") {
   sources = [
     "stale_credentials_illustration.imageset/Contents.json",
diff --git a/ios/chrome/credential_provider_extension/ui/resources/no_data_illustration.imageset/Contents.json b/ios/chrome/credential_provider_extension/ui/resources/no_data_illustration.imageset/Contents.json
new file mode 100644
index 0000000..e5b1b4d6
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/resources/no_data_illustration.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "illustration_light.png"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "illustration_dark.png",
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ]
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/ios/chrome/credential_provider_extension/ui/resources/no_data_illustration.imageset/illustration_dark.png b/ios/chrome/credential_provider_extension/ui/resources/no_data_illustration.imageset/illustration_dark.png
new file mode 100644
index 0000000..dc7bb7b
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/resources/no_data_illustration.imageset/illustration_dark.png
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/no_data_illustration.imageset/illustration_light.png b/ios/chrome/credential_provider_extension/ui/resources/no_data_illustration.imageset/illustration_light.png
new file mode 100644
index 0000000..9edcfcba
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/resources/no_data_illustration.imageset/illustration_light.png
Binary files differ
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 7665b29..9bc0146 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -253,6 +253,7 @@
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/authentication:authentication",
     "//ios/chrome/browser/ui/authentication/cells",
+    "//ios/chrome/browser/ui/authentication/signin/advanced_settings_signin:constants",
     "//ios/chrome/browser/ui/autofill:autofill_ui",
     "//ios/chrome/browser/ui/autofill/manual_fill",
     "//ios/chrome/browser/ui/autofill/manual_fill:manual_fill_ui",
@@ -392,6 +393,7 @@
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/authentication:eg_app_support+eg2",
     "//ios/chrome/browser/ui/authentication/cells",
+    "//ios/chrome/browser/ui/authentication/signin/advanced_settings_signin:constants",
     "//ios/chrome/browser/ui/autofill:autofill_ui",
     "//ios/chrome/browser/ui/autofill:eg_app_support+eg2",
     "//ios/chrome/browser/ui/autofill/manual_fill",
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index ec78272..1968237 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -9,6 +9,7 @@
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/autofill/form_suggestion_constants.h"
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_constants.h"
+#import "ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_constants.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/address_view_controller.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/card_coordinator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/card_view_controller.h"
@@ -38,7 +39,6 @@
 #import "ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_ui_constants.h"
 #import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanner_view_controller.h"
 #import "ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller_constants.h"
-#import "ios/chrome/browser/ui/settings/google_services/advanced_signin_settings_constants.h"
 #import "ios/chrome/browser/ui/settings/google_services/google_services_settings_constants.h"
 #import "ios/chrome/browser/ui/settings/import_data_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h"
diff --git a/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni b/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni
index f1a1a9c..f8806ec 100644
--- a/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni
+++ b/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni
@@ -137,9 +137,12 @@
   ios_eg2_test(target_name) {
     forward_variables_from(invoker,
                            [
-                             "xcode_test_application_name",
                              "deps",
                              "data_deps",
+                             "executable_args",
+                             "retries",
+                             "shards",
+                             "xcode_test_application_name",
                            ])
 
     xctest_bundle_principal_class = "ChromeEGTestBundleMain"
diff --git a/ios/third_party/earl_grey/ios_eg_test.gni b/ios/third_party/earl_grey/ios_eg_test.gni
index 82024fb..64bade8 100644
--- a/ios/third_party/earl_grey/ios_eg_test.gni
+++ b/ios/third_party/earl_grey/ios_eg_test.gni
@@ -2,14 +2,25 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/ios/ios_test_runner_wrapper.gni")
 import("//build/config/ios/rules.gni")
 import("//ios/build/chrome_build.gni")
 import("//ios/public/provider/chrome/browser/build_config.gni")
 
 # EarlGrey tests are just XCTests that also depends on EarlGrey.
 template("ios_eg_test") {
-  ios_xctest_test(target_name) {
-    forward_variables_from(invoker, "*")
+  _target_name = target_name
+  _test_target = "${target_name}_test"
+  ios_xctest_test(_test_target) {
+    forward_variables_from(invoker,
+                           "*",
+                           [
+                             "executable_args",
+                             "retries",
+                             "shards",
+                             "xctest",
+                             "xcode_parallelization",
+                           ])
     if (!defined(bundle_deps)) {
       bundle_deps = []
     }
@@ -28,6 +39,55 @@
       "//ios/third_party/earl_grey:earl_grey+link",
       "//ios/third_party/ochamcrest:ochamcrest+link",
     ]
+
+    # TODO(crbug.com/1056328) Because we change the target name, the subnodes
+    # are going to append with the _test in the naming, which won't be backwards
+    # compatible during migration from iOS recipe to Chromium.
+    output_name = "${_target_name}"
+  }
+
+  ios_test_runner_wrapper(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "data",
+                             "data_deps",
+                             "deps",
+                             "executable_args",
+                             "retries",
+                             "shards",
+                             "xctest",
+                             "xcode_parallelization",
+                           ])
+
+    _root_build_dir = rebase_path("${root_build_dir}", root_build_dir)
+
+    # include the test target above as data_deps
+    if (!defined(data_deps)) {
+      data_deps = []
+    }
+    data_deps += [ ":${_test_target}" ]
+
+    if (!defined(executable_args)) {
+      executable_args = []
+    }
+
+    # EG test apps are *.app format. No host, but required xctest and may need
+    # xcode-parallelization
+    executable_args += [
+      "--app",
+      "@WrappedPath(${_root_build_dir}/${target_name}.app)",
+    ]
+
+    # Default xctest to true for EG tests. If set, only set arg if value = true
+    if (!defined(xctest) || (defined(xctest) && xctest)) {
+      executable_args += [ "--xctest" ]
+    }
+
+    # Default xcode_parallelization to true for EG tests.
+    if (!defined(xcode_parallelization) ||
+        (defined(xcode_parallelization) && xcode_parallelization)) {
+      executable_args += [ "--xcode-parallelization" ]
+    }
   }
 }
 
diff --git a/ios/third_party/earl_grey2/ios_eg2_test.gni b/ios/third_party/earl_grey2/ios_eg2_test.gni
index 02dd117..5f2c44f 100644
--- a/ios/third_party/earl_grey2/ios_eg2_test.gni
+++ b/ios/third_party/earl_grey2/ios_eg2_test.gni
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/ios/ios_test_runner_wrapper.gni")
 import("//build/config/ios/rules.gni")
 import("//ios/build/chrome_build.gni")
 import("//ios/public/provider/chrome/browser/build_config.gni")
@@ -63,7 +64,9 @@
       defined(invoker.deps),
       "deps must be defined for $target_name to include at least one earl grey test file.")
 
-  ios_xcuitest_test(target_name) {
+  _target_name = target_name
+  _test_target = "${target_name}_test"
+  ios_xcuitest_test(_test_target) {
     forward_variables_from(invoker,
                            [
                              "xcode_test_application_name",
@@ -77,6 +80,51 @@
       deps = []
     }
     deps += [ "//ios/third_party/earl_grey2:test_lib" ]
+
+    # TODO(crbug.com/1056328) Because we change the target name, the subnodes
+    # are going to append with the _test in the naming, which won't be backwards
+    # compatible during migration from iOS recipe to Chromium.
+    output_name = "${_target_name}"
+  }
+
+  ios_test_runner_wrapper(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "data",
+                             "data_deps",
+                             "deps",
+                             "executable_args",
+                             "retries",
+                             "shards",
+                             "xcode_test_application_name",
+                           ])
+    _root_build_dir = rebase_path("${root_build_dir}", root_build_dir)
+
+    if (!defined(data_deps)) {
+      data_deps = []
+    }
+
+    # Include the top ios_eg2_test target, and the host app
+    data_deps += [ ":${_test_target}" ]
+
+    if (!defined(executable_args)) {
+      executable_args = []
+    }
+
+    # EG2 tests app are bundled as *-Runner.app, while the host app is bundled
+    # as *.app.
+    executable_args += [
+      "--app",
+      "@WrappedPath(${_root_build_dir}/${target_name}-Runner.app)",
+    ]
+    executable_args += [
+      "--host-app",
+      "@WrappedPath(${_root_build_dir}/${xcode_test_application_name}.app)",
+    ]
+
+    # All EG2 tests require xcode-parallelization. EG2 doesn't use xctest, so we
+    # leave undefined
+    executable_args += [ "--xcode-parallelization" ]
   }
 }
 
diff --git a/media/audio/pulse/audio_manager_pulse.cc b/media/audio/pulse/audio_manager_pulse.cc
index 829846f..1024ada3 100644
--- a/media/audio/pulse/audio_manager_pulse.cc
+++ b/media/audio/pulse/audio_manager_pulse.cc
@@ -160,7 +160,7 @@
     const std::string& device_id,
     const LogCallback& log_callback) {
   DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
-  return MakeInputStream(params, device_id);
+  return MakeInputStream(params, device_id, log_callback);
 }
 
 AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream(
@@ -168,7 +168,7 @@
     const std::string& device_id,
     const LogCallback& log_callback) {
   DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
-  return MakeInputStream(params, device_id);
+  return MakeInputStream(params, device_id, log_callback);
 }
 
 std::string AudioManagerPulse::GetDefaultInputDeviceID() {
@@ -246,15 +246,18 @@
 AudioOutputStream* AudioManagerPulse::MakeOutputStream(
     const AudioParameters& params,
     const std::string& device_id,
-    const LogCallback& log_callback) {
+    LogCallback log_callback) {
   DCHECK(!device_id.empty());
-  return new PulseAudioOutputStream(params, device_id, this, log_callback);
+  return new PulseAudioOutputStream(params, device_id, this,
+                                    std::move(log_callback));
 }
 
 AudioInputStream* AudioManagerPulse::MakeInputStream(
-    const AudioParameters& params, const std::string& device_id) {
-  return new PulseAudioInputStream(this, device_id, params,
-                                   input_mainloop_, input_context_);
+    const AudioParameters& params,
+    const std::string& device_id,
+    LogCallback log_callback) {
+  return new PulseAudioInputStream(this, device_id, params, input_mainloop_,
+                                   input_context_, std::move(log_callback));
 }
 
 void AudioManagerPulse::UpdateNativeAudioHardwareInfo() {
diff --git a/media/audio/pulse/audio_manager_pulse.h b/media/audio/pulse/audio_manager_pulse.h
index 256c163..4e10d88 100644
--- a/media/audio/pulse/audio_manager_pulse.h
+++ b/media/audio/pulse/audio_manager_pulse.h
@@ -90,11 +90,12 @@
   // Called by MakeLinearOutputStream and MakeLowLatencyOutputStream.
   AudioOutputStream* MakeOutputStream(const AudioParameters& params,
                                       const std::string& device_id,
-                                      const LogCallback& log_callback);
+                                      LogCallback log_callback);
 
   // Called by MakeLinearInputStream and MakeLowLatencyInputStream.
   AudioInputStream* MakeInputStream(const AudioParameters& params,
-                                    const std::string& device_id);
+                                    const std::string& device_id,
+                                    LogCallback log_callback);
 
   // Updates |native_input_sample_rate_| and |native_channel_count_|.
   void UpdateNativeAudioHardwareInfo();
diff --git a/media/audio/pulse/pulse_input.cc b/media/audio/pulse/pulse_input.cc
index 8afeadc..ed42cdb 100644
--- a/media/audio/pulse/pulse_input.cc
+++ b/media/audio/pulse/pulse_input.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include "base/logging.h"
+#include "base/strings/stringprintf.h"
 #include "media/audio/audio_device_description.h"
 #include "media/audio/pulse/audio_manager_pulse.h"
 #include "media/audio/pulse/pulse_util.h"
@@ -14,19 +15,37 @@
 
 namespace media {
 
+namespace {
+
+PRINTF_FORMAT(2, 3)
+void SendLogMessage(const AudioManagerBase::LogCallback& callback,
+                    const char* format,
+                    ...) {
+  if (callback.is_null())
+    return;
+  va_list args;
+  va_start(args, format);
+  callback.Run("PAIS::" + base::StringPrintV(format, args));
+  va_end(args);
+}
+
+}  // namespace
+
 using pulse::AutoPulseLock;
 using pulse::WaitForOperationCompletion;
 
 // Number of blocks of buffers used in the |fifo_|.
 const int kNumberOfBlocksBufferInFifo = 2;
 
-PulseAudioInputStream::PulseAudioInputStream(AudioManagerPulse* audio_manager,
-                                             const std::string& device_name,
-                                             const AudioParameters& params,
-                                             pa_threaded_mainloop* mainloop,
-                                             pa_context* context)
+PulseAudioInputStream::PulseAudioInputStream(
+    AudioManagerPulse* audio_manager,
+    const std::string& device_name,
+    const AudioParameters& params,
+    pa_threaded_mainloop* mainloop,
+    pa_context* context,
+    AudioManager::LogCallback log_callback)
     : audio_manager_(audio_manager),
-      callback_(NULL),
+      callback_(nullptr),
       device_name_(device_name),
       params_(params),
       channels_(0),
@@ -38,10 +57,13 @@
             kNumberOfBlocksBufferInFifo),
       pa_mainloop_(mainloop),
       pa_context_(context),
-      handle_(NULL) {
+      log_callback_(std::move(log_callback)),
+      handle_(nullptr) {
   DCHECK(mainloop);
   DCHECK(context);
   CHECK(params_.IsValid());
+  SendLogMessage(log_callback_, "%s({device_id=%s}, {params=[%s]})", __func__,
+                 device_name.c_str(), params.AsHumanReadableString().c_str());
 }
 
 PulseAudioInputStream::~PulseAudioInputStream() {
@@ -52,13 +74,19 @@
 
 bool PulseAudioInputStream::Open() {
   DCHECK(thread_checker_.CalledOnValidThread());
+  SendLogMessage(log_callback_, "%s()", __func__);
   if (device_name_ == AudioDeviceDescription::kDefaultDeviceId &&
-      audio_manager_->DefaultSourceIsMonitor())
+      audio_manager_->DefaultSourceIsMonitor()) {
+    SendLogMessage(log_callback_, "%s => (ERROR: can't open monitor device)",
+                   __func__);
     return false;
+  }
 
   AutoPulseLock auto_lock(pa_mainloop_);
   if (!pulse::CreateInputStream(pa_mainloop_, pa_context_, &handle_, params_,
                                 device_name_, &StreamNotifyCallback, this)) {
+    SendLogMessage(log_callback_, "%s => (ERROR: failed to open PA stream)",
+                   __func__);
     return false;
   }
 
@@ -71,6 +99,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(callback);
   DCHECK(handle_);
+  SendLogMessage(log_callback_, "%s()", __func__);
 
   // AGC needs to be started out of the lock.
   StartAgc();
@@ -97,6 +126,7 @@
 
 void PulseAudioInputStream::Stop() {
   DCHECK(thread_checker_.CalledOnValidThread());
+  SendLogMessage(log_callback_, "%s()", __func__);
   AutoPulseLock auto_lock(pa_mainloop_);
   if (!stream_started_)
     return;
@@ -118,23 +148,24 @@
   }
 
   // Stop the stream.
-  pa_stream_set_read_callback(handle_, NULL, NULL);
+  pa_stream_set_read_callback(handle_, nullptr, nullptr);
   operation =
       pa_stream_cork(handle_, 1, &pulse::StreamSuccessCallback, pa_mainloop_);
   if (!WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
                                   handle_)) {
     callback_->OnError();
   }
-  callback_ = NULL;
+  callback_ = nullptr;
 }
 
 void PulseAudioInputStream::Close() {
   DCHECK(thread_checker_.CalledOnValidThread());
+  SendLogMessage(log_callback_, "%s()", __func__);
   {
     AutoPulseLock auto_lock(pa_mainloop_);
     if (handle_) {
       // Disable all the callbacks before disconnecting.
-      pa_stream_set_state_callback(handle_, NULL, NULL);
+      pa_stream_set_state_callback(handle_, nullptr, nullptr);
       pa_operation* operation =
           pa_stream_flush(handle_, &pulse::StreamSuccessCallback, pa_mainloop_);
       WaitForOperationCompletion(pa_mainloop_, operation, pa_context_, handle_);
@@ -144,7 +175,7 @@
 
       // Release PulseAudio structures.
       pa_stream_unref(handle_);
-      handle_ = NULL;
+      handle_ = nullptr;
     }
   }
 
@@ -161,9 +192,10 @@
   AutoPulseLock auto_lock(pa_mainloop_);
   if (!handle_)
     return;
+  SendLogMessage(log_callback_, "%s({volume=%.2f})", __func__, volume);
 
   size_t index = pa_stream_get_device_index(handle_);
-  pa_operation* operation = NULL;
+  pa_operation* operation = nullptr;
   if (!channels_) {
     // Get the number of channels for the source only when the |channels_| is 0.
     // We are assuming the stream source is not changed on the fly here.
@@ -172,7 +204,9 @@
     if (!WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
                                     handle_) ||
         !channels_) {
-      DLOG(WARNING) << "Failed to get the number of channels for the source";
+      SendLogMessage(log_callback_,
+                     "%s => (WARNING: failed to read number of channels)",
+                     __func__);
       return;
     }
   }
@@ -180,7 +214,7 @@
   pa_cvolume pa_volume;
   pa_cvolume_set(&pa_volume, channels_, volume);
   operation = pa_context_set_source_volume_by_index(
-      pa_context_, index, &pa_volume, NULL, NULL);
+      pa_context_, index, &pa_volume, nullptr, nullptr);
 
   // Don't need to wait for this task to complete.
   pa_operation_unref(operation);
@@ -309,7 +343,7 @@
                                           params_.sample_rate()));
   do {
     size_t length = 0;
-    const void* data = NULL;
+    const void* data = nullptr;
     pa_stream_peek(handle_, &data, &length);
     if (!data || length == 0)
       break;
diff --git a/media/audio/pulse/pulse_input.h b/media/audio/pulse/pulse_input.h
index 56b3906..6287ec8 100644
--- a/media/audio/pulse/pulse_input.h
+++ b/media/audio/pulse/pulse_input.h
@@ -14,6 +14,7 @@
 #include "media/audio/agc_audio_stream.h"
 #include "media/audio/audio_device_name.h"
 #include "media/audio/audio_io.h"
+#include "media/audio/audio_manager.h"
 #include "media/base/audio_block_fifo.h"
 #include "media/base/audio_parameters.h"
 
@@ -27,7 +28,8 @@
                         const std::string& device_name,
                         const AudioParameters& params,
                         pa_threaded_mainloop* mainloop,
-                        pa_context* context);
+                        pa_context* context,
+                        AudioManager::LogCallback log_callback);
 
   ~PulseAudioInputStream() override;
 
@@ -76,7 +78,12 @@
 
   // PulseAudio API structs.
   pa_threaded_mainloop* pa_mainloop_; // Weak.
+
   pa_context* pa_context_;  // Weak.
+
+  // Callback to send log messages to registered clients.
+  AudioManager::LogCallback log_callback_;
+
   pa_stream* handle_;
 
   base::ThreadChecker thread_checker_;
diff --git a/media/audio/pulse/pulse_output.cc b/media/audio/pulse/pulse_output.cc
index 65f307e4..40a390c 100644
--- a/media/audio/pulse/pulse_output.cc
+++ b/media/audio/pulse/pulse_output.cc
@@ -63,7 +63,7 @@
     const AudioParameters& params,
     const std::string& device_id,
     AudioManagerBase* manager,
-    const AudioManager::LogCallback log_callback)
+    AudioManager::LogCallback log_callback)
     : params_(AudioParameters(params.format(),
                               params.channel_layout(),
                               params.sample_rate(),
diff --git a/media/audio/pulse/pulse_output.h b/media/audio/pulse/pulse_output.h
index 4e58ad5..59ce6168 100644
--- a/media/audio/pulse/pulse_output.h
+++ b/media/audio/pulse/pulse_output.h
@@ -43,7 +43,7 @@
   PulseAudioOutputStream(const AudioParameters& params,
                          const std::string& device_id,
                          AudioManagerBase* manager,
-                         const AudioManager::LogCallback log_callback);
+                         AudioManager::LogCallback log_callback);
 
   ~PulseAudioOutputStream() override;
 
diff --git a/media/audio/win/audio_low_latency_input_win.cc b/media/audio/win/audio_low_latency_input_win.cc
index db4b1d1..8df4203 100644
--- a/media/audio/win/audio_low_latency_input_win.cc
+++ b/media/audio/win/audio_low_latency_input_win.cc
@@ -34,6 +34,11 @@
 
 constexpr uint32_t KSAUDIO_SPEAKER_UNSUPPORTED = 0;
 
+// Converts a COM error into a human-readable string.
+std::string ErrorToString(HRESULT hresult) {
+  return CoreAudioUtil::ErrorToString(hresult);
+}
+
 // Errors when initializing the audio client related to the audio format. Split
 // by whether we're using format conversion or not. Used for reporting stats -
 // do not renumber entries.
@@ -88,14 +93,65 @@
   }
 }
 
+const char* StreamOpenResultToString(
+    WASAPIAudioInputStream::StreamOpenResult result) {
+  switch (result) {
+    case WASAPIAudioInputStream::OPEN_RESULT_OK:
+      return "OK";
+    case WASAPIAudioInputStream::OPEN_RESULT_CREATE_INSTANCE:
+      return "CREATE_INSTANCE";
+    case WASAPIAudioInputStream::OPEN_RESULT_NO_ENDPOINT:
+      return "NO_ENDPOINT";
+    case WASAPIAudioInputStream::OPEN_RESULT_NO_STATE:
+      return "NO_STATE";
+    case WASAPIAudioInputStream::OPEN_RESULT_DEVICE_NOT_ACTIVE:
+      return "DEVICE_NOT_ACTIVE";
+    case WASAPIAudioInputStream::OPEN_RESULT_ACTIVATION_FAILED:
+      return "ACTIVATION_FAILED";
+    case WASAPIAudioInputStream::OPEN_RESULT_FORMAT_NOT_SUPPORTED:
+      return "FORMAT_NOT_SUPPORTED";
+    case WASAPIAudioInputStream::OPEN_RESULT_AUDIO_CLIENT_INIT_FAILED:
+      return "AUDIO_CLIENT_INIT_FAILED";
+    case WASAPIAudioInputStream::OPEN_RESULT_GET_BUFFER_SIZE_FAILED:
+      return "GET_BUFFER_SIZE_FAILED";
+    case WASAPIAudioInputStream::OPEN_RESULT_LOOPBACK_ACTIVATE_FAILED:
+      return "LOOPBACK_ACTIVATE_FAILED";
+    case WASAPIAudioInputStream::OPEN_RESULT_LOOPBACK_INIT_FAILED:
+      return "LOOPBACK_INIT_FAILED";
+    case WASAPIAudioInputStream::OPEN_RESULT_SET_EVENT_HANDLE:
+      return "SET_EVENT_HANDLE";
+    case WASAPIAudioInputStream::OPEN_RESULT_NO_CAPTURE_CLIENT:
+      return "NO_CAPTURE_CLIENT";
+    case WASAPIAudioInputStream::OPEN_RESULT_NO_AUDIO_VOLUME:
+      return "NO_AUDIO_VOLUME";
+    case WASAPIAudioInputStream::OPEN_RESULT_OK_WITH_RESAMPLING:
+      return "OK_WITH_RESAMPLING";
+  }
+  return "UNKNOWN";
+}
+
+std::string GetOpenLogString(WASAPIAudioInputStream::StreamOpenResult result,
+                             HRESULT hr,
+                             WAVEFORMATEXTENSIBLE input_format,
+                             WAVEFORMATEX output_format) {
+  return base::StringPrintf(
+      "WAIS::Open => (ERROR: result=%s, hresult=%#lx, input_format=[%s], "
+      "output_format=[%s])",
+      StreamOpenResultToString(result), hr,
+      CoreAudioUtil::WaveFormatToString(&input_format).c_str(),
+      CoreAudioUtil::WaveFormatToString(&output_format).c_str());
+}
+
 }  // namespace
 
 WASAPIAudioInputStream::WASAPIAudioInputStream(
     AudioManagerWin* manager,
     const AudioParameters& params,
     const std::string& device_id,
-    const AudioManager::LogCallback& log_callback)
-    : manager_(manager), device_id_(device_id), log_callback_(log_callback) {
+    AudioManager::LogCallback log_callback)
+    : manager_(manager),
+      device_id_(device_id),
+      log_callback_(std::move(log_callback)) {
   DCHECK(manager_);
   DCHECK(!device_id_.empty());
   DCHECK(!log_callback_.is_null());
@@ -103,10 +159,13 @@
   DCHECK(params.channel_layout() == CHANNEL_LAYOUT_MONO ||
          params.channel_layout() == CHANNEL_LAYOUT_STEREO ||
          params.channel_layout() == CHANNEL_LAYOUT_DISCRETE);
+  SendLogMessage("%s({device_id=%s}, {params=[%s]})", __func__,
+                 device_id.c_str(), params.AsHumanReadableString().c_str());
 
   // Load the Avrt DLL if not already loaded. Required to support MMCSS.
   bool avrt_init = avrt::Initialize();
-  DCHECK(avrt_init) << "Failed to load the Avrt.dll";
+  if (!avrt_init)
+    SendLogMessage("%s => (WARNING: failed to load Avrt.dll)", __func__);
 
   const SampleFormat kSampleFormat = kSampleFormatS16;
 
@@ -117,7 +176,6 @@
   // to fit the current input audio device. If so, a FIFO and/or and audio
   // converter might be needed to ensure that the output format of this stream
   // matches what the client asks for.
-  DVLOG(1) << params.AsHumanReadableString();
   WAVEFORMATEX* format = &input_format_.Format;
   format->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
   format->nChannels = params.channels();
@@ -133,7 +191,8 @@
   input_format_.dwChannelMask =
       ChannelLayoutToChannelConfig(params.channel_layout());
   input_format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-  DVLOG(1) << "Input: " << CoreAudioUtil::WaveFormatToString(&input_format_);
+  SendLogMessage("%s => (audio engine format=[%s])", __func__,
+                 CoreAudioUtil::WaveFormatToString(&input_format_).c_str());
 
   // Set up the fixed output format based on |params|. Will not be changed and
   // does not required an extended wave format structure since any multi-channel
@@ -145,7 +204,8 @@
   output_format_.nBlockAlign = format->nBlockAlign;
   output_format_.nAvgBytesPerSec = format->nAvgBytesPerSec;
   output_format_.cbSize = 0;
-  DVLOG(1) << "Output: " << CoreAudioUtil::WaveFormatToString(&output_format_);
+  SendLogMessage("%s => (audio sink format=[%s])", __func__,
+                 CoreAudioUtil::WaveFormatToString(&output_format_).c_str());
 
   // Size in bytes of each audio frame.
   frame_size_bytes_ = format->nBlockAlign;
@@ -154,8 +214,10 @@
   // endpoint device in each capture event.
   packet_size_bytes_ = params.GetBytesPerBuffer(kSampleFormat);
   packet_size_frames_ = packet_size_bytes_ / format->nBlockAlign;
-  DVLOG(1) << "Number of bytes per audio frame  : " << frame_size_bytes_;
-  DVLOG(1) << "Number of audio frames per packet: " << packet_size_frames_;
+  SendLogMessage(
+      "%s => (packet size=[%zu bytes/%zu audio frames/%.3f milliseconds])",
+      __func__, packet_size_bytes_, packet_size_frames_,
+      params.GetBufferDuration().InMillisecondsF());
 
   // All events are auto-reset events and non-signaled initially.
 
@@ -175,16 +237,13 @@
 
 bool WASAPIAudioInputStream::Open() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Verify that we are not already opened.
+  SendLogMessage("%s([opened=%s])", __func__, opened_ ? "true" : "false");
   if (opened_) {
-    log_callback_.Run("WASAPIAIS::Open: already open");
     return false;
   }
 
-  // Obtain a reference to the IMMDevice interface of the capturing
-  // device with the specified unique identifier or role which was
-  // set at construction.
+  // Obtain a reference to the IMMDevice interface of the capturing device with
+  // the specified unique identifier or role which was set at construction.
   HRESULT hr = SetCaptureDevice();
   if (FAILED(hr)) {
     ReportOpenResult(hr);
@@ -231,7 +290,8 @@
 void WASAPIAudioInputStream::Start(AudioInputCallback* callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(callback);
-  DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully";
+  SendLogMessage("%s([opened=%s, started=%s])", __func__,
+                 opened_ ? "true" : "false", started_ ? "true" : "false");
   if (!opened_)
     return;
 
@@ -243,7 +303,8 @@
   // Valid volume levels are in the range 0.0 to 1.0.
   // See http://crbug.com/1014443 for details why this is needed.
   if (GetVolume() == 0.0) {
-    DLOG(WARNING) << "Input audio session starts with zero volume";
+    SendLogMessage("%s => (WARNING: Input audio session starts at zero volume)",
+                   __func__);
     audio_session_starts_at_zero_volume_ = true;
   }
 
@@ -279,18 +340,15 @@
   // Start streaming data between the endpoint buffer and the audio engine.
   HRESULT hr = audio_client_->Start();
   if (FAILED(hr)) {
-    DLOG(ERROR) << "Failed to start input streaming.";
-    log_callback_.Run(base::StringPrintf(
-        "WASAPIAIS::Start: Failed to start audio client, hresult = %#lx", hr));
+    SendLogMessage("%s => (ERROR: IAudioClient::Start=[%s])", __func__,
+                   ErrorToString(hr).c_str());
   }
 
   if (SUCCEEDED(hr) && audio_render_client_for_loopback_.Get()) {
     hr = audio_render_client_for_loopback_->Start();
     if (FAILED(hr))
-      log_callback_.Run(base::StringPrintf(
-          "WASAPIAIS::Start: Failed to start render client for loopback, "
-          "hresult = %#lx",
-          hr));
+      SendLogMessage("%s => (ERROR: IAudioClient::Start=[%s] (loopback))",
+                     __func__, ErrorToString(hr).c_str());
   }
 
   started_ = SUCCEEDED(hr);
@@ -298,7 +356,7 @@
 
 void WASAPIAudioInputStream::Stop() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DVLOG(1) << "WASAPIAudioInputStream::Stop()";
+  SendLogMessage("%s([started=%s])", __func__, started_ ? "true" : "false");
   if (!started_)
     return;
 
@@ -328,7 +386,8 @@
   // Stop the input audio streaming.
   HRESULT hr = audio_client_->Stop();
   if (FAILED(hr)) {
-    LOG(ERROR) << "Failed to stop input streaming.";
+    SendLogMessage("%s => (ERROR: IAudioClient::Stop=[%s])", __func__,
+                   ErrorToString(hr).c_str());
   }
 
   // Wait until the thread completes and perform cleanup.
@@ -343,8 +402,6 @@
   if (add_uma_histogram) {
     base::UmaHistogramBoolean("Media.Audio.InputVolumeStartsAtZeroWin",
                               audio_session_starts_at_zero_volume_);
-    DVLOG(1) << "Media.Audio.InputVolumeStartsAtZeroWin: "
-             << audio_session_starts_at_zero_volume_;
     audio_session_starts_at_zero_volume_ = false;
   }
 
@@ -353,7 +410,7 @@
 }
 
 void WASAPIAudioInputStream::Close() {
-  DVLOG(1) << "WASAPIAudioInputStream::Close()";
+  SendLogMessage("%s()", __func__);
   // It is valid to call Close() before calling open or Start().
   // It is also valid to call Close() after Start() has been called.
   Stop();
@@ -369,7 +426,7 @@
 }
 
 double WASAPIAudioInputStream::GetMaxVolume() {
-  // Verify that Open() has been called succesfully, to ensure that an audio
+  // Verify that Open() has been called successfully, to ensure that an audio
   // session exists and that an ISimpleAudioVolume interface has been created.
   DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully";
   if (!opened_)
@@ -384,8 +441,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_GE(volume, 0.0);
   DCHECK_LE(volume, 1.0);
-
-  DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully";
+  SendLogMessage("%s({volume=%.2f} [opened=%s])", __func__, volume,
+                 opened_ ? "true" : "false");
   if (!opened_)
     return;
 
@@ -393,8 +450,10 @@
   // 0.0 to 1.0. Ignore volume-change events.
   HRESULT hr = simple_audio_volume_->SetMasterVolume(static_cast<float>(volume),
                                                      nullptr);
-  if (FAILED(hr))
-    DLOG(WARNING) << "Failed to set new input master volume.";
+  if (FAILED(hr)) {
+    SendLogMessage("%s => (ERROR: ISimpleAudioVolume::SetMasterVolume=[%s])",
+                   __func__, ErrorToString(hr).c_str());
+  }
 
   // Update the AGC volume level based on the last setting above. Note that,
   // the volume-level resolution is not infinite and it is therefore not
@@ -412,8 +471,10 @@
   // Retrieve the current volume level. The value is in the range 0.0 to 1.0.
   float level = 0.0f;
   HRESULT hr = simple_audio_volume_->GetMasterVolume(&level);
-  if (FAILED(hr))
-    DLOG(WARNING) << "Failed to get input master volume.";
+  if (FAILED(hr)) {
+    SendLogMessage("%s => (ERROR: ISimpleAudioVolume::GetMasterVolume=[%s])",
+                   __func__, ErrorToString(hr).c_str());
+  }
 
   return static_cast<double>(level);
 }
@@ -427,8 +488,10 @@
   // Retrieves the current muting state for the audio session.
   BOOL is_muted = FALSE;
   HRESULT hr = simple_audio_volume_->GetMute(&is_muted);
-  if (FAILED(hr))
-    DLOG(WARNING) << "Failed to get input master volume.";
+  if (FAILED(hr)) {
+    SendLogMessage("%s => (ERROR: ISimpleAudioVolume::GetMute=[%s])", __func__,
+                   ErrorToString(hr).c_str());
+  }
 
   return is_muted != FALSE;
 }
@@ -438,6 +501,15 @@
   // Not supported. Do nothing.
 }
 
+void WASAPIAudioInputStream::SendLogMessage(const char* format, ...) {
+  if (log_callback_.is_null())
+    return;
+  va_list args;
+  va_start(args, format);
+  log_callback_.Run("WAIS::" + base::StringPrintV(format, args));
+  va_end(args);
+}
+
 void WASAPIAudioInputStream::Run() {
   ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
 
@@ -452,7 +524,9 @@
     // Failed to enable MMCSS on this thread. It is not fatal but can lead
     // to reduced QoS at high load.
     DWORD err = GetLastError();
-    LOG(WARNING) << "Failed to enable MMCSS (error code=" << err << ").";
+    LOG(ERROR) << "WAIS::" << __func__
+               << " => (ERROR: Failed to enable MMCSS (error code=" << err
+               << "))";
   }
 
   // Allocate a buffer with a size that enables us to take care of cases like:
@@ -476,7 +550,6 @@
   DCHECK(!fifo_);
   fifo_.reset(new AudioBlockFifo(input_format_.Format.nChannels,
                                  packet_size_frames_, buffers_required));
-
   DVLOG(1) << "AudioBlockFifo buffer count: " << buffers_required;
 
   bool recording = true;
@@ -552,7 +625,9 @@
     // rely on the next WaitForMultipleObjects? Do we expect the next wait to be
     // successful sometimes?
     if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to get data from the capture buffer";
+      LOG(ERROR) << "WAIS::" << __func__
+                 << " => (ERROR: IAudioCaptureClient::GetBuffer=["
+                 << ErrorToString(hr).c_str() << "])";
       break;
     }
 
@@ -607,7 +682,12 @@
     }
 
     hr = audio_capture_client_->ReleaseBuffer(num_frames_to_read);
-    DLOG_IF(ERROR, FAILED(hr)) << "Failed to release capture buffer";
+    if (FAILED(hr)) {
+      LOG(ERROR) << "WAIS::" << __func__
+                 << " => (ERROR: IAudioCaptureClient::ReleaseBuffer=["
+                 << ErrorToString(hr).c_str() << "])";
+      break;
+    }
 
     // Get a cached AGC volume level which is updated once every second on the
     // audio manager thread. Note that, |volume| is also updated each time
@@ -652,6 +732,7 @@
 HRESULT WASAPIAudioInputStream::SetCaptureDevice() {
   DCHECK_EQ(OPEN_RESULT_OK, open_result_);
   DCHECK(!endpoint_device_.Get());
+  SendLogMessage("%s()", __func__);
 
   Microsoft::WRL::ComPtr<IMMDeviceEnumerator> enumerator;
   HRESULT hr = ::CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
@@ -729,6 +810,7 @@
 }
 
 bool WASAPIAudioInputStream::DesiredFormatIsSupported(HRESULT* hr) {
+  SendLogMessage("%s()", __func__);
   // An application that uses WASAPI to manage shared-mode streams can rely
   // on the audio engine to perform only limited format conversions. The audio
   // engine can convert between a standard PCM sample size used by the
@@ -742,12 +824,15 @@
   HRESULT hresult = audio_client_->IsFormatSupported(
       AUDCLNT_SHAREMODE_SHARED,
       reinterpret_cast<const WAVEFORMATEX*>(&input_format_), &closest_match);
-  DLOG_IF(ERROR, hresult == S_FALSE)
-      << "Format is not supported but a closest match exists.";
-  if (FAILED(hresult))
-    LOG(ERROR) << "Input format is not supported: " << std::hex << hresult;
+  if (FAILED(hresult)) {
+    SendLogMessage("%s => (ERROR: IAudioClient::IsFormatSupported=[%s])",
+                   __func__, ErrorToString(hresult).c_str());
+  }
 
   if (hresult == S_FALSE) {
+    SendLogMessage(
+        "%s => (WARNING: Format is not supported but a closest match exists)",
+        __func__);
     // Change the format we're going to ask for to better match with what the OS
     // can provide.  If we succeed in initializing the audio client in this
     // format and are able to convert from this format, we will do that
@@ -771,10 +856,10 @@
         input_format->nSamplesPerSec * input_format->nBlockAlign;
 
     if (IsSupportedFormatForConversion(&input_format_)) {
-      DVLOG(1) << "Will convert captured audio: \n"
-               << CoreAudioUtil::WaveFormatToString(&input_format_) << " ==> \n"
-               << CoreAudioUtil::WaveFormatToString(&output_format_);
-
+      SendLogMessage(
+          "%s => (WARNING: Captured audio will be converted: [%s] ==> [%s])",
+          __func__, CoreAudioUtil::WaveFormatToString(&input_format_).c_str(),
+          CoreAudioUtil::WaveFormatToString(&output_format_).c_str());
       SetupConverterAndStoreFormatInfo();
 
       // Indicate that we're good to go with a close match.
@@ -826,12 +911,16 @@
 
   imperfect_buffer_size_conversion_ =
       std::modf(new_frames_per_buffer, &new_frames_per_buffer) != 0.0;
-  DVLOG_IF(1, imperfect_buffer_size_conversion_)
-      << "Audio capture data conversion: Need to inject fifo";
+  if (imperfect_buffer_size_conversion_) {
+    SendLogMessage("%s => (WARNING: Audio capture conversion requires a FIFO)",
+                   __func__);
+  }
 }
 
 HRESULT WASAPIAudioInputStream::InitializeAudioEngine() {
   DCHECK_EQ(OPEN_RESULT_OK, open_result_);
+  SendLogMessage("%s()", __func__);
+
   DWORD flags;
   // Use event-driven mode only for regular input devices. For loopback the
   // EVENTCALLBACK flag is specified when initializing
@@ -850,8 +939,6 @@
   // however cases when there are glitches anyway and it's avoided by setting a
   // larger buffer size. The larger size does not create higher latency for
   // properly implemented drivers.
-  DVLOG(1) << "Audio format used in IAudioClient::Initialize: "
-           << CoreAudioUtil::WaveFormatToString(&input_format_);
   HRESULT hr = audio_client_->Initialize(
       AUDCLNT_SHAREMODE_SHARED, flags,
       100 * 1000 * 10,  // Buffer duration, 100 ms expressed in 100-ns units.
@@ -862,6 +949,8 @@
           : nullptr);
 
   if (FAILED(hr)) {
+    SendLogMessage("%s => (ERROR: IAudioClient::Initialize=[%s])", __func__,
+                   ErrorToString(hr).c_str());
     open_result_ = OPEN_RESULT_AUDIO_CLIENT_INIT_FAILED;
     base::UmaHistogramSparse("Media.Audio.Capture.Win.InitError", hr);
     MaybeReportFormatRelatedInitError(hr);
@@ -877,15 +966,14 @@
     open_result_ = OPEN_RESULT_GET_BUFFER_SIZE_FAILED;
     return hr;
   }
-
-#ifndef NDEBUG
   const int endpoint_buffer_size_ms =
       static_cast<double>(endpoint_buffer_size_frames_ * 1000) /
           input_format_.Format.nSamplesPerSec +
-      0.5;  // Round to closest integer
-  DVLOG(1) << "Endpoint buffer size: " << endpoint_buffer_size_frames_
-           << " frames (" << endpoint_buffer_size_ms << " ms)";
+      0.5;
+  SendLogMessage("%s => (endpoint_buffer_size_frames=%u (%d ms))", __func__,
+                 endpoint_buffer_size_frames_, endpoint_buffer_size_ms);
 
+#ifndef NDEBUG
   // The period between processing passes by the audio engine is fixed for a
   // particular audio endpoint device and represents the smallest processing
   // quantum for the audio engine. This period plus the stream latency between
@@ -926,6 +1014,7 @@
   //
   // http://msdn.microsoft.com/en-us/library/windows/desktop/dd316551(v=vs.85).aspx
   if (AudioDeviceDescription::IsLoopbackDevice(device_id_)) {
+    SendLogMessage("%s => (WARNING: loopback mode is selected", __func__);
     hr = endpoint_device_->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr,
                                     &audio_render_client_for_loopback_);
     if (FAILED(hr)) {
@@ -971,36 +1060,25 @@
     open_result_ = OPEN_RESULT_NO_AUDIO_VOLUME;
 
   audio_client_->GetService(IID_PPV_ARGS(&audio_clock_));
-  if (!audio_clock_)
-    LOG(WARNING) << "IAudioClock unavailable, capture times may be inaccurate.";
+  if (!audio_clock_) {
+    SendLogMessage(
+        "%s => (WARNING: IAudioClock unavailable, capture times will be "
+        "inaccurate)",
+        __func__);
+  }
 
   return hr;
 }
 
-void WASAPIAudioInputStream::ReportOpenResult(HRESULT hr) const {
-  DCHECK(!opened_);  // This method must be called before we set this flag.
+void WASAPIAudioInputStream::ReportOpenResult(HRESULT hr) {
+  DCHECK(!opened_);
   UMA_HISTOGRAM_ENUMERATION("Media.Audio.Capture.Win.Open", open_result_,
                             OPEN_RESULT_MAX + 1);
   if (open_result_ != OPEN_RESULT_OK &&
       open_result_ != OPEN_RESULT_OK_WITH_RESAMPLING) {
-    log_callback_.Run(base::StringPrintf(
-        "WASAPIAIS::Open: failed, result = %d, hresult = %#lx, "
-        "input format = %#x/%d/%ld/%d/%d/%ld/%d, "
-        "output format = %#x/%d/%ld/%d/%d/%ld/%d",
-        // clang-format off
-        open_result_, hr,
-        input_format_.Format.wFormatTag, input_format_.Format.nChannels,
-        input_format_.Format.nSamplesPerSec,
-        input_format_.Format.wBitsPerSample,
-        input_format_.Format.nBlockAlign, input_format_.Format.nAvgBytesPerSec,
-        input_format_.Format.cbSize,
-        output_format_.wFormatTag, output_format_.nChannels,
-        output_format_.nSamplesPerSec,
-        output_format_.wBitsPerSample,
-        output_format_.nBlockAlign,
-        output_format_.nAvgBytesPerSec,
-        output_format_.cbSize));
-    // clang-format on
+    SendLogMessage(
+        "%s", GetOpenLogString(open_result_, hr, input_format_, output_format_)
+                  .c_str());
   }
 }
 
@@ -1043,14 +1121,11 @@
 
 void WASAPIAudioInputStream::ReportAndResetGlitchStats() {
   UMA_HISTOGRAM_COUNTS_1M("Media.Audio.Capture.Glitches", total_glitches_);
-
   double lost_frames_ms =
       (total_lost_frames_ * 1000) / input_format_.Format.nSamplesPerSec;
-  std::string log_message = base::StringPrintf(
-      "WASAPIAIS: Total glitches=%d. Total frames lost=%llu (%.0lf ms).",
-      total_glitches_, total_lost_frames_, lost_frames_ms);
-  log_callback_.Run(log_message);
-
+  SendLogMessage(
+      "%s => (total glitches=[%d], total frames lost=[%llu/%.0lf ms])",
+      __func__, total_glitches_, total_lost_frames_, lost_frames_ms);
   if (total_glitches_ != 0) {
     UMA_HISTOGRAM_LONG_TIMES("Media.Audio.Capture.LostFramesInMs",
                              base::TimeDelta::FromMilliseconds(lost_frames_ms));
@@ -1061,7 +1136,6 @@
         base::TimeDelta::FromMilliseconds(largest_glitch_ms),
         base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
         50);
-    DLOG(WARNING) << log_message;
   }
 
   expected_next_device_position_ = 0;
diff --git a/media/audio/win/audio_low_latency_input_win.h b/media/audio/win/audio_low_latency_input_win.h
index d28cca49..417c22f 100644
--- a/media/audio/win/audio_low_latency_input_win.h
+++ b/media/audio/win/audio_low_latency_input_win.h
@@ -91,65 +91,6 @@
       public base::DelegateSimpleThread::Delegate,
       public AudioConverter::InputCallback {
  public:
-  // The ctor takes all the usual parameters, plus |manager| which is the
-  // the audio manager who is creating this object.
-  WASAPIAudioInputStream(AudioManagerWin* manager,
-                         const AudioParameters& params,
-                         const std::string& device_id,
-                         const AudioManager::LogCallback& log_callback);
-
-  // The dtor is typically called by the AudioManager only and it is usually
-  // triggered by calling AudioInputStream::Close().
-  ~WASAPIAudioInputStream() override;
-
-  // Implementation of AudioInputStream.
-  bool Open() override;
-  void Start(AudioInputCallback* callback) override;
-  void Stop() override;
-  void Close() override;
-  double GetMaxVolume() override;
-  void SetVolume(double volume) override;
-  double GetVolume() override;
-  bool IsMuted() override;
-  void SetOutputDeviceForAec(const std::string& output_device_id) override;
-
-  bool started() const { return started_; }
-
- private:
-  // DelegateSimpleThread::Delegate implementation.
-  void Run() override;
-
-  // Pulls capture data from the endpoint device and pushes it to the sink.
-  void PullCaptureDataAndPushToSink();
-
-  // Issues the OnError() callback to the |sink_|.
-  void HandleError(HRESULT err);
-
-  // The Open() method is divided into these sub methods.
-  HRESULT SetCaptureDevice();
-  HRESULT GetAudioEngineStreamFormat();
-  // Returns whether the desired format is supported or not and writes the
-  // result of a failing system call to |*hr|, or S_OK if successful. If this
-  // function returns false with |*hr| == S_FALSE, the OS supports a closest
-  // match but we don't support conversion to it.
-  bool DesiredFormatIsSupported(HRESULT* hr);
-  void SetupConverterAndStoreFormatInfo();
-  HRESULT InitializeAudioEngine();
-  void ReportOpenResult(HRESULT hr) const;
-  // Reports stats for format related audio client initilization
-  // (IAudioClient::Initialize) errors, that is if |hr| is an error related to
-  // the format.
-  void MaybeReportFormatRelatedInitError(HRESULT hr) const;
-
-  // AudioConverter::InputCallback implementation.
-  double ProvideInput(AudioBus* audio_bus, uint32_t frames_delayed) override;
-
-  // Detects and counts glitches based on |device_position|.
-  void UpdateGlitchCount(UINT64 device_position);
-
-  // Reports glitch stats and resets associated variables.
-  void ReportAndResetGlitchStats();
-
   // Used to track down where we fail during initialization which at the
   // moment seems to be happening frequently and we're not sure why.
   // The reason might be expected (e.g. trying to open "default" on a machine
@@ -175,6 +116,67 @@
     OPEN_RESULT_MAX = OPEN_RESULT_OK_WITH_RESAMPLING
   };
 
+  // The ctor takes all the usual parameters, plus |manager| which is the
+  // the audio manager who is creating this object.
+  WASAPIAudioInputStream(AudioManagerWin* manager,
+                         const AudioParameters& params,
+                         const std::string& device_id,
+                         AudioManager::LogCallback log_callback);
+
+  // The dtor is typically called by the AudioManager only and it is usually
+  // triggered by calling AudioInputStream::Close().
+  ~WASAPIAudioInputStream() override;
+
+  // Implementation of AudioInputStream.
+  bool Open() override;
+  void Start(AudioInputCallback* callback) override;
+  void Stop() override;
+  void Close() override;
+  double GetMaxVolume() override;
+  void SetVolume(double volume) override;
+  double GetVolume() override;
+  bool IsMuted() override;
+  void SetOutputDeviceForAec(const std::string& output_device_id) override;
+
+  bool started() const { return started_; }
+
+ private:
+  void SendLogMessage(const char* format, ...) PRINTF_FORMAT(2, 3);
+
+  // DelegateSimpleThread::Delegate implementation.
+  void Run() override;
+
+  // Pulls capture data from the endpoint device and pushes it to the sink.
+  void PullCaptureDataAndPushToSink();
+
+  // Issues the OnError() callback to the |sink_|.
+  void HandleError(HRESULT err);
+
+  // The Open() method is divided into these sub methods.
+  HRESULT SetCaptureDevice();
+  HRESULT GetAudioEngineStreamFormat();
+  // Returns whether the desired format is supported or not and writes the
+  // result of a failing system call to |*hr|, or S_OK if successful. If this
+  // function returns false with |*hr| == S_FALSE, the OS supports a closest
+  // match but we don't support conversion to it.
+  bool DesiredFormatIsSupported(HRESULT* hr);
+  void SetupConverterAndStoreFormatInfo();
+  HRESULT InitializeAudioEngine();
+  void ReportOpenResult(HRESULT hr);
+  // Reports stats for format related audio client initialization
+  // (IAudioClient::Initialize) errors, that is if |hr| is an error related to
+  // the format.
+  void MaybeReportFormatRelatedInitError(HRESULT hr) const;
+
+  // AudioConverter::InputCallback implementation.
+  double ProvideInput(AudioBus* audio_bus, uint32_t frames_delayed) override;
+
+  // Detects and counts glitches based on |device_position|.
+  void UpdateGlitchCount(UINT64 device_position);
+
+  // Reports glitch stats and resets associated variables.
+  void ReportAndResetGlitchStats();
+
   // Our creator, the audio manager needs to be notified when we close.
   AudioManagerWin* const manager_;
 
@@ -283,7 +285,7 @@
   std::unique_ptr<AudioBus> convert_bus_;
   bool imperfect_buffer_size_conversion_ = false;
 
-  // Callback to send log messages.
+  // Callback to send log messages to registered clients.
   AudioManager::LogCallback log_callback_;
 
   // For detecting and reporting glitches.
diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc
index 490c843..b8da03b9 100644
--- a/media/audio/win/audio_low_latency_output_win.cc
+++ b/media/audio/win/audio_low_latency_output_win.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
@@ -35,6 +36,28 @@
 
 namespace media {
 
+namespace {
+
+// Converts a COM error into a human-readable string.
+std::string ErrorToString(HRESULT hresult) {
+  return CoreAudioUtil::ErrorToString(hresult);
+}
+
+const char* RoleToString(const ERole role) {
+  switch (role) {
+    case eConsole:
+      return "Console";
+    case eMultimedia:
+      return "Multimedia";
+    case eCommunications:
+      return "Communications";
+    default:
+      return "Unsupported";
+  }
+}
+
+}  // namespace
+
 // static
 AUDCLNT_SHAREMODE
 WASAPIAudioOutputStream::GetShareMode() {
@@ -44,10 +67,12 @@
   return AUDCLNT_SHAREMODE_SHARED;
 }
 
-WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager,
-                                                 const std::string& device_id,
-                                                 const AudioParameters& params,
-                                                 ERole device_role)
+WASAPIAudioOutputStream::WASAPIAudioOutputStream(
+    AudioManagerWin* manager,
+    const std::string& device_id,
+    const AudioParameters& params,
+    ERole device_role,
+    AudioManager::LogCallback log_callback)
     : creating_thread_id_(base::PlatformThread::CurrentId()),
       manager_(manager),
       format_(),
@@ -61,7 +86,8 @@
       device_role_(device_role),
       share_mode_(GetShareMode()),
       num_written_frames_(0),
-      source_(NULL) {
+      source_(nullptr),
+      log_callback_(std::move(log_callback)) {
   DCHECK(manager_);
 
   // The empty string is used to indicate a default device and the
@@ -70,14 +96,14 @@
   DCHECK_NE(device_id_, AudioDeviceDescription::kDefaultDeviceId);
   DCHECK_NE(device_id_, AudioDeviceDescription::kCommunicationsDeviceId);
 
-  DVLOG(1) << "WASAPIAudioOutputStream::WASAPIAudioOutputStream()";
-  DVLOG_IF(1, share_mode_ == AUDCLNT_SHAREMODE_EXCLUSIVE)
-       << "Core Audio (WASAPI) EXCLUSIVE MODE is enabled.";
-  DVLOG(1) << params.AsHumanReadableString();
+  SendLogMessage("%s({device_id=%s}, {params=[%s]}, {role=%s})", __func__,
+                 device_id.c_str(), params.AsHumanReadableString().c_str(),
+                 RoleToString(device_role));
 
   // Load the Avrt DLL if not already loaded. Required to support MMCSS.
   bool avrt_init = avrt::Initialize();
-  DCHECK(avrt_init) << "Failed to load the avrt.dll";
+  if (!avrt_init)
+    SendLogMessage("%s => (WARNING: failed to load Avrt.dll)", __func__);
 
   audio_bus_ = AudioBus::Create(params);
 
@@ -99,17 +125,16 @@
   format_.Samples.wValidBitsPerSample = format->wBitsPerSample;
   format_.dwChannelMask = CoreAudioUtil::GetChannelConfig(device_id, eRender);
   format_.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
-  DVLOG(1) << "Format: " << CoreAudioUtil::WaveFormatToString(&format_);
+  SendLogMessage("%s => (audio engine format=[%s])", __func__,
+                 CoreAudioUtil::WaveFormatToString(&format_).c_str());
 
   // Store size (in different units) of audio packets which we expect to
   // get from the audio endpoint device in each render event.
   packet_size_frames_ = params.frames_per_buffer();
   packet_size_bytes_ = params.GetBytesPerBuffer(kSampleFormatF32);
-  DVLOG(1) << "Number of bytes per audio frame  : " << format->nBlockAlign;
-  DVLOG(1) << "Number of audio frames per packet: " << packet_size_frames_;
-  DVLOG(1) << "Number of bytes per packet       : " << packet_size_bytes_;
-  DVLOG(1) << "Number of milliseconds per packet: "
-           << params.GetBufferDuration().InMillisecondsF();
+  SendLogMessage("%s => (packet size=[%zu bytes/%zu audio frames/%.3f ms])",
+                 __func__, packet_size_bytes_, packet_size_frames_,
+                 params.GetBufferDuration().InMillisecondsF());
 
   AudioParameters::HardwareCapabilities hardware_capabilities =
       params.hardware_capabilities().value_or(
@@ -127,11 +152,11 @@
 
   // Create the event which the audio engine will signal each time
   // a buffer becomes ready to be processed by the client.
-  audio_samples_render_event_.Set(CreateEvent(NULL, FALSE, FALSE, NULL));
+  audio_samples_render_event_.Set(CreateEvent(nullptr, FALSE, FALSE, nullptr));
   DCHECK(audio_samples_render_event_.IsValid());
 
   // Create the event which will be set in Stop() when capturing shall stop.
-  stop_render_event_.Set(CreateEvent(NULL, FALSE, FALSE, NULL));
+  stop_render_event_.Set(CreateEvent(nullptr, FALSE, FALSE, nullptr));
   DCHECK(stop_render_event_.IsValid());
 }
 
@@ -140,8 +165,8 @@
 }
 
 bool WASAPIAudioOutputStream::Open() {
-  DVLOG(1) << "WASAPIAudioOutputStream::Open()";
   DCHECK_EQ(GetCurrentThreadId(), creating_thread_id_);
+  SendLogMessage("%s([opened=%s])", __func__, opened_ ? "true" : "false");
   if (opened_)
     return true;
 
@@ -153,14 +178,15 @@
 
   Microsoft::WRL::ComPtr<IAudioClient> audio_client(
       CoreAudioUtil::CreateClient(device_id_, eRender, device_role_));
-
-  if (!audio_client.Get())
+  if (!audio_client.Get()) {
+    SendLogMessage("%s => (ERROR: CAU::CreateClient failed)", __func__);
     return false;
+  }
 
   // Extra sanity to ensure that the provided device format is still valid.
   if (!CoreAudioUtil::IsFormatSupported(audio_client.Get(), share_mode_,
                                         &format_)) {
-    LOG(ERROR) << "Audio parameters are not supported.";
+    SendLogMessage("%s => (ERROR: CAU::IsFormatSupported failed)", __func__);
     return false;
   }
 
@@ -171,9 +197,12 @@
     hr = CoreAudioUtil::SharedModeInitialize(
         audio_client.Get(), &format_, audio_samples_render_event_.Get(),
         requested_iaudioclient3_buffer_size_, &endpoint_buffer_size_frames_,
-        communications_device ? &kCommunicationsSessionId : NULL);
-    if (FAILED(hr))
+        communications_device ? &kCommunicationsSessionId : nullptr);
+    if (FAILED(hr)) {
+      SendLogMessage("%s => (ERROR: IAudioClient::SharedModeInitialize=[%s])",
+                     __func__, ErrorToString(hr).c_str());
       return false;
+    }
 
     REFERENCE_TIME device_period = 0;
     if (FAILED(CoreAudioUtil::GetDevicePeriod(
@@ -186,6 +215,8 @@
             CoreAudioUtil::ReferenceTimeToTimeDelta(device_period)
                 .InSecondsF() +
         0.5);
+    SendLogMessage("%s => (preferred_frames_per_buffer=[%d audio frames])",
+                   __func__, preferred_frames_per_buffer);
 
     // Packet size should always be an even divisor of the device period for
     // best performance; things will still work otherwise, but may glitch for a
@@ -207,14 +238,14 @@
     // Log a warning in these cases so we can help users in the field.
     // Examples: 48kHz => 960 % 480, 44.1kHz => 896 % 448 or 882 % 441.
     if (preferred_frames_per_buffer % packet_size_frames_) {
-      LOG(WARNING)
-          << "Using WASAPI output with a non-optimal buffer size, glitches from"
-          << " back to back shared memory reads and partial fills of WASAPI"
-          << " output buffers may occur.  Buffer size of "
-          << packet_size_frames_ << " is not an even divisor of "
-          << preferred_frames_per_buffer;
+      SendLogMessage(
+          "%s => (WARNING: Using output audio with a non-optimal buffer size)",
+          __func__);
     }
   } else {
+    SendLogMessage(
+        "%s => (WARNING: Using exclusive mode can lead to bad performance)",
+        __func__);
     // TODO(henrika): break out to CoreAudioUtil::ExclusiveModeInitialize()
     // when removing the enable-exclusive-audio flag.
     hr = ExclusiveModeInitialization(audio_client.Get(),
@@ -237,8 +268,10 @@
   // a rendering endpoint buffer.
   Microsoft::WRL::ComPtr<IAudioRenderClient> audio_render_client =
       CoreAudioUtil::CreateRenderClient(audio_client.Get());
-  if (!audio_render_client.Get())
+  if (!audio_render_client.Get()) {
+    SendLogMessage("%s => (ERROR: CAU::CreateRenderClient failed)", __func__);
     return false;
+  }
 
   // Store valid COM interfaces.
   audio_client_ = audio_client;
@@ -246,7 +279,8 @@
 
   hr = audio_client_->GetService(IID_PPV_ARGS(&audio_clock_));
   if (FAILED(hr)) {
-    LOG(ERROR) << "Failed to get IAudioClock service.";
+    SendLogMessage("%s => (ERROR: IAudioClient::GetService(IAudioClock)=[%s])",
+                   __func__, ErrorToString(hr).c_str());
     return false;
   }
 
@@ -264,6 +298,8 @@
   DCHECK_EQ(GetCurrentThreadId(), creating_thread_id_);
   CHECK(callback);
   CHECK(opened_);
+  SendLogMessage("%s([opened=%s, started=%s])", __func__,
+                 opened_ ? "true" : "false", render_thread_ ? "true" : "false");
 
   if (render_thread_) {
     CHECK_EQ(callback, source_);
@@ -289,16 +325,17 @@
   if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) {
     if (!CoreAudioUtil::FillRenderEndpointBufferWithSilence(
             audio_client_.Get(), audio_render_client_.Get())) {
-      DLOG(WARNING) << "Failed to prepare endpoint buffers with silence. "
-                       "Attempting recovery with a new IAudioClient and "
-                       "IAudioRenderClient.";
-
+      // Failed to prepare endpoint buffers with silence. Attempting recovery
+      // with a new IAudioClient and IAudioRenderClient."
+      SendLogMessage(
+          "%s => (WARNING: CAU::FillRenderEndpointBufferWithSilence failed)",
+          __func__);
       opened_ = false;
       audio_client_.Reset();
       audio_render_client_.Reset();
       if (!Open() || !CoreAudioUtil::FillRenderEndpointBufferWithSilence(
                          audio_client_.Get(), audio_render_client_.Get())) {
-        DLOG(ERROR) << "Failed recovery of audio clients; Start() failed.";
+        SendLogMessage("%s => (ERROR: Recovery attempt failed)", __func__);
         callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
         return;
       }
@@ -317,7 +354,8 @@
       base::SimpleThread::Options(base::ThreadPriority::REALTIME_AUDIO)));
   render_thread_->Start();
   if (!render_thread_->HasBeenStarted()) {
-    LOG(ERROR) << "Failed to start WASAPI render thread.";
+    SendLogMessage("%s => (ERROR: Failed to start \"wasapi_render_thread\")",
+                   __func__);
     StopThread();
     callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
     return;
@@ -326,7 +364,8 @@
   // Start streaming data between the endpoint buffer and the audio engine.
   HRESULT hr = audio_client_->Start();
   if (FAILED(hr)) {
-    PLOG(ERROR) << "Failed to start output streaming: " << std::hex << hr;
+    SendLogMessage("%s => (ERROR: IAudioClient::Start=[%s])", __func__,
+                   ErrorToString(hr).c_str());
     StopThread();
     callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
   }
@@ -335,13 +374,17 @@
 void WASAPIAudioOutputStream::Stop() {
   DVLOG(1) << "WASAPIAudioOutputStream::Stop()";
   DCHECK_EQ(GetCurrentThreadId(), creating_thread_id_);
+  SendLogMessage("%s([started=%s])", __func__,
+                 render_thread_ ? "true" : "false");
+
   if (!render_thread_)
     return;
 
   // Stop output audio streaming.
   HRESULT hr = audio_client_->Stop();
   if (FAILED(hr)) {
-    PLOG(ERROR) << "Failed to stop output streaming: " << std::hex << hr;
+    SendLogMessage("%s => (ERROR: IAudioClient::Stop=[%s])", __func__,
+                   ErrorToString(hr).c_str());
     source_->OnError(AudioSourceCallback::ErrorType::kUnknown);
   }
 
@@ -352,7 +395,8 @@
   // Flush all pending data and reset the audio clock stream position to 0.
   hr = audio_client_->Reset();
   if (FAILED(hr)) {
-    PLOG(ERROR) << "Failed to reset streaming: " << std::hex << hr;
+    SendLogMessage("%s => (ERROR: IAudioClient::Reset=[%s])", __func__,
+                   ErrorToString(hr).c_str());
     callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
   }
 
@@ -366,12 +410,17 @@
     UINT32 num_queued_frames = 0;
     audio_client_->GetCurrentPadding(&num_queued_frames);
     DCHECK_EQ(0u, num_queued_frames);
+    if (num_queued_frames > 0) {
+      SendLogMessage("%s => (WARNING: Buffers are not cleared correctly)",
+                     __func__);
+    }
   }
 }
 
 void WASAPIAudioOutputStream::Close() {
   DVLOG(1) << "WASAPIAudioOutputStream::Close()";
   DCHECK_EQ(GetCurrentThreadId(), creating_thread_id_);
+  SendLogMessage("%s()", __func__);
 
   session_listener_.reset();
 
@@ -389,7 +438,7 @@
 void WASAPIAudioOutputStream::Flush() {}
 
 void WASAPIAudioOutputStream::SetVolume(double volume) {
-  DVLOG(1) << "SetVolume(volume=" << volume << ")";
+  SendLogMessage("%s({volume=%.2f})", __func__, volume);
   float volume_float = static_cast<float>(volume);
   if (volume_float < 0.0f || volume_float > 1.0f) {
     return;
@@ -398,10 +447,18 @@
 }
 
 void WASAPIAudioOutputStream::GetVolume(double* volume) {
-  DVLOG(1) << "GetVolume()";
   *volume = static_cast<double>(volume_);
 }
 
+void WASAPIAudioOutputStream::SendLogMessage(const char* format, ...) {
+  if (log_callback_.is_null())
+    return;
+  va_list args;
+  va_start(args, format);
+  log_callback_.Run("WAOS::" + base::StringPrintV(format, args));
+  va_end(args);
+}
+
 void WASAPIAudioOutputStream::Run() {
   ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
 
@@ -416,7 +473,9 @@
     // Failed to enable MMCSS on this thread. It is not fatal but can lead
     // to reduced QoS at high load.
     DWORD err = GetLastError();
-    LOG(WARNING) << "Failed to enable MMCSS (error code=" << err << ").";
+    LOG(ERROR) << "WAOS::" << __func__
+               << " => (ERROR: Failed to enable MMCSS (error code=" << err
+               << "))";
   }
 
   HRESULT hr = S_FALSE;
@@ -431,8 +490,11 @@
   // the audio device. The GetFrequency() method reports a constant frequency.
   hr = audio_clock_->GetFrequency(&device_frequency);
   error = FAILED(hr);
-  PLOG_IF(ERROR, error) << "Failed to acquire IAudioClock interface: "
-                        << std::hex << hr;
+  if (error) {
+    LOG(ERROR) << "WAOS::" << __func__
+               << " => (ERROR: IAudioClock::GetFrequency=["
+               << ErrorToString(hr).c_str() << "])";
+  }
 
   // Keep rendering audio until the stop event or the stream-switch event
   // is signaled. An error event can also break the main thread loop.
@@ -457,7 +519,8 @@
   }
 
   if (playing && error) {
-    LOG(ERROR) << "WASAPI rendering failed.";
+    LOG(ERROR) << "WAOS::" << __func__
+               << " => (ERROR: WASAPI rendering failed)";
 
     // Stop audio rendering since something has gone wrong in our main thread
     // loop. Note that, we are still in a "started" state, hence a Stop() call
@@ -471,7 +534,8 @@
 
   // Disable MMCSS.
   if (mm_task && !avrt::AvRevertMmThreadCharacteristics(mm_task)) {
-    PLOG(WARNING) << "Failed to disable MMCSS";
+    LOG(WARNING) << "WAOS::" << __func__
+                 << " => (WARNING: Failed to disable MMCSS)";
   }
 }
 
@@ -480,7 +544,7 @@
 
   HRESULT hr = S_FALSE;
   UINT32 num_queued_frames = 0;
-  uint8_t* audio_data = NULL;
+  uint8_t* audio_data = nullptr;
 
   // Contains how much new data we can write to the buffer without
   // the risk of overwriting previously written data that the audio
@@ -494,8 +558,9 @@
     num_available_frames =
         endpoint_buffer_size_frames_ - num_queued_frames;
     if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to retrieve amount of available space: "
-                  << std::hex << hr;
+      LOG(ERROR) << "WAOS::" << __func__
+                 << " => (ERROR: IAudioClient::GetCurrentPadding=["
+                 << ErrorToString(hr).c_str() << "])";
       return false;
     }
   } else {
@@ -538,8 +603,9 @@
     hr = audio_render_client_->GetBuffer(packet_size_frames_,
                                          &audio_data);
     if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to use rendering audio buffer: "
-                 << std::hex << hr;
+      LOG(ERROR) << "WAOS::" << __func__
+                 << " => (ERROR: IAudioRenderClient::GetBuffer=["
+                 << ErrorToString(hr).c_str() << "])";
       return false;
     }
 
@@ -608,6 +674,9 @@
       // by 10.0 since 10x100ns = 1us.
       delay_timestamp += base::TimeDelta::FromMicroseconds(qpc_position * 0.1);
     } else {
+      LOG(ERROR) << "WAOS::" << __func__
+                 << " => (ERROR: IAudioClock::GetPosition=["
+                 << ErrorToString(hr).c_str() << "])";
       // Use a delay of zero.
       delay_timestamp = base::TimeTicks::Now();
     }
@@ -649,8 +718,8 @@
       static_cast<REFERENCE_TIME>(f * 10000.0 + 0.5);
 
   DWORD stream_flags = AUDCLNT_STREAMFLAGS_NOPERSIST;
-  bool use_event = (event_handle != NULL &&
-                    event_handle != INVALID_HANDLE_VALUE);
+  bool use_event =
+      (event_handle != nullptr && event_handle != INVALID_HANDLE_VALUE);
   if (use_event)
     stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
   DVLOG(2) << "stream_flags: 0x" << std::hex << stream_flags;
@@ -664,12 +733,9 @@
   // Following the Initialize call for a rendering stream, the caller should
   // fill the first of the two buffers before starting the stream.
   HRESULT hr = S_FALSE;
-  hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE,
-                          stream_flags,
-                          requested_buffer_duration,
-                          requested_buffer_duration,
-                          reinterpret_cast<WAVEFORMATEX*>(&format_),
-                          NULL);
+  hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, stream_flags,
+                          requested_buffer_duration, requested_buffer_duration,
+                          reinterpret_cast<WAVEFORMATEX*>(&format_), nullptr);
   if (FAILED(hr)) {
     if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
       LOG(ERROR) << "AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED";
@@ -734,7 +800,7 @@
     ResetEvent(stop_render_event_.Get());
   }
 
-  source_ = NULL;
+  source_ = nullptr;
 }
 
 void WASAPIAudioOutputStream::ReportAndResetStats() {
@@ -749,6 +815,11 @@
     UMA_HISTOGRAM_COUNTS_1M("Media.Audio.Render.LargestGlitchMs",
                             largest_glitch_.InMilliseconds());
   }
+  SendLogMessage(
+      "%s => (num_glitches_detected=[%d], cumulative_audio_lost=[%llu ms], "
+      "largest_glitch=[%llu ms])",
+      __func__, num_glitches_detected_, cumulative_audio_lost_.InMilliseconds(),
+      largest_glitch_.InMilliseconds());
   num_glitches_detected_ = 0;
   cumulative_audio_lost_ = base::TimeDelta();
   largest_glitch_ = base::TimeDelta();
diff --git a/media/audio/win/audio_low_latency_output_win.h b/media/audio/win/audio_low_latency_output_win.h
index 7dcfaa5..7447419 100644
--- a/media/audio/win/audio_low_latency_output_win.h
+++ b/media/audio/win/audio_low_latency_output_win.h
@@ -111,6 +111,7 @@
 #include "base/win/scoped_com_initializer.h"
 #include "base/win/scoped_handle.h"
 #include "media/audio/audio_io.h"
+#include "media/audio/win/audio_manager_win.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/media_export.h"
 
@@ -129,7 +130,8 @@
   WASAPIAudioOutputStream(AudioManagerWin* manager,
                           const std::string& device_id,
                           const AudioParameters& params,
-                          ERole device_role);
+                          ERole device_role,
+                          AudioManager::LogCallback log_callback);
 
   // The dtor is typically called by the AudioManager only and it is usually
   // triggered by calling AudioOutputStream::Close().
@@ -151,6 +153,8 @@
   bool started() const { return render_thread_.get() != NULL; }
 
  private:
+  void SendLogMessage(const char* format, ...) PRINTF_FORMAT(2, 3);
+
   // DelegateSimpleThread::Delegate implementation.
   void Run() override;
 
@@ -247,6 +251,9 @@
   // Pointer to the client that will deliver audio samples to be played out.
   AudioSourceCallback* source_;
 
+  // Callback to send log messages to registered clients.
+  AudioManager::LogCallback log_callback_;
+
   // An IAudioClient interface which enables a client to create and initialize
   // an audio stream between an audio application and the audio engine.
   Microsoft::WRL::ComPtr<IAudioClient> audio_client_;
diff --git a/media/audio/win/audio_manager_win.cc b/media/audio/win/audio_manager_win.cc
index 92989314..2e1477d2 100644
--- a/media/audio/win/audio_manager_win.cc
+++ b/media/audio/win/audio_manager_win.cc
@@ -241,7 +241,7 @@
       communications || device_id == AudioDeviceDescription::kDefaultDeviceId
           ? std::string()
           : device_id,
-      params, communications ? eCommunications : eConsole);
+      params, communications ? eCommunications : eConsole, log_callback);
 }
 
 // Factory for the implementations of AudioInputStream for AUDIO_PCM_LINEAR
@@ -261,7 +261,6 @@
     const std::string& device_id,
     const LogCallback& log_callback) {
   // Used for both AUDIO_PCM_LOW_LATENCY and AUDIO_PCM_LINEAR.
-  DVLOG(1) << "MakeLowLatencyInputStream: " << device_id;
   return new WASAPIAudioInputStream(this, params, device_id, log_callback);
 }
 
diff --git a/media/audio/win/core_audio_util_win.cc b/media/audio/win/core_audio_util_win.cc
index aa84f51..676959a 100644
--- a/media/audio/win/core_audio_util_win.cc
+++ b/media/audio/win/core_audio_util_win.cc
@@ -4,6 +4,7 @@
 
 #include "media/audio/win/core_audio_util_win.h"
 
+#include <comdef.h>
 #include <devicetopology.h>
 #include <functiondiscoverykeys_devpkey.h>
 #include <objbase.h>
@@ -684,7 +685,15 @@
   return g_is_supported;
 }
 
-// CoreAudioUtil implementation.
+std::string CoreAudioUtil::ErrorToString(HRESULT hresult) {
+  const _com_error error(hresult);
+  // If the HRESULT is within the range 0x80040200 to 0x8004FFFF, the WCode()
+  // method returns the HRESULT minus 0x80040200; otherwise, it returns zero.
+  return base::StringPrintf("HRESULT: 0x%08lX, WCode: %u, message: \"%s\"",
+                            error.Error(), error.WCode(),
+                            base::UTF16ToUTF8(error.ErrorMessage()).c_str());
+}
+
 std::string CoreAudioUtil::WaveFormatToString(const WaveFormatWrapper format) {
   // Start with the WAVEFORMATEX part.
   std::string wave_format = base::StringPrintf(
diff --git a/media/audio/win/core_audio_util_win.h b/media/audio/win/core_audio_util_win.h
index 1a797e3..f60e9c9 100644
--- a/media/audio/win/core_audio_util_win.h
+++ b/media/audio/win/core_audio_util_win.h
@@ -66,6 +66,9 @@
   // it is safe to call from other threads.
   static bool IsSupported();
 
+  // Converts a COM error into a human-readable string.
+  static std::string ErrorToString(HRESULT hresult);
+
   // Prints/logs all fields of the format structure in |format|.
   // Also supports extended versions (WAVEFORMATEXTENSIBLE).
   static std::string WaveFormatToString(WaveFormatWrapper format);
diff --git a/media/formats/mp2t/es_adapter_video.cc b/media/formats/mp2t/es_adapter_video.cc
index a086afa..70e7a59 100644
--- a/media/formats/mp2t/es_adapter_video.cc
+++ b/media/formats/mp2t/es_adapter_video.cc
@@ -25,10 +25,9 @@
 // to emulate the H264 dpb bumping process.
 static const size_t kHistorySize = 5;
 
-EsAdapterVideo::EsAdapterVideo(
-    const NewVideoConfigCB& new_video_config_cb,
-    const EmitBufferCB& emit_buffer_cb)
-    : new_video_config_cb_(new_video_config_cb),
+EsAdapterVideo::EsAdapterVideo(NewVideoConfigCB new_video_config_cb,
+                               const EmitBufferCB& emit_buffer_cb)
+    : new_video_config_cb_(std::move(new_video_config_cb)),
       emit_buffer_cb_(emit_buffer_cb),
       has_valid_config_(false),
       has_valid_frame_(false),
@@ -36,8 +35,7 @@
           base::TimeDelta::FromMilliseconds(kDefaultFrameDurationMs)),
       buffer_index_(0),
       has_valid_initial_timestamp_(false),
-      discarded_frame_count_(0) {
-}
+      discarded_frame_count_(0) {}
 
 EsAdapterVideo::~EsAdapterVideo() {
 }
diff --git a/media/formats/mp2t/es_adapter_video.h b/media/formats/mp2t/es_adapter_video.h
index 793b441f..5c9b69b 100644
--- a/media/formats/mp2t/es_adapter_video.h
+++ b/media/formats/mp2t/es_adapter_video.h
@@ -33,12 +33,12 @@
 //   creating a hole in the video timeline.
 class MEDIA_EXPORT EsAdapterVideo {
  public:
-  typedef base::Callback<void(const VideoDecoderConfig&)> NewVideoConfigCB;
+  using NewVideoConfigCB =
+      base::RepeatingCallback<void(const VideoDecoderConfig&)>;
   typedef base::Callback<void(scoped_refptr<StreamParserBuffer>)> EmitBufferCB;
 
-  EsAdapterVideo(
-      const NewVideoConfigCB& new_video_config_cb,
-      const EmitBufferCB& emit_buffer_cb);
+  EsAdapterVideo(NewVideoConfigCB new_video_config_cb,
+                 const EmitBufferCB& emit_buffer_cb);
   ~EsAdapterVideo();
 
   // Force the emission of the pending video buffers.
@@ -68,7 +68,7 @@
   // (this one must be a key frame).
   void ReplaceDiscardedFrames(const StreamParserBuffer& stream_parser_buffer);
 
-  NewVideoConfigCB new_video_config_cb_;
+  const NewVideoConfigCB new_video_config_cb_;
   EmitBufferCB emit_buffer_cb_;
 
   bool has_valid_config_;
diff --git a/media/formats/mp2t/es_parser_h264.cc b/media/formats/mp2t/es_parser_h264.cc
index 5532d8f2..66e458c 100644
--- a/media/formats/mp2t/es_parser_h264.cc
+++ b/media/formats/mp2t/es_parser_h264.cc
@@ -177,9 +177,9 @@
 // 3 bytes for the start code + 1 byte for the NALU type.
 const int kMinAUDSize = 4;
 
-EsParserH264::EsParserH264(const NewVideoConfigCB& new_video_config_cb,
+EsParserH264::EsParserH264(NewVideoConfigCB new_video_config_cb,
                            const EmitBufferCB& emit_buffer_cb)
-    : es_adapter_(new_video_config_cb, emit_buffer_cb),
+    : es_adapter_(std::move(new_video_config_cb), emit_buffer_cb),
       h264_parser_(new H264Parser()),
       current_access_unit_pos_(0),
       next_access_unit_pos_(0)
@@ -192,11 +192,11 @@
 }
 
 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
-EsParserH264::EsParserH264(const NewVideoConfigCB& new_video_config_cb,
+EsParserH264::EsParserH264(NewVideoConfigCB new_video_config_cb,
                            const EmitBufferCB& emit_buffer_cb,
                            EncryptionScheme init_encryption_scheme,
                            const GetDecryptConfigCB& get_decrypt_config_cb)
-    : es_adapter_(new_video_config_cb, emit_buffer_cb),
+    : es_adapter_(std::move(new_video_config_cb), emit_buffer_cb),
       h264_parser_(new H264Parser()),
       current_access_unit_pos_(0),
       next_access_unit_pos_(0),
diff --git a/media/formats/mp2t/es_parser_h264.h b/media/formats/mp2t/es_parser_h264.h
index 76b37dc..785d659 100644
--- a/media/formats/mp2t/es_parser_h264.h
+++ b/media/formats/mp2t/es_parser_h264.h
@@ -40,12 +40,13 @@
 //
 class MEDIA_EXPORT EsParserH264 : public EsParser {
  public:
-  typedef base::Callback<void(const VideoDecoderConfig&)> NewVideoConfigCB;
+  using NewVideoConfigCB =
+      base::RepeatingCallback<void(const VideoDecoderConfig&)>;
 
-  EsParserH264(const NewVideoConfigCB& new_video_config_cb,
+  EsParserH264(NewVideoConfigCB new_video_config_cb,
                const EmitBufferCB& emit_buffer_cb);
 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
-  EsParserH264(const NewVideoConfigCB& new_video_config_cb,
+  EsParserH264(NewVideoConfigCB new_video_config_cb,
                const EmitBufferCB& emit_buffer_cb,
                EncryptionScheme init_encryption_scheme,
                const GetDecryptConfigCB& get_decrypt_config_cb);
diff --git a/media/formats/mp2t/mp2t_stream_parser.cc b/media/formats/mp2t/mp2t_stream_parser.cc
index 2c7165d..67fa5971 100644
--- a/media/formats/mp2t/mp2t_stream_parser.cc
+++ b/media/formats/mp2t/mp2t_stream_parser.cc
@@ -395,12 +395,12 @@
 }
 
 std::unique_ptr<EsParser> Mp2tStreamParser::CreateH264Parser(int pes_pid) {
-  auto on_video_config_changed = base::Bind(
+  auto on_video_config_changed = base::BindRepeating(
       &Mp2tStreamParser::OnVideoConfigChanged, base::Unretained(this), pes_pid);
   auto on_emit_video_buffer = base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer,
                                          base::Unretained(this), pes_pid);
 
-  return std::make_unique<EsParserH264>(on_video_config_changed,
+  return std::make_unique<EsParserH264>(std::move(on_video_config_changed),
                                         on_emit_video_buffer);
 }
 
@@ -435,7 +435,7 @@
 std::unique_ptr<EsParser> Mp2tStreamParser::CreateEncryptedH264Parser(
     int pes_pid,
     bool emit_clear_buffers) {
-  auto on_video_config_changed = base::Bind(
+  auto on_video_config_changed = base::BindRepeating(
       &Mp2tStreamParser::OnVideoConfigChanged, base::Unretained(this), pes_pid);
   auto on_emit_video_buffer = base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer,
                                          base::Unretained(this), pes_pid);
@@ -444,8 +444,8 @@
                          : base::Bind(&Mp2tStreamParser::GetDecryptConfig,
                                       base::Unretained(this));
   return std::make_unique<EsParserH264>(
-      on_video_config_changed, on_emit_video_buffer, initial_encryption_scheme_,
-      get_decrypt_config);
+      std::move(on_video_config_changed), on_emit_video_buffer,
+      initial_encryption_scheme_, get_decrypt_config);
 }
 
 std::unique_ptr<EsParser> Mp2tStreamParser::CreateEncryptedAacParser(
diff --git a/net/android/network_change_notifier_android.cc b/net/android/network_change_notifier_android.cc
index 9cf47f0..263ac45 100644
--- a/net/android/network_change_notifier_android.cc
+++ b/net/android/network_change_notifier_android.cc
@@ -91,11 +91,12 @@
 class NetworkChangeNotifierAndroid::BlockingThreadObjects {
  public:
   BlockingThreadObjects()
-      : address_tracker_(base::DoNothing(),
-                         base::DoNothing(),
-                         // We're only interested in tunnel interface changes.
-                         base::Bind(NotifyNetworkChangeNotifierObservers),
-                         std::unordered_set<std::string>()) {}
+      : address_tracker_(
+            base::DoNothing(),
+            base::DoNothing(),
+            // We're only interested in tunnel interface changes.
+            base::BindRepeating(NotifyNetworkChangeNotifierObservers),
+            std::unordered_set<std::string>()) {}
 
   void Init() {
     address_tracker_.Init();
diff --git a/net/android/network_change_notifier_android_unittest.cc b/net/android/network_change_notifier_android_unittest.cc
index 6eeb9e4..1960ee98 100644
--- a/net/android/network_change_notifier_android_unittest.cc
+++ b/net/android/network_change_notifier_android_unittest.cc
@@ -161,8 +161,9 @@
   ~BaseNetworkChangeNotifierAndroidTest() override {}
 
   void RunTest(
-      const base::Callback<int(void)>& notifications_count_getter,
-      const base::Callback<ConnectionType(void)>&  connection_type_getter) {
+      const base::RepeatingCallback<int(void)>& notifications_count_getter,
+      const base::RepeatingCallback<ConnectionType(void)>&
+          connection_type_getter) {
     EXPECT_EQ(0, notifications_count_getter.Run());
     EXPECT_EQ(NetworkChangeNotifier::CONNECTION_UNKNOWN,
               connection_type_getter.Run());
@@ -310,10 +311,10 @@
 // delegate's observers are instances of NetworkChangeNotifierAndroid.
 TEST_F(NetworkChangeNotifierDelegateAndroidTest, DelegateObserverNotified) {
   // Test the logic with a single observer.
-  RunTest(base::Bind(&NetworkChangeNotifierDelegateAndroidObserver::
-                         type_notifications_count,
-                     base::Unretained(&delegate_observer_)),
-          base::Bind(
+  RunTest(base::BindRepeating(&NetworkChangeNotifierDelegateAndroidObserver::
+                                  type_notifications_count,
+                              base::Unretained(&delegate_observer_)),
+          base::BindRepeating(
               &NetworkChangeNotifierDelegateAndroid::GetCurrentConnectionType,
               base::Unretained(&delegate_)));
   // Check that *all* the observers are notified. Both observers should have the
@@ -329,10 +330,12 @@
 // NetworkChangeNotifierAndroid should reflect that state.
 TEST_F(NetworkChangeNotifierAndroidTest,
        NotificationsSentToNetworkChangeNotifierAndroid) {
-  RunTest(base::Bind(&NetworkChangeNotifierObserver::notifications_count,
-                     base::Unretained(&connection_type_observer_)),
-          base::Bind(&NetworkChangeNotifierAndroid::GetCurrentConnectionType,
-                     base::Unretained(&notifier_)));
+  RunTest(
+      base::BindRepeating(&NetworkChangeNotifierObserver::notifications_count,
+                          base::Unretained(&connection_type_observer_)),
+      base::BindRepeating(
+          &NetworkChangeNotifierAndroid::GetCurrentConnectionType,
+          base::Unretained(&notifier_)));
 }
 
 // When a NetworkChangeNotifierAndroid's connection state changes, it should
@@ -340,10 +343,9 @@
 TEST_F(NetworkChangeNotifierAndroidTest,
        NotificationsSentToClientsOfNetworkChangeNotifier) {
   RunTest(
-      base::Bind(
-          &NetworkChangeNotifierObserver::notifications_count,
-          base::Unretained(&connection_type_observer_)),
-      base::Bind(&NetworkChangeNotifier::GetConnectionType));
+      base::BindRepeating(&NetworkChangeNotifierObserver::notifications_count,
+                          base::Unretained(&connection_type_observer_)),
+      base::BindRepeating(&NetworkChangeNotifier::GetConnectionType));
   // Check that *all* the observers are notified.
   EXPECT_EQ(connection_type_observer_.notifications_count(),
             other_connection_type_observer_.notifications_count());
diff --git a/net/base/address_tracker_linux.cc b/net/base/address_tracker_linux.cc
index 888281e..ffb6e6ef211 100644
--- a/net/base/address_tracker_linux.cc
+++ b/net/base/address_tracker_linux.cc
@@ -156,9 +156,9 @@
       threads_waiting_for_connection_type_initialization_(0) {}
 
 AddressTrackerLinux::AddressTrackerLinux(
-    const base::Closure& address_callback,
-    const base::Closure& link_callback,
-    const base::Closure& tunnel_callback,
+    const base::RepeatingClosure& address_callback,
+    const base::RepeatingClosure& link_callback,
+    const base::RepeatingClosure& tunnel_callback,
     const std::unordered_set<std::string>& ignored_interfaces)
     : get_interface_name_(GetInterfaceName),
       address_callback_(address_callback),
diff --git a/net/base/address_tracker_linux.h b/net/base/address_tracker_linux.h
index a2c11162..a18450d 100644
--- a/net/base/address_tracker_linux.h
+++ b/net/base/address_tracker_linux.h
@@ -55,9 +55,9 @@
   // interfaces used to connect to the internet can cause critical network
   // changed signals to be lost allowing incorrect stale state to persist.
   AddressTrackerLinux(
-      const base::Closure& address_callback,
-      const base::Closure& link_callback,
-      const base::Closure& tunnel_callback,
+      const base::RepeatingClosure& address_callback,
+      const base::RepeatingClosure& link_callback,
+      const base::RepeatingClosure& tunnel_callback,
       const std::unordered_set<std::string>& ignored_interfaces);
   virtual ~AddressTrackerLinux();
 
@@ -151,9 +151,9 @@
   // overridden by tests.
   GetInterfaceNameFunction get_interface_name_;
 
-  base::Closure address_callback_;
-  base::Closure link_callback_;
-  base::Closure tunnel_callback_;
+  base::RepeatingClosure address_callback_;
+  base::RepeatingClosure link_callback_;
+  base::RepeatingClosure tunnel_callback_;
 
   // Note that |watcher_| must be inactive when |netlink_fd_| is closed.
   base::ScopedFD netlink_fd_;
diff --git a/net/cert/cert_verify_proc_builtin_unittest.cc b/net/cert/cert_verify_proc_builtin_unittest.cc
index 847f21f..29ba255 100644
--- a/net/cert/cert_verify_proc_builtin_unittest.cc
+++ b/net/cert/cert_verify_proc_builtin_unittest.cc
@@ -41,9 +41,9 @@
 };
 
 std::unique_ptr<test_server::HttpResponse> HangRequestAndCallback(
-    base::Closure callback,
+    base::OnceClosure callback,
     const test_server::HttpRequest& request) {
-  callback.Run();
+  std::move(callback).Run();
   return std::make_unique<test_server::HungResponse>();
 }
 
@@ -55,7 +55,7 @@
     const std::string& message,
     scoped_refptr<base::TaskRunner> main_task_runner,
     const test_server::HttpRequest& request) {
-  main_task_runner->PostTask(FROM_HERE, base::Bind(FailTest, message));
+  main_task_runner->PostTask(FROM_HERE, base::BindOnce(FailTest, message));
   auto response = std::make_unique<test_server::BasicHttpResponse>();
   response->set_code(HTTP_NOT_ACCEPTABLE);
   return response;
@@ -191,7 +191,7 @@
   for (int i = expected_request_count; i < expected_request_count + 1; ++i) {
     std::string path = base::StringPrintf("/failtest/%i", i);
     crl_urls.emplace_back(test_server.GetURL(path));
-    test_server.RegisterRequestHandler(base::Bind(
+    test_server.RegisterRequestHandler(base::BindRepeating(
         &test_server::HandlePrefixedRequest, path,
         base::BindRepeating(FailRequestAndFailTest,
                             "additional request made after deadline exceeded",
@@ -261,7 +261,7 @@
   for (int i = expected_request_count; i < expected_request_count + 1; ++i) {
     std::string path = base::StringPrintf("/failtest/%i", i);
     ocsp_urls.emplace_back(test_server.GetURL(path));
-    test_server.RegisterRequestHandler(base::Bind(
+    test_server.RegisterRequestHandler(base::BindRepeating(
         &test_server::HandlePrefixedRequest, path,
         base::BindRepeating(FailRequestAndFailTest,
                             "additional request made after deadline exceeded",
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 5054c4b6..9663b25 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -2411,7 +2411,7 @@
   ASSERT_EQ(1, counter->callback_count());
 
   // NULL callback is safe.
-  cm->FlushStore(base::Closure());
+  cm->FlushStore(base::OnceClosure());
   base::RunLoop().RunUntilIdle();
 
   ASSERT_EQ(0, store->flush_count());
diff --git a/net/disk_cache/disk_cache_test_base.cc b/net/disk_cache/disk_cache_test_base.cc
index e6970469..9da60b3 100644
--- a/net/disk_cache/disk_cache_test_base.cc
+++ b/net/disk_cache/disk_cache_test_base.cc
@@ -248,14 +248,14 @@
   EXPECT_THAT(cb.GetResult(rv), IsOk());
 }
 
-void DiskCacheTestWithCache::RunTaskForTest(const base::Closure& closure) {
+void DiskCacheTestWithCache::RunTaskForTest(base::OnceClosure closure) {
   if (memory_only_ || !cache_impl_) {
-    closure.Run();
+    std::move(closure).Run();
     return;
   }
 
   net::TestCompletionCallback cb;
-  int rv = cache_impl_->RunTaskForTest(closure, cb.callback());
+  int rv = cache_impl_->RunTaskForTest(std::move(closure), cb.callback());
   EXPECT_THAT(cb.GetResult(rv), IsOk());
 }
 
@@ -311,16 +311,17 @@
   if (memory_only_ || !cache_impl_)
     return;
 
-  RunTaskForTest(base::Bind(&disk_cache::BackendImpl::TrimForTest,
-                            base::Unretained(cache_impl_), empty));
+  RunTaskForTest(base::BindOnce(&disk_cache::BackendImpl::TrimForTest,
+                                base::Unretained(cache_impl_), empty));
 }
 
 void DiskCacheTestWithCache::TrimDeletedListForTest(bool empty) {
   if (memory_only_ || !cache_impl_)
     return;
 
-  RunTaskForTest(base::Bind(&disk_cache::BackendImpl::TrimDeletedListForTest,
-                            base::Unretained(cache_impl_), empty));
+  RunTaskForTest(
+      base::BindOnce(&disk_cache::BackendImpl::TrimDeletedListForTest,
+                     base::Unretained(cache_impl_), empty));
 }
 
 void DiskCacheTestWithCache::AddDelay() {
diff --git a/net/disk_cache/disk_cache_test_base.h b/net/disk_cache/disk_cache_test_base.h
index 9d00822e..f7b5395b 100644
--- a/net/disk_cache/disk_cache_test_base.h
+++ b/net/disk_cache/disk_cache_test_base.h
@@ -155,7 +155,7 @@
   int DoomEntriesSince(const base::Time initial_time);
   std::unique_ptr<TestIterator> CreateIterator();
   void FlushQueueForTest();
-  void RunTaskForTest(const base::Closure& closure);
+  void RunTaskForTest(base::OnceClosure closure);
   int ReadData(disk_cache::Entry* entry, int index, int offset,
                net::IOBuffer* buf, int len);
   int WriteData(disk_cache::Entry* entry, int index, int offset,
diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc
index 41e24783..1e3226a 100644
--- a/net/disk_cache/entry_unittest.cc
+++ b/net/disk_cache/entry_unittest.cc
@@ -164,10 +164,8 @@
   ASSERT_TRUE(nullptr != entry);
 
   // The bulk of the test runs from within the callback, on the cache thread.
-  RunTaskForTest(base::Bind(&DiskCacheEntryTest::InternalSyncIOBackground,
-                            base::Unretained(this),
-                            entry));
-
+  RunTaskForTest(base::BindOnce(&DiskCacheEntryTest::InternalSyncIOBackground,
+                                base::Unretained(this), entry));
 
   entry->Doom();
   entry->Close();
@@ -400,9 +398,8 @@
   ASSERT_THAT(CreateEntry("the first key", &entry), IsOk());
 
   // The bulk of the test runs from within the callback, on the cache thread.
-  RunTaskForTest(base::Bind(&DiskCacheEntryTest::ExternalSyncIOBackground,
-                            base::Unretained(this),
-                            entry));
+  RunTaskForTest(base::BindOnce(&DiskCacheEntryTest::ExternalSyncIOBackground,
+                                base::Unretained(this), entry));
 
   entry->Doom();
   entry->Close();
diff --git a/net/http/structured_headers.cc b/net/http/structured_headers.cc
index eca50ed..8075e869 100644
--- a/net/http/structured_headers.cc
+++ b/net/http/structured_headers.cc
@@ -305,7 +305,8 @@
 
   // Parses a Token ([SH09] 4.2.10, [SH15] 4.2.6).
   base::Optional<Item> ReadToken() {
-    if (input_.empty() || !base::IsAsciiAlpha(input_.front())) {
+    if (input_.empty() ||
+        !(base::IsAsciiAlpha(input_.front()) || input_.front() == '*')) {
       LogParseError("ReadToken", "ALPHA");
       return base::nullopt;
     }
@@ -538,7 +539,8 @@
     if (value.is_token()) {
       // Serializes a Token ([SH15] 4.1.7).
       if (!value.GetString().size() ||
-          !base::IsAsciiAlpha(value.GetString().front()))
+          !(base::IsAsciiAlpha(value.GetString().front()) ||
+            value.GetString().front() == '*'))
         return false;
       if (value.GetString().find_first_not_of(kTokenChars15) !=
           std::string::npos)
diff --git a/net/http/structured_headers_unittest.cc b/net/http/structured_headers_unittest.cc
index 285675a..d234d72 100644
--- a/net/http/structured_headers_unittest.cc
+++ b/net/http/structured_headers_unittest.cc
@@ -61,6 +61,7 @@
     {"bad token - item", "abc$@%!", base::nullopt},
     {"leading whitespace", " foo", Token("foo"), "foo"},
     {"trailing whitespace", "foo ", Token("foo"), "foo"},
+    {"leading asterisk", "*foo", Token("*foo")},
     // Number
     {"basic integer", "42", Integer(42L)},
     {"zero integer", "0", Integer(0L)},
@@ -427,6 +428,7 @@
      "*iQ==*"},
     {"non-ASCII binary", "*/+Ah*", Item("\xFF\xE0!", Item::kByteSequenceType)},
     {"base64url binary", "*_-Ah*", base::nullopt},
+    {"token with leading asterisk", "*foo", base::nullopt},
 };
 
 // For Structured Headers Draft 15
@@ -763,7 +765,6 @@
       {"begins with colon", ":token"},
       {"begins with percent", "%token"},
       {"begins with period", ".token"},
-      {"begins with asterisk", "*token"},
       {"begins with slash", "/token"},
   };
   for (const auto& bad_token : bad_tokens) {
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index cc91356..6a4a8b45 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -183,8 +183,9 @@
   NetworkChangeNotifier::AddConnectionTypeObserver(this);
   throughput_analyzer_.reset(new nqe::internal::ThroughputAnalyzer(
       this, params_.get(), base::ThreadTaskRunnerHandle::Get(),
-      base::Bind(&NetworkQualityEstimator::OnNewThroughputObservationAvailable,
-                 weak_ptr_factory_.GetWeakPtr()),
+      base::BindRepeating(
+          &NetworkQualityEstimator::OnNewThroughputObservationAvailable,
+          weak_ptr_factory_.GetWeakPtr()),
       tick_clock_, net_log_));
 
   watcher_factory_.reset(new nqe::internal::SocketWatcherFactory(
diff --git a/net/nqe/throughput_analyzer.h b/net/nqe/throughput_analyzer.h
index 5e3e14c..a5378cf8 100644
--- a/net/nqe/throughput_analyzer.h
+++ b/net/nqe/throughput_analyzer.h
@@ -20,7 +20,7 @@
 #include "net/log/net_log_with_source.h"
 
 namespace {
-typedef base::Callback<void(int32_t)> ThroughputObservationCallback;
+typedef base::RepeatingCallback<void(int32_t)> ThroughputObservationCallback;
 }
 
 namespace base {
diff --git a/net/nqe/throughput_analyzer_unittest.cc b/net/nqe/throughput_analyzer_unittest.cc
index 3a6b50a..8d6b87e 100644
--- a/net/nqe/throughput_analyzer_unittest.cc
+++ b/net/nqe/throughput_analyzer_unittest.cc
@@ -59,7 +59,7 @@
             network_quality_estimator,
             params,
             base::ThreadTaskRunnerHandle::Get(),
-            base::Bind(
+            base::BindRepeating(
                 &TestThroughputAnalyzer::OnNewThroughputObservationAvailable,
                 base::Unretained(this)),
             tick_clock,
diff --git a/services/device/serial/serial_io_handler.cc b/services/device/serial/serial_io_handler.cc
index 23346a7..a68b67c5 100644
--- a/services/device/serial/serial_io_handler.cc
+++ b/services/device/serial/serial_io_handler.cc
@@ -162,10 +162,13 @@
   return true;
 }
 
+void SerialIoHandler::PreClose() {}
+
 void SerialIoHandler::Close(base::OnceClosure callback) {
   if (file_.IsValid()) {
     CancelRead(mojom::SerialReceiveError::DISCONNECTED);
     CancelWrite(mojom::SerialSendError::DISCONNECTED);
+    PreClose();
     base::ThreadPool::PostTaskAndReply(
         FROM_HERE,
         {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
diff --git a/services/device/serial/serial_io_handler.h b/services/device/serial/serial_io_handler.h
index 8cd8c59d..b70906f 100644
--- a/services/device/serial/serial_io_handler.h
+++ b/services/device/serial/serial_io_handler.h
@@ -136,6 +136,9 @@
   // Performs platform-specific, one-time port configuration on open.
   virtual bool PostOpen();
 
+  // Performs platform-specific operations before |file_| is closed.
+  virtual void PreClose();
+
   // Called by the implementation to signal that the active read has completed.
   // WARNING: Calling this method can destroy the SerialIoHandler instance
   // if the associated I/O operation was the only thing keeping it alive.
diff --git a/services/device/serial/serial_io_handler_posix.cc b/services/device/serial/serial_io_handler_posix.cc
index 13ab169..452d8153 100644
--- a/services/device/serial/serial_io_handler_posix.cc
+++ b/services/device/serial/serial_io_handler_posix.cc
@@ -295,6 +295,11 @@
 #endif
 }
 
+void SerialIoHandlerPosix::PreClose() {
+  StopWatchingFileRead();
+  StopWatchingFileWrite();
+}
+
 SerialIoHandlerPosix::SerialIoHandlerPosix(
     const base::FilePath& port,
     scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner)
diff --git a/services/device/serial/serial_io_handler_posix.h b/services/device/serial/serial_io_handler_posix.h
index 39d187c..3d69c6d0 100644
--- a/services/device/serial/serial_io_handler_posix.h
+++ b/services/device/serial/serial_io_handler_posix.h
@@ -30,6 +30,7 @@
   void CancelWriteImpl() override;
   bool ConfigurePortImpl() override;
   bool PostOpen() override;
+  void PreClose() override;
   bool Flush() const override;
   mojom::SerialPortControlSignalsPtr GetControlSignals() const override;
   bool SetControlSignals(
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index e938cfa9..8727935 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -59,16 +59,6 @@
 const base::Feature kDelayRequestsOnMultiplexedConnections{
     "DelayRequestsOnMultiplexedConnections", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Implementation of https://mikewest.github.io/sec-metadata/
-const base::Feature kFetchMetadata{"FetchMetadata",
-                                   base::FEATURE_ENABLED_BY_DEFAULT};
-
-// The `Sec-Fetch-Dest` header is split out from the main "FetchMetadata"
-// feature so we can ship the broader feature without this specifific bit
-// while we continue discussion.
-const base::Feature kFetchMetadataDestination{"FetchMetadataDestination",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
-
 // When kRequestInitiatorSiteLock is enabled, then CORB, CORP and Sec-Fetch-Site
 // will validate network::ResourceRequest::request_initiator against
 // network::mojom::URLLoaderFactoryParams::request_initiator_site_lock.
diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h
index 95590a9..29f15af 100644
--- a/services/network/public/cpp/features.h
+++ b/services/network/public/cpp/features.h
@@ -29,10 +29,6 @@
 COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kDelayRequestsOnMultiplexedConnections;
 COMPONENT_EXPORT(NETWORK_CPP)
-extern const base::Feature kFetchMetadata;
-COMPONENT_EXPORT(NETWORK_CPP)
-extern const base::Feature kFetchMetadataDestination;
-COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kRequestInitiatorSiteLock;
 COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kPauseBrowserInitiatedHeavyTrafficForP2P;
diff --git a/services/network/sec_header_helpers.cc b/services/network/sec_header_helpers.cc
index c14fddb..a02160b2 100644
--- a/services/network/sec_header_helpers.cc
+++ b/services/network/sec_header_helpers.cc
@@ -153,8 +153,6 @@
     const mojom::URLLoaderFactoryParams& factory_params) {
   DCHECK(request);
   DCHECK_NE(0u, request->url_chain().size());
-  if (!base::FeatureList::IsEnabled(features::kFetchMetadata))
-    return;
 
   // Only append the header to potentially trustworthy URLs.
   const GURL& target_url =
@@ -172,9 +170,6 @@
                            const GURL& pending_redirect_url) {
   DCHECK(request);
 
-  if (!base::FeatureList::IsEnabled(features::kFetchMetadata))
-    return;
-
   // If our redirect destination is not trusted it would not have had sec-ch- or
   // sec-fetch- prefixed headers added to it. Our previous hops may have added
   // these headers if the current url is trustworthy though so we should try to
diff --git a/storage/browser/file_system/file_system_quota_client.cc b/storage/browser/file_system/file_system_quota_client.cc
index 8718c845..0fa79a6 100644
--- a/storage/browser/file_system/file_system_quota_client.cc
+++ b/storage/browser/file_system/file_system_quota_client.cc
@@ -36,10 +36,10 @@
   FileSystemQuotaUtil* quota_util = context->GetQuotaUtil(type);
   if (!quota_util)
     return;
-  std::set<GURL> origins;
+  std::set<url::Origin> origins;
   quota_util->GetOriginsForTypeOnFileTaskRunner(type, &origins);
   for (auto origin : origins)
-    origins_ptr->insert(url::Origin::Create(origin));
+    origins_ptr->insert(origin);
 }
 
 void GetOriginsForHostOnFileTaskRunner(FileSystemContext* context,
@@ -52,10 +52,10 @@
   FileSystemQuotaUtil* quota_util = context->GetQuotaUtil(type);
   if (!quota_util)
     return;
-  std::set<GURL> origins;
+  std::set<url::Origin> origins;
   quota_util->GetOriginsForHostOnFileTaskRunner(type, host, &origins);
   for (auto origin : origins)
-    origins_ptr->insert(url::Origin::Create(origin));
+    origins_ptr->insert(origin);
 }
 
 void DidGetFileSystemQuotaClientOrigins(
diff --git a/storage/browser/file_system/file_system_quota_util.h b/storage/browser/file_system/file_system_quota_util.h
index dfaa6f0f..94f66d8 100644
--- a/storage/browser/file_system/file_system_quota_util.h
+++ b/storage/browser/file_system/file_system_quota_util.h
@@ -14,7 +14,6 @@
 #include "base/files/file.h"
 #include "base/memory/scoped_refptr.h"
 #include "storage/common/file_system/file_system_types.h"
-#include "url/gurl.h"
 
 namespace url {
 class Origin;
@@ -46,12 +45,14 @@
                                                      QuotaManagerProxy* proxy,
                                                      FileSystemType type) = 0;
 
-  virtual void GetOriginsForTypeOnFileTaskRunner(FileSystemType type,
-                                                 std::set<GURL>* origins) = 0;
+  virtual void GetOriginsForTypeOnFileTaskRunner(
+      FileSystemType type,
+      std::set<url::Origin>* origins) = 0;
 
-  virtual void GetOriginsForHostOnFileTaskRunner(FileSystemType type,
-                                                 const std::string& host,
-                                                 std::set<GURL>* origins) = 0;
+  virtual void GetOriginsForHostOnFileTaskRunner(
+      FileSystemType type,
+      const std::string& host,
+      std::set<url::Origin>* origins) = 0;
 
   // Returns the amount of data used for the origin for usage tracking.
   virtual int64_t GetOriginUsageOnFileTaskRunner(
diff --git a/storage/browser/file_system/plugin_private_file_system_backend.cc b/storage/browser/file_system/plugin_private_file_system_backend.cc
index 0625582..084b305 100644
--- a/storage/browser/file_system/plugin_private_file_system_backend.cc
+++ b/storage/browser/file_system/plugin_private_file_system_backend.cc
@@ -253,20 +253,20 @@
 
 void PluginPrivateFileSystemBackend::GetOriginsForTypeOnFileTaskRunner(
     FileSystemType type,
-    std::set<GURL>* origins) {
+    std::set<url::Origin>* origins) {
   if (!CanHandleType(type))
     return;
   std::unique_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> enumerator(
       obfuscated_file_util()->CreateOriginEnumerator());
   base::Optional<url::Origin> origin;
   while ((origin = enumerator->Next()).has_value())
-    origins->insert(origin->GetURL());
+    origins->insert(origin.value());
 }
 
 void PluginPrivateFileSystemBackend::GetOriginsForHostOnFileTaskRunner(
     FileSystemType type,
     const std::string& host,
-    std::set<GURL>* origins) {
+    std::set<url::Origin>* origins) {
   if (!CanHandleType(type))
     return;
   std::unique_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> enumerator(
@@ -274,7 +274,7 @@
   base::Optional<url::Origin> origin;
   while ((origin = enumerator->Next()).has_value()) {
     if (host == net::GetHostOrSpecFromURL(origin->GetURL()))
-      origins->insert(origin->GetURL());
+      origins->insert(origin.value());
   }
 }
 
diff --git a/storage/browser/file_system/plugin_private_file_system_backend.h b/storage/browser/file_system/plugin_private_file_system_backend.h
index 8a0b161..cd26fea8 100644
--- a/storage/browser/file_system/plugin_private_file_system_backend.h
+++ b/storage/browser/file_system/plugin_private_file_system_backend.h
@@ -110,11 +110,13 @@
   void PerformStorageCleanupOnFileTaskRunner(FileSystemContext* context,
                                              QuotaManagerProxy* proxy,
                                              FileSystemType type) override;
-  void GetOriginsForTypeOnFileTaskRunner(FileSystemType type,
-                                         std::set<GURL>* origins) override;
-  void GetOriginsForHostOnFileTaskRunner(FileSystemType type,
-                                         const std::string& host,
-                                         std::set<GURL>* origins) override;
+  void GetOriginsForTypeOnFileTaskRunner(
+      FileSystemType type,
+      std::set<url::Origin>* origins) override;
+  void GetOriginsForHostOnFileTaskRunner(
+      FileSystemType type,
+      const std::string& host,
+      std::set<url::Origin>* origins) override;
   int64_t GetOriginUsageOnFileTaskRunner(FileSystemContext* context,
                                          const url::Origin& origin,
                                          FileSystemType type) override;
diff --git a/storage/browser/file_system/sandbox_file_system_backend_delegate.cc b/storage/browser/file_system/sandbox_file_system_backend_delegate.cc
index d9fce8b..c1bb191 100644
--- a/storage/browser/file_system/sandbox_file_system_backend_delegate.cc
+++ b/storage/browser/file_system/sandbox_file_system_backend_delegate.cc
@@ -371,14 +371,14 @@
 
 void SandboxFileSystemBackendDelegate::GetOriginsForTypeOnFileTaskRunner(
     FileSystemType type,
-    std::set<GURL>* origins) {
+    std::set<url::Origin>* origins) {
   DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(origins);
   std::unique_ptr<OriginEnumerator> enumerator(CreateOriginEnumerator());
   base::Optional<url::Origin> origin;
   while ((origin = enumerator->Next()).has_value()) {
     if (enumerator->HasFileSystemType(type))
-      origins->insert(origin->GetURL());
+      origins->insert(origin.value());
   }
   switch (type) {
     case kFileSystemTypeTemporary:
@@ -395,7 +395,7 @@
 void SandboxFileSystemBackendDelegate::GetOriginsForHostOnFileTaskRunner(
     FileSystemType type,
     const std::string& host,
-    std::set<GURL>* origins) {
+    std::set<url::Origin>* origins) {
   DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(origins);
   std::unique_ptr<OriginEnumerator> enumerator(CreateOriginEnumerator());
@@ -403,7 +403,7 @@
   while ((origin = enumerator->Next()).has_value()) {
     if (host == net::GetHostOrSpecFromURL(origin->GetURL()) &&
         enumerator->HasFileSystemType(type))
-      origins->insert(origin->GetURL());
+      origins->insert(origin.value());
   }
 }
 
diff --git a/storage/browser/file_system/sandbox_file_system_backend_delegate.h b/storage/browser/file_system/sandbox_file_system_backend_delegate.h
index 9de2196..affeed7 100644
--- a/storage/browser/file_system/sandbox_file_system_backend_delegate.h
+++ b/storage/browser/file_system/sandbox_file_system_backend_delegate.h
@@ -138,11 +138,13 @@
   void PerformStorageCleanupOnFileTaskRunner(FileSystemContext* context,
                                              QuotaManagerProxy* proxy,
                                              FileSystemType type) override;
-  void GetOriginsForTypeOnFileTaskRunner(FileSystemType type,
-                                         std::set<GURL>* origins) override;
-  void GetOriginsForHostOnFileTaskRunner(FileSystemType type,
-                                         const std::string& host,
-                                         std::set<GURL>* origins) override;
+  void GetOriginsForTypeOnFileTaskRunner(
+      FileSystemType type,
+      std::set<url::Origin>* origins) override;
+  void GetOriginsForHostOnFileTaskRunner(
+      FileSystemType type,
+      const std::string& host,
+      std::set<url::Origin>* origins) override;
   int64_t GetOriginUsageOnFileTaskRunner(FileSystemContext* context,
                                          const url::Origin& origin,
                                          FileSystemType type) override;
diff --git a/storage/browser/test/test_file_system_backend.cc b/storage/browser/test/test_file_system_backend.cc
index bfe59b70..60e4621 100644
--- a/storage/browser/test/test_file_system_backend.cc
+++ b/storage/browser/test/test_file_system_backend.cc
@@ -82,14 +82,16 @@
     return scoped_refptr<QuotaReservation>();
   }
 
-  void GetOriginsForTypeOnFileTaskRunner(FileSystemType type,
-                                         std::set<GURL>* origins) override {
+  void GetOriginsForTypeOnFileTaskRunner(
+      FileSystemType type,
+      std::set<url::Origin>* origins) override {
     NOTREACHED();
   }
 
-  void GetOriginsForHostOnFileTaskRunner(FileSystemType type,
-                                         const std::string& host,
-                                         std::set<GURL>* origins) override {
+  void GetOriginsForHostOnFileTaskRunner(
+      FileSystemType type,
+      const std::string& host,
+      std::set<url::Origin>* origins) override {
     NOTREACHED();
   }
 
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 0a932d8..72517d8 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -184,6 +184,22 @@
       },
       {
         "args": [
+          "--enable-features=StorageServiceOutOfProcess"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "storage_service_unsandboxed_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 15
+        },
+        "test": "browser_tests",
+        "test_target": "//chrome/test:browser_tests"
+      },
+      {
+        "args": [
           "--enable-features=NetworkService"
         ],
         "merge": {
@@ -226,6 +242,22 @@
       },
       {
         "args": [
+          "--enable-features=StorageServiceOutOfProcess"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "storage_service_unsandboxed_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 2
+        },
+        "test": "content_browsertests",
+        "test_target": "//content/test:content_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=NetworkService"
         ],
         "merge": {
@@ -241,6 +273,21 @@
       },
       {
         "args": [
+          "--enable-features=StorageServiceOutOfProcess"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "storage_service_unsandboxed_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests",
+        "test_target": "//extensions:extensions_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=NetworkService"
         ],
         "merge": {
@@ -256,6 +303,22 @@
         "test_target": "//chrome/test:interactive_ui_tests"
       },
       {
+        "args": [
+          "--enable-features=StorageServiceOutOfProcess"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "storage_service_unsandboxed_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 3
+        },
+        "test": "interactive_ui_tests",
+        "test_target": "//chrome/test:interactive_ui_tests"
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -318,6 +381,22 @@
       },
       {
         "args": [
+          "--enable-features=StorageServiceOutOfProcess"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "storage_service_unsandboxed_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 15
+        },
+        "test": "browser_tests",
+        "test_target": "//chrome/test:browser_tests"
+      },
+      {
+        "args": [
           "--enable-features=NetworkService,NetworkServiceInProcess"
         ],
         "merge": {
@@ -333,6 +412,53 @@
         "test_target": "//content/test:content_browsertests"
       },
       {
+        "args": [
+          "--enable-features=StorageServiceOutOfProcess"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "storage_service_unsandboxed_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 2
+        },
+        "test": "content_browsertests",
+        "test_target": "//content/test:content_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=StorageServiceOutOfProcess"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "storage_service_unsandboxed_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests",
+        "test_target": "//extensions:extensions_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=StorageServiceOutOfProcess"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "storage_service_unsandboxed_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 3
+        },
+        "test": "interactive_ui_tests",
+        "test_target": "//chrome/test:interactive_ui_tests"
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -10170,42 +10296,6 @@
             }
           ]
         },
-        "test": "boringssl_crypto_tests",
-        "test_target": "//third_party/boringssl:boringssl_crypto_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "boringssl_ssl_tests",
-        "test_target": "//third_party/boringssl:boringssl_ssl_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
         "test": "cast_runner_browsertests",
         "test_target": "//fuchsia/runners:cast_runner_browsertests"
       },
@@ -10247,27 +10337,6 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.cc_unittests.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "cc_unittests",
-        "test_target": "//cc:cc_unittests"
-      },
-      {
-        "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.content_unittests.filter"
         ],
         "merge": {
@@ -10603,24 +10672,6 @@
         "test_target": "//ui/base:ui_base_unittests"
       },
       {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "url_unittests",
-        "test_target": "//url:url_unittests"
-      },
-      {
         "args": [
           "--child-arg=--ozone-platform=headless"
         ],
diff --git a/testing/buildbot/filters/android.emulator.chrome_public_test_apk.filter b/testing/buildbot/filters/android.emulator.chrome_public_test_apk.filter
index 905aed3..90dfc65 100644
--- a/testing/buildbot/filters/android.emulator.chrome_public_test_apk.filter
+++ b/testing/buildbot/filters/android.emulator.chrome_public_test_apk.filter
@@ -1,13 +1,11 @@
-# crbug/1036553
--org.chromium.chrome.browser.translate.TranslateCompactInfoBarTest.testTabMenuDismissedOnOrientationChange
--org.chromium.chrome.browser.translate.TranslateCompactInfoBarTest.testTranslateCompactInfoBarAppears
--org.chromium.chrome.browser.translate.TranslateCompactInfoBarTest.testTranslateCompactInfoBarOverflowMenus
--org.chromium.chrome.browser.translate.TranslateCompactInfoBarTest.testTranslateCompactInfoBarReopenOnTarget
-
 # crbug/1036571
+-org.chromium.shape_detection.BarcodeDetectionImplTest.testDetectBarcodeWithHint
+-org.chromium.shape_detection.BarcodeDetectionImplTest.testDetectBarcodeWithoutHint
 -org.chromium.shape_detection.FaceDetectionImplTest.testDetectRotatedFaceWithGmsCore
+-org.chromium.shape_detection.FaceDetectionImplTest.testDetectValidImageWithGmsCore
 -org.chromium.shape_detection.TextDetectionImplTest.testDetectSucceedsOnValidBitmap
 -org.chromium.chrome.browser.shape_detection.ShapeDetectionTest.testBarcodeDetection
+-org.chromium.chrome.browser.shape_detection.ShapeDetectionTest.testTextDetection
 
 # crbug/1017141
 -org.chromium.chrome.browser.tasks.tab_management.TabSwitcherMultiWindowTest.testMoveTabsAcrossWindow_GTS_WithoutGroup
@@ -19,8 +17,14 @@
 # vr tests do not apply to emulator
 -org.chromium.chrome.browser.hardware_acceleration.ManifestHWATest.testAccelerationDisabled
 
-# crbug/1017141
--org.chromium.chrome.browser.tasks.tab_management.TabSwitcherMultiWindowTest.testMoveTabsAcrossWindow_GTS_WithoutGroup
-
 # crbug/1032118
 -org.chromium.chrome.browser.notifications.StandardNotificationBuilderTest.testSetAll
+
+# crbug/1036551
+-org.chromium.chrome.browser.tabmodel.TabModelMergingTest.testMergeOnColdStartIntoChromeTabbedActivity2
+
+# crbug/1036459
+-org.chromium.chrome.browser.ntp.NewTabPageTest.testFocusFakebox
+
+# crbug/1040088
+-org.chromium.chrome.browser.payments.PaymentRequestRetryTest.testRetryWithShippingAddressErrorsAndPayerErrors
diff --git a/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter b/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
index 866f90a..9791d6f 100644
--- a/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
+++ b/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
@@ -2,7 +2,3 @@
 -NavigationBrowserTest.HistoryBackCancelPendingNavigationUserGesture/1
 -SitePerProcessBrowserTest.*
 -TextFragmentAnchorBrowserTest.DisabledOnScriptNavigation/0
-
-# crbug.com/1037939
--BackForwardCacheBrowserTest.VideoSuspendAndResume
--P/CompositorImplBrowserTestRefreshRate.VideoPreference/0
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 6ebfb05..41257da 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -382,6 +382,7 @@
       # https://crbug.com/1054545
       'Fuchsia ARM64',
       'Fuchsia x64',
+      'fuchsia-fyi-arm64-rel',
     ],
   },
   'boringssl_ssl_tests': {
@@ -389,6 +390,7 @@
       # https://crbug.com/1054545
       'Fuchsia ARM64',
       'Fuchsia x64',
+      'fuchsia-fyi-arm64-rel',
     ],
   },
   'breakpad_unittests': {
@@ -569,6 +571,7 @@
     'remove_from': [
       'Fuchsia ARM64', # https://crbug.com/1046552
       'Fuchsia x64', # https://crbug.com/1046552
+      'fuchsia-fyi-arm64-rel',
     ],
   },
   'checkbins': {
@@ -2165,6 +2168,7 @@
       # crbug.com/1054240
       'Fuchsia ARM64',
       'Fuchsia x64',
+      'fuchsia-fyi-arm64-rel',
     ],
   },
   'video_decode_accelerator_gl_unittest': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 7d2c0008..73ff3b3 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3275,6 +3275,42 @@
       },
     },
 
+    'storage_service_gtests': {
+      'storage_service_unsandboxed_browser_tests': {
+        'args': [
+          '--enable-features=StorageServiceOutOfProcess',
+        ],
+        'swarming': {
+          'shards': 15,
+        },
+        'test': 'browser_tests',
+      },
+      'storage_service_unsandboxed_content_browsertests': {
+        'args': [
+          '--enable-features=StorageServiceOutOfProcess',
+        ],
+        'swarming': {
+          'shards': 2,
+        },
+        'test': 'content_browsertests',
+      },
+      'storage_service_unsandboxed_extensions_browsertests': {
+        'args': [
+          '--enable-features=StorageServiceOutOfProcess',
+        ],
+        'test': 'extensions_browsertests',
+      },
+      'storage_service_unsandboxed_interactive_ui_tests': {
+        'args': [
+          '--enable-features=StorageServiceOutOfProcess'
+        ],
+        'swarming': {
+          'shards': 3,
+        },
+        'test': 'interactive_ui_tests',
+      },
+    },
+
     'swangle_gtests': {
       'angle_deqp_egl_tests': {
         'args': [
@@ -4401,6 +4437,7 @@
       'aura_gtests',
       'mojo_chromiumos_specific_gtests',
       'network_service_gtests',
+      'storage_service_gtests',
     ],
 
     'mojo_linux_gtests': [
@@ -4410,6 +4447,7 @@
       'mojo_windows_specific_gtests',
       'network_service_fyi_gtests',
       'network_service_in_process_gtests',
+      'storage_service_gtests',
     ],
 
     'network_service_extra_gtests': [
diff --git a/testing/iossim/BUILD.gn b/testing/iossim/BUILD.gn
index 71a20741..0358723 100644
--- a/testing/iossim/BUILD.gn
+++ b/testing/iossim/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/ios/ios_sdk.gni")
 import("//build/config/mac/mac_sdk.gni")
 
 if (current_toolchain == host_toolchain) {
@@ -16,4 +17,8 @@
     sources = [ get_label_info(":iossim($host_toolchain)", "root_out_dir") +
                 "/iossim" ]
   }
+} else {
+  group("iossim") {
+    public_deps = [ ":iossim($default_toolchain)" ]
+  }
 }
diff --git a/testing/test.gni b/testing/test.gni
index 2b4e0f7f..eda4330 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -23,6 +23,12 @@
   import("//build/config/chromeos/rules.gni")
 }
 
+if (is_ios) {
+  import("//build/config/ios/ios_sdk.gni")
+  import("//build/config/ios/ios_test_runner_wrapper.gni")
+  import("//build/config/ios/rules.gni")
+}
+
 # Define a test as an executable (or apk on Android) with the "testonly" flag
 # set.
 # Variable:
@@ -227,9 +233,6 @@
       output_name = _exec_target
     }
   } else if (is_ios) {
-    import("//build/config/ios/ios_sdk.gni")
-    import("//build/config/ios/rules.gni")
-
     declare_args() {
       # Keep the unittest-as-xctest functionality defaulted to off until the
       # bots are updated to handle it properly.
@@ -239,6 +242,31 @@
     }
 
     _test_target = target_name
+    _wrapper_output_name = "run_${target_name}"
+    ios_test_runner_wrapper(_wrapper_output_name) {
+      forward_variables_from(invoker,
+                             [
+                               "data",
+                               "data_deps",
+                               "deps",
+                               "executable_args",
+                               "retries",
+                               "shards",
+                             ])
+
+      _root_build_dir = rebase_path("${root_build_dir}", root_build_dir)
+
+      if (!defined(executable_args)) {
+        executable_args = []
+      }
+      executable_args += [
+        "--app",
+        "@WrappedPath(${_root_build_dir}/${_test_target}.app)",
+      ]
+
+      wrapper_output_name = "${_wrapper_output_name}"
+    }
+
     _resources_bundle_data = target_name + "_resources_bundle_data"
 
     bundle_data(_resources_bundle_data) {
@@ -281,6 +309,13 @@
         bundle_deps = []
       }
       bundle_deps += [ ":$_resources_bundle_data" ]
+
+      if (!defined(data_deps)) {
+        data_deps = []
+      }
+
+      # Include the generate_wrapper as part of data_deps
+      data_deps += [ ":${_wrapper_output_name}" ]
     }
   } else if (is_chromeos && cros_board != "") {
     # Building for a cros board (ie: not linux-chromeos).
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index b610d1c..004dead 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -93,21 +93,6 @@
             ]
         }
     ],
-    "AndroidClipboardTextSuggestions": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "OmniboxEnableClipboardProviderTextSuggestions"
-                    ]
-                }
-            ]
-        }
-    ],
     "AndroidInProductHelpContextualSearchPromotePanelOpen": [
         {
             "platforms": [
@@ -495,26 +480,6 @@
             ]
         }
     ],
-    "AppendFrameOriginToNetworkIsolationKey": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AppendFrameOriginToNetworkIsolationKey"
-                    ]
-                }
-            ]
-        }
-    ],
     "AssumeOverlapAfterFixedOrStickyPosition": [
         {
             "platforms": [
@@ -5205,9 +5170,11 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "Enabled_Stable",
                     "enable_features": [
-                        "SplitCacheByNetworkIsolationKey"
+                        "AppendFrameOriginToNetworkIsolationKey",
+                        "SplitCacheByNetworkIsolationKey",
+                        "UseRegistrableDomainInNetworkIsolationKey"
                     ]
                 }
             ]
@@ -5861,29 +5828,10 @@
             ]
         }
     ],
-    "UseRegistrableDomainInNetworkIsolationKey": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "UseRegistrableDomainInNetworkIsolationKey"
-                    ]
-                }
-            ]
-        }
-    ],
     "UseSkiaRenderer": [
         {
             "platforms": [
+                "android",
                 "linux",
                 "windows"
             ],
diff --git a/third_party/blink/common/bluetooth/web_bluetooth_device_id.cc b/third_party/blink/common/bluetooth/web_bluetooth_device_id.cc
index b3d9ba9..7ba88c0 100644
--- a/third_party/blink/common/bluetooth/web_bluetooth_device_id.cc
+++ b/third_party/blink/common/bluetooth/web_bluetooth_device_id.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h"
 
+#include <utility>
+
 #include "base/base64.h"
 #include "base/strings/string_util.h"
 #include "crypto/random.h"
@@ -83,6 +85,11 @@
   return !(*this == device_id);
 }
 
+bool WebBluetoothDeviceId::operator<(
+    const WebBluetoothDeviceId& device_id) const {
+  return str() < device_id.str();
+}
+
 std::ostream& operator<<(std::ostream& out,
                          const WebBluetoothDeviceId& device_id) {
   return out << device_id.str();
diff --git a/third_party/blink/perf_tests/css/CustomPropertiesPendingSubstitution.html b/third_party/blink/perf_tests/css/CustomPropertiesPendingSubstitution.html
new file mode 100644
index 0000000..88b1871
--- /dev/null
+++ b/third_party/blink/perf_tests/css/CustomPropertiesPendingSubstitution.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<script src="../resources/runner.js"></script>
+<script src="resources/utils.js"></script>
+<link help="https://drafts.csswg.org/css-variables/#pending-substitution-value">
+<div id="container" style="height: 100px; overflow: hidden"></div>
+<script>
+    createDOMTree(container, 2, 3);
+    applyCSSRule(':root { --border: 4mm ridge rgba(170, 50, 220, .6); }');
+    applyCSSRule(':root { --margin: 1px 2px 3px 4px; }');
+    applyCSSRule(':root { --padding: 1px 2px 3px 4px; }');
+    applyCSSRule(':root { --background: content-box radial-gradient(crimson, skyblue); }');
+
+    const sequence_size = 2000;
+
+    function create_var_sequence() {
+        let chain = [];
+        for (let i = 0; i < sequence_size; ++i) {
+            chain.push(`var(--v${i}, )`);
+        }
+        return chain.join(' ');
+    }
+
+    applyCSSRule(`div { border: ${create_var_sequence()} var(--border); }`);
+    applyCSSRule(`div { margin: ${create_var_sequence()} var(--margin); }`);
+    applyCSSRule(`div { padding: ${create_var_sequence()} var(--padding); }`);
+    applyCSSRule(`div { background: ${create_var_sequence()} var(--bakground); }`);
+
+    PerfTestRunner.measureTime({
+        description: 'Measure impact of resolving pending-substitution-values',
+        run: function() {
+            container.style.setProperty('display', 'none');
+            forceStyleRecalc(container);
+            container.style.setProperty('display', 'block');
+            forceStyleRecalc(container);
+        }
+    });
+</script>
diff --git a/third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h b/third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h
index 5d00935..0177372 100644
--- a/third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h
+++ b/third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h
@@ -41,6 +41,7 @@
 
   bool operator==(const WebBluetoothDeviceId& device_id) const;
   bool operator!=(const WebBluetoothDeviceId& device_id) const;
+  bool operator<(const WebBluetoothDeviceId& device_id) const;
 
  private:
   std::string device_id_;
diff --git a/third_party/blink/public/common/css/preferred_color_scheme.h b/third_party/blink/public/common/css/preferred_color_scheme.h
index b86c667e..d14dde5 100644
--- a/third_party/blink/public/common/css/preferred_color_scheme.h
+++ b/third_party/blink/public/common/css/preferred_color_scheme.h
@@ -12,6 +12,7 @@
   kNoPreference,
   kDark,
   kLight,
+  kMaxValue = kLight,
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 0fda7e0..c2a6fca 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -165,9 +165,7 @@
   BLINK_PLATFORM_EXPORT static void EnableLazyImageLoadingMetadataFetch(bool);
   BLINK_PLATFORM_EXPORT static void EnableScriptedSpeechRecognition(bool);
   BLINK_PLATFORM_EXPORT static void EnableScriptedSpeechSynthesis(bool);
-  BLINK_PLATFORM_EXPORT static void EnableFetchMetadata(bool);
   BLINK_PLATFORM_EXPORT static void EnableAutoLazyLoadOnReloads(bool);
-  BLINK_PLATFORM_EXPORT static void EnableFetchMetadataDestination(bool);
   BLINK_PLATFORM_EXPORT static void EnableSharedArrayBuffer(bool);
   BLINK_PLATFORM_EXPORT static void EnableSharedWorker(bool);
   BLINK_PLATFORM_EXPORT static void EnableTextFragmentAnchor(bool);
diff --git a/third_party/blink/public/platform/web_theme_engine.h b/third_party/blink/public/platform/web_theme_engine.h
index ecd3c9f..19523a8 100644
--- a/third_party/blink/public/platform/web_theme_engine.h
+++ b/third_party/blink/public/platform/web_theme_engine.h
@@ -34,7 +34,6 @@
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "third_party/blink/public/common/css/forced_colors.h"
-#include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/public/platform/web_color_scheme.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_scrollbar_overlay_color_theme.h"
@@ -233,11 +232,6 @@
 
   virtual ForcedColors GetForcedColors() const { return ForcedColors::kNone; }
   virtual void SetForcedColors(const blink::ForcedColors forced_colors) {}
-  virtual blink::PreferredColorScheme PreferredColorScheme() const {
-    return PreferredColorScheme::kNoPreference;
-  }
-  virtual void SetPreferredColorScheme(
-      const blink::PreferredColorScheme preferred_color_scheme) {}
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/web/web_settings.h b/third_party/blink/public/web/web_settings.h
index 1fe16cd..9a47e89b9 100644
--- a/third_party/blink/public/web/web_settings.h
+++ b/third_party/blink/public/web/web_settings.h
@@ -34,6 +34,7 @@
 #include <unicode/uscript.h>
 
 #include "third_party/blink/public/common/css/navigation_controls.h"
+#include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/public/platform/pointer_properties.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_effective_connection_type.h"
@@ -293,6 +294,7 @@
   virtual void SetLazyImageFirstKFullyLoad3G(int) = 0;
   virtual void SetLazyImageFirstKFullyLoad4G(int) = 0;
   virtual void SetForceDarkModeEnabled(bool) = 0;
+  virtual void SetPreferredColorScheme(PreferredColorScheme) = 0;
   virtual void SetNavigationControls(NavigationControls) = 0;
 
  protected:
diff --git a/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
index 0731143..9f2cb82c 100644
--- a/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
+++ b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
@@ -213,7 +213,7 @@
       V8StringResource<mode> string(value);
       if (!string.Prepare(isolate, exception_state))
         return String();
-      return GetStringFromTrustedHTML(string, execution_context,
+      return TrustedTypesCheckForHTML(string, execution_context,
                                       exception_state);
     }
   }
@@ -235,7 +235,7 @@
       V8StringResource<mode> string(value);
       if (!string.Prepare(isolate, exception_state))
         return String();
-      return GetStringFromTrustedScript(string, execution_context,
+      return TrustedTypesCheckForScript(string, execution_context,
                                         exception_state);
     }
   }
@@ -257,7 +257,7 @@
       V8StringResource<mode> string(value);
       if (!string.Prepare(isolate, exception_state))
         return String();
-      return GetStringFromTrustedScriptURL(string, execution_context,
+      return TrustedTypesCheckForScriptURL(string, execution_context,
                                            exception_state);
     }
   }
diff --git a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc
index 075b2083..769795b 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc
@@ -219,15 +219,13 @@
   if (try_catch.HasCaught())
     return false;
 
-  // To report InvalidStateError Exception, when the constructor returns some
-  // different object
+  // Report a TypeError Exception if the constructor returns a different object.
   if (result != &element) {
     const String& message =
         "custom element constructors must call super() first and must "
         "not return a different object";
-    v8::Local<v8::Value> exception = V8ThrowDOMException::CreateOrEmpty(
-        script_state_->GetIsolate(), DOMExceptionCode::kInvalidStateError,
-        message);
+    v8::Local<v8::Value> exception =
+        V8ThrowException::CreateTypeError(script_state_->GetIsolate(), message);
     if (!exception.IsEmpty())
       V8ScriptRunner::ReportException(isolate, exception);
     return false;
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_html_constructor.cc b/third_party/blink/renderer/bindings/core/v8/v8_html_constructor.cc
index 04fd5ae..3fca5b2 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_html_constructor.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_html_constructor.cc
@@ -128,8 +128,7 @@
       // During upgrade an element has invoked the same constructor
       // before calling 'super' and that invocation has poached the
       // element.
-      exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                        "this instance is already constructed");
+      exception_state.ThrowTypeError("This instance is already constructed");
       return;
     }
   }
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
index 8cb1276..696a6170 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
@@ -413,7 +413,7 @@
     return {false, v8::MaybeLocal<v8::String>()};
   }
 
-  String stringified_source = GetStringFromTrustedScript(
+  String stringified_source = TrustedTypesCheckForScript(
       string_or_trusted_script, ToExecutionContext(context), exception_state);
   if (exception_state.HadException()) {
     exception_state.ClearException();
diff --git a/third_party/blink/renderer/core/DEPS b/third_party/blink/renderer/core/DEPS
index b603f6f0..8759ba0 100644
--- a/third_party/blink/renderer/core/DEPS
+++ b/third_party/blink/renderer/core/DEPS
@@ -11,6 +11,7 @@
     "+base/memory/scoped_policy.h",
     "+base/memory/scoped_refptr.h",
     "+base/metrics/field_trial_params.h",
+    "+base/numerics/ranges.h",
     "+base/strings/stringprintf.h",
     "+base/synchronization/waitable_event.h",
     "+base/task/sequence_manager/task_time_observer.h",
diff --git a/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc b/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc
index 405e1b1..d8073d5 100644
--- a/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc
+++ b/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc
@@ -65,9 +65,8 @@
   ScopedCSSColorSchemeForTest css_feature_scope(true);
   ScopedMetaColorSchemeForTest meta_feature_scope(true);
   GetDocument().GetSettings()->SetForceDarkModeEnabled(true);
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kDark);
+  ColorSchemeHelper color_scheme_helper(GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
   GetDocument().head()->SetInnerHTMLFromString(R"HTML(
     <meta name="color-scheme" content="dark">
   )HTML");
diff --git a/third_party/blink/renderer/core/css/invalidation/pending_invalidations.cc b/third_party/blink/renderer/core/css/invalidation/pending_invalidations.cc
index 678a929e..9453376 100644
--- a/third_party/blink/renderer/core/css/invalidation/pending_invalidations.cc
+++ b/third_party/blink/renderer/core/css/invalidation/pending_invalidations.cc
@@ -26,6 +26,7 @@
     const InvalidationLists& invalidation_lists,
     ContainerNode& node) {
   DCHECK(node.InActiveDocument());
+  DCHECK(!node.GetDocument().InStyleRecalc());
   bool requires_descendant_invalidation = false;
 
   if (node.GetStyleChangeType() < kSubtreeStyleChange) {
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.cc b/third_party/blink/renderer/core/css/parser/css_parser.cc
index 925df43..4cf1abc 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser.cc
@@ -259,14 +259,6 @@
   if (!StyleColor::IsSystemColor(id))
     return false;
 
-  if (!RuntimeEnabledFeatures::LinkSystemColorsEnabled() &&
-      (id == CSSValueID::kLinktext || id == CSSValueID::kVisitedtext)) {
-    return false;
-  } else if (!RuntimeEnabledFeatures::NewSystemColorsEnabled() &&
-             (id == CSSValueID::kActivetext || id == CSSValueID::kField ||
-              id == CSSValueID::kFieldtext)) {
-    return false;
-  }
   color = LayoutTheme::GetTheme().SystemColor(id, color_scheme);
   return true;
 }
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
index 0d766a9..c2034ea 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -928,15 +928,6 @@
     if (!isValueAllowedInMode(id, context.Mode()))
       return nullptr;
     CSSIdentifierValue* color = ConsumeIdent(range);
-    if (!RuntimeEnabledFeatures::LinkSystemColorsEnabled() &&
-        (color->GetValueID() == CSSValueID::kLinktext ||
-         color->GetValueID() == CSSValueID::kVisitedtext)) {
-      return nullptr;
-    } else if (!RuntimeEnabledFeatures::NewSystemColorsEnabled() &&
-               (id == CSSValueID::kActivetext || id == CSSValueID::kField ||
-                id == CSSValueID::kFieldtext)) {
-      return nullptr;
-    }
     return color;
   }
   RGBA32 color = Color::kTransparent;
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade.cc b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
index 1a0ac3c..ed90e6fd 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
@@ -490,6 +490,7 @@
       return cssvalue::CSSUnsetValue::Create();
     }
 
+    resolver.shorthand_cache_.value = &value;
     resolver.shorthand_cache_.parsed_properties = std::move(parsed_properties);
   }
 
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 6bc6616..fd300fb 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -104,11 +104,10 @@
     viewport_resolver_ = MakeGarbageCollected<ViewportStyleResolver>(document);
   if (IsMaster())
     global_rule_set_ = MakeGarbageCollected<CSSGlobalRuleSet>();
-  if (Platform::Current() && Platform::Current()->ThemeEngine()) {
-    preferred_color_scheme_ =
-        Platform::Current()->ThemeEngine()->PreferredColorScheme();
+  if (auto* settings = GetDocument().GetSettings())
+    preferred_color_scheme_ = settings->GetPreferredColorScheme();
+  if (Platform::Current() && Platform::Current()->ThemeEngine())
     forced_colors_ = Platform::Current()->ThemeEngine()->GetForcedColors();
-  }
 }
 
 StyleEngine::~StyleEngine() = default;
@@ -894,6 +893,26 @@
 bool StyleEngine::ShouldSkipInvalidationFor(const Element& element) const {
   if (!element.InActiveDocument())
     return true;
+  if (GetDocument().InStyleRecalc()) {
+#if DCHECK_IS_ON()
+    // TODO(futhark): The InStyleRecalc() if-guard above should have been a
+    // DCHECK(!InStyleRecalc()), but there are a couple of cases where we try to
+    // invalidate style from style recalc:
+    //
+    // 1. We may animate the class attribute of an SVG element and change it
+    //    during style recalc when applying the animation effect.
+    // 2. We may call SetInlineStyle on elements in a UA shadow tree as part of
+    //    style recalc. For instance from HTMLImageFallbackHelper.
+    //
+    // If there are more cases, we need to adjust the DCHECKs below, but ideally
+    // The origin of these invalidations should be fixed.
+    if (!element.IsSVGElement()) {
+      DCHECK(element.ContainingShadowRoot());
+      DCHECK(element.ContainingShadowRoot()->IsUserAgent());
+    }
+#endif  // DCHECK_IS_ON()
+    return true;
+  }
   if (GetDocument().GetStyleChangeType() == kSubtreeStyleChange)
     return true;
   Element* root = GetDocument().documentElement();
@@ -2017,7 +2036,7 @@
   forced_colors_ = web_theme_engine->GetForcedColors();
 
   PreferredColorScheme old_preferred_color_scheme = preferred_color_scheme_;
-  preferred_color_scheme_ = web_theme_engine->PreferredColorScheme();
+  preferred_color_scheme_ = settings->GetPreferredColorScheme();
   if (const auto* overrides =
           GetDocument().GetPage()->GetMediaFeatureOverrides()) {
     MediaQueryExpValue value = overrides->GetOverride("prefers-color-scheme");
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 3275154..e750feac 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -586,9 +586,9 @@
   // scheme is used to opt-out of forced darkening.
   Member<const CSSValue> meta_color_scheme_;
 
-  // The preferred color scheme is set in WebThemeEngine, but may be overridden
-  // by the ForceDarkMode setting where the preferred_color_scheme_ will be set
-  // to kNoPreference to avoid dark styling to be applied before auto darkening.
+  // The preferred color scheme is set in settings, but may be overridden by the
+  // ForceDarkMode setting where the preferred_color_scheme_ will be set to
+  // kNoPreference to avoid dark styling to be applied before auto darkening.
   PreferredColorScheme preferred_color_scheme_ =
       PreferredColorScheme::kNoPreference;
 
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index e404e25..637302e 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -1493,9 +1493,8 @@
 }
 
 TEST_F(StyleEngineTest, MediaQueriesChangeColorScheme) {
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kLight);
+  ColorSchemeHelper color_scheme_helper(GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kLight);
 
   GetDocument().body()->SetInnerHTMLFromString(R"HTML(
     <style>
@@ -1512,8 +1511,7 @@
             GetDocument().body()->GetComputedStyle()->VisitedDependentColor(
                 GetCSSPropertyColor()));
 
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kDark);
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(MakeRGB(0, 128, 0),
             GetDocument().body()->GetComputedStyle()->VisitedDependentColor(
@@ -1522,9 +1520,8 @@
 
 TEST_F(StyleEngineTest, MediaQueriesChangeColorSchemeForcedDarkMode) {
   GetDocument().GetSettings()->SetForceDarkModeEnabled(true);
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kDark);
+  ColorSchemeHelper color_scheme_helper(GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
 
   GetDocument().body()->SetInnerHTMLFromString(R"HTML(
     <style>
@@ -1589,7 +1586,7 @@
             GetDocument().body()->GetComputedStyle()->VisitedDependentColor(
                 GetCSSPropertyColor()));
 
-  ColorSchemeHelper color_scheme_helper;
+  ColorSchemeHelper color_scheme_helper(GetDocument());
   color_scheme_helper.SetForcedColors(GetDocument(), ForcedColors::kActive);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(MakeRGB(0, 128, 0),
@@ -1624,18 +1621,16 @@
   )HTML");
 
   // ForcedColors = kNone, PreferredColorScheme = kLight
-  ColorSchemeHelper color_scheme_helper;
+  ColorSchemeHelper color_scheme_helper(GetDocument());
   color_scheme_helper.SetForcedColors(GetDocument(), ForcedColors::kNone);
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kLight);
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kLight);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(MakeRGB(255, 0, 0),
             GetDocument().body()->GetComputedStyle()->VisitedDependentColor(
                 GetCSSPropertyColor()));
 
   // ForcedColors = kNone, PreferredColorScheme = kDark
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kDark);
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(MakeRGB(0, 128, 0),
             GetDocument().body()->GetComputedStyle()->VisitedDependentColor(
@@ -1650,15 +1645,14 @@
 
   // ForcedColors = kActive, PreferredColorScheme = kNoPreference
   color_scheme_helper.SetPreferredColorScheme(
-      GetDocument(), PreferredColorScheme::kNoPreference);
+      PreferredColorScheme::kNoPreference);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(MakeRGB(255, 255, 0),
             GetDocument().body()->GetComputedStyle()->VisitedDependentColor(
                 GetCSSPropertyColor()));
 
   // ForcedColors = kActive, PreferredColorScheme = kLight
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kLight);
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kLight);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(MakeRGB(0, 0, 255),
             GetDocument().body()->GetComputedStyle()->VisitedDependentColor(
@@ -1666,11 +1660,10 @@
 }
 
 TEST_F(StyleEngineTest, MediaQueriesColorSchemeOverride) {
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kLight);
+  ColorSchemeHelper color_scheme_helper(GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kLight);
   EXPECT_EQ(PreferredColorScheme::kLight,
-            Platform::Current()->ThemeEngine()->PreferredColorScheme());
+            GetDocument().GetSettings()->GetPreferredColorScheme());
 
   GetDocument().body()->SetInnerHTMLFromString(R"HTML(
     <style>
@@ -2130,9 +2123,8 @@
 
 TEST_F(StyleEngineTest, ColorSchemeBaseBackgroundChange) {
   ScopedCSSColorSchemeForTest enable_color_scheme(true);
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kDark);
+  ColorSchemeHelper color_scheme_helper(GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
   UpdateAllLifecyclePhases();
 
   EXPECT_EQ(Color::kWhite, GetDocument().View()->BaseBackgroundColor());
@@ -2148,9 +2140,8 @@
   ScopedCSSColorSchemeForTest enable_color_scheme(true);
   ScopedCSSColorSchemeUARenderingForTest enable_color_scheme_ua(true);
 
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kLight);
+  ColorSchemeHelper color_scheme_helper(GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kLight);
 
   GetDocument().documentElement()->SetInlineStyleProperty(
       CSSPropertyID::kColorScheme, "light dark");
@@ -2524,9 +2515,8 @@
 
 TEST_F(StyleEngineTest, InitialColorChange) {
   // Set color scheme to light.
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kLight);
+  ColorSchemeHelper color_scheme_helper(GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kLight);
 
   GetDocument().body()->SetInnerHTMLFromString(R"HTML(
     <style>
@@ -2552,8 +2542,7 @@
             initial_style->VisitedDependentColor(GetCSSPropertyColor()));
 
   // Change color scheme to dark.
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kDark);
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
   UpdateAllLifecyclePhases();
 
   document_element_style = GetDocument().documentElement()->GetComputedStyle();
diff --git a/third_party/blink/renderer/core/css/svg.css b/third_party/blink/renderer/core/css/svg.css
index 6337533..15438d9 100644
--- a/third_party/blink/renderer/core/css/svg.css
+++ b/third_party/blink/renderer/core/css/svg.css
@@ -95,10 +95,6 @@
    https://drafts.csswg.org/css-color-adjust-1/#forced-colors-properties
 */
 @media forced-colors {
-  svg:root {
-      color: CanvasText;
-  }
-
   svg {
       forced-color-adjust: none;
   }
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 81494da..822aa14e 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -4353,7 +4353,7 @@
   for (const String& string : text)
     builder.Append(string);
   String string =
-      GetStringFromTrustedHTML(builder.ToString(), this, exception_state);
+      TrustedTypesCheckForHTML(builder.ToString(), this, exception_state);
   if (exception_state.HadException())
     return;
 
@@ -4370,7 +4370,7 @@
   for (const String& string : text)
     builder.Append(string);
   String string =
-      GetStringFromTrustedHTML(builder.ToString(), this, exception_state);
+      TrustedTypesCheckForHTML(builder.ToString(), this, exception_state);
   if (exception_state.HadException())
     return;
 
diff --git a/third_party/blink/renderer/core/dom/document_test.cc b/third_party/blink/renderer/core/dom/document_test.cc
index 01fd1e0..981a673 100644
--- a/third_party/blink/renderer/core/dom/document_test.cc
+++ b/third_party/blink/renderer/core/dom/document_test.cc
@@ -1145,9 +1145,8 @@
 }
 
 TEST_F(DocumentTest, PrefersColorSchemeChanged) {
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kLight);
+  ColorSchemeHelper color_scheme_helper(GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kLight);
   UpdateAllLifecyclePhasesForTest();
 
   auto* list = GetDocument().GetMediaQueryMatcher().MatchMedia(
@@ -1157,8 +1156,7 @@
 
   EXPECT_FALSE(listener->IsNotified());
 
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kDark);
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
 
   UpdateAllLifecyclePhasesForTest();
   GetDocument().ServiceScriptedAnimations(base::TimeTicks());
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index b83762c..907aed66 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2247,9 +2247,9 @@
   QualifiedName q_name = QualifiedName::Null();
   std::tie(index, q_name) = LookupAttributeQNameInternal(local_name);
 
-  String trusted_value = GetStringFromSpecificTrustedType(
-      value, ExpectedTrustedTypeForAttribute(q_name),
-      GetDocument().ToExecutionContext(), exception_state);
+  String trusted_value =
+      TrustedTypesCheckFor(ExpectedTrustedTypeForAttribute(q_name), value,
+                           GetDocument().ToExecutionContext(), exception_state);
   if (exception_state.HadException())
     return;
 
@@ -2280,9 +2280,9 @@
                          ? GetElementData()->Attributes().FindIndex(name)
                          : kNotFound;
 
-  String trusted_value = GetStringFromSpecificTrustedType(
-      value, ExpectedTrustedTypeForAttribute(name),
-      GetDocument().ToExecutionContext(), exception_state);
+  String trusted_value =
+      TrustedTypesCheckFor(ExpectedTrustedTypeForAttribute(name), value,
+                           GetDocument().ToExecutionContext(), exception_state);
   if (exception_state.HadException())
     return;
 
@@ -2300,7 +2300,8 @@
 
 void Element::setAttribute(
     const AtomicString& local_name,
-    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL& string_or_TT,
+    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL&
+        string_or_trusted,
     ExceptionState& exception_state) {
   if (!Document::IsValidName(local_name)) {
     exception_state.ThrowDOMException(
@@ -2313,8 +2314,8 @@
   wtf_size_t index;
   QualifiedName q_name = QualifiedName::Null();
   std::tie(index, q_name) = LookupAttributeQNameInternal(local_name);
-  String value = GetStringFromSpecificTrustedType(
-      string_or_TT, ExpectedTrustedTypeForAttribute(q_name),
+  String value = TrustedTypesCheckFor(
+      ExpectedTrustedTypeForAttribute(q_name), string_or_trusted,
       GetDocument().ToExecutionContext(), exception_state);
   if (exception_state.HadException())
     return;
@@ -2350,7 +2351,7 @@
     // starting with "on", including e.g. "one". We use this pattern elsewhere
     // (e.g. in IsEventHandlerAttribute) but it's not ideal. Consider using
     // the event attribute of the resulting AttributeTriggers.
-    return SpecificTrustedType::kTrustedScript;
+    return SpecificTrustedType::kScript;
   }
 
   return SpecificTrustedType::kNone;
@@ -2360,7 +2361,7 @@
                            const StringOrTrustedHTML& stringOrHTML,
                            ExceptionState& exception_state) {
   String valueString =
-      GetStringFromTrustedHTML(stringOrHTML, &GetDocument(), exception_state);
+      TrustedTypesCheckForHTML(stringOrHTML, &GetDocument(), exception_state);
   if (!exception_state.HadException()) {
     setAttribute(name, AtomicString(valueString));
   }
@@ -2369,7 +2370,7 @@
 void Element::setAttribute(const QualifiedName& name,
                            const StringOrTrustedScript& stringOrScript,
                            ExceptionState& exception_state) {
-  String valueString = GetStringFromTrustedScript(
+  String valueString = TrustedTypesCheckForScript(
       stringOrScript, GetDocument().ToExecutionContext(), exception_state);
   if (!exception_state.HadException()) {
     setAttribute(name, AtomicString(valueString));
@@ -2379,7 +2380,7 @@
 void Element::setAttribute(const QualifiedName& name,
                            const StringOrTrustedScriptURL& stringOrURL,
                            ExceptionState& exception_state) {
-  String valueString = GetStringFromTrustedScriptURL(
+  String valueString = TrustedTypesCheckForScriptURL(
       stringOrURL, GetDocument().ToExecutionContext(), exception_state);
   if (!exception_state.HadException()) {
     setAttribute(name, AtomicString(valueString));
@@ -3919,10 +3920,9 @@
   SynchronizeAllAttributes();
   const UniqueElementData& element_data = EnsureUniqueElementData();
 
-  String value = GetStringFromSpecificTrustedType(
-      attr_node->value(),
+  String value = TrustedTypesCheckFor(
       ExpectedTrustedTypeForAttribute(attr_node->GetQualifiedName()),
-      GetDocument().ToExecutionContext(), exception_state);
+      attr_node->value(), GetDocument().ToExecutionContext(), exception_state);
   if (exception_state.HadException())
     return nullptr;
 
@@ -4033,15 +4033,16 @@
 void Element::setAttributeNS(
     const AtomicString& namespace_uri,
     const AtomicString& qualified_name,
-    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL& string_or_TT,
+    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL&
+        string_or_trusted,
     ExceptionState& exception_state) {
   QualifiedName parsed_name = g_any_name;
   if (!ParseAttributeName(parsed_name, namespace_uri, qualified_name,
                           exception_state))
     return;
 
-  String value = GetStringFromSpecificTrustedType(
-      string_or_TT, ExpectedTrustedTypeForAttribute(parsed_name),
+  String value = TrustedTypesCheckFor(
+      ExpectedTrustedTypeForAttribute(parsed_name), string_or_trusted,
       GetDocument().ToExecutionContext(), exception_state);
   if (exception_state.HadException())
     return;
@@ -4693,7 +4694,7 @@
 void Element::setInnerHTML(const StringOrTrustedHTML& string_or_html,
                            ExceptionState& exception_state) {
   String html =
-      GetStringFromTrustedHTML(string_or_html, &GetDocument(), exception_state);
+      TrustedTypesCheckForHTML(string_or_html, &GetDocument(), exception_state);
   if (!exception_state.HadException()) {
     SetInnerHTMLFromString(html, exception_state);
   }
@@ -4758,7 +4759,7 @@
 void Element::setOuterHTML(const StringOrTrustedHTML& string_or_html,
                            ExceptionState& exception_state) {
   String html =
-      GetStringFromTrustedHTML(string_or_html, &GetDocument(), exception_state);
+      TrustedTypesCheckForHTML(string_or_html, &GetDocument(), exception_state);
   if (!exception_state.HadException()) {
     SetOuterHTMLFromString(html, exception_state);
   }
@@ -4908,7 +4909,7 @@
                                  const StringOrTrustedHTML& string_or_html,
                                  ExceptionState& exception_state) {
   String markup =
-      GetStringFromTrustedHTML(string_or_html, &GetDocument(), exception_state);
+      TrustedTypesCheckForHTML(string_or_html, &GetDocument(), exception_state);
   if (!exception_state.HadException()) {
     insertAdjacentHTML(where, markup, exception_state);
   }
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 7a197a00..a5b34d8 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -826,7 +826,7 @@
                             ? node_or_string.GetAsString()
                             : node_or_string.GetAsNode()->textContent();
 
-  string_value = GetStringFromTrustedScript(
+  string_value = TrustedTypesCheckForScript(
       string_value, document.ToExecutionContext(), exception_state);
   if (exception_state.HadException())
     return nullptr;
diff --git a/third_party/blink/renderer/core/dom/range.cc b/third_party/blink/renderer/core/dom/range.cc
index 8aa5076..ce2ec76 100644
--- a/third_party/blink/renderer/core/dom/range.cc
+++ b/third_party/blink/renderer/core/dom/range.cc
@@ -973,7 +973,7 @@
   Document& document = start_.Container().GetDocument();
 
   String markup =
-      GetStringFromTrustedHTML(string_or_html, &document, exception_state);
+      TrustedTypesCheckForHTML(string_or_html, &document, exception_state);
   if (!exception_state.HadException()) {
     return createContextualFragmentFromString(markup, exception_state);
   }
diff --git a/third_party/blink/renderer/core/dom/shadow_root.cc b/third_party/blink/renderer/core/dom/shadow_root.cc
index aaa3849..afd186608 100644
--- a/third_party/blink/renderer/core/dom/shadow_root.cc
+++ b/third_party/blink/renderer/core/dom/shadow_root.cc
@@ -132,7 +132,7 @@
 void ShadowRoot::setInnerHTML(const StringOrTrustedHTML& stringOrHtml,
                               ExceptionState& exception_state) {
   String html =
-      GetStringFromTrustedHTML(stringOrHtml, &GetDocument(), exception_state);
+      TrustedTypesCheckForHTML(stringOrHtml, &GetDocument(), exception_state);
   if (!exception_state.HadException()) {
     SetInnerHTMLFromString(html, exception_state);
   }
diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.cc b/third_party/blink/renderer/core/exported/web_settings_impl.cc
index ce3bb3a..075b02a 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.cc
@@ -770,6 +770,11 @@
   settings_->SetForceDarkModeEnabled(enabled);
 }
 
+void WebSettingsImpl::SetPreferredColorScheme(
+    PreferredColorScheme color_scheme) {
+  settings_->SetPreferredColorScheme(color_scheme);
+}
+
 void WebSettingsImpl::SetNavigationControls(
     NavigationControls navigation_controls) {
   settings_->SetNavigationControls(navigation_controls);
diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.h b/third_party/blink/renderer/core/exported/web_settings_impl.h
index 9720f98..deb6bb3 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.h
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.h
@@ -220,6 +220,7 @@
   void SetLazyImageFirstKFullyLoad4G(int) override;
 
   void SetForceDarkModeEnabled(bool) override;
+  void SetPreferredColorScheme(PreferredColorScheme) override;
   void SetNavigationControls(NavigationControls) override;
 
   bool RenderVSyncNotificationEnabled() const {
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 8f101d341..33fc2074 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -581,9 +581,8 @@
   ScopedCSSColorSchemeForTest enable_color_scheme(true);
 
   WebViewImpl* web_view = web_view_helper_.Initialize();
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(*(web_view->GetPage()),
-                                              PreferredColorScheme::kLight);
+  ColorSchemeHelper color_scheme_helper(*(web_view->GetPage()));
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kLight);
   web_view->SetBaseBackgroundColor(SK_ColorBLUE);
 
   WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
@@ -595,8 +594,7 @@
   LocalFrameView* frame_view = web_view->MainFrameImpl()->GetFrame()->View();
   EXPECT_EQ(Color(0, 0, 255), frame_view->BaseBackgroundColor());
 
-  color_scheme_helper.SetPreferredColorScheme(*(web_view->GetPage()),
-                                              PreferredColorScheme::kDark);
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(Color::kBlack, frame_view->BaseBackgroundColor());
 
@@ -606,8 +604,7 @@
   web_view->SetBaseBackgroundColor(SK_ColorBLUE);
   EXPECT_EQ(Color::kBlack, frame_view->BaseBackgroundColor());
 
-  color_scheme_helper.SetPreferredColorScheme(*(web_view->GetPage()),
-                                              PreferredColorScheme::kLight);
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kLight);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(Color(0, 0, 255), frame_view->BaseBackgroundColor());
 }
diff --git a/third_party/blink/renderer/core/frame/settings.h b/third_party/blink/renderer/core/frame/settings.h
index 1793196..eeb0560 100644
--- a/third_party/blink/renderer/core/frame/settings.h
+++ b/third_party/blink/renderer/core/frame/settings.h
@@ -32,6 +32,7 @@
 
 #include "base/macros.h"
 #include "third_party/blink/public/common/css/navigation_controls.h"
+#include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/blink/public/platform/pointer_properties.h"
 #include "third_party/blink/public/platform/web_effective_connection_type.h"
diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5
index 6f330231..f93688ff 100644
--- a/third_party/blink/renderer/core/frame/settings.json5
+++ b/third_party/blink/renderer/core/frame/settings.json5
@@ -1048,6 +1048,15 @@
       type: "int",
     },
 
+    // Preferred color scheme from the OS/application passed to the renderer for
+    // evaluating the prefers-color-scheme media query.
+    {
+      name: "preferredColorScheme",
+      initial: "PreferredColorScheme::kNoPreference",
+      invalidate: "ColorScheme",
+      type: "PreferredColorScheme",
+    },
+
     // Preferred motion-reduction setting from the OS/application passed to the
     // renderer for evaluating the prefers-reduced-motion media query.
     {
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 3db933d..c4a2319 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -2747,9 +2747,8 @@
   ScopedCSSColorSchemeForTest color_scheme_enabled(true);
   ScopedCSSColorSchemeUARenderingForTest color_scheme_ua_enabled(true);
 
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(*(WebView().GetPage()),
-                                              PreferredColorScheme::kDark);
+  ColorSchemeHelper color_scheme_helper(*(WebView().GetPage()));
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
   WebView().MainFrameWidget()->Resize(WebSize(400, 600));
 
   const VisualViewport& visual_viewport =
diff --git a/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc b/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc
index 01db5a95..dcb8551 100644
--- a/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc
@@ -156,7 +156,7 @@
     const HeapVector<ScriptValue>& arguments,
     ExceptionState& exception_state) {
   ExecutionContext* execution_context = event_target.GetExecutionContext();
-  String handler = GetStringFromTrustedScript(
+  String handler = TrustedTypesCheckForScript(
       string_or_trusted_script, execution_context, exception_state);
   if (exception_state.HadException())
     return 0;
@@ -211,7 +211,7 @@
     const HeapVector<ScriptValue>& arguments,
     ExceptionState& exception_state) {
   ExecutionContext* execution_context = event_target.GetExecutionContext();
-  String handler = GetStringFromTrustedScript(
+  String handler = TrustedTypesCheckForScript(
       string_or_trusted_script, execution_context, exception_state);
   if (exception_state.HadException())
     return 0;
diff --git a/third_party/blink/renderer/core/html/html_embed_element.cc b/third_party/blink/renderer/core/html/html_embed_element.cc
index f27a829..0b78fee 100644
--- a/third_party/blink/renderer/core/html/html_embed_element.cc
+++ b/third_party/blink/renderer/core/html/html_embed_element.cc
@@ -52,7 +52,7 @@
 const AttrNameToTrustedType& HTMLEmbedElement::GetCheckedAttributeTypes()
     const {
   DEFINE_STATIC_LOCAL(AttrNameToTrustedType, attribute_map,
-                      ({{"src", SpecificTrustedType::kTrustedScriptURL}}));
+                      ({{"src", SpecificTrustedType::kScriptURL}}));
   return attribute_map;
 }
 
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.cc b/third_party/blink/renderer/core/html/html_iframe_element.cc
index 657c7d3..5b06845 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element.cc
@@ -64,7 +64,7 @@
 const AttrNameToTrustedType& HTMLIFrameElement::GetCheckedAttributeTypes()
     const {
   DEFINE_STATIC_LOCAL(AttrNameToTrustedType, attribute_map,
-                      ({{"srcdoc", SpecificTrustedType::kTrustedHTML}}));
+                      ({{"srcdoc", SpecificTrustedType::kHTML}}));
   return attribute_map;
 }
 
diff --git a/third_party/blink/renderer/core/html/html_meta_element_test.cc b/third_party/blink/renderer/core/html/html_meta_element_test.cc
index 39e8faf..3b7298f 100644
--- a/third_party/blink/renderer/core/html/html_meta_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_meta_element_test.cc
@@ -225,9 +225,8 @@
 }
 
 TEST_F(HTMLMetaElementTest, ColorSchemeForcedDarkeningAndMQ) {
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kDark);
+  ColorSchemeHelper color_scheme_helper(GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
 
   auto* media_query = GetDocument().GetMediaQueryMatcher().MatchMedia(
       "(prefers-color-scheme: dark)");
diff --git a/third_party/blink/renderer/core/html/html_object_element.cc b/third_party/blink/renderer/core/html/html_object_element.cc
index fe93dcdb..8ff7344 100644
--- a/third_party/blink/renderer/core/html/html_object_element.cc
+++ b/third_party/blink/renderer/core/html/html_object_element.cc
@@ -66,8 +66,8 @@
 const AttrNameToTrustedType& HTMLObjectElement::GetCheckedAttributeTypes()
     const {
   DEFINE_STATIC_LOCAL(AttrNameToTrustedType, attribute_map,
-                      ({{"data", SpecificTrustedType::kTrustedScriptURL},
-                        {"codebase", SpecificTrustedType::kTrustedScriptURL}}));
+                      ({{"data", SpecificTrustedType::kScriptURL},
+                        {"codebase", SpecificTrustedType::kScriptURL}}));
   return attribute_map;
 }
 
diff --git a/third_party/blink/renderer/core/html/html_script_element.cc b/third_party/blink/renderer/core/html/html_script_element.cc
index e539c8f..cdf74725 100644
--- a/third_party/blink/renderer/core/html/html_script_element.cc
+++ b/third_party/blink/renderer/core/html/html_script_element.cc
@@ -53,7 +53,7 @@
 const AttrNameToTrustedType& HTMLScriptElement::GetCheckedAttributeTypes()
     const {
   DEFINE_STATIC_LOCAL(AttrNameToTrustedType, attribute_map,
-                      ({{"src", SpecificTrustedType::kTrustedScriptURL}}));
+                      ({{"src", SpecificTrustedType::kScriptURL}}));
   return attribute_map;
 }
 
@@ -136,7 +136,7 @@
 void HTMLScriptElement::setInnerText(
     const StringOrTrustedScript& string_or_trusted_script,
     ExceptionState& exception_state) {
-  String value = GetStringFromTrustedScript(string_or_trusted_script,
+  String value = TrustedTypesCheckForScript(string_or_trusted_script,
                                             GetDocument().ToExecutionContext(),
                                             exception_state);
   if (!exception_state.HadException()) {
@@ -151,7 +151,7 @@
 void HTMLScriptElement::setTextContent(
     const StringOrTrustedScript& string_or_trusted_script,
     ExceptionState& exception_state) {
-  String value = GetStringFromTrustedScript(string_or_trusted_script,
+  String value = TrustedTypesCheckForScript(string_or_trusted_script,
                                             GetDocument().ToExecutionContext(),
                                             exception_state);
   if (!exception_state.HadException()) {
diff --git a/third_party/blink/renderer/core/html/resources/forced_colors.css b/third_party/blink/renderer/core/html/resources/forced_colors.css
index c35e7e7..8f71b5c5 100644
--- a/third_party/blink/renderer/core/html/resources/forced_colors.css
+++ b/third_party/blink/renderer/core/html/resources/forced_colors.css
@@ -11,7 +11,6 @@
 @media forced-colors {
   body {
     background-color: Canvas;
-    color: CanvasText;
     fill: currentColor;
   }
 
diff --git a/third_party/blink/renderer/core/layout/layout_theme_test.cc b/third_party/blink/renderer/core/layout/layout_theme_test.cc
index 4e57faa..f4c315b 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_test.cc
@@ -104,9 +104,8 @@
             style->VisitedDependentColor(GetCSSPropertyColor()));
 
   // Change color scheme to dark.
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kDark);
+  ColorSchemeHelper color_scheme_helper(GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
   UpdateAllLifecyclePhasesForTest();
 
   style = dark_element->GetComputedStyle();
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
index f0f7d81..f770adf6 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
@@ -30,7 +30,7 @@
 }
 
 scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
-  // TODO(almaher): Handle fragmentation for fieldset children.
+  // TODO(almaher): Finish fragmentation for legends.
 
   // Layout of a fieldset container consists of two parts: Create a child
   // fragment for the rendered legend (if any), and create a child fragment for
@@ -65,8 +65,30 @@
       container_builder_.SetIsInitialColumnBalancingPass();
   }
 
+  scoped_refptr<const NGBlockBreakToken> content_break_token;
+  bool has_seen_all_children = false;
+  if (const auto* token = BreakToken()) {
+    const auto child_tokens = token->ChildBreakTokens();
+    if (wtf_size_t break_token_count = child_tokens.size()) {
+      scoped_refptr<const NGBlockBreakToken> child_token =
+          To<NGBlockBreakToken>(child_tokens[0]);
+      if (child_token) {
+        DCHECK(!child_token->InputNode().IsRenderedLegend());
+        content_break_token = child_token;
+      }
+      // There shouldn't be any additional break tokens.
+      DCHECK_EQ(child_tokens.size(), 1u);
+    }
+
+    if (token->HasSeenAllChildren()) {
+      has_seen_all_children = true;
+      container_builder_.SetHasSeenAllChildren();
+    }
+  }
+
   // TODO(vmpstr): Skip child (including legend) layout for fieldset elements.
-  if (NGBlockNode legend = Node().GetRenderedLegend()) {
+  NGBlockNode legend = Node().GetRenderedLegend();
+  if (!content_break_token && legend && !has_seen_all_children) {
     // Lay out the legend. While the fieldset container normally ignores its
     // padding, the legend is laid out within what would have been the content
     // box had the fieldset been a regular block with no weirdness.
@@ -111,42 +133,54 @@
     }
 
     container_builder_.AddChild(physical_fragment, legend_offset);
+    DCHECK(!physical_fragment.BreakToken());
   }
 
   NGBoxStrut borders_with_legend = borders;
   borders_with_legend.block_start = block_start_padding_edge;
+  LogicalSize adjusted_padding_box_size =
+      ShrinkAvailableSize(border_box_size, borders_with_legend);
+  if (IsResumingLayout(BreakToken()))
+    borders_with_legend.block_start = LayoutUnit();
   LayoutUnit intrinsic_block_size = borders_with_legend.BlockSum();
 
   // Proceed with normal fieldset children (excluding the rendered legend). They
   // all live inside an anonymous child box of the fieldset container.
-  if (auto fieldset_content = Node().GetFieldsetContent()) {
-    LogicalSize adjusted_padding_box_size =
-        ShrinkAvailableSize(border_box_size, borders_with_legend);
-    auto child_space =
-        CreateConstraintSpaceForFieldsetContent(adjusted_padding_box_size);
-    auto result = fieldset_content.Layout(child_space, BreakToken());
+  auto fieldset_content = Node().GetFieldsetContent();
+  if (fieldset_content && (content_break_token || !has_seen_all_children)) {
+    auto child_space = CreateConstraintSpaceForFieldsetContent(
+        fieldset_content, adjusted_padding_box_size,
+        borders_with_legend.block_start);
+    auto result =
+        fieldset_content.Layout(child_space, content_break_token.get());
 
     // TODO(layout-dev): Handle abortions caused by block fragmentation.
     DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess);
 
-    const auto& physical_fragment = result->PhysicalFragment();
-    container_builder_.AddChild(physical_fragment,
-                                borders_with_legend.StartOffset());
+    container_builder_.AddResult(*result, borders_with_legend.StartOffset());
 
+    const auto& physical_fragment = result->PhysicalFragment();
     intrinsic_block_size +=
         NGFragment(writing_mode, physical_fragment).BlockSize();
-  } else {
+    container_builder_.SetHasSeenAllChildren();
+  }
+  if (!fieldset_content) {
     // There was no anonymous child to provide the padding, so we have to add it
     // ourselves.
+    container_builder_.SetHasSeenAllChildren();
     intrinsic_block_size += padding.BlockSum();
   }
 
   intrinsic_block_size = ClampIntrinsicBlockSize(
       ConstraintSpace(), Node(), adjusted_border_padding, intrinsic_block_size);
 
+  LayoutUnit consumed_block_size =
+      BreakToken() ? BreakToken()->ConsumedBlockSize() : LayoutUnit();
+
   // Recompute the block-axis size now that we know our content size.
-  border_box_size.block_size = ComputeBlockSizeForFragment(
-      ConstraintSpace(), Style(), border_padding_, intrinsic_block_size);
+  border_box_size.block_size =
+      ComputeBlockSizeForFragment(ConstraintSpace(), Style(), border_padding_,
+                                  intrinsic_block_size + consumed_block_size);
 
   // The above computation utility knows nothing about fieldset weirdness. The
   // legend may eat from the available content box block size. Make room for
@@ -161,9 +195,6 @@
         std::max(border_box_size.block_size, minimum_border_box_block_size);
   }
 
-  LayoutUnit consumed_block_size =
-      BreakToken() ? BreakToken()->ConsumedBlockSize() : LayoutUnit();
-
   // TODO(almaher): end border and padding may overflow the parent
   // fragmentainer, and we should avoid that.
   LayoutUnit block_size = border_box_size.block_size - consumed_block_size;
@@ -239,7 +270,9 @@
 
 const NGConstraintSpace
 NGFieldsetLayoutAlgorithm::CreateConstraintSpaceForFieldsetContent(
-    LogicalSize padding_box_size) {
+    NGBlockNode fieldset_content,
+    LogicalSize padding_box_size,
+    LayoutUnit block_offset) {
   NGConstraintSpaceBuilder builder(ConstraintSpace(),
                                    ConstraintSpace().GetWritingMode(),
                                    /* is_new_fc */ true);
@@ -247,6 +280,12 @@
   builder.SetPercentageResolutionSize(
       ConstraintSpace().PercentageResolutionSize());
   builder.SetIsFixedBlockSize(padding_box_size.block_size != kIndefiniteSize);
+
+  if (ConstraintSpace().HasBlockFragmentation()) {
+    SetupFragmentation(ConstraintSpace(), fieldset_content, block_offset,
+                       &builder, /* is_new_fc */ true);
+    builder.SetEarlyBreakAppeal(container_builder_.BreakAppeal());
+  }
   return builder.ToConstraintSpace();
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
index 35b6ada..b3f8f0e1 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
@@ -32,7 +32,9 @@
       NGBlockNode legend,
       LogicalSize available_size);
   const NGConstraintSpace CreateConstraintSpaceForFieldsetContent(
-      LogicalSize padding_box_size);
+      NGBlockNode fieldset_content,
+      LogicalSize padding_box_size,
+      LayoutUnit block_offset);
 
   const NGBoxStrut border_padding_;
 };
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
index 44355539..4979bb575 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
@@ -574,5 +574,131 @@
   ASSERT_FALSE(fragment->BreakToken());
 }
 
+// Tests that a fieldset with auto height will fragment when its content reaches
+// the fragmentation line.
+TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentationAutoHeight) {
+  SetBodyInnerHTML(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        #fieldset {
+          border:3px solid; margin:0; padding:10px; width: 150px;
+        }
+        #child {
+          margin:0; width: 50px; height: 500px;
+        }
+      </style>
+      <fieldset id="fieldset">
+        <div id="child"></child>
+      </fieldset>
+  )HTML");
+
+  LayoutUnit kFragmentainerSpaceAvailable(200);
+
+  NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
+  NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
+      WritingMode::kHorizontalTb, TextDirection::kLtr,
+      LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
+      node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
+
+  scoped_refptr<const NGPhysicalBoxFragment> fragment =
+      NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  String dump = DumpFragmentTree(fragment.get());
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x200
+    offset:3,3 size:170x197
+      offset:10,10 size:50x187
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x200
+    offset:3,0 size:170x200
+      offset:10,0 size:50x200
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x126
+    offset:3,0 size:170x123
+      offset:10,0 size:50x113
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+// Tests that a fieldset with a set height will fragment when its content
+// reaches the fragmentation line.
+TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentation) {
+  SetBodyInnerHTML(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        #fieldset {
+          border:3px solid; margin:0; padding:10px; width: 150px; height: 100px;
+        }
+        #child {
+          margin:0; width: 50px; height: 500px;
+        }
+      </style>
+      <fieldset id="fieldset">
+        <div id="child"></child>
+      </fieldset>
+  )HTML");
+
+  LayoutUnit kFragmentainerSpaceAvailable(200);
+
+  NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
+  NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
+      WritingMode::kHorizontalTb, TextDirection::kLtr,
+      LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
+      node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
+
+  scoped_refptr<const NGPhysicalBoxFragment> fragment =
+      NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  String dump = DumpFragmentTree(fragment.get());
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x126
+    offset:3,3 size:170x120
+      offset:10,10 size:50x187
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x0
+    offset:3,0 size:170x0
+      offset:10,0 size:50x200
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x0
+    offset:3,0 size:170x0
+      offset:10,0 size:50x113
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
 }  // anonymous namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/scrollbars_test.cc b/third_party/blink/renderer/core/layout/scrollbars_test.cc
index d46860e..7562c57 100644
--- a/third_party/blink/renderer/core/layout/scrollbars_test.cc
+++ b/third_party/blink/renderer/core/layout/scrollbars_test.cc
@@ -75,19 +75,8 @@
     return painted_color_scheme_[part];
   }
 
-  blink::PreferredColorScheme PreferredColorScheme() const override {
-    return preferred_color_scheme_;
-  }
-
-  void SetPreferredColorScheme(
-      const blink::PreferredColorScheme preferred_color_scheme) override {
-    preferred_color_scheme_ = preferred_color_scheme;
-  }
-
  private:
   std::array<WebColorScheme, kPartProgressBar + 1> painted_color_scheme_;
-  blink::PreferredColorScheme preferred_color_scheme_ =
-      blink::PreferredColorScheme::kNoPreference;
 };
 
 constexpr int StubWebThemeEngine::kMinimumHorizontalLength;
@@ -2832,9 +2821,8 @@
     </div>
   )HTML");
 
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(GetDocument(),
-                                              PreferredColorScheme::kDark);
+  ColorSchemeHelper color_scheme_helper(GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
 
   Compositor().BeginFrame();
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
index 20a540c..da375b6 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
@@ -84,7 +84,7 @@
 void LayoutSVGContainer::AddChild(LayoutObject* child,
                                   LayoutObject* before_child) {
   LayoutSVGModelObject::AddChild(child, before_child);
-  SVGResourcesCache::ClientWasAddedToTree(*child, child->StyleRef());
+  SVGResourcesCache::ClientWasAddedToTree(*child);
 
   bool should_isolate_descendants =
       (child->IsBlendingAllowed() && child->StyleRef().HasBlendMode()) ||
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
index 78e884af..09e1acd 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
@@ -145,7 +145,7 @@
 void LayoutSVGInline::AddChild(LayoutObject* child,
                                LayoutObject* before_child) {
   LayoutInline::AddChild(child, before_child);
-  SVGResourcesCache::ClientWasAddedToTree(*child, child->StyleRef());
+  SVGResourcesCache::ClientWasAddedToTree(*child);
 
   if (LayoutSVGText* text_layout_object =
           LayoutSVGText::LocateLayoutSVGTextAncestor(this))
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
index a39d5b6..eeaf8f2 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
@@ -336,7 +336,7 @@
 
 void LayoutSVGRoot::AddChild(LayoutObject* child, LayoutObject* before_child) {
   LayoutReplaced::AddChild(child, before_child);
-  SVGResourcesCache::ClientWasAddedToTree(*child, child->StyleRef());
+  SVGResourcesCache::ClientWasAddedToTree(*child);
 
   bool should_isolate_descendants =
       (child->IsBlendingAllowed() && child->StyleRef().HasBlendMode()) ||
@@ -383,7 +383,7 @@
 
 void LayoutSVGRoot::InsertedIntoTree() {
   LayoutReplaced::InsertedIntoTree();
-  SVGResourcesCache::ClientWasAddedToTree(*this, StyleRef());
+  SVGResourcesCache::ClientWasAddedToTree(*this);
 }
 
 void LayoutSVGRoot::WillBeRemovedFromTree() {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
index bbfd3b0..a0539f0 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
@@ -428,7 +428,7 @@
 void LayoutSVGText::AddChild(LayoutObject* child, LayoutObject* before_child) {
   LayoutSVGBlock::AddChild(child, before_child);
 
-  SVGResourcesCache::ClientWasAddedToTree(*child, child->StyleRef());
+  SVGResourcesCache::ClientWasAddedToTree(*child);
   SubtreeChildWasAdded();
 }
 
diff --git a/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc b/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc
index 58d59417..98eec5c 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc
@@ -194,8 +194,7 @@
       layout_object, true);
 }
 
-void SVGResourcesCache::ClientWasAddedToTree(LayoutObject& layout_object,
-                                             const ComputedStyle& new_style) {
+void SVGResourcesCache::ClientWasAddedToTree(LayoutObject& layout_object) {
   if (!layout_object.GetNode())
     return;
   LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(
@@ -204,7 +203,8 @@
   if (!LayoutObjectCanHaveResources(layout_object))
     return;
   SVGResourcesCache& cache = ResourcesCache(layout_object.GetDocument());
-  if (cache.AddResourcesFromLayoutObject(layout_object, new_style))
+  if (cache.AddResourcesFromLayoutObject(layout_object,
+                                         layout_object.StyleRef()))
     layout_object.SetNeedsPaintPropertyUpdate();
 }
 
diff --git a/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h b/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h
index e95be4d..fe545ef7 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h
+++ b/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h
@@ -42,8 +42,7 @@
   static SVGResources* CachedResourcesForLayoutObject(const LayoutObject&);
 
   // Called from all SVG layoutObjects addChild() methods.
-  static void ClientWasAddedToTree(LayoutObject&,
-                                   const ComputedStyle& new_style);
+  static void ClientWasAddedToTree(LayoutObject&);
 
   // Called from all SVG layoutObjects removeChild() methods.
   static void ClientWillBeRemovedFromTree(LayoutObject&);
diff --git a/third_party/blink/renderer/core/loader/frame_load_request.cc b/third_party/blink/renderer/core/loader/frame_load_request.cc
index f3191fd..b4b1838 100644
--- a/third_party/blink/renderer/core/loader/frame_load_request.cc
+++ b/third_party/blink/renderer/core/loader/frame_load_request.cc
@@ -45,7 +45,7 @@
                                    const ResourceRequest& resource_request)
     : origin_document_(origin_document),
       should_send_referrer_(kMaybeSendReferrer) {
-  resource_request_.CopyHeadFrom(&resource_request);
+  resource_request_.CopyHeadFrom(resource_request);
   resource_request_.SetHttpBody(resource_request.HttpBody());
   resource_request_.SetMode(network::mojom::RequestMode::kNavigate);
   resource_request_.SetCredentialsMode(
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index abcce7db..4ab46f3 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -1093,8 +1093,9 @@
     last_ancestor = current;
     current->child_needs_compositing_inputs_update_ = true;
     if (Compositor() &&
-        (current != initial_layer &&
-         current->GetLayoutObject().ShouldApplyStrictContainment()))
+        (current != initial_layer ||
+         !current->GetLayoutObject().IsStickyPositioned()) &&
+        current->GetLayoutObject().ShouldApplyStrictContainment())
       break;
   }
 
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index c714e3f..9b48f61 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -27,7 +27,9 @@
 #include <memory>
 #include <utility>
 
+#include "base/metrics/histogram_functions.h"
 #include "base/numerics/clamped_math.h"
+#include "base/numerics/ranges.h"
 #include "build/build_config.h"
 #include "cc/input/overscroll_behavior.h"
 #include "third_party/blink/renderer/core/animation/css/css_animation_data.h"
@@ -1337,6 +1339,23 @@
   SetColorInternal(v);
 }
 
+bool ComputedStyle::SetEffectiveZoom(float f) {
+  // Clamp the effective zoom value to a smaller (but hopeful still large
+  // enough) range, to avoid overflow in derived computations.
+  float clamped_effective_zoom = clampTo<float>(f, 1e-6, 1e6);
+  if (EffectiveZoom() == clamped_effective_zoom)
+    return false;
+  SetInternalEffectiveZoom(clamped_effective_zoom);
+  // Record UMA for the effective zoom in order to assess the relative
+  // importance of sub-pixel behavior, and related features and bugs.
+  // Clamp to a max of 400%, to make the histogram behave better at no
+  // real cost to our understanding of the zooms in use.
+  base::UmaHistogramSparse(
+      "Blink.EffectiveZoom",
+      base::ClampToRange<float>(clamped_effective_zoom * 100, 0, 400));
+  return true;
+}
+
 static FloatRoundedRect::Radii CalcRadiiFor(const LengthSize& top_left,
                                             const LengthSize& top_right,
                                             const LengthSize& bottom_left,
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 8c487e58..01503206 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -909,7 +909,7 @@
     SetZIndexInternal(0);
   }
 
-  bool SetEffectiveZoom(float);
+  CORE_EXPORT bool SetEffectiveZoom(float);
   float EffectiveZoom() const;
 
   // -webkit-clip-path
@@ -2986,16 +2986,6 @@
   FRIEND_TEST_ALL_PREFIXES(ComputedStyleTest, CustomPropertiesEqual_Data);
 };
 
-inline bool ComputedStyle::SetEffectiveZoom(float f) {
-  // Clamp the effective zoom value to a smaller (but hopeful still large
-  // enough) range, to avoid overflow in derived computations.
-  float clamped_effective_zoom = clampTo<float>(f, 1e-6, 1e6);
-  if (EffectiveZoom() == clamped_effective_zoom)
-    return false;
-  SetInternalEffectiveZoom(clamped_effective_zoom);
-  return true;
-}
-
 inline float ComputedStyle::EffectiveZoom() const {
   return InternalEffectiveZoom();
 }
diff --git a/third_party/blink/renderer/core/style/computed_style_test.cc b/third_party/blink/renderer/core/style/computed_style_test.cc
index f304eb9..8978518 100644
--- a/third_party/blink/renderer/core/style/computed_style_test.cc
+++ b/third_party/blink/renderer/core/style/computed_style_test.cc
@@ -529,9 +529,8 @@
       std::make_unique<DummyPageHolder>(IntSize(0, 0), nullptr);
   const ComputedStyle* initial = &ComputedStyle::InitialStyle();
 
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(dummy_page_holder_->GetDocument(),
-                                              PreferredColorScheme::kDark);
+  ColorSchemeHelper color_scheme_helper(dummy_page_holder_->GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
   StyleResolverState state(dummy_page_holder_->GetDocument(),
                            *dummy_page_holder_->GetDocument().documentElement(),
                            initial, initial);
@@ -570,9 +569,8 @@
       CSSPropertyID::kColor, "-internal-light-dark-color(black, white)",
       ua_context);
 
-  ColorSchemeHelper color_scheme_helper;
-  color_scheme_helper.SetPreferredColorScheme(dummy_page_holder_->GetDocument(),
-                                              PreferredColorScheme::kDark);
+  ColorSchemeHelper color_scheme_helper(dummy_page_holder_->GetDocument());
+  color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
   StyleResolverState state(dummy_page_holder_->GetDocument(),
                            *dummy_page_holder_->GetDocument().documentElement(),
                            initial, initial);
diff --git a/third_party/blink/renderer/core/svg/svg_script_element.cc b/third_party/blink/renderer/core/svg/svg_script_element.cc
index 4aaa601..79ea6d9 100644
--- a/third_party/blink/renderer/core/svg/svg_script_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_script_element.cc
@@ -181,11 +181,11 @@
 
 const AttrNameToTrustedType& SVGScriptElement::GetCheckedAttributeTypes()
     const {
-  DEFINE_STATIC_LOCAL(AttrNameToTrustedType, attribute_map,
-                      ({
-                          {svg_names::kHrefAttr.LocalName(),
-                           SpecificTrustedType::kTrustedScriptURL},
-                      }));
+  DEFINE_STATIC_LOCAL(
+      AttrNameToTrustedType, attribute_map,
+      ({
+          {svg_names::kHrefAttr.LocalName(), SpecificTrustedType::kScriptURL},
+      }));
   return attribute_map;
 }
 
diff --git a/third_party/blink/renderer/core/testing/color_scheme_helper.cc b/third_party/blink/renderer/core/testing/color_scheme_helper.cc
index f673819..2101c0c 100644
--- a/third_party/blink/renderer/core/testing/color_scheme_helper.cc
+++ b/third_party/blink/renderer/core/testing/color_scheme_helper.cc
@@ -7,35 +7,36 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_theme_engine.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/page/page.h"
 
 namespace blink {
 
-ColorSchemeHelper::ColorSchemeHelper() {
+ColorSchemeHelper::ColorSchemeHelper(Document& document)
+    : settings_(*document.GetSettings()) {
   DCHECK(Platform::Current() && Platform::Current()->ThemeEngine());
   web_theme_engine_ = Platform::Current()->ThemeEngine();
-  default_preferred_color_scheme_ = web_theme_engine_->PreferredColorScheme();
+  default_preferred_color_scheme_ = settings_.GetPreferredColorScheme();
+  default_forced_colors_ = web_theme_engine_->GetForcedColors();
+}
+
+ColorSchemeHelper::ColorSchemeHelper(Page& page)
+    : settings_(page.GetSettings()) {
+  DCHECK(Platform::Current() && Platform::Current()->ThemeEngine());
+  web_theme_engine_ = Platform::Current()->ThemeEngine();
+  default_preferred_color_scheme_ = settings_.GetPreferredColorScheme();
   default_forced_colors_ = web_theme_engine_->GetForcedColors();
 }
 
 ColorSchemeHelper::~ColorSchemeHelper() {
   // Reset preferred color scheme and forced colors to their original values.
-  web_theme_engine_->SetPreferredColorScheme(default_preferred_color_scheme_);
+  settings_.SetPreferredColorScheme(default_preferred_color_scheme_);
   web_theme_engine_->SetForcedColors(default_forced_colors_);
 }
 
 void ColorSchemeHelper::SetPreferredColorScheme(
-    Document& document,
     const PreferredColorScheme preferred_color_scheme) {
-  web_theme_engine_->SetPreferredColorScheme(preferred_color_scheme);
-  document.ColorSchemeChanged();
-}
-
-void ColorSchemeHelper::SetPreferredColorScheme(
-    Page& page,
-    const PreferredColorScheme preferred_color_scheme) {
-  web_theme_engine_->SetPreferredColorScheme(preferred_color_scheme);
-  page.ColorSchemeChanged();
+  settings_.SetPreferredColorScheme(preferred_color_scheme);
 }
 
 void ColorSchemeHelper::SetForcedColors(Document& document,
diff --git a/third_party/blink/renderer/core/testing/color_scheme_helper.h b/third_party/blink/renderer/core/testing/color_scheme_helper.h
index 3059873..25df460e 100644
--- a/third_party/blink/renderer/core/testing/color_scheme_helper.h
+++ b/third_party/blink/renderer/core/testing/color_scheme_helper.h
@@ -12,6 +12,7 @@
 
 class Document;
 class Page;
+class Settings;
 class WebThemeEngine;
 
 // ColorSchemeHelper is used to update the values of PreferredColorScheme and
@@ -19,20 +20,18 @@
 // and ForcedColors back to their default values upon deconstruction.
 class ColorSchemeHelper {
  public:
-  ColorSchemeHelper();
+  ColorSchemeHelper(Document& document);
+  ColorSchemeHelper(Page& page);
   ~ColorSchemeHelper();
 
   void SetPreferredColorScheme(
-      Document& document,
-      const PreferredColorScheme preferred_color_scheme);
-  void SetPreferredColorScheme(
-      Page& page,
       const PreferredColorScheme preferred_color_scheme);
   void SetForcedColors(Document& document, const ForcedColors forced_colors);
   void SetForcedColors(Page& page, const ForcedColors forced_colors);
 
  private:
   WebThemeEngine* web_theme_engine_ = nullptr;
+  Settings& settings_;
   PreferredColorScheme default_preferred_color_scheme_ =
       PreferredColorScheme::kNoPreference;
   ForcedColors default_forced_colors_ = ForcedColors::kNone;
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
index 1531aec59..b459828e 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
@@ -140,20 +140,18 @@
   bool is_not_property : 1;
   bool is_not_attribute : 1;
 } kTypeTable[] = {
-    {"embed", "src", nullptr, SpecificTrustedType::kTrustedScriptURL},
-    {"iframe", "srcdoc", nullptr, SpecificTrustedType::kTrustedHTML},
-    {"object", "codeBase", nullptr, SpecificTrustedType::kTrustedScriptURL},
-    {"object", "data", nullptr, SpecificTrustedType::kTrustedScriptURL},
-    {"script", "innerText", nullptr, SpecificTrustedType::kTrustedScript, false,
+    {"embed", "src", nullptr, SpecificTrustedType::kScriptURL},
+    {"iframe", "srcdoc", nullptr, SpecificTrustedType::kHTML},
+    {"object", "codeBase", nullptr, SpecificTrustedType::kScriptURL},
+    {"object", "data", nullptr, SpecificTrustedType::kScriptURL},
+    {"script", "innerText", nullptr, SpecificTrustedType::kScript, false, true},
+    {"script", "src", nullptr, SpecificTrustedType::kScriptURL},
+    {"script", "text", nullptr, SpecificTrustedType::kScript, false, true},
+    {"script", "textContent", nullptr, SpecificTrustedType::kScript, false,
      true},
-    {"script", "src", nullptr, SpecificTrustedType::kTrustedScriptURL},
-    {"script", "text", nullptr, SpecificTrustedType::kTrustedScript, false,
-     true},
-    {"script", "textContent", nullptr, SpecificTrustedType::kTrustedScript,
-     false, true},
-    {"*", "innerHTML", nullptr, SpecificTrustedType::kTrustedHTML, false, true},
-    {"*", "outerHTML", nullptr, SpecificTrustedType::kTrustedHTML, false, true},
-    {"*", "on*", nullptr, SpecificTrustedType::kTrustedScript, true, false},
+    {"*", "innerHTML", nullptr, SpecificTrustedType::kHTML, false, true},
+    {"*", "outerHTML", nullptr, SpecificTrustedType::kHTML, false, true},
+    {"*", "on*", nullptr, SpecificTrustedType::kScript, true, false},
 };
 
 // Does a type table entry match a property?
@@ -185,11 +183,11 @@
 
 String getTrustedTypeName(SpecificTrustedType type) {
   switch (type) {
-    case SpecificTrustedType::kTrustedHTML:
+    case SpecificTrustedType::kHTML:
       return "TrustedHTML";
-    case SpecificTrustedType::kTrustedScript:
+    case SpecificTrustedType::kScript:
       return "TrustedScript";
-    case SpecificTrustedType::kTrustedScriptURL:
+    case SpecificTrustedType::kScriptURL:
       return "TrustedScriptURL";
     case SpecificTrustedType::kNone:
       return String();
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
index 7393364..43c6a563 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
@@ -27,7 +27,6 @@
 namespace {
 
 enum TrustedTypeViolationKind {
-  kAnyTrustedTypeAssignment,
   kTrustedHTMLAssignment,
   kTrustedScriptAssignment,
   kTrustedScriptURLAssignment,
@@ -45,8 +44,6 @@
 
 const char* GetMessage(TrustedTypeViolationKind kind) {
   switch (kind) {
-    case kAnyTrustedTypeAssignment:
-      return "This document requires any trusted type assignment.";
     case kTrustedHTMLAssignment:
       return "This document requires 'TrustedHTML' assignment.";
     case kTrustedScriptAssignment:
@@ -149,7 +146,7 @@
              : nullptr;
 }
 
-// Functionally identical to GetStringFromTrustedScript(const String&, ..), but
+// Functionally identical to TrustedTypesCheckForScript(const String&, ..), but
 // to be called outside of regular script execution. This is required for both
 // GetStringForScriptExecution & TrustedTypesCheckForJavascriptURLinNavigation,
 // and has a number of additional parameters to enable proper error reporting
@@ -230,143 +227,33 @@
          !ContentSecurityPolicy::ShouldBypassMainWorld(execution_context);
 }
 
-String GetStringFromTrustedType(
-    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL&
-        string_or_trusted_type,
-    const ExecutionContext* execution_context,
-    ExceptionState& exception_state) {
-  DCHECK(!string_or_trusted_type.IsNull());
-
-  if (string_or_trusted_type.IsString() &&
-      RequireTrustedTypesCheck(execution_context)) {
-    TrustedTypeFail(
-        kAnyTrustedTypeAssignment, execution_context, exception_state,
-        GetStringFromTrustedTypeWithoutCheck(string_or_trusted_type));
-    return g_empty_string;
-  }
-
-  if (string_or_trusted_type.IsTrustedHTML())
-    return string_or_trusted_type.GetAsTrustedHTML()->toString();
-  if (string_or_trusted_type.IsTrustedScript())
-    return string_or_trusted_type.GetAsTrustedScript()->toString();
-  if (string_or_trusted_type.IsTrustedScriptURL())
-    return string_or_trusted_type.GetAsTrustedScriptURL()->toString();
-
-  return string_or_trusted_type.GetAsString();
-}
-
-String GetStringFromTrustedTypeWithoutCheck(
-    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL&
-        string_or_trusted_type) {
-  if (string_or_trusted_type.IsTrustedHTML())
-    return string_or_trusted_type.GetAsTrustedHTML()->toString();
-  if (string_or_trusted_type.IsTrustedScript())
-    return string_or_trusted_type.GetAsTrustedScript()->toString();
-  if (string_or_trusted_type.IsTrustedScriptURL())
-    return string_or_trusted_type.GetAsTrustedScriptURL()->toString();
-  if (string_or_trusted_type.IsString())
-    return string_or_trusted_type.GetAsString();
-
-  return g_empty_string;
-}
-
-String GetStringFromSpecificTrustedType(
-    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL&
-        string_or_trusted_type,
-    SpecificTrustedType specific_trusted_type,
-    const ExecutionContext* execution_context,
-    ExceptionState& exception_state) {
-  switch (specific_trusted_type) {
-    case SpecificTrustedType::kNone:
-      return GetStringFromTrustedTypeWithoutCheck(string_or_trusted_type);
-    case SpecificTrustedType::kTrustedHTML: {
-      StringOrTrustedHTML string_or_trusted_html =
-          string_or_trusted_type.IsTrustedHTML()
-              ? StringOrTrustedHTML::FromTrustedHTML(
-                    string_or_trusted_type.GetAsTrustedHTML())
-              : StringOrTrustedHTML::FromString(
-                    GetStringFromTrustedTypeWithoutCheck(
-                        string_or_trusted_type));
-      return GetStringFromTrustedHTML(string_or_trusted_html, execution_context,
-                                      exception_state);
-    }
-    case SpecificTrustedType::kTrustedScript: {
-      StringOrTrustedScript string_or_trusted_script =
-          string_or_trusted_type.IsTrustedScript()
-              ? StringOrTrustedScript::FromTrustedScript(
-                    string_or_trusted_type.GetAsTrustedScript())
-              : StringOrTrustedScript::FromString(
-                    GetStringFromTrustedTypeWithoutCheck(
-                        string_or_trusted_type));
-      return GetStringFromTrustedScript(string_or_trusted_script,
-                                        execution_context, exception_state);
-    }
-    case SpecificTrustedType::kTrustedScriptURL: {
-      StringOrTrustedScriptURL string_or_trusted_script_url =
-          string_or_trusted_type.IsTrustedScriptURL()
-              ? StringOrTrustedScriptURL::FromTrustedScriptURL(
-                    string_or_trusted_type.GetAsTrustedScriptURL())
-              : StringOrTrustedScriptURL::FromString(
-                    GetStringFromTrustedTypeWithoutCheck(
-                        string_or_trusted_type));
-      return GetStringFromTrustedScriptURL(string_or_trusted_script_url,
-                                           execution_context, exception_state);
-    }
-  }
-}
-
-String GetStringFromSpecificTrustedType(
-    const String& string,
-    SpecificTrustedType specific_trusted_type,
-    const ExecutionContext* execution_context,
-    ExceptionState& exception_state) {
-  if (specific_trusted_type == SpecificTrustedType::kNone)
-    return string;
-  return GetStringFromSpecificTrustedType(
-      StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL::FromString(string),
-      specific_trusted_type, execution_context, exception_state);
-}
-
-String GetStringFromTrustedHTML(StringOrTrustedHTML string_or_trusted_html,
-                                const ExecutionContext* execution_context,
-                                ExceptionState& exception_state) {
-  DCHECK(!string_or_trusted_html.IsNull());
-
-  if (string_or_trusted_html.IsTrustedHTML()) {
-    return string_or_trusted_html.GetAsTrustedHTML()->toString();
-  }
-
-  return GetStringFromTrustedHTML(string_or_trusted_html.GetAsString(),
-                                  execution_context, exception_state);
-}
-
-String GetStringFromTrustedHTML(const String& string,
+String TrustedTypesCheckForHTML(const String& html,
                                 const ExecutionContext* execution_context,
                                 ExceptionState& exception_state) {
   bool require_trusted_type = RequireTrustedTypesCheck(execution_context);
   if (!require_trusted_type) {
-    return string;
+    return html;
   }
 
   TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
   if (!default_policy) {
     if (TrustedTypeFail(kTrustedHTMLAssignment, execution_context,
-                        exception_state, string)) {
+                        exception_state, html)) {
       return g_empty_string;
     }
-    return string;
+    return html;
   }
 
   if (!default_policy->HasCreateHTML()) {
     if (TrustedTypeFail(kTrustedHTMLAssignmentAndNoDefaultPolicyExisted,
-                        execution_context, exception_state, string)) {
+                        execution_context, exception_state, html)) {
       return g_empty_string;
     } else {
-      return string;
+      return html;
     }
   }
   TrustedHTML* result =
-      default_policy->CreateHTML(execution_context->GetIsolate(), string,
+      default_policy->CreateHTML(execution_context->GetIsolate(), html,
                                  HeapVector<ScriptValue>(), exception_state);
   if (exception_state.HadException()) {
     return g_empty_string;
@@ -374,82 +261,60 @@
 
   if (result->toString().IsNull()) {
     if (TrustedTypeFail(kTrustedHTMLAssignmentAndDefaultPolicyFailed,
-                        execution_context, exception_state, string)) {
+                        execution_context, exception_state, html)) {
       return g_empty_string;
     } else {
-      return string;
+      return html;
     }
   }
 
   return result->toString();
 }
 
-String GetStringFromTrustedHTML(StringOrTrustedHTML string_or_trusted_html,
+String TrustedTypesCheckForHTML(StringOrTrustedHTML string_or_trusted_html,
                                 const Document* document,
                                 ExceptionState& exception_state) {
-  return GetStringFromTrustedHTML(
+  return TrustedTypesCheckForHTML(
       string_or_trusted_html,
       document ? document->ToExecutionContext() : nullptr, exception_state);
 }
 
-String GetStringFromTrustedHTML(const String& string,
+String TrustedTypesCheckForHTML(const String& html,
                                 const Document* document,
                                 ExceptionState& exception_state) {
-  return GetStringFromTrustedHTML(
-      string, document ? document->ToExecutionContext() : nullptr,
+  return TrustedTypesCheckForHTML(
+      html, document ? document->ToExecutionContext() : nullptr,
       exception_state);
 }
 
-String GetStringFromTrustedScript(
-    StringOrTrustedScript string_or_trusted_script,
-    const ExecutionContext* execution_context,
-    ExceptionState& exception_state) {
-  // To remain compatible with legacy behaviour, HTMLElement uses extended IDL
-  // attributes to allow for nullable union of (DOMString or TrustedScript).
-  // Thus, this method is required to handle the case where
-  // string_or_trusted_script.IsNull(), unlike the various similar methods in
-  // this file.
-
-  if (string_or_trusted_script.IsTrustedScript()) {
-    return string_or_trusted_script.GetAsTrustedScript()->toString();
-  }
-
-  if (string_or_trusted_script.IsNull()) {
-    string_or_trusted_script =
-        StringOrTrustedScript::FromString(g_empty_string);
-  }
-  return GetStringFromTrustedScript(string_or_trusted_script.GetAsString(),
-                                    execution_context, exception_state);
-}
-
-String GetStringFromTrustedScript(const String& potential_script,
+String TrustedTypesCheckForScript(const String& script,
                                   const ExecutionContext* execution_context,
                                   ExceptionState& exception_state) {
   bool require_trusted_type = RequireTrustedTypesCheck(execution_context);
   if (!require_trusted_type) {
-    return potential_script;
+    return script;
   }
 
   TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
   if (!default_policy) {
     if (TrustedTypeFail(kTrustedScriptAssignment, execution_context,
-                        exception_state, potential_script)) {
+                        exception_state, script)) {
       return g_empty_string;
     }
-    return potential_script;
+    return script;
   }
 
   if (!default_policy->HasCreateScript()) {
     if (TrustedTypeFail(kTrustedScriptAssignmentAndNoDefaultPolicyExisted,
-                        execution_context, exception_state, potential_script)) {
+                        execution_context, exception_state, script)) {
       return g_empty_string;
     } else {
-      return potential_script;
+      return script;
     }
   }
-  TrustedScript* result = default_policy->CreateScript(
-      execution_context->GetIsolate(), potential_script,
-      HeapVector<ScriptValue>(), exception_state);
+  TrustedScript* result =
+      default_policy->CreateScript(execution_context->GetIsolate(), script,
+                                   HeapVector<ScriptValue>(), exception_state);
   DCHECK_EQ(!result, exception_state.HadException());
   if (exception_state.HadException()) {
     return g_empty_string;
@@ -457,59 +322,45 @@
 
   if (result->toString().IsNull()) {
     if (TrustedTypeFail(kTrustedScriptAssignmentAndDefaultPolicyFailed,
-                        execution_context, exception_state, potential_script)) {
+                        execution_context, exception_state, script)) {
       return g_empty_string;
     } else {
-      return potential_script;
+      return script;
     }
   }
 
   return result->toString();
 }
 
-String GetStringFromTrustedScriptURL(
-    StringOrTrustedScriptURL string_or_trusted_script_url,
-    const ExecutionContext* execution_context,
-    ExceptionState& exception_state) {
-  DCHECK(!string_or_trusted_script_url.IsNull());
-  if (string_or_trusted_script_url.IsTrustedScriptURL()) {
-    return string_or_trusted_script_url.GetAsTrustedScriptURL()->toString();
-  }
-  DCHECK(string_or_trusted_script_url.IsString());
-  return GetStringFromTrustedScriptURL(
-      string_or_trusted_script_url.GetAsString(), execution_context,
-      exception_state);
-}
-
-String GetStringFromTrustedScriptURL(const String& string,
+String TrustedTypesCheckForScriptURL(const String& script_url,
                                      const ExecutionContext* execution_context,
                                      ExceptionState& exception_state) {
   bool require_trusted_type =
       RequireTrustedTypesCheck(execution_context) &&
       RuntimeEnabledFeatures::TrustedDOMTypesEnabled(execution_context);
   if (!require_trusted_type) {
-    return string;
+    return script_url;
   }
 
   TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
   if (!default_policy) {
     if (TrustedTypeFail(kTrustedScriptURLAssignment, execution_context,
-                        exception_state, string)) {
+                        exception_state, script_url)) {
       return g_empty_string;
     }
-    return string;
+    return script_url;
   }
 
   if (!default_policy->HasCreateScriptURL()) {
     if (TrustedTypeFail(kTrustedScriptURLAssignmentAndNoDefaultPolicyExisted,
-                        execution_context, exception_state, string)) {
+                        execution_context, exception_state, script_url)) {
       return g_empty_string;
     } else {
-      return string;
+      return script_url;
     }
   }
   TrustedScriptURL* result = default_policy->CreateScriptURL(
-      execution_context->GetIsolate(), string, HeapVector<ScriptValue>(),
+      execution_context->GetIsolate(), script_url, HeapVector<ScriptValue>(),
       exception_state);
 
   if (exception_state.HadException()) {
@@ -518,16 +369,106 @@
 
   if (result->toString().IsNull()) {
     if (TrustedTypeFail(kTrustedScriptURLAssignmentAndDefaultPolicyFailed,
-                        execution_context, exception_state, string)) {
+                        execution_context, exception_state, script_url)) {
       return g_empty_string;
     } else {
-      return string;
+      return script_url;
     }
   }
 
   return result->toString();
 }
 
+String TrustedTypesCheckFor(
+    SpecificTrustedType type,
+    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL& trusted,
+    const ExecutionContext* execution_context,
+    ExceptionState& exception_state) {
+  // Whatever happens below, we will need the string value:
+  String value;
+  if (trusted.IsTrustedHTML()) {
+    value = trusted.GetAsTrustedHTML()->toString();
+  } else if (trusted.IsTrustedScript()) {
+    value = trusted.GetAsTrustedScript()->toString();
+  } else if (trusted.IsTrustedScriptURL()) {
+    value = trusted.GetAsTrustedScriptURL()->toString();
+  } else if (trusted.IsString()) {
+    value = trusted.GetAsString();
+  }  // else: trusted.IsNull(). But we don't have anything to do in that case.
+
+  // The check passes if we have the proper trusted type:
+  if (type == SpecificTrustedType::kNone ||
+      (trusted.IsTrustedHTML() && type == SpecificTrustedType::kHTML) ||
+      (trusted.IsTrustedScript() && type == SpecificTrustedType::kScript) ||
+      (trusted.IsTrustedScriptURL() &&
+       type == SpecificTrustedType::kScriptURL)) {
+    return value;
+  }
+
+  // In all other cases: run the full check against the string value.
+  return TrustedTypesCheckFor(type, value, execution_context, exception_state);
+}
+
+String TrustedTypesCheckForHTML(StringOrTrustedHTML trusted,
+                                const ExecutionContext* execution_context,
+                                ExceptionState& exception_state) {
+  DCHECK(!trusted.IsNull());
+  if (trusted.IsTrustedHTML()) {
+    return trusted.GetAsTrustedHTML()->toString();
+  }
+  return TrustedTypesCheckForHTML(trusted.GetAsString(), execution_context,
+                                  exception_state);
+}
+
+String TrustedTypesCheckForScript(StringOrTrustedScript trusted,
+                                  const ExecutionContext* execution_context,
+                                  ExceptionState& exception_state) {
+  // To remain compatible with legacy behaviour, HTMLElement uses extended IDL
+  // attributes to allow for nullable union of (DOMString or TrustedScript).
+  // Thus, this method is required to handle the case where
+  // string_or_trusted_script.IsNull(), unlike the various similar methods in
+  // this file.
+  if (trusted.IsTrustedScript()) {
+    return trusted.GetAsTrustedScript()->toString();
+  }
+  if (trusted.IsNull()) {
+    trusted = StringOrTrustedScript::FromString(g_empty_string);
+  }
+  return TrustedTypesCheckForScript(trusted.GetAsString(), execution_context,
+                                    exception_state);
+}
+String TrustedTypesCheckForScriptURL(StringOrTrustedScriptURL trusted,
+                                     const ExecutionContext* execution_context,
+                                     ExceptionState& exception_state) {
+  DCHECK(!trusted.IsNull());
+  if (trusted.IsTrustedScriptURL()) {
+    return trusted.GetAsTrustedScriptURL()->toString();
+  }
+  return TrustedTypesCheckForScriptURL(trusted.GetAsString(), execution_context,
+                                       exception_state);
+}
+
+String TrustedTypesCheckFor(SpecificTrustedType type,
+                            const String& trusted,
+                            const ExecutionContext* execution_context,
+                            ExceptionState& exception_state) {
+  switch (type) {
+    case SpecificTrustedType::kHTML:
+      return TrustedTypesCheckForHTML(trusted, execution_context,
+                                      exception_state);
+    case SpecificTrustedType::kScript:
+      return TrustedTypesCheckForScript(trusted, execution_context,
+                                        exception_state);
+    case SpecificTrustedType::kScriptURL:
+      return TrustedTypesCheckForScriptURL(trusted, execution_context,
+                                           exception_state);
+    case SpecificTrustedType::kNone:
+      return trusted;
+  }
+  NOTREACHED();
+  return "";
+}
+
 String CORE_EXPORT GetStringForScriptExecution(const String& script,
                                                Document* doc) {
   return GetStringFromScriptHelper(script, doc, "script", "text",
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.h b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.h
index 1533b58d..152a10b 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.h
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.h
@@ -20,68 +20,62 @@
 
 enum class SpecificTrustedType {
   kNone,
-  kTrustedHTML,
-  kTrustedScript,
-  kTrustedScriptURL,
+  kHTML,
+  kScript,
+  kScriptURL,
 };
 
-String CORE_EXPORT GetStringFromTrustedType(
-    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL&,
-    const ExecutionContext*,
-    ExceptionState&);
-
-String CORE_EXPORT GetStringFromTrustedTypeWithoutCheck(
-    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL&);
-
-String CORE_EXPORT GetStringFromSpecificTrustedType(
-    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL&,
-    SpecificTrustedType,
-    const ExecutionContext*,
-    ExceptionState&);
-
-String CORE_EXPORT GetStringFromSpecificTrustedType(const String&,
-                                                    SpecificTrustedType,
-                                                    const ExecutionContext*,
-                                                    ExceptionState&);
-
-String CORE_EXPORT GetStringFromTrustedHTML(StringOrTrustedHTML,
-                                            const ExecutionContext*,
-                                            ExceptionState&);
-
-String GetStringFromTrustedHTML(const String&,
-                                const ExecutionContext*,
-                                ExceptionState&);
-
 // TODO(crbug.com/1029822): Temporary helpers to ease migrating ExecutionContext
 // to LocalDOMWindow.
-String CORE_EXPORT GetStringFromTrustedHTML(StringOrTrustedHTML,
+CORE_EXPORT String TrustedTypesCheckForHTML(StringOrTrustedHTML,
                                             const Document*,
                                             ExceptionState&);
-String GetStringFromTrustedHTML(const String&,
+String TrustedTypesCheckForHTML(const String&,
                                 const Document*,
                                 ExceptionState&);
 
-String CORE_EXPORT GetStringFromTrustedScript(StringOrTrustedScript,
+// Perform Trusted Type checks, with the IDL union types as input. All of these
+// will call String& versions below to do the heavy lifting.
+CORE_EXPORT String TrustedTypesCheckFor(
+    SpecificTrustedType,
+    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL&,
+    const ExecutionContext*,
+    ExceptionState&) WARN_UNUSED_RESULT;
+String TrustedTypesCheckForHTML(StringOrTrustedHTML,
+                                const ExecutionContext*,
+                                ExceptionState&) WARN_UNUSED_RESULT;
+CORE_EXPORT String TrustedTypesCheckForScript(StringOrTrustedScript,
                                               const ExecutionContext*,
-                                              ExceptionState&);
-
-String CORE_EXPORT GetStringFromTrustedScript(const String&,
-                                              const ExecutionContext*,
-                                              ExceptionState&);
-
-String CORE_EXPORT GetStringFromTrustedScriptURL(StringOrTrustedScriptURL,
+                                              ExceptionState&)
+    WARN_UNUSED_RESULT;
+CORE_EXPORT String TrustedTypesCheckForScriptURL(StringOrTrustedScriptURL,
                                                  const ExecutionContext*,
-                                                 ExceptionState&);
+                                                 ExceptionState&)
+    WARN_UNUSED_RESULT;
 
-String CORE_EXPORT GetStringFromTrustedScriptURL(const String&,
-                                                 const ExecutionContext*,
-                                                 ExceptionState&);
+// Perform Trusted Type checks, for a dynamically or statically determined
+// type.
+// Returns the effective value (which may have been modified by the "default"
+// policy. We use WARN_UNUSED_RESULT to prevent erroneous usage.
+String TrustedTypesCheckFor(SpecificTrustedType,
+                            const String&,
+                            const ExecutionContext*,
+                            ExceptionState&) WARN_UNUSED_RESULT;
+String TrustedTypesCheckForHTML(const String&,
+                                const ExecutionContext*,
+                                ExceptionState&) WARN_UNUSED_RESULT;
+String TrustedTypesCheckForScript(const String&,
+                                  const ExecutionContext*,
+                                  ExceptionState&) WARN_UNUSED_RESULT;
+String TrustedTypesCheckForScriptURL(const String&,
+                                     const ExecutionContext*,
+                                     ExceptionState&) WARN_UNUSED_RESULT;
 
-// Functionally equivalent to GetStringFromTrustedScript(const String&, ...),
+// Functionally equivalent to TrustedTypesCheckForScript(const String&, ...),
 // but with setup & error handling suitable for the asynchronous execution
 // cases.
 String TrustedTypesCheckForJavascriptURLinNavigation(const String&, Document*);
-String CORE_EXPORT GetStringForScriptExecution(const String&, Document*);
+CORE_EXPORT String GetStringForScriptExecution(const String&, Document*);
 
 // Determine whether a Trusted Types check is needed in this execution context.
 //
@@ -90,7 +84,7 @@
 // immediately imply "okay" this method can be used.
 // Example: To determine whether 'eval' may pass, one needs to also take CSP
 // into account.
-bool CORE_EXPORT RequireTrustedTypesCheck(const ExecutionContext*);
+CORE_EXPORT bool RequireTrustedTypesCheck(const ExecutionContext*);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc b/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc
index c2f4e3bca..36f4c9a 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc
@@ -22,33 +22,7 @@
 
 namespace blink {
 
-// Functions for checking throwing cases.
-void GetStringFromTrustedTypeThrows(
-    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL&
-        string_or_trusted_type) {
-  auto* document = MakeGarbageCollected<Document>();
-  document->GetContentSecurityPolicy()->DidReceiveHeader(
-      "trusted-types *", network::mojom::ContentSecurityPolicyType::kEnforce,
-      network::mojom::ContentSecurityPolicySource::kMeta);
-  DummyExceptionStateForTesting exception_state;
-  ASSERT_FALSE(exception_state.HadException());
-  String s = GetStringFromTrustedType(
-      string_or_trusted_type, document->ToExecutionContext(), exception_state);
-  EXPECT_FALSE(exception_state.HadException());
-
-  document->GetContentSecurityPolicy()->DidReceiveHeader(
-      "require-trusted-types-for 'script'",
-      network::mojom::ContentSecurityPolicyType::kEnforce,
-      network::mojom::ContentSecurityPolicySource::kMeta);
-  ASSERT_FALSE(exception_state.HadException());
-  String s1 = GetStringFromTrustedType(
-      string_or_trusted_type, document->ToExecutionContext(), exception_state);
-  EXPECT_TRUE(exception_state.HadException());
-  EXPECT_EQ(ESErrorType::kTypeError, exception_state.CodeAs<ESErrorType>());
-  exception_state.ClearException();
-}
-
-void GetStringFromTrustedHTMLThrows(
+void TrustedTypesCheckForHTMLThrows(
     const StringOrTrustedHTML& string_or_trusted_html) {
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -58,7 +32,7 @@
   V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   ASSERT_FALSE(exception_state.HadException());
-  String s = GetStringFromTrustedHTML(string_or_trusted_html, &document,
+  String s = TrustedTypesCheckForHTML(string_or_trusted_html, &document,
                                       exception_state);
   EXPECT_FALSE(exception_state.HadException());
 
@@ -67,14 +41,14 @@
       network::mojom::ContentSecurityPolicyType::kEnforce,
       network::mojom::ContentSecurityPolicySource::kMeta);
   ASSERT_FALSE(exception_state.HadException());
-  String s1 = GetStringFromTrustedHTML(string_or_trusted_html, &document,
+  String s1 = TrustedTypesCheckForHTML(string_or_trusted_html, &document,
                                        exception_state);
   EXPECT_TRUE(exception_state.HadException());
   EXPECT_EQ(ESErrorType::kTypeError, exception_state.CodeAs<ESErrorType>());
   exception_state.ClearException();
 }
 
-void GetStringFromTrustedScriptThrows(
+void TrustedTypesCheckForScriptThrows(
     const StringOrTrustedScript& string_or_trusted_script) {
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -84,7 +58,7 @@
   V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   ASSERT_FALSE(exception_state.HadException());
-  String s = GetStringFromTrustedScript(
+  String s = TrustedTypesCheckForScript(
       string_or_trusted_script, document.ToExecutionContext(), exception_state);
   EXPECT_FALSE(exception_state.HadException());
 
@@ -93,14 +67,14 @@
       network::mojom::ContentSecurityPolicyType::kEnforce,
       network::mojom::ContentSecurityPolicySource::kMeta);
   ASSERT_FALSE(exception_state.HadException());
-  String s1 = GetStringFromTrustedScript(
+  String s1 = TrustedTypesCheckForScript(
       string_or_trusted_script, document.ToExecutionContext(), exception_state);
   EXPECT_TRUE(exception_state.HadException());
   EXPECT_EQ(ESErrorType::kTypeError, exception_state.CodeAs<ESErrorType>());
   exception_state.ClearException();
 }
 
-void GetStringFromTrustedScriptURLThrows(
+void TrustedTypesCheckForScriptURLThrows(
     const StringOrTrustedScriptURL& string_or_trusted_script_url) {
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -110,7 +84,7 @@
   V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   ASSERT_FALSE(exception_state.HadException());
-  String s = GetStringFromTrustedScriptURL(string_or_trusted_script_url,
+  String s = TrustedTypesCheckForScriptURL(string_or_trusted_script_url,
                                            document.ToExecutionContext(),
                                            exception_state);
   EXPECT_FALSE(exception_state.HadException());
@@ -120,7 +94,7 @@
       network::mojom::ContentSecurityPolicyType::kEnforce,
       network::mojom::ContentSecurityPolicySource::kMeta);
   ASSERT_FALSE(exception_state.HadException());
-  String s1 = GetStringFromTrustedScriptURL(string_or_trusted_script_url,
+  String s1 = TrustedTypesCheckForScriptURL(string_or_trusted_script_url,
                                             document.ToExecutionContext(),
                                             exception_state);
   EXPECT_TRUE(exception_state.HadException());
@@ -128,22 +102,7 @@
   exception_state.ClearException();
 }
 
-// Functions for checking non-throwing cases.
-void GetStringFromTrustedTypeWorks(
-    const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL&
-        string_or_trusted_type,
-    String expected) {
-  auto* document = MakeGarbageCollected<Document>();
-  document->GetContentSecurityPolicy()->DidReceiveHeader(
-      "trusted-types *", network::mojom::ContentSecurityPolicyType::kEnforce,
-      network::mojom::ContentSecurityPolicySource::kMeta);
-  DummyExceptionStateForTesting exception_state;
-  String s = GetStringFromTrustedType(
-      string_or_trusted_type, document->ToExecutionContext(), exception_state);
-  ASSERT_EQ(s, expected);
-}
-
-void GetStringFromTrustedHTMLWorks(
+void TrustedTypesCheckForHTMLWorks(
     const StringOrTrustedHTML& string_or_trusted_html,
     String expected) {
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
@@ -153,12 +112,12 @@
       network::mojom::ContentSecurityPolicySource::kMeta);
   V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
-  String s = GetStringFromTrustedHTML(string_or_trusted_html, &document,
+  String s = TrustedTypesCheckForHTML(string_or_trusted_html, &document,
                                       exception_state);
   ASSERT_EQ(s, expected);
 }
 
-void GetStringFromTrustedScriptWorks(
+void TrustedTypesCheckForScriptWorks(
     const StringOrTrustedScript& string_or_trusted_script,
     String expected) {
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
@@ -168,12 +127,12 @@
       network::mojom::ContentSecurityPolicySource::kMeta);
   V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
-  String s = GetStringFromTrustedScript(
+  String s = TrustedTypesCheckForScript(
       string_or_trusted_script, document.ToExecutionContext(), exception_state);
   ASSERT_EQ(s, expected);
 }
 
-void GetStringFromTrustedScriptURLWorks(
+void TrustedTypesCheckForScriptURLWorks(
     const StringOrTrustedScriptURL& string_or_trusted_script_url,
     String expected) {
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
@@ -183,139 +142,52 @@
       network::mojom::ContentSecurityPolicySource::kMeta);
   V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
-  String s = GetStringFromTrustedScriptURL(string_or_trusted_script_url,
+  String s = TrustedTypesCheckForScriptURL(string_or_trusted_script_url,
                                            document.ToExecutionContext(),
                                            exception_state);
   ASSERT_EQ(s, expected);
 }
 
-// GetStringFromTrustedType() tests
-TEST(TrustedTypesUtilTest, GetStringFromTrustedType_TrustedHTML) {
-  auto* html = MakeGarbageCollected<TrustedHTML>("A string");
-  StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL trusted_value =
-      StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL::FromTrustedHTML(
-          html);
-  GetStringFromTrustedTypeWorks(trusted_value, "A string");
-}
-
-TEST(TrustedTypesUtilTest, GetStringFromTrustedType_TrustedScript) {
-  auto* script = MakeGarbageCollected<TrustedScript>("A string");
-  StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL trusted_value =
-      StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL::FromTrustedScript(
-          script);
-  GetStringFromTrustedTypeWorks(trusted_value, "A string");
-}
-
-TEST(TrustedTypesUtilTest, GetStringFromTrustedType_TrustedScriptURL) {
-  String url_address = "http://www.example.com/";
-  auto* script_url = MakeGarbageCollected<TrustedScriptURL>(url_address);
-  StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL trusted_value =
-      StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL::
-          FromTrustedScriptURL(script_url);
-  GetStringFromTrustedTypeWorks(trusted_value, "http://www.example.com/");
-}
-
-TEST(TrustedTypesUtilTest, GetStringFromTrustedType_TrustedScriptURL_Relative) {
-  String url_address = "relative/url.html";
-  auto* script_url = MakeGarbageCollected<TrustedScriptURL>(url_address);
-  StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL trusted_value =
-      StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL::
-          FromTrustedScriptURL(script_url);
-  GetStringFromTrustedTypeWorks(trusted_value, "relative/url.html");
-}
-
-TEST(TrustedTypesUtilTest, GetStringFromTrustedType_String) {
-  StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL string_value =
-      StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL::FromString(
-          "A string");
-  GetStringFromTrustedTypeThrows(string_value);
-}
-
-// GetStringFromTrustedTypeWithoutCheck() tests
-TEST(TrustedTypesUtilTest, GetStringFromTrustedTypeWithoutCheck_TrustedHTML) {
-  auto* html = MakeGarbageCollected<TrustedHTML>("A string");
-  StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL trusted_value =
-      StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL::FromTrustedHTML(
-          html);
-  String s = GetStringFromTrustedTypeWithoutCheck(trusted_value);
-  ASSERT_EQ(s, "A string");
-}
-
-TEST(TrustedTypesUtilTest, GetStringFromTrustedTypeWithoutCheck_TrustedScript) {
-  auto* script = MakeGarbageCollected<TrustedScript>("A string");
-  StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL trusted_value =
-      StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL::FromTrustedScript(
-          script);
-  String s = GetStringFromTrustedTypeWithoutCheck(trusted_value);
-  ASSERT_EQ(s, "A string");
-}
-
-TEST(TrustedTypesUtilTest,
-     GetStringFromTrustedTypeWithoutCheck_TrustedScriptURL) {
-  String url_address = "http://www.example.com/";
-  auto* script_url = MakeGarbageCollected<TrustedScriptURL>(url_address);
-  StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL trusted_value =
-      StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL::
-          FromTrustedScriptURL(script_url);
-  String s = GetStringFromTrustedTypeWithoutCheck(trusted_value);
-  ASSERT_EQ(s, "http://www.example.com/");
-}
-
-TEST(TrustedTypesUtilTest, GetStringFromTrustedTypeWithoutCheck_String) {
-  StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL string_value =
-      StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL::FromString(
-          "A string");
-  String s = GetStringFromTrustedTypeWithoutCheck(string_value);
-  ASSERT_EQ(s, "A string");
-}
-
-TEST(TrustedTypesUtilTest, GetStringFromTrustedTypeWithoutCheck_Null) {
-  StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL null_value =
-      StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL();
-  String s = GetStringFromTrustedTypeWithoutCheck(null_value);
-  ASSERT_EQ(s, "");
-}
-
-// GetStringFromTrustedHTML tests
-TEST(TrustedTypesUtilTest, GetStringFromTrustedHTML_TrustedHTML) {
+// TrustedTypesCheckForHTML tests
+TEST(TrustedTypesUtilTest, TrustedTypesCheckForHTML_TrustedHTML) {
   auto* html = MakeGarbageCollected<TrustedHTML>("A string");
   StringOrTrustedHTML trusted_value =
       StringOrTrustedHTML::FromTrustedHTML(html);
-  GetStringFromTrustedHTMLWorks(trusted_value, "A string");
+  TrustedTypesCheckForHTMLWorks(trusted_value, "A string");
 }
 
-TEST(TrustedTypesUtilTest, GetStringFromTrustedHTML_String) {
+TEST(TrustedTypesUtilTest, TrustedTypesCheckForHTML_String) {
   StringOrTrustedHTML string_value =
       StringOrTrustedHTML::FromString("A string");
-  GetStringFromTrustedHTMLThrows(string_value);
+  TrustedTypesCheckForHTMLThrows(string_value);
 }
 
-// GetStringFromTrustedScript tests
-TEST(TrustedTypesUtilTest, GetStringFromTrustedScript_TrustedScript) {
+// TrustedTypesCheckForScript tests
+TEST(TrustedTypesUtilTest, TrustedTypesCheckForScript_TrustedScript) {
   auto* script = MakeGarbageCollected<TrustedScript>("A string");
   StringOrTrustedScript trusted_value =
       StringOrTrustedScript::FromTrustedScript(script);
-  GetStringFromTrustedScriptWorks(trusted_value, "A string");
+  TrustedTypesCheckForScriptWorks(trusted_value, "A string");
 }
 
-TEST(TrustedTypesUtilTest, GetStringFromTrustedScript_String) {
+TEST(TrustedTypesUtilTest, TrustedTypesCheckForScript_String) {
   StringOrTrustedScript string_value =
       StringOrTrustedScript::FromString("A string");
-  GetStringFromTrustedScriptThrows(string_value);
+  TrustedTypesCheckForScriptThrows(string_value);
 }
 
-// GetStringFromTrustedScriptURL tests
-TEST(TrustedTypesUtilTest, GetStringFromTrustedScriptURL_TrustedScriptURL) {
+// TrustedTypesCheckForScriptURL tests
+TEST(TrustedTypesUtilTest, TrustedTypesCheckForScriptURL_TrustedScriptURL) {
   String url_address = "http://www.example.com/";
   auto* script_url = MakeGarbageCollected<TrustedScriptURL>(url_address);
   StringOrTrustedScriptURL trusted_value =
       StringOrTrustedScriptURL::FromTrustedScriptURL(script_url);
-  GetStringFromTrustedScriptURLWorks(trusted_value, "http://www.example.com/");
+  TrustedTypesCheckForScriptURLWorks(trusted_value, "http://www.example.com/");
 }
 
-TEST(TrustedTypesUtilTest, GetStringFromTrustedScriptURL_String) {
+TEST(TrustedTypesUtilTest, TrustedTypesCheckForScriptURL_String) {
   StringOrTrustedScriptURL string_value =
       StringOrTrustedScriptURL::FromString("A string");
-  GetStringFromTrustedScriptURLThrows(string_value);
+  TrustedTypesCheckForScriptURLThrows(string_value);
 }
 }  // namespace blink
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 b9480613..bb08f9e 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -174,7 +174,7 @@
     ExceptionState& exception_state) {
   Vector<String> string_urls;
   for (const StringOrTrustedScriptURL& stringOrUrl : urls) {
-    String string_url = GetStringFromTrustedScriptURL(
+    String string_url = TrustedTypesCheckForScriptURL(
         stringOrUrl, GetExecutionContext(), exception_state);
     if (exception_state.HadException())
       return;
diff --git a/third_party/blink/renderer/core/xml/dom_parser.cc b/third_party/blink/renderer/core/xml/dom_parser.cc
index 9890fa3..a60e49c8 100644
--- a/third_party/blink/renderer/core/xml/dom_parser.cc
+++ b/third_party/blink/renderer/core/xml/dom_parser.cc
@@ -32,7 +32,7 @@
 Document* DOMParser::parseFromString(const StringOrTrustedHTML& stringOrHTML,
                                      const String& type,
                                      ExceptionState& exception_state) {
-  String value = GetStringFromTrustedHTML(stringOrHTML, context_document_,
+  String value = TrustedTypesCheckForHTML(stringOrHTML, context_document_,
                                           exception_state);
   if (!exception_state.HadException()) {
     return parseFromStringInternal(value, type);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 3bd5e9c3..3f2530d 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -2436,27 +2436,17 @@
   if (HasAOMPropertyOrARIAAttribute(AOMStringProperty::kRole, role))
     return true;
 
-  if (!layout_object_->IsTable())
-    return false;
-
   // When a section of the document is contentEditable, all tables should be
   // treated as data tables, otherwise users may not be able to work with rich
   // text editors that allow creating and editing tables.
   if (GetNode() && HasEditableStyle(*GetNode()))
     return true;
 
-  // If there's no node, it's definitely a layout table. This happens
-  // when table CSS styles are used without a complete table DOM structure.
-  LayoutNGTableInterface* table =
-      ToInterface<LayoutNGTableInterface>(layout_object_);
-  table->RecalcSectionsIfNeeded();
-  Node* table_node = layout_object_->GetNode();
-
   // This employs a heuristic to determine if this table should appear.
   // Only "data" tables should be exposed as tables.
   // Unfortunately, there is no good way to determine the difference
   // between a "layout" table and a "data" table.
-  auto* table_element = DynamicTo<HTMLTableElement>(table_node);
+  auto* table_element = DynamicTo<HTMLTableElement>(GetNode());
   if (!table_element)
     return false;
 
@@ -2474,6 +2464,23 @@
   if (Traversal<HTMLTableColElement>::FirstChild(*table_element))
     return true;
 
+  // Everything from here forward uses cell style info, but only if a CSS table.
+  // If this code is reached for a <table> with another display type, consider
+  // this to be a layout table.
+  // TODO(accessibility) consider rewriting the following cell inspection code
+  // using purely DOM methods, such as table->rows()->Item(index)->cells().
+  if (!layout_object_->IsTable())
+    return false;
+
+  // If there's no node, it's definitely a layout table. This happens
+  // when table CSS styles are used without a complete table DOM structure.
+  LayoutNGTableInterface* table =
+      ToInterface<LayoutNGTableInterface>(layout_object_);
+  table->RecalcSectionsIfNeeded();
+  Node* table_node = layout_object_->GetNode();
+  if (!table_node)
+    return false;
+
   // go through the cell's and check for tell-tale signs of "data" table status
   // cells have borders, or use attributes like headers, abbr, scope or axis
   table->RecalcSectionsIfNeeded();
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_client.cc b/third_party/blink/renderer/modules/mediastream/user_media_client.cc
index d38d10f..f40e32f 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_client.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_client.cc
@@ -148,8 +148,8 @@
 
   int request_id = g_next_request_id++;
   blink::WebRtcLogMessage(base::StringPrintf(
-      "UMCI::RequestUserMedia. request_id=%d, audio constraints=%s, "
-      "video constraints=%s",
+      "UMCI::RequestUserMedia({request_id=%d}, {audio constraints=%s}, "
+      "{video constraints=%s})",
       request_id,
       user_media_request->AudioConstraints().ToString().Utf8().c_str(),
       user_media_request->VideoConstraints().ToString().Utf8().c_str()));
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink.cc
index 58bc919..620c1af 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink.cc
@@ -6,6 +6,7 @@
 
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_encoded_video_frame.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h"
 #include "third_party/webrtc/api/video/encoded_frame.h"
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink_test.cc
index c985294..150ecb9c 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink_test.cc
@@ -9,6 +9,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
+#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_encoded_video_frame.h"
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 2fd082a..0e063a2f 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -1967,6 +1967,25 @@
   return String();
 }
 
+bool RTCPeerConnection::canTrickleIceCandidates(bool& is_null) const {
+  is_null = true;
+  if (closed_ || !peer_handler_->RemoteDescription()) {
+    return false;
+  }
+  webrtc::PeerConnectionInterface* native_connection =
+      peer_handler_->NativePeerConnection();
+  if (!native_connection) {
+    return false;
+  }
+  absl::optional<bool> can_trickle =
+      native_connection->can_trickle_ice_candidates();
+  if (!can_trickle) {
+    return false;
+  }
+  is_null = false;
+  return *can_trickle;
+}
+
 void RTCPeerConnection::restartIce() {
   if (closed_)
     return;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
index 5759cfe..e32be36 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -227,6 +227,8 @@
 
   String connectionState() const;
 
+  bool canTrickleIceCandidates(bool&) const;
+
   void restartIce();
 
   // A local stream is any stream associated with a sender.
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
index 2ccb2bd..65dbef0 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
@@ -83,7 +83,7 @@
     readonly attribute RTCIceGatheringState iceGatheringState;
     readonly attribute RTCIceConnectionState iceConnectionState;
     readonly attribute RTCPeerConnectionState connectionState;
-    // readonly attribute boolean? canTrickleIceCandidates;
+    readonly attribute boolean? canTrickleIceCandidates;
     void restartIce();
     [CallWith=ScriptState] RTCConfiguration getConfiguration();
     [CallWith=ScriptState, RaisesException] void setConfiguration(RTCConfiguration configuration);
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index ddd3314..c6d562bd 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -505,14 +505,6 @@
   RuntimeEnabledFeatures::SetExpensiveBackgroundTimerThrottlingEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableFetchMetadata(bool enable) {
-  RuntimeEnabledFeatures::SetFetchMetadataEnabled(enable);
-}
-
-void WebRuntimeFeatures::EnableFetchMetadataDestination(bool enable) {
-  RuntimeEnabledFeatures::SetFetchMetadataDestinationEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableTimerThrottlingForBackgroundTabs(bool enable) {
   RuntimeEnabledFeatures::SetTimerThrottlingForBackgroundTabsEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/exported/web_url_request.cc b/third_party/blink/renderer/platform/exported/web_url_request.cc
index ce8819c4..e2b35ca 100644
--- a/third_party/blink/renderer/platform/exported/web_url_request.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_request.cc
@@ -117,7 +117,7 @@
   DCHECK_EQ(owned_resource_request_.get(), resource_request_);
   DCHECK(owned_resource_request_->IsNull());
   DCHECK(this != &r);
-  resource_request_->CopyHeadFrom(r.resource_request_);
+  resource_request_->CopyHeadFrom(*r.resource_request_);
   resource_request_->SetHttpBody(r.resource_request_->HttpBody());
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc
index c858317..4a887fa 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -490,7 +490,7 @@
   DCHECK(!request.IsNull());
   CHECK(!is_revalidation_start_forbidden_);
   is_revalidating_ = true;
-  resource_request_.CopyFrom(request);
+  resource_request_.CopyHeadFrom(request);
   status_ = ResourceStatus::kNotStarted;
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index 29fe8ba1..cc3431e 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -2090,7 +2090,7 @@
   // TODO(dtapuska): revisit this when we have a better way to re-dispatch
   // requests.
   ResourceRequest request;
-  request.CopyFrom(stale_resource->GetResourceRequest());
+  request.CopyHeadFrom(stale_resource->GetResourceRequest());
   FetchParameters params(std::move(request));
   params.SetStaleRevalidation(true);
   params.MutableResourceRequest().SetSkipServiceWorker(true);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
index 9e136c3..a7b4142a 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
@@ -113,8 +113,8 @@
   *this = src;
 }
 
-void ResourceRequest::CopyHeadFrom(const ResourceRequestHead* src) {
-  this->ResourceRequestHead::operator=(*src);
+void ResourceRequest::CopyHeadFrom(const ResourceRequestHead& src) {
+  this->ResourceRequestHead::operator=(src);
 }
 
 std::unique_ptr<ResourceRequest> ResourceRequest::CreateRedirectRequest(
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
index ca9b3fe..be58e01 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
@@ -557,7 +557,7 @@
   // TODO(yoichio): Use move semantics as much as possible.
   // See crbug.com/787704.
   void CopyFrom(const ResourceRequest&);
-  void CopyHeadFrom(const ResourceRequestHead*);
+  void CopyHeadFrom(const ResourceRequestHead&);
 
   // Constructs a new ResourceRequest for a redirect from this instance.
   std::unique_ptr<ResourceRequest> CreateRedirectRequest(
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 37c5051..8031be3 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -721,14 +721,6 @@
       name: "FeaturePolicyVibrateFeature"
     },
     {
-      name: "FetchMetadata",
-      status: "stable"
-    },
-    {
-      name: "FetchMetadataDestination",
-      status: "experimental"
-    },
-    {
       // Also enabled when blink::features::kFileHandlingAPI is overridden
       // on the command line (or via chrome://flags).
       name: "FileHandling",
@@ -959,10 +951,6 @@
       // Enabled by features::kLegacyWindowsDWriteFontFallback;
     },
     {
-      name: "LinkSystemColors",
-      status: "stable",
-    },
-    {
       name:"ManualSlotting",
       status:"experimental",
     },
@@ -1137,10 +1125,6 @@
       name: "NewRemotePlaybackPipeline",
     },
     {
-      name: "NewSystemColors",
-      status: "stable",
-    },
-    {
       name: "NoIdleEncodingForWebTests",
       status: "test",
     },
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index e89b8ef..9907e90 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -56,6 +56,8 @@
 crbug.com/906791 external/wpt/fullscreen/api/element-ready-check-allowed-cross-origin-manual.sub.html [ Timeout ]
 crbug.com/860713 external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html [ Failure Timeout ]
 crbug.com/860713 external/wpt/bluetooth/requestDevice/request-from-sandboxed-iframe.https.html [ Failure Timeout ]
+crbug.com/860713 virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html [ Failure Timeout ]
+crbug.com/860713 virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/requestDevice/request-from-sandboxed-iframe.https.html [ Failure Timeout ]
 
 # The following tests would pass with User Activation Delegation.
 crbug.com/928838 external/wpt/html/user-activation/activation-transfer-with-click.tentative.html [ Failure ]
@@ -6277,7 +6279,8 @@
 
 # Enable scroll-snap tests on impl thread
 # These are currently failing on Mac which needs more investigation, snap-scrolls-visual-viewport seems flaky
-crbug.com/878878 [ Mac ] virtual/threaded/fast/scroll-snap/snaps-after-keyboard-scrolling.html [ Timeout ]
+crbug.com/878878 [ Mac ] virtual/threaded/fast/scroll-snap/snaps-after-keyboard-scrolling.html [ Pass Timeout ]
+crbug.com/878878 [ Win10 ] virtual/threaded/fast/scroll-snap/snaps-after-keyboard-scrolling.html [ Pass Timeout ]
 crbug.com/878878 [ Mac ] virtual/threaded/fast/scroll-snap/snaps-after-scrollbar-scrolling.html [ Timeout ]
 crbug.com/878878 [ Mac ] virtual/threaded/fast/scroll-snap/snaps-for-different-key-granularity.html [ Timeout ]
 
@@ -6753,3 +6756,5 @@
 
 crbug.com/1057822 http/tests/misc/synthetic-gesture-initiated-in-cross-origin-frame.html [ Pass Failure ]
 crbug.com/1057807 http/tests/misc/destroy-middle-click-locked-target-crash.html [ Pass Timeout ]
+crbug.com/1057351 virtual/threaded-no-composited-antialiasing/animations/responsive/interpolation/offset-rotate-responsive.html [ Pass Failure ]
+crbug.com/1057351 virtual/threaded-no-composited-antialiasing/animations/stability/empty-keyframes.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 6770420..4aab5a1 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -731,5 +731,10 @@
     "prefix": "storage-access-api",
     "bases": [ "external/wpt/storage-access-api" ],
     "args": [ "--enable-features=StorageAccessAPI" ]
+  },
+  {
+    "prefix": "web-bluetooth-new-permissions-backend",
+    "bases": ["bluetooth", "external/wpt/bluetooth"],
+    "args": ["--enable-features=WebBluetoothNewPermissionsBackend"]
   }
 ]
diff --git a/third_party/blink/web_tests/custom-elements/constructor-may-poach-upgrading-element.html b/third_party/blink/web_tests/custom-elements/constructor-may-poach-upgrading-element.html
index fce72a8..bda6d9a 100644
--- a/third_party/blink/web_tests/custom-elements/constructor-may-poach-upgrading-element.html
+++ b/third_party/blink/web_tests/custom-elements/constructor-may-poach-upgrading-element.html
@@ -61,9 +61,7 @@
                 'the recursive "new" should steal the upgrade candidate');
   assert_equals(poacher, invocations[0],
                 'the recursize "new" should happen first');
-  assert_true(invocations[1] instanceof w.DOMException,
-              'the super call should have thrown a DOMException');
-  assert_equals(invocations[1].name, 'InvalidStateError',
-                'the exception should be an InvalidStateError');
+  assert_true(invocations[1] instanceof w.TypeError,
+              'the super call should have thrown a TypeError');
 }, 'HTMLElement constructor: poach upgrade candidate');
 </script>
diff --git a/third_party/blink/web_tests/custom-elements/spec/construct.html b/third_party/blink/web_tests/custom-elements/spec/construct.html
index 11e3fce..658efc4 100644
--- a/third_party/blink/web_tests/custom-elements/spec/construct.html
+++ b/third_party/blink/web_tests/custom-elements/spec/construct.html
@@ -146,7 +146,7 @@
   }
   w.customElements.define('a-a', A);
   let d = w.document.createElement('div');
-  assert_reports(window, 'INVALID_STATE_ERR', () => {
+  assert_reports_js(window, TypeError, () => {
     d.innerHTML = '<a-a>';
   }, 'Creating an element that is already constructed marker should report ' +
      'InvalidStateError');
@@ -171,7 +171,7 @@
   }
   w.customElements.define('a-a', A);
   w.document.body.appendChild(e);
-  assert_array_equals(errors, ['InvalidStateError'], 'Upgrading an element ' +
-    'that is already constructed marker should throw InvalidStateError');
+  assert_array_equals(errors, ['TypeError'], 'Upgrading an element ' +
+    'that is already constructed marker should throw TypeError');
 }, 'Already constructed marker, upgrade element');
 </script>
diff --git a/third_party/blink/web_tests/custom-elements/spec/report-the-exception.html b/third_party/blink/web_tests/custom-elements/spec/report-the-exception.html
index 06b3b2ad..d4e7230 100644
--- a/third_party/blink/web_tests/custom-elements/spec/report-the-exception.html
+++ b/third_party/blink/web_tests/custom-elements/spec/report-the-exception.html
@@ -147,11 +147,6 @@
   assert_equals(error.error.name, 'TypeError');
 }
 
-function assert_invalid_state_dom_error_event(error) {
-  assert_not_muted_error_event(error);
-  assert_equals(error.error.name, 'InvalidStateError');
-}
-
 test_with_window((w) => {
   // "create an element" 6.1.3, throw a TypeError.
   // https://dom.spec.whatwg.org/#concept-create-element
@@ -160,10 +155,10 @@
   constructor_returns_bad_object + instantiate);
 
 test_with_window((w) => {
-  // "upgrade an element" 10, throw an InvalidStateError DOMException.
+  // "upgrade an element" 10, throw a TypeError.
   // https://html.spec.whatwg.org/multipage/scripting.html#upgrades
   w.document.body.innerHTML = instantiate;
-  assert_invalid_state_dom_error_event(w.errors[0]);
+  assert_type_error_event(w.errors[0]);
 }, 'Upgrade reaction invokes the constructor that returns a bad object',
   constructor_returns_bad_object);
 </script>
diff --git a/third_party/blink/web_tests/custom-elements/spec/resources/custom-elements-helpers.js b/third_party/blink/web_tests/custom-elements/spec/resources/custom-elements-helpers.js
index 44871eeb..7de434d1 100644
--- a/third_party/blink/web_tests/custom-elements/spec/resources/custom-elements-helpers.js
+++ b/third_party/blink/web_tests/custom-elements/spec/resources/custom-elements-helpers.js
@@ -70,3 +70,26 @@
   // accessed by throwing the error
   assert_throws(expected_error, () => { throw errors[0]; });
 }
+
+// Asserts that func synchronously invokes the error event handler in w
+// with the expected error.
+function assert_reports_js(w, expected_error, func, description) {
+  let old_onerror = w.onerror;
+  let errors = [];
+  w.onerror = (event, source, line_number, column_number, error) => {
+    errors.push(error);
+    return true;  // the error is handled
+  };
+  try {
+    func();
+  } catch (e) {
+    assert_unreached(`should report, not throw, an exception: ${e}`);
+  } finally {
+    w.onerror = old_onerror;
+  }
+  assert_equals(errors.length, 1, 'only one error should have been reported');
+  assert_true(
+      typeof errors[0] === 'object' && errors[0] !== null,
+      'got something other than an error');
+  assert_equals(expected_error.name, errors[0].name);
+}
diff --git a/third_party/blink/web_tests/custom-elements/spec/upgrade-element.html b/third_party/blink/web_tests/custom-elements/spec/upgrade-element.html
index 8d728cb64..27f9f92d 100644
--- a/third_party/blink/web_tests/custom-elements/spec/upgrade-element.html
+++ b/third_party/blink/web_tests/custom-elements/spec/upgrade-element.html
@@ -115,7 +115,7 @@
     }
   }
   w.customElements.define('a-a', X);
-  assert_array_equals(error_log, ['InvalidStateError'], 'returning object that is different from element should throw "InvalidStateError"');
+  assert_array_equals(error_log, ['TypeError'], 'returning object that is different from element should throw "TypeError"');
 }, 'Upgrading an element with constructor that returns different object');
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
index f84f24e7d..3546f46 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
@@ -164560,9 +164560,6 @@
    "html/cross-origin-embedder-policy/data.https.html.headers": [
     []
    ],
-   "html/cross-origin-embedder-policy/dedicated-worker-cache-storage.https-expected.txt": [
-    []
-   ],
    "html/cross-origin-embedder-policy/javascript.https.html.headers": [
     []
    ],
@@ -193660,9 +193657,15 @@
    "workers/modules/resources/static-import-script-block-cross-origin.js": [
     []
    ],
+   "workers/modules/resources/static-import-syntax-error.js": [
+    []
+   ],
    "workers/modules/resources/static-import-worker.js": [
     []
    ],
+   "workers/modules/resources/syntax-error.js": [
+    []
+   ],
    "workers/modules/shared-worker-import-meta-expected.txt": [
     []
    ],
@@ -236237,6 +236240,12 @@
      {}
     ]
    ],
+   "css/css-values/update-subpixel-rem-unit.html": [
+    [
+     "css/css-values/update-subpixel-rem-unit.html",
+     {}
+    ]
+   ],
    "css/css-values/urls/empty.html": [
     [
      "css/css-values/urls/empty.html",
@@ -371169,6 +371178,12 @@
      {}
     ]
    ],
+   "workers/modules/dedicated-worker-parse-error-failure.html": [
+    [
+     "workers/modules/dedicated-worker-parse-error-failure.html",
+     {}
+    ]
+   ],
    "workers/modules/shared-worker-import-blob-url.any.js": [
     [
      "workers/modules/shared-worker-import-blob-url.any.html",
@@ -469310,6 +469325,10 @@
    "5869e9e6072910301ba41746f03eaf297079d98e",
    "testharness"
   ],
+  "css/css-values/update-subpixel-rem-unit.html": [
+   "98d4f00f92ae90165dd9b0c82cf0496523bc9c2a",
+   "testharness"
+  ],
   "css/css-values/urls/empty.html": [
    "3ab7079396c517d7abd6334b0cfadf7eda471115",
    "testharness"
@@ -500910,10 +500929,6 @@
    "6604450991a122e3e241e40b1b9e0516c525389d",
    "support"
   ],
-  "html/cross-origin-embedder-policy/dedicated-worker-cache-storage.https-expected.txt": [
-   "c2bc85a1f5b9f7005f1240f2625530d7435acb10",
-   "support"
-  ],
   "html/cross-origin-embedder-policy/dedicated-worker-cache-storage.https.html": [
    "2559de839a304dce0c2700dc4f8ee0002de04332",
    "testharness"
@@ -508671,11 +508686,11 @@
    "reftest"
   ],
   "html/rendering/unmapped-attributes-expected.txt": [
-   "4179bfbb0420e5141741be44c2c7ea211b24247d",
+   "5fb804bc64d52fdef34d989a596be1564e8da682",
    "support"
   ],
   "html/rendering/unmapped-attributes.html": [
-   "3f457eb492f0b72b2a9c5891e8784d0b69958b38",
+   "5824f836f0d654c116a55a252dfbda5fc6e5bbc8",
    "testharness"
   ],
   "html/rendering/widgets/appearance/default-styles-expected.txt": [
@@ -606747,7 +606762,7 @@
    "testharness"
   ],
   "workers/modules/dedicated-worker-import-failure.html": [
-   "33eeea261893bc2d920bb348efff7a07f264729f",
+   "4c705e73251f41d8b7ff796cda5a5629968ec130",
    "testharness"
   ],
   "workers/modules/dedicated-worker-import-meta-expected.txt": [
@@ -606778,6 +606793,10 @@
    "bb37a18f2cc93312fd10fd7e0ed64d7be738a45f",
    "testharness"
   ],
+  "workers/modules/dedicated-worker-parse-error-failure.html": [
+   "99eae49fc91296c55c51fb772bc4d46058ea0693",
+   "testharness"
+  ],
   "workers/modules/resources/dynamic-import-and-then-static-import-worker.js": [
    "527702f551174f8f2578fce16c7854351126e3a5",
    "support"
@@ -606942,10 +606961,18 @@
    "82be8e7ce72edab7050b14cccc658ff39ddfe632",
    "support"
   ],
+  "workers/modules/resources/static-import-syntax-error.js": [
+   "3a20e792c455ee53c26066f99afd79b041a46fb6",
+   "support"
+  ],
   "workers/modules/resources/static-import-worker.js": [
    "19a347999d386fd4df8dc831c6d164b27630ef7a",
    "support"
   ],
+  "workers/modules/resources/syntax-error.js": [
+   "8c5c4df671bcc3f75ac1e474fc651927e57e8701",
+   "support"
+  ],
   "workers/modules/shared-worker-import-blob-url.any.js": [
    "f56c1a5525f8c46e5d06f6549b3f355c4ccb4a0a",
    "testharness"
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/upgrading/Node-cloneNode-expected.txt b/third_party/blink/web_tests/external/wpt/custom-elements/upgrading/Node-cloneNode-expected.txt
deleted file mode 100644
index 6ad8ab1..0000000
--- a/third_party/blink/web_tests/external/wpt/custom-elements/upgrading/Node-cloneNode-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This is a testharness.js-based test.
-PASS Node.prototype.cloneNode(false) must be able to clone a custom element
-PASS Node.prototype.cloneNode(false) must be able to clone as a autonomous custom element when it contains is attribute
-PASS Node.prototype.cloneNode(false) must be able to clone as a customized built-in element when it has an inconsistent "is" attribute
-PASS Node.prototype.cloneNode(false) must be able to clone a custom element inside an iframe
-PASS Node.prototype.cloneNode(true) must be able to clone a descendent custom element
-PASS Node.prototype.cloneNode(true) must set parentNode, previousSibling, and nextSibling before upgrading custom elements
-FAIL HTMLElement constructor must throw an TypeError when the top of the construction stack is marked AlreadyConstructed due to a custom element constructor constructing itself after super() call assert_equals: expected "TypeError" but got "InvalidStateError"
-FAIL HTMLElement constructor must throw an TypeError when the top of the construction stack is marked AlreadyConstructed due to a custom element constructor constructing itself before super() call assert_equals: expected "TypeError" but got "InvalidStateError"
-FAIL Upgrading a custom element must throw TypeError when the custom element's constructor returns another element assert_equals: expected "TypeError" but got "InvalidStateError"
-PASS Inserting an element must not try to upgrade a custom element when it had already failed to upgrade once
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/upgrading/upgrading-parser-created-element-expected.txt b/third_party/blink/web_tests/external/wpt/custom-elements/upgrading/upgrading-parser-created-element-expected.txt
deleted file mode 100644
index c520307..0000000
--- a/third_party/blink/web_tests/external/wpt/custom-elements/upgrading/upgrading-parser-created-element-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-PASS Element.prototype.createElement must add an unresolved custom element to the upgrade candidates map
-FAIL HTMLElement constructor must throw an TypeError when the top of the construction stack is marked AlreadyConstructed due to a custom element constructor constructing itself after super() call assert_equals: expected "TypeError" but got "InvalidStateError"
-FAIL HTMLElement constructor must throw an TypeError when the top of the construction stack is marked AlreadyConstructed due to a custom element constructor constructing itself before super() call assert_equals: expected "TypeError" but got "InvalidStateError"
-FAIL Upgrading a custom element must throw an TypeError when the returned element is not SameValue as the upgraded element assert_equals: expected "TypeError" but got "InvalidStateError"
-FAIL Upgrading a custom element whose constructor returns a Text node must throw assert_equals: expected "TypeError" but got "InvalidStateError"
-FAIL Upgrading a custom element whose constructor returns an Element must throw assert_equals: expected "TypeError" but got "InvalidStateError"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-alt-crash-001.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-alt-crash-001.html
new file mode 100644
index 0000000..b057967
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-alt-crash-001.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>Crash test: img alt rendering in combination with style attribute selector</title>
+<link rel="help" href="https://crbug.com/1057210">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  img { display: block; width: 100px; }
+  [style] + * {}
+</style>
+<img id="img" alt="alternative text">
+<script>
+  test(() => {
+    assert_equals(getComputedStyle(img).width, "100px");
+  }, "Should not crash.");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/unmapped-attributes-expected.txt b/third_party/blink/web_tests/external/wpt/html/rendering/unmapped-attributes-expected.txt
index 4179bfbb..5fb804b 100644
--- a/third_party/blink/web_tests/external/wpt/html/rendering/unmapped-attributes-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/unmapped-attributes-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 54 tests; 46 PASS, 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 62 tests; 54 PASS, 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS We should be in standards mode
 PASS Subframe should be in quirks mode
 PASS <table hspace> should not be mapped to style marginLeft in CSS1Compat mode
@@ -26,6 +26,14 @@
 PASS <iframe border> should not be mapped to style borderBottomWidth in BackCompat mode
 PASS <iframe border> should not be mapped to style borderLeftWidth in CSS1Compat mode
 PASS <iframe border> should not be mapped to style borderLeftWidth in BackCompat mode
+PASS <iframe hspace> should not be mapped to style marginLeft in CSS1Compat mode
+PASS <iframe hspace> should not be mapped to style marginLeft in BackCompat mode
+PASS <iframe hspace> should not be mapped to style marginRight in CSS1Compat mode
+PASS <iframe hspace> should not be mapped to style marginRight in BackCompat mode
+PASS <iframe vspace> should not be mapped to style marginTop in CSS1Compat mode
+PASS <iframe vspace> should not be mapped to style marginTop in BackCompat mode
+PASS <iframe vspace> should not be mapped to style marginBottom in CSS1Compat mode
+PASS <iframe vspace> should not be mapped to style marginBottom in BackCompat mode
 PASS <marquee border> should not be mapped to style borderTopWidth in CSS1Compat mode
 PASS <marquee border> should not be mapped to style borderTopWidth in BackCompat mode
 PASS <marquee border> should not be mapped to style borderRightWidth in CSS1Compat mode
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/unmapped-attributes.html b/third_party/blink/web_tests/external/wpt/html/rendering/unmapped-attributes.html
index 3f457eb..5824f83 100644
--- a/third_party/blink/web_tests/external/wpt/html/rendering/unmapped-attributes.html
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/unmapped-attributes.html
@@ -52,6 +52,10 @@
   [ newElem("iframe"), "border", "borderRightWidth" ],
   [ newElem("iframe"), "border", "borderBottomWidth" ],
   [ newElem("iframe"), "border", "borderLeftWidth" ],
+  [ newElem("iframe"), "hspace", "marginLeft" ],
+  [ newElem("iframe"), "hspace", "marginRight" ],
+  [ newElem("iframe"), "vspace", "marginTop" ],
+  [ newElem("iframe"), "vspace", "marginBottom" ],
   [ newElem("marquee"), "border", "borderTopWidth" ],
   [ newElem("marquee"), "border", "borderRightWidth" ],
   [ newElem("marquee"), "border", "borderBottomWidth" ],
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-canTrickleIceCandidates-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-canTrickleIceCandidates-expected.txt
deleted file mode 100644
index e7d8297..0000000
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-canTrickleIceCandidates-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL canTrickleIceCandidates property is null prior to setRemoteDescription assert_equals: canTrickleIceCandidates property is null expected (object) null but got (undefined) undefined
-FAIL canTrickleIceCandidates property is true after setRemoteDescription with a=ice-options:trickle assert_true: canTrickleIceCandidates property is true after setRemoteDescription expected true got undefined
-FAIL canTrickleIceCandidates property is false after setRemoteDescription without a=ice-options:trickle assert_false: canTrickleIceCandidates property is false after setRemoteDescription expected false got undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-constructor-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-constructor-expected.txt
deleted file mode 100644
index 79341f7..0000000
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-constructor-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-This is a testharness.js-based test.
-PASS RTCPeerConnection.length
-PASS new RTCPeerConnection()
-PASS new RTCPeerConnection(null)
-PASS new RTCPeerConnection(undefined)
-PASS new RTCPeerConnection({})
-PASS new RTCPeerConnection({ certificates: null })
-PASS new RTCPeerConnection({ certificates: undefined })
-PASS new RTCPeerConnection({ certificates: [] })
-PASS new RTCPeerConnection({ certificates: [null] })
-PASS new RTCPeerConnection({ certificates: [undefined] })
-PASS new RTCPeerConnection({ iceCandidatePoolSize: toNumberThrows })
-PASS localDescription initial value
-PASS currentLocalDescription initial value
-PASS pendingLocalDescription initial value
-PASS remoteDescription initial value
-PASS currentRemoteDescription initial value
-PASS pendingRemoteDescription initial value
-PASS signalingState initial value
-PASS iceGatheringState initial value
-PASS iceConnectionState initial value
-PASS connectionState initial value
-FAIL canTrickleIceCandidates initial value assert_equals: expected (object) null but got (undefined) undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
index 482ce5fe..a416ef5 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 496 tests; 472 PASS, 24 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 496 tests; 474 PASS, 22 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 PASS Test driver for asyncInitCertificate
@@ -38,7 +38,7 @@
 PASS RTCPeerConnection interface: attribute iceGatheringState
 PASS RTCPeerConnection interface: attribute iceConnectionState
 PASS RTCPeerConnection interface: attribute connectionState
-FAIL RTCPeerConnection interface: attribute canTrickleIceCandidates assert_true: The prototype object must have a property "canTrickleIceCandidates" expected true got false
+PASS RTCPeerConnection interface: attribute canTrickleIceCandidates
 PASS RTCPeerConnection interface: operation restartIce()
 PASS RTCPeerConnection interface: operation getConfiguration()
 FAIL RTCPeerConnection interface: operation setConfiguration(optional RTCConfiguration) assert_equals: property has wrong .length expected 0 but got 1
@@ -89,7 +89,7 @@
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "iceGatheringState" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "iceConnectionState" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "connectionState" with the proper type
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "canTrickleIceCandidates" with the proper type assert_inherits: property "canTrickleIceCandidates" not found in prototype chain
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "canTrickleIceCandidates" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "restartIce()" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getConfiguration()" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setConfiguration(optional RTCConfiguration)" with the proper type
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/dynamic-scripts-expected.txt b/third_party/blink/web_tests/http/tests/devtools/startup/dynamic-scripts-expected.txt
deleted file mode 100644
index b129381..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/dynamic-scripts-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Tests that scripts for dynamically added script elements are shown in sources panel if inspector is opened after the scripts were loaded. https://bugs.webkit.org/show_bug.cgi?id=99324
-
-UISourceCodes:
-    dynamic-script.js
-    dynamic-scripts.html
-    evalSourceURL.js
-    scriptElementContentSourceURL.js
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/dynamic-scripts.js b/third_party/blink/web_tests/http/tests/devtools/startup/dynamic-scripts.js
deleted file mode 100644
index 602d8e35..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/dynamic-scripts.js
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  await TestRunner.setupStartupTest('resources/dynamic-scripts.html');
-  TestRunner.addResult(
-      `Tests that scripts for dynamically added script elements are shown in sources panel if inspector is opened after the scripts were loaded. https://bugs.webkit.org/show_bug.cgi?id=99324\n`);
-  await TestRunner.loadModule('sources_test_runner');
-
-  SourcesTestRunner.startDebuggerTest(step2);
-
-  function step2() {
-    TestRunner.deprecatedRunAfterPendingDispatches(step3);
-  }
-
-  function step3() {
-    var panel = UI.panels.sources;
-    var uiSourceCodes = Workspace.workspace.uiSourceCodesForProjectType(Workspace.projectTypes.Network);
-    var urls = uiSourceCodes.map(function(uiSourceCode) {
-      return uiSourceCode.name();
-    });
-    urls.sort();
-
-    var whiteList = [
-      'dynamic-script.js', 'dynamic-scripts.html', 'evalSourceURL.js', 'scriptElementContentSourceURL.js'
-    ];
-    function filter(url) {
-      for (var i = 0; i < whiteList.length; ++i) {
-        if (url.indexOf(whiteList[i]) !== -1)
-          return true;
-      }
-
-      return false;
-    }
-    urls = urls.filter(filter);
-
-    TestRunner.addResult('UISourceCodes:');
-    for (var i = 0; i < urls.length; ++i)
-      TestRunner.addResult('    ' + urls[i]);
-    SourcesTestRunner.completeDebuggerTest();
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/main-resource-content-expected.txt b/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/main-resource-content-expected.txt
deleted file mode 100644
index 2a38b660..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/main-resource-content-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Tests main resource content is correctly loaded and decoded using correct encoding.
-
-Requesting content: 
-Resource url: http://127.0.0.1:8000/devtools/startup/resource-tree/resources/main-resource-content-frame.html
-Resource content: <html>
-<body>
-SUCCESS
-</body>
-</html>
-
-Requesting utf8 content: 
-Resource url: http://127.0.0.1:8000/devtools/startup/resource-tree/resources/main-resource-content-frame-utf8.php
-Resource content: <html>
-<body>
-The following word is written using cyrillic letters and should look like "SUCCESS": SUССЕSS.
-</body>
-</html>
-
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/main-resource-content.js b/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/main-resource-content.js
deleted file mode 100644
index 75e2ad4..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/main-resource-content.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  await TestRunner.setupStartupTest('resources/main-resource-content.html');
-  TestRunner.addResult(`Tests main resource content is correctly loaded and decoded using correct encoding.\n`);
-  await TestRunner.loadModule('application_test_runner');
-
-  ApplicationTestRunner.runAfterResourcesAreFinished(
-      ['main-resource-content-frame-utf8.php', 'main-resource-content-frame.html'], step2);
-
-  async function step2() {
-    TestRunner.addResult('Requesting content: ');
-    var resource = ApplicationTestRunner.resourceMatchingURL('main-resource-content-frame.html');
-    var content = await TestRunner.PageAgent.getResourceContent(resource.frameId, resource.url);
-
-    TestRunner.assertTrue(!!content, 'No content available.');
-    TestRunner.addResult('Resource url: ' + resource.url);
-    TestRunner.addResult('Resource content: ' + content);
-
-    TestRunner.addResult('Requesting utf8 content: ');
-    resource = ApplicationTestRunner.resourceMatchingURL('main-resource-content-frame-utf8.php');
-    content = await TestRunner.PageAgent.getResourceContent(resource.frameId, resource.url);
-
-    TestRunner.assertTrue(!!content, 'No content available.');
-    TestRunner.addResult('Resource url: ' + resource.url);
-    TestRunner.addResult('Resource content: ' + content);
-    TestRunner.completeTest();
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resource-tree-mimetype-expected.txt b/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resource-tree-mimetype-expected.txt
deleted file mode 100644
index 9d8ac30..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resource-tree-mimetype-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-Tests that resources panel correctly shows mime type when it loads data from memory cache. https://bugs.webkit.org/show_bug.cgi?id=63701
-
-image text/html http://127.0.0.1:8000/devtools/devtools/resource-tree/resources/empty.png
-stylesheet text/html http://127.0.0.1:8000/devtools/devtools/resource-tree/resources/styles-initial.css
-document text/html http://127.0.0.1:8000/devtools/startup/resource-tree/resources/resource-tree-mimetype.html
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resource-tree-mimetype.js b/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resource-tree-mimetype.js
deleted file mode 100644
index 90e2508..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resource-tree-mimetype.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  await TestRunner.setupStartupTest('resources/resource-tree-mimetype.html');
-  TestRunner.addResult(
-      `Tests that resources panel correctly shows mime type when it loads data from memory cache. https://bugs.webkit.org/show_bug.cgi?id=63701\n`);
-  await TestRunner.loadModule('application_test_runner');
-
-  function format(resource) {
-    return resource.resourceType().name() + ' ' + resource.mimeType + ' ' + resource.url;
-  }
-
-  ApplicationTestRunner.dumpResources(format);
-  TestRunner.completeTest();
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/main-resource-content-frame-utf8.php b/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/main-resource-content-frame-utf8.php
deleted file mode 100644
index fa6ed63a..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/main-resource-content-frame-utf8.php
+++ /dev/null
@@ -1,8 +0,0 @@
-<?
-header('Content-Type: text/html; charset=utf-8');
-?>
-<html>
-<body>
-The following word is written using cyrillic letters and should look like "SUCCESS": SUССЕSS.
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/main-resource-content-frame.html b/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/main-resource-content-frame.html
deleted file mode 100644
index 8e47a386..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/main-resource-content-frame.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body>
-SUCCESS
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/main-resource-content.html b/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/main-resource-content.html
deleted file mode 100644
index 3545de5a..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/main-resource-content.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<body onload="testRunner.inspectSecondaryWindow()">
-<iframe src="main-resource-content-frame.html"></iframe>
-<iframe src="main-resource-content-frame-utf8.php"></iframe>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/resource-tree-mimetype.html b/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/resource-tree-mimetype.html
deleted file mode 100644
index e2cf16a..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/resource-tree/resources/resource-tree-mimetype.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<html>
-<head>
-<link rel="stylesheet" href="../../../devtools/resource-tree/resources/styles-initial.css">
-<script>
-function onload()
-{
-    // This test enumerates all resources currently registered, potentially
-    // including ones no longer reachable, but not yet garbage collected
-    // and finalized.
-    //
-    // To ensure stable test output, perform a GC right here to flush out
-    // the finalizable resources.
-    gc();
-    testRunner.inspectSecondaryWindow();
-}
-</script>
-</head>
-<body onload="onload()">
-<img src="../../../devtools/resource-tree/resources/empty.png">
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/resources/dynamic-script.js b/third_party/blink/web_tests/http/tests/devtools/startup/resources/dynamic-script.js
deleted file mode 100644
index fb9fb81..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/resources/dynamic-script.js
+++ /dev/null
@@ -1,5 +0,0 @@
-function fooDynamicScript()
-{
-}
-
-scriptLoaded();
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/resources/dynamic-scripts.html b/third_party/blink/web_tests/http/tests/devtools/startup/resources/dynamic-scripts.html
deleted file mode 100644
index 9d77cc89..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/resources/dynamic-scripts.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script>
-function appendDynamicScriptElement(src, content)
-{
-    var scriptElement = document.createElement("script");
-    if (src)
-        scriptElement.src = src;
-    else
-        scriptElement.textContent = content;
-    document.head.appendChild(scriptElement);
-}
-
-function loadScripts()
-{
-    var sourceURLComment = "\n //# sourceURL=";
-    window.eval("function fooEval() {}");
-    window.eval("function fooEvalSourceURL() {}" + sourceURLComment + "evalSourceURL.js");
-    appendDynamicScriptElement("", "function fooScriptElementContent1() {}");
-    appendDynamicScriptElement("", "function fooScriptElementContent2() {}");
-    appendDynamicScriptElement("", "function fooScriptElementContentSourceURL() {}" + sourceURLComment + "scriptElementContentSourceURL.js");
-    appendDynamicScriptElement("dynamic-script.js");
-}
-
-function scriptLoaded()
-{
-    testRunner.inspectSecondaryWindow();
-}
-</script>
-</head>
-<body onload="loadScripts()">
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/linkifier-expected.txt b/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/linkifier-expected.txt
deleted file mode 100644
index 469c415..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/linkifier-expected.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-Tests that Linkifier works correctly.
-
-listeners added on raw source code: 1
-original location: linkifier.html:9
-pretty printed location: linkifier.html:formatted:11
-pretty printed content:
-<html>
-    <head>
-        <script>
-            // It is important that script starts on line 5 (zero-based 4)
-            function nonFormattedFunction() {
-                var i = 2 + 2;
-                var a = 4;
-                return a + i;
-            }
-
-            function dummyScript() {
-                console.log("dummy string");
-            }
-
-            function onload() {
-                testRunner.inspectSecondaryWindow();
-            }
-        </script>
-    </head>
-    <body onload="onload()"></body>
-</html>
-
-reverted location: linkifier.html:9
-listeners removed from raw source code: 1
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/linkifier.js b/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/linkifier.js
deleted file mode 100644
index 937b1c5..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/linkifier.js
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  await TestRunner.setupStartupTest('resources/linkifier.html');
-  TestRunner.addResult(`Tests that Linkifier works correctly.\n`);
-  await TestRunner.loadModule('sources_test_runner');
-  await TestRunner.showPanel('sources');
-
-  var resourceURL = TestRunner.url('resources/linkifier.html');
-
-  var scriptFormatter;
-  var linkifier;
-  var link;
-  var script;
-  var uiSourceCode;
-
-  SourcesTestRunner.scriptFormatter().then(startDebuggerTest);
-
-  function startDebuggerTest(sf) {
-    scriptFormatter = sf;
-    SourcesTestRunner.startDebuggerTest(waitForScripts);
-  }
-
-  function waitForScripts() {
-    SourcesTestRunner.showScriptSource('linkifier.html', debuggerTest);
-  }
-
-  async function debuggerTest() {
-    for (var scriptCandidate of TestRunner.debuggerModel.scripts()) {
-      if (scriptCandidate.sourceURL === resourceURL && scriptCandidate.lineOffset === 4) {
-        script = scriptCandidate;
-        break;
-      }
-    }
-
-    uiSourceCode = Workspace.workspace.uiSourceCodeForURL(resourceURL);
-
-    linkifier = new Components.Linkifier();
-    var count1 = liveLocationsCount();
-    link = linkifier.linkifyScriptLocation(
-        SDK.targetManager.mainTarget(), null, resourceURL, 8, 0, 'dummy-class');
-    // The script location in the link is a live location that may be unresolved.
-    await TestRunner.waitForPendingLiveLocationUpdates();
-    var count2 = liveLocationsCount();
-
-    TestRunner.addResult('listeners added on raw source code: ' + (count2 - count1));
-    TestRunner.addResult('original location: ' + link.textContent);
-    TestRunner.addSniffer(Sources.ScriptFormatterEditorAction.prototype, '_updateButton', uiSourceCodeScriptFormatted);
-    scriptFormatter._toggleFormatScriptSource();
-  }
-
-  async function uiSourceCodeScriptFormatted() {
-    TestRunner.addResult('pretty printed location: ' + link.textContent);
-    var formattedContent = (await Formatter.sourceFormatter._formattedSourceCodes.get(uiSourceCode).formatData.formattedSourceCode.requestContent()).content;
-    TestRunner.addResult('pretty printed content:');
-    TestRunner.addResult(formattedContent);
-    await Formatter.sourceFormatter.discardFormattedUISourceCode(UI.panels.sources.visibleView.uiSourceCode());
-    await TestRunner.waitForPendingLiveLocationUpdates();
-    TestRunner.addResult('reverted location: ' + link.textContent);
-
-    var count1 = liveLocationsCount();
-    linkifier.reset();
-    var count2 = liveLocationsCount();
-
-    TestRunner.addResult('listeners removed from raw source code: ' + (count1 - count2));
-
-    SourcesTestRunner.completeDebuggerTest();
-  }
-
-  function liveLocationsCount() {
-    var modelData = Bindings.debuggerWorkspaceBinding._debuggerModelToData.get(script.debuggerModel);
-    var locations = modelData._locations.get(script);
-    return locations.size;
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/resources/linkifier.html b/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/resources/linkifier.html
deleted file mode 100644
index 024b7667..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/resources/linkifier.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<html>
-<head>
-
-
-<script>// It is important that script starts on line 5 (zero-based 4)
-function nonFormattedFunction() { var  i = 2 + 2; var a = 4; return a + i; }
-
-function dummyScript()
-{
-    console.log("dummy string");
-}
-
-function onload()
-{
-    testRunner.inspectSecondaryWindow();
-}
-</script>
-</head>
-<body onload="onload()"></body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/resources/script-formatter-breakpoints-inline-with-sourceURL.html b/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/resources/script-formatter-breakpoints-inline-with-sourceURL.html
deleted file mode 100644
index 15e45b7..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/resources/script-formatter-breakpoints-inline-with-sourceURL.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<html>
-<head>
-<script>
-    function functionInInlineScriptWithSourceURL() {
-
-
-      console.log("Hello!");
-
-      let x = 10;
-
-      return x;
-
-    }
-//# sourceURL=named-inline-script.js
-</script>
-</head>
-<body onload="testRunner.inspectSecondaryWindow()">
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-inline-with-sourceURL-expected.txt b/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-inline-with-sourceURL-expected.txt
deleted file mode 100644
index c05ee21..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-inline-with-sourceURL-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Tests the script formatting is working with breakpoints for inline scripts with #sourceURL=.
-
-
-Running: testSetup
-
-Running: testBreakpointsInOriginalAndFormattedSource
-Script execution paused.
-Breakpoint sidebar pane while paused in raw
-named-inline-script.js:5checked breakpoint hit      console.log("Hello!");
-Script execution resumed.
-Script execution paused.
-Breakpoint sidebar pane while paused in pretty printed
-named-inline-script.js:formatted:3checked breakpoint hit    console.log("Hello!");
-Breakpoint sidebar pane while paused after removing breakpoint in pretty printed and closing pretty printed
-No breakpoints
-Breakpoint sidebar pane while paused in original script again
-named-inline-script.js:5checked breakpoint hit      console.log("Hello!");
-Script execution resumed.
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-inline-with-sourceURL.js b/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-inline-with-sourceURL.js
deleted file mode 100644
index d63a2b38..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-inline-with-sourceURL.js
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  await TestRunner.setupStartupTest('resources/script-formatter-breakpoints-inline-with-sourceURL.html');
-  TestRunner.addResult(`Tests the script formatting is working with breakpoints for inline scripts with #sourceURL=.\n`);
-  await TestRunner.loadModule('sources_test_runner');
-  await TestRunner.showPanel('sources');
-
-  Bindings.breakpointManager._storage._breakpoints = new Map();
-  let panel = UI.panels.sources;
-  let scriptFormatter;
-  let sourceFrame;
-  let formattedSourceFrame;
-
-  SourcesTestRunner.runDebuggerTestSuite([
-    function testSetup(next) {
-      SourcesTestRunner.scriptFormatter().then(function(sf) {
-        scriptFormatter = sf;
-        next();
-      });
-    },
-
-    async function testBreakpointsInOriginalAndFormattedSource(next) {
-      SourcesTestRunner.showScriptSource('named-inline-script.js', didShowScriptSource);
-
-      async function didShowScriptSource(frame) {
-        sourceFrame = frame;
-        await SourcesTestRunner.setBreakpoint(sourceFrame, 4, '', true);  // Lines here are zero based.
-        Promise.all([SourcesTestRunner.waitBreakpointSidebarPane(true), SourcesTestRunner.waitUntilPausedPromise()])
-              .then(pausedInFunctionInInlineScriptWithSourceURL);
-        TestRunner.evaluateInPageWithTimeout('functionInInlineScriptWithSourceURL()');
-      }
-
-      function pausedInFunctionInInlineScriptWithSourceURL(callFrames) {
-        SourcesTestRunner.dumpBreakpointSidebarPane('while paused in raw');
-        SourcesTestRunner.resumeExecution(resumed);
-      }
-
-      function resumed() {
-        TestRunner.addSniffer(
-            Sources.ScriptFormatterEditorAction.prototype, '_updateButton', uiSourceCodeScriptFormatted);
-        scriptFormatter._toggleFormatScriptSource();
-      }
-
-      function uiSourceCodeScriptFormatted() {
-        // There should be a breakpoint in functionInInlineScriptWithSourceURL although script is pretty-printed.
-        Promise.all([SourcesTestRunner.waitBreakpointSidebarPane(true), SourcesTestRunner.waitUntilPausedPromise()])
-            .then(pausedInFunctionInInlineScriptWithSourceURLAgain);
-        TestRunner.evaluateInPageWithTimeout('functionInInlineScriptWithSourceURL()');
-      }
-
-      function pausedInFunctionInInlineScriptWithSourceURLAgain(callFrames) {
-        SourcesTestRunner.dumpBreakpointSidebarPane('while paused in pretty printed');
-        SourcesTestRunner.showScriptSource('named-inline-script.js:formatted', didShowFormattedScriptSource);
-      }
-
-      async function didShowFormattedScriptSource(frame) {
-        formattedSourceFrame = frame;
-        SourcesTestRunner.removeBreakpoint(formattedSourceFrame, 2);  // Lines here are zero based.
-        await Formatter.sourceFormatter.discardFormattedUISourceCode(formattedSourceFrame.uiSourceCode());
-        SourcesTestRunner.waitBreakpointSidebarPane().then(onBreakpointsUpdated);
-      }
-
-      async function onBreakpointsUpdated() {
-        SourcesTestRunner.dumpBreakpointSidebarPane('while paused after removing breakpoint in pretty printed and closing pretty printed');
-        await SourcesTestRunner.setBreakpoint(sourceFrame, 4, '', true);  // Lines here are zero based.
-        Promise.all([SourcesTestRunner.waitBreakpointSidebarPane(true), SourcesTestRunner.waitUntilPausedPromise()])
-          .then(pausedInFunctionInInlineScriptWithSourceURLThirdTime);
-        TestRunner.evaluateInPageWithTimeout('functionInInlineScriptWithSourceURL()');
-      }
-
-      function pausedInFunctionInInlineScriptWithSourceURLThirdTime() {
-        SourcesTestRunner.dumpBreakpointSidebarPane('while paused in original script again');
-        SourcesTestRunner.removeBreakpoint(sourceFrame, 4);  // Lines here are zero based.
-        SourcesTestRunner.resumeExecution(next);
-      }
-    }
-  ]);
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/user-metrics-perf-expected.txt b/third_party/blink/web_tests/http/tests/devtools/user-metrics-perf-expected.txt
deleted file mode 100644
index b17c0e959..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/user-metrics-perf-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Tests list of user metrics performance codes and invocations.
-
-recordPanelLoaded:
-Performance mark: DevTools.Launch.Console
-Performance histogram: DevTools.Launch.Console - positive duration?: true
-Performance mark: DevTools.Launch.Elements
-Performance histogram: DevTools.Launch.Elements - positive duration?: true
-Performance mark: DevTools.Launch.Network
-Performance histogram: DevTools.Launch.Network - positive duration?: true
-Performance mark: DevTools.Launch.Sources
-Performance histogram: DevTools.Launch.Sources - positive duration?: true
-
-Test that loading the tool only triggers the marker:
-Performance mark: DevTools.Launch.Console
-Performance mark: DevTools.Launch.Elements
-Performance mark: DevTools.Launch.Network
-Performance mark: DevTools.Launch.Sources
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/user-metrics-perf.js b/third_party/blink/web_tests/http/tests/devtools/user-metrics-perf.js
deleted file mode 100644
index f42057d2..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/user-metrics-perf.js
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  TestRunner.addResult(`Tests list of user metrics performance codes and invocations.\n`);
-
-  const testHistogramRecording = (panel, histogram, expectHistogram, testExecutor) => {
-    let recordHistogramComplete;
-    performance.mark = function(name) {
-      TestRunner.addResult(`Performance mark: ${name}`);
-      if (!expectHistogram)
-        recordHistogramComplete();
-    };
-    InspectorFrontendHost.recordPerformanceHistogram = function(name, duration) {
-      TestRunner.addResult(`Performance histogram: ${name} - positive duration?: ${duration > 0}`);
-      recordHistogramComplete();
-    };
-
-    return new Promise((resolve, reject) => {
-      Host.userMetrics._firedLaunchHistogram = undefined;
-      Host.userMetrics.setLaunchPanel(panel);
-      testExecutor(panel, histogram);
-      recordHistogramComplete = resolve;
-    });
-  };
-
-  const testPanelLoaded = (panel, histogram) =>
-      testHistogramRecording(panel, histogram, true, Host.userMetrics.panelLoaded.bind(Host.userMetrics));
-
-  const testShowView = (panel, histogram) =>
-      testHistogramRecording(panel, histogram, false, UI.viewManager.showView.bind(UI.viewManager));
-
-  TestRunner.addResult('recordPanelLoaded:');
-  await testPanelLoaded('console', 'DevTools.Launch.Console');
-  await testPanelLoaded('elements', 'DevTools.Launch.Elements');
-  await testPanelLoaded('network', 'DevTools.Launch.Network');
-  await testPanelLoaded('sources', 'DevTools.Launch.Sources');
-
-  TestRunner.addResult('\nTest that loading the tool only triggers the marker:');
-  await testShowView('console', 'DevTools.Launch.Console');
-  await testShowView('elements', 'DevTools.Launch.Elements');
-  await testShowView('network', 'DevTools.Launch.Network');
-  await testShowView('sources', 'DevTools.Launch.Sources');
-
-  TestRunner.completeTest();
-})();
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 3a92de1..0a8da78 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -5346,6 +5346,7 @@
 interface RTCPeerConnection : EventTarget
     static method generateCertificate
     attribute @@toStringTag
+    getter canTrickleIceCandidates
     getter connectionState
     getter currentLocalDescription
     getter currentRemoteDescription
@@ -9182,6 +9183,7 @@
 interface webkitRTCPeerConnection : EventTarget
     static method generateCertificate
     attribute @@toStringTag
+    getter canTrickleIceCandidates
     getter connectionState
     getter currentLocalDescription
     getter currentRemoteDescription
diff --git a/third_party/blink/web_tests/virtual/web-bluetooth-new-permissions-backend/README.md b/third_party/blink/web_tests/virtual/web-bluetooth-new-permissions-backend/README.md
new file mode 100644
index 0000000..dd5209b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/web-bluetooth-new-permissions-backend/README.md
@@ -0,0 +1,15 @@
+# Web Bluetooth New Permissions Backend
+
+This virtual test suite runs content_shell with
+`--enable-features=WebBluetoothNewPermissionsBackend`. This flag enables the
+Web Bluetooth tests to use the
+[`FakeBluetoothDelegate`](https://source.chromium.org/chromium/chromium/src/+/master:content/shell/browser/web_test/fake_bluetooth_delegate.h)
+interface for granting and checking permissions. This class emulates the
+behavior of the new Web Bluetooth permissions backend based on
+[`ChooserContextBase`](https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/permissions/chooser_context_base.h).
+
+The new permissions backend is implemented as part of the [Web Bluetooth
+Persistent Permissions project](https://docs.google.com/document/d/1h3uAVXJARHrNWaNACUPiQhLt7XI-fFFQoARSs1WgMDM).
+
+TODO(https://crbug.com/589228): Remove this virtual test suite when the
+`WebBluetoothNewPermissionsBackend` flag is enabled by default.
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/web-bluetooth-new-permissions-backend/bluetooth/README.txt b/third_party/blink/web_tests/virtual/web-bluetooth-new-permissions-backend/bluetooth/README.txt
new file mode 100644
index 0000000..a43806c
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/web-bluetooth-new-permissions-backend/bluetooth/README.txt
@@ -0,0 +1,4 @@
+This directory includes Web Bluetooth tests that use the [out of date Web
+Bluetooth test API](https://webbluetoothcg.github.io/web-bluetooth/tests.html)
+as well as some tests using the [redesigned test
+API](https://docs.google.com/document/d/1Nhv_oVDCodd1pEH_jj9k8gF4rPGb_84VYaZ9IG8M_WY).
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/README.txt b/third_party/blink/web_tests/virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/README.txt
new file mode 100644
index 0000000..c49c2d09
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/README.txt
@@ -0,0 +1,5 @@
+This directory includes Web Bluetooth tests that use the [redesigned Web
+Bluetooth test
+API](https://docs.google.com/document/d/1Nhv_oVDCodd1pEH_jj9k8gF4rPGb_84VYaZ9IG8M_WY)
+and are available in
+[web-platform-tests/wpt](https://github.com/web-platform-tests/wpt).
\ No newline at end of file
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 cca7abed..e5688dd 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
@@ -6336,6 +6336,7 @@
 interface RTCPeerConnection : EventTarget
     static method generateCertificate
     attribute @@toStringTag
+    getter canTrickleIceCandidates
     getter connectionState
     getter currentLocalDescription
     getter currentRemoteDescription
@@ -11447,6 +11448,7 @@
 interface webkitRTCPeerConnection : EventTarget
     static method generateCertificate
     attribute @@toStringTag
+    getter canTrickleIceCandidates
     getter connectionState
     getter currentLocalDescription
     getter currentRemoteDescription
diff --git a/tools/binary_size/diagnose_bloat.py b/tools/binary_size/diagnose_bloat.py
index cfb49ab..881399c 100755
--- a/tools/binary_size/diagnose_bloat.py
+++ b/tools/binary_size/diagnose_bloat.py
@@ -334,6 +334,9 @@
     gn_args += ' treat_warnings_as_errors=false'
     # Speed things up a bit by skipping lint & errorprone.
     gn_args += ' disable_android_lint=true'
+    # Down from default of 2 to speed up compile and use less disk.
+    # Compiles need at least symbol_level=1 for pak whitelist to work.
+    gn_args += ' symbol_level=1'
     gn_args += ' use_errorprone_java_compiler=false'
     gn_args += ' use_goma=%s' % str(self.use_goma).lower()
     gn_args += ' target_os="%s"' % self.target_os
diff --git a/tools/chrome_proxy/webdriver/https_previews.py b/tools/chrome_proxy/webdriver/https_previews.py
index d3de090..2cc99ae 100644
--- a/tools/chrome_proxy/webdriver/https_previews.py
+++ b/tools/chrome_proxy/webdriver/https_previews.py
@@ -22,7 +22,10 @@
 class HttpsPreviews(IntegrationTest):
 
   def EnableLitePageServerPreviewsAndInit(self, t):
-    t.EnableChromeFeature('Previews')
+    t.AddChromeArg('--force-fieldtrials=Previews/Enabled')
+    # Skip optimization guide models to trigger lite page redirect preview.
+    t.AddChromeArg('--force-fieldtrial-params='
+                   'Previews.Enabled:override_should_show_preview_check/true')
     t.EnableChromeFeature('LitePageServerPreviews')
 
     # RLH and NoScript may disable use of LitePageRedirect Previews.
@@ -34,6 +37,8 @@
     t.AddChromeArg('--ignore-previews-blacklist')
     t.AddChromeArg('--force-effective-connection-type=2G')
     t.AddChromeArg('--ignore-litepage-redirect-optimization-blacklist')
+    t.AddChromeArg('--litepage_redirect_overrides_page_hints')
+
     t.SetExperiment('external_chrome_integration_test')
 
     # Wait for the server probe to complete before starting the test, otherwise
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 3083dad..0c1c5029 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1310,6 +1310,7 @@
                vals.get('cros_passthrough', False))
     is_mac = self.platform == 'darwin'
     is_msan = 'is_msan=true' in vals['gn_args']
+    is_ios = 'target_os="ios"' in vals['gn_args']
 
     err = ''
     for f in files:
@@ -1319,6 +1320,11 @@
       if is_android:
         break
 
+      # iOS has generated directories in gn data items.
+      # Skipping for iOS instead of listing all apps.
+      if is_ios:
+        break
+
       # Skip a few existing violations that need to be cleaned up. Each of
       # these will lead to incorrect incremental builds if their directory
       # contents change. Do not add to this list.
diff --git a/tools/metrics/histograms/README.md b/tools/metrics/histograms/README.md
index 4ad9b88..2ab10eb 100644
--- a/tools/metrics/histograms/README.md
+++ b/tools/metrics/histograms/README.md
@@ -261,16 +261,20 @@
 You can also easily emit any ratio as a linear histogram (for equally
 sized buckets).
 
-For such histograms, you should think carefully about _when_ the values are
-emitted.  Normally, you should emit values periodically at a set time interval,
-such as every 5 minutes.  Conversely, we strongly discourage emitting values
-based on event triggers.  For example, we do not recommend recording a ratio
-at the end of a video playback.
+For such histograms, you want each value recorded to cover approximately
+the same span of time.  This typically means emitting values periodically
+at a set time interval, such as every 5 minutes.  We do not recommend
+recording a ratio at the end of a video playback, as lengths of videos
+vary greatly.
 
-Why?  You typically cannot make decisions based on histograms whose values are
-recorded in response to an event, because such metrics can conflate heavy usage
-with light usage.  It's easier to reason about metrics that route around this
-source of bias.
+It is okay to emit at the end of an animation sequence when what's being
+animated is fixed / known.  In this case, each value will represent
+roughly the same span of time.
+
+Why?  You typically cannot make decisions based on histograms whose
+values are recorded in response to an event that varies in length,
+because such metrics can conflate heavy usage with light usage.  It's
+easier to reason about metrics that route around this source of bias.
 
 Many developers have been bitten by this.  For example, it was previously common
 to emit an actions-per-minute ratio whenever Chrome was backgrounded.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 3b085c9..24eea7ae 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -8811,6 +8811,25 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Ash.NavigationWidget.AnimationSmoothness"
+    units="%" expires_after="M85">
+<!-- Name completed by histogram suffixes
+     name="HotseatTransitionType" -->
+
+<!-- Name completed by histogram suffixes
+     name="NavigationWidgetElement" -->
+
+  <owner>anasalazar@chromium.org</owner>
+  <owner>newcomer@chromium.org</owner>
+  <summary>
+    Relative smoothness of animations of the navigation widget's elements, which
+    are the widget itself and its child views. 100% represents ideally smooth 60
+    frames per second. 50% represents only 30 frames per second is achieved
+    during the animations. Recorded every time an animation is triggered in the
+    Navigation Widget. We record this metric for each element separately.
+  </summary>
+</histogram>
+
 <histogram name="Ash.NightLight.AutoNightLightNotificationShown"
     enum="BooleanShown" expires_after="2020-12-02">
   <owner>afakhry@chromium.org</owner>
@@ -17266,6 +17285,16 @@
   <summary>Image codec inferred during decode.</summary>
 </histogram>
 
+<histogram name="Blink.EffectiveZoom" units="%" expires_after="2021-03-01">
+  <owner>schenney@chromium.org</owner>
+  <owner>paint-dev@chromium.org</owner>
+  <summary>
+    The EffectiveZoom as reported by ComputedStyle in a Blink renderer. The
+    value is reported each time the effective zoom is set to a new value. The
+    maximum zoom reported is 400% to keep the histogram of reasonable size.
+  </summary>
+</histogram>
+
 <histogram name="Blink.EventListenerDuration.Resize" units="microseconds"
     expires_after="2018-01-17">
   <obsolete>
@@ -102846,9 +102875,6 @@
 
 <histogram name="OAuth2Login.PostMergeVerification"
     enum="PostMergeVerificationOutcome" expires_after="M78">
-  <obsolete>
-    Removed 2020-02-04
-  </obsolete>
   <owner>tbarzic@chromium.org</owner>
   <summary>
     Outcome of Chrome OS GAIA cookie post-merge session verification process. It
@@ -102859,9 +102885,6 @@
 
 <histogram name="OAuth2Login.PreMergeVerification"
     enum="PostMergeVerificationOutcome" expires_after="M78">
-  <obsolete>
-    Removed 2020-02-04
-  </obsolete>
   <owner>tbarzic@chromium.org</owner>
   <summary>
     Outcome of Chrome OS GAIA cookie pre-merge session verification process. It
@@ -139467,6 +139490,9 @@
 
 <histogram name="Scheduling.Renderer.MainAndImplFrameTimeDelta2"
     units="microseconds" expires_after="M81">
+  <obsolete>
+    This is no longer usefull and would not be replaced. as of 02/2020
+  </obsolete>
   <owner>vmiura@chromium.org</owner>
   <owner>sadrul@chromium.org</owner>
   <summary>
@@ -186242,6 +186268,7 @@
       label="Transition to the shown hotseat"/>
   <affected-histogram name="Ash.HotseatTransition.AnimationSmoothness"/>
   <affected-histogram name="Ash.HotseatWidgetAnimation.AnimationSmoothness"/>
+  <affected-histogram name="Ash.NavigationWidget.AnimationSmoothness"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="HstsState" separator=".">
@@ -188283,6 +188310,14 @@
   <affected-histogram name="Navigation.TimeToReadyToCommit2.Subframe"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="NavigationWidgetElement" separator="."
+    ordering="prefix,2">
+  <suffix name="BackButton" label="Navigation widget's back button"/>
+  <suffix name="HomeButton" label="Navigation widget's home button"/>
+  <suffix name="Widget" label="Navigation widget"/>
+  <affected-histogram name="Ash.NavigationWidget.AnimationSmoothness"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="Net.BidirectionalStreamExperiment" separator=".">
   <suffix name="HTTP2" label="Bidirectional stream that use HTTP2 protocol"/>
   <owner>mef@chromium.org</owner>
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index 6b561d9..5c5debbb 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -59,7 +59,7 @@
 system_health.common_mobile,"charliea@chromium.org, sullivan@chromium.org, tdresser@chromium.org, chrome-speed-metrics-dev@chromium.org",Speed>Metrics>SystemHealthRegressions,https://bit.ly/system-health-benchmarks,"2016,2018,2019,emerging_market,health_check,images,infinite_scroll,international,javascript_heavy"
 system_health.memory_desktop,"pasko@chromium.org, crouleau@chromium.org, chrome-android-perf-status@chromium.org",,https://bit.ly/system-health-benchmarks,"2016,2018,2019,2020,accessibility,emerging_market,health_check,images,infinite_scroll,international,javascript_heavy,keyboard_input,scroll,tabs_switching,webgl"
 system_health.memory_mobile,"pasko@chromium.org, crouleau@chromium.org, chrome-android-perf-status@chromium.org",,https://bit.ly/system-health-benchmarks,"2016,2018,2019,emerging_market,health_check,images,infinite_scroll,international,javascript_heavy"
-system_health.webview_startup,"oksamyt@chromium.org, torne@chromium.org, changwan@chromium.org",Mobile>WebView>Perf,,"2016,health_check"
+system_health.webview_startup,"oksamyt@chromium.org, torne@chromium.org, changwan@chromium.org",Mobile>WebView>Perf,,2016
 tab_switching.typical_25,vovoy@chromium.org,OS>Performance,,"2016,tabs_switching"
 tracing.tracing_with_background_memory_infra,ssid@chromium.org,,,
 tracing_perftests,"kkraynov@chromium.org, primiano@chromium.org",,,
diff --git a/tools/perf/benchmark_schedules.csv b/tools/perf/benchmark_schedules.csv
index c5ae105..4b1a856d 100644
--- a/tools/perf/benchmark_schedules.csv
+++ b/tools/perf/benchmark_schedules.csv
@@ -3,8 +3,8 @@
 benchmark name,total device usage hours per cycle,platforms count (unabridged),platforms count (abridged),platforms where unabridged,platforms where abridged
 rendering.mobile,8.67,3,0,"Android Nexus5X WebView Perf, android-pixel2-perf, android-pixel2_webview-perf",
 rendering.desktop,6.89,5,1,"linux-perf, mac-10_12_laptop_low_end-perf, mac-10_13_laptop_high_end-perf, win-10-perf, win-10_laptop_low_end-perf",Win 7 Nvidia GPU Perf
-system_health.common_mobile,5.00,5,2,"Android Nexus5X WebView Perf, android-go-perf, android-go_webview-perf, android-pixel2-perf, android-pixel2_webview-perf","Android Nexus5 Perf, android-pixel2_weblayer-perf"
-system_health.memory_mobile,4.71,5,1,"Android Nexus5X WebView Perf, android-go-perf, android-go_webview-perf, android-pixel2-perf, android-pixel2_webview-perf",android-pixel2_weblayer-perf
+system_health.common_mobile,4.63,5,2,"Android Nexus5X WebView Perf, android-go-perf, android-go_webview-perf, android-pixel2-perf, android-pixel2_webview-perf","Android Nexus5 Perf, android-pixel2_weblayer-perf"
+system_health.memory_mobile,4.55,5,1,"Android Nexus5X WebView Perf, android-go-perf, android-go_webview-perf, android-pixel2-perf, android-pixel2_webview-perf",android-pixel2_weblayer-perf
 v8.browsing_mobile,3.87,5,0,"Android Nexus5X WebView Perf, android-go-perf, android-go_webview-perf, android-pixel2-perf, android-pixel2_webview-perf",
 system_health.common_desktop,3.23,5,0,"linux-perf, mac-10_12_laptop_low_end-perf, mac-10_13_laptop_high_end-perf, win-10-perf, win-10_laptop_low_end-perf",
 system_health.memory_desktop,2.66,5,0,"linux-perf, mac-10_12_laptop_low_end-perf, mac-10_13_laptop_high_end-perf, win-10-perf, win-10_laptop_low_end-perf",
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel2_weblayer-perf_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel2_weblayer-perf_timing.json
index 9a3883d..785059c 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel2_weblayer-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel2_weblayer-perf_timing.json
@@ -16,187 +16,27 @@
         "name": "startup.mobile/maps_pwa:with_http_cache"
     },
     {
-        "duration": "23.0",
-        "name": "system_health.common_mobile/background:media:imgur:2019"
-    },
-    {
-        "duration": "21.0",
-        "name": "system_health.common_mobile/background:search:google:2019"
-    },
-    {
         "duration": "3.0",
         "name": "system_health.common_mobile/background:tools:gmail:2019"
     },
     {
-        "duration": "54.0",
-        "name": "system_health.common_mobile/browse:media:youtube:2019"
-    },
-    {
         "duration": "49.0",
         "name": "system_health.common_mobile/browse:news:cnn:2018"
     },
     {
-        "duration": "35.0",
-        "name": "system_health.common_mobile/browse:news:qq:2019"
-    },
-    {
-        "duration": "49.0",
-        "name": "system_health.common_mobile/browse:shopping:avito:2019"
-    },
-    {
-        "duration": "20.0",
-        "name": "system_health.common_mobile/load:chrome:blank"
-    },
-    {
-        "duration": "19.0",
-        "name": "system_health.common_mobile/load:games:bubbles:2019"
-    },
-    {
-        "duration": "20.0",
-        "name": "system_health.common_mobile/load:games:lazors"
-    },
-    {
-        "duration": "25.0",
-        "name": "system_health.common_mobile/load:games:spychase:2018"
-    },
-    {
-        "duration": "20.0",
-        "name": "system_health.common_mobile/load:media:google_images:2018"
-    },
-    {
-        "duration": "22.0",
-        "name": "system_health.common_mobile/load:media:imgur:2018"
-    },
-    {
         "duration": "20.0",
         "name": "system_health.common_mobile/load:media:youtube:2018"
     },
     {
-        "duration": "21.0",
-        "name": "system_health.common_mobile/load:news:irctc:2019"
-    },
-    {
-        "duration": "26.0",
-        "name": "system_health.common_mobile/load:news:nytimes:2019"
-    },
-    {
-        "duration": "20.0",
-        "name": "system_health.common_mobile/load:news:reddit:2019"
-    },
-    {
-        "duration": "20.0",
-        "name": "system_health.common_mobile/load:news:washingtonpost:2019"
-    },
-    {
-        "duration": "21.0",
-        "name": "system_health.common_mobile/load:search:baidu:2018"
-    },
-    {
-        "duration": "20.0",
-        "name": "system_health.common_mobile/load:search:ebay:2018"
-    },
-    {
-        "duration": "20.0",
-        "name": "system_health.common_mobile/load:search:taobao:2019"
-    },
-    {
-        "duration": "19.0",
-        "name": "system_health.common_mobile/load:tools:docs:2019"
-    },
-    {
-        "duration": "20.0",
-        "name": "system_health.common_mobile/load:tools:stackoverflow:2018"
-    },
-    {
-        "duration": "28.0",
-        "name": "system_health.memory_mobile/background:media:imgur:2019"
-    },
-    {
-        "duration": "26.0",
-        "name": "system_health.memory_mobile/background:search:google:2019"
-    },
-    {
         "duration": "0.0",
         "name": "system_health.memory_mobile/background:tools:gmail:2019"
     },
     {
-        "duration": "57.0",
-        "name": "system_health.memory_mobile/browse:media:youtube:2019"
-    },
-    {
         "duration": "52.0",
         "name": "system_health.memory_mobile/browse:news:cnn:2018"
     },
     {
-        "duration": "39.0",
-        "name": "system_health.memory_mobile/browse:news:qq:2019"
-    },
-    {
-        "duration": "52.0",
-        "name": "system_health.memory_mobile/browse:shopping:avito:2019"
-    },
-    {
-        "duration": "24.0",
-        "name": "system_health.memory_mobile/load:chrome:blank"
-    },
-    {
-        "duration": "24.0",
-        "name": "system_health.memory_mobile/load:games:bubbles:2019"
-    },
-    {
-        "duration": "25.0",
-        "name": "system_health.memory_mobile/load:games:lazors"
-    },
-    {
-        "duration": "28.0",
-        "name": "system_health.memory_mobile/load:games:spychase:2018"
-    },
-    {
-        "duration": "24.0",
-        "name": "system_health.memory_mobile/load:media:google_images:2018"
-    },
-    {
-        "duration": "26.0",
-        "name": "system_health.memory_mobile/load:media:imgur:2018"
-    },
-    {
         "duration": "24.0",
         "name": "system_health.memory_mobile/load:media:youtube:2018"
-    },
-    {
-        "duration": "25.0",
-        "name": "system_health.memory_mobile/load:news:irctc:2019"
-    },
-    {
-        "duration": "29.0",
-        "name": "system_health.memory_mobile/load:news:nytimes:2019"
-    },
-    {
-        "duration": "25.0",
-        "name": "system_health.memory_mobile/load:news:reddit:2019"
-    },
-    {
-        "duration": "24.0",
-        "name": "system_health.memory_mobile/load:news:washingtonpost:2019"
-    },
-    {
-        "duration": "26.0",
-        "name": "system_health.memory_mobile/load:search:baidu:2018"
-    },
-    {
-        "duration": "25.0",
-        "name": "system_health.memory_mobile/load:search:ebay:2018"
-    },
-    {
-        "duration": "24.0",
-        "name": "system_health.memory_mobile/load:search:taobao:2019"
-    },
-    {
-        "duration": "24.0",
-        "name": "system_health.memory_mobile/load:tools:docs:2019"
-    },
-    {
-        "duration": "24.0",
-        "name": "system_health.memory_mobile/load:tools:stackoverflow:2018"
     }
 ]
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/timing_data/android_nexus5_perf_timing.json b/tools/perf/core/shard_maps/timing_data/android_nexus5_perf_timing.json
index 4a45949..f87bd05 100644
--- a/tools/perf/core/shard_maps/timing_data/android_nexus5_perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android_nexus5_perf_timing.json
@@ -40,98 +40,18 @@
         "name": "startup.mobile/maps_pwa:with_http_cache"
     },
     {
-        "duration": "43.0",
-        "name": "system_health.common_mobile/background:media:imgur:2019"
-    },
-    {
-        "duration": "42.0",
-        "name": "system_health.common_mobile/background:search:google:2019"
-    },
-    {
         "duration": "52.0",
         "name": "system_health.common_mobile/background:tools:gmail:2019"
     },
     {
-        "duration": "77.0",
-        "name": "system_health.common_mobile/browse:media:youtube:2019"
-    },
-    {
         "duration": "135.0",
         "name": "system_health.common_mobile/browse:news:cnn:2018"
     },
     {
-        "duration": "55.0",
-        "name": "system_health.common_mobile/browse:news:qq:2019"
-    },
-    {
-        "duration": "76.0",
-        "name": "system_health.common_mobile/browse:shopping:avito:2019"
-    },
-    {
-        "duration": "33.0",
-        "name": "system_health.common_mobile/load:chrome:blank"
-    },
-    {
-        "duration": "31.0",
-        "name": "system_health.common_mobile/load:games:bubbles:2019"
-    },
-    {
-        "duration": "34.0",
-        "name": "system_health.common_mobile/load:games:lazors"
-    },
-    {
-        "duration": "39.0",
-        "name": "system_health.common_mobile/load:games:spychase:2018"
-    },
-    {
-        "duration": "32.0",
-        "name": "system_health.common_mobile/load:media:google_images:2018"
-    },
-    {
-        "duration": "39.0",
-        "name": "system_health.common_mobile/load:media:imgur:2018"
-    },
-    {
         "duration": "32.0",
         "name": "system_health.common_mobile/load:media:youtube:2018"
     },
     {
-        "duration": "41.0",
-        "name": "system_health.common_mobile/load:news:irctc:2019"
-    },
-    {
-        "duration": "58.0",
-        "name": "system_health.common_mobile/load:news:nytimes:2019"
-    },
-    {
-        "duration": "34.0",
-        "name": "system_health.common_mobile/load:news:reddit:2019"
-    },
-    {
-        "duration": "32.0",
-        "name": "system_health.common_mobile/load:news:washingtonpost:2019"
-    },
-    {
-        "duration": "42.0",
-        "name": "system_health.common_mobile/load:search:baidu:2018"
-    },
-    {
-        "duration": "33.0",
-        "name": "system_health.common_mobile/load:search:ebay:2018"
-    },
-    {
-        "duration": "30.0",
-        "name": "system_health.common_mobile/load:search:taobao:2019"
-    },
-    {
-        "duration": "30.0",
-        "name": "system_health.common_mobile/load:tools:docs:2019"
-    },
-    {
-        "duration": "32.0",
-        "name": "system_health.common_mobile/load:tools:stackoverflow:2018"
-    },
-    {
         "duration": "110.0",
         "name": "components_perftests/_gtest_"
     },
diff --git a/tools/perf/cycletime_contributions.csv b/tools/perf/cycletime_contributions.csv
index 1fa803c..65dc4fd 100644
--- a/tools/perf/cycletime_contributions.csv
+++ b/tools/perf/cycletime_contributions.csv
@@ -2,7 +2,7 @@
 View a prettier version of this at,https://docs.google.com/spreadsheets/d/15pJY4cxtM2NVNFKQDgDnoT5PLo0Nm5Td-Ov-5PZefAw
 platform,Android Nexus5 Perf,Android Nexus5X WebView Perf,Win 7 Nvidia GPU Perf,Win 7 Perf,android-go-perf,android-go_webview-perf,android-pixel2-perf,android-pixel2_weblayer-perf,android-pixel2_webview-perf,linux-perf,mac-10_12_laptop_low_end-perf,mac-10_13_laptop_high_end-perf,win-10-perf,win-10_laptop_low_end-perf
 shards,16,16,4,4,19,13,35,4,21,26,26,26,26,26
-idealized cycle time (hours),0.07,0.47,0.31,0.03,0.40,0.16,0.76,0.11,0.28,0.31,0.44,0.35,0.44,0.71
+idealized cycle time (hours),0.04,0.47,0.31,0.03,0.40,0.16,0.76,0.03,0.28,0.31,0.44,0.35,0.44,0.71
 angle_perftests,0.000,0.000,0.276,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.042,0.000
 base_perftests,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.006,0.000,0.006,0.006,0.000
 blink_perf.accessibility,0.000,0.001,0.000,0.000,0.000,0.000,0.001,0.000,0.001,0.001,0.001,0.001,0.001,0.001
@@ -49,9 +49,9 @@
 speedometer2-future,0.000,0.002,0.000,0.000,0.000,0.000,0.001,0.000,0.001,0.001,0.002,0.002,0.001,0.003
 startup.mobile,0.019,0.000,0.000,0.000,0.018,0.000,0.059,0.022,0.000,0.000,0.000,0.000,0.000,0.000
 system_health.common_desktop,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.042,0.052,0.042,0.045,0.067
-system_health.common_mobile,0.037,0.052,0.000,0.000,0.130,0.078,0.050,0.039,0.028,0.000,0.000,0.000,0.000,0.000
+system_health.common_mobile,0.008,0.052,0.000,0.000,0.130,0.078,0.050,0.005,0.028,0.000,0.000,0.000,0.000,0.000
 system_health.memory_desktop,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.030,0.044,0.036,0.040,0.055
-system_health.memory_mobile,0.000,0.048,0.000,0.000,0.129,0.074,0.052,0.045,0.031,0.000,0.000,0.000,0.000,0.000
+system_health.memory_mobile,0.000,0.048,0.000,0.000,0.129,0.074,0.052,0.005,0.031,0.000,0.000,0.000,0.000,0.000
 system_health.webview_startup,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000
 tab_switching.typical_25,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000
 tracing.tracing_with_background_memory_infra,0.000,0.002,0.000,0.000,0.000,0.000,0.002,0.000,0.001,0.002,0.003,0.002,0.003,0.004
diff --git a/tools/perf/page_sets/system_health/background_stories.py b/tools/perf/page_sets/system_health/background_stories.py
index 8d351aac..13dedb6 100644
--- a/tools/perf/page_sets/system_health/background_stories.py
+++ b/tools/perf/page_sets/system_health/background_stories.py
@@ -31,7 +31,7 @@
 class BackgroundGoogleStory2019(_BackgroundStory):
   NAME = 'background:search:google:2019'
   URL = 'https://www.google.co.uk/#q=tom+cruise+movies'
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2019]
+  TAGS = [story_tags.YEAR_2019]
 
   def _DidLoadDocument(self, action_runner):
     # Activte the immersive movie browsing experience
@@ -64,7 +64,7 @@
   NAME = 'background:media:imgur:2019'
   URL = 'http://imgur.com/gallery/hUita'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2019]
+  TAGS = [story_tags.YEAR_2019]
 
 
 class BackgroundGmailMobileStory2019(loading_stories.LoadGmailStory2019):
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index 8b18004..82195ae80 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -133,8 +133,9 @@
   URL = 'http://edition.cnn.com/'
   ITEM_SELECTOR = '.cd__content > h3 > a'
   ITEMS_TO_VISIT = 2
-  TAGS = [story_tags.JAVASCRIPT_HEAVY, story_tags.HEALTH_CHECK,
-          story_tags.YEAR_2018]
+  TAGS = [
+      story_tags.HEALTH_CHECK, story_tags.JAVASCRIPT_HEAVY, story_tags.YEAR_2018
+  ]
 
 
 class FacebookMobileStory2019(_ArticleBrowsingStory):
@@ -241,8 +242,7 @@
   ITEMS_TO_VISIT = 2
 
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.INTERNATIONAL, story_tags.HEALTH_CHECK,
-          story_tags.YEAR_2019]
+  TAGS = [story_tags.INTERNATIONAL, story_tags.YEAR_2019]
 
 
 class RedditDesktopStory2018(_ArticleBrowsingStory):
@@ -570,8 +570,10 @@
   IS_SINGLE_PAGE_APP = True
   ITEM_SELECTOR_INDEX = 3
   ITEMS_TO_VISIT = 8
-  TAGS = [story_tags.JAVASCRIPT_HEAVY, story_tags.EMERGING_MARKET,
-          story_tags.HEALTH_CHECK, story_tags.YEAR_2019]
+  TAGS = [
+      story_tags.JAVASCRIPT_HEAVY, story_tags.EMERGING_MARKET,
+      story_tags.YEAR_2019
+  ]
 
 
 class YouTubeDesktopStory2019(_MediaBrowsingStory):
@@ -854,8 +856,7 @@
   NAME = 'browse:shopping:avito:2019'
   URL = 'https://www.avito.ru/rossiya'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.EMERGING_MARKET, story_tags.HEALTH_CHECK,
-          story_tags.YEAR_2019]
+  TAGS = [story_tags.EMERGING_MARKET, story_tags.YEAR_2019]
 
   ITEM_SELECTOR = '._3eXe2'
   ITEMS_TO_VISIT = 4
diff --git a/tools/perf/page_sets/system_health/chrome_stories.py b/tools/perf/page_sets/system_health/chrome_stories.py
index 8adc534..2596a42 100644
--- a/tools/perf/page_sets/system_health/chrome_stories.py
+++ b/tools/perf/page_sets/system_health/chrome_stories.py
@@ -13,7 +13,7 @@
   """Story that loads the about:blank page."""
   NAME = 'load:chrome:blank'
   URL = 'about:blank'
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2016]
+  TAGS = [story_tags.YEAR_2016]
 
   def _DidLoadDocument(self, action_runner):
     # Request a RAF and wait for it to be processed to ensure that the metric
diff --git a/tools/perf/page_sets/system_health/loading_stories.py b/tools/perf/page_sets/system_health/loading_stories.py
index 73e864f1..1e6cde0 100644
--- a/tools/perf/page_sets/system_health/loading_stories.py
+++ b/tools/perf/page_sets/system_health/loading_stories.py
@@ -46,8 +46,7 @@
 class LoadBaiduStory2018(_LoadingStory):
   NAME = 'load:search:baidu:2018'
   URL = 'https://www.baidu.com/s?word=google'
-  TAGS = [story_tags.INTERNATIONAL, story_tags.HEALTH_CHECK,
-          story_tags.YEAR_2018]
+  TAGS = [story_tags.INTERNATIONAL, story_tags.YEAR_2018]
 
 
 class LoadYahooStory2018(_LoadingStory):
@@ -89,8 +88,7 @@
   NAME = 'load:search:taobao:2019'
   URL = 'http://m.intl.taobao.com/'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.INTERNATIONAL, story_tags.HEALTH_CHECK,
-          story_tags.YEAR_2019]
+  TAGS = [story_tags.INTERNATIONAL, story_tags.YEAR_2019]
 
 
 class LoadYandexStory2018(_LoadingStory):
@@ -102,7 +100,7 @@
 class LoadEbayStory2018(_LoadingStory):
   NAME = 'load:search:ebay:2018'
   URL = 'https://www.ebay.com/sch/i.html?_nkw=headphones'
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2018]
+  TAGS = [story_tags.YEAR_2018]
 
 
 ################################################################################
@@ -201,7 +199,7 @@
   NAME = 'load:news:nytimes:2019'
   URL = 'http://mobile.nytimes.com'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2019]
+  TAGS = [story_tags.YEAR_2019]
 
 
 class LoadQqMobileStory2019(_LoadingStory):
@@ -229,14 +227,14 @@
   NAME = 'load:news:reddit:2019'
   URL = 'https://www.reddit.com/r/news/top/?sort=top&t=week'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2019]
+  TAGS = [story_tags.YEAR_2019]
 
 
 class LoadWashingtonPostMobileStory2019(_LoadingStory):
   NAME = 'load:news:washingtonpost:2019'
   URL = 'https://www.washingtonpost.com/pwa'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2019]
+  TAGS = [story_tags.YEAR_2019]
   _CONTINUE_FREE_BUTTON_SELECTOR = '.continue-btn.button.free'
   _ACCEPT_GDPR_SELECTOR = '.agree-ckb'
   _CONTINUE_TO_SITE_SELECTOR = '.continue-btn.button.accept-consent'
@@ -269,8 +267,7 @@
   NAME = 'load:news:irctc:2019'
   URL = 'https://www.irctc.co.in'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.EMERGING_MARKET, story_tags.HEALTH_CHECK,
-          story_tags.YEAR_2019]
+  TAGS = [story_tags.EMERGING_MARKET, story_tags.YEAR_2019]
 
   def _Login(self, action_runner):
     # There is an error on replay that pops up the first time. If we
@@ -288,8 +285,9 @@
   # No way to disable autoplay on desktop.
   NAME = 'load:media:youtube:2018'
   URL = 'https://www.youtube.com/watch?v=QGfhS1hfTWw&autoplay=false'
-  TAGS = [story_tags.EMERGING_MARKET, story_tags.HEALTH_CHECK,
-          story_tags.YEAR_2018]
+  TAGS = [
+      story_tags.HEALTH_CHECK, story_tags.EMERGING_MARKET, story_tags.YEAR_2018
+  ]
 
 
 class LoadDailymotionStory2019(_LoadingStory):
@@ -301,7 +299,7 @@
 class LoadGoogleImagesStory2018(_LoadingStory):
   NAME = 'load:media:google_images:2018'
   URL = 'https://www.google.co.uk/search?tbm=isch&q=love'
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2018]
+  TAGS = [story_tags.YEAR_2018]
 
 
 class LoadSoundCloudStory2018(_LoadingStory):
@@ -321,7 +319,7 @@
 class LoadImgurStory2018(_LoadingStory):
   NAME = 'load:media:imgur:2018'
   URL = 'http://imgur.com/gallery/5UlBN'
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2018]
+  TAGS = [story_tags.YEAR_2018]
 
 
 class LoadFlickrStory2018(_LoadingStory):
@@ -358,7 +356,7 @@
   NAME = 'load:tools:docs:2019'
   URL = (
       'https://docs.google.com/document/d/1GvzDP-tTLmJ0myRhUAfTYWs3ZUFilUICg8psNHyccwQ/edit?usp=sharing')
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2019]
+  TAGS = [story_tags.YEAR_2019]
 
 
 class _LoadGmailBaseStory(_LoadingStory):
@@ -422,7 +420,7 @@
   NAME = 'load:tools:stackoverflow:2018'
   URL = (
       'https://stackoverflow.com/questions/36827659/compiling-an-application-for-use-in-highly-radioactive-environments')
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2018]
+  TAGS = [story_tags.YEAR_2018]
 
 
 class LoadDropboxStory2019(_LoadingStory):
@@ -464,21 +462,21 @@
   NAME = 'load:games:bubbles:2019'
   URL = (
       'https://games.cdn.famobi.com/html5games/s/smarty-bubbles/v010/?fg_domain=play.famobi.com&fg_uid=d8f24956-dc91-4902-9096-a46cb1353b6f&fg_pid=4638e320-4444-4514-81c4-d80a8c662371&fg_beat=620')
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2019]
+  TAGS = [story_tags.YEAR_2019]
 
 
 class LoadLazorsStory(_LoadingStory):
   NAME = 'load:games:lazors'
   # Using "https://" hangs and shows "This site can't be reached".
   URL = 'http://www8.games.mobi/games/html5/lazors/lazors.html'
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2016]
+  TAGS = [story_tags.YEAR_2016]
 
 
 class LoadSpyChaseStory2018(_LoadingStory):
   NAME = 'load:games:spychase:2018'
   # Using "https://" shows "Your connection is not private".
   URL = 'http://playstar.mobi/games/spychase/index.php'
-  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2018]
+  TAGS = [story_tags.YEAR_2018]
 
   def _DidLoadDocument(self, action_runner):
     # The background of the game canvas is set when the "Tap screen to play"
diff --git a/tools/perf/system_health_stories.csv b/tools/perf/system_health_stories.csv
index b880f96..1024a46 100644
--- a/tools/perf/system_health_stories.csv
+++ b/tools/perf/system_health_stories.csv
@@ -1,9 +1,9 @@
 AUTOGENERATED FILE DO NOT EDIT
 See //tools/perf/core/perf_data_generator.py to make changes
 Story,Description,Platforms,Tags
-background:media:imgur:2019,,mobile,"2019,health_check"
+background:media:imgur:2019,,mobile,2019
 background:news:nytimes:2019,,mobile,"2019,javascript_heavy"
-background:search:google:2019,,mobile,"2019,health_check"
+background:search:google:2019,,mobile,2019
 background:social:facebook:2019,,mobile,"2019,health_check"
 background:tools:gmail:2019,,mobile,"2019,health_check"
 browse:chrome:newtab:2019,Story that loads new tab page and performs searches.,mobile,"2019,emerging_market"
@@ -16,7 +16,7 @@
 browse:media:imgur:2019,,mobile,"2019,emerging_market"
 browse:media:pinterest:2018,,desktop,2018
 browse:media:tumblr:2018,,desktop,2018
-browse:media:youtube:2019,Load a typical YouTube video then navigate to a next few videos. Stop and,"desktop,mobile","2019,emerging_market,health_check,javascript_heavy"
+browse:media:youtube:2019,Load a typical YouTube video then navigate to a next few videos. Stop and,"desktop,mobile","2019,emerging_market,javascript_heavy"
 browse:media:youtubetv:2019,Load a typical YouTube TV video then navigate to a next few videos. Stop,desktop,2019
 browse:news:cnn:2018,The second top website in http://www.alexa.com/topsites/category/News,"desktop,mobile","2018,health_check,javascript_heavy"
 browse:news:cricbuzz:2019,,mobile,"2019,emerging_market"
@@ -25,7 +25,7 @@
 browse:news:hackernews:2018,,desktop,2018
 browse:news:nytimes:2018,The third top website in http://www.alexa.com/topsites/category/News,desktop,2018
 browse:news:nytimes:2019,The third top website in http://www.alexa.com/topsites/category/News,mobile,2019
-browse:news:qq:2019,,mobile,"2019,health_check,international"
+browse:news:qq:2019,,mobile,"2019,international"
 browse:news:reddit:2018,The top website in http://www.alexa.com/topsites/category/News,desktop,2018
 browse:news:reddit:2019,,mobile,"2019,health_check"
 browse:news:toi:2019,,mobile,"2019,emerging_market"
@@ -35,7 +35,7 @@
 browse:search:google:2018,A typical google search story:,desktop,2018
 browse:search:google_india:2018,A typical google search story in India:,desktop,"2018,international"
 browse:shopping:amazon:2019,,mobile,"2019,emerging_market"
-browse:shopping:avito:2019,,mobile,"2019,emerging_market,health_check"
+browse:shopping:avito:2019,,mobile,"2019,emerging_market"
 browse:shopping:flipkart:2019,,mobile,"2019,emerging_market"
 browse:shopping:lazada:2019,,mobile,"2019,emerging_market"
 browse:social:facebook:2019,,mobile,"2019,emerging_market"
@@ -52,19 +52,19 @@
 browse:tools:sheets:2019,,desktop,"2019,health_check,javascript_heavy"
 browse_accessibility:media:youtube,Tests interacting with the YouTube home page.,desktop,"2016,accessibility,keyboard_input"
 browse_accessibility:tech:codesearch:2018,Tests scrolling an element within a page.,desktop,"2018,accessibility,scroll"
-load:chrome:blank,Story that loads the about:blank page.,"desktop,mobile","2016,health_check"
+load:chrome:blank,Story that loads the about:blank page.,"desktop,mobile",2016
 load:games:alphabetty:2018,,desktop,2018
-load:games:bubbles:2019,"Load ""smarty bubbles"" game on famobi.com","desktop,mobile","2019,health_check"
-load:games:lazors,,"desktop,mobile","2016,health_check"
+load:games:bubbles:2019,"Load ""smarty bubbles"" game on famobi.com","desktop,mobile",2019
+load:games:lazors,,"desktop,mobile",2016
 load:games:miniclip:2018,,desktop,2018
-load:games:spychase:2018,,"desktop,mobile","2018,health_check"
+load:games:spychase:2018,,"desktop,mobile",2018
 load:media:9gag,,desktop,2016
 load:media:dailymotion:2019,,"desktop,mobile",2019
 load:media:facebook_photos:2018,Load a page of rihanna's facebook with a photo.,desktop,2018
 load:media:facebook_photos:2019,Load a page of rihanna's facebook with a photo.,mobile,"2019,emerging_market"
 load:media:flickr:2018,,"desktop,mobile",2018
-load:media:google_images:2018,,"desktop,mobile","2018,health_check"
-load:media:imgur:2018,,"desktop,mobile","2018,health_check"
+load:media:google_images:2018,,"desktop,mobile",2018
+load:media:imgur:2018,,"desktop,mobile",2018
 load:media:soundcloud:2018,,"desktop,mobile",2018
 load:media:youtube:2018,,"desktop,mobile","2018,emerging_market,health_check"
 load:news:bbc:2018,,desktop,2018
@@ -72,23 +72,23 @@
 load:news:cnn:2018,,"desktop,mobile","2018,health_check,javascript_heavy"
 load:news:flipboard,,desktop,2016
 load:news:hackernews:2018,,desktop,2018
-load:news:irctc:2019,,mobile,"2019,emerging_market,health_check"
+load:news:irctc:2019,,mobile,"2019,emerging_market"
 load:news:nytimes:2018,,desktop,2018
-load:news:nytimes:2019,,mobile,"2019,health_check"
+load:news:nytimes:2019,,mobile,2019
 load:news:qq:2018,,desktop,"2018,international"
 load:news:qq:2019,,mobile,"2019,international"
 load:news:reddit:2018,,desktop,2018
-load:news:reddit:2019,,mobile,"2019,health_check"
-load:news:washingtonpost:2019,,mobile,"2019,health_check"
+load:news:reddit:2019,,mobile,2019
+load:news:washingtonpost:2019,,mobile,2019
 load:news:wikipedia:2018,,"desktop,mobile","2018,emerging_market"
 load:search:amazon:2018,,desktop,2018
 load:search:amazon:2019,,mobile,2019
-load:search:baidu:2018,,"desktop,mobile","2018,health_check,international"
-load:search:ebay:2018,,"desktop,mobile","2018,health_check"
+load:search:baidu:2018,,"desktop,mobile","2018,international"
+load:search:ebay:2018,,"desktop,mobile",2018
 load:search:flipkart:2018,,desktop,"2018,international"
 load:search:google:2018,,"desktop,mobile",2018
 load:search:taobao:2018,,desktop,"2018,international"
-load:search:taobao:2019,,mobile,"2019,health_check,international"
+load:search:taobao:2019,,mobile,"2019,international"
 load:search:yahoo:2018,,"desktop,mobile",2018
 load:search:yandex:2018,,"desktop,mobile","2018,international"
 load:social:instagram:2018,,desktop,"2018,health_check"
@@ -97,11 +97,11 @@
 load:social:twitter:2019,,mobile,2019
 load:social:vk:2018,,desktop,"2018,health_check,international"
 load:tools:chat:2020,,desktop,2020
-load:tools:docs:2019,Load a typical google doc page (2019).,"desktop,mobile","2019,health_check"
+load:tools:docs:2019,Load a typical google doc page (2019).,"desktop,mobile",2019
 load:tools:drive:2019,,"desktop,mobile","2019,javascript_heavy"
 load:tools:dropbox:2019,,mobile,2019
 load:tools:gmail:2019,,"desktop,mobile","2019,health_check"
-load:tools:stackoverflow:2018,Load a typical question & answer page of stackoverflow.com,"desktop,mobile","2018,health_check"
+load:tools:stackoverflow:2018,Load a typical question & answer page of stackoverflow.com,"desktop,mobile",2018
 load:tools:weather:2019,,"desktop,mobile","2019,health_check,javascript_heavy"
 load_accessibility:media:wikipedia:2018,"Wikipedia page on Accessibility. Long, but very simple, clean layout.",desktop,"2018,accessibility"
 load_accessibility:shopping:amazon:2018,Amazon results page. Good example of a site with a data table.,desktop,"2018,accessibility"
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index 23db1a1..0956f07 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -181,9 +181,6 @@
     : client_(client),
       input_handler_(input_handler),
       synchronous_input_handler_(nullptr),
-#if DCHECK_IS_ON()
-      expect_scroll_update_end_(false),
-#endif
       gesture_scroll_on_impl_thread_(false),
       scroll_sequence_ignored_(false),
       touch_result_(kEventDispositionUndefined),
@@ -810,9 +807,6 @@
   if (scroll_predictor_)
     scroll_predictor_->ResetOnGestureScrollBegin(gesture_event);
 
-#if DCHECK_IS_ON()
-  expect_scroll_update_end_ = true;
-#endif
   cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event);
   cc::InputHandler::ScrollStatus scroll_status;
   cc::ElementIdType element_id_type =
@@ -876,10 +870,6 @@
                -gesture_event.data.scroll_update.delta_x, "dy",
                -gesture_event.data.scroll_update.delta_y);
 
-#if DCHECK_IS_ON()
-  DCHECK(expect_scroll_update_end_);
-#endif
-
   if (scroll_sequence_ignored_) {
     TRACE_EVENT_INSTANT0("input", "Scroll Sequence Ignored",
                          TRACE_EVENT_SCOPE_THREAD);
@@ -898,9 +888,6 @@
 
   if (snap_fling_controller_->HandleGestureScrollUpdate(
           GetGestureScrollUpdateInfo(gesture_event))) {
-#if DCHECK_IS_ON()
-    expect_scroll_update_end_ = false;
-#endif
     gesture_scroll_on_impl_thread_ = false;
     return DROP_EVENT;
   }
@@ -938,11 +925,6 @@
 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd(
     const WebGestureEvent& gesture_event) {
   TRACE_EVENT0("input", "InputHandlerProxy::HandleGestureScrollEnd");
-#if DCHECK_IS_ON()
-  DCHECK(expect_scroll_update_end_);
-  expect_scroll_update_end_ = false;
-#endif
-
   if (scroll_sequence_ignored_)
     return DROP_EVENT;
 
diff --git a/ui/events/blink/input_handler_proxy.h b/ui/events/blink/input_handler_proxy.h
index 8944072..585ed7028 100644
--- a/ui/events/blink/input_handler_proxy.h
+++ b/ui/events/blink/input_handler_proxy.h
@@ -187,9 +187,6 @@
 
   SynchronousInputHandler* synchronous_input_handler_;
 
-#if DCHECK_IS_ON()
-  bool expect_scroll_update_end_;
-#endif
   bool gesture_scroll_on_impl_thread_;
   bool gesture_pinch_in_progress_ = false;
   bool in_inertial_scrolling_ = false;
diff --git a/ui/gfx/transform_util.cc b/ui/gfx/transform_util.cc
index 97d58b5..4f2cd39 100644
--- a/ui/gfx/transform_util.cc
+++ b/ui/gfx/transform_util.cc
@@ -576,7 +576,7 @@
 }
 
 Transform TransformBetweenRects(const RectF& src, const RectF& dst) {
-  DCHECK(!src.IsEmpty() && !dst.IsEmpty());
+  DCHECK(!src.IsEmpty());
   Transform result;
   result.Translate(dst.origin() - src.origin());
   result.Scale(dst.width() / src.width(), dst.height() / src.height());
diff --git a/ui/gfx/transform_util_unittest.cc b/ui/gfx/transform_util_unittest.cc
index 66b6e2f4..5a524d9 100644
--- a/ui/gfx/transform_util_unittest.cc
+++ b/ui/gfx/transform_util_unittest.cc
@@ -343,6 +343,9 @@
     verify(test_case.first, test_case.second);
     verify(test_case.second, test_case.first);
   }
+
+  // Tests the case where the destination is an empty rectangle.
+  verify(RectF(0.f, 0.f, 3.f, 5.f), RectF());
 }
 
 }  // namespace
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc
index 8fb4f241..5049f5bb 100644
--- a/ui/native_theme/native_theme.cc
+++ b/ui/native_theme/native_theme.cc
@@ -119,7 +119,6 @@
 bool NativeTheme::UpdateSystemColorInfo(
     bool is_dark_mode,
     bool is_high_contrast,
-    PreferredColorScheme preferred_color_scheme,
     const base::flat_map<SystemThemeColor, uint32_t>& colors) {
   bool did_system_color_info_change = false;
   if (is_dark_mode != ShouldUseDarkColors()) {
@@ -130,10 +129,6 @@
     did_system_color_info_change = true;
     set_high_contrast(is_high_contrast);
   }
-  if (preferred_color_scheme != GetPreferredColorScheme()) {
-    did_system_color_info_change = true;
-    set_preferred_color_scheme(preferred_color_scheme);
-  }
   for (const auto& color : colors) {
     if (color.second != GetSystemThemeColor(color.first)) {
       did_system_color_info_change = true;
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index 832585d..374bf1f 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -525,7 +525,6 @@
   bool UpdateSystemColorInfo(
       bool is_dark_mode,
       bool is_high_contrast,
-      PreferredColorScheme preferred_color_scheme,
       const base::flat_map<SystemThemeColor, uint32_t>& colors);
 
  protected:
diff --git a/ui/views/animation/bounds_animator.cc b/ui/views/animation/bounds_animator.cc
index 87fd3ccb..9959a35 100644
--- a/ui/views/animation/bounds_animator.cc
+++ b/ui/views/animation/bounds_animator.cc
@@ -59,7 +59,7 @@
   data.animation = CreateAnimation();
   data.delegate = std::move(delegate);
 
-  if (use_transforms_) {
+  if (use_transforms_ && !data.start_bounds.IsEmpty()) {
     // Calculate the target transform. Note that we don't reset the transform if
     // there already was one, otherwise users will end up with visual bounds
     // different than what they set.
@@ -222,7 +222,7 @@
   // Save the data for later clean up.
   Data data = RemoveFromMaps(view);
 
-  if (use_transforms_) {
+  if (data.target_transform) {
     // Set the bounds at the end of the animation and reset the transform.
     view->SetTransform(gfx::Transform());
     if (type == AnimationEndType::kEnded)
@@ -248,8 +248,7 @@
   DCHECK(view);
   const Data& data = data_[view];
 
-  if (use_transforms_) {
-    DCHECK(data.target_transform);
+  if (data.target_transform) {
     const gfx::Transform current_transform = gfx::Tween::TransformValueBetween(
         animation->GetCurrentValue(), gfx::Transform(), *data.target_transform);
     view->SetTransform(current_transform);
@@ -282,7 +281,6 @@
 void BoundsAnimator::AnimationContainerProgressed(
     gfx::AnimationContainer* container) {
   if (!repaint_bounds_.IsEmpty()) {
-    DCHECK(!use_transforms_);
     // Adjust for rtl.
     repaint_bounds_.set_x(parent_->GetMirroredXWithWidthInView(
         repaint_bounds_.x(), repaint_bounds_.width()));
diff --git a/ui/views/animation/bounds_animator.h b/ui/views/animation/bounds_animator.h
index 4a624b1..620145bc 100644
--- a/ui/views/animation/bounds_animator.h
+++ b/ui/views/animation/bounds_animator.h
@@ -176,7 +176,9 @@
   // transform of the views and therefore skips repainting and relayouting until
   // the end of the animation. Note that this may not look as good as the
   // regular version, depending on the content and the source and destination
-  // bounds.
+  // bounds. In the case the provided source bounds is empty, we cannot derive a
+  // transform so that particular view will still use a bounds animation, even
+  // with this flag on.
   const bool use_transforms_;
 
   base::ObserverList<BoundsAnimatorObserver>::Unchecked observers_;
diff --git a/ui/views/animation/bounds_animator_unittest.cc b/ui/views/animation/bounds_animator_unittest.cc
index 22896c8..790c747 100644
--- a/ui/views/animation/bounds_animator_unittest.cc
+++ b/ui/views/animation/bounds_animator_unittest.cc
@@ -244,4 +244,28 @@
   EXPECT_EQ(repaint_count, child()->repaint_count());
 }
 
+// Tests that the transforms option does not crash when a view's bounds start
+// off empty.
+TEST_F(BoundsAnimatorTest, UseTransformsAnimateViewToEmptySrc) {
+  RecreateAnimator(/*use_transforms=*/true);
+
+  gfx::Rect initial_bounds(0, 0, 0, 0);
+  child()->SetBoundsRect(initial_bounds);
+  gfx::Rect target_bounds(10, 10, 20, 20);
+
+  child()->set_repaint_count(0);
+  animator()->AnimateViewTo(child(), target_bounds);
+  animator()->SetAnimationDelegate(child(),
+                                   std::make_unique<TestAnimationDelegate>());
+
+  // The animator should be animating now.
+  EXPECT_TRUE(animator()->IsAnimating());
+  EXPECT_TRUE(animator()->IsAnimating(child()));
+
+  // Run the message loop; the delegate exits the loop when the animation is
+  // done.
+  base::RunLoop().Run();
+  EXPECT_EQ(target_bounds, child()->bounds());
+}
+
 }  // namespace views
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
index 34fc414..b82f4431 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
@@ -149,7 +149,8 @@
         --cr-input-input: {
           font-size: 28px;
         };
-        --cr-input-letter-spacing: var(--cr-input-letter-spacing, 18px);
+        --cr-input-letter-spacing: var(--pin-keyboard-input-letter-spacing,
+                                       18px);
         --cr-input-padding-end: 0;
         --cr-input-padding-start: 0;
         background-color: white;
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java b/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java
index a51e0e55..435c18e 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java
@@ -265,6 +265,7 @@
         mLastHeight = getHeight();
         TopControlsContainerViewJni.get().setTopControlsSize(mNativeTopControlsContainerView,
                 TopControlsContainerView.this, mLastWidth, mLastHeight);
+        setTopControlsOffset(0, mLastHeight);
     }
 
     private void finishTopControlsScroll(int topContentOffsetY) {