diff --git a/AUTHORS b/AUTHORS
index 60b619ca..ec18c04f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -418,6 +418,7 @@
 Jihoon Chung <j.c@navercorp.com>
 Jihoon Chung <jihoon@gmail.com>
 Jihun Brent Kim <devgrapher@gmail.com>
+Jihwan Marc Kim <bluewhale.marc@gmail.com>
 Jin Yang <jin.a.yang@intel.com>
 Jincheol Jo <jincheol.jo@navercorp.com>
 Jingwei Liu <kingweiliu@gmail.com>
diff --git a/DEPS b/DEPS
index c90f66a..b9b6ba5 100644
--- a/DEPS
+++ b/DEPS
@@ -112,6 +112,10 @@
   # luci-go CIPD package version.
   'luci_go': 'git_revision:25958d48e89e980e2a97daeddc977fb5e2e1fb8c',
 
+  # This can be overridden, e.g. with custom_vars, to build clang from HEAD
+  # instead of downloading the prebuilt pinned revision.
+  'llvm_force_head_revision': False,
+
   'android_git': 'https://android.googlesource.com',
   'aomedia_git': 'https://aomedia.googlesource.com',
   'boringssl_git': 'https://boringssl.googlesource.com',
@@ -129,11 +133,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': 'bc94e79eb7b6c0f370101012199c4753a336cc18',
+  'skia_revision': 'd425dee662d15a074da981c2fa85d38dc5fd094c',
   # 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': '0a278d064c61f6f482db698cd2c8096850991247',
+  'v8_revision': 'bb661bbc49a6e50abdf968530a0472ee8a421eb5',
   # 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.
@@ -145,7 +149,7 @@
   # 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': '28f142f1b0a2ec18304ec2f11518f72747ac6d11',
+  'swiftshader_revision': 'f149d95a5e270ccc184a97a4552b8e923ee6adca',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -196,11 +200,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'd235eb23657332468da6ceeb78789a4bd693fda6',
+  'catapult_revision': '72493839e6fd214dca98a9afd0605c01a7a20e05',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
-  'libfuzzer_revision': 'e847d8a9b47158695593d5693b0f69250472b229',
+  'libfuzzer_revision': 'ff7e2bdf49a44e8d713c9d0cda5c0bd8d981dad3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-node-modules
   # and whatever else without interference from each other.
@@ -268,7 +272,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '9286adcb0f7e46b1cb628315cdfb75b0f6a1afd6',
+  'dawn_revision': 'bbed4577964d1f3c7ba1412a1515fbc7fa084a4c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -392,7 +396,7 @@
     'packages': [
       {
         'package': 'chromium/chrome/test/data/autofill/captured_sites',
-        'version': 'lNNtoOkHdBZvXPqRgJQqIj7fk0ytOnVGRCYYawadahAC',
+        'version': 'x-MlaiKZT7T-YaM3_ZEyIqsILkx9IvGpvrZHmXq0EiEC',
       }
     ],
     'condition': 'checkout_chromium_autofill_test_dependencies',
@@ -1183,7 +1187,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'dfd2f8c4fcb0dcab4ddcb873aa37cc71084c184f',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'c501481303f64664680d953367f5adf5dc8048c0',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1354,7 +1358,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'e670fd97951a967283f74ff9b313dc16aa19e7af',
+    Var('webrtc_git') + '/src.git' + '@' + '7cca042dd409c08a2a6e2383c39c692626f8a2d9',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1395,7 +1399,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c0a0741eb59828931b200197ef026715d30d7957',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ca79ffa2937d80c9c79049f77562d5bd886893c6',
     'condition': 'checkout_src_internal',
   },
 
@@ -2394,10 +2398,22 @@
     ],
   },
   {
+    # Update the prebuilt clang toolchain.
     # Note: On Win, this should run after win_toolchain, as it may use it.
     'name': 'clang',
     'pattern': '.',
-    'action': ['python', 'src/tools/clang/scripts/update.py', '--with-android={checkout_android}'],
+    'condition': 'not llvm_force_head_revision',
+    'action': ['python', 'src/tools/clang/scripts/update.py'],
+  },
+  {
+    # Build the clang toolchain from tip-of-tree.
+    # Note: On Win, this should run after win_toolchain, as it may use it.
+    'name': 'clang_tot',
+    'pattern': '.',
+    'condition': 'llvm_force_head_revision',
+    'action': ['python', 'src/tools/clang/scripts/update.py',
+               '--llvm-force-head-revision',
+               '--with-android={checkout_android}'],
   },
   {
     # This is supposed to support the same set of platforms as 'clang' above.
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 6a28dfd..6783ba18 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -718,6 +718,16 @@
       True,
       (),
     ),
+    (
+      'SHFileOperation',
+      (
+        'SHFileOperation was deprecated in Windows Vista, and there are less ',
+        'complex functions to achieve the same goals. Use IFileOperation for ',
+        'any esoteric actions instead.'
+      ),
+      True,
+      (),
+    ),
 )
 
 
diff --git a/WATCHLISTS b/WATCHLISTS
index e3f7f9a..394a3d0 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -224,8 +224,8 @@
     'base_scheduler': {
       'filepath': '^base/task/',
     },
-    'base_task_scheduler': {
-      'filepath': '^base/task/task_scheduler',
+    'base_thread_pool': {
+      'filepath': '^base/task/thread_pool',
     },
     'base_win': {
       'filepath': '^base/win',
@@ -1952,9 +1952,9 @@
                        'wfh+watch@chromium.org'],
     'base_memory': ['gavinp+memory@chromium.org'],
     'base_scheduler': ['scheduler-bugs+base@chromium.org'],
-    'base_task_scheduler': ['fdoray+watch@chromium.org',
-                            'gab+watch@chromium.org',
-                            'robliao+watch@chromium.org'],
+    'base_thread_pool': ['fdoray+watch@chromium.org',
+                         'gab+watch@chromium.org',
+                         'robliao+watch@chromium.org'],
     'base_win': ['grt+watch@chromium.org',
                  'robliao+watch@chromium.org',
                  'wfh+watch@chromium.org'],
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index c0e10dd0..9e4e6d5 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -1118,5 +1118,23 @@
     apk_name = "TrichromeWebView"
     use_trichrome_library = true
     uncompress_dex = use_uncompressed_dex
+    if (trichrome_synchronized_proguard) {
+      static_library_provider = "//chrome/android:trichrome_library_apk"
+    }
+  }
+
+  # This target should be removed once Trichrome only supports Chrome bundle
+  # builds.
+  system_webview_apk_tmpl("trichrome_webview_for_bundle_apk") {
+    android_manifest = trichrome_webview_android_manifest
+    android_manifest_dep = ":trichrome_webview_manifest"
+    deps = upstream_only_webview_deps
+    apk_name = "TrichromeWebViewForBundle"
+    use_trichrome_library = true
+    uncompress_dex = use_uncompressed_dex
+    if (trichrome_synchronized_proguard) {
+      static_library_provider =
+          "//chrome/android:trichrome_library_for_bundle_apk"
+    }
   }
 }
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni
index e4e8995..4df323b 100644
--- a/android_webview/system_webview_apk_tmpl.gni
+++ b/android_webview/system_webview_apk_tmpl.gni
@@ -64,9 +64,6 @@
       uncompress_shared_libraries = true
 
       app_as_shared_lib = true
-      if (trichrome_synchronized_proguard) {
-        emit_resource_ids = true
-      }
 
       # Include placeholder libraries to ensure we are treated as the desired
       # architecture.
diff --git a/ash/autoclick/autoclick_controller.cc b/ash/autoclick/autoclick_controller.cc
index 0e46ddc..b6dab61 100644
--- a/ash/autoclick/autoclick_controller.cc
+++ b/ash/autoclick/autoclick_controller.cc
@@ -183,9 +183,7 @@
 }
 
 void AutoclickController::DoAutoclickAction() {
-  // The gesture_anchor_location_ is the position at which the animation is
-  // anchored, and where the click should occur.
-  aura::Window* root_window = wm::GetRootWindowAt(gesture_anchor_location_);
+  aura::Window* root_window = wm::GetRootWindowAt(anchor_location_);
   DCHECK(root_window) << "Root window not found while attempting autoclick.";
 
   // But if the thing that would be acted upon is an autoclick menu button, do a
@@ -193,9 +191,8 @@
   // ensures that no matter the autoclick setting, users can always change to
   // another autoclick setting. By using a fake click we avoid closing dialogs
   // and menus, allowing autoclick users to interact with those items.
-  if (!DragInProgress() &&
-      AutoclickMenuContainsPoint(gesture_anchor_location_)) {
-    menu_bubble_controller_->ClickOnBubble(gesture_anchor_location_,
+  if (!DragInProgress() && AutoclickMenuContainsPoint(anchor_location_)) {
+    menu_bubble_controller_->ClickOnBubble(anchor_location_,
                                            mouse_event_flags_);
     // Reset UI.
     CancelAutoclickAction();
@@ -207,7 +204,7 @@
   mojom::AutoclickEventType in_progress_event_type = event_type_;
   RecordUserAction(in_progress_event_type);
 
-  gfx::Point location_in_pixels(gesture_anchor_location_);
+  gfx::Point location_in_pixels(anchor_location_);
   ::wm::ConvertPointFromScreen(root_window, &location_in_pixels);
   aura::WindowTreeHost* host = root_window->GetHost();
   host->ConvertDIPToPixels(&location_in_pixels);
@@ -286,9 +283,7 @@
     }
     // Otherwise, go ahead and start the gesture.
   }
-  // The anchor is always the point in the screen where the timer starts, and is
-  // used to determine when the cursor has moved far enough to cancel the
-  // autoclick.
+  // The anchor is always the point in the screen where the timer starts.
   anchor_location_ = gesture_anchor_location_;
   autoclick_ring_handler_->StartGesture(
       delay_ - CalculateStartGestureDelay(delay_), anchor_location_,
@@ -433,7 +428,7 @@
     } else if (autoclick_timer_->IsRunning() && !stabilize_click_position_) {
       // If we are not stabilizing the click position, update the gesture
       // center with each mouse move event.
-      gesture_anchor_location_ = point_in_screen;
+      anchor_location_ = point_in_screen;
       autoclick_ring_handler_->SetGestureCenter(point_in_screen, widget_.get());
     }
   } else if (event->type() == ui::ET_MOUSE_PRESSED ||
diff --git a/ash/autoclick/autoclick_unittest.cc b/ash/autoclick/autoclick_unittest.cc
index 3428638..508186e 100644
--- a/ash/autoclick/autoclick_unittest.cc
+++ b/ash/autoclick/autoclick_unittest.cc
@@ -232,20 +232,9 @@
   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
   EXPECT_EQ(2u, root_windows.size());
 
-  int animation_delay = 5;
-
-  const struct {
-    int movement_threshold;
-    bool stabilize_click_position;
-  } kTestCases[] = {
-      {10, false}, {20, false}, {30, false}, {40, false}, {50, false},
-      {10, true},  {20, true},  {30, true},  {40, true},  {50, true},
-  };
-
-  for (const auto& test : kTestCases) {
-    GetAutoclickController()->set_stabilize_click_position(
-        test.stabilize_click_position);
-    int movement_threshold = test.movement_threshold;
+  // Try at a couple different thresholds.
+  for (int movement_threshold = 10; movement_threshold < 50;
+       movement_threshold += 10) {
     GetAutoclickController()->SetMovementThreshold(movement_threshold);
 
     // Run test for the secondary display too to test fix for crbug.com/449870.
@@ -254,7 +243,6 @@
 
       GetAutoclickController()->SetEnabled(true);
       GetEventGenerator()->MoveMouseTo(center);
-      ClearMouseEvents();
       EXPECT_EQ(2u, WaitForMouseEvents().size());
 
       // Small mouse movements should not trigger an autoclick, i.e. movements
@@ -277,28 +265,11 @@
           center +
           gfx::Vector2d(movement_threshold + 1, movement_threshold + 1));
       EXPECT_EQ(2u, WaitForMouseEvents().size());
-
-      // Moving outside the threshold after the gesture begins should cancel
-      // the autoclick. Update the delay so we can do events between the initial
-      // trigger of the feature and the click.
-      int full_delay = UpdateAnimationDelayAndGetFullDelay(animation_delay);
-      GetEventGenerator()->MoveMouseTo(
-          center - gfx::Vector2d(movement_threshold, movement_threshold));
-      FastForwardBy(animation_delay + 1);
-      GetEventGenerator()->MoveMouseTo(center);
-      ClearMouseEvents();
-      FastForwardBy(full_delay);
-      EXPECT_EQ(0u, GetMouseEvents().size());
-
-      // Move it out of the way so the next cycle starts properly.
-      GetEventGenerator()->MoveMouseTo(gfx::Point(0, 0));
-      GetAutoclickController()->SetAutoclickDelay(base::TimeDelta());
     }
   }
 
-  // Reset to defaults.
+  // Reset to default threshold.
   GetAutoclickController()->SetMovementThreshold(20);
-  GetAutoclickController()->set_stabilize_click_position(false);
 }
 
 TEST_F(AutoclickTest, MovementWithinThresholdWhileTimerRunning) {
diff --git a/ash/system/message_center/message_center_style.h b/ash/system/message_center/message_center_style.h
index cc1c45d..2b4d6a47 100644
--- a/ash/system/message_center/message_center_style.h
+++ b/ash/system/message_center/message_center_style.h
@@ -29,10 +29,6 @@
 constexpr SkColor kSwipeControlBackgroundColor =
     SkColorSetRGB(0xee, 0xee, 0xee);
 
-// The ratio to multiply with the swipe control width to get the width to
-// display at full opacity when swiping.
-constexpr float kSwipeControlFullOpacityRatio = 1.5f;
-
 }  // namespace message_center_style
 
 }  // namespace ash
diff --git a/ash/system/message_center/notification_swipe_control_view.cc b/ash/system/message_center/notification_swipe_control_view.cc
index f00022b..519626b 100644
--- a/ash/system/message_center/notification_swipe_control_view.cc
+++ b/ash/system/message_center/notification_swipe_control_view.cc
@@ -88,19 +88,6 @@
       message_center_style::kSwipeControlButtonHorizontalMargin *
           (control_button_count + 1);
   message_view_->SetSlideButtonWidth(control_button_width);
-
-  // Update opacity based on the swipe progress. The swipe controls should
-  // gradually disappear as the user swipes the notification away.
-  float full_opacity_width =
-      message_center_style::kSwipeControlFullOpacityRatio *
-      control_button_width;
-  float fade_out_width = message_view_->width() - full_opacity_width;
-  DCHECK(fade_out_width > 0);
-  float swipe_progress = std::max(
-      0.0f, (fabs(gesture_amount) - full_opacity_width) / fade_out_width);
-  float opacity = std::max(0.0f, 1.0f - swipe_progress);
-
-  layer()->SetOpacity(opacity);
 }
 
 void NotificationSwipeControlView::UpdateCornerRadius(int top_radius,
diff --git a/ash/system/message_center/unified_message_list_view.cc b/ash/system/message_center/unified_message_list_view.cc
index 098eee2..0431c12 100644
--- a/ash/system/message_center/unified_message_list_view.cc
+++ b/ash/system/message_center/unified_message_list_view.cc
@@ -157,10 +157,6 @@
     control_view_->UpdateButtonsVisibility();
   }
 
-  void OnSlideOut(const std::string& notification_id) override {
-    is_slid_out_by_user_ = true;
-  }
-
   gfx::Rect start_bounds() const { return start_bounds_; }
   gfx::Rect ideal_bounds() const { return ideal_bounds_; }
   bool is_removed() const { return is_removed_; }
@@ -175,8 +171,6 @@
 
   void set_is_removed() { is_removed_ = true; }
 
-  bool is_slid_out_by_user() { return is_slid_out_by_user_; }
-
  private:
   // The bounds that the container starts animating from. If not animating, it's
   // ignored.
@@ -190,9 +184,6 @@
   // Unused if |state_| is not SLIDE_OUT.
   bool is_removed_ = false;
 
-  // True if the notification is slid out completely by the user.
-  bool is_slid_out_by_user_ = false;
-
   MessageView* const message_view_;
   NotificationSwipeControlView* const control_view_;
 
@@ -343,12 +334,9 @@
   if (child)
     child->set_is_removed();
 
-  if (child->is_slid_out_by_user())
-    DeleteRemovedNotifications();
-
   UpdateBounds();
 
-  state_ = child->is_slid_out_by_user() ? State::MOVE_DOWN : State::SLIDE_OUT;
+  state_ = State::SLIDE_OUT;
   StartAnimation();
 }
 
diff --git a/ash/system/message_center/unified_message_list_view_unittest.cc b/ash/system/message_center/unified_message_list_view_unittest.cc
index 1d7c582..84655b3 100644
--- a/ash/system/message_center/unified_message_list_view_unittest.cc
+++ b/ash/system/message_center/unified_message_list_view_unittest.cc
@@ -512,29 +512,4 @@
   EXPECT_EQ(1u, message_list_view()->children().size());
 }
 
-TEST_F(UnifiedMessageListViewTest, UserSwipesAwayNotification) {
-  // Show message list with two notifications.
-  AddNotification();
-  AddNotification();
-  CreateMessageListView();
-
-  // Start swiping the notification away.
-  GetMessageViewAt(1)->OnSlideStarted();
-  GetMessageViewAt(1)->OnSlideChanged(true);
-  EXPECT_EQ(2u, MessageCenter::Get()->GetVisibleNotifications().size());
-  EXPECT_EQ(2u, message_list_view()->children().size());
-
-  // Swiping away the notification should remove it both in the MessageCenter
-  // and the MessageListView.
-  GetMessageViewAt(1)->OnSlideOut();
-  EXPECT_EQ(1u, MessageCenter::Get()->GetVisibleNotifications().size());
-  EXPECT_EQ(1u, message_list_view()->children().size());
-
-  // The next and only animation should be the move down animation.
-  int previous_height = message_list_view()->GetPreferredSize().height();
-  AnimateToEnd();
-  EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
-  EXPECT_FALSE(message_list_view()->IsAnimating());
-}
-
 }  // namespace ash
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index afcf04d..8ed3481f 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -163,7 +163,9 @@
 MessageLoop::MessageLoop(Type type, std::unique_ptr<MessagePump> custom_pump)
     : sequence_manager_(
           sequence_manager::internal::SequenceManagerImpl::CreateUnbound(
-              sequence_manager::SequenceManager::Settings{type})),
+              sequence_manager::SequenceManager::Settings::Builder()
+                  .SetMessageLoopType(type)
+                  .Build())),
       default_task_queue_(CreateDefaultTaskQueue()),
       type_(type),
       custom_pump_(std::move(custom_pump)) {
diff --git a/base/message_loop/message_pump.h b/base/message_loop/message_pump.h
index 76c0ed2e..17de885 100644
--- a/base/message_loop/message_pump.h
+++ b/base/message_loop/message_pump.h
@@ -59,8 +59,8 @@
     // |!delayed_run_time.is_max()|; and it will not be invoked again until
     // ScheduleWork() otherwise. Redundant/spurious invocations outside of those
     // guarantees are not impossible however. DoIdleWork() will not be called so
-    // long as this returns a null |delayed_run_time|. See design doc for
-    // details :
+    // long as this returns a NextWorkInfo which is_immediate(). See design doc
+    // for details :
     // https://docs.google.com/document/d/1no1JMli6F1r8gTF9KDIOvoWkUUZcXDktPf4A1IXYc3M/edit#
     virtual NextWorkInfo DoSomeWork() = 0;
 
@@ -158,12 +158,16 @@
   // only be used on the thread that called Run.
   virtual void Quit() = 0;
 
-  // Schedule a DoWork callback to happen reasonably soon.  Does nothing if a
-  // DoWork callback is already scheduled. Once this call is made, DoWork should
-  // not be "starved" at least until it returns a value of false. Thread-safe
-  // (and callers should avoid holding a Lock at all cost while making this call
-  // as some platforms' priority boosting features have been observed to cause
-  // the caller to get descheduled : https://crbug.com/890978).
+  // Schedule a DoSomeWork callback to happen reasonably soon.  Does nothing if
+  // a DoSomeWork callback is already scheduled. Once this call is made,
+  // DoSomeWork is guaranteed to be called repeatedly at least until it returns
+  // a non-immediate NextWorkInfo (or, if this pump wasn't yet migrated,
+  // DoWork() will be called until it returns false). This call can be expensive
+  // and callers should attempt not to invoke it again before a non-immediate
+  // NextWorkInfo was returned from DoSomeWork(). Thread-safe (and callers
+  // should avoid holding a Lock at all cost while making this call as some
+  // platforms' priority boosting features have been observed to cause the
+  // caller to get descheduled : https://crbug.com/890978).
   virtual void ScheduleWork() = 0;
 
   // Schedule a DoDelayedWork callback to happen at the specified time,
diff --git a/base/message_loop/message_pump_android.cc b/base/message_loop/message_pump_android.cc
index 9ca29cf..b1d0813 100644
--- a/base/message_loop/message_pump_android.cc
+++ b/base/message_loop/message_pump_android.cc
@@ -121,6 +121,8 @@
 }
 
 void MessagePumpForUI::OnDelayedLooperCallback() {
+  // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
+  // resulted in Quit() in the same round.
   if (ShouldQuit())
     return;
 
@@ -139,74 +141,98 @@
   // there are no obvious timing or multi-threading related issues.
   DPCHECK(ret >= 0 || errno == EAGAIN);
 
-  delayed_scheduled_time_ = base::TimeTicks();
+  delayed_scheduled_time_.reset();
 
-  base::TimeTicks next_delayed_work_time;
-  delegate_->DoDelayedWork(&next_delayed_work_time);
-  if (!next_delayed_work_time.is_null()) {
-    ScheduleDelayedWork(next_delayed_work_time);
-  }
+  Delegate::NextWorkInfo next_work_info = delegate_->DoSomeWork();
+
   if (ShouldQuit())
     return;
-  // We may be idle now, so pump the loop to find out.
-  ScheduleWork();
+
+  if (next_work_info.is_immediate()) {
+    ScheduleWork();
+    return;
+  }
+
+  DoIdleWork();
+  if (!next_work_info.delayed_run_time.is_max())
+    ScheduleDelayedWork(next_work_info.delayed_run_time);
 }
 
 void MessagePumpForUI::OnNonDelayedLooperCallback() {
-  base::TimeTicks next_delayed_work_time;
-  bool did_any_work = false;
-
-  // Runs all native tasks scheduled to run, scheduling delayed work if
-  // necessary.
-  while (true) {
-    bool did_work_this_loop = false;
-    if (ShouldQuit())
-      return;
-    did_work_this_loop = delegate_->DoWork();
-    if (ShouldQuit())
-      return;
-
-    did_work_this_loop |= delegate_->DoDelayedWork(&next_delayed_work_time);
-
-    did_any_work |= did_work_this_loop;
-
-    // If we didn't do any work, we're out of native tasks to run, and we should
-    // return control to the looper to run Java tasks.
-    if (!did_work_this_loop)
-      break;
-  }
-  // If we did any work, return control to the looper to run java tasks before
-  // we call DoIdleWork(). We haven't cleared the fd yet, so we'll get woken up
-  // again soon to check for idle-ness.
-  if (did_any_work)
-    return;
+  // ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
+  // resulted in Quit() in the same round.
   if (ShouldQuit())
     return;
 
-  // Read the file descriptor, resetting its contents to 0 and reading back the
-  // stored value.
-  // See http://man7.org/linux/man-pages/man2/eventfd.2.html
-  uint64_t value = 0;
-  int ret = read(non_delayed_fd_, &value, sizeof(value));
-  DPCHECK(ret >= 0);
+  // A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
+  // java tasks below.
+  constexpr uint64_t kTryJavaTasksBeforeIdleBit = uint64_t(1) << 32;
 
-  // If we read a value > 1, it means we lost the race to clear the fd before a
-  // new task was posted. This is okay, we can just re-schedule work.
-  if (value > 1) {
-    ScheduleWork();
-  } else {
-    // At this point, the java looper might not be idle - it's impossible to
-    // know pre-Android-M, so we may end up doing Idle work while java tasks are
-    // still queued up. Note that this won't cause us to fail to run java tasks
-    // using QuitWhenIdle, as the JavaHandlerThread will finish running all
-    // currently scheduled tasks before it quits. Also note that we can't just
-    // add an idle callback to the java looper, as that will fire even if native
-    // tasks are still queued up.
-    DoIdleWork();
-    if (!next_delayed_work_time.is_null()) {
-      ScheduleDelayedWork(next_delayed_work_time);
-    }
+  // We're about to process all the work requested by ScheduleWork().
+  // MessagePump users are expected to do their best not to invoke
+  // ScheduleWork() again before DoSomeWork() returns a non-immediate
+  // NextWorkInfo below. Hence, capturing the file descriptor's value now and
+  // resetting its contents to 0 should be okay. The value currently stored
+  // should be greater than 0 since work having been scheduled is the reason
+  // we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
+  uint64_t pre_work_value = 0;
+  int ret = read(non_delayed_fd_, &pre_work_value, sizeof(pre_work_value));
+  DPCHECK(ret >= 0);
+  DCHECK_GT(pre_work_value, 0U);
+
+  // Note: We can't skip DoSomeWork() even if
+  // |pre_work_value == kTryJavaTasksBeforeIdleBit| here (i.e. no additional
+  // ScheduleWork() since yielding to java) as delayed tasks might have come in
+  // and we need to re-sample |next_work_info|.
+
+  // Runs all application tasks scheduled to run.
+  Delegate::NextWorkInfo next_work_info;
+  do {
+    if (ShouldQuit())
+      return;
+
+    next_work_info = delegate_->DoSomeWork();
+  } while (next_work_info.is_immediate());
+
+  // Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
+  // allow nesting so needing to resume in an outer loop is not an issue
+  // either).
+  if (ShouldQuit())
+    return;
+
+  // Before declaring this loop idle, yield to java tasks and arrange to be
+  // called again (unless we're already in that second call).
+  if (pre_work_value != kTryJavaTasksBeforeIdleBit) {
+    // Note: This write() is racing with potential ScheduleWork() calls. This is
+    // fine as write() is adding this bit, not overwriting the existing value,
+    // and as such racing ScheduleWork() calls would merely add 1 to the lower
+    // bits and we would find |pre_work_value != kTryJavaTasksBeforeIdleBit| in
+    // the next cycle again, retrying this.
+    ret = write(non_delayed_fd_, &kTryJavaTasksBeforeIdleBit,
+                sizeof(kTryJavaTasksBeforeIdleBit));
+    DPCHECK(ret >= 0);
+    return;
   }
+
+  // We yielded to java tasks already and they didn't generate a ScheduleWork()
+  // request so we can declare idleness. It's possible for a ScheduleWork()
+  // request to come in racily while this method unwinds, this is fine and will
+  // merely result in it being re-invoked shortly after it returns.
+  DCHECK_EQ(pre_work_value, kTryJavaTasksBeforeIdleBit);
+
+  if (ShouldQuit())
+    return;
+
+  // At this point, the java looper might not be idle - it's impossible to know
+  // pre-Android-M, so we may end up doing Idle work while java tasks are still
+  // queued up. Note that this won't cause us to fail to run java tasks using
+  // QuitWhenIdle, as the JavaHandlerThread will finish running all currently
+  // scheduled tasks before it quits. Also note that we can't just add an idle
+  // callback to the java looper, as that will fire even if application tasks
+  // are still queued up.
+  DoIdleWork();
+  if (!next_work_info.delayed_run_time.is_max())
+    ScheduleDelayedWork(next_work_info.delayed_run_time);
 }
 
 void MessagePumpForUI::DoIdleWork() {
@@ -296,10 +322,8 @@
   if (ShouldQuit())
     return;
 
-  if (!delayed_scheduled_time_.is_null() &&
-      delayed_work_time >= delayed_scheduled_time_) {
+  if (delayed_scheduled_time_ && *delayed_scheduled_time_ == delayed_work_time)
     return;
-  }
 
   DCHECK(!delayed_work_time.is_null());
   delayed_scheduled_time_ = delayed_work_time;
diff --git a/base/message_loop/message_pump_android.h b/base/message_loop/message_pump_android.h
index d7e0f50..a6014b4 100644
--- a/base/message_loop/message_pump_android.h
+++ b/base/message_loop/message_pump_android.h
@@ -14,6 +14,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/message_loop/message_pump.h"
+#include "base/optional.h"
 #include "base/time/time.h"
 
 struct ALooper;
@@ -79,8 +80,10 @@
   Delegate* delegate_ = nullptr;
 
   // The time at which we are currently scheduled to wake up and perform a
-  // delayed task.
-  base::TimeTicks delayed_scheduled_time_;
+  // delayed task. This avoids redundantly scheduling |delayed_fd_| with the
+  // same timeout when subsequent work phases all go idle on the same pending
+  // delayed task; nullopt if no wakeup is currently scheduled.
+  Optional<TimeTicks> delayed_scheduled_time_;
 
   // If set, a callback to fire when the message pump is quit.
   base::OnceClosure on_quit_callback_;
diff --git a/base/message_loop/message_pump_unittest.cc b/base/message_loop/message_pump_unittest.cc
index 220a7e0..16c0d04 100644
--- a/base/message_loop/message_pump_unittest.cc
+++ b/base/message_loop/message_pump_unittest.cc
@@ -44,7 +44,7 @@
       // iOS uses a MessagePumpDefault for UI in unit tests, ref.
       // test_support_ios.mm::CreateMessagePumpForUIForTests().
       return true;
-#elif defined(OS_WIN)
+#elif defined(OS_WIN) || defined(OS_ANDROID)
       return true;
 #elif defined(OS_POSIX) && !defined(OS_NACL_SFI)
       // MessagePumpLibevent was migrated (ref. message_pump_for_ui.h and
diff --git a/base/task/sequence_manager/sequence_manager.cc b/base/task/sequence_manager/sequence_manager.cc
index 62fe19a..04a8199 100644
--- a/base/task/sequence_manager/sequence_manager.cc
+++ b/base/task/sequence_manager/sequence_manager.cc
@@ -13,5 +13,94 @@
           base::ThreadTicks::IsSupported() ? task_thread_time_sampling_rate
                                            : 0) {}
 
+SequenceManager::Settings::Settings() = default;
+
+SequenceManager::Settings::Settings(Settings&& move_from) noexcept = default;
+
+SequenceManager::Settings::Builder::Builder() = default;
+
+SequenceManager::Settings::Builder::~Builder() = default;
+
+SequenceManager::Settings::Builder&
+SequenceManager::Settings::Builder::SetMessageLoopType(
+    MessageLoop::Type message_loop_type_val) {
+  settings_.message_loop_type = message_loop_type_val;
+  return *this;
+}
+
+SequenceManager::Settings::Builder&
+SequenceManager::Settings::Builder::SetRandomisedSamplingEnabled(
+    bool randomised_sampling_enabled_val) {
+  settings_.randomised_sampling_enabled = randomised_sampling_enabled_val;
+  return *this;
+}
+
+SequenceManager::Settings::Builder&
+SequenceManager::Settings::Builder::SetTickClock(const TickClock* clock_val) {
+  settings_.clock = clock_val;
+  return *this;
+}
+
+#if DCHECK_IS_ON()
+
+SequenceManager::Settings::Builder&
+SequenceManager::Settings::Builder::SetTaskLogging(
+    TaskLogging task_execution_logging_val) {
+  settings_.task_execution_logging = task_execution_logging_val;
+  return *this;
+}
+
+SequenceManager::Settings::Builder&
+SequenceManager::Settings::Builder::SetLogPostTask(bool log_post_task_val) {
+  settings_.log_post_task = log_post_task_val;
+  return *this;
+}
+
+SequenceManager::Settings::Builder&
+SequenceManager::Settings::Builder::SetLogTaskDelayExpiry(
+    bool log_task_delay_expiry_val) {
+  settings_.log_task_delay_expiry = log_task_delay_expiry_val;
+  return *this;
+}
+
+SequenceManager::Settings::Builder&
+SequenceManager::Settings::Builder::SetLogRunloopQuitAndQuitWhenIdle(
+    bool log_runloop_quit_and_quit_when_idle_val) {
+  settings_.log_runloop_quit_and_quit_when_idle =
+      log_runloop_quit_and_quit_when_idle_val;
+  return *this;
+}
+
+SequenceManager::Settings::Builder&
+SequenceManager::Settings::Builder::SetPerPriorityCrossThreadTaskDelay(
+    std::array<TimeDelta, TaskQueue::kQueuePriorityCount>
+        per_priority_cross_thread_task_delay_val) {
+  settings_.per_priority_cross_thread_task_delay =
+      per_priority_cross_thread_task_delay_val;
+  return *this;
+}
+
+SequenceManager::Settings::Builder&
+SequenceManager::Settings::Builder::SetPerPrioritySameThreadTaskDelay(
+    std::array<TimeDelta, TaskQueue::kQueuePriorityCount>
+        per_priority_same_thread_task_delay_val) {
+  settings_.per_priority_same_thread_task_delay =
+      per_priority_same_thread_task_delay_val;
+  return *this;
+}
+
+SequenceManager::Settings::Builder&
+SequenceManager::Settings::Builder::SetRandomTaskSelectionSeed(
+    int random_task_selection_seed_val) {
+  settings_.random_task_selection_seed = random_task_selection_seed_val;
+  return *this;
+}
+
+#endif  // DCHECK_IS_ON()
+
+SequenceManager::Settings SequenceManager::Settings::Builder::Build() {
+  return std::move(settings_);
+}
+
 }  // namespace sequence_manager
 }  // namespace base
diff --git a/base/task/sequence_manager/sequence_manager.h b/base/task/sequence_manager/sequence_manager.h
index 39d1c18..8479a97 100644
--- a/base/task/sequence_manager/sequence_manager.h
+++ b/base/task/sequence_manager/sequence_manager.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/message_loop/timer_slack.h"
 #include "base/single_thread_task_runner.h"
@@ -25,7 +26,7 @@
 // a single backing sequence (currently bound to a single thread, which is
 // refererred as *main thread* in the comments below). SequenceManager
 // implementation can be used in a various ways to apply scheduling logic.
-class SequenceManager {
+class BASE_EXPORT SequenceManager {
  public:
   class Observer {
    public:
@@ -59,11 +60,13 @@
 
   // Settings defining the desired SequenceManager behaviour: the type of the
   // MessageLoop and whether randomised sampling should be enabled.
-  struct Settings {
-    Settings() = default;
+  struct BASE_EXPORT Settings {
+    class Builder;
+
+    Settings();
     // In the future MessagePump (which is move-only) will also be a setting,
     // so we are making Settings move-only in preparation.
-    Settings(Settings&& move_from) noexcept = default;
+    Settings(Settings&& move_from) noexcept;
 
     MessageLoop::Type message_loop_type = MessageLoop::TYPE_DEFAULT;
     bool randomised_sampling_enabled = false;
@@ -85,14 +88,17 @@
     // to run.
     bool log_task_delay_expiry = false;
 
+    // If true usages of the RunLoop API will be logged.
+    bool log_runloop_quit_and_quit_when_idle = false;
+
     // Scheduler policy induced raciness is an area of concern. This lets us
     // apply an extra delay per priority for cross thread posting.
-    TimeDelta
-        per_priority_cross_thread_task_delay[TaskQueue::kQueuePriorityCount];
+    std::array<TimeDelta, TaskQueue::kQueuePriorityCount>
+        per_priority_cross_thread_task_delay;
 
     // Like the above but for same thread posting.
-    TimeDelta
-        per_priority_same_thread_task_delay[TaskQueue::kQueuePriorityCount];
+    std::array<TimeDelta, TaskQueue::kQueuePriorityCount>
+        per_priority_same_thread_task_delay;
 
     // If not zero this seeds a PRNG used by the task selection logic to choose
     // a random TaskQueue for a given priority rather than the TaskQueue with
@@ -201,6 +207,58 @@
       const TaskQueue::Spec& spec) = 0;
 };
 
+class BASE_EXPORT SequenceManager::Settings::Builder {
+ public:
+  Builder();
+  ~Builder();
+
+  // Sets the MessageLoop::Type which is sued to create a MessagePump.
+  Builder& SetMessageLoopType(MessageLoop::Type message_loop_type);
+
+  Builder& SetRandomisedSamplingEnabled(bool randomised_sampling_enabled);
+
+  // Sets the TickClock the SequenceManager uses to obtain Now.
+  Builder& SetTickClock(const TickClock* clock);
+
+#if DCHECK_IS_ON()
+  // Controls task execution logging.
+  Builder& SetTaskLogging(TaskLogging task_execution_logging);
+
+  // Whether or not PostTask will emit a debug log.
+  Builder& SetLogPostTask(bool log_post_task);
+
+  // Whether or not debug logs will be emitted when a delayed task becomes
+  // eligible to run.
+  Builder& SetLogTaskDelayExpiry(bool log_task_delay_expiry);
+
+  // Whether or not usages of the RunLoop API will be logged.
+  Builder& SetLogRunloopQuitAndQuitWhenIdle(
+      bool log_runloop_quit_and_quit_when_idle);
+
+  // Scheduler policy induced raciness is an area of concern. This lets us
+  // apply an extra delay per priority for cross thread posting.
+  Builder& SetPerPriorityCrossThreadTaskDelay(
+      std::array<TimeDelta, TaskQueue::kQueuePriorityCount>
+          per_priority_cross_thread_task_delay);
+
+  // Scheduler policy induced raciness is an area of concern. This lets us
+  // apply an extra delay per priority for same thread posting.
+  Builder& SetPerPrioritySameThreadTaskDelay(
+      std::array<TimeDelta, TaskQueue::kQueuePriorityCount>
+          per_priority_same_thread_task_delay);
+
+  // If not zero this seeds a PRNG used by the task selection logic to choose a
+  // random TaskQueue for a given priority rather than the TaskQueue with the
+  // oldest EnqueueOrder.
+  Builder& SetRandomTaskSelectionSeed(int random_task_selection_seed);
+#endif  // DCHECK_IS_ON()
+
+  Settings Build();
+
+ private:
+  Settings settings_;
+};
+
 // Create SequenceManager using MessageLoop on the current thread.
 // Implementation is located in sequence_manager_impl.cc.
 // TODO(scheduler-dev): Remove after every thread has a SequenceManager.
diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc
index 3fc2086..62d866f 100644
--- a/base/task/sequence_manager/sequence_manager_impl.cc
+++ b/base/task/sequence_manager/sequence_manager_impl.cc
@@ -243,7 +243,7 @@
 std::unique_ptr<SequenceManagerImpl> SequenceManagerImpl::CreateUnbound(
     SequenceManager::Settings settings) {
   return WrapUnique(new SequenceManagerImpl(
-      ThreadControllerWithMessagePumpImpl::CreateUnbound(settings.clock),
+      ThreadControllerWithMessagePumpImpl::CreateUnbound(settings),
       std::move(settings)));
 }
 
diff --git a/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
index 73b7014..4a8eb12 100644
--- a/base/task/sequence_manager/sequence_manager_impl_unittest.cc
+++ b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
@@ -155,8 +155,11 @@
             nullptr,
             ThreadTaskRunnerHandle::Get(),
             mock_tick_clock(),
-            SequenceManager::Settings{MessageLoop::Type::TYPE_DEFAULT, false,
-                                      mock_tick_clock()})) {
+            SequenceManager::Settings::Builder()
+                .SetMessageLoopType(MessageLoop::Type::TYPE_DEFAULT)
+                .SetRandomisedSamplingEnabled(false)
+                .SetTickClock(mock_tick_clock())
+                .Build())) {
     // A null clock triggers some assertions.
     AdvanceMockTickClock(TimeDelta::FromMilliseconds(1));
 
@@ -223,11 +226,15 @@
 
     auto pump = std::make_unique<MockTimeMessagePump>(&mock_clock_);
     pump_ = pump.get();
+    auto settings = SequenceManager::Settings::Builder()
+                        .SetMessageLoopType(MessageLoop::Type::TYPE_DEFAULT)
+                        .SetRandomisedSamplingEnabled(false)
+                        .SetTickClock(mock_tick_clock())
+                        .Build();
     sequence_manager_ = SequenceManagerForTest::Create(
-        std::make_unique<ThreadControllerWithMessagePumpImpl>(
-            std::move(pump), mock_tick_clock()),
-        SequenceManager::Settings{MessageLoop::Type::TYPE_DEFAULT, false,
-                                  mock_tick_clock()});
+        std::make_unique<ThreadControllerWithMessagePumpImpl>(std::move(pump),
+                                                              settings),
+        std::move(settings));
     sequence_manager_->SetDefaultTaskRunner(MakeRefCounted<NullTaskRunner>());
 
     // The SequenceManager constructor calls Now() once for setting up
@@ -305,9 +312,12 @@
     pump_ = pump.get();
     message_loop_ = std::make_unique<MessageLoop>(std::move(pump));
 
-    sequence_manager_ =
-        SequenceManagerForTest::CreateOnCurrentThread(SequenceManager::Settings{
-            MessageLoop::Type::TYPE_DEFAULT, false, mock_tick_clock()});
+    sequence_manager_ = SequenceManagerForTest::CreateOnCurrentThread(
+        SequenceManager::Settings::Builder()
+            .SetMessageLoopType(MessageLoop::Type::TYPE_DEFAULT)
+            .SetRandomisedSamplingEnabled(false)
+            .SetTickClock(mock_tick_clock())
+            .Build());
 
     // The SequenceManager constructor calls Now() once for setting up
     // housekeeping. The MessageLoop also contains a SequenceManager so two
diff --git a/base/task/sequence_manager/sequence_manager_perftest.cc b/base/task/sequence_manager/sequence_manager_perftest.cc
index 34d0568..818c54a3 100644
--- a/base/task/sequence_manager/sequence_manager_perftest.cc
+++ b/base/task/sequence_manager/sequence_manager_perftest.cc
@@ -162,7 +162,9 @@
   explicit SequenceManagerWithMessageLoopPerfTestDelegate(const char* name)
       : name_(name), message_loop_(new MessageLoopType()) {
     SetSequenceManager(CreateSequenceManagerOnCurrentThread(
-        SequenceManager::Settings{.randomised_sampling_enabled = false}));
+        SequenceManager::Settings::Builder()
+            .SetRandomisedSamplingEnabled(false)
+            .Build()));
   }
 
   ~SequenceManagerWithMessageLoopPerfTestDelegate() override { ShutDown(); }
@@ -182,12 +184,14 @@
       MessageLoop::Type type,
       bool randomised_sampling_enabled = false)
       : name_(name) {
+    auto settings =
+        SequenceManager::Settings::Builder()
+            .SetRandomisedSamplingEnabled(randomised_sampling_enabled)
+            .Build();
     SetSequenceManager(SequenceManagerForTest::Create(
         std::make_unique<internal::ThreadControllerWithMessagePumpImpl>(
-            MessageLoop ::CreateMessagePumpForType(type),
-            DefaultTickClock::GetInstance()),
-        SequenceManager::Settings{.randomised_sampling_enabled =
-                                      randomised_sampling_enabled}));
+            MessageLoop ::CreateMessagePumpForType(type), settings),
+        std::move(settings)));
 
     // ThreadControllerWithMessagePumpImpl doesn't provide a default task
     // runner.
diff --git a/base/task/sequence_manager/test/sequence_manager_for_test.h b/base/task/sequence_manager/test/sequence_manager_for_test.h
index a609fa0..bb4cf3f 100644
--- a/base/task/sequence_manager/test/sequence_manager_for_test.h
+++ b/base/task/sequence_manager/test/sequence_manager_for_test.h
@@ -29,15 +29,18 @@
       const TickClock* clock,
       // Since most test calls are in Blink, randomised sampling is enabled
       // by default in the test SequenceManager, as opposed to production code.
-      SequenceManager::Settings settings = SequenceManager::Settings{
-          .randomised_sampling_enabled = true});
+      SequenceManager::Settings settings =
+          SequenceManager::Settings::Builder()
+              .SetRandomisedSamplingEnabled(true)
+              .Build());
 
   // Creates SequenceManagerForTest using the provided ThreadController.
   static std::unique_ptr<SequenceManagerForTest> Create(
       std::unique_ptr<internal::ThreadController> thread_controller,
-      SequenceManager::Settings settings = SequenceManager::Settings{
-          base::MessageLoop::TYPE_DEFAULT,
-          /*randomised_sampling_enabled=*/true});
+      SequenceManager::Settings settings =
+          SequenceManager::Settings::Builder()
+              .SetRandomisedSamplingEnabled(true)
+              .Build());
 
   static std::unique_ptr<SequenceManagerForTest> CreateOnCurrentThread(
       SequenceManager::Settings);
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
index 85ca15f..76057a2 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
@@ -34,15 +34,20 @@
 }  // namespace
 
 ThreadControllerWithMessagePumpImpl::ThreadControllerWithMessagePumpImpl(
-    const TickClock* time_source)
+    const SequenceManager::Settings& settings)
     : associated_thread_(AssociatedThreadId::CreateUnbound()),
       work_deduplicator_(associated_thread_),
-      time_source_(time_source) {}
+#if DCHECK_IS_ON()
+      log_runloop_quit_and_quit_when_idle_(
+          settings.log_runloop_quit_and_quit_when_idle),
+#endif
+      time_source_(settings.clock) {
+}
 
 ThreadControllerWithMessagePumpImpl::ThreadControllerWithMessagePumpImpl(
     std::unique_ptr<MessagePump> message_pump,
-    const TickClock* time_source)
-    : ThreadControllerWithMessagePumpImpl(time_source) {
+    const SequenceManager::Settings& settings)
+    : ThreadControllerWithMessagePumpImpl(settings) {
   BindToCurrentThread(std::move(message_pump));
 }
 
@@ -56,8 +61,8 @@
 // static
 std::unique_ptr<ThreadControllerWithMessagePumpImpl>
 ThreadControllerWithMessagePumpImpl::CreateUnbound(
-    const TickClock* time_source) {
-  return base::WrapUnique(new ThreadControllerWithMessagePumpImpl(time_source));
+    const SequenceManager::Settings& settings) {
+  return base::WrapUnique(new ThreadControllerWithMessagePumpImpl(settings));
 }
 
 ThreadControllerWithMessagePumpImpl::MainThreadOnly::MainThreadOnly() = default;
@@ -363,6 +368,14 @@
       task_annotator_.RunTask("ThreadController::Task", &*task);
     }
 
+#if DCHECK_IS_ON()
+    if (log_runloop_quit_and_quit_when_idle_ && !quit_when_idle_requested_ &&
+        ShouldQuitWhenIdle()) {
+      DVLOG(1) << "ThreadControllerWithMessagePumpImpl::QuitWhenIdle";
+      quit_when_idle_requested_ = true;
+    }
+#endif
+
     *ran_task = true;
     main_thread_only().task_execution_allowed = true;
     main_thread_only().task_source->DidRunTask();
@@ -433,6 +446,11 @@
       &main_thread_only().quit_runloop_after,
       (timeout == TimeDelta::Max()) ? TimeTicks::Max()
                                     : time_source_->NowTicks() + timeout);
+
+#if DCHECK_IS_ON()
+  AutoReset<bool> quit_when_idle_requested(&quit_when_idle_requested_, false);
+#endif
+
   // Quit may have been called outside of a Run(), so |quit_pending| might be
   // true here. We can't use InTopLevelDoWork() in Quit() as this call may be
   // outside top-level DoWork but still in Run().
@@ -447,6 +465,12 @@
   } else {
     pump_->Run(this);
   }
+
+#if DCHECK_IS_ON()
+  if (log_runloop_quit_and_quit_when_idle_)
+    DVLOG(1) << "ThreadControllerWithMessagePumpImpl::Quit";
+#endif
+
   main_thread_only().runloop_count--;
   main_thread_only().quit_pending = false;
 }
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
index 8e2f13ea..af1ec41f 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
@@ -34,14 +34,15 @@
       public RunLoop::Delegate,
       public RunLoop::NestingObserver {
  public:
-  ThreadControllerWithMessagePumpImpl(std::unique_ptr<MessagePump> message_pump,
-                                      const TickClock* time_source);
+  ThreadControllerWithMessagePumpImpl(
+      std::unique_ptr<MessagePump> message_pump,
+      const SequenceManager::Settings& settings);
   ~ThreadControllerWithMessagePumpImpl() override;
 
   using ShouldScheduleWork = WorkDeduplicator::ShouldScheduleWork;
 
   static std::unique_ptr<ThreadControllerWithMessagePumpImpl> CreateUnbound(
-      const TickClock* time_source);
+      const SequenceManager::Settings& settings);
 
   // ThreadController implementation:
   void SetSequencedTaskSource(SequencedTaskSource* task_source) override;
@@ -74,7 +75,8 @@
   void OnExitNestedRunLoop() override;
 
  protected:
-  explicit ThreadControllerWithMessagePumpImpl(const TickClock* time_source);
+  explicit ThreadControllerWithMessagePumpImpl(
+      const SequenceManager::Settings& settings);
 
   // MessagePump::Delegate implementation.
   void BeforeDoInternalWork() override;
@@ -154,6 +156,12 @@
   std::unique_ptr<MessagePump> pump_;
 
   TaskAnnotator task_annotator_;
+
+#if DCHECK_IS_ON()
+  const bool log_runloop_quit_and_quit_when_idle_;
+  bool quit_when_idle_requested_ = false;
+#endif
+
   const TickClock* time_source_;  // Not owned.
 
   // Non-null provider of id state for identifying distinct work items executed
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
index aa45ebc1..3b8bf47 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
@@ -30,8 +30,8 @@
     : public internal::ThreadControllerWithMessagePumpImpl {
  public:
   ThreadControllerForTest(std::unique_ptr<MessagePump> pump,
-                          const TickClock* clock)
-      : ThreadControllerWithMessagePumpImpl(std::move(pump), clock) {}
+                          SequenceManager::Settings& settings)
+      : ThreadControllerWithMessagePumpImpl(std::move(pump), settings) {}
 
   using ThreadControllerWithMessagePumpImpl::DoDelayedWork;
   using ThreadControllerWithMessagePumpImpl::DoIdleWork;
@@ -130,8 +130,10 @@
  public:
   ThreadControllerWithMessagePumpTest()
       : message_pump_(new testing::StrictMock<MockMessagePump>()),
+        settings_(
+            SequenceManager::Settings::Builder().SetTickClock(&clock_).Build()),
         thread_controller_(std::unique_ptr<MessagePump>(message_pump_),
-                           &clock_),
+                           settings_),
         task_source_(&clock_) {
     thread_controller_.SetWorkBatchSize(1);
     thread_controller_.SetSequencedTaskSource(&task_source_);
@@ -139,6 +141,7 @@
 
  protected:
   MockMessagePump* message_pump_;
+  SequenceManager::Settings settings_;
   SimpleTestTickClock clock_;
   ThreadControllerForTest thread_controller_;
   FakeSequencedTaskSource task_source_;
diff --git a/base/task/sequence_manager/time_domain_unittest.cc b/base/task/sequence_manager/time_domain_unittest.cc
index 7a20bbd..9c0d952 100644
--- a/base/task/sequence_manager/time_domain_unittest.cc
+++ b/base/task/sequence_manager/time_domain_unittest.cc
@@ -396,7 +396,10 @@
   constexpr auto kDelay = TimeDelta::FromMilliseconds(20);
   SimpleTestTickClock clock;
   auto sequence_manager = sequence_manager::CreateUnboundSequenceManager(
-      SequenceManager::Settings{.message_loop_type = kType, .clock = &clock});
+      SequenceManager::Settings::Builder()
+          .SetMessageLoopType(kType)
+          .SetTickClock(&clock)
+          .Build());
   sequence_manager->BindToMessagePump(
       MessageLoop::CreateMessagePumpForType(kType));
   auto high_prio_queue =
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc
index 2c8b093..9a13ee3 100644
--- a/base/test/scoped_task_environment.cc
+++ b/base/test/scoped_task_environment.cc
@@ -65,10 +65,11 @@
   if (!type) {
     return nullptr;
   } else {
-    auto settings = base::sequence_manager::SequenceManager::Settings{
-        .message_loop_type = *type};
     return sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump(
-        MessageLoop::CreateMessagePumpForType(*type), std::move(settings));
+        MessageLoop::CreateMessagePumpForType(*type),
+        base::sequence_manager::SequenceManager::Settings::Builder()
+            .SetMessageLoopType(*type)
+            .Build());
   }
 }
 
diff --git a/base/test/scoped_task_environment.h b/base/test/scoped_task_environment.h
index 55e7323b..b6386fd 100644
--- a/base/test/scoped_task_environment.h
+++ b/base/test/scoped_task_environment.h
@@ -323,4 +323,4 @@
 }  // namespace test
 }  // namespace base
 
-#endif  // BASE_TEST_SCOPED_ASYNC_TASK_SCHEDULER_H_
+#endif  // BASE_TEST_SCOPED_TASK_ENVIRONMENT_H_
diff --git a/base/test/test_support_android.cc b/base/test/test_support_android.cc
index c7437de..7c07905 100644
--- a/base/test/test_support_android.cc
+++ b/base/test/test_support_android.cc
@@ -113,13 +113,8 @@
           break;
       }
 
-      more_work_is_plausible = g_state->delegate->DoWork();
-      if (g_state->should_quit)
-        break;
-
-      base::TimeTicks delayed_work_time;
-      more_work_is_plausible |=
-          g_state->delegate->DoDelayedWork(&delayed_work_time);
+      Delegate::NextWorkInfo next_work_info = g_state->delegate->DoSomeWork();
+      more_work_is_plausible = next_work_info.is_immediate();
       if (g_state->should_quit)
         break;
 
@@ -130,7 +125,7 @@
       if (g_state->should_quit)
         break;
 
-      more_work_is_plausible |= !delayed_work_time.is_null();
+      more_work_is_plausible |= !next_work_info.delayed_run_time.is_max();
     }
   }
 
diff --git a/base/timer/timer.h b/base/timer/timer.h
index d66e17c..fbc2d91 100644
--- a/base/timer/timer.h
+++ b/base/timer/timer.h
@@ -7,7 +7,7 @@
 // delay expires.
 // RepeatingTimer on the other hand calls you back periodically with the
 // prescribed time interval.
-// RetainingOneShotTimer doesn't repeat the task itself like OneShotTimer, but
+// RetainingOneShotTimer doesn't repeat the task itself like RepeatingTimer, but
 // retains the given task after the time out. You can restart it with Reset
 // again without giving new task to Start.
 //
diff --git a/build/android/gyp/apkbuilder.py b/build/android/gyp/apkbuilder.py
index 310a192..9058e39 100755
--- a/build/android/gyp/apkbuilder.py
+++ b/build/android/gyp/apkbuilder.py
@@ -231,6 +231,12 @@
   # dependencies when is_component_build=true.
   depfile_deps = list(native_libs)
 
+  # For targets that depend on static library APKs, dex paths are created by
+  # the static library's dexsplitter target and GN doesn't know about these
+  # paths.
+  if options.dex_file:
+    depfile_deps.append(options.dex_file)
+
   secondary_native_libs = []
   if options.secondary_native_libs:
     secondary_native_libs = sorted(options.secondary_native_libs)
diff --git a/build/android/gyp/dexsplitter.py b/build/android/gyp/dexsplitter.py
index a0761581..926d2cd 100755
--- a/build/android/gyp/dexsplitter.py
+++ b/build/android/gyp/dexsplitter.py
@@ -81,7 +81,7 @@
   args = build_utils.ExpandFileArgs(args)
   options = _ParseOptions(args)
 
-  input_paths = []
+  input_paths = [options.input_dex_zip]
   for feature_jars in options.features.itervalues():
     for feature_jar in feature_jars:
       input_paths.append(feature_jar)
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index bb86b2d..89135ae 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -55,6 +55,9 @@
   parser.add_option('--mapping-output',
                     help='Path for proguard to output mapping file to.')
   parser.add_option(
+      '--extra-mapping-output-paths',
+      help='Additional paths to copy output mapping file to.')
+  parser.add_option(
       '--output-config',
       help='Path to write the merged proguard config file to.')
   parser.add_option(
@@ -229,6 +232,10 @@
         with open(tmp_mapping_path) as tmp:
           mapping.writelines(l for l in tmp if not l.startswith('#'))
 
+      for output in build_utils.ParseGnList(options.extra_mapping_output_paths):
+        shutil.copy(tmp_mapping_path, output)
+
+
     with build_utils.AtomicOutput(options.output_config) as f:
       f.write(merged_configs)
       if temp_config_string:
diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py
index e4d7cc61..f37615d0 100644
--- a/build/android/gyp/util/build_utils.py
+++ b/build/android/gyp/util/build_utils.py
@@ -151,9 +151,11 @@
       subprocess.check_call(['prog', '--output', tmp_file.name])
   """
   # Create in same directory to ensure same filesystem when moving.
-  with tempfile.NamedTemporaryFile(suffix=os.path.basename(path),
-                                   dir=os.path.dirname(path),
-                                   delete=False) as f:
+  dirname = os.path.dirname(path)
+  if not os.path.exists(dirname):
+    MakeDirectory(dirname)
+  with tempfile.NamedTemporaryFile(
+      suffix=os.path.basename(path), dir=dirname, delete=False) as f:
     try:
       yield f
 
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index a0cd13f..05e2c1b 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -111,6 +111,15 @@
 * `deps_info['android_manifest']`:
 Path to an AndroidManifest.xml file related to the current target.
 
+* `deps_info['base_module_config']`:
+Only seen for the [`android_app_bundle`](#target_android_app_bundle) type.
+Path to the base module for the bundle.
+
+* `deps_info['is_base_module']`:
+Only seen for the
+[`android_app_bundle_module`](#target_android_app_bundle_module)
+type. Whether or not this module is the base module for some bundle.
+
 # Top-level `resources` dictionary:
 
 This dictionary only appears for a few target types that can contain or
@@ -908,6 +917,9 @@
       '--static-library-dependent-configs',
       help='GN list of .build_configs of targets that use this target as a '
       'static library.')
+  parser.add_option(
+      '--static-library-jar-path',
+      help='Equivalent to what normally would be the jar_path for the APK.')
 
   parser.add_option('--tested-apk-config',
       help='Path to the build config of the tested apk (for an instrumentation '
@@ -934,6 +946,9 @@
   parser.add_option(
       '--base-whitelist-rtxt-path',
       help='Path to R.txt file for the base resources whitelist.')
+  parser.add_option(
+      '--is-base-module',
+      help='Specifies that this module is a base module for some app bundle.')
 
   parser.add_option('--generate-markdown-format-doc', action='store_true',
                     help='Dump the Markdown .build_config format documentation '
@@ -944,6 +959,11 @@
       help='Path to the base module\'s build config '
       'if this is a feature module.')
 
+  parser.add_option(
+      '--module-build-configs',
+      help='For bundles, the paths of all non-async module .build_configs '
+      'for modules that are part of the bundle.')
+
   options, args = parser.parse_args(argv)
 
   if args:
@@ -975,7 +995,7 @@
       'junit_binary': ['build_config'],
       'resource_rewriter': ['build_config'],
       'system_java_library': ['build_config'],
-      'android_app_bundle': ['build_config'],
+      'android_app_bundle': ['build_config', 'module_build_configs'],
   }
   required_options = required_options_map.get(options.type)
   if not required_options:
@@ -993,6 +1013,9 @@
     if options.base_whitelist_rtxt_path:
       raise Exception('--base-whitelist-rtxt-path can only be used with '
                       '--type=android_app_bundle_module')
+    if options.is_base_module:
+      raise Exception('--is-base-module can only be used with '
+                      '--type=android_app_bundle_module')
 
   is_apk_or_module_target = options.type in ('android_apk',
       'android_app_bundle_module')
@@ -1021,10 +1044,13 @@
 
   is_static_library_dex_provider_target = (
       options.static_library_dependent_configs and options.proguard_enabled)
-  if is_static_library_dex_provider_target and options.type != 'android_apk':
-    raise Exception(
-        '--static-library-dependent-configs only supports --type=android_apk')
-
+  if is_static_library_dex_provider_target:
+    if options.type != 'android_apk':
+      raise Exception(
+          '--static-library-dependent-configs only supports --type=android_apk')
+    if not options.static_library_jar_path:
+      raise Exception('Can\'t have --static-library-dependent-configs without '
+                      '--static-library-jar-path')
   options.static_library_dependent_configs = build_utils.ParseGnList(
       options.static_library_dependent_configs)
   static_library_dependent_configs_by_path = {
@@ -1082,6 +1108,9 @@
     all_resources_deps = [
         d for d in all_resources_deps if not d in tested_apk_resources_deps]
 
+  if options.type == 'android_app_bundle_module':
+    deps_info['is_base_module'] = bool(options.is_base_module)
+
   # Required for generating gradle files.
   if options.type == 'java_library':
     deps_info['is_prebuilt'] = bool(options.is_prebuilt)
@@ -1370,23 +1399,41 @@
 
   all_configs = build_utils.ParseGnList(options.proguard_configs)
   deps_info['proguard_configs'] = list(all_configs)
-  extra_jars = []
+  extra_proguard_classpath_jars = []
+
+  if options.type == 'android_app_bundle':
+    module_configs = [
+        GetDepConfig(c)
+        for c in build_utils.ParseGnList(options.module_build_configs)
+    ]
+    base_module_configs = [c for c in module_configs if c['is_base_module']]
+    assert len(base_module_configs) == 1, 'Must have exactly 1 base module!'
+    deps_info['base_module_config'] = base_module_configs[0]['path']
 
   if is_static_library_dex_provider_target:
     # Map classpath entries to configs that include them in their classpath.
     configs_by_classpath_entry = collections.defaultdict(list)
+    static_lib_jar_paths = {}
     for config_path, dep_config in (
         static_library_dependent_configs_by_path.iteritems()):
+      # For bundles, only the jar path and jni sources of the base module
+      # are relevant for proguard. Should be updated when bundle feature
+      # modules support JNI.
+      base_config = dep_config
+      if dep_config['type'] == 'android_app_bundle':
+        base_config = GetDepConfig(dep_config['base_module_config'])
+      static_lib_jar_paths[config_path] = base_config['jar_path']
       all_configs.extend(dep_config['proguard_all_configs'])
-      extra_jars.extend(dep_config['proguard_classpath_jars'])
-      all_java_sources.extend(dep_config['jni']['all_source'])
+      extra_proguard_classpath_jars.extend(
+          dep_config['proguard_classpath_jars'])
+      all_java_sources.extend(base_config['jni']['all_source'])
       for cp_entry in dep_config['java_runtime_classpath']:
         # The APK Java targets for the static library dependent targets will
         # have some of the same classes (R.java) due to shared resource
         # dependencies. To avoid Proguard failures due to duplicate classes, we
         # merge the APK jars into the static library's jar_path as a
         # preprocessing build step.
-        if cp_entry != dep_config['jar_path']:
+        if cp_entry != base_config['jar_path']:
           configs_by_classpath_entry[cp_entry].append(config_path)
 
     for cp_entry in java_full_classpath:
@@ -1401,14 +1448,22 @@
       classpath_entries_by_owning_config[config_path].append(cp_entry)
       java_full_classpath.append(cp_entry)
 
-    classpath_entries_by_owning_config[options.build_config].append(
-        deps_info['jar_path'])
-
     java_full_classpath = sorted(set(java_full_classpath))
     deps_info['static_library_dependent_classpath_configs'] = {
         path: sorted(set(classpath))
         for path, classpath in classpath_entries_by_owning_config.iteritems()
     }
+    # Order matters here, must match the order passed in.
+    static_lib_jar_paths = [
+        static_lib_jar_paths[x]
+        for x in options.static_library_dependent_configs
+    ]
+    static_lib_jar_paths.append(options.static_library_jar_path)
+    deps_info['static_library_dependent_apk_jars'] = static_lib_jar_paths
+    deps_info['static_library_proguard_mapping_output_paths'] = [
+        d['proguard_mapping_path']
+        for d in static_library_dependent_configs_by_path.itervalues()
+    ]
 
   if is_apk_or_module_target or options.type in ('group', 'java_library',
                                                  'junit_binary'):
@@ -1425,17 +1480,19 @@
       'dist_jar', 'android_app_bundle_module', 'android_app_bundle'):
     for c in all_library_deps:
       all_configs.extend(p for p in c.get('proguard_configs', []))
-      extra_jars.extend(p for p in c.get('extra_classpath_jars', []))
+      extra_proguard_classpath_jars.extend(
+          p for p in c.get('extra_classpath_jars', []))
     for c in all_group_deps:
-      extra_jars.extend(p for p in c.get('extra_classpath_jars', []))
+      extra_proguard_classpath_jars.extend(
+          p for p in c.get('extra_classpath_jars', []))
     if options.type == 'android_app_bundle':
       for c in deps.Direct('android_app_bundle_module'):
         all_configs.extend(p for p in c.get('proguard_configs', []))
     if options.type == 'android_app_bundle':
       for d in deps.Direct('android_app_bundle_module'):
-        extra_jars.extend(
+        extra_proguard_classpath_jars.extend(
             c for c in d.get('proguard_classpath_jars', [])
-            if c not in extra_jars)
+            if c not in extra_proguard_classpath_jars)
 
     if options.type == 'android_app_bundle':
       deps_proguard_enabled = []
@@ -1454,8 +1511,9 @@
                                                deps_proguard_disabled))
     else:
       deps_info['proguard_enabled'] = bool(options.proguard_enabled)
-      if options.proguard_mapping_path:
-        deps_info['proguard_mapping_path'] = options.proguard_mapping_path
+
+    if options.proguard_mapping_path:
+      deps_info['proguard_mapping_path'] = options.proguard_mapping_path
 
   # The java code for an instrumentation test apk is assembled differently for
   # ProGuard vs. non-ProGuard.
@@ -1471,13 +1529,15 @@
   # doesn't work with ProGuard's whole-program optimizations. Although the
   # apk-under-test still has all of its code in its classes.dex, none of it is
   # used at runtime because the copy of it within the test apk takes precidence.
+
   if options.type == 'android_apk' and options.tested_apk_config:
     if tested_apk_config['proguard_enabled']:
       assert options.proguard_enabled, ('proguard must be enabled for '
           'instrumentation apks if it\'s enabled for the tested apk.')
       # Mutating lists, so no need to explicitly re-assign to dict.
       all_configs.extend(p for p in tested_apk_config['proguard_all_configs'])
-      extra_jars.extend(p for p in tested_apk_config['proguard_classpath_jars'])
+      extra_proguard_classpath_jars.extend(
+          p for p in tested_apk_config['proguard_classpath_jars'])
       tested_apk_config = GetDepConfig(options.tested_apk_config)
       deps_info['proguard_under_test_mapping'] = (
           tested_apk_config['proguard_mapping_path'])
@@ -1521,14 +1581,16 @@
   if options.type in ('android_apk', 'dist_aar', 'dist_jar',
                       'android_app_bundle_module', 'android_app_bundle'):
     deps_info['proguard_all_configs'] = sorted(set(all_configs))
-    deps_info['proguard_classpath_jars'] = sorted(set(extra_jars))
+    deps_info['proguard_classpath_jars'] = sorted(
+        set(extra_proguard_classpath_jars))
 
   # Dependencies for the final dex file of an apk.
-  if is_apk_or_module_target:
+  if is_apk_or_module_target or options.final_dex_path:
     config['final_dex'] = {}
     dex_config = config['final_dex']
-    dex_config['dependency_dex_files'] = deps_dex_files
     dex_config['path'] = options.final_dex_path
+  if is_apk_or_module_target:
+    dex_config['dependency_dex_files'] = deps_dex_files
 
   if is_java_target:
     config['javac']['classpath'] = javac_classpath
diff --git a/build/android/resource_sizes.gni b/build/android/resource_sizes.gni
index f048a633..0b37837 100644
--- a/build/android/resource_sizes.gni
+++ b/build/android/resource_sizes.gni
@@ -7,32 +7,41 @@
 # Generates a script in the bin directory that runs
 # //build/android/resource_sizes.py against the provided apk.
 #
+# Only one of apk_name or file_path should be provided.
+#
 # Variables:
-#   apk: The APK target against which resource_sizes.py should run.
+#   apk_name: The name of the apk, without the extension.
+#   file_path: The path to the apk or .minimal.apks.
 template("android_resource_sizes_test") {
   generate_android_wrapper(target_name) {
     executable = "//build/android/resource_sizes.py"
     wrapper_script = "$root_out_dir/bin/${target_name}"
 
-    # Getting the _apk_path below at build time requires the APK's
-    # build config.
-    deps = [
-      invoker.apk,
-    ]
+    assert(defined(invoker.apk_name) != defined(invoker.file_path),
+           "Exactly one of apk_name or file_path should be provided.")
 
     data_deps = [
       "//build/android:resource_sizes_py",
     ]
+    if (defined(invoker.data_deps)) {
+      data_deps += invoker.data_deps
+    }
 
-    _apk_build_config = get_label_info(invoker.apk, "target_gen_dir") + "/" +
-                        get_label_info(invoker.apk, "name") + ".build_config"
-    _rebased_apk_build_config = rebase_path(_apk_build_config, root_build_dir)
-    _apk_path =
-        "@WrappedPath(@FileArg($_rebased_apk_build_config:deps_info:apk_path))"
+    data = []
+    if (defined(invoker.apk_name)) {
+      _file_path = "$root_out_dir/apks/${invoker.apk_name}.apk"
+      data += [ "$root_out_dir/arsc/apks/${invoker.apk_name}.ap_" ]
+    } else if (defined(invoker.file_path)) {
+      _file_path = invoker.file_path
+    }
+    _rebased_file_path = rebase_path(_file_path, root_build_dir)
+
+    data += [ _file_path ]
+
     executable_args = [
       "--output-format",
       "histograms",
-      _apk_path,
+      "@WrappedPath(${_rebased_file_path})",
       "--chromium-output-directory",
       "@WrappedPath(.)",
     ]
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index d583667f..b1842aa 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -350,6 +350,10 @@
       ]
     }
 
+    if (defined(invoker.is_base_module) && invoker.is_base_module) {
+      args += [ "--is-base-module" ]
+    }
+
     if (defined(invoker.loadable_modules) && invoker.loadable_modules != []) {
       _rebased_modules = rebase_path(invoker.loadable_modules, root_build_dir)
       args += [ "--native-libs=$_rebased_modules" ]
@@ -460,6 +464,11 @@
       args += [ "--proguard-configs=$_rebased_proguard_configs" ]
     }
     if (defined(invoker.static_library_dependent_targets)) {
+      assert(defined(invoker.static_library_jar_path))
+      args += [
+        "--static-library-jar-path",
+        rebase_path(invoker.static_library_jar_path, root_build_dir),
+      ]
       _dependent_configs = []
       foreach(d, invoker.static_library_dependent_targets) {
         _target_label = get_label_info(d, "label_no_toolchain")
@@ -468,8 +477,10 @@
         _dep_name = get_label_info(d, "name")
         _dependent_configs += [ "$_dep_gen_dir/$_dep_name.build_config" ]
       }
-      _rebased_depdent_configs = rebase_path(_dependent_configs, root_build_dir)
-      args += [ "--static-library-dependent-configs=$_rebased_depdent_configs" ]
+      _rebased_dependent_configs =
+          rebase_path(_dependent_configs, root_build_dir)
+      args +=
+          [ "--static-library-dependent-configs=$_rebased_dependent_configs" ]
     }
     if (defined(invoker.gradle_treat_as_prebuilt) &&
         invoker.gradle_treat_as_prebuilt) {
@@ -492,6 +503,11 @@
         rebase_path("$_dep_gen_dir/$_dep_name.build_config", root_build_dir),
       ]
     }
+    if (defined(invoker.module_build_configs)) {
+      _rebased_configs =
+          rebase_path(invoker.module_build_configs, root_build_dir)
+      args += [ "--module-build-configs=$_rebased_configs" ]
+    }
     if (current_toolchain != default_toolchain) {
       # This has to be a built-time error rather than a GN assert because many
       # packages have a mix of java and non-java targets. For example, the
@@ -1034,6 +1050,13 @@
         "@FileArg($_rebased_build_config:android:sdk_jars)",
       ]
 
+      if (defined(invoker.is_static_library) && invoker.is_static_library) {
+        args += [
+          "--extra-mapping-output-paths",
+          "@FileArg($_rebased_build_config:deps_info:static_library_proguard_mapping_output_paths)",
+        ]
+      }
+
       if (defined(invoker.config_output_path)) {
         _config_output_path = invoker.config_output_path
         outputs += [ _config_output_path ]
@@ -1200,6 +1223,7 @@
                                  "proguard_expectations_file",
                                  "proguard_jar_path",
                                  "proguard_mapping_path",
+                                 "is_static_library",
                                  "testonly",
                                ])
         inputs = []
@@ -2582,9 +2606,6 @@
                  _apksigner,
                  _zipalign,
                ]
-      if (defined(invoker.dex_path)) {
-        inputs += [ invoker.dex_path ]
-      }
 
       outputs = [
         invoker.output_apk_path,
@@ -3085,6 +3106,8 @@
   #    java_files is empty. If not
   #  jar_path: Optional path to a prebuilt .jar file for this target.
   #    Mutually exclusive with java_files and srcjar_deps.
+  #  intermediate_jar_path: Optional path to the output .jar file. If used,
+  #    final_jar_path must be created by another target.
   #  final_jar_path: Optional path to the final output .jar file (after
   #    processing). If not provided, the output will go under
   #    $root_build_dir/lib.java/
@@ -3250,6 +3273,16 @@
       if (defined(invoker.final_jar_path)) {
         _final_jar_path = invoker.final_jar_path
       }
+
+      # For static libraries, the javac jar output is created at the intermediate
+      # path so that it can be processed by another target and moved to the final
+      # spot that the .build_config knows about. Technically this should be done
+      # for the ijar as well, but this is only used for APK targets where
+      # the ijar path isn't actually used.
+      _build_config_jar_path = _final_jar_path
+      if (defined(invoker.intermediate_jar_path)) {
+        _final_jar_path = invoker.intermediate_jar_path
+      }
       _final_ijar_path =
           get_path_info(_final_jar_path, "dir") + "/" +
           get_path_info(_final_jar_path, "name") + ".interface.jar"
@@ -3401,6 +3434,7 @@
         forward_variables_from(invoker,
                                [
                                  "base_module_target",
+                                 "is_base_module",
                                  "module_rtxt_path",
                                  "proto_resources_path",
                                ])
@@ -3416,8 +3450,11 @@
       bypass_platform_checks = defined(invoker.bypass_platform_checks) &&
                                invoker.bypass_platform_checks
 
+      if (defined(invoker.intermediate_jar_path)) {
+        static_library_jar_path = invoker.intermediate_jar_path
+      }
       if (defined(_final_jar_path)) {
-        jar_path = _final_jar_path
+        jar_path = _build_config_jar_path
         ijar_path = _final_ijar_path
         unprocessed_jar_path = _unprocessed_jar_path
       }
@@ -3815,13 +3852,14 @@
 #   input_dex_zip: Path to zipped dex files to split.
 #   all_modules: Path to list of all modules. Each Module must have
 #     build_config, name, and build_config_target properties.
+#   feature_jars_args: Optional list of args to be passed to dexsplitter.py.
+#     If used should include the jars owned by each feature (in the same order
+#     as all_modules). Allows invoker to pull the list of jars from a different
+#     .build_config than the module's .build_config.
 template("dexsplitter") {
   action_with_pydeps(target_name) {
     forward_variables_from(invoker, [ "deps" ])
     script = "//build/android/gyp/dexsplitter.py"
-    inputs = [
-      invoker.input_dex_zip,
-    ]
     _stamp = "${target_gen_dir}/${target_name}.stamp"
     outputs = [
       _stamp,
@@ -3847,10 +3885,15 @@
       args += [
         "--feature-name",
         _feature_module.name,
-        "--feature-jars=@FileArg($_rebased_module_build_config:deps_info:java_runtime_classpath)",
         "--dex-dest=@FileArg($_rebased_module_build_config:final_dex:path)",
       ]
+      if (!defined(invoker.feature_jars_args)) {
+        args += [ "--feature-jars=@FileArg($_rebased_module_build_config:deps_info:java_runtime_classpath)" ]
+      }
       deps += [ _feature_module.build_config_target ]
     }
+    if (defined(invoker.feature_jars_args)) {
+      args += invoker.feature_jars_args
+    }
   }
 }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 02ec843..80c9274 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2049,6 +2049,13 @@
   #     resources with acceptable/non-acceptable optimizations.
   #   verify_android_configuration: Enables verification of expected merged
   #     manifest and proguard flags based on a golden file.
+  #   static_library_dependent_targets: A list of targets that use this target
+  #     as a static library. Common Java code from the targets listed in
+  #     static_library_dependent_targets will be moved into this target.
+  #   static_library_provider: Specifies a single target that this target will
+  #     use as a static library APK. When proguard is enabled, the
+  #     static_library_provider target will provide the dex file(s) for this
+  #     target.
   template("android_apk_or_module") {
     forward_variables_from(invoker, [ "testonly" ])
 
@@ -2266,6 +2273,23 @@
       _proguard_mapping_path = "$_final_apk_path.mapping"
     }
 
+    # TODO(http://crbug.com/901465): Move shared Java code to static libraries
+    # when !_proguard_enabled too.
+    _is_static_library_provider =
+        defined(invoker.static_library_dependent_targets) && _proguard_enabled
+    if (_is_static_library_provider) {
+      _static_library_apk_java_target_output = _jar_path
+      _static_library_sync_dex_path =
+          "$_gen_dir/static_library_synchronized_proguard.classes.dex.zip"
+    }
+
+    _uses_static_library = defined(invoker.static_library_provider)
+    if (_uses_static_library) {
+      # These will be provided by the static library APK.
+      _generate_buildconfig_java = false
+      _generate_final_jni = false
+    }
+
     # TODO(crbug.com/864142): Allow incremental installs of bundle modules.
     _incremental_allowed =
         !_is_bundle_module &&
@@ -2627,6 +2651,7 @@
       if (_is_bundle_module) {
         type = "android_app_bundle_module"
         res_size_info_path = _res_size_info_path
+        is_base_module = _is_base_module
       } else {
         type = "android_apk"
       }
@@ -2637,6 +2662,10 @@
 
       srcjar_deps = _srcjar_deps
       final_jar_path = _jar_path
+      if (_is_static_library_provider) {
+        intermediate_jar_path = "$_base_path.intermediate.jar"
+        final_jar_path = _static_library_apk_java_target_output
+      }
       dex_path = _lib_dex_path
       final_dex_path = _final_dex_path
 
@@ -2705,11 +2734,48 @@
       }
     }
 
-    # Dex generation for app bundle modules with proguarding enabled takes
-    # place later due to synchronized proguarding. For more details,
-    # read build/android/docs/android_app_bundles.md
-    if (!(_is_bundle_module && _proguard_enabled)) {
+    if (_is_static_library_provider) {
+      _static_library_apk_java_target = "${_template_name}__combine_apk_jars"
+
+      # Since some of the static_libary_dependent_targets may have overlapping
+      # resource dependencies, we can't include all jars created by the
+      # static_library_dependent_targets or else proguard will fail with
+      # duplicate class definitions. This step combines these jars into
+      # a single jar with duplicates ignored.
+      action_with_pydeps(_static_library_apk_java_target) {
+        script = "//build/android/gyp/zip.py"
+        deps = [
+          ":$_build_config_target",
+          ":$_java_target",
+        ]
+        foreach(_dep, invoker.static_library_dependent_targets) {
+          _target_label = get_label_info(_dep, "label_no_toolchain")
+          deps += [ "${_target_label}__java" ]
+        }
+        inputs = [
+          _build_config,
+        ]
+        outputs = [
+          _static_library_apk_java_target_output,
+        ]
+        args = [
+          "--input-zips=@FileArg($_rebased_build_config:deps_info:static_library_dependent_apk_jars)",
+          "--output",
+          rebase_path(_static_library_apk_java_target_output, root_build_dir),
+        ]
+      }
+    }
+
+    if (_proguard_enabled && _uses_static_library) {
+      _final_dex_target_dep = "${invoker.static_library_provider}__dexsplitter"
+    } else if (!(_is_bundle_module && _proguard_enabled)) {
+      # Dex generation for app bundle modules with proguarding enabled takes
+      # place later due to synchronized proguarding. For more details,
+      # read build/android/docs/android_app_bundles.md
       _final_dex_target_name = "${_template_name}__final_dex"
+      if (!_is_bundle_module) {
+        _final_dex_target_dep = ":$_final_dex_target_name"
+      }
       dex(_final_dex_target_name) {
         forward_variables_from(invoker,
                                [
@@ -2722,6 +2788,9 @@
           ":$_build_config_target",
           ":$_java_target",
         ]
+        if (_is_static_library_provider) {
+          deps += [ ":$_static_library_apk_java_target" ]
+        }
         if (_proguard_enabled) {
           forward_variables_from(invoker, [ "proguard_jar_path" ])
           deps += _deps + [ ":$_compile_resources_target" ]
@@ -2741,7 +2810,12 @@
           }
         }
 
-        output = _final_dex_path
+        if (_is_static_library_provider) {
+          output = _static_library_sync_dex_path
+          is_static_library = true
+        } else {
+          output = _final_dex_path
+        }
         enable_multidex = _enable_multidex
 
         if (_enable_main_dex_list) {
@@ -2754,6 +2828,59 @@
           }
         }
       }
+
+      # For static libraries, a single Proguard run is performed that includes
+      # code from the static library APK and the APKs that use the static
+      # library (done via. classpath merging in write_build_config.py).
+      # This dexsplitter target splits the synchronized dex output into dex
+      # files for each APK/Bundle. In the Bundle case, another dexsplitter step
+      # is later performed to split the dex further for each feature module.
+      if (_is_static_library_provider && _proguard_enabled) {
+        _static_library_dexsplitter_target = "${_template_name}__dexsplitter"
+        dexsplitter(_static_library_dexsplitter_target) {
+          input_dex_zip = _static_library_sync_dex_path
+          proguard_mapping = _proguard_mapping_path
+          deps = [
+            ":$_build_config_target",
+            "$_final_dex_target_dep",
+          ]
+          all_modules = [
+            {
+              name = "base"
+              build_config = _build_config
+              build_config_target = ":$_build_config_target"
+            },
+          ]
+          feature_jars_args = [
+            "--feature-jars",
+            "@FileArg($_rebased_build_config:deps_info:" +
+                "static_library_dependent_classpath_configs:" +
+                "$_rebased_build_config)",
+          ]
+          foreach(_apk_as_module, invoker.static_library_dependent_targets) {
+            _module_config_target =
+                "${_apk_as_module}$build_config_target_suffix"
+            _module_gen_dir = get_label_info(_apk_as_module, "target_gen_dir")
+            _module_name = get_label_info(_apk_as_module, "name")
+            _module_config = "$_module_gen_dir/$_module_name.build_config"
+            _rebased_module_config = rebase_path(_module_config, root_build_dir)
+            all_modules += [
+              {
+                name = _module_name
+                build_config = _module_config
+                build_config_target = _module_config_target
+              },
+            ]
+            feature_jars_args += [
+              "--feature-jars",
+              "@FileArg($_rebased_build_config:deps_info:" +
+                  "static_library_dependent_classpath_configs:" +
+                  "$_rebased_module_config)",
+            ]
+          }
+        }
+        _deps += [ ":$_static_library_dexsplitter_target" ]
+      }
     } else {
       # A small sanity check to help developers with a subtle point!
       assert(
@@ -2871,7 +2998,7 @@
         deps = _deps + [
                  ":$_merge_manifest_target",
                  ":$_build_config_target",
-                 ":$_final_dex_target_name",
+                 "$_final_dex_target_dep",
                  ":$_compile_resources_target",
                ]
 
@@ -3093,7 +3220,6 @@
                                "dexlayout_profile",
                                "dist_ijar_path",
                                "dont_load_shared_libraries",
-                               "emit_resource_ids",
                                "emma_never_instrument",
                                "enable_chromium_linker_tests",
                                "enable_multidex",
@@ -3140,6 +3266,7 @@
                                "shared_resources_whitelist_target",
                                "srcjar_deps",
                                "static_library_dependent_targets",
+                               "static_library_provider",
                                "support_zh_hk",
                                "testonly",
                                "uncompress_shared_libraries",
@@ -3243,6 +3370,7 @@
                                "proguard_jar_path",
                                "resource_blacklist_exceptions",
                                "resource_blacklist_regex",
+                               "resource_ids_provider_dep",
                                "resources_config_path",
                                "secondary_abi_shared_libraries",
                                "shared_libraries",
@@ -3250,6 +3378,7 @@
                                "shared_resources_whitelist_locales",
                                "shared_resources_whitelist_target",
                                "srcjar_deps",
+                               "static_library_provider",
                                "support_zh_hk",
                                "testonly",
                                "uncompress_shared_libraries",
@@ -4078,6 +4207,10 @@
   #    system_image_locale_whitelist: List of locales that should be included
   #      on system APKs generated from this bundle.
   #
+  #    static_library_provider: Specifies a single target that this target will
+  #      use as a static library APK. When proguard is enabled, the
+  #      static_library_provider target will perform the synchronized dex step.
+  #
   # Example:
   #   android_app_bundle("chrome_public_bundle") {
   #      base_module_target = "//chrome/android:chrome_public_apk"
@@ -4136,7 +4269,13 @@
     not_needed([ "_enable_multidex" ])
 
     if (_proguard_enabled) {
-      _sync_dex_target = "${target_name}__sync_dex"
+      _uses_static_library = defined(invoker.static_library_provider)
+      if (_uses_static_library) {
+        _sync_dex_target_dep = "${invoker.static_library_provider}__dexsplitter"
+      } else {
+        _sync_dex_target = "${target_name}__sync_dex"
+        _sync_dex_target_dep = ":$_sync_dex_target"
+      }
 
       # TODO(crbug.com/938635): Combine synchronous proguard run mapping file
       # and asynchronous proguard run mapping files into single .mapping.
@@ -4202,8 +4341,8 @@
               apply_mapping = _sync_proguard_mapping_path
 
               deps = [
-                ":${_sync_dex_target}",
                 _module.module_target,
+                _sync_dex_target_dep,
               ]
 
               output = _dex_zip
@@ -4220,10 +4359,20 @@
     }
 
     # Make build config, which is required for synchronized proguarding.
+    _sync_module_java_targets = []
+    _sync_module_build_configs = []
     _sync_module_targets = []
     foreach(_module, _sync_modules) {
       _sync_module_targets += [ _module.module_target ]
+      _sync_module_java_targets += [ "${_module.module_target}__java" ]
+      _sync_module_build_configs += [ _module.build_config ]
     }
+
+    # Used to expose the module Java targets of the bundle.
+    group("${target_name}__java") {
+      deps = _sync_module_java_targets
+    }
+
     _build_config = "$target_gen_dir/${target_name}.build_config"
     _rebased_build_config = rebase_path(_build_config, root_build_dir)
     _build_config_target = "$target_name$build_config_target_suffix"
@@ -4232,6 +4381,14 @@
     } else {
       proguard_android_sdk_dep_ = "//third_party/android_sdk:android_sdk_java"
     }
+
+    if (_proguard_enabled) {
+      _proguard_mapping_path = "${_bundle_path}.mapping"
+    }
+
+    _unsplit_dex_zip =
+        "${target_gen_dir}/${target_name}/${target_name}__unsplit_dex.zip"
+
     write_build_config(_build_config_target) {
       # We don't want async modules to be proguarded synchronously, so we leave
       # them out of possible_config_deps.
@@ -4240,34 +4397,50 @@
           _sync_module_targets + [ proguard_android_sdk_dep_ ]
       build_config = _build_config
       proguard_enabled = _proguard_enabled
+      module_build_configs = _sync_module_build_configs
+      final_dex_path = _unsplit_dex_zip
+
+      if (_proguard_enabled) {
+        proguard_mapping_path = _proguard_mapping_path
+      }
     }
 
     if (_proguard_enabled) {
-      _unsplit_dex_zip =
-          "${target_gen_dir}/${target_name}/${target_name}__sync_dex.zip"
-      dex(_sync_dex_target) {
-        enable_multidex = _enable_multidex
-        proguard_enabled = true
-        proguard_mapping_path = _sync_proguard_mapping_path
-        forward_variables_from(invoker,
-                               [
-                                 "proguard_jar_path",
-                                 "min_sdk_version",
-                               ])
-        build_config = _build_config
+      # If this Bundle uses a static library, the static library APK will
+      # create the synchronized dex file path.
+      if (_uses_static_library) {
+        if (defined(invoker.min_sdk_version)) {
+          not_needed(invoker, [ "min_sdk_version" ])
+        }
+        if (defined(proguard_jar_path)) {
+          not_needed(invoker, [ "proguard_jar_path" ])
+        }
+      } else {
+        dex(_sync_dex_target) {
+          enable_multidex = _enable_multidex
+          proguard_enabled = true
+          proguard_mapping_path = _proguard_mapping_path
+          forward_variables_from(invoker,
+                                 [
+                                   "proguard_jar_path",
+                                   "min_sdk_version",
+                                 ])
+          build_config = _build_config
 
-        deps = _sync_module_targets + [ ":$_build_config_target" ]
-        output = _unsplit_dex_zip
+          deps = _sync_module_java_targets + [ ":$_build_config_target" ]
+          output = _unsplit_dex_zip
+        }
       }
 
-      _dexsplitter_target = "${_sync_dex_target}__dexsplitter"
+      _dexsplitter_target = "${target_name}__dexsplitter"
       dexsplitter(_dexsplitter_target) {
         input_dex_zip = _unsplit_dex_zip
         proguard_mapping = _sync_proguard_mapping_path
         all_modules = _sync_modules
         deps = [
-          ":${_sync_dex_target}",
-        ]
+                 ":$_build_config_target",
+                 _sync_dex_target_dep,
+               ] + _sync_module_java_targets
       }
     }
 
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index fab6fe1ab..5cf911f 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8914872956664439568
\ No newline at end of file
+8914845776329525072
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index ee8554c..4a60987 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8914885528414328080
\ No newline at end of file
+8914845448229350400
\ No newline at end of file
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 556d26d..05c1e95 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/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/android/resource_sizes.gni")
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
 import("//build/config/python.gni")
@@ -272,6 +273,7 @@
     "//base:jni_java",
     "//chrome/android/features/keyboard_accessory/factory:java",
     "//chrome/android/features/keyboard_accessory/public:java",
+    "//chrome/android/public/lifecycle:java",
     "//chrome/android/third_party/compositor_animator:compositor_animator_java",
     "//chrome/android/webapk/libs/client:client_java",
     "//chrome/android/webapk/libs/common:common_java",
@@ -1661,6 +1663,13 @@
   enable_multidex = is_java_debug
 }
 
+android_resource_sizes_test("resource_sizes_chrome_public_apk") {
+  apk_name = "ChromePublic"
+  data_deps = [
+    ":chrome_public_apk",
+  ]
+}
+
 chrome_public_apk_or_module_tmpl("chrome_modern_public_apk") {
   target_type = "android_apk"
   apk_name = "ChromeModernPublic"
@@ -1754,6 +1763,7 @@
                              "verify_android_configuration",
                              "proguard_jar_path",
                              "resource_ids_provider_dep",
+                             "static_library_provider",
                              "target_type",
                              "use_trichrome_library",
                              "version_code",
@@ -1804,8 +1814,32 @@
     android_manifest_dep = ":trichrome_library_android_manifest"
     if (trichrome_synchronized_proguard) {
       static_library_dependent_targets = [
-        ":trichrome_chrome_apk",
         "//android_webview:trichrome_webview_apk",
+
+        # Webview must be listed first WebView's R classes take precedence.
+        # TODO(http://crbug.com/901465): Make this less subtle by handling
+        # order in writing_build_config.py by prioritizing the entry that
+        # matches resource_ids_provider_dep.
+        ":trichrome_chrome_apk",
+      ]
+    }
+  }
+
+  # This target should be removed once Trichrome only supports Chrome bundle
+  # builds.
+  trichrome_library_apk_tmpl("trichrome_library_for_bundle_apk") {
+    apk_name = "TrichromeLibraryForBundle"
+    android_manifest = trichrome_library_android_manifest
+    android_manifest_dep = ":trichrome_library_android_manifest"
+    if (trichrome_synchronized_proguard) {
+      static_library_dependent_targets = [
+        "//android_webview:trichrome_webview_for_bundle_apk",
+
+        # Webview must be listed first WebView's R classes take precedence.
+        # TODO(http://crbug.com/901465): Make this less subtle by handling
+        # order in write_build_config.py by prioritizing the entry that
+        # matches resource_ids_provider_dep.
+        ":trichrome_chrome_bundle",
       ]
     }
   }
@@ -1825,6 +1859,7 @@
   target_type = "android_apk"
   use_trichrome_library = true
   if (trichrome_synchronized_proguard) {
+    static_library_provider = ":trichrome_library_apk"
     resource_ids_provider_dep = "//android_webview:trichrome_webview_apk"
   }
 }
@@ -2071,6 +2106,14 @@
     ]
     bundle_path = "$root_build_dir/apks/ChromeModernPublic.aab"
   }
+
+  android_resource_sizes_test(
+      "resource_sizes_chrome_modern_public_minimal_apks") {
+    file_path = "$root_build_dir/apks/ChromeModernPublic.minimal.apks"
+    data_deps = [
+      ":chrome_modern_public_minimal_apks",
+    ]
+  }
 }
 
 template("monochrome_or_trichrome_public_bundle_tmpl") {
@@ -2129,6 +2172,10 @@
         !_is_trichrome) {
       verify_android_configuration = true
     }
+    if (trichrome_synchronized_proguard) {
+      resource_ids_provider_dep =
+          "//android_webview:trichrome_webview_for_bundle_apk"
+    }
   }
 
   if (enable_arcore || enable_vr) {
@@ -2195,6 +2242,10 @@
     enable_language_splits = enable_chrome_language_splits
     min_sdk_version = _min_sdk_version
 
+    if (trichrome_synchronized_proguard) {
+      static_library_provider = ":trichrome_library_for_bundle_apk"
+    }
+
     extra_modules = []
     if (enable_arcore) {
       extra_modules += [
@@ -2235,6 +2286,12 @@
     ]
     bundle_path = "$root_build_dir/apks/MonochromePublic.aab"
   }
+  android_resource_sizes_test("resource_sizes_monochrome_public_minimal_apks") {
+    file_path = "$root_build_dir/apks/MonochromePublic.minimal.apks"
+    data_deps = [
+      ":monochrome_public_minimal_apks",
+    ]
+  }
 }
 
 monochrome_or_trichrome_public_bundle_tmpl("trichrome_chrome_bundle") {
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 881a6870..83dd17ae 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -805,7 +805,7 @@
   "java/src/org/chromium/chrome/browser/infobar/translate/TranslateMenuHelper.java",
   "java/src/org/chromium/chrome/browser/infobar/translate/TranslateTabContent.java",
   "java/src/org/chromium/chrome/browser/infobar/translate/TranslateTabLayout.java",
-  "java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcher.java",
+  "java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcherImpl.java",
   "java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java",
   "java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java",
   "java/src/org/chromium/chrome/browser/init/BrowserParts.java",
@@ -841,15 +841,6 @@
   "java/src/org/chromium/chrome/browser/jsdialog/JavascriptModalDialog.java",
   "java/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialog.java",
   "java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java",
-  "java/src/org/chromium/chrome/browser/lifecycle/ActivityResultWithNativeObserver.java",
-  "java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java",
-  "java/src/org/chromium/chrome/browser/lifecycle/InflationObserver.java",
-  "java/src/org/chromium/chrome/browser/lifecycle/LifecycleObserver.java",
-  "java/src/org/chromium/chrome/browser/lifecycle/NativeInitObserver.java",
-  "java/src/org/chromium/chrome/browser/lifecycle/PauseResumeWithNativeObserver.java",
-  "java/src/org/chromium/chrome/browser/lifecycle/SaveInstanceStateObserver.java",
-  "java/src/org/chromium/chrome/browser/lifecycle/StartStopWithNativeObserver.java",
-  "java/src/org/chromium/chrome/browser/lifecycle/WindowFocusChangedObserver.java",
   "java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java",
   "java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java",
   "java/src/org/chromium/chrome/browser/locale/LocaleChangedBroadcastReceiver.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 06e8c52..3fd6e9fc 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -245,7 +245,10 @@
     } else {
       deps += [ "//chrome/android:bundle_canary_java" ]
     }
-    product_version_resources_dep = "//chrome/android:product_version_resources"
+    if (!_is_trichrome) {
+      product_version_resources_dep =
+          "//chrome/android:product_version_resources"
+    }
 
     if (defined(invoker.add_unwind_tables_in_apk) &&
         invoker.add_unwind_tables_in_apk) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
index f544795..b7641780 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
@@ -11,7 +11,7 @@
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeController;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabList;
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 5cee4546..0ddf740 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
@@ -15,7 +15,7 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.ThemeColorProvider;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
 import org.chromium.chrome.browser.tab.Tab;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeInactivityTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeInactivityTracker.java
index ee35000..f4b1187 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeInactivityTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeInactivityTracker.java
@@ -9,7 +9,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
 import org.chromium.chrome.browser.lifecycle.StartStopWithNativeObserver;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index 998fda4..976e657 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -132,6 +132,11 @@
          * Called when the input values in the unmask prompt have been validated.
          */
         void onCardUnmaskPromptValidationDone(CardUnmaskPrompt prompt);
+
+        /**
+         * Called when submitting through the soft keyboard was disallowed.
+         */
+        void onCardUnmaskPromptSubmitRejected(CardUnmaskPrompt prompt);
     }
 
     public CardUnmaskPrompt(Context context, CardUnmaskPromptDelegate delegate, String title,
@@ -189,7 +194,11 @@
         // Hitting the "submit" button on the software keyboard should submit the form if valid.
         mCardUnmaskInput.setOnEditorActionListener((v14, actionId, event) -> {
             if (actionId == EditorInfo.IME_ACTION_DONE) {
-                onClick(mDialogModel, ModalDialogProperties.ButtonType.POSITIVE);
+                if (!mDialogModel.get(ModalDialogProperties.POSITIVE_BUTTON_DISABLED)) {
+                    onClick(mDialogModel, ModalDialogProperties.ButtonType.POSITIVE);
+                } else if (sObserverForTest != null) {
+                    sObserverForTest.onCardUnmaskPromptSubmitRejected(this);
+                }
                 return true;
             }
             return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureController.java
index fe009d7..51719ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureController.java
@@ -14,7 +14,7 @@
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityModel;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationState;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationStatus;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorder.java
index 6f1962d..271fc803 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorder.java
@@ -11,7 +11,7 @@
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationState;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationStatus;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
 
 import javax.inject.Inject;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityToolbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityToolbarController.java
index 4d4d983..d35c92ee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityToolbarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityToolbarController.java
@@ -10,7 +10,7 @@
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationState;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationStatus;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 
 import javax.inject.Inject;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
index 938cc0c6..fe176af9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
@@ -24,7 +24,7 @@
 import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.lifecycle.SaveInstanceStateObserver;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashScreenController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashScreenController.java
index 0840dc0..546ba20 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashScreenController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashScreenController.java
@@ -27,7 +27,7 @@
 import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
 import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
 import org.chromium.chrome.browser.customtabs.TranslucentCustomTabActivity;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityDisclosureView.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityDisclosureView.java
index fea51e9..f99ecd2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityDisclosureView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityDisclosureView.java
@@ -15,7 +15,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityModel;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.StartStopWithNativeObserver;
 import org.chromium.chrome.browser.snackbar.Snackbar;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java
index e0e6a9b..6758122 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java
@@ -11,7 +11,7 @@
 import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.preferences.ContextualSuggestionsPreference;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java
index 159b31b..9bd8a67 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java
@@ -15,7 +15,7 @@
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.gsa.GSAState;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
 import org.chromium.chrome.browser.rappor.RapporServiceBridge;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
index ea32135..d633301 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
@@ -10,7 +10,7 @@
 import android.support.v7.app.AppCompatDelegate;
 
 import org.chromium.base.ObserverList;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
 import org.chromium.chrome.browser.night_mode.SystemNightModeMonitor;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/TabObserverRegistrar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/TabObserverRegistrar.java
index f7ffbbd..04d372a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/TabObserverRegistrar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/TabObserverRegistrar.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.customtabs;
 
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.metrics.PageLoadMetrics;
 import org.chromium.chrome.browser.tab.Tab;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java
index f06e69a..611e5f0b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java
@@ -31,8 +31,8 @@
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.StartStopWithNativeObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index f2dd7f4..203a2600 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -34,7 +34,7 @@
 import org.chromium.chrome.browser.customtabs.PageLoadMetricsObserver;
 import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegate.java
index d959ed16..b82738bd3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegate.java
@@ -9,7 +9,7 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
 import org.chromium.chrome.browser.lifecycle.SaveInstanceStateObserver;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
index 549aed4..4036c59 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
@@ -36,7 +36,7 @@
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.metrics.PageLoadMetrics;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleToolbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleToolbarController.java
index d155f83..35243b5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleToolbarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleToolbarController.java
@@ -10,7 +10,7 @@
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java
index 7031adb3..0493eb3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java
@@ -16,7 +16,7 @@
 import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcherImpl.java
similarity index 93%
rename from chrome/android/java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcher.java
rename to chrome/android/java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcherImpl.java
index 76ede8b..f64d281 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcherImpl.java
@@ -8,6 +8,7 @@
 import android.os.Bundle;
 
 import org.chromium.base.ObserverList;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.ActivityResultWithNativeObserver;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
@@ -24,7 +25,7 @@
  *
  * All observers will be automatically cleared when the backing activity is destroyed.
  */
-public class ActivityLifecycleDispatcher {
+public class ActivityLifecycleDispatcherImpl implements ActivityLifecycleDispatcher {
     private final ObserverList<InflationObserver> mInflationObservers = new ObserverList<>();
     private final ObserverList<NativeInitObserver> mNativeInitObservers = new ObserverList<>();
     private final ObserverList<PauseResumeWithNativeObserver> mPauseResumeObservers =
@@ -39,11 +40,7 @@
     private final ObserverList<ActivityResultWithNativeObserver>
             mActivityResultWithNativeObservers = new ObserverList<>();
 
-    /**
-     * Registers an observer.
-     * @param observer must implement one or several observer interfaces in
-     * {@link org.chromium.chrome.browser.lifecycle} in order to receive corresponding events.
-     */
+    @Override
     public void register(LifecycleObserver observer) {
         if (observer instanceof InflationObserver) {
             mInflationObservers.addObserver((InflationObserver) observer);
@@ -72,9 +69,7 @@
         }
     }
 
-    /**
-     * Unregisters an observer.
-     */
+    @Override
     public void unregister(LifecycleObserver observer) {
         if (observer instanceof InflationObserver) {
             mInflationObservers.removeObserver((InflationObserver) observer);
@@ -164,13 +159,13 @@
     }
 
     void dispatchOnSaveInstanceState(Bundle outBundle) {
-        for (SaveInstanceStateObserver observer: mSaveInstanceStateObservers) {
+        for (SaveInstanceStateObserver observer : mSaveInstanceStateObservers) {
             observer.onSaveInstanceState(outBundle);
         }
     }
 
     void dispatchOnWindowFocusChanged(boolean hasFocus) {
-        for (WindowFocusChangedObserver observer: mWindowFocusChangesObservers) {
+        for (WindowFocusChangedObserver observer : mWindowFocusChangesObservers) {
             observer.onWindowFocusChanged(hasFocus);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
index 62f9aaa..577ba3c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -39,6 +39,7 @@
 import org.chromium.chrome.browser.LaunchIntentDispatcher;
 import org.chromium.chrome.browser.WarmupManager;
 import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tabmodel.DocumentModeAssassin;
 import org.chromium.chrome.browser.upgrade.UpgradeActivity;
@@ -63,8 +64,8 @@
     private final NativeInitializationController mNativeInitializationController =
             new NativeInitializationController(this);
 
-    private final ActivityLifecycleDispatcher mLifecycleDispatcher =
-            new ActivityLifecycleDispatcher();
+    private final ActivityLifecycleDispatcherImpl mLifecycleDispatcher =
+            new ActivityLifecycleDispatcherImpl();
 
     /** Time at which onCreate is called. This is realtime, counted in ms since device boot. */
     private long mOnCreateTimestampMs;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/ActivityResultWithNativeObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/ActivityResultWithNativeObserver.java
deleted file mode 100644
index 78deb16..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/ActivityResultWithNativeObserver.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.lifecycle;
-
-import android.content.Intent;
-
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.init.AsyncInitializationActivity;
-
-/**
- * Implement this interface and register in {@link ActivityLifecycleDispatcher} to receive
- * activity result methods.
- */
-public interface ActivityResultWithNativeObserver extends LifecycleObserver {
-    /**
-     * Called when {@link AsyncInitializationActivity#onActivityResult(int, int, Intent)} is called.
-     */
-    void onActivityResultWithNative(int requestCode, int resultCode, Intent data);
-}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/InflationObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/InflationObserver.java
deleted file mode 100644
index c20863b..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/InflationObserver.java
+++ /dev/null
@@ -1,27 +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.
-
-package org.chromium.chrome.browser.lifecycle;
-
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.init.BrowserParts;
-
-/**
- * Implement this interface and register in {@link ActivityLifecycleDispatcher} to receive
- * inflation-related events.
- */
-public interface InflationObserver extends LifecycleObserver {
-
-    /**
-     * Called immediately before the view hierarchy is inflated.
-     * See {@link BrowserParts#preInflationStartup()}.
-     */
-    void onPreInflationStartup();
-
-    /**
-     * Called immediately after the view hierarchy is inflated.
-     * See {@link BrowserParts#postInflationStartup()}.
-     */
-    void onPostInflationStartup();
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/MarketURLGetter.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/MarketURLGetter.java
index 09f07b7..ffd1b116 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/MarketURLGetter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/MarketURLGetter.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.omaha;
 
-import android.content.Context;
 import android.content.SharedPreferences;
 
 import org.chromium.base.ThreadUtils;
@@ -20,12 +19,11 @@
         private static final MarketURLGetter INSTANCE = new MarketURLGetter();
     }
 
-    /** See {@link #getMarketUrl(Context)} */
-    static String getMarketUrl(Context context) {
+    static String getMarketUrl() {
         assert !ThreadUtils.runningOnUiThread();
         MarketURLGetter instance =
                 sInstanceForTests == null ? LazyHolder.INSTANCE : sInstanceForTests;
-        return instance.getMarketUrlInternal(context);
+        return instance.getMarketUrlInternal();
     }
 
     @VisibleForTesting
@@ -38,9 +36,9 @@
     protected MarketURLGetter() { }
 
     /** Returns the Play Store URL that points to Chrome. */
-    protected String getMarketUrlInternal(Context context) {
+    protected String getMarketUrlInternal() {
         assert !ThreadUtils.runningOnUiThread();
-        SharedPreferences prefs = OmahaBase.getSharedPreferences(context);
+        SharedPreferences prefs = OmahaBase.getSharedPreferences();
         return prefs.getString(OmahaBase.PREF_MARKET_URL, "");
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
index 002dfd1..7a53809c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
@@ -12,6 +12,7 @@
 import android.text.format.DateUtils;
 
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.StreamUtil;
 import org.chromium.base.VisibleForTesting;
@@ -381,7 +382,7 @@
         ExponentialBackoffScheduler scheduler = getBackoffScheduler();
         long currentTime = scheduler.getCurrentTime();
 
-        SharedPreferences preferences = OmahaBase.getSharedPreferences(context);
+        SharedPreferences preferences = OmahaBase.getSharedPreferences();
         mTimestampForNewRequest =
                 preferences.getLong(OmahaBase.PREF_TIMESTAMP_FOR_NEW_REQUEST, currentTime);
         mTimestampForNextPostAttempt =
@@ -430,7 +431,7 @@
      * Writes out the current state to a file.
      */
     private void saveState(Context context) {
-        SharedPreferences prefs = OmahaBase.getSharedPreferences(context);
+        SharedPreferences prefs = OmahaBase.getSharedPreferences();
         SharedPreferences.Editor editor = prefs.edit();
         editor.putBoolean(OmahaBase.PREF_SEND_INSTALL_EVENT, mSendInstallEvent);
         editor.putLong(OmahaBase.PREF_TIMESTAMP_OF_INSTALL, mTimestampOfInstall);
@@ -477,7 +478,7 @@
 
     /** Checks whether Chrome has ever tried contacting Omaha before. */
     public static boolean isProbablyFreshInstall(Context context) {
-        SharedPreferences prefs = getSharedPreferences(context);
+        SharedPreferences prefs = getSharedPreferences();
         return prefs.getLong(PREF_TIMESTAMP_OF_INSTALL, -1) == -1;
     }
 
@@ -526,8 +527,9 @@
     }
 
     /** Returns the Omaha SharedPreferences. */
-    static SharedPreferences getSharedPreferences(Context context) {
-        return context.getSharedPreferences(PREF_PACKAGE, Context.MODE_PRIVATE);
+    static SharedPreferences getSharedPreferences() {
+        return ContextUtils.getApplicationContext().getSharedPreferences(
+                PREF_PACKAGE, Context.MODE_PRIVATE);
     }
 
     static void setVersionConfig(SharedPreferences.Editor editor, VersionConfig versionConfig) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java
index 0c4ffc0d..b69298f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java
@@ -152,15 +152,13 @@
     private static boolean shouldPushNotification() {
         if (!isUpdateNotificationEnabled()) return false;
         long currentTime = System.currentTimeMillis();
-        SharedPreferences preferences =
-                OmahaBase.getSharedPreferences(ContextUtils.getApplicationContext());
+        SharedPreferences preferences = OmahaBase.getSharedPreferences();
         long lastPushedTimeStamp = preferences.getLong(PREF_LAST_TIME_UPDATE_NOTIFICATION_KEY, 0);
         return currentTime - lastPushedTimeStamp >= getUpdateNotificationInterval();
     }
 
     private static void updateLastPushedTimeStamp() {
-        SharedPreferences preferences =
-                OmahaBase.getSharedPreferences(ContextUtils.getApplicationContext());
+        SharedPreferences preferences = OmahaBase.getSharedPreferences();
         SharedPreferences.Editor editor = preferences.edit();
         editor.putLong(PREF_LAST_TIME_UPDATE_NOTIFICATION_KEY, System.currentTimeMillis());
         editor.apply();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java
index b8d43ed3..8d7c569 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java
@@ -383,7 +383,7 @@
             UpdateStatus status = new UpdateStatus();
 
             if (VersionNumberGetter.isNewerVersionAvailable(context)) {
-                status.updateUrl = MarketURLGetter.getMarketUrl(context);
+                status.updateUrl = MarketURLGetter.getMarketUrl();
                 status.latestVersion =
                         VersionNumberGetter.getInstance().getLatestKnownVersion(context);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java
index 0bcacf3f..d80bd295 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java
@@ -53,7 +53,7 @@
      */
     public String getLatestKnownVersion(Context context) {
         assert !ThreadUtils.runningOnUiThread();
-        SharedPreferences prefs = OmahaBase.getSharedPreferences(context);
+        SharedPreferences prefs = OmahaBase.getSharedPreferences();
         return prefs.getString(OmahaBase.PREF_LATEST_VERSION, "");
     }
 
@@ -122,7 +122,7 @@
 
         // If the market link is bad, don't show an update to avoid frustrating users trying to
         // hit the "Update" button.
-        if ("".equals(MarketURLGetter.getMarketUrl(context))) {
+        if ("".equals(MarketURLGetter.getMarketUrl())) {
             return false;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index 77d63a4..bd42d6ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -23,8 +23,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.StartStopWithNativeObserver;
 import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/JourneyManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/JourneyManager.java
index a7c69a2..8dcd0dd0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/JourneyManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/JourneyManager.java
@@ -16,7 +16,7 @@
 import org.chromium.chrome.browser.ChromeVersionInfo;
 import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java
index 327b933b..23450d5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java
@@ -107,9 +107,7 @@
     private static final Object LOCK = new Object();
     private static List<ActivityAssigner> sInstances;
 
-    private final Context mContext;
     private final List<ActivityEntry> mActivityList;
-
     private final String mPrefPackage;
     private final String mPrefNumSavedEntries;
     private final String mPrefActivityIndex;
@@ -154,8 +152,6 @@
     }
 
     private ActivityAssigner(int activityTypeIndex) {
-        mContext = ContextUtils.getApplicationContext();
-
         mPrefPackage = PREF_PACKAGE[activityTypeIndex];
         mPrefNumSavedEntries = PREF_NUM_SAVED_ENTRIES[activityTypeIndex];
         mPrefActivityIndex = PREF_ACTIVITY_INDEX[activityTypeIndex];
@@ -274,7 +270,8 @@
 
         // Restore any entries that were previously saved.  If it seems that the preferences have
         // been corrupted somehow, just discard the whole map.
-        SharedPreferences prefs = mContext.getSharedPreferences(mPrefPackage, Context.MODE_PRIVATE);
+        SharedPreferences prefs = ContextUtils.getApplicationContext().getSharedPreferences(
+                mPrefPackage, Context.MODE_PRIVATE);
         try {
             long time = SystemClock.elapsedRealtime();
             final int numSavedEntries = prefs.getInt(mPrefNumSavedEntries, 0);
@@ -328,7 +325,8 @@
      * Saves the mapping between apps and Activities.
      */
     private void storeActivityList() {
-        SharedPreferences prefs = mContext.getSharedPreferences(mPrefPackage, Context.MODE_PRIVATE);
+        SharedPreferences prefs = ContextUtils.getApplicationContext().getSharedPreferences(
+                mPrefPackage, Context.MODE_PRIVATE);
         SharedPreferences.Editor editor = prefs.edit();
         editor.clear();
         editor.putInt(mPrefNumSavedEntries, mActivityList.size());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java
index 9bef6908..1bbc61c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java
@@ -13,7 +13,7 @@
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager.OverlayPanelManagerObserver;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
index b6d445ff1..1ef1d965 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
@@ -307,7 +307,7 @@
         mDelegate.getScheduler().setCurrentTime(now);
 
         // Record that an install event has already been sent and that we're due for a new request.
-        SharedPreferences.Editor editor = OmahaBase.getSharedPreferences(mContext).edit();
+        SharedPreferences.Editor editor = OmahaBase.getSharedPreferences().edit();
         editor.putBoolean(OmahaBase.PREF_SEND_INSTALL_EVENT, false);
         editor.putLong(OmahaBase.PREF_TIMESTAMP_FOR_NEW_REQUEST, now);
         editor.putLong(OmahaBase.PREF_TIMESTAMP_FOR_NEXT_POST_ATTEMPT, now);
@@ -342,7 +342,7 @@
         mDelegate.getScheduler().setCurrentTime(now);
 
         // Put the time for the next request in the future.
-        SharedPreferences prefs = OmahaBase.getSharedPreferences(mContext);
+        SharedPreferences prefs = OmahaBase.getSharedPreferences();
         prefs.edit().putLong(OmahaBase.PREF_TIMESTAMP_FOR_NEW_REQUEST, later).apply();
 
         // Trigger Omaha.
@@ -372,7 +372,7 @@
         mDelegate = new MockOmahaDelegate(mContext, DeviceType.HANDSET, InstallSource.ORGANIC);
         mDelegate.getScheduler().setCurrentTime(now);
 
-        SharedPreferences prefs = OmahaBase.getSharedPreferences(mContext);
+        SharedPreferences prefs = OmahaBase.getSharedPreferences();
         SharedPreferences.Editor editor = prefs.edit();
 
         // Make it so that a request was generated and is just waiting to be sent.
@@ -415,7 +415,7 @@
         mDelegate = new MockOmahaDelegate(mContext, DeviceType.HANDSET, InstallSource.ORGANIC);
         mDelegate.getScheduler().setCurrentTime(now);
 
-        SharedPreferences prefs = OmahaBase.getSharedPreferences(mContext);
+        SharedPreferences prefs = OmahaBase.getSharedPreferences();
         SharedPreferences.Editor editor = prefs.edit();
 
         // Make it so that a regular <ping> was generated and is just waiting to be sent.
@@ -460,7 +460,7 @@
         mDelegate = new MockOmahaDelegate(mContext, DeviceType.HANDSET, InstallSource.ORGANIC);
         mDelegate.getScheduler().setCurrentTime(now);
 
-        SharedPreferences prefs = OmahaBase.getSharedPreferences(mContext);
+        SharedPreferences prefs = OmahaBase.getSharedPreferences();
         SharedPreferences.Editor editor = prefs.edit();
 
         // Make it so that a regular <ping> was generated and is just waiting to be sent.
@@ -505,7 +505,7 @@
         mDelegate = new MockOmahaDelegate(mContext, DeviceType.HANDSET, InstallSource.ORGANIC);
         mDelegate.getScheduler().setCurrentTime(now);
 
-        SharedPreferences prefs = OmahaBase.getSharedPreferences(mContext);
+        SharedPreferences prefs = OmahaBase.getSharedPreferences();
         SharedPreferences.Editor editor = prefs.edit();
 
         // Indicate that the next request should be generated way past an expected timeframe.
@@ -548,7 +548,7 @@
 
         // Record that a regular <ping> was generated, but not sent, then assign it an invalid
         // timestamp and try to send it now.
-        SharedPreferences prefs = OmahaBase.getSharedPreferences(mContext);
+        SharedPreferences prefs = OmahaBase.getSharedPreferences();
         SharedPreferences.Editor editor = prefs.edit();
         editor.putBoolean(OmahaBase.PREF_SEND_INSTALL_EVENT, false);
         editor.putLong(OmahaBase.PREF_TIMESTAMP_FOR_NEW_REQUEST, timeRegisterNewRequest);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelperTest.java
index 2d792a2..5f27202 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelperTest.java
@@ -88,7 +88,7 @@
         }
 
         @Override
-        protected String getMarketUrlInternal(Context context) {
+        protected String getMarketUrlInternal() {
             return mURL;
         }
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExpiredLocalCardTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExpiredLocalCardTest.java
index 05e6674..2dbcbb32 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExpiredLocalCardTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExpiredLocalCardTest.java
@@ -219,4 +219,20 @@
 
         mRule.hitSoftwareKeyboardSubmitButtonAndWait(R.id.card_unmask_input, mRule.getDismissed());
     }
+
+    /**
+     * Tests that hitting "submit" on the software keyboard in the CVC number field with no CVC set
+     * will not submit the CVC unmask dialog.
+     */
+    @MediumTest
+    @Feature({"Payments"})
+    @Test
+    public void testNoSoftwareKeyboardSubmitInCvcNumberFieldIfInvalid()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.triggerUIAndWait(mRule.getReadyToPay());
+        mRule.clickAndWait(R.id.button_primary, mRule.getReadyForUnmaskInput());
+
+        mRule.hitSoftwareKeyboardSubmitButtonAndWait(
+                R.id.card_unmask_input, mRule.getSubmitRejected());
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
index 9d569c0..a822373d4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
@@ -107,6 +107,7 @@
     final PaymentsCallbackHelper<CardUnmaskPrompt> mReadyForUnmaskInput;
     final PaymentsCallbackHelper<CardUnmaskPrompt> mReadyToUnmask;
     final PaymentsCallbackHelper<CardUnmaskPrompt> mUnmaskValidationDone;
+    final PaymentsCallbackHelper<CardUnmaskPrompt> mSubmitRejected;
     final CallbackHelper mReadyToEdit;
     final CallbackHelper mEditorValidationError;
     final CallbackHelper mEditorTextUpdate;
@@ -138,6 +139,7 @@
         mReadyForUnmaskInput = new PaymentsCallbackHelper<>();
         mReadyToUnmask = new PaymentsCallbackHelper<>();
         mUnmaskValidationDone = new PaymentsCallbackHelper<>();
+        mSubmitRejected = new PaymentsCallbackHelper<>();
         mReadyToEdit = new CallbackHelper();
         mEditorValidationError = new CallbackHelper();
         mEditorTextUpdate = new CallbackHelper();
@@ -198,6 +200,9 @@
     public PaymentsCallbackHelper<CardUnmaskPrompt> getUnmaskValidationDone() {
         return mUnmaskValidationDone;
     }
+    public PaymentsCallbackHelper<CardUnmaskPrompt> getSubmitRejected() {
+        return mSubmitRejected;
+    }
     public CallbackHelper getReadyToEdit() {
         return mReadyToEdit;
     }
@@ -1011,6 +1016,12 @@
     }
 
     @Override
+    public void onCardUnmaskPromptSubmitRejected(CardUnmaskPrompt prompt) {
+        ThreadUtils.assertOnUiThread();
+        mSubmitRejected.notifyCalled(prompt);
+    }
+
+    @Override
     public void onPaymentResponseReady() {
         ThreadUtils.assertOnUiThread();
         mPaymentResponseReady.notifyCalled();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureControllerTest.java
index 0a247e3c..9991625 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureControllerTest.java
@@ -31,7 +31,7 @@
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityModel;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationState;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationStatus;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 
 /**
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorderTest.java
index c31a8af..597a27f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorderTest.java
@@ -29,7 +29,7 @@
 import org.chromium.chrome.browser.browserservices.TrustedWebActivityUmaRecorder;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationState;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationStatus;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 
 import java.util.concurrent.TimeUnit;
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifierTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifierTest.java
index db32d7d..00d140f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifierTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifierTest.java
@@ -39,7 +39,7 @@
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
 import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.test.util.browser.Features;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
index 5c13b0a4..ab544b65 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
@@ -44,8 +44,8 @@
 import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
 import org.chromium.chrome.browser.customtabs.shadows.ShadowExternalNavigationDelegateImpl;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
 import org.chromium.chrome.browser.tabmodel.TabModel;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java
index af740e2..c460d61 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java
@@ -28,7 +28,7 @@
 import org.chromium.base.task.test.BackgroundShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tabmodel.TabList;
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 6ed6bad0..4c1c9e9 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-76.0.3779.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-76.0.3780.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/android/public/DEPS b/chrome/android/public/DEPS
new file mode 100644
index 0000000..f26c162c
--- /dev/null
+++ b/chrome/android/public/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "-chrome/android/java",
+  "-chrome/android/features",
+]
diff --git a/chrome/android/public/lifecycle/BUILD.gn b/chrome/android/public/lifecycle/BUILD.gn
new file mode 100644
index 0000000..5b823649
--- /dev/null
+++ b/chrome/android/public/lifecycle/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  java_files = [
+    "java/src/org/chromium/chrome/browser/lifecycle/ActivityLifecycleDispatcher.java",
+    "java/src/org/chromium/chrome/browser/lifecycle/ActivityResultWithNativeObserver.java",
+    "java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java",
+    "java/src/org/chromium/chrome/browser/lifecycle/InflationObserver.java",
+    "java/src/org/chromium/chrome/browser/lifecycle/LifecycleObserver.java",
+    "java/src/org/chromium/chrome/browser/lifecycle/NativeInitObserver.java",
+    "java/src/org/chromium/chrome/browser/lifecycle/PauseResumeWithNativeObserver.java",
+    "java/src/org/chromium/chrome/browser/lifecycle/SaveInstanceStateObserver.java",
+    "java/src/org/chromium/chrome/browser/lifecycle/StartStopWithNativeObserver.java",
+    "java/src/org/chromium/chrome/browser/lifecycle/WindowFocusChangedObserver.java",
+  ]
+}
diff --git a/chrome/android/public/lifecycle/OWNERS b/chrome/android/public/lifecycle/OWNERS
new file mode 100644
index 0000000..e30356a
--- /dev/null
+++ b/chrome/android/public/lifecycle/OWNERS
@@ -0,0 +1,4 @@
+pshmakov@chromium.org
+
+# COMPONENT: UI>Browser>Mobile
+# OS: Android
diff --git a/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/ActivityLifecycleDispatcher.java b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/ActivityLifecycleDispatcher.java
new file mode 100644
index 0000000..1833464
--- /dev/null
+++ b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/ActivityLifecycleDispatcher.java
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.lifecycle;
+
+/**
+ * Manages registration of {@link LifecycleObserver} instances.
+ */
+public interface ActivityLifecycleDispatcher {
+    /**
+     * Registers an observer.
+     * @param observer must implement one or several observer interfaces in
+     * {@link org.chromium.chrome.browser.lifecycle} in order to receive corresponding events.
+     */
+    public void register(LifecycleObserver observer);
+
+    /**
+     * Unregisters an observer.
+     */
+    public void unregister(LifecycleObserver observer);
+}
diff --git a/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/ActivityResultWithNativeObserver.java b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/ActivityResultWithNativeObserver.java
new file mode 100644
index 0000000..06b5903
--- /dev/null
+++ b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/ActivityResultWithNativeObserver.java
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.lifecycle;
+
+import android.content.Intent;
+
+/**
+ * Implement this interface and register in {@link
+ * org.chromium.chrome.browser.init.ActivityLifecycleDispatcher} to receive activity result methods.
+ */
+public interface ActivityResultWithNativeObserver extends LifecycleObserver {
+    /**
+     * Called when {@link
+     * org.chromium.chrome.browser.init.AsyncInitializationActivity#onActivityResult(int, int,
+     * Intent)} is called.
+     */
+    void onActivityResultWithNative(int requestCode, int resultCode, Intent data);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java
similarity index 66%
rename from chrome/android/java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java
rename to chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java
index b5ea62e..a33c2b9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java
+++ b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java
@@ -4,14 +4,11 @@
 
 package org.chromium.chrome.browser.lifecycle;
 
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
-
 /**
- * Implement this interface and register in {@link ActivityLifecycleDispatcher} to receive
- * destroy events.
+ * Implement this interface and register in {@link
+ * org.chromium.chrome.browser.init.ActivityLifecycleDispatcher} to receive destroy events.
  */
 public interface Destroyable extends LifecycleObserver {
-
     /**
      * Called when activity is being destroyed.
      */
diff --git a/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/InflationObserver.java b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/InflationObserver.java
new file mode 100644
index 0000000..dceeec83
--- /dev/null
+++ b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/InflationObserver.java
@@ -0,0 +1,24 @@
+// 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.
+
+package org.chromium.chrome.browser.lifecycle;
+
+/**
+ * Implement this interface and register in {@link
+ * org.chromium.chrome.browser.init.ActivityLifecycleDispatcher} to receive inflation-related
+ * events.
+ */
+public interface InflationObserver extends LifecycleObserver {
+    /**
+     * Called immediately before the view hierarchy is inflated.
+     * See {@link org.chromium.chrome.browser.init.BrowserParts#preInflationStartup()}.
+     */
+    void onPreInflationStartup();
+
+    /**
+     * Called immediately after the view hierarchy is inflated.
+     * See {@link org.chromium.chrome.browser.init.BrowserParts#postInflationStartup()}.
+     */
+    void onPostInflationStartup();
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/LifecycleObserver.java b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/LifecycleObserver.java
similarity index 87%
rename from chrome/android/java/src/org/chromium/chrome/browser/lifecycle/LifecycleObserver.java
rename to chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/LifecycleObserver.java
index 90279928..6c4a394 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/LifecycleObserver.java
+++ b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/LifecycleObserver.java
@@ -7,5 +7,4 @@
 /**
  * Parent interface for lifecycle observer interfaces.
  */
-public interface LifecycleObserver {
-}
+public interface LifecycleObserver {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/NativeInitObserver.java b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/NativeInitObserver.java
similarity index 66%
rename from chrome/android/java/src/org/chromium/chrome/browser/lifecycle/NativeInitObserver.java
rename to chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/NativeInitObserver.java
index af5dc65..81a040c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/NativeInitObserver.java
+++ b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/NativeInitObserver.java
@@ -4,14 +4,12 @@
 
 package org.chromium.chrome.browser.lifecycle;
 
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
-
 /**
- * Implement this interface and register in {@link ActivityLifecycleDispatcher} to get
- * notified of native having been loaded.
+ * Implement this interface and register in {@link
+ * org.chromium.chrome.browser.init.ActivityLifecycleDispatcher} to get notified of native having
+ * been loaded.
  */
 public interface NativeInitObserver extends LifecycleObserver {
-
     /**
      * Called when the native library has finished loading.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/PauseResumeWithNativeObserver.java b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/PauseResumeWithNativeObserver.java
similarity index 75%
rename from chrome/android/java/src/org/chromium/chrome/browser/lifecycle/PauseResumeWithNativeObserver.java
rename to chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/PauseResumeWithNativeObserver.java
index 4fb1fed3..9cb5f9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/PauseResumeWithNativeObserver.java
+++ b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/PauseResumeWithNativeObserver.java
@@ -4,14 +4,12 @@
 
 package org.chromium.chrome.browser.lifecycle;
 
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
-
 /**
- * Implement this interface and register in {@link ActivityLifecycleDispatcher} to receive
- * pause and resume with native events.
+ * Implement this interface and register in {@link
+ * org.chromium.chrome.browser.init.ActivityLifecycleDispatcher} to receive pause and resume with
+ * native events.
  */
 public interface PauseResumeWithNativeObserver extends LifecycleObserver {
-
     /**
      * Called when activity is resumed, provided that native is initialized.
      * If native is not initialized at that point, the call is postponed until it is.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/SaveInstanceStateObserver.java b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/SaveInstanceStateObserver.java
similarity index 69%
rename from chrome/android/java/src/org/chromium/chrome/browser/lifecycle/SaveInstanceStateObserver.java
rename to chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/SaveInstanceStateObserver.java
index a3d1333..c814f97e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/SaveInstanceStateObserver.java
+++ b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/SaveInstanceStateObserver.java
@@ -6,16 +6,14 @@
 
 import android.os.Bundle;
 
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
-
 /**
- * Implement this interface and register in {@link ActivityLifecycleDispatcher} to receive
- * onSaveInstanceState events.
+ * Implement this interface and register in {@link
+ * org.chromium.chrome.browser.init.ActivityLifecycleDispatcher} to receive onSaveInstanceState
+ * events.
  */
 public interface SaveInstanceStateObserver extends LifecycleObserver {
-
     /**
      * Called before activity begins to stop.
      */
     void onSaveInstanceState(Bundle outState);
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/StartStopWithNativeObserver.java b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/StartStopWithNativeObserver.java
similarity index 75%
rename from chrome/android/java/src/org/chromium/chrome/browser/lifecycle/StartStopWithNativeObserver.java
rename to chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/StartStopWithNativeObserver.java
index 4ab8b64..89d4b58 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/StartStopWithNativeObserver.java
+++ b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/StartStopWithNativeObserver.java
@@ -4,14 +4,12 @@
 
 package org.chromium.chrome.browser.lifecycle;
 
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
-
 /**
- * Implement this interface and register in {@link ActivityLifecycleDispatcher} to receive
- * start and stop with native events.
+ * Implement this interface and register in {@link
+ * org.chromium.chrome.browser.init.ActivityLifecycleDispatcher} to receive start and stop with
+ * native events.
  */
 public interface StartStopWithNativeObserver extends LifecycleObserver {
-
     /**
      * Called when activity is started, provided that native is initialized.
      * If native is not initialized at that point, the call is postponed until it is.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/WindowFocusChangedObserver.java b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/WindowFocusChangedObserver.java
similarity index 69%
rename from chrome/android/java/src/org/chromium/chrome/browser/lifecycle/WindowFocusChangedObserver.java
rename to chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/WindowFocusChangedObserver.java
index 624d1093..e472cb82 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/lifecycle/WindowFocusChangedObserver.java
+++ b/chrome/android/public/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/WindowFocusChangedObserver.java
@@ -4,16 +4,14 @@
 
 package org.chromium.chrome.browser.lifecycle;
 
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
-
 /**
- * Implement this interface and register in {@link ActivityLifecycleDispatcher} to receive
- * onWindowFocusChange events.
+ * Implement this interface and register in {@link
+ * org.chromium.chrome.browser.init.ActivityLifecycleDispatcher} to receive onWindowFocusChange
+ * events.
  */
 public interface WindowFocusChangedObserver extends LifecycleObserver {
-
     /**
      * Called when the current Window of the activity gains or loses focus.
      */
     void onWindowFocusChanged(boolean hasFocus);
-}
\ No newline at end of file
+}
diff --git a/chrome/android/trichrome.gni b/chrome/android/trichrome.gni
index 3a74e606..3a642771e 100644
--- a/chrome/android/trichrome.gni
+++ b/chrome/android/trichrome.gni
@@ -47,14 +47,10 @@
                              "apk_name",
                              "android_manifest",
                              "android_manifest_dep",
+                             "proguard_jar_path",
                              "static_library_dependent_targets",
                            ])
 
-    # TODO(torne): this contains the list of locales amongst other things.
-    # Skip building it because the version in the library won't be used anyway;
-    # the one in the main APK will take precedence.
-    generate_buildconfig_java = false
-
     # TODO(torne): since there's no real java code in the library right now,
     # leave out the build hooks and let them get compiled into each APK. Later
     # this should probably be in the library.
@@ -68,6 +64,7 @@
     version_name = chrome_version_name
     version_code = trichrome_version_code
     min_sdk_version = 28
+    product_version_resources_dep = "//chrome/android:product_version_resources"
 
     # TODO(torne): using system_webview_resources just to get a temporary icon
     deps = [
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index dd9381c..7cb1945 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1199,8 +1199,8 @@
     "policy/policy_conversions.h",
     "policy/profile_policy_connector.cc",
     "policy/profile_policy_connector.h",
-    "policy/profile_policy_connector_factory.cc",
-    "policy/profile_policy_connector_factory.h",
+    "policy/profile_policy_connector_builder.cc",
+    "policy/profile_policy_connector_builder.h",
     "policy/schema_registry_service.cc",
     "policy/schema_registry_service.h",
     "policy/schema_registry_service_profile_builder.cc",
@@ -2913,6 +2913,7 @@
       "media/webrtc/desktop_media_picker_factory_impl.h",
       "media/webrtc/display_media_access_handler.cc",
       "media/webrtc/display_media_access_handler.h",
+      "media/webrtc/media_authorization_wrapper_mac.h",
       "media/webrtc/system_media_capture_permissions_mac.h",
       "media/webrtc/system_media_capture_permissions_mac.mm",
       "media/webrtc/system_media_capture_permissions_stats_mac.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 5c550a91..7093c7c 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1611,7 +1611,7 @@
     {"reduced-referrer-granularity",
      flag_descriptions::kReducedReferrerGranularityName,
      flag_descriptions::kReducedReferrerGranularityDescription, kOsAll,
-     SINGLE_VALUE_TYPE(switches::kReducedReferrerGranularity)},
+     FEATURE_VALUE_TYPE(features::kReducedReferrerGranularity)},
 #if defined(OS_CHROMEOS)
     {"crostini-backup", flag_descriptions::kCrostiniBackupName,
      flag_descriptions::kCrostiniBackupDescription, kOsCrOS,
@@ -2042,9 +2042,6 @@
          ntp_snippets::kNotificationsFeature,
          kContentSuggestionsNotificationsFeatureVariations,
          "ContentSuggestionsNotifications")},
-    {"enable-site-exploration-ui", flag_descriptions::kSiteExplorationUiName,
-     flag_descriptions::kSiteExplorationUiDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(ntp_tiles::kSiteExplorationUiFeature)},
 #endif  // OS_ANDROID
     {"user-activation-v2", flag_descriptions::kUserActivationV2Name,
      flag_descriptions::kUserActivationV2Description, kOsAll,
@@ -2184,9 +2181,6 @@
      flag_descriptions::kPassiveEventListenersDueToFlingName,
      flag_descriptions::kPassiveEventListenersDueToFlingDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kPassiveEventListenersDueToFling)},
-    {"enable-font-cache-scaling", flag_descriptions::kFontCacheScalingName,
-     flag_descriptions::kFontCacheScalingDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kFontCacheScaling)},
     {"per-method-can-make-payment-quota",
      flag_descriptions::kPerMethodCanMakePaymentQuotaName,
      flag_descriptions::kPerMethodCanMakePaymentQuotaDescription, kOsAll,
@@ -3155,55 +3149,6 @@
      flag_descriptions::kAutofillRichMetadataQueriesName,
      flag_descriptions::kAutofillRichMetadataQueriesDescription, kOsAll,
      FEATURE_VALUE_TYPE(autofill::features::kAutofillRichMetadataQueries)},
-    {"enable-sync-pseudo-uss-app-list",
-     flag_descriptions::kEnableSyncPseudoUSSAppListName,
-     flag_descriptions::kEnableSyncPseudoUSSAppListDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSAppList)},
-    {"enable-sync-pseudo-uss-apps",
-     flag_descriptions::kEnableSyncPseudoUSSAppsName,
-     flag_descriptions::kEnableSyncPseudoUSSAppsDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSApps)},
-    {"enable-sync-pseudo-uss-dictionary",
-     flag_descriptions::kEnableSyncPseudoUSSDictionaryName,
-     flag_descriptions::kEnableSyncPseudoUSSDictionaryDescription,
-     kOsWin | kOsLinux, FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSDictionary)},
-    {"enable-sync-pseudo-uss-extension-settings",
-     flag_descriptions::kEnableSyncPseudoUSSExtensionSettingsName,
-     flag_descriptions::kEnableSyncPseudoUSSExtensionSettingsDescription,
-     kOsDesktop, FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSExtensionSettings)},
-    {"enable-sync-pseudo-uss-extensions",
-     flag_descriptions::kEnableSyncPseudoUSSExtensionsName,
-     flag_descriptions::kEnableSyncPseudoUSSExtensionsDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSExtensions)},
-    {"enable-sync-pseudo-uss-favicons",
-     flag_descriptions::kEnableSyncPseudoUSSFaviconsName,
-     flag_descriptions::kEnableSyncPseudoUSSFaviconsDescription, kOsAll,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSFavicons)},
-    {"enable-sync-pseudo-uss-history-delete-directives",
-     flag_descriptions::kEnableSyncPseudoUSSHistoryDeleteDirectivesName,
-     flag_descriptions::kEnableSyncPseudoUSSHistoryDeleteDirectivesDescription,
-     kOsAll,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSHistoryDeleteDirectives)},
-    {"enable-sync-pseudo-uss-preferences",
-     flag_descriptions::kEnableSyncPseudoUSSPreferencesName,
-     flag_descriptions::kEnableSyncPseudoUSSPreferencesDescription, kOsAll,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSPreferences)},
-    {"enable-sync-pseudo-uss-priority-preferences",
-     flag_descriptions::kEnableSyncPseudoUSSPriorityPreferencesName,
-     flag_descriptions::kEnableSyncPseudoUSSPriorityPreferencesDescription,
-     kOsAll, FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSPriorityPreferences)},
-    {"enable-sync-pseudo-uss-search-engines",
-     flag_descriptions::kEnableSyncPseudoUSSSearchEnginesName,
-     flag_descriptions::kEnableSyncPseudoUSSSearchEnginesDescription,
-     kOsDesktop, FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSSearchEngines)},
-    {"enable-sync-pseudo-uss-supervised-users",
-     flag_descriptions::kEnableSyncPseudoUSSSupervisedUsersName,
-     flag_descriptions::kEnableSyncPseudoUSSSupervisedUsersDescription, kOsAll,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSSupervisedUsers)},
-    {"enable-sync-pseudo-uss-themes",
-     flag_descriptions::kEnableSyncPseudoUSSThemesName,
-     flag_descriptions::kEnableSyncPseudoUSSThemesDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSThemes)},
     {"enable-experimental-productivity-features",
      flag_descriptions::kExperimentalProductivityFeaturesName,
      flag_descriptions::kExperimentalProductivityFeaturesDescription, kOsAll,
diff --git a/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc b/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc
index 8b9ac045..0e61226 100644
--- a/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc
+++ b/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/ntp_snippets/contextual_content_suggestions_service_factory.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -76,7 +75,7 @@
     return true;
 
   policy::ProfilePolicyConnector* policy_connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+      profile->GetProfilePolicyConnector();
 
   const policy::PolicyMap& policies =
       policy_connector->policy_service()->GetPolicies(
diff --git a/chrome/browser/apps/user_type_filter.cc b/chrome/browser/apps/user_type_filter.cc
index cba8cdf..6c6c2e01 100644
--- a/chrome/browser/apps/user_type_filter.cc
+++ b/chrome/browser/apps/user_type_filter.cc
@@ -6,7 +6,6 @@
 
 #include "base/values.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -31,8 +30,7 @@
     return kUserTypeChild;
   if (profile->IsLegacySupervised())
     return kUserTypeSupervised;
-  if (policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
-          ->IsManaged()) {
+  if (profile->GetProfilePolicyConnector()->IsManaged()) {
     return kUserTypeManaged;
   }
   return kUserTypeUnmanaged;
diff --git a/chrome/browser/apps/user_type_filter_unittest.cc b/chrome/browser/apps/user_type_filter_unittest.cc
index f4a5cd0..5d7d0f8 100644
--- a/chrome/browser/apps/user_type_filter_unittest.cc
+++ b/chrome/browser/apps/user_type_filter_unittest.cc
@@ -11,7 +11,6 @@
 #include "base/macros.h"
 #include "base/values.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/supervised_user/supervised_user_constants.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -94,8 +93,7 @@
 
 TEST_F(UserTypeFilterTest, ManagedUser) {
   const auto profile = CreateProfile();
-  policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile.get())
-      ->OverrideIsManagedForTesting(true);
+  profile->GetProfilePolicyConnector()->OverrideIsManagedForTesting(true);
   EXPECT_FALSE(Match(profile, CreateJsonWithFilter({kUserTypeUnmanaged})));
   EXPECT_TRUE(Match(profile, CreateJsonWithFilter({kUserTypeManaged})));
   EXPECT_TRUE(Match(
@@ -138,8 +136,7 @@
   EXPECT_FALSE(MatchDefault(profile, default_filter));
   // Managed user.
   profile = CreateProfile();
-  policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile.get())
-      ->OverrideIsManagedForTesting(true);
+  profile->GetProfilePolicyConnector()->OverrideIsManagedForTesting(true);
   EXPECT_FALSE(MatchDefault(profile, default_filter));
 }
 
diff --git a/chrome/browser/background_sync/background_sync_controller_factory.h b/chrome/browser/background_sync/background_sync_controller_factory.h
index c2a10dd..1ad530b 100644
--- a/chrome/browser/background_sync/background_sync_controller_factory.h
+++ b/chrome/browser/background_sync/background_sync_controller_factory.h
@@ -24,7 +24,7 @@
   BackgroundSyncControllerFactory();
   ~BackgroundSyncControllerFactory() override;
 
-  // BrowserContextKeyedBaseFactory methods:
+  // BrowserContextKeyedServiceFactory methods:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* context) const override;
   content::BrowserContext* GetBrowserContextToUse(
diff --git a/chrome/browser/bookmarks/managed_bookmark_service_factory.cc b/chrome/browser/bookmarks/managed_bookmark_service_factory.cc
index 9208faae..ce25a3eb 100644
--- a/chrome/browser/bookmarks/managed_bookmark_service_factory.cc
+++ b/chrome/browser/bookmarks/managed_bookmark_service_factory.cc
@@ -9,7 +9,6 @@
 #include "base/bind.h"
 #include "base/memory/singleton.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/bookmarks/managed/managed_bookmark_service.h"
@@ -52,7 +51,7 @@
 std::string ManagedBookmarkServiceFactory::GetManagedBookmarksDomain(
     Profile* profile) {
   policy::ProfilePolicyConnector* connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+      profile->GetProfilePolicyConnector();
   if (connector->IsManaged() &&
       connector->IsProfilePolicy(policy::key::kManagedBookmarks)) {
     return gaia::ExtractDomainName(profile->GetProfileUserName());
@@ -63,9 +62,7 @@
 ManagedBookmarkServiceFactory::ManagedBookmarkServiceFactory()
     : BrowserContextKeyedServiceFactory(
           "ManagedBookmarkService",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(policy::ProfilePolicyConnectorFactory::GetInstance());
-}
+          BrowserContextDependencyManager::GetInstance()) {}
 
 ManagedBookmarkServiceFactory::~ManagedBookmarkServiceFactory() {}
 
diff --git a/chrome/browser/bookmarks/managed_bookmark_service_unittest.cc b/chrome/browser/bookmarks/managed_bookmark_service_unittest.cc
index f27e228..779b937 100644
--- a/chrome/browser/bookmarks/managed_bookmark_service_unittest.cc
+++ b/chrome/browser/bookmarks/managed_bookmark_service_unittest.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/bookmarks/chrome_bookmark_client.h"
 #include "chrome/browser/bookmarks/managed_bookmark_service_factory.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_node.h"
@@ -315,8 +314,7 @@
           .empty());
 
   // Managed profile
-  policy::ProfilePolicyConnectorFactory::GetForBrowserContext(&profile_)
-      ->OverrideIsManagedForTesting(true);
+  profile_.GetProfilePolicyConnector()->OverrideIsManagedForTesting(true);
   EXPECT_EQ(
       "google.com",
       ManagedBookmarkServiceFactory::GetManagedBookmarksDomain(&profile_));
diff --git a/chrome/browser/browser_switcher/browser_switcher_prefs.cc b/chrome/browser/browser_switcher/browser_switcher_prefs.cc
index 2df5f7fa..30eb0461 100644
--- a/chrome/browser/browser_switcher/browser_switcher_prefs.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_prefs.cc
@@ -11,7 +11,6 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_switcher/browser_switcher_sitelist.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
@@ -25,8 +24,7 @@
 BrowserSwitcherPrefs::BrowserSwitcherPrefs(Profile* profile)
     : BrowserSwitcherPrefs(
           profile->GetPrefs(),
-          policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
-              ->policy_service()) {}
+          profile->GetProfilePolicyConnector()->policy_service()) {}
 
 BrowserSwitcherPrefs::BrowserSwitcherPrefs(
     PrefService* prefs,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index d781699..d50c9144 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -29,6 +29,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_content_browser_overlay_manifest.h"
@@ -210,6 +211,7 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h"
 #include "components/dom_distiller/core/dom_distiller_switches.h"
 #include "components/dom_distiller/core/url_constants.h"
 #include "components/error_page/common/error_page_switches.h"
@@ -1101,7 +1103,11 @@
 
 ChromeContentBrowserClient::ChromeContentBrowserClient(
     StartupData* startup_data)
-    : startup_data_(startup_data), weak_factory_(this) {
+    : data_reduction_proxy_throttle_manager_(
+          nullptr,
+          base::OnTaskRunnerDeleter(nullptr)),
+      startup_data_(startup_data),
+      weak_factory_(this) {
 #if BUILDFLAG(ENABLE_PLUGINS)
   for (size_t i = 0; i < base::size(kPredefinedAllowedDevChannelOrigins); ++i)
     allowed_dev_channel_origins_.insert(kPredefinedAllowedDevChannelOrigins[i]);
@@ -2104,7 +2110,6 @@
     MaybeCopyDisableWebRtcEncryptionSwitch(command_line,
                                            browser_command_line,
                                            chrome::GetChannel());
-
     if (process) {
       PrefService* prefs = profile->GetPrefs();
       // Currently this pref is only registered if applied via a policy.
@@ -2156,9 +2161,10 @@
             network::switches::kUnsafelyTreatInsecureOriginAsSecure,
             prefs->GetString(prefs::kUnsafelyTreatInsecureOriginAsSecure));
       }
-
-      if (prefs->HasPrefPath(prefs::kAllowPopupsDuringPageUnload))
+      if (prefs->HasPrefPath(prefs::kAllowPopupsDuringPageUnload) &&
+          prefs->GetBoolean(prefs::kAllowPopupsDuringPageUnload)) {
         command_line->AppendSwitch(switches::kAllowPopupsDuringPageUnload);
+      }
     }
 
     if (IsAutoReloadEnabled())
@@ -4709,6 +4715,15 @@
   if (chrome_navigation_ui_data && io_data &&
       io_data->data_reduction_proxy_io_data() &&
       data_reduction_proxy::params::IsEnabledWithNetworkService()) {
+    if (!data_reduction_proxy_throttle_manager_) {
+      data_reduction_proxy_throttle_manager_ = std::unique_ptr<
+          data_reduction_proxy::DataReductionProxyThrottleManager,
+          base::OnTaskRunnerDeleter>(
+          new data_reduction_proxy::DataReductionProxyThrottleManager(
+              io_data->data_reduction_proxy_io_data(),
+              io_data->data_reduction_proxy_io_data()->CreateThrottleConfig()),
+          base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get()));
+    }
     net::HttpRequestHeaders headers;
     data_reduction_proxy::DataReductionProxyRequestOptions* request_options =
         io_data->data_reduction_proxy_io_data()->request_options();
@@ -4721,8 +4736,7 @@
     request_options->AddPageIDRequestHeader(&headers, page_id);
     result.push_back(std::make_unique<
                      data_reduction_proxy::DataReductionProxyURLLoaderThrottle>(
-        headers,
-        io_data->data_reduction_proxy_io_data()->GetThrottleManager()));
+        headers, data_reduction_proxy_throttle_manager_.get()));
   }
 
 #if defined(SAFE_BROWSING_DB_LOCAL) || defined(SAFE_BROWSING_DB_REMOTE)
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 8f0c382a..3a469a5 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -18,6 +18,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_service.h"
 #include "chrome/browser/startup_data.h"
@@ -51,6 +52,7 @@
 
 namespace data_reduction_proxy {
 class DataReductionProxyData;
+class DataReductionProxyThrottleManager;
 }  // namespace data_reduction_proxy
 
 namespace previews {
@@ -670,6 +672,10 @@
   scoped_refptr<safe_browsing::UrlCheckerDelegate>
       safe_browsing_url_checker_delegate_;
 
+  std::unique_ptr<data_reduction_proxy::DataReductionProxyThrottleManager,
+                  base::OnTaskRunnerDeleter>
+      data_reduction_proxy_throttle_manager_;
+
   std::unique_ptr<service_manager::BinderRegistry> frame_interfaces_;
   std::unique_ptr<
       service_manager::BinderRegistryWithArgs<content::RenderFrameHost*>>
diff --git a/chrome/browser/chrome_content_browser_client_browsertest.cc b/chrome/browser/chrome_content_browser_client_browsertest.cc
index 8b3acb0..c20e0df 100644
--- a/chrome/browser/chrome_content_browser_client_browsertest.cc
+++ b/chrome/browser/chrome_content_browser_client_browsertest.cc
@@ -24,12 +24,14 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/policy_header_service.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/render_frame_host.h"
@@ -98,6 +100,55 @@
   EXPECT_EQ(url, entry->GetVirtualURL());
 }
 
+class ChromeContentBrowserClientPopupsTest : public InProcessBrowserTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // Required setup for kAllowPopupsDuringPageUnload switch
+    // as its being checked (whether its going to be enabled or not)
+    // only if the process type is renderer process.
+    command_line_.AppendSwitchASCII(switches::kProcessType,
+                                    switches::kRendererProcess);
+  }
+  void SetUpOnMainThread() override {
+    kChildProcessId = browser()
+                          ->tab_strip_model()
+                          ->GetActiveWebContents()
+                          ->GetMainFrame()
+                          ->GetProcess()
+                          ->GetID();
+  }
+  ChromeContentBrowserClientPopupsTest()
+      : command_line_(base::CommandLine::NO_PROGRAM) {}
+
+  void AppendContentBrowserClientSwitches() {
+    client_.AppendExtraCommandLineSwitches(&command_line_, kChildProcessId);
+  }
+
+  const base::CommandLine& command_line() const { return command_line_; }
+
+ private:
+  ChromeContentBrowserClient client_;
+  base::CommandLine command_line_;
+  int kChildProcessId;
+};
+
+IN_PROC_BROWSER_TEST_F(ChromeContentBrowserClientPopupsTest,
+                       AllowPopupsDuringPageUnload) {
+  // Verify that the switch is included only when the
+  // pref AllowPopupsDuringPageUnload value is true.
+
+  PrefService* pref_service = browser()->profile()->GetPrefs();
+  pref_service->SetBoolean(prefs::kAllowPopupsDuringPageUnload, false);
+  AppendContentBrowserClientSwitches();
+  EXPECT_FALSE(
+      command_line().HasSwitch(switches::kAllowPopupsDuringPageUnload));
+  // When the pref value is being set to true
+  // the switch should be included.
+  pref_service->SetBoolean(prefs::kAllowPopupsDuringPageUnload, true);
+  AppendContentBrowserClientSwitches();
+  EXPECT_TRUE(command_line().HasSwitch(switches::kAllowPopupsDuringPageUnload));
+}
+
 // Helper class to mark "https://ntp.com/" as an isolated origin.
 class IsolatedOriginNTPBrowserTest : public InProcessBrowserTest,
                                      public InstantTestBase {
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc
index a73c1d8..044a784 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_launcher.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc b/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
index 1a21fed2..9dc5eb2 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/extensions/extension_function_test_utils.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -231,7 +230,7 @@
 
 IN_PROC_BROWSER_TEST_F(ArcSessionManagerTest, ManagedChromeAccount) {
   policy::ProfilePolicyConnector* const connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile());
+      profile()->GetProfilePolicyConnector();
   connector->OverrideIsManagedForTesting(true);
 
   EnableArc();
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
index 23bc070..04e35860 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/prefs/pref_service_syncable_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
@@ -1213,8 +1212,7 @@
 
     if (IsManagedUser()) {
       policy::ProfilePolicyConnector* const connector =
-          policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-              profile());
+          profile()->GetProfilePolicyConnector();
       connector->OverrideIsManagedForTesting(true);
 
       profile()->GetTestingPrefService()->SetManagedPref(
@@ -1435,8 +1433,7 @@
     if (GetParam().negotiation ==
         ArcSessionRetryTestParam::Negotiation::SKIPPED) {
       policy::ProfilePolicyConnector* const connector =
-          policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-              profile());
+          profile()->GetProfilePolicyConnector();
       connector->OverrideIsManagedForTesting(true);
 
       profile()->GetTestingPrefService()->SetManagedPref(
diff --git a/chrome/browser/chromeos/arc/arc_util_unittest.cc b/chrome/browser/chromeos/arc/arc_util_unittest.cc
index f89d4cf..5a6fed7 100644
--- a/chrome/browser/chromeos/arc/arc_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_util_unittest.cc
@@ -25,7 +25,6 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
@@ -59,7 +58,7 @@
 
 void SetProfileIsManagedForTesting(Profile* profile) {
   policy::ProfilePolicyConnector* const connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+      profile->GetProfilePolicyConnector();
   connector->OverrideIsManagedForTesting(true);
 }
 
diff --git a/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge.cc b/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge.cc
index 6cb7238..3d1fd02c 100644
--- a/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge.cc
+++ b/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/net/nss_context.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "components/arc/arc_service_manager.h"
@@ -46,9 +45,7 @@
 
  private:
   friend base::DefaultSingletonTraits<ArcCertStoreBridgeFactory>;
-  ArcCertStoreBridgeFactory() {
-    DependsOn(policy::ProfilePolicyConnectorFactory::GetInstance());
-  }
+  ArcCertStoreBridgeFactory() {}
   ~ArcCertStoreBridgeFactory() override = default;
 };
 
@@ -108,7 +105,7 @@
   DVLOG(1) << "ArcCertStoreBridge::ArcCertStoreBridge";
 
   const auto* profile_policy_connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(context_);
+      Profile::FromBrowserContext(context_)->GetProfilePolicyConnector();
   policy_service_ = profile_policy_connector->policy_service();
   DCHECK(policy_service_);
 
diff --git a/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc b/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc
index ed37626..3ebbbdec 100644
--- a/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc
+++ b/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/net/nss_context.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/pref_names.h"
@@ -202,8 +201,7 @@
     ASSERT_NO_FATAL_FAILURE(ImportCerts());
 
     policy::ProfilePolicyConnector* const policy_connector =
-        policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-            browser()->profile());
+        browser()->profile()->GetProfilePolicyConnector();
 
     extensions::StateStore* const state_store =
         extensions::ExtensionSystem::Get(browser()->profile())->state_store();
diff --git a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
index e38a0fcf..1eb4c0ee 100644
--- a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
+++ b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/policy/developer_tools_policy_handler.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
@@ -337,9 +336,7 @@
  private:
   friend base::DefaultSingletonTraits<ArcPolicyBridgeFactory>;
 
-  ArcPolicyBridgeFactory() {
-    DependsOn(policy::ProfilePolicyConnectorFactory::GetInstance());
-  }
+  ArcPolicyBridgeFactory() {}
   ~ArcPolicyBridgeFactory() override = default;
 };
 
@@ -513,7 +510,7 @@
 
 void ArcPolicyBridge::InitializePolicyService() {
   auto* profile_policy_connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(context_);
+      Profile::FromBrowserContext(context_)->GetProfilePolicyConnector();
   policy_service_ = profile_policy_connector->policy_service();
   is_managed_ = profile_policy_connector->IsManaged();
 }
diff --git a/chrome/browser/chromeos/arc/policy/arc_policy_util.cc b/chrome/browser/chromeos/arc/policy/arc_policy_util.cc
index 4a09d88a..06e6ab5 100644
--- a/chrome/browser/chromeos/arc/policy/arc_policy_util.cc
+++ b/chrome/browser/chromeos/arc/policy/arc_policy_util.cc
@@ -11,7 +11,6 @@
 #include "base/values.h"
 #include "chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/arc/arc_prefs.h"
@@ -35,7 +34,7 @@
 }  // namespace
 
 bool IsAccountManaged(const Profile* profile) {
-  return policy::ProfilePolicyConnectorFactory::IsProfileManaged(profile);
+  return profile->GetProfilePolicyConnector()->IsManaged();
 }
 
 bool IsArcDisabledForEnterprise() {
diff --git a/chrome/browser/chromeos/drive/drive_integration_service.cc b/chrome/browser/chromeos/drive/drive_integration_service.cc
index 3f649ee2..0870da1 100644
--- a/chrome/browser/chromeos/drive/drive_integration_service.cc
+++ b/chrome/browser/chromeos/drive/drive_integration_service.cc
@@ -54,6 +54,7 @@
 #include "components/drive/resource_metadata_storage.h"
 #include "components/drive/service/drive_api_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/metrics/metrics_pref_names.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "components/version_info/version_info.h"
@@ -548,6 +549,11 @@
                            GetAccountId().GetAccountIdKey());
   }
 
+  bool IsMetricsCollectionEnabled() override {
+    return g_browser_process->local_state()->GetBoolean(
+        metrics::prefs::kMetricsReportingEnabled);
+  }
+
   DriveNotificationManager& GetDriveNotificationManager() override {
     return *DriveNotificationManagerFactory::GetForBrowserContext(profile_);
   }
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router_factory.h b/chrome/browser/chromeos/extensions/file_manager/event_router_factory.h
index 40d5bff..7a0aabc6 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router_factory.h
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router_factory.h
@@ -24,7 +24,7 @@
   static EventRouterFactory* GetInstance();
 
  protected:
-  // BrowserContextKeyedBaseFactory overrides:
+  // BrowserContextKeyedServiceFactory overrides:
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
diff --git a/chrome/browser/chromeos/extensions/users_private/users_private_delegate_factory.h b/chrome/browser/chromeos/extensions/users_private/users_private_delegate_factory.h
index 533a3ba..411e981 100644
--- a/chrome/browser/chromeos/extensions/users_private/users_private_delegate_factory.h
+++ b/chrome/browser/chromeos/extensions/users_private/users_private_delegate_factory.h
@@ -30,7 +30,7 @@
   UsersPrivateDelegateFactory();
   ~UsersPrivateDelegateFactory() override;
 
-  // BrowserContextKeyedBaseFactory implementation.
+  // BrowserContextKeyedServiceFactory implementation.
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
 
diff --git a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
index 87fb383..16db619e 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
@@ -683,8 +683,6 @@
   SET_STRING("PASTE_BUTTON_LABEL", IDS_FILE_BROWSER_PASTE_BUTTON_LABEL);
   SET_STRING("PASTE_INTO_FOLDER_BUTTON_LABEL",
              IDS_FILE_BROWSER_PASTE_INTO_FOLDER_BUTTON_LABEL);
-  SET_STRING("PLUGIN_VM_DIRECTORY_LABEL",
-             IDS_FILE_BROWSER_PLUGIN_VM_DIRECTORY_LABEL);
   SET_STRING("PREPARING_LABEL", IDS_FILE_BROWSER_PREPARING_LABEL);
   SET_STRING("QUICK_VIEW_CLOSE_BUTTON_LABEL",
              IDS_FILE_BROWSER_QUICK_VIEW_CLOSE_BUTTON_LABEL);
diff --git a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
index 75b68240..9476a97 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
@@ -87,10 +87,6 @@
   RunTest("menu");
 }
 
-IN_PROC_BROWSER_TEST_F(FileManagerUITest, PluginVm) {
-  RunTest("pluginVm");
-}
-
 IN_PROC_BROWSER_TEST_F(FileManagerUITest, PluginVmShare) {
   RunTest("pluginVmShare");
 }
diff --git a/chrome/browser/chromeos/file_manager/volume_manager_factory.h b/chrome/browser/chromeos/file_manager/volume_manager_factory.h
index 23269c4..8aa0911b 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager_factory.h
+++ b/chrome/browser/chromeos/file_manager/volume_manager_factory.h
@@ -30,13 +30,11 @@
   static VolumeManagerFactory* GetInstance();
 
  protected:
-  // BrowserContextKeyedBaseFactory overrides:
+  // BrowserContextKeyedServiceFactory overrides:
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
   bool ServiceIsNULLWhileTesting() const override;
-
-  // BrowserContextKeyedServiceFactory overrides:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* context) const override;
 
diff --git a/chrome/browser/chromeos/first_run/first_run.cc b/chrome/browser/chromeos/first_run/first_run.cc
index ea5bdef1..525b479a 100644
--- a/chrome/browser/chromeos/first_run/first_run.cc
+++ b/chrome/browser/chromeos/first_run/first_run.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/prefs/pref_service_syncable_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -80,8 +79,7 @@
   if (TabletModeClient::Get() && TabletModeClient::Get()->tablet_mode_enabled())
     return;
 
-  if (policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
-          ->IsManaged())
+  if (profile->GetProfilePolicyConnector()->IsManaged())
     return;
 
   if (command_line->HasSwitch(::switches::kTestType))
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index 5749af0..19725f94 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -84,7 +84,6 @@
     ::switches::kBlinkSettings,
     ::switches::kDisable2dCanvasImageChromium,
     ::switches::kDisableAccelerated2dCanvas,
-    ::switches::kDisableAcceleratedJpegDecoding,
     ::switches::kDisableAcceleratedMjpegDecode,
     ::switches::kDisableAcceleratedVideoDecode,
     ::switches::kDisableAcceleratedVideoEncode,
@@ -105,6 +104,7 @@
     ::switches::kDisableRGBA4444Textures,
     ::switches::kDisableThreadedScrolling,
     ::switches::kDisableTouchDragDrop,
+    ::switches::kDisableYUVImageDecoding,
     ::switches::kDisableZeroCopy,
     ::switches::kEnableBlinkFeatures,
     ::switches::kEnableGpuMemoryBufferVideoFrames,
diff --git a/chrome/browser/chromeos/login/login_utils_browsertest.cc b/chrome/browser/chromeos/login/login_utils_browsertest.cc
index e18ae5f..322a7ff 100644
--- a/chrome/browser/chromeos/login/login_utils_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_utils_browsertest.cc
@@ -84,7 +84,13 @@
 };
 
 // Exercises login, like the desktopui_MashLogin Chrome OS autotest.
-IN_PROC_BROWSER_TEST_F(LoginUtilsTest, MashLogin) {
+// Test is flaky, see https://crbug.com/957584.
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_MashLogin DISABLED_MashLogin
+#else
+#define MAYBE_MashLogin MashLogin
+#endif
+IN_PROC_BROWSER_TEST_F(LoginUtilsTest, MAYBE_MashLogin) {
   // Test is relevant for both SingleProcessMash and MultiProcessMash, but
   // not classic ash.
   if (!features::IsUsingWindowService())
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index a01a87e..55995f46 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -84,7 +84,7 @@
 #include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/metrics/metrics_reporting_state.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/login_screen_client.h"
@@ -229,9 +229,9 @@
   const user_manager::UserManager* user_manager =
       user_manager::UserManager::Get();
   DCHECK(user_manager->IsUserLoggedIn());
-  bool is_managed_account =
-      policy::ProfilePolicyConnectorFactory::IsProfileManaged(
-          ProfileManager::GetActiveUserProfile());
+  bool is_managed_account = ProfileManager::GetActiveUserProfile()
+                                ->GetProfilePolicyConnector()
+                                ->IsManaged();
   bool is_child_account = user_manager->IsLoggedInAsChildUser();
   return !is_managed_account && !is_child_account &&
          base::FeatureList::IsEnabled(features::kOobeRecommendAppsScreen);
diff --git a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h
index c3fdfc37..bf7d6f6 100644
--- a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h
+++ b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h
@@ -55,10 +55,8 @@
 
   static KeyedService* BuildInstanceFor(content::BrowserContext* context);
 
-  // BrowserContextKeyedBaseFactory overrides:
+  // BrowserContextKeyedServiceFactory overrides:
   bool ServiceIsCreatedWithBrowserContext() const override;
-
-  // BrowserContextKeyedServiceFactory implementation:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* browser_context) const override;
 
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc
index 68a712d..fa6353e2 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/extensions/extension_system_factory.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/platform_keys_certificate_selector_chromeos.h"
@@ -88,7 +87,6 @@
           "PlatformKeysService",
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(extensions::ExtensionSystemFactory::GetInstance());
-  DependsOn(policy::ProfilePolicyConnectorFactory::GetInstance());
 }
 
 PlatformKeysServiceFactory::~PlatformKeysServiceFactory() {
@@ -105,7 +103,7 @@
       extensions::ExtensionSystem::Get(context)->state_store();
 
   policy::ProfilePolicyConnector* const policy_connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(context);
+      Profile::FromBrowserContext(context)->GetProfilePolicyConnector();
 
   Profile* const profile = Profile::FromBrowserContext(context);
 
diff --git a/chrome/browser/chromeos/policy/app_install_event_logger.cc b/chrome/browser/chromeos/policy/app_install_event_logger.cc
index 2a084ea3..6684f3f 100644
--- a/chrome/browser/chromeos/policy/app_install_event_logger.cc
+++ b/chrome/browser/chromeos/policy/app_install_event_logger.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/disks/disk.h"
 #include "chromeos/disks/disk_mount_manager.h"
@@ -118,8 +117,7 @@
   }
 
   policy::PolicyService* const policy_service =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile_)
-          ->policy_service();
+      profile_->GetProfilePolicyConnector()->policy_service();
   EvaluatePolicy(policy_service->GetPolicies(policy::PolicyNamespace(
                      policy::POLICY_DOMAIN_CHROME, std::string())),
                  true /* initial */);
@@ -137,9 +135,8 @@
   }
   if (observing_) {
     arc::ArcPolicyBridge::GetForBrowserContext(profile_)->RemoveObserver(this);
-    policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile_)
-        ->policy_service()
-        ->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
+    profile_->GetProfilePolicyConnector()->policy_service()->RemoveObserver(
+        policy::POLICY_DOMAIN_CHROME, this);
   }
 }
 
diff --git a/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc b/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
index d400f7a..a9b77e9 100644
--- a/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
+++ b/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/chromeos/policy/device_local_account.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "chromeos/settings/cros_settings_provider.h"
@@ -177,7 +176,7 @@
   }
 
   ProfilePolicyConnector* policy_connector =
-      ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+      profile->GetProfilePolicyConnector();
   logged_in_user_observers_[user_id] = std::make_unique<PolicyServiceObserver>(
       this, user_id, policy_connector->policy_service());
 }
diff --git a/chrome/browser/chromeos/policy/component_active_directory_policy_browsertest.cc b/chrome/browser/chromeos/policy/component_active_directory_policy_browsertest.cc
index ef28802..56199a4e 100644
--- a/chrome/browser/chromeos/policy/component_active_directory_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/component_active_directory_policy_browsertest.cc
@@ -11,7 +11,6 @@
 #include "base/path_service.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/common/chrome_paths.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
@@ -151,8 +150,7 @@
 
   void RefreshPolicies() {
     ProfilePolicyConnector* profile_connector =
-        ProfilePolicyConnectorFactory::GetForBrowserContext(
-            browser()->profile());
+        browser()->profile()->GetProfilePolicyConnector();
     PolicyService* policy_service = profile_connector->policy_service();
     base::RunLoop run_loop;
     policy_service->RefreshPolicies(run_loop.QuitClosure());
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index a446064..2c2ed7b 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -71,7 +71,6 @@
 #include "chrome/browser/extensions/updater/local_extension_cache.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -1449,7 +1448,7 @@
   // Verify that the external data reference has propagated to the device-local
   // account's ProfilePolicyConnector.
   ProfilePolicyConnector* policy_connector =
-      ProfilePolicyConnectorFactory::GetForBrowserContext(GetProfileForTest());
+      GetProfileForTest()->GetProfilePolicyConnector();
   ASSERT_TRUE(policy_connector);
   const PolicyMap& policies = policy_connector->policy_service()->GetPolicies(
       PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
@@ -2305,8 +2304,7 @@
   EXPECT_TRUE(extension_service->GetExtensionById(kShowManagedStorageID, true));
 
   // Wait for the app policy if it hasn't been fetched yet.
-  ProfilePolicyConnector* connector =
-      ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+  ProfilePolicyConnector* connector = profile->GetProfilePolicyConnector();
   ASSERT_TRUE(connector);
   PolicyService* policy_service = connector->policy_service();
   ASSERT_TRUE(policy_service);
diff --git a/chrome/browser/chromeos/policy/power_policy_browsertest.cc b/chrome/browser/chromeos/policy/power_policy_browsertest.cc
index bc86984..a0f54dc2 100644
--- a/chrome/browser/chromeos/policy/power_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/power_policy_browsertest.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
@@ -257,8 +256,7 @@
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
   EXPECT_CALL(observer, OnPolicyServiceInitialized(_)).Times(AnyNumber());
   PolicyService* policy_service =
-      ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
-          ->policy_service();
+      profile->GetProfilePolicyConnector()->policy_service();
   ASSERT_TRUE(policy_service);
   policy_service->AddObserver(POLICY_DOMAIN_CHROME, &observer);
   closure.Run();
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
index d613492..9f45514d 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
@@ -51,7 +51,6 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc b/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
index f9f7c1b3..bf51a6c3 100644
--- a/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
 #include "chrome/browser/chromeos/policy/user_policy_test_helper.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
@@ -94,7 +93,7 @@
           profile);
   ASSERT_TRUE(policy_manager);
   ProfilePolicyConnector* policy_connector =
-      ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+      profile->GetProfilePolicyConnector();
   ASSERT_TRUE(policy_connector);
 
   {
diff --git a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory.cc b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory.cc
index b503ed2..8cacffe8 100644
--- a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory.cc
+++ b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/chromeos/policy/user_network_configuration_updater.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/network/network_handler.h"
@@ -36,9 +35,7 @@
 UserNetworkConfigurationUpdaterFactory::UserNetworkConfigurationUpdaterFactory()
     : BrowserContextKeyedServiceFactory(
           "UserNetworkConfigurationUpdater",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(ProfilePolicyConnectorFactory::GetInstance());
-}
+          BrowserContextDependencyManager::GetInstance()) {}
 
 UserNetworkConfigurationUpdaterFactory::
     ~UserNetworkConfigurationUpdaterFactory() {}
@@ -82,7 +79,7 @@
       user->GetType() != user_manager::USER_TYPE_GUEST;
 
   ProfilePolicyConnector* profile_connector =
-      ProfilePolicyConnectorFactory::GetForBrowserContext(context);
+      profile->GetProfilePolicyConnector();
 
   return UserNetworkConfigurationUpdater::CreateForUserPolicy(
              profile, allow_trusted_certs_from_policy, *user,
diff --git a/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc b/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
index 564cf68..d728d25 100644
--- a/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/net/system_network_context_manager.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/policy/schema_registry_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
@@ -402,6 +403,14 @@
   Profile* profile = static_cast<Profile*>(context);
   if (profile->IsOffTheRecord())
     return;
+
+  // TODO(crbug.com/937770): Move the shut down of |profile_policy_connector|
+  // to where the |cloud_manager| is shut down.
+  ProfilePolicyConnector* profile_policy_connector =
+      profile->GetProfilePolicyConnector();
+  if (profile_policy_connector)
+    profile_policy_connector->Shutdown();
+
   UserCloudPolicyManagerChromeOS* cloud_manager =
       GetCloudPolicyManager(profile);
   if (cloud_manager)
diff --git a/chrome/browser/chromeos/policy/user_policy_test_helper.cc b/chrome/browser/chromeos/policy/user_policy_test_helper.cc
index 82b9c666..15e10938 100644
--- a/chrome/browser/chromeos/policy/user_policy_test_helper.cc
+++ b/chrome/browser/chromeos/policy/user_policy_test_helper.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
@@ -67,7 +66,7 @@
       std::string() /* requisition */, std::string() /* current_state_key */);
 
   policy::ProfilePolicyConnector* const profile_connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+      profile->GetProfilePolicyConnector();
   policy::PolicyService* const policy_service =
       profile_connector->policy_service();
 
@@ -83,7 +82,7 @@
   SetPolicy(mandatory_policy, recommended_policy);
 
   policy::ProfilePolicyConnector* const profile_connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+      profile->GetProfilePolicyConnector();
   policy::PolicyService* const policy_service =
       profile_connector->policy_service();
 
diff --git a/chrome/browser/chromeos/printing/calculators_policies_binder.cc b/chrome/browser/chromeos/printing/calculators_policies_binder.cc
index 1f422df..f21f408 100644
--- a/chrome/browser/chromeos/printing/calculators_policies_binder.cc
+++ b/chrome/browser/chromeos/printing/calculators_policies_binder.cc
@@ -11,8 +11,6 @@
 
 #include "chrome/browser/chromeos/printing/bulk_printers_calculator.h"
 #include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h"
-#include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "components/prefs/pref_change_registrar.h"
diff --git a/chrome/browser/chromeos/printing/enterprise_printers_provider.cc b/chrome/browser/chromeos/printing/enterprise_printers_provider.cc
index 0b930ca..5f42b97 100644
--- a/chrome/browser/chromeos/printing/enterprise_printers_provider.cc
+++ b/chrome/browser/chromeos/printing/enterprise_printers_provider.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h"
 #include "chrome/browser/chromeos/printing/calculators_policies_binder.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/printing/printer_translator.h"
@@ -193,7 +192,7 @@
   // Checks if given policy is set and if it is a dictionary
   bool PolicyWithDataIsSet(const char* policy_name) {
     policy::ProfilePolicyConnector* policy_connector =
-        policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile_);
+        profile_->GetProfilePolicyConnector();
     if (!policy_connector) {
       // something is wrong
       return false;
diff --git a/chrome/browser/content_settings/cookie_settings_factory.h b/chrome/browser/content_settings/cookie_settings_factory.h
index 3a255ab..4fb5a79e 100644
--- a/chrome/browser/content_settings/cookie_settings_factory.h
+++ b/chrome/browser/content_settings/cookie_settings_factory.h
@@ -35,7 +35,7 @@
   CookieSettingsFactory();
   ~CookieSettingsFactory() override;
 
-  // |BrowserContextKeyedBaseFactory| methods:
+  // |RefcountedBrowserContextKeyedServiceFactory| methods:
   void RegisterProfilePrefs(
       user_prefs::PrefRegistrySyncable* registry) override;
   content::BrowserContext* GetBrowserContextToUse(
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
index 69080a2..0200943 100644
--- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
+++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -95,6 +96,14 @@
   return std::make_unique<net::test_server::BasicHttpResponse>();
 }
 
+// Given a |request| to a proxy server, returns the destination host name.
+std::string GetDestinationHost(const net::test_server::HttpRequest& request) {
+  const auto it = request.headers.find("Host");
+  if (it == request.headers.end())
+    return {};
+  return it->second;
+}
+
 void SimulateNetworkChange(network::mojom::ConnectionType type) {
   if (base::FeatureList::IsEnabled(network::features::kNetworkService) &&
       !content::IsInProcessNetworkService()) {
@@ -1326,4 +1335,58 @@
   EXPECT_EQ(kExpectedLog2, event_log.GetAndReset());
 }
 
+IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, NestedWebWorker) {
+  // Nested Web Workers exercise URLLoaderThrottles in interesting ways. Each
+  // worker runs on a separate thread and each one has an associated
+  // URLLoaderThrottleProvider. When spawning a nested worker, the outer
+  // worker's throttle provider is cloned on the outer worker's thread and then
+  // used on the inner worker's thread. This test verifies that the mojo
+  // connections between the throttles and DRP are set up correctly given the
+  // non-trivial threading scenario.
+  constexpr char kHtml[] = R"(
+    <html><body><script language="javascript">
+      function workerImpl() {
+        function nestedWorkerImpl() {
+          postMessage('done');
+        }
+        var blob = new Blob(['(' + nestedWorkerImpl.toString() + ')()'],
+                            { type: 'application/javascript' });
+        var nestedWorker = new Worker(URL.createObjectURL(blob));
+        nestedWorker.onmessage = (event) => postMessage(event.data);
+      }
+      var blob = new Blob(['(' + workerImpl.toString() + ')()'],
+                          { type: 'application/javascript' });
+      var worker = new Worker(URL.createObjectURL(blob));
+      worker.onmessage = (event) => document.title = event.data;
+    </script></body></html>
+  )";
+  constexpr char kDestinationHost[] = "some.host";
+
+  net::EmbeddedTestServer drp_server;
+  drp_server.RegisterRequestHandler(base::BindLambdaForTesting(
+      [&kHtml, &kDestinationHost](const net::test_server::HttpRequest& request)
+          -> std::unique_ptr<net::test_server::HttpResponse> {
+        if (GetDestinationHost(request) != kDestinationHost)
+          return nullptr;
+
+        auto response = std::make_unique<net::test_server::BasicHttpResponse>();
+        response->set_content(kHtml);
+        return response;
+      }));
+  ASSERT_TRUE(drp_server.Start());
+
+  // Change the DRP configuration so that |drp_server| is the current DRP.
+  SetConfig(CreateConfigForServer(drp_server));
+  SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
+  WaitForConfig();
+
+  ui_test_utils::NavigateToURL(browser(),
+                               GURL(std::string("http://") + kDestinationHost));
+
+  const auto kExpectedTitle = base::ASCIIToUTF16("done");
+  content::TitleWatcher title_watcher(
+      browser()->tab_strip_model()->GetActiveWebContents(), kExpectedTitle);
+  EXPECT_EQ(title_watcher.WaitAndGetTitle(), kExpectedTitle);
+}
+
 }  // namespace data_reduction_proxy
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
index 7e1f1db..8e705596 100644
--- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
+++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
@@ -212,6 +212,8 @@
                      &Delegate::RecordEnumeratedHistogram, delegate);
   d->RegisterHandler("recordPerformanceHistogram",
                      &Delegate::RecordPerformanceHistogram, delegate);
+  d->RegisterHandler("recordUserMetricsAction",
+                     &Delegate::RecordUserMetricsAction, delegate);
   d->RegisterHandlerWithCallback("sendJsonRequest",
                                  &Delegate::SendJsonRequest, delegate);
   d->RegisterHandlerWithCallback("getPreferences",
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
index f949474..58769806 100644
--- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
+++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
@@ -94,6 +94,7 @@
                                            int boundary_value) = 0;
     virtual void RecordPerformanceHistogram(const std::string& name,
                                             double duration) = 0;
+    virtual void RecordUserMetricsAction(const std::string& name) = 0;
     virtual void SendJsonRequest(const DispatchCallback& callback,
                                  const std::string& browser_id,
                                  const std::string& url) = 0;
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index 656c6ea..6db9a80 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -19,6 +19,7 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -1149,6 +1150,14 @@
   base::UmaHistogramTimes(name, delta);
 }
 
+void DevToolsUIBindings::RecordUserMetricsAction(const std::string& name) {
+  if (!frontend_host_)
+    return;
+  // Use RecordComputedAction instead of RecordAction as the name comes from
+  // DevTools frontend javascript and so will always have the same call site.
+  base::RecordComputedAction(name);
+}
+
 void DevToolsUIBindings::SendJsonRequest(const DispatchCallback& callback,
                                          const std::string& browser_id,
                                          const std::string& url) {
diff --git a/chrome/browser/devtools/devtools_ui_bindings.h b/chrome/browser/devtools/devtools_ui_bindings.h
index 68e045fe..f832069 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.h
+++ b/chrome/browser/devtools/devtools_ui_bindings.h
@@ -152,6 +152,7 @@
                                  int boundary_value) override;
   void RecordPerformanceHistogram(const std::string& name,
                                   double duration) override;
+  void RecordUserMetricsAction(const std::string& name) override;
   void SendJsonRequest(const DispatchCallback& callback,
                        const std::string& browser_id,
                        const std::string& url) override;
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h b/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h
index 7870d8d..861c769 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h
+++ b/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h
@@ -28,7 +28,7 @@
   static AutofillPrivateEventRouterFactory* GetInstance();
 
  protected:
-  // BrowserContextKeyedBaseFactory overrides:
+  // BrowserContextKeyedServiceFactory overrides:
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_factory.h b/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_factory.h
index c88541a7..5f3198c 100644
--- a/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_factory.h
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_factory.h
@@ -29,7 +29,7 @@
   static LanguageSettingsPrivateDelegateFactory* GetInstance();
 
  protected:
-  // BrowserContextKeyedBaseFactory overrides:
+  // BrowserContextKeyedServiceFactory overrides:
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h
index a56f726..e28c3bf 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h
@@ -32,7 +32,7 @@
   PasswordsPrivateDelegateFactory();
   ~PasswordsPrivateDelegateFactory() override;
 
-  // BrowserContextKeyedBaseFactory implementation.
+  // BrowserContextKeyedServiceFactory implementation.
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h
index 42ede59..3e1c70a 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h
@@ -28,7 +28,7 @@
   static PasswordsPrivateEventRouterFactory* GetInstance();
 
  protected:
-  // BrowserContextKeyedBaseFactory overrides:
+  // BrowserContextKeyedServiceFactory overrides:
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
index 6c2854a49..f6600316d 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/extensions/api/platform_keys/platform_keys_test_base.h"
 #include "chrome/browser/net/nss_context.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/policy/policy_constants.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -135,7 +134,7 @@
         LoadExtension(test_data_dir_.AppendASCII("platform_keys_genkey"));
 
     policy::ProfilePolicyConnector* const policy_connector =
-        policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile());
+        profile()->GetProfilePolicyConnector();
 
     extensions::StateStore* const state_store =
         extensions::ExtensionSystem::Get(profile())->state_store();
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc
index 05881cd3..305c68f 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc
@@ -10,7 +10,6 @@
 #include "base/task/post_task.h"
 #include "chrome/browser/chromeos/policy/affiliation_test_helper.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -149,8 +148,7 @@
 
     if (user_status() != UserStatus::UNMANAGED) {
       policy::ProfilePolicyConnector* const connector =
-          policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-              profile());
+          profile()->GetProfilePolicyConnector();
       connector->OverrideIsManagedForTesting(true);
     }
   }
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h
index 67073596..b5e98c4 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h
@@ -28,7 +28,7 @@
   static SafeBrowsingPrivateEventRouterFactory* GetInstance();
 
  protected:
-  // BrowserContextKeyedBaseFactory overrides:
+  // BrowserContextKeyedServiceFactory overrides:
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
diff --git a/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h b/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h
index c435e5b..e755cf0 100644
--- a/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h
+++ b/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h
@@ -28,7 +28,7 @@
   GeneratedPrefsFactory();
   ~GeneratedPrefsFactory() override;
 
-  // BrowserContextKeyedBaseFactory implementation.
+  // BrowserContextKeyedServiceFactory implementation.
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
 
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h b/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h
index a69bf7f..65a19ab 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h
+++ b/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h
@@ -31,7 +31,7 @@
   SettingsPrivateDelegateFactory();
   ~SettingsPrivateDelegateFactory() override;
 
-  // BrowserContextKeyedBaseFactory implementation.
+  // BrowserContextKeyedServiceFactory implementation.
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
   content::BrowserContext* GetBrowserContextToUse(
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h b/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h
index 8ef02d8e..fe475609 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h
+++ b/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h
@@ -28,7 +28,7 @@
   static SettingsPrivateEventRouterFactory* GetInstance();
 
  protected:
-  // BrowserContextKeyedBaseFactory overrides:
+  // BrowserContextKeyedServiceFactory overrides:
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
diff --git a/chrome/browser/extensions/api/storage/managed_value_store_cache.cc b/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
index d553819a..417d22e 100644
--- a/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
+++ b/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
@@ -18,7 +18,6 @@
 #include "base/task/post_task.h"
 #include "chrome/browser/extensions/api/storage/policy_value_store.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/policy/schema_registry_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/extensions/api/storage/storage_schema_manifest_handler.h"
@@ -236,9 +235,7 @@
     scoped_refptr<SettingsObserverList> observers)
     : profile_(Profile::FromBrowserContext(context)),
       policy_domain_(GetPolicyDomain(profile_)),
-      policy_service_(
-          policy::ProfilePolicyConnectorFactory::GetForBrowserContext(context)
-              ->policy_service()),
+      policy_service_(profile_->GetProfilePolicyConnector()->policy_service()),
       storage_factory_(std::move(factory)),
       observers_(std::move(observers)) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h b/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h
index 3828a7d..3e4e91b3 100644
--- a/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h
+++ b/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h
@@ -26,7 +26,7 @@
   SystemIndicatorManagerFactory();
   ~SystemIndicatorManagerFactory() override;
 
-  // BrowserContextKeyedBaseFactory implementation.
+  // BrowserContextKeyedServiceFactory implementation.
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
 };
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 6419a76..d9b9567 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -71,7 +71,6 @@
 #include "chrome/browser/extensions/unpacked_installer.h"
 #include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/ui/global_error/global_error.h"
 #include "chrome/browser/ui/global_error/global_error_service.h"
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
@@ -5704,8 +5703,7 @@
   ASSERT_TRUE(base_path.IsAbsolute());
   MockProviderVisitor visitor(base_path);
   policy::ProfilePolicyConnector* const connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-          visitor.profile());
+      visitor.profile()->GetProfilePolicyConnector();
   connector->OverrideIsManagedForTesting(true);
   EXPECT_TRUE(connector->IsManaged());
 
diff --git a/chrome/browser/extensions/extension_system_factory.cc b/chrome/browser/extensions/extension_system_factory.cc
index 1375bf2..ac4534a 100644
--- a/chrome/browser/extensions/extension_system_factory.cc
+++ b/chrome/browser/extensions/extension_system_factory.cc
@@ -7,7 +7,6 @@
 #include "chrome/browser/extensions/blacklist_factory.h"
 #include "chrome/browser/extensions/extension_management.h"
 #include "chrome/browser/extensions/install_verifier_factory.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
@@ -48,7 +47,6 @@
   DependsOn(ExtensionRegistryFactory::GetInstance());
   DependsOn(GlobalErrorServiceFactory::GetInstance());
   DependsOn(InstallVerifierFactory::GetInstance());
-  DependsOn(policy::ProfilePolicyConnectorFactory::GetInstance());
   DependsOn(ProcessManagerFactory::GetInstance());
   DependsOn(RendererStartupHelperFactory::GetInstance());
   DependsOn(BlacklistFactory::GetInstance());
diff --git a/chrome/browser/extensions/external_component_loader.cc b/chrome/browser/extensions/external_component_loader.cc
index f56cbe98..02ebfb5 100644
--- a/chrome/browser/extensions/external_component_loader.cc
+++ b/chrome/browser/extensions/external_component_loader.cc
@@ -15,7 +15,7 @@
 #include "extensions/common/manifest.h"
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
 #include "chromeos/constants/chromeos_switches.h"
 #endif
 
@@ -36,7 +36,7 @@
 #if defined(OS_CHROMEOS)
   {
     // Only load the Assessment Assistant if the current session is managed.
-    if (policy::ProfilePolicyConnectorFactory::IsProfileManaged(profile_))
+    if (profile_->GetProfilePolicyConnector()->IsManaged())
       AddExternalExtension(extension_misc::kAssessmentAssistantExtensionId,
                            prefs.get());
   }
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index 9343eda..c891532 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -32,7 +32,6 @@
 #include "chrome/browser/extensions/external_pref_loader.h"
 #include "chrome/browser/extensions/forced_extensions/installation_reporter.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
@@ -553,7 +552,7 @@
                             &do_not_install_for_enterprise) &&
       do_not_install_for_enterprise) {
     const policy::ProfilePolicyConnector* const connector =
-        policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile_);
+        profile_->GetProfilePolicyConnector();
     if (connector->IsManaged()) {
       unsupported_extensions->insert(extension_id);
       InstallationReporter::ReportFailure(
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 044a42c..3bdc30b 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1598,11 +1598,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "enable-site-exploration-ui",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "enable-site-isolation-for-password-sites",
     "owners": [ "site-isolation-dev", "alexmos", "lukasza" ],
     "expiry_milestone": 79
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 11ddf2f..885e365 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -875,74 +875,6 @@
 const char kEnableSensorContentSettingDescription[] =
     "Enable UI in content settings to control access to the sensor APIs.";
 
-const char kEnableSyncPseudoUSSAppListName[] =
-    "Enable pseudo-USS for APP_LIST sync.";
-const char kEnableSyncPseudoUSSAppListDescription[] =
-    "Enable new USS-based codepath for sync datatype APP_LIST.";
-
-const char kEnableSyncPseudoUSSAppsName[] = "Enable pseudo-USS for APPS sync.";
-const char kEnableSyncPseudoUSSAppsDescription[] =
-    "Enable new USS-based codepath for sync datatype APPS.";
-
-const char kEnableSyncPseudoUSSDictionaryName[] =
-    "Enable pseudo-USS for DICTIONARY sync.";
-const char kEnableSyncPseudoUSSDictionaryDescription[] =
-    "Enable new USS-based codepath for sync datatype DICTIONARY.";
-
-const char kEnableSyncPseudoUSSExtensionSettingsName[] =
-    "Enable pseudo-USS for EXTENSION_SETTINGS and APP_SETTINGS sync.";
-const char kEnableSyncPseudoUSSExtensionSettingsDescription[] =
-    "Enable new USS-based codepath for sync datatypes EXTENSION_SETTINGS and "
-    "APP_SETTINGS.";
-
-const char kEnableSyncPseudoUSSExtensionsName[] =
-    "Enable pseudo-USS for EXTENSIONS sync.";
-const char kEnableSyncPseudoUSSExtensionsDescription[] =
-    "Enable new USS-based codepath for sync datatype EXTENSIONS.";
-
-const char kEnableSyncPseudoUSSFaviconsName[] =
-    "Enable pseudo-USS for favicon sync.";
-const char kEnableSyncPseudoUSSFaviconsDescription[] =
-    "Enable new USS-based codepath for sync datatypes FAVICON_IMAGES and "
-    "FAVICON_TRACKING.";
-
-const char kEnableSyncPseudoUSSHistoryDeleteDirectivesName[] =
-    "Enable pseudo-USS for HISTORY_DELETE_DIRECTIVES sync.";
-const char kEnableSyncPseudoUSSHistoryDeleteDirectivesDescription[] =
-    "Enable new USS-based codepath for sync datatype "
-    "HISTORY_DELETE_DIRECTIVES.";
-
-const char kEnableSyncPseudoUSSPasswordsName[] =
-    "Enable pseudo-USS for PASSWORDS sync.";
-const char kEnableSyncPseudoUSSPasswordsDescription[] =
-    "Enable new USS-based codepath for sync datatype PASSWORDS (pseudo-USS).";
-
-const char kEnableSyncPseudoUSSPreferencesName[] =
-    "Enable pseudo-USS for PREFERENCES sync.";
-const char kEnableSyncPseudoUSSPreferencesDescription[] =
-    "Enable new USS-based codepath for sync datatype PREFERENCES.";
-
-const char kEnableSyncPseudoUSSPriorityPreferencesName[] =
-    "Enable pseudo-USS for PRIORITY_PREFERENCES sync.";
-const char kEnableSyncPseudoUSSPriorityPreferencesDescription[] =
-    "Enable new USS-based codepath for sync datatype PRIORITY_PREFERENCES.";
-
-const char kEnableSyncPseudoUSSSearchEnginesName[] =
-    "Enable pseudo-USS for SEARCH_ENGINES sync.";
-const char kEnableSyncPseudoUSSSearchEnginesDescription[] =
-    "Enable new USS-based codepath for sync datatype SEARCH_ENGINES.";
-
-const char kEnableSyncPseudoUSSSupervisedUsersName[] =
-    "Enable pseudo-USS for supervised users sync.";
-const char kEnableSyncPseudoUSSSupervisedUsersDescription[] =
-    "Enable new USS-based codepath for sync datatypes SUPERVISED_USER_SETTINGS "
-    "and SUPERVISED_USER_WHITELISTS.";
-
-const char kEnableSyncPseudoUSSThemesName[] =
-    "Enable pseudo-USS for THEMES sync.";
-const char kEnableSyncPseudoUSSThemesDescription[] =
-    "Enable new USS-based codepath for sync datatype THEMES.";
-
 const char kEnableSyncUSSBookmarksName[] = "Enable USS for bookmarks sync";
 const char kEnableSyncUSSBookmarksDescription[] =
     "Enables the new, experimental implementation of bookmark sync";
@@ -1086,11 +1018,6 @@
     "Enables granting and removing access to features through the "
     "Feature-Policy HTTP header.";
 
-const char kFontCacheScalingName[] = "FontCache scaling";
-const char kFontCacheScalingDescription[] =
-    "Reuse a cached font in the renderer to serve different sizes of font for "
-    "faster layout.";
-
 const char kForceEffectiveConnectionTypeName[] =
     "Override effective connection type";
 const char kForceEffectiveConnectionTypeDescription[] =
@@ -2597,10 +2524,6 @@
 const char kShoppingAssistDescription[] =
     "Show some shopping assistance when available";
 
-const char kSiteExplorationUiName[] = "Site Exploration UI";
-const char kSiteExplorationUiDescription[] =
-    "Show site suggestions in the Exploration UI";
-
 const char kSiteIsolationForPasswordSitesName[] =
     "Site Isolation For Password Sites";
 const char kSiteIsolationForPasswordSitesDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 0e13d9b..eae7139c 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -525,45 +525,6 @@
 extern const char kEnableSensorContentSettingName[];
 extern const char kEnableSensorContentSettingDescription[];
 
-extern const char kEnableSyncPseudoUSSAppListName[];
-extern const char kEnableSyncPseudoUSSAppListDescription[];
-
-extern const char kEnableSyncPseudoUSSAppsName[];
-extern const char kEnableSyncPseudoUSSAppsDescription[];
-
-extern const char kEnableSyncPseudoUSSDictionaryName[];
-extern const char kEnableSyncPseudoUSSDictionaryDescription[];
-
-extern const char kEnableSyncPseudoUSSExtensionSettingsName[];
-extern const char kEnableSyncPseudoUSSExtensionSettingsDescription[];
-
-extern const char kEnableSyncPseudoUSSExtensionsName[];
-extern const char kEnableSyncPseudoUSSExtensionsDescription[];
-
-extern const char kEnableSyncPseudoUSSFaviconsName[];
-extern const char kEnableSyncPseudoUSSFaviconsDescription[];
-
-extern const char kEnableSyncPseudoUSSHistoryDeleteDirectivesName[];
-extern const char kEnableSyncPseudoUSSHistoryDeleteDirectivesDescription[];
-
-extern const char kEnableSyncPseudoUSSPasswordsName[];
-extern const char kEnableSyncPseudoUSSPasswordsDescription[];
-
-extern const char kEnableSyncPseudoUSSPreferencesName[];
-extern const char kEnableSyncPseudoUSSPreferencesDescription[];
-
-extern const char kEnableSyncPseudoUSSPriorityPreferencesName[];
-extern const char kEnableSyncPseudoUSSPriorityPreferencesDescription[];
-
-extern const char kEnableSyncPseudoUSSSearchEnginesName[];
-extern const char kEnableSyncPseudoUSSSearchEnginesDescription[];
-
-extern const char kEnableSyncPseudoUSSSupervisedUsersName[];
-extern const char kEnableSyncPseudoUSSSupervisedUsersDescription[];
-
-extern const char kEnableSyncPseudoUSSThemesName[];
-extern const char kEnableSyncPseudoUSSThemesDescription[];
-
 extern const char kEnableSyncUSSBookmarksName[];
 extern const char kEnableSyncUSSBookmarksDescription[];
 
@@ -651,7 +612,6 @@
 extern const char kFeaturePolicyName[];
 extern const char kFeaturePolicyDescription[];
 
-extern const char kFontCacheScalingName[];
 extern const char kFontCacheScalingDescription[];
 
 extern const char kForceEffectiveConnectionTypeName[];
@@ -1551,9 +1511,6 @@
 extern const char kShoppingAssistName[];
 extern const char kShoppingAssistDescription[];
 
-extern const char kSiteExplorationUiName[];
-extern const char kSiteExplorationUiDescription[];
-
 extern const char kSiteIsolationForPasswordSitesName[];
 extern const char kSiteIsolationForPasswordSitesDescription[];
 
diff --git a/chrome/browser/media/media_engagement_service_factory.h b/chrome/browser/media/media_engagement_service_factory.h
index 1eee510..84755ab9 100644
--- a/chrome/browser/media/media_engagement_service_factory.h
+++ b/chrome/browser/media/media_engagement_service_factory.h
@@ -23,7 +23,7 @@
   MediaEngagementServiceFactory();
   ~MediaEngagementServiceFactory() override;
 
-  // BrowserContextKeyedBaseFactory methods:
+  // BrowserContextKeyedServiceFactory methods:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
   content::BrowserContext* GetBrowserContextToUse(
diff --git a/chrome/browser/media/webrtc/media_authorization_wrapper_mac.h b/chrome/browser/media/webrtc/media_authorization_wrapper_mac.h
new file mode 100644
index 0000000..cd5c578
--- /dev/null
+++ b/chrome/browser/media/webrtc/media_authorization_wrapper_mac.h
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_AUTHORIZATION_WRAPPER_MAC_H_
+#define CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_AUTHORIZATION_WRAPPER_MAC_H_
+
+#import <Foundation/NSString.h>
+
+#include "base/callback_forward.h"
+
+namespace system_media_permissions {
+
+class MediaAuthorizationWrapper {
+ public:
+  virtual ~MediaAuthorizationWrapper() {}
+
+  virtual NSInteger AuthorizationStatusForMediaType(NSString* media_type) = 0;
+  virtual void RequestAccessForMediaType(NSString* media_type,
+                                         base::RepeatingClosure callback,
+                                         const base::TaskTraits& traits) = 0;
+};
+
+}  // namespace system_media_permissions
+
+#endif  // CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_AUTHORIZATION_WRAPPER_MAC_H_
diff --git a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h
index f075cea..bd9aec5 100644
--- a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h
+++ b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h
@@ -13,6 +13,8 @@
 
 namespace system_media_permissions {
 
+class MediaAuthorizationWrapper;
+
 // System permission state. These are also used in stats - do not remove or
 // re-arrange the values.
 enum class SystemPermission {
@@ -42,6 +44,9 @@
 void RequestSystemVideoCapturePermisson(base::OnceClosure callback,
                                         const base::TaskTraits& traits);
 
+// Sets the wrapper object for OS calls. For test mocking purposes.
+void SetMediaAuthorizationWrapperForTesting(MediaAuthorizationWrapper* wrapper);
+
 }  // namespace system_media_permissions
 
 #endif  // CHROME_BROWSER_MEDIA_WEBRTC_SYSTEM_MEDIA_CAPTURE_PERMISSIONS_MAC_H_
diff --git a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
index 64cfb77..90cd4aa 100644
--- a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
+++ b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
@@ -22,8 +22,11 @@
 #include "base/callback.h"
 #include "base/callback_helpers.h"
 #include "base/logging.h"
+#include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
+#include "chrome/browser/media/webrtc/media_authorization_wrapper_mac.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -31,18 +34,74 @@
 
 namespace {
 
+// Pointer to OS call wrapper that tests can set.
+MediaAuthorizationWrapper* g_media_authorization_wrapper_for_tests = nullptr;
+
+// Implementation of OS call wrapper that does the actual OS calls.
+class MediaAuthorizationWrapperImpl : public MediaAuthorizationWrapper {
+ public:
+  MediaAuthorizationWrapperImpl() = default;
+  ~MediaAuthorizationWrapperImpl() final = default;
+
+  NSInteger AuthorizationStatusForMediaType(NSString* media_type) final {
+    if (@available(macOS 10.14, *)) {
+      AVCaptureDevice* target = [AVCaptureDevice class];
+      SEL selector = @selector(authorizationStatusForMediaType:);
+      NSInteger auth_status = 0;
+      if ([target respondsToSelector:selector]) {
+        auth_status =
+            (NSInteger)[target performSelector:selector withObject:media_type];
+      } else {
+        DLOG(WARNING)
+            << "authorizationStatusForMediaType could not be executed";
+      }
+      return auth_status;
+    }
+
+    NOTREACHED();
+    return 0;
+  }
+
+  void RequestAccessForMediaType(NSString* media_type,
+                                 base::RepeatingClosure callback,
+                                 const base::TaskTraits& traits) final {
+    if (@available(macOS 10.14, *)) {
+      AVCaptureDevice* target = [AVCaptureDevice class];
+      SEL selector = @selector(requestAccessForMediaType:completionHandler:);
+      if ([target respondsToSelector:selector]) {
+        [target performSelector:selector
+                     withObject:media_type
+                     withObject:^(BOOL granted) {
+                       base::PostTaskWithTraits(FROM_HERE, traits,
+                                                std::move(callback));
+                     }];
+      } else {
+        DLOG(WARNING) << "requestAccessForMediaType could not be executed";
+        base::PostTaskWithTraits(FROM_HERE, traits, std::move(callback));
+      }
+    } else {
+      NOTREACHED();
+      base::PostTaskWithTraits(FROM_HERE, traits, std::move(callback));
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MediaAuthorizationWrapperImpl);
+};
+
+MediaAuthorizationWrapper& GetMediaAuthorizationWrapper() {
+  if (g_media_authorization_wrapper_for_tests)
+    return *g_media_authorization_wrapper_for_tests;
+
+  static base::NoDestructor<MediaAuthorizationWrapperImpl>
+      media_authorization_wrapper;
+  return *media_authorization_wrapper;
+}
+
 NSInteger MediaAuthorizationStatus(NSString* media_type) {
   if (@available(macOS 10.14, *)) {
-    AVCaptureDevice* target = [AVCaptureDevice class];
-    SEL selector = @selector(authorizationStatusForMediaType:);
-    NSInteger auth_status = 0;
-    if ([target respondsToSelector:selector]) {
-      auth_status =
-          (NSInteger)[target performSelector:selector withObject:media_type];
-    } else {
-      DLOG(WARNING) << "authorizationStatusForMediaType could not be executed";
-    }
-    return auth_status;
+    return GetMediaAuthorizationWrapper().AuthorizationStatusForMediaType(
+        media_type);
   }
 
   NOTREACHED();
@@ -77,18 +136,8 @@
                                          base::RepeatingClosure callback,
                                          const base::TaskTraits& traits) {
   if (@available(macOS 10.14, *)) {
-      AVCaptureDevice* target = [AVCaptureDevice class];
-      SEL selector = @selector(requestAccessForMediaType:completionHandler:);
-      if ([target respondsToSelector:selector]) {
-        [target performSelector:selector
-                     withObject:media_type
-                     withObject:^(BOOL granted) {
-                       base::PostTaskWithTraits(FROM_HERE, traits,
-                                                std::move(callback));
-                     }];
-      } else {
-        DLOG(WARNING) << "requestAccessForMediaType could not be executed";
-      }
+    GetMediaAuthorizationWrapper().RequestAccessForMediaType(
+        media_type, std::move(callback), traits);
   } else {
     NOTREACHED();
     // Should never happen since for pre-10.14 system permissions don't exist
@@ -122,4 +171,10 @@
       traits);
 }
 
+void SetMediaAuthorizationWrapperForTesting(
+    MediaAuthorizationWrapper* wrapper) {
+  CHECK(!g_media_authorization_wrapper_for_tests);
+  g_media_authorization_wrapper_for_tests = wrapper;
+}
+
 }  // namespace system_media_permissions
diff --git a/chrome/browser/net/errorpage_browsertest.cc b/chrome/browser/net/errorpage_browsertest.cc
index 176bef1..c0a6e0c 100644
--- a/chrome/browser/net/errorpage_browsertest.cc
+++ b/chrome/browser/net/errorpage_browsertest.cc
@@ -91,7 +91,7 @@
 #include "chromeos/tpm/stub_install_attributes.h"
 #include "components/policy/core/common/policy_types.h"
 #else
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/profile_policy_connector_builder.h"
 #endif
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 
@@ -1168,8 +1168,7 @@
     policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
         &policy_provider_);
 #else
-    policy::ProfilePolicyConnectorFactory::GetInstance()
-        ->PushProviderForTesting(&policy_provider_);
+    policy::PushProfilePolicyConnectorProviderForTesting(&policy_provider_);
 #endif
 
     ErrorPageTest::SetUpInProcessBrowserTestFixture();
diff --git a/chrome/browser/notifications/metrics/notification_metrics_logger_factory.h b/chrome/browser/notifications/metrics/notification_metrics_logger_factory.h
index c506cea..5dee2d4 100644
--- a/chrome/browser/notifications/metrics/notification_metrics_logger_factory.h
+++ b/chrome/browser/notifications/metrics/notification_metrics_logger_factory.h
@@ -24,7 +24,7 @@
 
   NotificationMetricsLoggerFactory();
 
-  // BrowserContextKeyedBaseFactory implementation.
+  // BrowserContextKeyedServiceFactory implementation.
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* context) const override;
   content::BrowserContext* GetBrowserContextToUse(
diff --git a/chrome/browser/notifications/notification_display_service_factory.h b/chrome/browser/notifications/notification_display_service_factory.h
index a4636ef..79a7adb 100644
--- a/chrome/browser/notifications/notification_display_service_factory.h
+++ b/chrome/browser/notifications/notification_display_service_factory.h
@@ -23,7 +23,7 @@
 
   NotificationDisplayServiceFactory();
 
-  // BrowserContextKeyedBaseFactory implementation.
+  // BrowserContextKeyedServiceFactory implementation.
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* context) const override;
   content::BrowserContext* GetBrowserContextToUse(
diff --git a/chrome/browser/notifications/notifier_state_tracker_factory.h b/chrome/browser/notifications/notifier_state_tracker_factory.h
index ea887ed..a737373 100644
--- a/chrome/browser/notifications/notifier_state_tracker_factory.h
+++ b/chrome/browser/notifications/notifier_state_tracker_factory.h
@@ -23,7 +23,7 @@
   NotifierStateTrackerFactory();
   ~NotifierStateTrackerFactory() override;
 
-  // BrowserContextKeyedBaseFactory implementation.
+  // BrowserContextKeyedServiceFactory implementation.
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
   content::BrowserContext* GetBrowserContextToUse(
diff --git a/chrome/browser/notifications/platform_notification_service_factory.h b/chrome/browser/notifications/platform_notification_service_factory.h
index 72d10ab..e9be556 100644
--- a/chrome/browser/notifications/platform_notification_service_factory.h
+++ b/chrome/browser/notifications/platform_notification_service_factory.h
@@ -24,7 +24,7 @@
 
   PlatformNotificationServiceFactory();
 
-  // BrowserContextKeyedBaseFactory implementation.
+  // BrowserContextKeyedServiceFactory implementation.
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
   content::BrowserContext* GetBrowserContextToUse(
diff --git a/chrome/browser/password_manager/password_manager_internals_service_unittest.cc b/chrome/browser/password_manager/password_manager_internals_service_unittest.cc
index 39e2615..a4d8312e 100644
--- a/chrome/browser/password_manager/password_manager_internals_service_unittest.cc
+++ b/chrome/browser/password_manager/password_manager_internals_service_unittest.cc
@@ -68,8 +68,8 @@
   PasswordManagerInternalsService* service =
       PasswordManagerInternalsServiceFactory::GetForBrowserContext(
           incognito_profile);
-  // BrowserContextKeyedBaseFactory::GetBrowserContextToUse should return NULL
-  // for |profile|, because |profile| is incognito. Therefore the returned
+  // BrowserContextKeyedServiceFactory::GetBrowserContextToUse should return
+  // NULL for |profile|, because |profile| is incognito. Therefore the returned
   // |service| should also be NULL.
   EXPECT_FALSE(service);
 }
diff --git a/chrome/browser/permissions/permission_manager_factory.h b/chrome/browser/permissions/permission_manager_factory.h
index be4990571..8b25b70c 100644
--- a/chrome/browser/permissions/permission_manager_factory.h
+++ b/chrome/browser/permissions/permission_manager_factory.h
@@ -27,7 +27,7 @@
   PermissionManagerFactory();
   ~PermissionManagerFactory() override;
 
-  // BrowserContextKeyedBaseFactory methods:
+  // BrowserContextKeyedServiceFactory methods:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
   content::BrowserContext* GetBrowserContextToUse(
diff --git a/chrome/browser/plugins/flash_temporary_permission_tracker_factory.h b/chrome/browser/plugins/flash_temporary_permission_tracker_factory.h
index 5fc392f..3c2f05c 100644
--- a/chrome/browser/plugins/flash_temporary_permission_tracker_factory.h
+++ b/chrome/browser/plugins/flash_temporary_permission_tracker_factory.h
@@ -35,8 +35,6 @@
   // RefcountedBrowserContextKeyedServiceFactory methods:
   scoped_refptr<RefcountedKeyedService> BuildServiceInstanceFor(
       content::BrowserContext* context) const override;
-
-  // BrowserContextKeyedBaseFactory methods:
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
 
diff --git a/chrome/browser/policy/cloud/cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
index 83af3a7..6ff603b 100644
--- a/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/cloud/cloud_policy_test_utils.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -292,8 +291,7 @@
 
   PolicyService* GetPolicyService() {
     ProfilePolicyConnector* profile_connector =
-        ProfilePolicyConnectorFactory::GetForBrowserContext(
-            browser()->profile());
+        browser()->profile()->GetProfilePolicyConnector();
     return profile_connector->policy_service();
   }
 
diff --git a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
index 8e80896a..b24109f 100644
--- a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -229,8 +228,7 @@
 
   void RefreshPolicies() {
     ProfilePolicyConnector* profile_connector =
-        ProfilePolicyConnectorFactory::GetForBrowserContext(
-            browser()->profile());
+        browser()->profile()->GetProfilePolicyConnector();
     PolicyService* policy_service = profile_connector->policy_service();
     base::RunLoop run_loop;
     policy_service->RefreshPolicies(run_loop.QuitClosure());
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 7245f40a..018ea85 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -608,6 +608,9 @@
   { key::kVirtualKeyboardEnabled,
     ash::prefs::kAccessibilityVirtualKeyboardEnabled,
     base::Value::Type::BOOLEAN },
+  { key::kStickyKeysEnabled,
+    ash::prefs::kAccessibilityStickyKeysEnabled,
+    base::Value::Type::BOOLEAN },
   { key::kDeviceLoginScreenDefaultLargeCursorEnabled,
     nullptr,
     base::Value::Type::BOOLEAN },
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 60c5f63..38cd08d 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -80,7 +80,6 @@
 #include "chrome/browser/permissions/permission_request_manager.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/tab_load_tracker_test_support.h"
@@ -1592,7 +1591,9 @@
           ->GetPolicies(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
   EXPECT_TRUE(expected.Equals(actual_from_browser));
   const PolicyMap& actual_from_profile =
-      ProfilePolicyConnectorFactory::GetForBrowserContext(browser()->profile())
+      browser()
+          ->profile()
+          ->GetProfilePolicyConnector()
           ->policy_service()
           ->GetPolicies(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
   EXPECT_TRUE(expected.Equals(actual_from_profile));
@@ -3951,6 +3952,32 @@
   EXPECT_FALSE(accessibility_manager->IsVirtualKeyboardEnabled());
 }
 
+IN_PROC_BROWSER_TEST_F(PolicyTest, StickyKeysEnabled) {
+  // Verifies that the sticky keys accessibility feature can be
+  // controlled through policy.
+  chromeos::AccessibilityManager* accessibility_manager =
+      chromeos::AccessibilityManager::Get();
+
+  // Verify that the sticky keys is initially disabled
+  EXPECT_FALSE(accessibility_manager->IsStickyKeysEnabled());
+
+  // Manually enable the sticky keys.
+  accessibility_manager->EnableStickyKeys(true);
+  EXPECT_TRUE(accessibility_manager->IsStickyKeysEnabled());
+
+  // Verify that policy overrides the manual setting.
+  PolicyMap policies;
+  policies.Set(key::kStickyKeysEnabled, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               std::make_unique<base::Value>(false), nullptr);
+  UpdateProviderPolicy(policies);
+  EXPECT_FALSE(accessibility_manager->IsStickyKeysEnabled());
+
+  // Verify that the sticky keys cannot be enabled manually anymore.
+  accessibility_manager->EnableStickyKeys(true);
+  EXPECT_FALSE(accessibility_manager->IsStickyKeysEnabled());
+}
+
 IN_PROC_BROWSER_TEST_F(PolicyTest, VirtualKeyboardEnabled) {
   auto* keyboard_client = ChromeKeyboardControllerClient::Get();
   ASSERT_TRUE(keyboard_client);
diff --git a/chrome/browser/policy/policy_conversions.cc b/chrome/browser/policy/policy_conversions.cc
index 4fc15d3..78266c4 100644
--- a/chrome/browser/policy/policy_conversions.cc
+++ b/chrome/browser/policy/policy_conversions.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/policy/schema_registry_service.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
@@ -99,8 +98,8 @@
 }
 
 PolicyService* GetPolicyService(content::BrowserContext* context) {
-  return ProfilePolicyConnectorFactory::GetForBrowserContext(context)
-      ->policy_service();
+  Profile* profile = Profile::FromBrowserContext(context);
+  return profile->GetProfilePolicyConnector()->policy_service();
 }
 
 // Returns the Schema for |policy_name| if that policy is known. If the policy
diff --git a/chrome/browser/policy/policy_network_browsertest.cc b/chrome/browser/policy/policy_network_browsertest.cc
index 47809e2..e62c6b5 100644
--- a/chrome/browser/policy/policy_network_browsertest.cc
+++ b/chrome/browser/policy/policy_network_browsertest.cc
@@ -13,7 +13,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/system_network_context_manager.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/profile_policy_connector_builder.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profiles_state.h"
@@ -312,8 +312,8 @@
     // Set the overriden policy provider for the first Profile (profile_1_).
     EXPECT_CALL(policy_for_profile_1_, IsInitializationComplete(testing::_))
         .WillRepeatedly(testing::Return(true));
-    policy::ProfilePolicyConnectorFactory::GetInstance()
-        ->PushProviderForTesting(&policy_for_profile_1_);
+    policy::PushProfilePolicyConnectorProviderForTesting(
+        &policy_for_profile_1_);
   }
 
   void SetUpOnMainThread() override {
@@ -329,8 +329,8 @@
     // Prepare policy provider for second profile.
     EXPECT_CALL(policy_for_profile_2_, IsInitializationComplete(testing::_))
         .WillRepeatedly(testing::Return(true));
-    policy::ProfilePolicyConnectorFactory::GetInstance()
-        ->PushProviderForTesting(&policy_for_profile_2_);
+    policy::PushProfilePolicyConnectorProviderForTesting(
+        &policy_for_profile_2_);
 
     ProfileManager* profile_manager = g_browser_process->profile_manager();
 
diff --git a/chrome/browser/policy/profile_policy_connector.cc b/chrome/browser/policy/profile_policy_connector.cc
index 655e7920..e35525d 100644
--- a/chrome/browser/policy/profile_policy_connector.cc
+++ b/chrome/browser/policy/profile_policy_connector.cc
@@ -46,17 +46,16 @@
     SchemaRegistry* schema_registry,
     ConfigurationPolicyProvider* configuration_policy_provider,
     const CloudPolicyStore* policy_store,
+    policy::ChromeBrowserPolicyConnector* connector,
     bool force_immediate_load) {
   configuration_policy_provider_ = configuration_policy_provider;
   policy_store_ = policy_store;
 
 #if defined(OS_CHROMEOS)
-  BrowserPolicyConnectorChromeOS* connector =
-      g_browser_process->platform_part()->browser_policy_connector_chromeos();
+  auto* browser_policy_connector =
+      static_cast<BrowserPolicyConnectorChromeOS*>(connector);
 #else
   DCHECK_EQ(nullptr, user);
-  ChromeBrowserPolicyConnector* connector =
-      g_browser_process->browser_policy_connector();
 #endif
 
   if (connector->GetPlatformProvider()) {
@@ -68,12 +67,13 @@
   }
 
 #if defined(OS_CHROMEOS)
-  if (connector->GetDeviceCloudPolicyManager()) {
-    policy_providers_.push_back(connector->GetDeviceCloudPolicyManager());
-  }
-  if (connector->GetDeviceActiveDirectoryPolicyManager()) {
+  if (browser_policy_connector->GetDeviceCloudPolicyManager()) {
     policy_providers_.push_back(
-        connector->GetDeviceActiveDirectoryPolicyManager());
+        browser_policy_connector->GetDeviceCloudPolicyManager());
+  }
+  if (browser_policy_connector->GetDeviceActiveDirectoryPolicyManager()) {
+    policy_providers_.push_back(
+        browser_policy_connector->GetDeviceActiveDirectoryPolicyManager());
   }
 #else
   for (auto* provider : connector->GetPolicyProviders()) {
@@ -97,8 +97,8 @@
   if (!user) {
     DCHECK(schema_registry);
     // This case occurs for the signin and the lock screen app profiles.
-    special_user_policy_provider_.reset(
-        new LoginProfilePolicyProvider(connector->GetPolicyService()));
+    special_user_policy_provider_.reset(new LoginProfilePolicyProvider(
+        browser_policy_connector->GetPolicyService()));
   } else {
     // |user| should never be nullptr except for the signin and the lock screen
     // app profile.
@@ -108,7 +108,8 @@
     // the user supplied is not a device-local account user.
     special_user_policy_provider_ = DeviceLocalAccountPolicyProvider::Create(
         user->GetAccountId().GetUserEmail(),
-        connector->GetDeviceLocalAccountPolicyService(), force_immediate_load);
+        browser_policy_connector->GetDeviceLocalAccountPolicyService(),
+        force_immediate_load);
   }
   if (special_user_policy_provider_) {
     special_user_policy_provider_->Init(schema_registry);
@@ -121,9 +122,11 @@
 #if defined(OS_CHROMEOS)
   if (is_primary_user_) {
     if (configuration_policy_provider)
-      connector->SetUserPolicyDelegate(configuration_policy_provider);
+      browser_policy_connector->SetUserPolicyDelegate(
+          configuration_policy_provider);
     else if (special_user_policy_provider_)
-      connector->SetUserPolicyDelegate(special_user_policy_provider_.get());
+      browser_policy_connector->SetUserPolicyDelegate(
+          special_user_policy_provider_.get());
   }
 #endif
 }
diff --git a/chrome/browser/policy/profile_policy_connector.h b/chrome/browser/policy/profile_policy_connector.h
index a312c9fa..c50a3c9 100644
--- a/chrome/browser/policy/profile_policy_connector.h
+++ b/chrome/browser/policy/profile_policy_connector.h
@@ -11,7 +11,6 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
-#include "components/keyed_service/core/keyed_service.h"
 
 namespace user_manager {
 class User;
@@ -23,12 +22,16 @@
 class ConfigurationPolicyProvider;
 class PolicyService;
 class SchemaRegistry;
+class ChromeBrowserPolicyConnector;
 
-// A KeyedService that creates and manages the per-Profile policy components.
-class ProfilePolicyConnector : public KeyedService {
+// The ProfilePolicyConnector creates and manages the per-Profile policy
+// components. Since the ProfilePolicyConnector instance is accessed from
+// Profile, not from a KeyedServiceFactory anymore, the ProfilePolicyConnector
+// no longer needs to be a KeyedService.
+class ProfilePolicyConnector final {
  public:
   ProfilePolicyConnector();
-  ~ProfilePolicyConnector() override;
+  ~ProfilePolicyConnector();
 
   // |user| is only used in Chrome OS builds and should be set to nullptr
   // otherwise.  |configuration_policy_provider| and |policy_store| are nullptr
@@ -39,13 +42,13 @@
             SchemaRegistry* schema_registry,
             ConfigurationPolicyProvider* configuration_policy_provider,
             const CloudPolicyStore* policy_store,
+            policy::ChromeBrowserPolicyConnector* browser_policy_connector,
             bool force_immediate_load);
 
   void InitForTesting(std::unique_ptr<PolicyService> service);
   void OverrideIsManagedForTesting(bool is_managed);
 
-  // KeyedService:
-  void Shutdown() override;
+  void Shutdown();
 
   // This is never NULL.
   PolicyService* policy_service() const { return policy_service_.get(); }
diff --git a/chrome/browser/policy/profile_policy_connector_builder.cc b/chrome/browser/policy/profile_policy_connector_builder.cc
new file mode 100644
index 0000000..37d4d2b
--- /dev/null
+++ b/chrome/browser/policy/profile_policy_connector_builder.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2013 The Chromium 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/policy/profile_policy_connector_builder.h"
+
+#include <list>
+#include <utility>
+
+#include "base/no_destructor.h"
+#include "build/build_config.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/policy/schema_registry_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/policy/core/common/policy_service_impl.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/policy/active_directory_policy_manager.h"
+#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
+#include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#else  // Non-ChromeOS.
+#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+#endif
+
+namespace policy {
+
+namespace {
+
+std::list<ConfigurationPolicyProvider*>* GetTestProviders() {
+  static base::NoDestructor<std::list<ConfigurationPolicyProvider*>> instances;
+  return instances.get();
+}
+
+}  // namespace
+
+std::unique_ptr<ProfilePolicyConnector>
+CreateProfilePolicyConnectorForBrowserContext(
+    SchemaRegistry* schema_registry,
+    UserCloudPolicyManager* user_cloud_policy_manager,
+    policy::ChromeBrowserPolicyConnector* browser_policy_connector,
+    bool force_immediate_load,
+    content::BrowserContext* context) {
+  const user_manager::User* user = nullptr;
+  ConfigurationPolicyProvider* policy_provider = nullptr;
+  const CloudPolicyStore* policy_store = nullptr;
+
+#if defined(OS_CHROMEOS)
+  Profile* const profile = Profile::FromBrowserContext(context);
+  if (!chromeos::ProfileHelper::IsSigninProfile(profile) &&
+      !chromeos::ProfileHelper::IsLockScreenAppProfile(profile)) {
+    user = chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+    CHECK(user);
+  }
+
+  // On ChromeOS, we always pass nullptr for the |user_cloud_policy_manager|.
+  // This is because the |policy_provider| could be either a
+  // UserCloudPolicyManagerChromeOS or a ActiveDirectoryPolicyManager, both of
+  // which should be obtained via UserPolicyManagerFactoryChromeOS APIs.
+  CloudPolicyManager* cloud_policy_manager =
+      UserPolicyManagerFactoryChromeOS::GetCloudPolicyManagerForProfile(
+          profile);
+  ActiveDirectoryPolicyManager* active_directory_manager =
+      UserPolicyManagerFactoryChromeOS::
+          GetActiveDirectoryPolicyManagerForProfile(profile);
+  if (cloud_policy_manager) {
+    policy_provider = cloud_policy_manager;
+    policy_store = cloud_policy_manager->core()->store();
+  } else if (active_directory_manager) {
+    policy_provider = active_directory_manager;
+    policy_store = active_directory_manager->store();
+  }
+#else
+  if (user_cloud_policy_manager) {
+    policy_provider = user_cloud_policy_manager;
+    policy_store = user_cloud_policy_manager->core()->store();
+  }
+#endif  // defined(OS_CHROMEOS)
+
+  return CreateAndInitProfilePolicyConnector(
+      schema_registry, browser_policy_connector, policy_provider, policy_store,
+      force_immediate_load, user);
+}
+
+std::unique_ptr<ProfilePolicyConnector> CreateAndInitProfilePolicyConnector(
+    SchemaRegistry* schema_registry,
+    policy::ChromeBrowserPolicyConnector* browser_policy_connector,
+    ConfigurationPolicyProvider* policy_provider,
+    const CloudPolicyStore* policy_store,
+    bool force_immediate_load,
+    const user_manager::User* user) {
+  auto connector = std::make_unique<ProfilePolicyConnector>();
+
+  std::list<ConfigurationPolicyProvider*>* test_providers = GetTestProviders();
+  if (test_providers->empty()) {
+    connector->Init(user, schema_registry, policy_provider, policy_store,
+                    browser_policy_connector, force_immediate_load);
+  } else {
+    PolicyServiceImpl::Providers providers;
+    providers.push_back(test_providers->front());
+    test_providers->pop_front();
+    auto service = std::make_unique<PolicyServiceImpl>(std::move(providers));
+    connector->InitForTesting(std::move(service));
+  }
+
+  return connector;
+}
+
+void PushProfilePolicyConnectorProviderForTesting(
+    ConfigurationPolicyProvider* provider) {
+  std::list<ConfigurationPolicyProvider*>* test_providers = GetTestProviders();
+  test_providers->push_back(provider);
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/profile_policy_connector_builder.h b/chrome/browser/policy/profile_policy_connector_builder.h
new file mode 100644
index 0000000..e8230e7
--- /dev/null
+++ b/chrome/browser/policy/profile_policy_connector_builder.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2013 The Chromium 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_POLICY_PROFILE_POLICY_CONNECTOR_BUILDER_H_
+#define CHROME_BROWSER_POLICY_PROFILE_POLICY_CONNECTOR_BUILDER_H_
+
+#include <memory>
+
+namespace user_manager {
+class User;
+}
+namespace content {
+class BrowserContext;
+}
+
+namespace policy {
+
+class ChromeBrowserPolicyConnector;
+class CloudPolicyStore;
+class ConfigurationPolicyProvider;
+class ProfilePolicyConnector;
+class SchemaRegistry;
+class UserCloudPolicyManager;
+
+// Factory method that creates and initializes a new instance of
+// ProfilePolicyConnector for the given |context|.
+std::unique_ptr<ProfilePolicyConnector>
+CreateProfilePolicyConnectorForBrowserContext(
+    SchemaRegistry* schema_registry,
+    UserCloudPolicyManager* user_cloud_policy_manager,
+    policy::ChromeBrowserPolicyConnector* browser_policy_connector,
+    bool force_immediate_load,
+    content::BrowserContext* context);
+
+// Factory method that creates and initializes a ProfilePolicyConnector.
+std::unique_ptr<ProfilePolicyConnector> CreateAndInitProfilePolicyConnector(
+    SchemaRegistry* schema_registry,
+    policy::ChromeBrowserPolicyConnector* browser_policy_connector,
+    ConfigurationPolicyProvider* policy_provider,
+    const CloudPolicyStore* policy_store,
+    bool force_immediate_load,
+    const user_manager::User* user = nullptr);
+
+// The next caller to create a ProfilePolicyConnector will get a PolicyService
+// with |provider| as its sole policy provider. This can be called multiple
+// times to override the policy providers for more than one Profile.
+void PushProfilePolicyConnectorProviderForTesting(
+    ConfigurationPolicyProvider* provider);
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_POLICY_PROFILE_POLICY_CONNECTOR_BUILDER_H_
diff --git a/chrome/browser/policy/profile_policy_connector_factory.cc b/chrome/browser/policy/profile_policy_connector_factory.cc
deleted file mode 100644
index bfa89a22..0000000
--- a/chrome/browser/policy/profile_policy_connector_factory.cc
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright (c) 2013 The Chromium 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/policy/profile_policy_connector_factory.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "base/memory/singleton.h"
-#include "build/build_config.h"
-#include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/schema_registry_service.h"
-#include "chrome/browser/profiles/incognito_helpers.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/policy/core/common/policy_service.h"
-#include "components/policy/core/common/policy_service_impl.h"
-
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/policy/active_directory_policy_manager.h"
-#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
-#include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "components/user_manager/user.h"
-#else  // Non-ChromeOS.
-#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
-#endif
-
-namespace policy {
-
-// static
-ProfilePolicyConnectorFactory* ProfilePolicyConnectorFactory::GetInstance() {
-  return base::Singleton<ProfilePolicyConnectorFactory>::get();
-}
-
-// static
-ProfilePolicyConnector* ProfilePolicyConnectorFactory::GetForBrowserContext(
-    content::BrowserContext* context) {
-  return GetInstance()->GetForBrowserContextInternal(context);
-}
-
-// static
-std::unique_ptr<ProfilePolicyConnector>
-ProfilePolicyConnectorFactory::CreateForBrowserContext(
-    content::BrowserContext* context,
-    bool force_immediate_load) {
-  return GetInstance()->CreateForBrowserContextInternal(context,
-                                                        force_immediate_load);
-}
-
-// static
-bool ProfilePolicyConnectorFactory::IsProfileManaged(
-    const content::BrowserContext* context) {
-  // Note: GetForBrowserContextInternal CHECK-fails if there is no
-  // ProfilePolicyConnector for |context| yet.
-  return GetInstance()->GetForBrowserContextInternal(context)->IsManaged();
-}
-
-void ProfilePolicyConnectorFactory::SetServiceForTesting(
-    content::BrowserContext* context,
-    ProfilePolicyConnector* connector) {
-  ProfilePolicyConnector*& map_entry = connectors_[context];
-  CHECK(!map_entry);
-  map_entry = connector;
-}
-
-void ProfilePolicyConnectorFactory::PushProviderForTesting(
-    ConfigurationPolicyProvider* provider) {
-  test_providers_.push_back(provider);
-}
-
-ProfilePolicyConnectorFactory::ProfilePolicyConnectorFactory()
-    : BrowserContextKeyedBaseFactory(
-        "ProfilePolicyConnector",
-        BrowserContextDependencyManager::GetInstance()) {
-#if defined(OS_CHROMEOS)
-  DependsOn(UserPolicyManagerFactoryChromeOS::GetInstance());
-#endif
-}
-
-ProfilePolicyConnectorFactory::~ProfilePolicyConnectorFactory() {
-  DCHECK(connectors_.empty());
-}
-
-ProfilePolicyConnector*
-ProfilePolicyConnectorFactory::GetForBrowserContextInternal(
-    const content::BrowserContext* context) const {
-  // Get the connector for the original Profile, so that the incognito Profile
-  // gets managed settings from the same PolicyService.
-  const content::BrowserContext* const original_context =
-      chrome::GetBrowserContextRedirectedInIncognito(context);
-  const ConnectorMap::const_iterator it = connectors_.find(original_context);
-  CHECK(it != connectors_.end());
-  return it->second;
-}
-
-std::unique_ptr<ProfilePolicyConnector>
-ProfilePolicyConnectorFactory::CreateForBrowserContextInternal(
-    content::BrowserContext* context,
-    bool force_immediate_load) {
-  DCHECK(connectors_.find(context) == connectors_.end());
-
-  Profile* const profile = Profile::FromBrowserContext(context);
-  const user_manager::User* user = nullptr;
-  SchemaRegistry* schema_registry =
-      profile->GetPolicySchemaRegistryService()->registry();
-
-  ConfigurationPolicyProvider* policy_provider = nullptr;
-  const CloudPolicyStore* policy_store = nullptr;
-
-#if defined(OS_CHROMEOS)
-  if (!chromeos::ProfileHelper::IsSigninProfile(profile) &&
-      !chromeos::ProfileHelper::IsLockScreenAppProfile(profile)) {
-    user = chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
-    CHECK(user);
-  }
-
-  CloudPolicyManager* user_cloud_policy_manager =
-      UserPolicyManagerFactoryChromeOS::GetCloudPolicyManagerForProfile(
-          profile);
-  ActiveDirectoryPolicyManager* active_directory_manager =
-      UserPolicyManagerFactoryChromeOS::
-          GetActiveDirectoryPolicyManagerForProfile(profile);
-  if (user_cloud_policy_manager) {
-    policy_provider = user_cloud_policy_manager;
-    policy_store = user_cloud_policy_manager->core()->store();
-  } else if (active_directory_manager) {
-    policy_provider = active_directory_manager;
-    policy_store = active_directory_manager->store();
-  }
-#else
-  CloudPolicyManager* user_cloud_policy_manager =
-      profile->GetUserCloudPolicyManager();
-  if (user_cloud_policy_manager) {
-    policy_provider = user_cloud_policy_manager;
-    policy_store = user_cloud_policy_manager->core()->store();
-  }
-#endif  // defined(OS_CHROMEOS)
-
-  std::unique_ptr<ProfilePolicyConnector> connector(
-      new ProfilePolicyConnector());
-
-  if (test_providers_.empty()) {
-    connector->Init(user, schema_registry, policy_provider, policy_store,
-                    force_immediate_load);
-  } else {
-    PolicyServiceImpl::Providers providers;
-    providers.push_back(test_providers_.front());
-    test_providers_.pop_front();
-    std::unique_ptr<PolicyServiceImpl> service =
-        std::make_unique<PolicyServiceImpl>(std::move(providers));
-    connector->InitForTesting(std::move(service));
-  }
-
-  connectors_[context] = connector.get();
-  return connector;
-}
-
-void ProfilePolicyConnectorFactory::BrowserContextShutdown(
-    content::BrowserContext* context) {
-  if (Profile::FromBrowserContext(context)->IsOffTheRecord())
-    return;
-  const ConnectorMap::const_iterator it = connectors_.find(context);
-  if (it != connectors_.end())
-    it->second->Shutdown();
-}
-
-void ProfilePolicyConnectorFactory::BrowserContextDestroyed(
-    content::BrowserContext* context) {
-  const ConnectorMap::iterator it = connectors_.find(context);
-  if (it != connectors_.end())
-    connectors_.erase(it);
-  BrowserContextKeyedBaseFactory::BrowserContextDestroyed(context);
-}
-
-void ProfilePolicyConnectorFactory::SetEmptyTestingFactory(
-    content::BrowserContext* context) {}
-
-bool ProfilePolicyConnectorFactory::HasTestingFactory(
-    content::BrowserContext* context) {
-  return false;
-}
-
-void ProfilePolicyConnectorFactory::CreateServiceNow(
-    content::BrowserContext* context) {}
-
-}  // namespace policy
diff --git a/chrome/browser/policy/profile_policy_connector_factory.h b/chrome/browser/policy/profile_policy_connector_factory.h
deleted file mode 100644
index 50911ae..0000000
--- a/chrome/browser/policy/profile_policy_connector_factory.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2013 The Chromium 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_POLICY_PROFILE_POLICY_CONNECTOR_FACTORY_H_
-#define CHROME_BROWSER_POLICY_PROFILE_POLICY_CONNECTOR_FACTORY_H_
-
-#include <list>
-#include <map>
-#include <memory>
-
-#include "base/macros.h"
-#include "components/keyed_service/content/browser_context_keyed_base_factory.h"
-
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-
-}  // namespace base
-
-namespace content {
-class BrowserContext;
-}
-
-namespace policy {
-
-class ConfigurationPolicyProvider;
-class ProfilePolicyConnector;
-
-// Creates ProfilePolicyConnectors for Profiles, which manage the common
-// policy providers and other policy components.
-// TODO(joaodasilva): convert this class to a proper BCKS once the PrefService,
-// which depends on this class, becomes a BCKS too.
-class ProfilePolicyConnectorFactory : public BrowserContextKeyedBaseFactory {
- public:
-  // Returns the ProfilePolicyConnectorFactory singleton.
-  static ProfilePolicyConnectorFactory* GetInstance();
-
-  // Returns the ProfilePolicyConnector associated with |context|. This is only
-  // valid before |context| is shut down.
-  static ProfilePolicyConnector* GetForBrowserContext(
-      content::BrowserContext* context);
-
-  // Creates a new ProfilePolicyConnector for |context|, which must be managed
-  // by the caller. Subsequent calls to GetForBrowserContext() will return the
-  // instance created, as long as it lives.
-  // If |force_immediate_load| is true then policy is loaded synchronously on
-  // startup.
-  static std::unique_ptr<ProfilePolicyConnector> CreateForBrowserContext(
-      content::BrowserContext* context,
-      bool force_immediate_load);
-
-  // Returns true if |context| is a managed profile. CHECK-fails if the profile
-  // does not have a ProfilePolicyConnector yet (i.e. if
-  // ProfilePolicyConnectorFactory::CreateForBrowserContext has not been called
-  // for it yet).
-  static bool IsProfileManaged(const content::BrowserContext* context);
-
-  // Overrides the |connector| for the given |context|; use only in tests.
-  // Once this class becomes a proper BCKS then it can reuse the testing
-  // methods of BrowserContextKeyedServiceFactory.
-  void SetServiceForTesting(content::BrowserContext* context,
-                            ProfilePolicyConnector* connector);
-
-  // The next Profile to call CreateForBrowserContext() will get a PolicyService
-  // with |provider| as its sole policy provider. This can be called multiple
-  // times to override the policy providers for more than 1 Profile.
-  void PushProviderForTesting(ConfigurationPolicyProvider* provider);
-
- private:
-  friend struct base::DefaultSingletonTraits<ProfilePolicyConnectorFactory>;
-
-  ProfilePolicyConnectorFactory();
-  ~ProfilePolicyConnectorFactory() override;
-
-  ProfilePolicyConnector* GetForBrowserContextInternal(
-      const content::BrowserContext* context) const;
-
-  std::unique_ptr<ProfilePolicyConnector> CreateForBrowserContextInternal(
-      content::BrowserContext* context,
-      bool force_immediate_load);
-
-  // BrowserContextKeyedBaseFactory:
-  void BrowserContextShutdown(content::BrowserContext* context) override;
-  void BrowserContextDestroyed(content::BrowserContext* context) override;
-  void SetEmptyTestingFactory(content::BrowserContext* context) override;
-  bool HasTestingFactory(content::BrowserContext* context) override;
-  void CreateServiceNow(content::BrowserContext* context) override;
-
-  typedef std::map<const content::BrowserContext*, ProfilePolicyConnector*>
-      ConnectorMap;
-  ConnectorMap connectors_;
-  std::list<ConfigurationPolicyProvider*> test_providers_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProfilePolicyConnectorFactory);
-};
-
-}  // namespace policy
-
-#endif  // CHROME_BROWSER_POLICY_PROFILE_POLICY_CONNECTOR_FACTORY_H_
diff --git a/chrome/browser/policy/profile_policy_connector_unittest.cc b/chrome/browser/policy/profile_policy_connector_unittest.cc
index 91f273f7..8a098f10 100644
--- a/chrome/browser/policy/profile_policy_connector_unittest.cc
+++ b/chrome/browser/policy/profile_policy_connector_unittest.cc
@@ -87,7 +87,8 @@
 TEST_F(ProfilePolicyConnectorTest, IsManagedForManagedUsers) {
   ProfilePolicyConnector connector;
   connector.Init(nullptr /* user */, &schema_registry_,
-                 cloud_policy_manager_.get(), &cloud_policy_store_, false);
+                 cloud_policy_manager_.get(), &cloud_policy_store_,
+                 g_browser_process->browser_policy_connector(), false);
   EXPECT_FALSE(connector.IsManaged());
 
   cloud_policy_store_.policy_.reset(new enterprise_management::PolicyData());
@@ -109,7 +110,8 @@
       AccountId::AdFromUserEmailObjGuid("user@realm.example", "obj-guid");
   std::unique_ptr<user_manager::User> user = CreateRegularUser(account_id);
   connector.Init(user.get(), &schema_registry_, cloud_policy_manager_.get(),
-                 &cloud_policy_store_, false);
+                 &cloud_policy_store_,
+                 g_browser_process->browser_policy_connector(), false);
   cloud_policy_store_.policy_.reset(new enterprise_management::PolicyData());
   cloud_policy_store_.policy_->set_state(
       enterprise_management::PolicyData::ACTIVE);
@@ -128,7 +130,8 @@
 TEST_F(ProfilePolicyConnectorTest, IsProfilePolicy) {
   ProfilePolicyConnector connector;
   connector.Init(nullptr /* user */, &schema_registry_,
-                 cloud_policy_manager_.get(), &cloud_policy_store_, false);
+                 cloud_policy_manager_.get(), &cloud_policy_store_,
+                 g_browser_process->browser_policy_connector(), false);
 
   // No policy is set initially.
   EXPECT_FALSE(
diff --git a/chrome/browser/prefs/chrome_pref_service_factory.cc b/chrome/browser/prefs/chrome_pref_service_factory.cc
index c848923..abd18c28 100644
--- a/chrome/browser/prefs/chrome_pref_service_factory.cc
+++ b/chrome/browser/prefs/chrome_pref_service_factory.cc
@@ -430,6 +430,7 @@
     SupervisedUserSettingsService* supervised_user_settings,
     scoped_refptr<PrefStore> extension_prefs,
     scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry,
+    policy::BrowserPolicyConnector* connector,
     bool async,
     scoped_refptr<base::SequencedTaskRunner> io_task_runner,
     std::unique_ptr<PrefValueStore::Delegate> delegate) {
@@ -449,8 +450,7 @@
               std::move(validation_delegate));
   PrepareFactory(&factory, profile_path, policy_service,
                  supervised_user_settings, std::move(user_pref_store),
-                 std::move(extension_prefs), async,
-                 g_browser_process->browser_policy_connector());
+                 std::move(extension_prefs), async, connector);
   return factory.CreateSyncable(std::move(pref_registry), std::move(delegate));
 }
 
diff --git a/chrome/browser/prefs/chrome_pref_service_factory.h b/chrome/browser/prefs/chrome_pref_service_factory.h
index 26741a40..b2cc3f7 100644
--- a/chrome/browser/prefs/chrome_pref_service_factory.h
+++ b/chrome/browser/prefs/chrome_pref_service_factory.h
@@ -80,6 +80,7 @@
     SupervisedUserSettingsService* supervised_user_settings,
     scoped_refptr<PrefStore> extension_prefs,
     scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry,
+    policy::BrowserPolicyConnector* connector,
     bool async,
     scoped_refptr<base::SequencedTaskRunner> io_task_runner,
     std::unique_ptr<PrefValueStore::Delegate> delegate);
diff --git a/chrome/browser/profile_resetter/triggered_profile_resetter_factory.h b/chrome/browser/profile_resetter/triggered_profile_resetter_factory.h
index d6f4208..84877e8 100644
--- a/chrome/browser/profile_resetter/triggered_profile_resetter_factory.h
+++ b/chrome/browser/profile_resetter/triggered_profile_resetter_factory.h
@@ -40,8 +40,6 @@
   // BrowserContextKeyedServiceFactory:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* context) const override;
-
-  // BrowserContextKeyedBaseFactory:
   void RegisterProfilePrefs(
       user_prefs::PrefRegistrySyncable* registry) override;
   bool ServiceIsCreatedWithBrowserContext() const override;
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index b416225..f618904 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -50,7 +50,6 @@
 #include "chrome/browser/plugins/plugin_prefs_factory.h"
 #include "chrome/browser/policy/cloud/policy_header_service_factory.h"
 #include "chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/predictors/autocomplete_action_predictor_factory.h"
 #include "chrome/browser/predictors/loading_predictor_factory.h"
 #include "chrome/browser/predictors/predictor_database_factory.h"
@@ -346,7 +345,6 @@
   PluginPrefsFactory::GetInstance();
 #endif
   PrefsTabHelper::GetServiceInstance();
-  policy::ProfilePolicyConnectorFactory::GetInstance();
 #if defined(OS_CHROMEOS)
   chromeos::OwnerSettingsServiceChromeOSFactory::GetInstance();
   policy::PolicyCertServiceFactory::GetInstance();
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc
index b92d752..ea38c1592 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.cc
+++ b/chrome/browser/profiles/off_the_record_profile_impl.cc
@@ -557,6 +557,16 @@
   return key_.get();
 }
 
+policy::ProfilePolicyConnector*
+OffTheRecordProfileImpl::GetProfilePolicyConnector() {
+  return profile_->GetProfilePolicyConnector();
+}
+
+const policy::ProfilePolicyConnector*
+OffTheRecordProfileImpl::GetProfilePolicyConnector() const {
+  return profile_->GetProfilePolicyConnector();
+}
+
 void OffTheRecordProfileImpl::SetExitType(ExitType exit_type) {
 }
 
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.h b/chrome/browser/profiles/off_the_record_profile_impl.h
index 0d00a16..183c07c 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.h
+++ b/chrome/browser/profiles/off_the_record_profile_impl.h
@@ -80,6 +80,9 @@
   bool IsSameProfile(Profile* profile) override;
   base::Time GetStartTime() const override;
   ProfileKey* GetProfileKey() const override;
+  policy::ProfilePolicyConnector* GetProfilePolicyConnector() override;
+  const policy::ProfilePolicyConnector* GetProfilePolicyConnector()
+      const override;
   base::FilePath last_selected_directory() override;
   void set_last_selected_directory(const base::FilePath& path) override;
   bool WasCreatedByVersionOrLater(const std::string& version) override;
diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h
index e77d8ab..450560d 100644
--- a/chrome/browser/profiles/profile.h
+++ b/chrome/browser/profiles/profile.h
@@ -39,6 +39,7 @@
 namespace policy {
 class SchemaRegistryService;
 class UserCloudPolicyManager;
+class ProfilePolicyConnector;
 }  // namespace policy
 
 namespace network {
@@ -237,6 +238,10 @@
   // Returns the UserCloudPolicyManager.
   virtual policy::UserCloudPolicyManager* GetUserCloudPolicyManager();
 
+  virtual policy::ProfilePolicyConnector* GetProfilePolicyConnector() = 0;
+  virtual const policy::ProfilePolicyConnector* GetProfilePolicyConnector()
+      const = 0;
+
   // Returns the last directory that was chosen for uploading or opening a file.
   virtual base::FilePath last_selected_directory() = 0;
   virtual void set_last_selected_directory(const base::FilePath& path) = 0;
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 86ffb0c6..732378e 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -62,7 +62,7 @@
 #include "chrome/browser/plugins/plugin_prefs.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/profile_policy_connector_builder.h"
 #include "chrome/browser/policy/schema_registry_service.h"
 #include "chrome/browser/policy/schema_registry_service_profile_builder.h"
 #include "chrome/browser/prefs/browser_prefs.h"
@@ -116,6 +116,7 @@
 #include "components/language/core/common/locale_util.h"
 #include "components/metrics/metrics_service.h"
 #include "components/omnibox/browser/autocomplete_classifier.h"
+#include "components/policy/core/common/cloud/cloud_policy_manager.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/signin/core/browser/signin_pref_names.h"
@@ -535,8 +536,10 @@
   configuration_policy_provider_ = std::move(user_cloud_policy_manager);
 #endif
   profile_policy_connector_ =
-      policy::ProfilePolicyConnectorFactory::CreateForBrowserContext(
-          this, force_immediate_policy_load);
+      policy::CreateProfilePolicyConnectorForBrowserContext(
+          schema_registry_service_->registry(), user_cloud_policy_manager_,
+          g_browser_process->browser_policy_connector(),
+          force_immediate_policy_load, this);
 
   DCHECK(create_mode == CREATE_MODE_ASYNCHRONOUS ||
          create_mode == CREATE_MODE_SYNCHRONOUS);
@@ -584,8 +587,8 @@
     prefs_ = chrome_prefs::CreateProfilePrefs(
         path_, std::move(pref_validation_delegate),
         profile_policy_connector_->policy_service(), supervised_user_settings,
-        CreateExtensionPrefStore(this, false), pref_registry_, async_prefs,
-        GetIOTaskRunner(), std::move(delegate));
+        CreateExtensionPrefStore(this, false), pref_registry_, connector,
+        async_prefs, GetIOTaskRunner(), std::move(delegate));
     // Register on BrowserContext.
     user_prefs::UserPrefs::Set(this, prefs_.get());
   }
@@ -829,6 +832,7 @@
   // not have the responsibility to call Shutdown() anymore, remove this
   // condition.
 #if !defined(OS_CHROMEOS)
+  profile_policy_connector_->Shutdown();
   configuration_policy_provider_->Shutdown();
 #endif
 
@@ -1120,6 +1124,15 @@
   return user_cloud_policy_manager_;
 }
 
+policy::ProfilePolicyConnector* ProfileImpl::GetProfilePolicyConnector() {
+  return profile_policy_connector_.get();
+}
+
+const policy::ProfilePolicyConnector* ProfileImpl::GetProfilePolicyConnector()
+    const {
+  return profile_policy_connector_.get();
+}
+
 content::ResourceContext* ProfileImpl::GetResourceContext() {
   return io_data_.GetResourceContext();
 }
diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h
index f7de7a9..1d24e86 100644
--- a/chrome/browser/profiles/profile_impl.h
+++ b/chrome/browser/profiles/profile_impl.h
@@ -143,6 +143,9 @@
   PrefService* GetReadOnlyOffTheRecordPrefs() override;
   policy::SchemaRegistryService* GetPolicySchemaRegistryService() override;
   policy::UserCloudPolicyManager* GetUserCloudPolicyManager() override;
+  policy::ProfilePolicyConnector* GetProfilePolicyConnector() override;
+  const policy::ProfilePolicyConnector* GetProfilePolicyConnector()
+      const override;
   net::URLRequestContextGetter* GetRequestContext() override;
   base::OnceCallback<net::CookieStore*()> GetExtensionsCookieStoreGetter()
       override;
diff --git a/chrome/browser/referrer_policy_browsertest.cc b/chrome/browser/referrer_policy_browsertest.cc
index 971a2fa..3c85ee7cc 100644
--- a/chrome/browser/referrer_policy_browsertest.cc
+++ b/chrome/browser/referrer_policy_browsertest.cc
@@ -24,7 +24,7 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/content_switches.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test_utils.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 
@@ -243,9 +243,13 @@
 class ReferrerPolicyWithReduceReferrerGranularityFlagTest
     : public ReferrerPolicyTest {
  public:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(switches::kReducedReferrerGranularity);
+  ReferrerPolicyWithReduceReferrerGranularityFlagTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kReducedReferrerGranularity);
   }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // The basic behavior of referrer policies is covered by layout tests in
diff --git a/chrome/browser/resources/discards/graph_doc.js b/chrome/browser/resources/discards/graph_doc.js
index ada3896d..1ff2ae5 100644
--- a/chrome/browser/resources/discards/graph_doc.js
+++ b/chrome/browser/resources/discards/graph_doc.js
@@ -21,7 +21,11 @@
 /** @implements {d3.ForceNode} */
 class GraphNode {
   constructor(id) {
+    /** @type {number} */
     this.id = id;
+    /** @type {string} */
+    this.color = 'black';
+
     // Implementation of the d3.ForceNode interface.
     // See https://github.com/d3/d3-force#simulation_nodes.
     this.index = null;
@@ -72,12 +76,22 @@
   linkTargets() {
     return [];
   }
+
+  /**
+   * Selects a color string from an id.
+   * @param {number} id: The id the returned color is selected from.
+   * @return {string}
+   */
+  selectColor(id) {
+    return d3.schemeSet3[Math.abs(id) % 12];
+  }
 }
 
 class PageNode extends GraphNode {
-  /** @param {resourceCoordinator.mojom.WebUIPageInfo} page */
+  /** @param {!resourceCoordinator.mojom.WebUIPageInfo} page */
   constructor(page) {
     super(page.id);
+    /** @type {!resourceCoordinator.mojom.WebUIPageInfo} */
     this.page = page;
     this.y = kPageNodesTargetY;
   }
@@ -114,10 +128,12 @@
 }
 
 class FrameNode extends GraphNode {
-  /** @param {resourceCoordinator.mojom.WebUIFrameInfo} frame */
+  /** @param {!resourceCoordinator.mojom.WebUIFrameInfo} frame */
   constructor(frame) {
     super(frame.id);
+    /** @type {!resourceCoordinator.mojom.WebUIFrameInfo} frame */
     this.frame = frame;
+    this.color = this.selectColor(frame.processId);
   }
 
   /** override */
@@ -145,8 +161,10 @@
   /** @param {!resourceCoordinator.mojom.WebUIProcessInfo} process */
   constructor(process) {
     super(process.id);
-    /** {!resourceCoordinator.mojom.WebUIProcessInfo} */
+    /** @type {!resourceCoordinator.mojom.WebUIProcessInfo} */
     this.process = process;
+
+    this.color = this.selectColor(process.id);
   }
 
   /** override */
@@ -281,21 +299,24 @@
 
       const circles = node.enter()
                           .append('circle')
-                          .attr('r', 7.5)
+                          .attr('r', 9)
                           .attr('fill', 'green')  // New nodes appear green.
                           .call(drag);
       circles.append('title');
 
-      // Transition new nodes to black over 2 seconds.
-      circles.transition().duration(2000).attr('fill', 'black').attr('r', 5);
+      // Transition new nodes to their chosen color in 2 seconds.
+      circles.transition()
+          .duration(2000)
+          .attr('fill', d => d.color)
+          .attr('r', 6);
     }
 
-    // Give dead notes a distinguishing class to exclude them from the selection
+    // Give dead nodes a distinguishing class to exclude them from the selection
     // above. Interrupt any ongoing transitions, then transition them out.
     node.exit()
         .classed('dead', true)
         .interrupt()
-        .attr('r', 7.5)
+        .attr('r', 9)
         .attr('fill', 'red')
         .transition()
         .duration(2000)
diff --git a/chrome/browser/resources/discards/graph_doc_template.html b/chrome/browser/resources/discards/graph_doc_template.html
index 7bce9769..dae29c0 100644
--- a/chrome/browser/resources/discards/graph_doc_template.html
+++ b/chrome/browser/resources/discards/graph_doc_template.html
@@ -24,7 +24,7 @@
       }
 
       .nodes circle {
-        stroke: #fff;
+        stroke: #000;
         stroke-width: 1.5px;
       }
     </style>
diff --git a/chrome/browser/serial/serial_chooser_context_factory.h b/chrome/browser/serial/serial_chooser_context_factory.h
index d19b033..ad2968a 100644
--- a/chrome/browser/serial/serial_chooser_context_factory.h
+++ b/chrome/browser/serial/serial_chooser_context_factory.h
@@ -23,7 +23,7 @@
   SerialChooserContextFactory();
   ~SerialChooserContextFactory() override;
 
-  // BrowserContextKeyedBaseFactory methods:
+  // BrowserContextKeyedServiceFactory methods:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
   content::BrowserContext* GetBrowserContextToUse(
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate_factory.h b/chrome/browser/ssl/chrome_ssl_host_state_delegate_factory.h
index fe7549e..8227f6e 100644
--- a/chrome/browser/ssl/chrome_ssl_host_state_delegate_factory.h
+++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate_factory.h
@@ -29,7 +29,7 @@
   ChromeSSLHostStateDelegateFactory();
   ~ChromeSSLHostStateDelegateFactory() override;
 
-  // BrowserContextKeyedBaseFactory methods:
+  // BrowserContextKeyedServiceFactory methods:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
   void RegisterProfilePrefs(
diff --git a/chrome/browser/ssl/ssl_error_controller_client.cc b/chrome/browser/ssl/ssl_error_controller_client.cc
index 8f1272b..b8f15976 100644
--- a/chrome/browser/ssl/ssl_error_controller_client.cc
+++ b/chrome/browser/ssl/ssl_error_controller_client.cc
@@ -214,8 +214,7 @@
       profile->GetSSLHostStateDelegate());
   // ChromeSSLHostStateDelegate can be null during tests.
   if (state) {
-    state->AllowCert(request_url_.host(), *ssl_info_.cert.get(),
-                     net::MapCertStatusToNetError(ssl_info_.cert_status));
+    state->AllowCert(request_url_.host(), *ssl_info_.cert.get(), cert_error_);
     Reload();
   }
 }
diff --git a/chrome/browser/ssl/ssl_error_navigation_throttle.cc b/chrome/browser/ssl/ssl_error_navigation_throttle.cc
index 1af4793..f0e6c98 100644
--- a/chrome/browser/ssl/ssl_error_navigation_throttle.cc
+++ b/chrome/browser/ssl/ssl_error_navigation_throttle.cc
@@ -56,8 +56,9 @@
   const net::SSLInfo info = handle->GetSSLInfo().value_or(net::SSLInfo());
   int cert_status = info.cert_status;
   QueueShowInterstitial(std::move(handle_ssl_error_callback_),
-                        handle->GetWebContents(), cert_status, info,
-                        handle->GetURL(), std::move(ssl_cert_reporter_));
+                        handle->GetWebContents(), handle->GetNetErrorCode(),
+                        cert_status, info, handle->GetURL(),
+                        std::move(ssl_cert_reporter_));
   return content::NavigationThrottle::ThrottleCheckResult(
       content::NavigationThrottle::DEFER);
 }
@@ -90,9 +91,14 @@
       chrome::FindBrowserWithWebContents(handle->GetWebContents());
   if (browser &&
       WebAppBrowserController::IsForExperimentalWebAppBrowser(browser)) {
-    QueueShowInterstitial(std::move(handle_ssl_error_callback_),
-                          handle->GetWebContents(), cert_status, info,
-                          handle->GetURL(), std::move(ssl_cert_reporter_));
+    QueueShowInterstitial(
+        std::move(handle_ssl_error_callback_), handle->GetWebContents(),
+        // The navigation handle's net error code will be
+        // net::OK, because the net stack has allowed the
+        // response to proceed. Synthesize a net error from
+        // the cert status instead.
+        net::MapCertStatusToNetError(cert_status), cert_status, info,
+        handle->GetURL(), std::move(ssl_cert_reporter_));
     return content::NavigationThrottle::ThrottleCheckResult(
         content::NavigationThrottle::DEFER);
   }
@@ -108,6 +114,7 @@
 void SSLErrorNavigationThrottle::QueueShowInterstitial(
     HandleSSLErrorCallback handle_ssl_error_callback,
     content::WebContents* web_contents,
+    int net_error,
     int cert_status,
     const net::SSLInfo& ssl_info,
     const GURL& request_url,
@@ -116,24 +123,21 @@
   // call ShowInterstitial asynchronously, giving the throttle time to defer the
   // navigation.
   std::move(handle_ssl_error_callback)
-      .Run(web_contents, net::MapCertStatusToNetError(cert_status), ssl_info,
-           request_url, false /* expired_previous_decision */,
-           std::move(ssl_cert_reporter),
+      .Run(web_contents, net_error, ssl_info, request_url,
+           false /* expired_previous_decision */, std::move(ssl_cert_reporter),
            base::Callback<void(content::CertificateRequestResultType)>(),
            base::BindOnce(&SSLErrorNavigationThrottle::ShowInterstitial,
-                          weak_ptr_factory_.GetWeakPtr()));
+                          weak_ptr_factory_.GetWeakPtr(), net_error));
 }
 
 void SSLErrorNavigationThrottle::ShowInterstitial(
+    int net_error,
     std::unique_ptr<security_interstitials::SecurityInterstitialPage>
         blocking_page) {
-  content::NavigationHandle* handle = navigation_handle();
-  int net_error =
-      net::MapCertStatusToNetError(handle->GetSSLInfo()->cert_status);
-
   // Get the error page content before giving up ownership of |blocking_page|.
   std::string error_page_content = blocking_page->GetHTMLContents();
 
+  content::NavigationHandle* handle = navigation_handle();
   security_interstitials::SecurityInterstitialTabHelper::AssociateBlockingPage(
       handle->GetWebContents(), handle->GetNavigationId(),
       std::move(blocking_page));
diff --git a/chrome/browser/ssl/ssl_error_navigation_throttle.h b/chrome/browser/ssl/ssl_error_navigation_throttle.h
index 3a0a57f..c7981543 100644
--- a/chrome/browser/ssl/ssl_error_navigation_throttle.h
+++ b/chrome/browser/ssl/ssl_error_navigation_throttle.h
@@ -58,11 +58,13 @@
   void QueueShowInterstitial(
       HandleSSLErrorCallback handle_ssl_error_callback,
       content::WebContents* web_contents,
+      int net_error,
       int cert_status,
       const net::SSLInfo& ssl_info,
       const GURL& request_url,
       std::unique_ptr<SSLCertReporter> ssl_cert_reporter);
   void ShowInterstitial(
+      int net_error,
       std::unique_ptr<security_interstitials::SecurityInterstitialPage>
           blocking_page);
 
diff --git a/chrome/browser/ssl/ssl_error_navigation_throttle_unittest.cc b/chrome/browser/ssl/ssl_error_navigation_throttle_unittest.cc
index c00f983f..fff7ed6 100644
--- a/chrome/browser/ssl/ssl_error_navigation_throttle_unittest.cc
+++ b/chrome/browser/ssl/ssl_error_navigation_throttle_unittest.cc
@@ -143,7 +143,7 @@
   ssl_info.cert =
       net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
   ssl_info.cert_status = net::CERT_STATUS_COMMON_NAME_INVALID;
-  handle_->set_net_error_code(net::ERR_CERT_INVALID);
+  handle_->set_net_error_code(net::ERR_CERT_COMMON_NAME_INVALID);
   handle_->set_ssl_info(ssl_info);
   content::NavigationThrottle::ThrottleCheckResult synchronous_result =
       throttle_->WillFailRequest();
diff --git a/chrome/browser/sync/test/integration/single_client_apps_sync_test.cc b/chrome/browser/sync/test/integration/single_client_apps_sync_test.cc
index 4b3d064..596ecf5 100644
--- a/chrome/browser/sync/test/integration/single_client_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_apps_sync_test.cc
@@ -4,11 +4,9 @@
 
 #include "base/macros.h"
 #include "chrome/browser/sync/test/integration/apps_helper.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/sync/driver/profile_sync_service.h"
-#include "components/sync/driver/sync_driver_switches.h"
 
 namespace {
 
@@ -16,10 +14,9 @@
 using apps_helper::InstallApp;
 using apps_helper::InstallPlatformApp;
 
-class SingleClientAppsSyncTest : public FeatureToggler, public SyncTest {
+class SingleClientAppsSyncTest : public SyncTest {
  public:
-  SingleClientAppsSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSApps), SyncTest(SINGLE_CLIENT) {}
+  SingleClientAppsSyncTest() : SyncTest(SINGLE_CLIENT) {}
 
   ~SingleClientAppsSyncTest() override {}
 
@@ -27,12 +24,12 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientAppsSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, StartWithNoApps) {
+IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, StartWithNoApps) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, StartWithSomeLegacyApps) {
+IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, StartWithSomeLegacyApps) {
   ASSERT_TRUE(SetupClients());
 
   const int kNumApps = 5;
@@ -45,7 +42,7 @@
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, StartWithSomePlatformApps) {
+IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, StartWithSomePlatformApps) {
   ASSERT_TRUE(SetupClients());
 
   const int kNumApps = 5;
@@ -58,7 +55,7 @@
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, InstallSomeLegacyApps) {
+IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, InstallSomeLegacyApps) {
   ASSERT_TRUE(SetupSync());
 
   const int kNumApps = 5;
@@ -71,7 +68,7 @@
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, InstallSomePlatformApps) {
+IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, InstallSomePlatformApps) {
   ASSERT_TRUE(SetupSync());
 
   const int kNumApps = 5;
@@ -84,7 +81,7 @@
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, InstallSomeApps) {
+IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, InstallSomeApps) {
   ASSERT_TRUE(SetupSync());
 
   int i = 0;
@@ -105,8 +102,4 @@
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         SingleClientAppsSyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_arc_package_sync_test.cc b/chrome/browser/sync/test/integration/single_client_arc_package_sync_test.cc
index fb2ee0a0..5fb85626 100644
--- a/chrome/browser/sync/test/integration/single_client_arc_package_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_arc_package_sync_test.cc
@@ -3,12 +3,10 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/sync_arc_package_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "chrome/browser/ui/app_list/arc/arc_package_syncable_service.h"
-#include "components/sync/driver/sync_driver_switches.h"
 
 namespace arc {
 
@@ -21,11 +19,9 @@
 
 }  // namespace
 
-class SingleClientArcPackageSyncTest : public FeatureToggler, public SyncTest {
+class SingleClientArcPackageSyncTest : public SyncTest {
  public:
-  SingleClientArcPackageSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSArcPackage),
-        SyncTest(SINGLE_CLIENT) {}
+  SingleClientArcPackageSyncTest() : SyncTest(SINGLE_CLIENT) {}
 
   ~SingleClientArcPackageSyncTest() override {}
 
@@ -33,13 +29,13 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientArcPackageSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(SingleClientArcPackageSyncTest, ArcPackageEmpty) {
+IN_PROC_BROWSER_TEST_F(SingleClientArcPackageSyncTest, ArcPackageEmpty) {
   ASSERT_TRUE(SetupSync());
 
   ASSERT_TRUE(AllProfilesHaveSameArcPackageDetails());
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientArcPackageSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientArcPackageSyncTest,
                        ArcPackageInstallSomePackages) {
   ASSERT_TRUE(SetupSync());
 
@@ -53,8 +49,4 @@
   ASSERT_TRUE(AllProfilesHaveSameArcPackageDetails());
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         SingleClientArcPackageSyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace arc
diff --git a/chrome/browser/sync/test/integration/single_client_dictionary_sync_test.cc b/chrome/browser/sync/test/integration/single_client_dictionary_sync_test.cc
index 089e2fb..2f38f6a 100644
--- a/chrome/browser/sync/test/integration/single_client_dictionary_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_dictionary_sync_test.cc
@@ -4,26 +4,23 @@
 
 #include "base/macros.h"
 #include "chrome/browser/sync/test/integration/dictionary_helper.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/sync/driver/profile_sync_service.h"
-#include "components/sync/driver/sync_driver_switches.h"
 
 namespace {
 
-class SingleClientDictionarySyncTest : public FeatureToggler, public SyncTest {
+class SingleClientDictionarySyncTest : public SyncTest {
  public:
-  SingleClientDictionarySyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSDictionary),
-        SyncTest(SINGLE_CLIENT) {}
+  SingleClientDictionarySyncTest() : SyncTest(SINGLE_CLIENT) {}
+
   ~SingleClientDictionarySyncTest() override {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SingleClientDictionarySyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(SingleClientDictionarySyncTest, Sanity) {
+IN_PROC_BROWSER_TEST_F(SingleClientDictionarySyncTest, Sanity) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
   ASSERT_TRUE(dictionary_helper::DictionariesMatch());
@@ -38,8 +35,4 @@
   ASSERT_TRUE(dictionary_helper::DictionariesMatch());
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         SingleClientDictionarySyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc
index d7b2389..8a012290 100644
--- a/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc
@@ -6,11 +6,9 @@
 #include "base/macros.h"
 #include "chrome/browser/sync/test/integration/await_match_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/extensions_helper.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/sync/driver/profile_sync_service.h"
-#include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/test/fake_server/fake_server.h"
 
 namespace {
@@ -21,11 +19,9 @@
 using extensions_helper::InstallExtension;
 using extensions_helper::InstallExtensionForAllProfiles;
 
-class SingleClientExtensionsSyncTest : public FeatureToggler, public SyncTest {
+class SingleClientExtensionsSyncTest : public SyncTest {
  public:
-  SingleClientExtensionsSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSExtensions),
-        SyncTest(SINGLE_CLIENT) {}
+  SingleClientExtensionsSyncTest() : SyncTest(SINGLE_CLIENT) {}
 
   ~SingleClientExtensionsSyncTest() override {}
 
@@ -33,12 +29,12 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientExtensionsSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(SingleClientExtensionsSyncTest, StartWithNoExtensions) {
+IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest, StartWithNoExtensions) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest,
                        StartWithSomeExtensions) {
   ASSERT_TRUE(SetupClients());
 
@@ -52,7 +48,7 @@
   ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientExtensionsSyncTest, InstallSomeExtensions) {
+IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest, InstallSomeExtensions) {
   ASSERT_TRUE(SetupSync());
 
   const int kNumExtensions = 5;
@@ -73,7 +69,7 @@
 
 // Tests the case of an uninstall from the server conflicting with a local
 // modification, which we expect to be resolved in favor of the uninstall.
-IN_PROC_BROWSER_TEST_P(SingleClientExtensionsSyncTest, UninstallWinsConflicts) {
+IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest, UninstallWinsConflicts) {
   ASSERT_TRUE(SetupClients());
 
   // Start with an extension installed, and setup sync.
@@ -110,8 +106,4 @@
   EXPECT_TRUE(GetInstalledExtensions(GetProfile(0)).empty());
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         SingleClientExtensionsSyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc b/chrome/browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc
index 277101e..91b102df 100644
--- a/chrome/browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc
@@ -10,13 +10,11 @@
 #include "base/test/bind_test_util.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/web_history_service_factory.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
-#include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/engine_impl/loopback_server/persistent_unique_client_entity.h"
 #include "components/sync/protocol/sync.pb.h"
 #include "components/sync/test/fake_server/fake_server.h"
@@ -69,12 +67,10 @@
   DISALLOW_COPY_AND_ASSIGN(HistoryDeleteDirectivesEqualityChecker);
 };
 
-class SingleClientHistoryDeleteDirectivesSyncTest : public FeatureToggler,
-                                                    public SyncTest {
+class SingleClientHistoryDeleteDirectivesSyncTest : public SyncTest {
  public:
-  SingleClientHistoryDeleteDirectivesSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSHistoryDeleteDirectives),
-        SyncTest(SINGLE_CLIENT) {}
+  SingleClientHistoryDeleteDirectivesSyncTest() : SyncTest(SINGLE_CLIENT) {}
+
   ~SingleClientHistoryDeleteDirectivesSyncTest() override {}
 
   bool WaitForHistoryDeleteDirectives(size_t num_expected_directives) {
@@ -109,7 +105,7 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientHistoryDeleteDirectivesSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(SingleClientHistoryDeleteDirectivesSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientHistoryDeleteDirectivesSyncTest,
                        ShouldCommitTimeRangeDeleteDirective) {
   const GURL kPageUrl = GURL("http://foo.com");
   const base::Time kHistoryEntryTime = base::Time::Now();
@@ -130,7 +126,7 @@
   EXPECT_TRUE(WaitForHistoryDeleteDirectives(1));
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientHistoryDeleteDirectivesSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientHistoryDeleteDirectivesSyncTest,
                        ShouldCommitUrlDeleteDirective) {
   const GURL kPageUrl = GURL("http://foo.com");
   const base::Time kHistoryEntryTime = base::Time::Now();
@@ -147,7 +143,7 @@
   EXPECT_TRUE(WaitForHistoryDeleteDirectives(1));
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientHistoryDeleteDirectivesSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientHistoryDeleteDirectivesSyncTest,
                        ShouldProcessDeleteDirectiveDuringStartup) {
   const GURL kPageUrl = GURL("http://foo.com");
   const base::Time kHistoryEntryTime = base::Time::Now();
@@ -190,8 +186,4 @@
   EXPECT_TRUE(WaitForHistoryDeleteDirectives(1));
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         SingleClientHistoryDeleteDirectivesSyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc b/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc
index aa6fd710..9271959 100644
--- a/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc
@@ -7,7 +7,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/preferences_helper.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
@@ -17,7 +16,6 @@
 #include "components/prefs/json_pref_store.h"
 #include "components/prefs/pref_service.h"
 #include "components/sync/driver/profile_sync_service.h"
-#include "components/sync/driver/sync_driver_switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using preferences_helper::BooleanPrefMatches;
@@ -30,18 +28,17 @@
 
 namespace {
 
-class SingleClientPreferencesSyncTest : public FeatureToggler, public SyncTest {
+class SingleClientPreferencesSyncTest : public SyncTest {
  public:
-  SingleClientPreferencesSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSPreferences),
-        SyncTest(SINGLE_CLIENT) {}
+  SingleClientPreferencesSyncTest() : SyncTest(SINGLE_CLIENT) {}
+
   ~SingleClientPreferencesSyncTest() override {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SingleClientPreferencesSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(SingleClientPreferencesSyncTest, Sanity) {
+IN_PROC_BROWSER_TEST_F(SingleClientPreferencesSyncTest, Sanity) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(BooleanPrefMatches(prefs::kHomePageIsNewTabPage));
   ChangeBooleanPref(0, prefs::kHomePageIsNewTabPage);
@@ -51,7 +48,7 @@
 
 // This test simply verifies that preferences registered after sync started
 // get properly synced.
-IN_PROC_BROWSER_TEST_P(SingleClientPreferencesSyncTest, LateRegistration) {
+IN_PROC_BROWSER_TEST_F(SingleClientPreferencesSyncTest, LateRegistration) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   PrefRegistrySyncable* registry = GetRegistry(GetProfile(0));
   const std::string pref_name = "testing.my-test-preference";
@@ -80,7 +77,7 @@
 #define MAYBE_ShouldRemoveBadDataWhenRegistering \
   ShouldRemoveBadDataWhenRegistering
 #endif
-IN_PROC_BROWSER_TEST_P(SingleClientPreferencesSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientPreferencesSyncTest,
                        MAYBE_ShouldRemoveBadDataWhenRegistering) {
   // Populate the data store with data of type boolean but register as string.
   SetPreexistingPreferencesFileContents(
@@ -109,7 +106,7 @@
 
 // Regression test to verify that pagination during GetUpdates() contributes
 // properly to UMA histograms.
-IN_PROC_BROWSER_TEST_P(SingleClientPreferencesSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientPreferencesSyncTest,
                        EmitModelTypeEntityChangeToUma) {
   const int kNumEntities = 17;
 
@@ -132,7 +129,7 @@
                               /*REMOTE_INITIAL_UPDATE=*/5));
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientPreferencesSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientPreferencesSyncTest,
                        PRE_PersistProgressMarkerOnRestart) {
   sync_pb::EntitySpecifics specifics;
   specifics.mutable_preference()->set_name("testing.my-test-preference");
@@ -150,7 +147,7 @@
                    /*REMOTE_INITIAL_UPDATE=*/5));
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientPreferencesSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientPreferencesSyncTest,
                        PersistProgressMarkerOnRestart) {
   sync_pb::EntitySpecifics specifics;
   specifics.mutable_preference()->set_name("testing.my-test-preference");
@@ -180,8 +177,4 @@
                    /*REMOTE_INITIAL_UPDATE=*/5));
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         SingleClientPreferencesSyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_search_engines_sync_test.cc b/chrome/browser/sync/test/integration/single_client_search_engines_sync_test.cc
index 6874afc5..c050718 100644
--- a/chrome/browser/sync/test/integration/single_client_search_engines_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_search_engines_sync_test.cc
@@ -3,34 +3,26 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/search_engines_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/sync/driver/profile_sync_service.h"
-#include "components/sync/driver/sync_driver_switches.h"
 
-class SingleClientSearchEnginesSyncTest : public FeatureToggler,
-                                          public SyncTest {
+class SingleClientSearchEnginesSyncTest : public SyncTest {
  public:
-  SingleClientSearchEnginesSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSSearchEngines),
-        SyncTest(SINGLE_CLIENT) {}
+  SingleClientSearchEnginesSyncTest() : SyncTest(SINGLE_CLIENT) {}
+
   ~SingleClientSearchEnginesSyncTest() override {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SingleClientSearchEnginesSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(SingleClientSearchEnginesSyncTest, Sanity) {
+IN_PROC_BROWSER_TEST_F(SingleClientSearchEnginesSyncTest, Sanity) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(search_engines_helper::ServiceMatchesVerifier(0));
   search_engines_helper::AddSearchEngine(0, 0);
   ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
   ASSERT_TRUE(search_engines_helper::ServiceMatchesVerifier(0));
 }
-
-INSTANTIATE_TEST_SUITE_P(USS,
-                         SingleClientSearchEnginesSyncTest,
-                         ::testing::Values(false, true));
diff --git a/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc b/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
index 07677be..f2f1034 100644
--- a/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
@@ -5,14 +5,12 @@
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/themes_helper.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "components/sync/driver/profile_sync_service.h"
-#include "components/sync/driver/sync_driver_switches.h"
 #include "content/public/test/test_utils.h"
 
 using themes_helper::GetCustomTheme;
@@ -25,11 +23,10 @@
 
 namespace {
 
-class SingleClientThemesSyncTest : public FeatureToggler, public SyncTest {
+class SingleClientThemesSyncTest : public SyncTest {
  public:
-  SingleClientThemesSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSThemes),
-        SyncTest(SINGLE_CLIENT) {}
+  SingleClientThemesSyncTest() : SyncTest(SINGLE_CLIENT) {}
+
   ~SingleClientThemesSyncTest() override {}
 
  private:
@@ -40,7 +37,7 @@
 // start with SetupClients(), change the theme state, then call
 // SetupSync()).
 
-IN_PROC_BROWSER_TEST_P(SingleClientThemesSyncTest, CustomTheme) {
+IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, CustomTheme) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
   EXPECT_FALSE(UsingCustomTheme(GetProfile(0)));
@@ -59,9 +56,9 @@
 
 // TODO(sync): Fails on Chrome OS. See http://crbug.com/84575.
 #if defined(OS_CHROMEOS)
-IN_PROC_BROWSER_TEST_P(SingleClientThemesSyncTest, DISABLED_NativeTheme) {
+IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, DISABLED_NativeTheme) {
 #else
-IN_PROC_BROWSER_TEST_P(SingleClientThemesSyncTest, NativeTheme) {
+IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, NativeTheme) {
 #endif  // OS_CHROMEOS
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
@@ -83,7 +80,7 @@
   EXPECT_TRUE(UsingSystemTheme(verifier()));
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientThemesSyncTest, DefaultTheme) {
+IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, DefaultTheme) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
   SetCustomTheme(GetProfile(0));
@@ -104,8 +101,4 @@
   EXPECT_TRUE(UsingDefaultTheme(verifier()));
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         SingleClientThemesSyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
index 6c9dbd73..002541b 100644
--- a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/test/integration/apps_helper.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_app_helper.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
@@ -26,7 +25,6 @@
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/extensions/manifest_handlers/app_theme_color_info.h"
 #include "chrome/common/web_application_info.h"
-#include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/model/string_ordinal.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
@@ -58,12 +56,9 @@
 
 }  // namespace
 
-class TwoClientAppsSyncTest : public FeatureToggler, public SyncTest {
+class TwoClientAppsSyncTest : public SyncTest {
  public:
-  TwoClientAppsSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSApps), SyncTest(TWO_CLIENT) {
-    DisableVerifier();
-  }
+  TwoClientAppsSyncTest() : SyncTest(TWO_CLIENT) { DisableVerifier(); }
 
   ~TwoClientAppsSyncTest() override {}
 
@@ -74,12 +69,12 @@
   DISALLOW_COPY_AND_ASSIGN(TwoClientAppsSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(StartWithNoApps)) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(StartWithNoApps)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(StartWithSameApps)) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(StartWithSameApps)) {
   ASSERT_TRUE(SetupClients());
 
   const int kNumApps = 5;
@@ -95,7 +90,7 @@
 // Install some apps on both clients, some on only one client, some on only the
 // other, and sync.  Both clients should end up with all apps, and the app and
 // page ordinals should be identical.
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, StartWithDifferentApps) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, StartWithDifferentApps) {
   ASSERT_TRUE(SetupClients());
 
   int i = 0;
@@ -128,7 +123,7 @@
 // Install some apps on both clients, then sync.  Then install some apps on only
 // one client, some on only the other, and then sync again.  Both clients should
 // end up with all apps, and the app and page ordinals should be identical.
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
                        E2E_ENABLED(InstallDifferentApps)) {
   ASSERT_TRUE(SetupClients());
 
@@ -155,7 +150,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(Add)) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(Add)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
@@ -164,7 +159,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(Uninstall)) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(Uninstall)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
@@ -179,7 +174,7 @@
 // client and sync again. Now install a new app on the first client and sync.
 // Both client should only have the second app, with identical app and page
 // ordinals.
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
                        E2E_ENABLED(UninstallThenInstall)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
@@ -194,7 +189,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(Merge)) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(Merge)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
@@ -212,7 +207,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
                        E2E_ENABLED(UpdateEnableDisableApp)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
@@ -227,7 +222,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
                        E2E_ENABLED(UpdateIncognitoEnableDisable)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
@@ -245,7 +240,7 @@
 // Install the same app on both clients, then sync. Change the page ordinal on
 // one client and sync. Both clients should have the updated page ordinal for
 // the app.
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(UpdatePageOrdinal)) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(UpdatePageOrdinal)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
@@ -262,7 +257,7 @@
 // Install the same app on both clients, then sync. Change the app launch
 // ordinal on one client and sync. Both clients should have the updated app
 // launch ordinal for the app.
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
                        E2E_ENABLED(UpdateAppLaunchOrdinal)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
@@ -281,7 +276,7 @@
 // Adjust the CWS location within a page on the first client and sync. Adjust
 // which page the CWS appears on and sync. Both clients should have the same
 // page and app launch ordinal values for the CWS.
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(UpdateCWSOrdinals)) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(UpdateCWSOrdinals)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
@@ -310,7 +305,7 @@
 
 // Adjust the launch type on the first client and sync. Both clients should
 // have the same launch type values for the CWS.
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(UpdateLaunchType)) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(UpdateLaunchType)) {
   ASSERT_TRUE(SetupSync());
   // Wait until sync settles before we override the apps below.
   ASSERT_TRUE(AwaitQuiescence());
@@ -338,7 +333,7 @@
       extensions::LAUNCH_TYPE_REGULAR);
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, UnexpectedLaunchType) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, UnexpectedLaunchType) {
   ASSERT_TRUE(SetupSync());
   // Wait until sync settles before we override the apps below.
   ASSERT_TRUE(AwaitQuiescence());
@@ -379,7 +374,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, BookmarkAppBasic) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkAppBasic) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 
@@ -416,7 +411,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, BookmarkAppMinimal) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkAppMinimal) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 
@@ -462,7 +457,7 @@
   return nullptr;
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, BookmarkAppThemeColor) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkAppThemeColor) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 
@@ -503,7 +498,7 @@
   EXPECT_EQ(SK_ColorBLUE, theme_color.value());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, IsLocallyInstalled) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, IsLocallyInstalled) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 
@@ -560,7 +555,3 @@
 // TODO(akalin): Add tests exercising:
 //   - Offline installation/uninstallation behavior
 //   - App-specific properties
-
-INSTANTIATE_TEST_SUITE_P(USS,
-                         TwoClientAppsSyncTest,
-                         ::testing::Values(false, true));
diff --git a/chrome/browser/sync/test/integration/two_client_arc_package_sync_test.cc b/chrome/browser/sync/test/integration/two_client_arc_package_sync_test.cc
index 6eacfb7..528be3d 100644
--- a/chrome/browser/sync/test/integration/two_client_arc_package_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_arc_package_sync_test.cc
@@ -3,11 +3,9 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/sync_arc_package_helper.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
-#include "components/sync/driver/sync_driver_switches.h"
 
 namespace arc {
 
@@ -20,13 +18,9 @@
 
 }  // namespace
 
-class TwoClientArcPackageSyncTest : public FeatureToggler, public SyncTest {
+class TwoClientArcPackageSyncTest : public SyncTest {
  public:
-  TwoClientArcPackageSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSArcPackage),
-        SyncTest(TWO_CLIENT) {
-    DisableVerifier();
-  }
+  TwoClientArcPackageSyncTest() : SyncTest(TWO_CLIENT) { DisableVerifier(); }
 
   ~TwoClientArcPackageSyncTest() override {}
 
@@ -34,13 +28,13 @@
   DISALLOW_COPY_AND_ASSIGN(TwoClientArcPackageSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(TwoClientArcPackageSyncTest, StartWithNoPackages) {
+IN_PROC_BROWSER_TEST_F(TwoClientArcPackageSyncTest, StartWithNoPackages) {
   ASSERT_TRUE(SetupSync());
 
   ASSERT_TRUE(AllProfilesHaveSameArcPackageDetails());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientArcPackageSyncTest, StartWithSamePackages) {
+IN_PROC_BROWSER_TEST_F(TwoClientArcPackageSyncTest, StartWithSamePackages) {
   ASSERT_TRUE(SetupClients());
 
   constexpr size_t kNumPackages = 5;
@@ -56,7 +50,7 @@
 
 // In this test, packages are installed before sync started. Client1 will have
 // package 0 to 4 installed while client2 has no package installed.
-IN_PROC_BROWSER_TEST_P(TwoClientArcPackageSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientArcPackageSyncTest,
                        OneClientHasPackagesAnotherHasNone) {
   ASSERT_TRUE(SetupClients());
 
@@ -74,7 +68,7 @@
 
 // In this test, packages are installed before sync started. Client1 will have
 // package 0 to 9 installed and client2 will have package 0 to 4 installed.
-IN_PROC_BROWSER_TEST_P(TwoClientArcPackageSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientArcPackageSyncTest,
                        OneClientHasPackagesAnotherHasSubSet) {
   ASSERT_TRUE(SetupClients());
 
@@ -97,7 +91,7 @@
 
 // In this test, packages are installed before sync started. Client1 will have
 // package 0 to 4 installed and client2 will have package 1 to 5 installed.
-IN_PROC_BROWSER_TEST_P(TwoClientArcPackageSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientArcPackageSyncTest,
                        StartWithDifferentPackages) {
   ASSERT_TRUE(SetupClients());
 
@@ -117,7 +111,7 @@
 }
 
 // Tests package installaton after sync started.
-IN_PROC_BROWSER_TEST_P(TwoClientArcPackageSyncTest, Install) {
+IN_PROC_BROWSER_TEST_F(TwoClientArcPackageSyncTest, Install) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameArcPackageDetails());
 
@@ -128,7 +122,7 @@
 
 // In this test, packages are installed after sync started. Client1 installs
 // package 0 to 4 and client2 installs package 3 to 7.
-IN_PROC_BROWSER_TEST_P(TwoClientArcPackageSyncTest, InstallDifferent) {
+IN_PROC_BROWSER_TEST_F(TwoClientArcPackageSyncTest, InstallDifferent) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameArcPackageDetails());
 
@@ -146,7 +140,7 @@
 
 // Installs package from one client and uninstalls from another after sync
 // started.
-IN_PROC_BROWSER_TEST_P(TwoClientArcPackageSyncTest, Uninstall) {
+IN_PROC_BROWSER_TEST_F(TwoClientArcPackageSyncTest, Uninstall) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameArcPackageDetails());
 
@@ -160,8 +154,4 @@
   EXPECT_TRUE(AllProfilesHaveSameArcPackageDetails());
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         TwoClientArcPackageSyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace arc
diff --git a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
index 534bfef..802229e 100644
--- a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
@@ -13,7 +13,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/profile_policy_connector_builder.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
 #include "chrome/browser/sync/test/integration/feature_toggler.h"
@@ -2097,8 +2097,7 @@
   // Make sure the first Profile has an overridden policy provider.
   EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
       .WillRepeatedly(testing::Return(true));
-  policy::ProfilePolicyConnectorFactory::GetInstance()->PushProviderForTesting(
-      &policy_provider_);
+  policy::PushProfilePolicyConnectorProviderForTesting(&policy_provider_);
 
   // Set up sync.
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
diff --git a/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc b/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc
index 7ecd580..bd52253 100644
--- a/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc
@@ -6,24 +6,21 @@
 #include "base/strings/string_number_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/sync/test/integration/dictionary_helper.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/spellcheck/common/spellcheck_common.h"
 #include "components/sync/base/model_type.h"
-#include "components/sync/driver/sync_driver_switches.h"
 
 namespace {
 
 using spellcheck::kMaxSyncableDictionaryWords;
 
-class TwoClientDictionarySyncTest : public FeatureToggler, public SyncTest {
+class TwoClientDictionarySyncTest : public SyncTest {
  public:
-  TwoClientDictionarySyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSDictionary),
-        SyncTest(TWO_CLIENT) {}
+  TwoClientDictionarySyncTest() : SyncTest(TWO_CLIENT) {}
+
   ~TwoClientDictionarySyncTest() override {}
 
   bool TestUsesSelfNotifications() override { return false; }
@@ -32,7 +29,7 @@
   DISALLOW_COPY_AND_ASSIGN(TwoClientDictionarySyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest, E2E_ENABLED(Sanity)) {
+IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest, E2E_ENABLED(Sanity)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
   ASSERT_TRUE(DictionaryMatchChecker().Wait());
@@ -61,7 +58,7 @@
   ASSERT_EQ(words.size(), dictionary_helper::GetDictionarySize(0));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest,
                        E2E_ENABLED(SimultaneousAdd)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
@@ -73,7 +70,7 @@
   ASSERT_EQ(1UL, dictionary_helper::GetDictionarySize(0));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest,
                        E2E_ENABLED(SimultaneousRemove)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
@@ -90,7 +87,7 @@
   ASSERT_EQ(0UL, dictionary_helper::GetDictionarySize(0));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest,
                        E2E_ENABLED(AddDifferentToEach)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
@@ -104,7 +101,7 @@
             static_cast<int>(dictionary_helper::GetDictionarySize(0)));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest,
                        E2E_ENABLED(RemoveOnAAddOnB)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
@@ -125,7 +122,7 @@
 
 // Tests the case where a client has more words added than the
 // kMaxSyncableDictionaryWords limit.
-IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest, Limit) {
+IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest, Limit) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
   ASSERT_TRUE(DictionaryMatchChecker().Wait());
@@ -170,8 +167,4 @@
       NumDictionaryEntriesChecker(0, kMaxSyncableDictionaryWords).Wait());
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         TwoClientDictionarySyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_extension_settings_and_app_settings_sync_test.cc b/chrome/browser/sync/test/integration/two_client_extension_settings_and_app_settings_sync_test.cc
index e816c09..0d13481 100644
--- a/chrome/browser/sync/test/integration/two_client_extension_settings_and_app_settings_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_extension_settings_and_app_settings_sync_test.cc
@@ -8,11 +8,9 @@
 #include "chrome/browser/sync/test/integration/apps_helper.h"
 #include "chrome/browser/sync/test/integration/extension_settings_helper.h"
 #include "chrome/browser/sync/test/integration/extensions_helper.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
-#include "components/sync/driver/sync_driver_switches.h"
 
 namespace {
 
@@ -60,12 +58,10 @@
   }
 }
 
-class TwoClientExtensionSettingsAndAppSettingsSyncTest : public FeatureToggler,
-                                                         public SyncTest {
+class TwoClientExtensionSettingsAndAppSettingsSyncTest : public SyncTest {
  public:
-  TwoClientExtensionSettingsAndAppSettingsSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSExtensionSettings),
-        SyncTest(TWO_CLIENT) {}
+  TwoClientExtensionSettingsAndAppSettingsSyncTest() : SyncTest(TWO_CLIENT) {}
+
   ~TwoClientExtensionSettingsAndAppSettingsSyncTest() override {}
 
  private:
@@ -184,7 +180,7 @@
   return testing::AssertionSuccess();
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionSettingsAndAppSettingsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionSettingsAndAppSettingsSyncTest,
                        ExtensionsStartWithSameSettings) {
   ASSERT_TRUE(SetupClients());
   ASSERT_PRED3(StartWithSameSettingsTest, InstallExtensionForAllProfiles(0),
@@ -192,14 +188,14 @@
                InstallExtensionForAllProfiles(2));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionSettingsAndAppSettingsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionSettingsAndAppSettingsSyncTest,
                        AppsStartWithSameSettings) {
   ASSERT_TRUE(SetupClients());
   ASSERT_PRED3(StartWithSameSettingsTest, InstallAppForAllProfiles(0),
                InstallAppForAllProfiles(1), InstallAppForAllProfiles(2));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionSettingsAndAppSettingsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionSettingsAndAppSettingsSyncTest,
                        ExtensionsStartWithDifferentSettings) {
   ASSERT_TRUE(SetupClients());
   ASSERT_PRED3(
@@ -207,15 +203,11 @@
       InstallExtensionForAllProfiles(1), InstallExtensionForAllProfiles(2));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionSettingsAndAppSettingsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionSettingsAndAppSettingsSyncTest,
                        AppsStartWithDifferentSettings) {
   ASSERT_TRUE(SetupClients());
   ASSERT_PRED3(StartWithDifferentSettingsTest, InstallAppForAllProfiles(0),
                InstallAppForAllProfiles(1), InstallAppForAllProfiles(2));
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         TwoClientExtensionSettingsAndAppSettingsSyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_extensions_sync_test.cc b/chrome/browser/sync/test/integration/two_client_extensions_sync_test.cc
index 158c30b..9cf8bad 100644
--- a/chrome/browser/sync/test/integration/two_client_extensions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_extensions_sync_test.cc
@@ -5,11 +5,9 @@
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "chrome/browser/sync/test/integration/extensions_helper.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
-#include "components/sync/driver/sync_driver_switches.h"
 
 namespace {
 
@@ -23,13 +21,9 @@
 using extensions_helper::InstallExtension;
 using extensions_helper::UninstallExtension;
 
-class TwoClientExtensionsSyncTest : public FeatureToggler, public SyncTest {
+class TwoClientExtensionsSyncTest : public SyncTest {
  public:
-  TwoClientExtensionsSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSExtensions),
-        SyncTest(TWO_CLIENT) {
-    DisableVerifier();
-  }
+  TwoClientExtensionsSyncTest() : SyncTest(TWO_CLIENT) { DisableVerifier(); }
 
   bool TestUsesSelfNotifications() override { return false; }
 
@@ -37,7 +31,7 @@
   DISALLOW_COPY_AND_ASSIGN(TwoClientExtensionsSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
                        E2E_ENABLED(StartWithNoExtensions)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(ExtensionsMatchChecker().Wait());
@@ -56,7 +50,7 @@
 #else
 #define MAYBE_StartWithSameExtensions StartWithSameExtensions
 #endif
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
                        E2E_ENABLED(MAYBE_StartWithSameExtensions)) {
   ASSERT_TRUE(SetupClients());
 
@@ -78,7 +72,7 @@
 #else
 #define MAYBE_StartWithDifferentExtensions StartWithDifferentExtensions
 #endif
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
                        MAYBE_E2E(MAYBE_StartWithDifferentExtensions)) {
   ASSERT_TRUE(SetupClients());
 
@@ -107,7 +101,7 @@
       static_cast<int>(GetInstalledExtensions(GetProfile(0)).size()));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
                        E2E_ENABLED(InstallDifferentExtensions)) {
   ASSERT_TRUE(SetupClients());
 
@@ -138,7 +132,7 @@
       static_cast<int>(GetInstalledExtensions(GetProfile(0)).size()));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest, MAYBE_E2E(Add)) {
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest, MAYBE_E2E(Add)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameExtensions());
 
@@ -148,7 +142,7 @@
   EXPECT_EQ(1u, GetInstalledExtensions(GetProfile(0)).size());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest, MAYBE_E2E(Uninstall)) {
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest, MAYBE_E2E(Uninstall)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameExtensions());
 
@@ -160,7 +154,7 @@
   EXPECT_TRUE(GetInstalledExtensions(GetProfile(0)).empty());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
                        MAYBE_E2E(UpdateEnableDisableExtension)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameExtensions());
@@ -179,7 +173,7 @@
   ASSERT_TRUE(ExtensionsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
                        E2E_ENABLED(UpdateIncognitoEnableDisable)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameExtensions());
@@ -200,7 +194,7 @@
 
 // Regression test for bug 104399: ensure that an extension installed prior to
 // setting up sync, when uninstalled, is also uninstalled from sync.
-IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
                        E2E_ENABLED(UninstallPreinstalledExtensions)) {
   ASSERT_TRUE(SetupClients());
   ASSERT_TRUE(AllProfilesHaveSameExtensions());
@@ -221,8 +215,4 @@
 // TODO(akalin): Add tests exercising:
 //   - Offline installation/uninstallation behavior
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         TwoClientExtensionsSyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_preferences_sync_test.cc b/chrome/browser/sync/test/integration/two_client_preferences_sync_test.cc
index 06042542..ebcb72b0 100644
--- a/chrome/browser/sync/test/integration/two_client_preferences_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_preferences_sync_test.cc
@@ -8,7 +8,6 @@
 #include "base/macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/preferences_helper.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
@@ -16,7 +15,6 @@
 #include "chrome/common/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
-#include "components/sync/driver/sync_driver_switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using preferences_helper::BooleanPrefMatches;
@@ -33,11 +31,10 @@
 
 namespace {
 
-class TwoClientPreferencesSyncTest : public FeatureToggler, public SyncTest {
+class TwoClientPreferencesSyncTest : public SyncTest {
  public:
-  TwoClientPreferencesSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSPreferences),
-        SyncTest(TWO_CLIENT) {}
+  TwoClientPreferencesSyncTest() : SyncTest(TWO_CLIENT) {}
+
   ~TwoClientPreferencesSyncTest() override {}
 
   // Needed for AwaitQuiescence().
@@ -47,7 +44,7 @@
   DISALLOW_COPY_AND_ASSIGN(TwoClientPreferencesSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTest, E2E_ENABLED(Sanity)) {
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, E2E_ENABLED(Sanity)) {
   DisableVerifier();
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // Wait until sync settles before we override the prefs below.
@@ -72,22 +69,18 @@
                    "Sync.ModelTypeEntityChange3.PREFERENCE",
                    /*REMOTE_NON_INITIAL_UPDATE=*/4));
 
-  // Metrics below are instrumented for the USS codepath only.
-  if (GetParam()) {
-    EXPECT_EQ(
-        1U,
-        histogram_tester
-            .GetAllSamples(
-                "Sync.NonReflectionUpdateFreshnessPossiblySkewed.PREFERENCE")
-            .size());
-    EXPECT_NE(0U, histogram_tester
-                      .GetAllSamples(
-                          "Sync.NonReflectionUpdateFreshnessPossiblySkewed")
-                      .size());
-  }
+  EXPECT_EQ(
+      1U, histogram_tester
+              .GetAllSamples(
+                  "Sync.NonReflectionUpdateFreshnessPossiblySkewed.PREFERENCE")
+              .size());
+  EXPECT_NE(
+      0U, histogram_tester
+              .GetAllSamples("Sync.NonReflectionUpdateFreshnessPossiblySkewed")
+              .size());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTest, E2E_ENABLED(BooleanPref)) {
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, E2E_ENABLED(BooleanPref)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(BooleanPrefMatchChecker(prefs::kHomePageIsNewTabPage).Wait());
 
@@ -95,7 +88,7 @@
   ASSERT_TRUE(BooleanPrefMatchChecker(prefs::kHomePageIsNewTabPage).Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
                        E2E_ENABLED(Bidirectional)) {
   ASSERT_TRUE(SetupSync());
 
@@ -112,7 +105,7 @@
             GetPrefs(0)->GetString(prefs::kHomePage));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
                        E2E_ENABLED(UnsyncableBooleanPref)) {
   ASSERT_TRUE(SetupSync());
   DisableVerifier();
@@ -131,7 +124,7 @@
   ASSERT_FALSE(BooleanPrefMatches(prefs::kDisableScreenshots));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTest, E2E_ENABLED(StringPref)) {
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, E2E_ENABLED(StringPref)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(StringPrefMatchChecker(prefs::kHomePage).Wait());
 
@@ -139,7 +132,7 @@
   ASSERT_TRUE(StringPrefMatchChecker(prefs::kHomePage).Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTest, E2E_ENABLED(ClearPref)) {
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, E2E_ENABLED(ClearPref)) {
   ASSERT_TRUE(SetupSync());
   ChangeStringPref(0, prefs::kHomePage, "http://news.google.com");
   ASSERT_TRUE(StringPrefMatchChecker(prefs::kHomePage).Wait());
@@ -149,7 +142,7 @@
   ASSERT_TRUE(ClearedPrefMatchChecker(prefs::kHomePage).Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
                        E2E_ENABLED(ComplexPrefs)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(IntegerPrefMatchChecker(prefs::kRestoreOnStartup).Wait());
@@ -167,7 +160,7 @@
   ASSERT_TRUE(ListPrefMatchChecker(prefs::kURLsToRestoreOnStartup).Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
                        E2E_ENABLED(SingleClientEnabledEncryptionBothChanged)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(BooleanPrefMatchChecker(prefs::kHomePageIsNewTabPage).Wait());
@@ -182,7 +175,7 @@
   ASSERT_TRUE(BooleanPrefMatchChecker(prefs::kHomePageIsNewTabPage).Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     TwoClientPreferencesSyncTest,
     E2E_ENABLED(BothClientsEnabledEncryptionAndChangedMultipleTimes)) {
   ASSERT_TRUE(SetupSync());
@@ -200,12 +193,10 @@
 
 // The following tests use lower-level mechanisms to wait for sync cycle
 // completions. Those only work reliably with self notifications turned on.
-class TwoClientPreferencesSyncTestWithSelfNotifications : public FeatureToggler,
-                                                          public SyncTest {
+class TwoClientPreferencesSyncTestWithSelfNotifications : public SyncTest {
  public:
-  TwoClientPreferencesSyncTestWithSelfNotifications()
-      : FeatureToggler(switches::kSyncPseudoUSSPreferences),
-        SyncTest(TWO_CLIENT) {}
+  TwoClientPreferencesSyncTestWithSelfNotifications() : SyncTest(TWO_CLIENT) {}
+
   ~TwoClientPreferencesSyncTestWithSelfNotifications() override {}
 
   void SetUp() override {
@@ -223,7 +214,7 @@
 };
 
 // Tests that late registered prefs are kept in sync with other clients.
-IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTestWithSelfNotifications,
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTestWithSelfNotifications,
                        E2E_ENABLED(LateRegisteredPrefsShouldSync)) {
   // client0 has the pref registered before sync and is modifying a pref before
   // that pref got registered with client1 (but after client1 started syncing).
@@ -262,7 +253,7 @@
   EXPECT_THAT(GetPrefs(0)->GetBoolean(pref_name), Eq(true));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTestWithSelfNotifications,
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTestWithSelfNotifications,
                        E2E_ENABLED(ShouldKeepLocalDataOnTypeMismatch)) {
   // Client 1 has type-conflicting data in it's pref file. Verify that incoming
   // values from sync of other type do not modify the local state.
@@ -298,7 +289,7 @@
 
 // Verifies that priority synced preferences and regular sycned preferences are
 // kept separate.
-IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTestWithSelfNotifications,
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTestWithSelfNotifications,
                        E2E_ENABLED(ShouldIsolatePriorityPreferences)) {
   // Register a pref as priority with client0 and regular synced with client1.
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
@@ -323,12 +314,4 @@
   EXPECT_THAT(GetPrefs(1)->GetString(pref_name), Eq("non-priority value"));
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         TwoClientPreferencesSyncTest,
-                         ::testing::Values(false, true));
-
-INSTANTIATE_TEST_SUITE_P(USS,
-                         TwoClientPreferencesSyncTestWithSelfNotifications,
-                         ::testing::Values(false, true));
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_search_engines_sync_test.cc b/chrome/browser/sync/test/integration/two_client_search_engines_sync_test.cc
index 4d9fd91..5d60f13 100644
--- a/chrome/browser/sync/test/integration/two_client_search_engines_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_search_engines_sync_test.cc
@@ -5,7 +5,6 @@
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/search_engines_helper.h"
 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
@@ -13,22 +12,20 @@
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
-#include "components/sync/driver/sync_driver_switches.h"
 
 using base::ASCIIToUTF16;
 
-class TwoClientSearchEnginesSyncTest : public FeatureToggler, public SyncTest {
+class TwoClientSearchEnginesSyncTest : public SyncTest {
  public:
-  TwoClientSearchEnginesSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSSearchEngines),
-        SyncTest(TWO_CLIENT) {}
+  TwoClientSearchEnginesSyncTest() : SyncTest(TWO_CLIENT) {}
+
   ~TwoClientSearchEnginesSyncTest() override {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TwoClientSearchEnginesSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest, E2E_ENABLED(Add)) {
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest, E2E_ENABLED(Add)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // TODO(crbug.com/953711): Ideally we could immediately assert
   // search_engines_helper::AllServicesMatch(), but that's not possible today
@@ -45,7 +42,7 @@
   ASSERT_TRUE(search_engines_helper::HasSearchEngine(1, search_engine_seed));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest, E2E_ENABLED(Delete)) {
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest, E2E_ENABLED(Delete)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // TODO(crbug.com/953711): Ideally we could immediately assert
   // search_engines_helper::AllServicesMatch(), but that's not possible today
@@ -67,7 +64,7 @@
   ASSERT_FALSE(search_engines_helper::HasSearchEngine(1, search_engine_seed));
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest,
                        E2E_ENABLED(AddMultiple)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // TODO(crbug.com/953711): Ideally we could immediately assert
@@ -82,7 +79,7 @@
   ASSERT_TRUE(SearchEnginesMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest, Duplicates) {
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest, Duplicates) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // TODO(crbug.com/953711): Ideally we could immediately assert
   // search_engines_helper::AllServicesMatch(), but that's not possible today
@@ -102,7 +99,7 @@
   ASSERT_TRUE(SearchEnginesMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest,
                        E2E_ENABLED(UpdateKeyword)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // TODO(crbug.com/953711): Ideally we could immediately assert
@@ -122,7 +119,7 @@
   ASSERT_TRUE(SearchEnginesMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest, E2E_ENABLED(UpdateUrl)) {
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest, E2E_ENABLED(UpdateUrl)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // TODO(crbug.com/953711): Ideally we could immediately assert
   // search_engines_helper::AllServicesMatch(), but that's not possible today
@@ -141,7 +138,7 @@
   ASSERT_TRUE(SearchEnginesMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest,
                        E2E_ENABLED(UpdateName)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // TODO(crbug.com/953711): Ideally we could immediately assert
@@ -160,7 +157,7 @@
   ASSERT_TRUE(SearchEnginesMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest, ConflictKeyword) {
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest, ConflictKeyword) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   DisableVerifier();
   // TODO(crbug.com/953711): Ideally we could immediately assert
@@ -183,7 +180,7 @@
   ASSERT_TRUE(search_engines_helper::AllServicesMatch());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest, MergeMultiple) {
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest, MergeMultiple) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   DisableVerifier();
   // TODO(crbug.com/953711): Ideally we could immediately assert
@@ -211,7 +208,7 @@
   ASSERT_TRUE(search_engines_helper::AllServicesMatch());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest, DisableSync) {
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest, DisableSync) {
   ASSERT_TRUE(SetupSync());
   // TODO(crbug.com/953711): Ideally we could immediately assert
   // search_engines_helper::AllServicesMatch(), but that's not possible today
@@ -229,7 +226,7 @@
   ASSERT_TRUE(search_engines_helper::AllServicesMatch());
 }
 
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest,
                        E2E_ENABLED(SyncDefault)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // TODO(crbug.com/953711): Ideally we could immediately assert
@@ -249,7 +246,7 @@
 
 // Ensure that we can change the search engine and immediately delete it
 // without putting the clients out of sync.
-IN_PROC_BROWSER_TEST_P(TwoClientSearchEnginesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest,
                        E2E_ENABLED(DeleteSyncedDefault)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // TODO(crbug.com/953711): Ideally we could immediately assert
@@ -269,7 +266,3 @@
   search_engines_helper::DeleteSearchEngineBySeed(0, 0);
   ASSERT_TRUE(SearchEnginesMatchChecker().Wait());
 }
-
-INSTANTIATE_TEST_SUITE_P(USS,
-                         TwoClientSearchEnginesSyncTest,
-                         ::testing::Values(false, true));
diff --git a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
index a38d771..6ea70c0 100644
--- a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
@@ -3,12 +3,10 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
-#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/themes_helper.h"
-#include "components/sync/driver/sync_driver_switches.h"
 
 namespace {
 
@@ -21,10 +19,10 @@
 using themes_helper::UsingDefaultTheme;
 using themes_helper::UsingSystemTheme;
 
-class TwoClientThemesSyncTest : public FeatureToggler, public SyncTest {
+class TwoClientThemesSyncTest : public SyncTest {
  public:
-  TwoClientThemesSyncTest()
-      : FeatureToggler(switches::kSyncPseudoUSSThemes), SyncTest(TWO_CLIENT) {}
+  TwoClientThemesSyncTest() : SyncTest(TWO_CLIENT) {}
+
   ~TwoClientThemesSyncTest() override {}
 
   // Needed for AwaitQuiescence().
@@ -37,7 +35,7 @@
 // Starts with default themes, then sets up sync and uses it to set all
 // profiles to use a custom theme.  Does not actually install any themes, but
 // instead verifies the custom theme is pending for install.
-IN_PROC_BROWSER_TEST_P(TwoClientThemesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest,
                        E2E_ENABLED(DefaultThenSyncCustom)) {
   ASSERT_TRUE(SetupSync());
   // Wait until sync settles before we override the theme below.
@@ -61,7 +59,7 @@
 
 // Starts with custom themes, then sets up sync and uses it to set all profiles
 // to the system theme.
-IN_PROC_BROWSER_TEST_P(TwoClientThemesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest,
                        E2E_ENABLED(CustomThenSyncNative)) {
   ASSERT_TRUE(SetupClients());
 
@@ -83,7 +81,7 @@
 
 // Starts with custom themes, then sets up sync and uses it to set all profiles
 // to the default theme.
-IN_PROC_BROWSER_TEST_P(TwoClientThemesSyncTest,
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest,
                        E2E_ENABLED(CustomThenSyncDefault)) {
   ASSERT_TRUE(SetupClients());
 
@@ -106,7 +104,7 @@
 //
 // Most other tests have significant coverage of model association.  This test
 // is intended to test steady-state scenarios.
-IN_PROC_BROWSER_TEST_P(TwoClientThemesSyncTest, E2E_ENABLED(CycleOptions)) {
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, E2E_ENABLED(CycleOptions)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // Wait until sync settles before we override the theme below.
   AwaitQuiescence();
@@ -135,8 +133,4 @@
   EXPECT_EQ(GetCustomTheme(1), GetThemeID(GetProfile(0)));
 }
 
-INSTANTIATE_TEST_SUITE_P(USS,
-                         TwoClientThemesSyncTest,
-                         ::testing::Values(false, true));
-
 }  // namespace
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 29b9d0c..c3adbd7 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
@@ -713,7 +712,7 @@
   // ArcPlayStoreAppTest:
   void OnBeforeArcTestSetup() override {
     policy::ProfilePolicyConnector* const connector =
-        policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile());
+        profile()->GetProfilePolicyConnector();
     connector->OverrideIsManagedForTesting(true);
     profile()->GetTestingPrefService()->SetManagedPref(
         arc::prefs::kArcEnabled,
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc
index 8b31555..c6afe4d5 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc
@@ -43,7 +43,7 @@
 
   base::test::ScopedTaskEnvironment scoped_task_environment_{
       base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT,
-      base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED};
+      base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::QUEUED};
   base::ScopedTempDir temp_dir_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_unittest.cc
index d4bf60de..4cab446 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_unittest.cc
@@ -69,7 +69,7 @@
   RecurrenceRankerConfigProto config_;
   base::test::ScopedTaskEnvironment scoped_task_environment_{
       base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT,
-      base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED};
+      base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::QUEUED};
   base::ScopedTempDir temp_dir_;
   base::test::ScopedFeatureList scoped_feature_list_;
 
diff --git a/chrome/browser/ui/ash/session_controller_client.cc b/chrome/browser/ui/ash/session_controller_client.cc
index fc63bb9f..732dc0f38 100644
--- a/chrome/browser/ui/ash/session_controller_client.cc
+++ b/chrome/browser/ui/ash/session_controller_client.cc
@@ -26,8 +26,6 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
-#include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profiles_state.h"
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index 71dc101..a208623e 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
 #include "chrome/browser/history/history_test_utils.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/profile_policy_connector_builder.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/blocked_content/list_item_position.h"
@@ -473,8 +473,7 @@
     policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
         &policy_provider_);
 #else
-    policy::ProfilePolicyConnectorFactory::GetInstance()
-        ->PushProviderForTesting(&policy_provider_);
+    policy::PushProfilePolicyConnectorProviderForTesting(&policy_provider_);
 #endif
   }
 
diff --git a/chrome/browser/ui/managed_ui.cc b/chrome/browser/ui/managed_ui.cc
index 39f9f15..3875524 100644
--- a/chrome/browser/ui/managed_ui.cc
+++ b/chrome/browser/ui/managed_ui.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 
@@ -37,8 +36,7 @@
 #endif
 
   // This profile may have policies configured.
-  auto* profile_connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+  auto* profile_connector = profile->GetProfilePolicyConnector();
   if (profile_connector->IsManaged())
     return true;
 
@@ -51,8 +49,7 @@
         chromeos::ProfileHelper::Get()->GetProfileByUser(primary_user);
     if (primary_profile) {
       auto* primary_profile_connector =
-          policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-              primary_profile);
+          primary_profile->GetProfilePolicyConnector();
       if (primary_profile_connector->IsManaged())
         return true;
     }
diff --git a/chrome/browser/ui/webui/managed_ui_handler.cc b/chrome/browser/ui/webui/managed_ui_handler.cc
index 538636e..234e7b6 100644
--- a/chrome/browser/ui/webui/managed_ui_handler.cc
+++ b/chrome/browser/ui/webui/managed_ui_handler.cc
@@ -11,7 +11,6 @@
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/managed_ui.h"
 #include "chrome/common/pref_names.h"
@@ -28,8 +27,7 @@
 namespace {
 
 policy::PolicyService* GetProfilePolicyService(Profile* profile) {
-  auto* profile_connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+  auto* profile_connector = profile->GetProfilePolicyConnector();
   return profile_connector->policy_service();
 }
 
diff --git a/chrome/browser/ui/webui/managed_ui_handler_unittest.cc b/chrome/browser/ui/webui/managed_ui_handler_unittest.cc
index 7f2201c..06fd62c 100644
--- a/chrome/browser/ui/webui/managed_ui_handler_unittest.cc
+++ b/chrome/browser/ui/webui/managed_ui_handler_unittest.cc
@@ -8,7 +8,6 @@
 #include "base/token.h"
 #include "base/values.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_profile.h"
@@ -59,8 +58,7 @@
     return &policy_provider_;
   }
   policy::ProfilePolicyConnector* profile_policy_connector() {
-    return policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-        profile());
+    return profile()->GetProfilePolicyConnector();
   }
 
   void InitializeHandler() {
diff --git a/chrome/browser/ui/webui/management_ui.cc b/chrome/browser/ui/webui/management_ui.cc
index 9b00b461..d3a0f6d 100644
--- a/chrome/browser/ui/webui/management_ui.cc
+++ b/chrome/browser/ui/webui/management_ui.cc
@@ -8,7 +8,7 @@
 
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/dark_mode_handler.h"
 #include "chrome/browser/ui/webui/localized_string.h"
@@ -134,7 +134,7 @@
       g_browser_process->platform_part()->browser_policy_connector_chromeos();
   const auto device_type = ui::GetChromeOSDeviceTypeResourceId();
   if (!connector->IsEnterpriseManaged() &&
-      !policy::ProfilePolicyConnectorFactory::IsProfileManaged(profile)) {
+      !profile->GetProfilePolicyConnector()->IsManaged()) {
     return l10n_util::GetStringFUTF16(IDS_MANAGEMENT_NOT_MANAGED_SUBTITLE,
                                       l10n_util::GetStringUTF16(device_type));
   }
@@ -155,7 +155,7 @@
 #else   // defined(OS_CHROMEOS)
   const auto management_domain = ManagementUIHandler::GetAccountDomain(profile);
   const auto managed =
-      policy::ProfilePolicyConnectorFactory::IsProfileManaged(profile) ||
+      profile->GetProfilePolicyConnector()->IsManaged() ||
       g_browser_process->browser_policy_connector()->HasMachineLevelPolicies();
   if (management_domain.empty()) {
     return l10n_util::GetStringUTF16(managed
diff --git a/chrome/browser/ui/webui/management_ui_browsertest.cc b/chrome/browser/ui/webui/management_ui_browsertest.cc
index efc934a..e921d9e 100644
--- a/chrome/browser/ui/webui/management_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/management_ui_browsertest.cc
@@ -6,7 +6,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/management_ui.h"
@@ -44,8 +43,7 @@
   policy::MockConfigurationPolicyProvider* provider() { return &provider_; }
 
   policy::ProfilePolicyConnector* profile_policy_connector() {
-    return policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-        browser()->profile());
+    return browser()->profile()->GetProfilePolicyConnector();
   }
 
  private:
diff --git a/chrome/browser/ui/webui/management_ui_handler.cc b/chrome/browser/ui/webui/management_ui_handler.cc
index d276f4c..900c76a 100644
--- a/chrome/browser/ui/webui/management_ui_handler.cc
+++ b/chrome/browser/ui/webui/management_ui_handler.cc
@@ -21,7 +21,7 @@
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/profiles/profile_util.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 
@@ -48,8 +48,6 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_util.h"
-#include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/common/extensions/permissions/chrome_permission_message_provider.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_namespace.h"
@@ -128,7 +126,7 @@
 namespace {
 
 bool IsProfileManaged(Profile* profile) {
-  return policy::ProfilePolicyConnectorFactory::IsProfileManaged(profile);
+  return profile->GetProfilePolicyConnector()->IsManaged();
 }
 
 #if defined(OS_CHROMEOS)
@@ -565,8 +563,8 @@
 }
 
 policy::PolicyService* ManagementUIHandler::GetPolicyService() const {
-  return policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-             Profile::FromWebUI(web_ui()))
+  return Profile::FromWebUI(web_ui())
+      ->GetProfilePolicyConnector()
       ->policy_service();
 }
 
@@ -891,10 +889,9 @@
   extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
       ->RemoveObserver(this);
 
-  policy::PolicyService* policy_service =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-          Profile::FromWebUI(web_ui()))
-          ->policy_service();
+  policy::PolicyService* policy_service = Profile::FromWebUI(web_ui())
+                                              ->GetProfilePolicyConnector()
+                                              ->policy_service();
   policy_service->RemoveObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
 
   pref_registrar_.RemoveAll();
diff --git a/chrome/browser/ui/webui/policy_ui_browsertest.cc b/chrome/browser/ui/webui/policy_ui_browsertest.cc
index 93cec93..5dcd5c9a4 100644
--- a/chrome/browser/ui/webui/policy_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/policy_ui_browsertest.cc
@@ -19,7 +19,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/profile_policy_connector_builder.h"
 #include "chrome/browser/policy/schema_registry_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -242,8 +242,7 @@
   EXPECT_CALL(provider_, IsInitializationComplete(_))
       .WillRepeatedly(Return(true));
   policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
-  policy::ProfilePolicyConnectorFactory::GetInstance()->PushProviderForTesting(
-      &provider_);
+  policy::PushProfilePolicyConnectorProviderForTesting(&provider_);
 
   // Create a directory for testing exporting policies.
   ASSERT_TRUE(export_policies_test_dir.CreateUniqueTempDir());
diff --git a/chrome/browser/ui/webui/policy_ui_handler.cc b/chrome/browser/ui/webui/policy_ui_handler.cc
index aa706ef..97bf5224 100644
--- a/chrome/browser/ui/webui/policy_ui_handler.cc
+++ b/chrome/browser/ui/webui/policy_ui_handler.cc
@@ -29,7 +29,6 @@
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/policy_conversions.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/policy/schema_registry_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
@@ -976,6 +975,7 @@
 }
 
 policy::PolicyService* PolicyUIHandler::GetPolicyService() const {
-  return policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-             web_ui()->GetWebContents()->GetBrowserContext())->policy_service();
+  Profile* profile = Profile::FromBrowserContext(
+      web_ui()->GetWebContents()->GetBrowserContext());
+  return profile->GetProfilePolicyConnector()->policy_service();
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
index adf3b539..1e723ef8 100644
--- a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
@@ -12,7 +12,7 @@
 #include "base/macros.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/chromeos/account_manager_welcome_dialog.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
@@ -192,8 +192,7 @@
     const Profile* const profile = Profile::FromWebUI(web_ui());
     if (profile->IsChild()) {
       device_account.SetString("organization", kFamilyLink);
-    } else if (policy::ProfilePolicyConnectorFactory::IsProfileManaged(
-                   profile)) {
+    } else if (profile->GetProfilePolicyConnector()->IsManaged()) {
       device_account.SetString(
           "organization",
           GetEnterpriseDomainFromUsername(
diff --git a/chrome/browser/ui/webui/welcome/nux_helper.cc b/chrome/browser/ui/webui/welcome/nux_helper.cc
index 757a1bf7..24d3020 100644
--- a/chrome/browser/ui/webui/welcome/nux_helper.cc
+++ b/chrome/browser/ui/webui/welcome/nux_helper.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/policy/browser_signin_policy_handler.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/ntp_features.h"
 #include "chrome/browser/search/search.h"
@@ -169,7 +168,7 @@
 
 const policy::PolicyMap& GetPoliciesFromProfile(Profile* profile) {
   policy::ProfilePolicyConnector* profile_connector =
-      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+      profile->GetProfilePolicyConnector();
   DCHECK(profile_connector);
   return profile_connector->policy_service()->GetPolicies(
       policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string()));
diff --git a/chrome/browser/usb/usb_chooser_context_factory.h b/chrome/browser/usb/usb_chooser_context_factory.h
index d25940b4..d492858 100644
--- a/chrome/browser/usb/usb_chooser_context_factory.h
+++ b/chrome/browser/usb/usb_chooser_context_factory.h
@@ -23,7 +23,7 @@
   UsbChooserContextFactory();
   ~UsbChooserContextFactory() override;
 
-  // BrowserContextKeyedBaseFactory methods:
+  // BrowserContextKeyedServiceFactory methods:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
   content::BrowserContext* GetBrowserContextToUse(
diff --git a/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc b/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc
index 04889c4f..35920850 100644
--- a/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc
@@ -29,7 +29,6 @@
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "components/user_manager/scoped_user_manager.h"
 #endif
 
@@ -326,8 +325,7 @@
 
 TEST_F(ScanDirForExternalWebAppsTest, ManagedUser) {
   const auto profile = CreateProfileAndLogin();
-  policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile.get())
-      ->OverrideIsManagedForTesting(true);
+  profile->GetProfilePolicyConnector()->OverrideIsManagedForTesting(true);
   VerifySetOfApps(profile.get(), {GURL(kAppAllUrl), GURL(kAppManagedUrl)});
 }
 
diff --git a/chrome/browser/web_data_service_factory.h b/chrome/browser/web_data_service_factory.h
index c13c076fc..6a5575b 100644
--- a/chrome/browser/web_data_service_factory.h
+++ b/chrome/browser/web_data_service_factory.h
@@ -72,7 +72,7 @@
   WebDataServiceFactory();
   ~WebDataServiceFactory() override;
 
-  // |BrowserContextKeyedBaseFactory| methods:
+  // |BrowserContextKeyedServiceFactory| methods:
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
   KeyedService* BuildServiceInstanceFor(
diff --git a/chrome/common/heap_profiler_controller_unittest.cc b/chrome/common/heap_profiler_controller_unittest.cc
index 86cb9e3..5eddab8 100644
--- a/chrome/common/heap_profiler_controller_unittest.cc
+++ b/chrome/common/heap_profiler_controller_unittest.cc
@@ -46,10 +46,13 @@
     int metadata_count_index =
         static_cast<int>(metadata_count_iterator - metadata_hashes.begin());
 
+    // The first sample is guaranteed to have metadata. It will be removed in
+    // subsequent samples if its value is the same as in the first sample.
+    EXPECT_EQ(1, profile.call_stack_profile().stack_sample(0).metadata_size());
+
     bool found = false;
     for (const metrics::CallStackProfile::StackSample& sample :
          profile.call_stack_profile().stack_sample()) {
-      EXPECT_LT(0, sample.metadata_size());
       for (const metrics::CallStackProfile::MetadataItem& item :
            sample.metadata()) {
         if (item.name_hash_index() == metadata_count_index &&
diff --git a/chrome/renderer/url_loader_throttle_provider_impl.cc b/chrome/renderer/url_loader_throttle_provider_impl.cc
index 6605405..a549b152 100644
--- a/chrome/renderer/url_loader_throttle_provider_impl.cc
+++ b/chrome/renderer/url_loader_throttle_provider_impl.cc
@@ -125,14 +125,9 @@
   }
 
   if (data_reduction_proxy::params::IsEnabledWithNetworkService()) {
-    data_reduction_proxy::mojom::DataReductionProxyPtr drp;
     content::RenderThread::Get()->GetConnector()->BindInterface(
-        content::mojom::kBrowserServiceName, mojo::MakeRequest(&drp));
-
-    data_reduction_proxy_manager_ = std::make_unique<
-        data_reduction_proxy::DataReductionProxyThrottleManager>(
-        std::move(drp),
-        data_reduction_proxy::mojom::DataReductionProxyThrottleConfigPtr());
+        content::mojom::kBrowserServiceName,
+        mojo::MakeRequest(&data_reduction_proxy_info_));
   }
 }
 
@@ -147,9 +142,9 @@
   DETACH_FROM_THREAD(thread_checker_);
   if (other.safe_browsing_)
     other.safe_browsing_->Clone(mojo::MakeRequest(&safe_browsing_info_));
-  if (other.data_reduction_proxy_manager_) {
-    data_reduction_proxy_manager_ =
-        other.data_reduction_proxy_manager_->Clone();
+  if (other.data_reduction_proxy_) {
+    other.data_reduction_proxy_->Clone(
+        mojo::MakeRequest(&data_reduction_proxy_info_));
   }
   // An ad_delay_factory_ is created, rather than cloning the existing one.
 }
@@ -159,6 +154,8 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (safe_browsing_info_)
     safe_browsing_.Bind(std::move(safe_browsing_info_));
+  if (data_reduction_proxy_info_)
+    data_reduction_proxy_.Bind(std::move(data_reduction_proxy_info_));
   return base::WrapUnique(new URLLoaderThrottleProviderImpl(*this));
 }
 
@@ -180,7 +177,15 @@
   DCHECK(!is_frame_resource ||
          type_ == content::URLLoaderThrottleProviderType::kFrame);
 
-  if (data_reduction_proxy_manager_) {
+  if (data_reduction_proxy::params::IsEnabledWithNetworkService()) {
+    if (data_reduction_proxy_info_)
+      data_reduction_proxy_.Bind(std::move(data_reduction_proxy_info_));
+    if (!data_reduction_proxy_manager_) {
+      data_reduction_proxy_manager_ = std::make_unique<
+          data_reduction_proxy::DataReductionProxyThrottleManager>(
+          data_reduction_proxy_.get(),
+          data_reduction_proxy::mojom::DataReductionProxyThrottleConfigPtr());
+    }
     throttles.push_back(
         std::make_unique<
             data_reduction_proxy::DataReductionProxyURLLoaderThrottle>(
diff --git a/chrome/renderer/url_loader_throttle_provider_impl.h b/chrome/renderer/url_loader_throttle_provider_impl.h
index f58e461..874e357cd 100644
--- a/chrome/renderer/url_loader_throttle_provider_impl.h
+++ b/chrome/renderer/url_loader_throttle_provider_impl.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/threading/thread_checker.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy.mojom.h"
 #include "components/safe_browsing/common/safe_browsing.mojom.h"
 #include "content/public/renderer/url_loader_throttle_provider.h"
 #include "extensions/buildflags/buildflags.h"
@@ -53,6 +54,9 @@
   safe_browsing::mojom::SafeBrowsingPtrInfo safe_browsing_info_;
   safe_browsing::mojom::SafeBrowsingPtr safe_browsing_;
 
+  data_reduction_proxy::mojom::DataReductionProxyPtrInfo
+      data_reduction_proxy_info_;
+  data_reduction_proxy::mojom::DataReductionProxyPtr data_reduction_proxy_;
   std::unique_ptr<data_reduction_proxy::DataReductionProxyThrottleManager>
       data_reduction_proxy_manager_;
 
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 0fb170a8..ad60e62 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -38,7 +38,6 @@
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/web_history_service_factory.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/policy/schema_registry_service.h"
 #include "chrome/browser/policy/schema_registry_service_profile_builder.h"
 #include "chrome/browser/prefs/browser_prefs.h"
@@ -563,6 +562,9 @@
 
   SimpleKeyMap::GetInstance()->Dissociate(this);
 
+  if (profile_policy_connector_)
+    profile_policy_connector_->Shutdown();
+
   if (user_cloud_policy_manager_)
     user_cloud_policy_manager_->Shutdown();
 
@@ -841,10 +843,6 @@
   }
   profile_policy_connector_.reset(new policy::ProfilePolicyConnector());
   profile_policy_connector_->InitForTesting(std::move(policy_service_));
-  policy::ProfilePolicyConnectorFactory::GetInstance()->SetServiceForTesting(
-      this, profile_policy_connector_.get());
-  CHECK_EQ(profile_policy_connector_.get(),
-           policy::ProfilePolicyConnectorFactory::GetForBrowserContext(this));
 }
 
 PrefService* TestingProfile::GetPrefs() {
@@ -939,6 +937,15 @@
   return user_cloud_policy_manager_.get();
 }
 
+policy::ProfilePolicyConnector* TestingProfile::GetProfilePolicyConnector() {
+  return profile_policy_connector_.get();
+}
+
+const policy::ProfilePolicyConnector*
+TestingProfile::GetProfilePolicyConnector() const {
+  return profile_policy_connector_.get();
+}
+
 base::FilePath TestingProfile::last_selected_directory() {
   return last_selected_directory_;
 }
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 049cd54..a10711d 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -342,6 +342,9 @@
   ProfileKey* GetProfileKey() const override;
   policy::SchemaRegistryService* GetPolicySchemaRegistryService() override;
   policy::UserCloudPolicyManager* GetUserCloudPolicyManager() override;
+  policy::ProfilePolicyConnector* GetProfilePolicyConnector() override;
+  const policy::ProfilePolicyConnector* GetProfilePolicyConnector()
+      const override;
   base::FilePath last_selected_directory() override;
   void set_last_selected_directory(const base::FilePath& path) override;
   bool WasCreatedByVersionOrLater(const std::string& version) override;
@@ -415,8 +418,7 @@
   // |original_profile_|.
   void CreateIncognitoPrefService();
 
-  // Creates a ProfilePolicyConnector that the ProfilePolicyConnectorFactory
-  // maps to this profile.
+  // Creates a ProfilePolicyConnector.
   void CreateProfilePolicyConnector();
 
   std::unique_ptr<net::CookieStore, content::BrowserThread::DeleteOnIOThread>
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 1ebd9679..6edb675a 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -3067,6 +3067,18 @@
     ]
   },
 
+  "StickyKeysEnabled": {
+    "os": ["chromeos"],
+    "test_policy": { "StickyKeysEnabled": true },
+    "pref_mappings": [
+      { "pref": "settings.a11y.sticky_keys_enabled",
+        "indicator_tests": [
+          { "policy": { "StickyKeysEnabled": true } }
+        ]
+      }
+    ]
+  },
+
   "SpokenFeedbackEnabled": {
     "os": ["chromeos"],
     "test_policy": { "SpokenFeedbackEnabled": true },
diff --git a/chromeos/components/drivefs/drivefs_auth.h b/chromeos/components/drivefs/drivefs_auth.h
index 8248c3a..49d2c58 100644
--- a/chromeos/components/drivefs/drivefs_auth.h
+++ b/chromeos/components/drivefs/drivefs_auth.h
@@ -39,6 +39,7 @@
     virtual service_manager::Connector* GetConnector() = 0;
     virtual const AccountId& GetAccountId() = 0;
     virtual std::string GetObfuscatedAccountId() = 0;
+    virtual bool IsMetricsCollectionEnabled() = 0;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(Delegate);
@@ -57,6 +58,10 @@
     return delegate_->GetObfuscatedAccountId();
   }
 
+  bool IsMetricsCollectionEnabled() {
+    return delegate_->IsMetricsCollectionEnabled();
+  }
+
   base::Optional<std::string> GetCachedAccessToken();
 
   virtual void GetAccessToken(
diff --git a/chromeos/components/drivefs/drivefs_auth_unittest.cc b/chromeos/components/drivefs/drivefs_auth_unittest.cc
index 3956e9c5..52265ec 100644
--- a/chromeos/components/drivefs/drivefs_auth_unittest.cc
+++ b/chromeos/components/drivefs/drivefs_auth_unittest.cc
@@ -49,6 +49,8 @@
     return "salt-" + account_id_.GetAccountIdKey();
   }
 
+  bool IsMetricsCollectionEnabled() override { return false; }
+
   const std::unique_ptr<service_manager::Connector> connector_;
   const AccountId account_id_;
 
diff --git a/chromeos/components/drivefs/drivefs_host.cc b/chromeos/components/drivefs/drivefs_host.cc
index 5c9eec11..729e85ab 100644
--- a/chromeos/components/drivefs/drivefs_host.cc
+++ b/chromeos/components/drivefs/drivefs_host.cc
@@ -72,7 +72,7 @@
     auto access_token = auth_delegate->GetCachedAccessToken();
     mojom::DriveFsConfigurationPtr config = {
         base::in_place, auth_delegate->GetAccountId().GetUserEmail(),
-        std::move(access_token)};
+        std::move(access_token), auth_delegate->IsMetricsCollectionEnabled()};
     return DriveFsConnection::Create(delegate->CreateMojoListener(),
                                      std::move(config));
   }
diff --git a/chromeos/components/drivefs/drivefs_host_unittest.cc b/chromeos/components/drivefs/drivefs_host_unittest.cc
index ca9046cf..b356920 100644
--- a/chromeos/components/drivefs/drivefs_host_unittest.cc
+++ b/chromeos/components/drivefs/drivefs_host_unittest.cc
@@ -133,6 +133,7 @@
   std::string GetObfuscatedAccountId() override {
     return "salt-" + account_id_.GetAccountIdKey();
   }
+  bool IsMetricsCollectionEnabled() override { return false; }
 
   std::unique_ptr<DriveFsBootstrapListener> CreateMojoListener() override {
     DCHECK(pending_bootstrap_);
diff --git a/chromeos/components/drivefs/mojom/drivefs.mojom b/chromeos/components/drivefs/mojom/drivefs.mojom
index 063f71a..9152279 100644
--- a/chromeos/components/drivefs/mojom/drivefs.mojom
+++ b/chromeos/components/drivefs/mojom/drivefs.mojom
@@ -100,12 +100,15 @@
   OnHeartbeat();
 };
 
-// Next MinVersion: 2
+// Next MinVersion: 3
 struct DriveFsConfiguration {
   string user_email;
 
   [MinVersion=1]
   string? access_token;
+
+  [MinVersion=2]
+  bool enable_metrics = false;
 };
 
 enum AccessTokenStatus {
diff --git a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h
index 3c17cd3..e4104516 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h
+++ b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h
@@ -97,18 +97,6 @@
 
   ~CryptAuthV2EnrollmentManagerImpl() override;
 
-  // CryptAuthEnrollmentManager:
-  void Start() override;
-  void ForceEnrollmentNow(
-      cryptauth::InvocationReason invocation_reason) override;
-  bool IsEnrollmentValid() const override;
-  base::Time GetLastEnrollmentTime() const override;
-  base::TimeDelta GetTimeToNextAttempt() const override;
-  bool IsEnrollmentInProgress() const override;
-  bool IsRecoveringFromFailure() const override;
-  std::string GetUserPublicKey() const override;
-  std::string GetUserPrivateKey() const override;
-
  protected:
   CryptAuthV2EnrollmentManagerImpl(
       ClientAppMetadataProvider* client_app_metadata_provider,
@@ -133,6 +121,18 @@
   static base::Optional<CryptAuthEnrollmentResult::ResultCode>
   ResultCodeErrorFromState(State state);
 
+  // CryptAuthEnrollmentManager:
+  void Start() override;
+  void ForceEnrollmentNow(
+      cryptauth::InvocationReason invocation_reason) override;
+  bool IsEnrollmentValid() const override;
+  base::Time GetLastEnrollmentTime() const override;
+  base::TimeDelta GetTimeToNextAttempt() const override;
+  bool IsEnrollmentInProgress() const override;
+  bool IsRecoveringFromFailure() const override;
+  std::string GetUserPublicKey() const override;
+  std::string GetUserPrivateKey() const override;
+
   // CryptAuthEnrollmentScheduler::Delegate:
   void OnEnrollmentRequested(const base::Optional<cryptauthv2::PolicyReference>&
                                  client_directive_policy_reference) override;
diff --git a/components/autofill/core/browser/autofill_profile.cc b/components/autofill/core/browser/autofill_profile.cc
index 504acbfa..1775a75 100644
--- a/components/autofill/core/browser/autofill_profile.cc
+++ b/components/autofill/core/browser/autofill_profile.cc
@@ -492,47 +492,64 @@
                                  const std::string& app_locale) const {
   ServerFieldTypeSet types;
   GetSupportedTypes(&types);
-  return IsSubsetOfForFieldSet(profile, app_locale, types);
+  return IsSubsetOfForFieldSet(AutofillProfileComparator(app_locale), profile,
+                               app_locale, types);
 }
 
 bool AutofillProfile::IsSubsetOfForFieldSet(
+    const AutofillProfileComparator& comparator,
     const AutofillProfile& profile,
     const std::string& app_locale,
     const ServerFieldTypeSet& types) const {
-  AutofillProfileComparator comparator(app_locale);
-
   for (ServerFieldType type : types) {
-    base::string16 value = GetRawInfo(type);
-    if (value.empty())
-      continue;
+    // Prefer GetInfo over GetRawInfo so that a reasonable value is retrieved
+    // when the raw data is empty or unnormalized. For example, suppose a
+    // profile's first and last names are set but its full name is not set.
+    // GetInfo for the NAME_FULL type returns the constituent name parts;
+    // however, GetRawInfo returns an empty string.
+    const base::string16 value = GetInfo(type, app_locale);
 
-    if (type == NAME_FULL || type == ADDRESS_HOME_STREET_ADDRESS) {
-      // Ignore the compound "full name" field type.  We are only interested in
-      // comparing the constituent parts.  For example, if |this| has a middle
-      // name saved, but |profile| lacks one, |profile| could still be a subset
-      // of |this|.  Likewise, ignore the compound "street address" type, as we
-      // are only interested in matching line-by-line.
+    if (value.empty() || type == ADDRESS_HOME_STREET_ADDRESS ||
+        type == ADDRESS_HOME_LINE1 || type == ADDRESS_HOME_LINE2 ||
+        type == ADDRESS_HOME_LINE3) {
+      // Ignore street addresses because comparing addresses such as 200 Elm St
+      // and 200 Elm Street could cause |profile| to not be seen as a subset of
+      // |this|. If the form includes a street address, then it is likely it
+      // contains another address field, e.g. a city or postal code, and
+      // comparing these other address parts is more reliable.
       continue;
+    } else if (type == NAME_FULL) {
+      if (!comparator.IsNameVariantOf(
+              comparator.NormalizeForComparison(
+                  profile.GetInfo(NAME_FULL, app_locale)),
+              comparator.NormalizeForComparison(value))) {
+        // Check whether the full name of |this| can be derived from the full
+        // name of |profile| if the form contains a full name field.
+        //
+        // Suppose the full name of |this| is Mia Park and |profile|'s full name
+        // is Mia L Park. Mia Park can be derived from Mia L Park, so |this|
+        // could be a subset of |profile|.
+        //
+        // If the form contains fields for a name's constiuent parts, e.g.
+        // NAME_FIRST, then these values are compared according to the
+        // conditions that follow.
+        return false;
+      }
     } else if (AutofillType(type).group() == PHONE_HOME) {
-      // Phone numbers should be canonicalized prior to being compared.
+      // Phone numbers should be canonicalized before comparing.
       if (type != PHONE_HOME_WHOLE_NUMBER) {
         continue;
       } else if (!i18n::PhoneNumbersMatch(
-                     value, profile.GetRawInfo(type),
+                     value, profile.GetInfo(type, app_locale),
                      base::UTF16ToASCII(GetRawInfo(ADDRESS_HOME_COUNTRY)),
                      app_locale)) {
         return false;
       }
-    } else {
-      const base::string16 this_value =
-          comparator.NormalizeForComparison(value);
-      const base::string16 that_value =
-          comparator.NormalizeForComparison(profile.GetRawInfo(type));
-      if (this_value != that_value)
-        return false;
+    } else if (!comparator.MatchesAfterNormalization(
+                   value, profile.GetInfo(type, app_locale))) {
+      return false;
     }
   }
-
   return true;
 }
 
diff --git a/components/autofill/core/browser/autofill_profile.h b/components/autofill/core/browser/autofill_profile.h
index 4fec90b..5dad1f4 100644
--- a/components/autofill/core/browser/autofill_profile.h
+++ b/components/autofill/core/browser/autofill_profile.h
@@ -26,6 +26,8 @@
 
 namespace autofill {
 
+class AutofillProfileComparator;
+
 struct AutofillMetadata;
 
 // A collection of FormGroups stored in a profile.  AutofillProfile also
@@ -122,13 +124,13 @@
   bool operator==(const AutofillProfile& profile) const;
   virtual bool operator!=(const AutofillProfile& profile) const;
 
-  // Returns true if the data in this AutofillProfile is a subset of the data in
-  // |profile|.
+  // Returns true if this AutofillProfile's data is a subset of |profile|'s.
   bool IsSubsetOf(const AutofillProfile& profile,
                   const std::string& app_locale) const;
 
-  // Like IsSubsetOf, but only considers the types present in |types|.
-  bool IsSubsetOfForFieldSet(const AutofillProfile& profile,
+  // Like IsSubsetOf, but considers only the given |types|.
+  bool IsSubsetOfForFieldSet(const AutofillProfileComparator& comparator,
+                             const AutofillProfile& profile,
                              const std::string& app_locale,
                              const ServerFieldTypeSet& types) const;
 
diff --git a/components/autofill/core/browser/autofill_profile_comparator.cc b/components/autofill/core/browser/autofill_profile_comparator.cc
index 328ea05..3c360fb 100644
--- a/components/autofill/core/browser/autofill_profile_comparator.cc
+++ b/components/autofill/core/browser/autofill_profile_comparator.cc
@@ -131,6 +131,17 @@
   return base::i18n::UnicodeStringToString16(value);
 }
 
+bool AutofillProfileComparator::MatchesAfterNormalization(
+    base::StringPiece16 text1,
+    base::StringPiece16 text2) const {
+  return NormalizeForComparison(
+             text1,
+             AutofillProfileComparator::WhitespaceSpec::DISCARD_WHITESPACE) ==
+         NormalizeForComparison(
+             text2,
+             AutofillProfileComparator::WhitespaceSpec::DISCARD_WHITESPACE);
+}
+
 bool AutofillProfileComparator::AreMergeable(const AutofillProfile& p1,
                                              const AutofillProfile& p2) const {
   // Sorted in order to relative expense of the tests to fail early and cheaply
diff --git a/components/autofill/core/browser/autofill_profile_comparator.h b/components/autofill/core/browser/autofill_profile_comparator.h
index c21b87b..a2be020 100644
--- a/components/autofill/core/browser/autofill_profile_comparator.h
+++ b/components/autofill/core/browser/autofill_profile_comparator.h
@@ -39,6 +39,12 @@
       base::StringPiece16 text,
       WhitespaceSpec whitespace_spec = RETAIN_WHITESPACE) const;
 
+  // Returns true if |text1| matches |text2| when the texts are normalized and
+  // white space and punctuation are removed. For example, if |text1| is H3B 2Y5
+  // and |text2| is H3B2Y5, then returns true.
+  bool MatchesAfterNormalization(base::StringPiece16 text1,
+                                 base::StringPiece16 text2) const;
+
   // Returns true if |p1| and |p2| are viable merge candidates. This means that
   // their names, addresses, email addreses, company names, and phone numbers
   // are all pairwise equivalent or mergeable.
diff --git a/components/autofill/core/browser/autofill_profile_comparator_unittest.cc b/components/autofill/core/browser/autofill_profile_comparator_unittest.cc
index 0fac040..1ef920c 100644
--- a/components/autofill/core/browser/autofill_profile_comparator_unittest.cc
+++ b/components/autofill/core/browser/autofill_profile_comparator_unittest.cc
@@ -346,6 +346,15 @@
       AutofillProfileComparator::DISCARD_WHITESPACE));
 }
 
+TEST_F(AutofillProfileComparatorTest, MatchesAfterNormalization) {
+  EXPECT_TRUE(comparator_.MatchesAfterNormalization(
+      base::ASCIIToUTF16("H3B 2Y5"), base::ASCIIToUTF16("H3B2Y5")));
+  EXPECT_TRUE(comparator_.MatchesAfterNormalization(
+      base::UTF8ToUTF16("Jean-François"), base::UTF8ToUTF16("Jean François")));
+  EXPECT_TRUE(comparator_.MatchesAfterNormalization(
+      base::ASCIIToUTF16("(234) 567-8900"), base::ASCIIToUTF16("2345678900")));
+}
+
 TEST_F(AutofillProfileComparatorTest, GetNamePartVariants) {
   std::set<base::string16> expected_variants = {
       UTF8ToUTF16("timothe noel"),
diff --git a/components/autofill/core/browser/autofill_profile_unittest.cc b/components/autofill/core/browser/autofill_profile_unittest.cc
index 539a255..52456b0 100644
--- a/components/autofill/core/browser/autofill_profile_unittest.cc
+++ b/components/autofill/core/browser/autofill_profile_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/autofill_metadata.h"
+#include "components/autofill/core/browser/autofill_profile_comparator.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/field_types.h"
@@ -672,37 +673,207 @@
   EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., Apt. 42"), labels[0]);
 }
 
-TEST(AutofillProfileTest, IsSubsetOf) {
-  std::unique_ptr<AutofillProfile> a, b;
+TEST(AutofillProfileTest, IsSubsetOfForProfiles) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Genevieve", "", "Fox",
+                       "genevieve@hotmail.com", "", "274 Main St", "",
+                       "Northhampton", "MA", "01060", "US", "");
 
-  // |a| is a subset of |b|.
-  a.reset(new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin));
-  b.reset(new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin));
-  test::SetProfileInfo(a.get(), "Thomas", nullptr, "Jefferson",
-                       "declaration_guy@gmail.com", nullptr, nullptr, nullptr,
-                       nullptr, nullptr, nullptr, nullptr, nullptr);
-  test::SetProfileInfo(b.get(), "Thomas", nullptr, "Jefferson",
-                       "declaration_guy@gmail.com", "United States Government",
-                       "Monticello", nullptr, "Charlottesville", "Virginia",
-                       "22902", nullptr, nullptr);
-  EXPECT_TRUE(a->IsSubsetOf(*b, "en-US"));
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Genevieve", "", "Fox",
+                       "genevieve@hotmail.com", "", "", "", "", "", "", "US",
+                       "");
 
-  // |b| is not a subset of |a|.
-  EXPECT_FALSE(b->IsSubsetOf(*a, "en-US"));
+  AutofillProfile profile3 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile3, "Genevieve", "", "Fuller",
+                       "genevieve@hotmail.com", "", "", "", "", "", "", "US",
+                       "");
 
-  // |a| is a subset of |a|.
-  EXPECT_TRUE(a->IsSubsetOf(*a, "en-US"));
+  EXPECT_FALSE(profile1.IsSubsetOf(profile2, "en-US"));
+  EXPECT_TRUE(profile2.IsSubsetOf(profile1, "en-US"));
+  EXPECT_FALSE(profile2.IsSubsetOf(profile3, "en-US"));
+  EXPECT_FALSE(profile3.IsSubsetOf(profile2, "en-US"));
+}
 
-  // One field in |b| is different.
-  a.reset(new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin));
-  b.reset(new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin));
-  test::SetProfileInfo(a.get(), "Thomas", nullptr, "Jefferson",
-                       "declaration_guy@gmail.com", nullptr, nullptr, nullptr,
-                       nullptr, nullptr, nullptr, nullptr, nullptr);
-  test::SetProfileInfo(a.get(), "Thomas", nullptr, "Adams",
-                       "declaration_guy@gmail.com", nullptr, nullptr, nullptr,
-                       nullptr, nullptr, nullptr, nullptr, nullptr);
-  EXPECT_FALSE(a->IsSubsetOf(*b, "en-US"));
+TEST(AutofillProfileTest, IsSubsetOfForFieldSet_DifferentMiddleNames) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Genevieve", "", "Fox", "", "", "", "", "",
+                       "", "", "US", "");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Genevieve", "M", "Fox", "", "", "", "", "",
+                       "", "", "US", "");
+
+  AutofillProfile profile3 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile3, "Genevieve", "Marie", "Fox", "", "", "", "",
+                       "", "", "", "US", "");
+
+  const AutofillProfileComparator comparator("en-US");
+
+  // When a form has a NAME_FULL field rather than a NAME_MIDDLE field, consider
+  // whether one profile's full name can be derived from the other's.
+  EXPECT_TRUE(profile1.IsSubsetOfForFieldSet(comparator, profile2, "en-US",
+                                             {NAME_FULL}));
+  EXPECT_FALSE(profile2.IsSubsetOfForFieldSet(comparator, profile1, "en-US",
+                                              {NAME_FULL}));
+  EXPECT_TRUE(profile1.IsSubsetOfForFieldSet(comparator, profile3, "en-US",
+                                             {NAME_FULL}));
+  EXPECT_FALSE(profile3.IsSubsetOfForFieldSet(comparator, profile1, "en-US",
+                                              {NAME_FULL}));
+  // True because Genevieve M Fox can be derived from Genevieve Marie Fox.
+  EXPECT_TRUE(profile2.IsSubsetOfForFieldSet(comparator, profile3, "en-US",
+                                             {NAME_FULL}));
+  EXPECT_FALSE(profile3.IsSubsetOfForFieldSet(comparator, profile2, "en-US",
+                                              {NAME_FULL}));
+
+  // When a form has a NAME_MIDDLE field rather than a NAME_FULL field, consider
+  // a name's constituent parts.
+  EXPECT_TRUE(profile1.IsSubsetOfForFieldSet(comparator, profile2, "en-US",
+                                             {NAME_MIDDLE}));
+  EXPECT_FALSE(profile2.IsSubsetOfForFieldSet(comparator, profile1, "en-US",
+                                              {NAME_MIDDLE}));
+  EXPECT_TRUE(profile1.IsSubsetOfForFieldSet(comparator, profile3, "en-US",
+                                             {NAME_MIDDLE}));
+  EXPECT_FALSE(profile3.IsSubsetOfForFieldSet(comparator, profile1, "en-US",
+                                              {NAME_MIDDLE}));
+  // False because the middle name M doesn't equal the middle name Marie.
+  EXPECT_FALSE(profile2.IsSubsetOfForFieldSet(comparator, profile3, "en-US",
+                                              {NAME_MIDDLE}));
+  EXPECT_FALSE(profile3.IsSubsetOfForFieldSet(comparator, profile2, "en-US",
+                                              {NAME_MIDDLE}));
+}
+
+TEST(AutofillProfileTest, IsSubsetOfForFieldSet_DifferentFirstNames) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Cynthia", "", "Fox", "", "", "", "", "", "",
+                       "", "US", "");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Genevieve", "", "Fox", "", "", "", "", "",
+                       "", "", "US", "");
+
+  const AutofillProfileComparator comparator("en-US");
+
+  EXPECT_FALSE(profile1.IsSubsetOfForFieldSet(comparator, profile2, "en-US",
+                                              {NAME_FULL}));
+  EXPECT_FALSE(profile2.IsSubsetOfForFieldSet(comparator, profile1, "en-US",
+                                              {NAME_FULL}));
+  EXPECT_FALSE(profile1.IsSubsetOfForFieldSet(comparator, profile2, "en-US",
+                                              {NAME_FIRST}));
+  EXPECT_FALSE(profile2.IsSubsetOfForFieldSet(comparator, profile1, "en-US",
+                                              {NAME_FIRST}));
+}
+
+TEST(AutofillProfileTest, IsSubsetOfForFieldSet_DifferentLastNames) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Genevieve", "", "Fuller", "", "", "", "", "",
+                       "", "", "US", "");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Genevieve", "", "Fox", "", "", "", "", "",
+                       "", "", "US", "");
+
+  const AutofillProfileComparator comparator("en-US");
+
+  EXPECT_FALSE(profile1.IsSubsetOfForFieldSet(comparator, profile2, "en-US",
+                                              {NAME_FULL}));
+  EXPECT_FALSE(profile2.IsSubsetOfForFieldSet(comparator, profile1, "en-US",
+                                              {NAME_FULL}));
+  EXPECT_FALSE(profile1.IsSubsetOfForFieldSet(comparator, profile2, "en-US",
+                                              {NAME_LAST}));
+  EXPECT_FALSE(profile2.IsSubsetOfForFieldSet(comparator, profile1, "en-US",
+                                              {NAME_LAST}));
+}
+
+TEST(AutofillProfileTest,
+     IsSubsetOfForFieldSet_DifferentStreetAddressesIgnored) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Genevieve", "", "Fox", "", "", "274 Main St",
+                       "", "", "", "", "US", "");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Genevieve", "", "Fox", "", "",
+                       "274 Main Street", "", "", "", "", "US", "");
+
+  const AutofillProfileComparator comparator("en-US");
+
+  EXPECT_TRUE(profile1.IsSubsetOfForFieldSet(
+      comparator, profile2, "en-US", {NAME_FULL, ADDRESS_HOME_STREET_ADDRESS}));
+  EXPECT_TRUE(profile2.IsSubsetOfForFieldSet(
+      comparator, profile1, "en-US", {NAME_FULL, ADDRESS_HOME_STREET_ADDRESS}));
+}
+
+TEST(AutofillProfileTest, IsSubsetOfForFieldSet_DifferentNonStreetAddresses) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Genevieve", "", "Fox", "", "", "274 Main St",
+                       "", "Northhampton", "", "", "US", "");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Genevieve", "", "Fox", "", "", "274 Main St",
+                       "", "Sturbridge", "", "", "US", "");
+
+  const AutofillProfileComparator comparator("en-US");
+
+  EXPECT_FALSE(profile1.IsSubsetOfForFieldSet(
+      comparator, profile2, "en-US",
+      {NAME_FULL, ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_CITY}));
+  EXPECT_FALSE(profile2.IsSubsetOfForFieldSet(
+      comparator, profile1, "en-US",
+      {NAME_FULL, ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_CITY}));
+}
+
+TEST(AutofillProfileTest,
+     IsSubsetOfForFieldSet_PostalCodesWithAndWithoutSpaces) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Genevieve", "", "Fox", "", "", "", "", "",
+                       "", "H3B 2Y5", "CA", "");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Genevieve", "", "Fox", "", "", "", "", "",
+                       "", "H3B2Y5", "CA", "");
+
+  const AutofillProfileComparator comparator("en-US");
+
+  EXPECT_TRUE(profile1.IsSubsetOfForFieldSet(comparator, profile2, "en-CA",
+                                             {NAME_FULL, ADDRESS_HOME_ZIP}));
+  EXPECT_TRUE(profile2.IsSubsetOfForFieldSet(comparator, profile1, "en-CA",
+                                             {NAME_FULL, ADDRESS_HOME_ZIP}));
+}
+
+TEST(AutofillProfileTest,
+     IsSubsetOfForFieldSet_PhoneNumbersWithAndWithoutSpacesAndPunctuation) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Genevieve", "", "Fox", "", "", "", "", "",
+                       "", "", "CA", "+1 (514) 444-5454");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Genevieve", "", "Fox", "", "", "", "", "",
+                       "", "", "CA", "15144445454");
+
+  const AutofillProfileComparator comparator("en-US");
+
+  EXPECT_TRUE(profile1.IsSubsetOfForFieldSet(
+      comparator, profile2, "en-CA", {NAME_FULL, PHONE_HOME_WHOLE_NUMBER}));
+  EXPECT_TRUE(profile2.IsSubsetOfForFieldSet(
+      comparator, profile1, "en-CA", {NAME_FULL, PHONE_HOME_WHOLE_NUMBER}));
 }
 
 TEST(AutofillProfileTest, SetRawInfo_UpdateValidityFlag) {
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 615bf70..21c247d 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -1132,7 +1132,7 @@
   if (IsInAutofillSuggestionsDisabledExperiment())
     return std::vector<Suggestion>();
 
-  AutofillProfileComparator comparator(app_locale_);
+  const AutofillProfileComparator comparator(app_locale_);
   base::string16 field_contents_canon =
       comparator.NormalizeForComparison(field_contents);
 
@@ -1162,9 +1162,9 @@
   // Don't show two suggestions if one is a subset of the other.
   std::vector<AutofillProfile*> unique_matched_profiles;
   std::vector<Suggestion> unique_suggestions =
-      suggestion_selection::GetUniqueSuggestions(field_types, app_locale_,
-                                                 matched_profiles, suggestions,
-                                                 &unique_matched_profiles);
+      suggestion_selection::GetUniqueSuggestions(
+          field_types, comparator, app_locale_, matched_profiles, suggestions,
+          &unique_matched_profiles);
 
   std::unique_ptr<LabelFormatter> formatter;
 
diff --git a/components/autofill/core/browser/suggestion_selection.cc b/components/autofill/core/browser/suggestion_selection.cc
index 014881e..5d18a56 100644
--- a/components/autofill/core/browser/suggestion_selection.cc
+++ b/components/autofill/core/browser/suggestion_selection.cc
@@ -15,6 +15,7 @@
 #include "components/autofill/core/browser/autofill_metrics.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_profile_comparator.h"
+#include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/suggestion.h"
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_features.h"
@@ -123,6 +124,7 @@
 
 std::vector<Suggestion> GetUniqueSuggestions(
     const std::vector<ServerFieldType>& field_types,
+    const AutofillProfileComparator& comparator,
     const std::string app_locale,
     const std::vector<AutofillProfile*> matched_profiles,
     const std::vector<Suggestion>& suggestions,
@@ -140,15 +142,18 @@
     for (size_t j = 0; j < matched_profiles.size(); ++j) {
       AutofillProfile* profile_b = matched_profiles[j];
       // Check if profile A is a subset of profile B. If not, continue.
-      if (i == j || suggestions[i].value != suggestions[j].value ||
-          !profile_a->IsSubsetOfForFieldSet(*profile_b, app_locale, types)) {
+      if (i == j ||
+          !comparator.MatchesAfterNormalization(suggestions[i].value,
+                                                suggestions[j].value) ||
+          !profile_a->IsSubsetOfForFieldSet(comparator, *profile_b, app_locale,
+                                            types)) {
         continue;
       }
 
       // Check if profile B is also a subset of profile A. If so, the
       // profiles are identical. Include the first one but not the second.
-      if (i < j &&
-          profile_b->IsSubsetOfForFieldSet(*profile_a, app_locale, types)) {
+      if (i < j && profile_b->IsSubsetOfForFieldSet(comparator, *profile_a,
+                                                    app_locale, types)) {
         continue;
       }
 
diff --git a/components/autofill/core/browser/suggestion_selection.h b/components/autofill/core/browser/suggestion_selection.h
index 817c0d20..dd8bf52 100644
--- a/components/autofill/core/browser/suggestion_selection.h
+++ b/components/autofill/core/browser/suggestion_selection.h
@@ -4,8 +4,11 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTION_SELECTION_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTION_SELECTION_H_
 
+#include <string>
 #include <vector>
 
+#include "base/strings/string16.h"
+#include "base/time/time.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/suggestion.h"
 
@@ -38,6 +41,7 @@
 // the field on which the user is currently focused.
 std::vector<Suggestion> GetUniqueSuggestions(
     const std::vector<ServerFieldType>& field_types,
+    const AutofillProfileComparator& comparator,
     const std::string app_locale,
     const std::vector<AutofillProfile*> matched_profiles,
     const std::vector<Suggestion>& suggestions,
diff --git a/components/autofill/core/browser/suggestion_selection_unittest.cc b/components/autofill/core/browser/suggestion_selection_unittest.cc
index cb36a7d..422afeb7 100644
--- a/components/autofill/core/browser/suggestion_selection_unittest.cc
+++ b/components/autofill/core/browser/suggestion_selection_unittest.cc
@@ -242,7 +242,7 @@
 
   std::vector<AutofillProfile*> unique_matched_profiles;
   auto unique_suggestions =
-      GetUniqueSuggestions({}, app_locale_, profile_pointers,
+      GetUniqueSuggestions({}, comparator_, app_locale_, profile_pointers,
                            CreateSuggestions(profile_pointers, NAME_FIRST),
                            &unique_matched_profiles);
 
@@ -267,10 +267,10 @@
   auto profile_pointers = {profile1.get(), profile2.get(), profile3.get()};
 
   std::vector<AutofillProfile*> unique_matched_profiles;
-  auto unique_suggestions =
-      GetUniqueSuggestions({NAME_LAST}, app_locale_, profile_pointers,
-                           CreateSuggestions(profile_pointers, NAME_FIRST),
-                           &unique_matched_profiles);
+  auto unique_suggestions = GetUniqueSuggestions(
+      {NAME_LAST}, comparator_, app_locale_, profile_pointers,
+      CreateSuggestions(profile_pointers, NAME_FIRST),
+      &unique_matched_profiles);
 
   ASSERT_EQ(3U, unique_suggestions.size());
   ASSERT_EQ(3U, unique_matched_profiles.size());
@@ -299,10 +299,10 @@
                  });
 
   std::vector<AutofillProfile*> unique_matched_profiles;
-  auto unique_suggestions =
-      GetUniqueSuggestions({NAME_LAST}, app_locale_, profiles_pointers,
-                           CreateSuggestions(profiles_pointers, NAME_FIRST),
-                           &unique_matched_profiles);
+  auto unique_suggestions = GetUniqueSuggestions(
+      {NAME_LAST}, comparator_, app_locale_, profiles_pointers,
+      CreateSuggestions(profiles_pointers, NAME_FIRST),
+      &unique_matched_profiles);
 
   ASSERT_EQ(kMaxUniqueSuggestionsCount, unique_suggestions.size());
   ASSERT_EQ(kMaxUniqueSuggestionsCount, unique_matched_profiles.size());
@@ -316,8 +316,8 @@
 
 TEST_F(SuggestionSelectionTest, GetUniqueSuggestions_EmptyMatchingProfiles) {
   std::vector<AutofillProfile*> unique_matched_profiles;
-  auto unique_suggestions = GetUniqueSuggestions({NAME_LAST}, app_locale_, {},
-                                                 {}, &unique_matched_profiles);
+  auto unique_suggestions = GetUniqueSuggestions(
+      {NAME_LAST}, comparator_, app_locale_, {}, {}, &unique_matched_profiles);
 
   ASSERT_EQ(0U, unique_matched_profiles.size());
   ASSERT_EQ(0U, unique_suggestions.size());
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index e89292d..d2511eb 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -971,7 +971,7 @@
       runtime::CallFunctionOnParams::Builder()
           .SetObjectId(element_object_id)
           .SetArguments(std::move(argument))
-          .SetFunctionDeclaration(std::string(kScrollIntoViewScript))
+          .SetFunctionDeclaration(std::string(kScrollIntoViewIfNeededScript))
           .SetReturnByValue(true)
           .Build(),
       base::BindOnce(&WebController::OnScrollIntoView,
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 969774e..92847e2 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -444,7 +444,10 @@
 }
 
 android_resource_sizes_test("resource_sizes_cronet_sample_apk") {
-  apk = ":cronet_sample_apk"
+  apk_name = "CronetSample"
+  data_deps = [
+    ":cronet_sample_apk",
+  ]
 }
 
 action("cronet_combine_proguard_flags") {
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc
index 6c565913..84bf606 100644
--- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc
+++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc
@@ -11,7 +11,6 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h"
 #include "content/public/common/previews_state.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -26,22 +25,8 @@
   void MarkProxiesAsBad(base::TimeDelta bypass_duration,
                         const net::ProxyList& bad_proxies,
                         MarkProxiesAsBadCallback callback) override {
-    ASSERT_FALSE(waiting_for_mark_as_bad_closure_.is_null())
-        << "Unexpected call to MarkProxyAsBadAndRestart";
-
-    std::move(callback).Run();
-
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, std::move(waiting_for_mark_as_bad_closure_));
-  }
-
-  // Wait for MarkProxiesAsBad() to be called, and then reply.
-  void WaitUntilMarkProxiesAsBadCalled() {
-    ASSERT_TRUE(waiting_for_mark_as_bad_closure_.is_null());
-
-    base::RunLoop run_loop;
-    waiting_for_mark_as_bad_closure_ = run_loop.QuitClosure();
-    run_loop.Run();
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  std::move(callback));
   }
 
   void AddThrottleConfigObserver(
@@ -50,8 +35,6 @@
   void Clone(mojom::DataReductionProxyRequest request) override {}
 
  private:
-  base::RepeatingClosure waiting_for_mark_as_bad_closure_;
-
   DISALLOW_COPY_AND_ASSIGN(MockMojoDataReductionProxy);
 };
 
@@ -64,7 +47,11 @@
     FAIL() << "Should not be reached";
   }
 
-  void Resume() override { resume_called++; }
+  void Resume() override {
+    resume_called++;
+    if (resume_callback)
+      std::move(resume_callback).Run();
+  }
 
   void RestartWithFlags(int additional_load_flags) override {
     restart_with_flags_called++;
@@ -75,26 +62,17 @@
   size_t restart_with_flags_called = 0;
   int restart_additional_load_flags = 0;
 
+  base::OnceClosure resume_callback;
+
   DISALLOW_COPY_AND_ASSIGN(MockDelegate);
 };
 
 // Creates a DataReductionProxyThrottleManager which is bound to a
-// MockMojoDataReductionProxy, and has an initial throttle configuration
+// mojo::DataReductionProxy, and has an initial throttle configuration
 // containing |initial_drp_servers|.
-//
-// If |out_mock_drp| is non-null, it is assigned a non-owned pointer to the
-// underlying mock implementation. This pointer will remain valid while the
-// DataReductionProxyThrottleManager is alive.
 std::unique_ptr<DataReductionProxyThrottleManager> CreateManager(
-    const std::vector<DataReductionProxyServer>& initial_drp_servers = {},
-    MockMojoDataReductionProxy** out_mock_drp = nullptr) {
-  auto mock_drp = std::make_unique<MockMojoDataReductionProxy>();
-  if (out_mock_drp)
-    *out_mock_drp = mock_drp.get();
-
-  mojom::DataReductionProxyPtr drp;
-  mojo::MakeStrongBinding(std::move(mock_drp), mojo::MakeRequest(&drp));
-
+    mojom::DataReductionProxy* mojo_data_reduction_proxy,
+    const std::vector<DataReductionProxyServer>& initial_drp_servers = {}) {
   auto initial_throttle_config =
       initial_drp_servers.empty()
           ? mojom::DataReductionProxyThrottleConfigPtr()
@@ -102,7 +80,7 @@
                 initial_drp_servers);
 
   return std::make_unique<DataReductionProxyThrottleManager>(
-      std::move(drp), std::move(initial_throttle_config));
+      mojo_data_reduction_proxy, std::move(initial_throttle_config));
 }
 
 DataReductionProxyServer MakeCoreDrpServer(const std::string pac_string) {
@@ -112,12 +90,18 @@
 }
 
 class DataReductionProxyURLLoaderThrottleTest : public ::testing::Test {
+ public:
+  MockMojoDataReductionProxy* mock_mojo_data_reduction_proxy() {
+    return &mock_mojo_data_reduction_proxy_;
+  }
+
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  MockMojoDataReductionProxy mock_mojo_data_reduction_proxy_;
 };
 
 TEST_F(DataReductionProxyURLLoaderThrottleTest, AcceptTransformHeaderSet) {
-  auto manager = CreateManager();
+  auto manager = CreateManager(mock_mojo_data_reduction_proxy());
   DataReductionProxyURLLoaderThrottle throttle(net::HttpRequestHeaders(),
                                                manager.get());
   network::ResourceRequest request;
@@ -136,7 +120,7 @@
 
 TEST_F(DataReductionProxyURLLoaderThrottleTest,
        AcceptTransformHeaderSetForMainFrame) {
-  auto manager = CreateManager();
+  auto manager = CreateManager(mock_mojo_data_reduction_proxy());
   DataReductionProxyURLLoaderThrottle throttle((net::HttpRequestHeaders()),
                                                manager.get());
   network::ResourceRequest request;
@@ -156,7 +140,7 @@
 
 TEST_F(DataReductionProxyURLLoaderThrottleTest,
        ConstructorHeadersAddedToPostCacheHeaders) {
-  auto manager = CreateManager();
+  auto manager = CreateManager(mock_mojo_data_reduction_proxy());
 
   net::HttpRequestHeaders headers;
   headers.SetHeader("foo", "bar");
@@ -174,7 +158,7 @@
 }
 
 TEST_F(DataReductionProxyURLLoaderThrottleTest, UseAlternateProxyList) {
-  auto manager = CreateManager();
+  auto manager = CreateManager(mock_mojo_data_reduction_proxy());
   DataReductionProxyURLLoaderThrottle throttle((net::HttpRequestHeaders()),
                                                manager.get());
   network::ResourceRequest request;
@@ -188,7 +172,7 @@
 }
 
 TEST_F(DataReductionProxyURLLoaderThrottleTest, DontUseAlternateProxyList) {
-  auto manager = CreateManager();
+  auto manager = CreateManager(mock_mojo_data_reduction_proxy());
   DataReductionProxyURLLoaderThrottle throttle((net::HttpRequestHeaders()),
                                                manager.get());
   network::ResourceRequest request;
@@ -201,11 +185,12 @@
   EXPECT_FALSE(request.custom_proxy_use_alternate_proxy_list);
 }
 
-void RestartBypassProxyAndCacheHelper(bool response_came_from_drp) {
+void RestartBypassProxyAndCacheHelper(
+    mojom::DataReductionProxy* mojo_data_reduction_proxy,
+    bool response_came_from_drp) {
   auto drp_server = MakeCoreDrpServer("HTTPS localhost");
 
-  MockMojoDataReductionProxy* drp;
-  auto manager = CreateManager({drp_server}, &drp);
+  auto manager = CreateManager(mojo_data_reduction_proxy, {drp_server});
   MockDelegate delegate;
   DataReductionProxyURLLoaderThrottle throttle((net::HttpRequestHeaders()),
                                                manager.get());
@@ -250,7 +235,8 @@
 // Tests that when "Chrome-Proxy: block-once" is received from a DRP response
 // the request is immediately restarted.
 TEST_F(DataReductionProxyURLLoaderThrottleTest, RestartBypassProxyAndCache) {
-  RestartBypassProxyAndCacheHelper(/*response_was_from_drp=*/true);
+  RestartBypassProxyAndCacheHelper(mock_mojo_data_reduction_proxy(),
+                                   /*response_was_from_drp=*/true);
 }
 
 // Tests that when "Chrome-proxy: block-once" is received from a non-DRP server
@@ -258,14 +244,15 @@
 // restarted).
 TEST_F(DataReductionProxyURLLoaderThrottleTest,
        ChromeProxyDisregardedForNonDrp) {
-  RestartBypassProxyAndCacheHelper(/*response_was_from_drp=*/false);
+  RestartBypassProxyAndCacheHelper(mock_mojo_data_reduction_proxy(),
+                                   /*response_was_from_drp=*/false);
 }
 
 TEST_F(DataReductionProxyURLLoaderThrottleTest,
        DisregardChromeProxyFromDirect) {
   auto drp_server = MakeCoreDrpServer("HTTPS localhost");
 
-  auto manager = CreateManager({drp_server}, nullptr);
+  auto manager = CreateManager(mock_mojo_data_reduction_proxy(), {drp_server});
   MockDelegate delegate;
   DataReductionProxyURLLoaderThrottle throttle((net::HttpRequestHeaders()),
                                                manager.get());
@@ -297,8 +284,7 @@
 TEST_F(DataReductionProxyURLLoaderThrottleTest, MarkProxyAsBadAndRestart) {
   auto drp_server = MakeCoreDrpServer("HTTPS localhost");
 
-  MockMojoDataReductionProxy* drp;
-  auto manager = CreateManager({drp_server}, &drp);
+  auto manager = CreateManager(mock_mojo_data_reduction_proxy(), {drp_server});
   MockDelegate delegate;
   DataReductionProxyURLLoaderThrottle throttle((net::HttpRequestHeaders()),
                                                manager.get());
@@ -326,9 +312,11 @@
   EXPECT_EQ(0u, delegate.resume_called);
   EXPECT_EQ(0u, delegate.restart_with_flags_called);
 
-  drp->WaitUntilMarkProxiesAsBadCalled();
-
   // The throttle should restart and resume.
+  base::RunLoop run_loop;
+  delegate.resume_callback = run_loop.QuitClosure();
+  run_loop.Run();
+
   EXPECT_EQ(1u, delegate.resume_called);
   EXPECT_EQ(1u, delegate.restart_with_flags_called);
   EXPECT_EQ(0, delegate.restart_additional_load_flags);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
index bccf224..65fe98e 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -468,18 +468,6 @@
   UpdateCustomProxyConfig();
 }
 
-DataReductionProxyThrottleManager*
-DataReductionProxyIOData::GetThrottleManager() {
-  if (!throttle_manager_) {
-    mojom::DataReductionProxyPtr drp;
-    Clone(mojo::MakeRequest(&drp));
-    throttle_manager_ = std::make_unique<DataReductionProxyThrottleManager>(
-        std::move(drp), CreateThrottleConfig());
-  }
-
-  return throttle_manager_.get();
-}
-
 void DataReductionProxyIOData::MarkProxiesAsBad(
     base::TimeDelta bypass_duration,
     const net::ProxyList& bad_proxies,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
index a26230a9..b30102ca 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
@@ -50,7 +50,6 @@
 class DataReductionProxyServer;
 class DataReductionProxyService;
 class NetworkPropertiesManager;
-class DataReductionProxyThrottleManager;
 
 // Contains and initializes all Data Reduction Proxy objects that operate on
 // the IO thread.
@@ -176,7 +175,7 @@
     return proxy_delegate_.get();
   }
 
-  DataReductionProxyThrottleManager* GetThrottleManager();
+  mojom::DataReductionProxyThrottleConfigPtr CreateThrottleConfig() const;
 
   const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner() const {
     return io_task_runner_;
@@ -271,8 +270,6 @@
   // config.
   void UpdateThrottleConfig();
 
-  mojom::DataReductionProxyThrottleConfigPtr CreateThrottleConfig() const;
-
   // The type of Data Reduction Proxy client.
   const Client client_;
 
@@ -335,8 +332,6 @@
   // is unavailable, then the destruction will happen on the UI thread.
   std::unique_ptr<NetworkPropertiesManager> network_properties_manager_;
 
-  std::unique_ptr<DataReductionProxyThrottleManager> throttle_manager_;
-
   // Current estimate of the effective connection type.
   net::EffectiveConnectionType effective_connection_type_;
 
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc
index d99e3a8..2c37c320 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc
@@ -4,6 +4,7 @@
 
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h"
 
+#include "base/memory/ptr_util.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
 
@@ -13,24 +14,29 @@
 
 namespace data_reduction_proxy {
 
-DataReductionProxyThrottleManager::~DataReductionProxyThrottleManager() =
-    default;
-
 DataReductionProxyThrottleManager::DataReductionProxyThrottleManager(
-    mojom::DataReductionProxyPtr data_reduction_proxy,
+    mojom::DataReductionProxy* data_reduction_proxy,
     mojom::DataReductionProxyThrottleConfigPtr initial_config)
-    : data_reduction_proxy_(std::move(data_reduction_proxy)), binding_(this) {
-  data_reduction_proxy::mojom::DataReductionProxyThrottleConfigObserverPtr
-      observer;
+    : data_reduction_proxy_(data_reduction_proxy), binding_(this) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  mojom::DataReductionProxyThrottleConfigObserverPtr observer;
   binding_.Bind(mojo::MakeRequest(&observer));
+
   data_reduction_proxy_->AddThrottleConfigObserver(std::move(observer));
 
   OnThrottleConfigChanged(std::move(initial_config));
 }
 
+DataReductionProxyThrottleManager::~DataReductionProxyThrottleManager() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
 base::Optional<DataReductionProxyTypeInfo>
 DataReductionProxyThrottleManager::FindConfiguredDataReductionProxy(
     const net::ProxyServer& proxy_server) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   // TODO(https://crbug.com/721403): The non-NS code also searches through the
   // recently seen proxies, not just the current ones.
   return params::FindConfiguredProxyInVector(proxies_for_http_, proxy_server);
@@ -40,6 +46,8 @@
     base::TimeDelta bypass_duration,
     const net::ProxyList& bad_proxies,
     mojom::DataReductionProxy::MarkProxiesAsBadCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   // There is no need to handle the case where |callback| is never invoked
   // (possible on connection error). That would imply disconnection from the
   // browser, which is not recoverable.
@@ -47,23 +55,10 @@
                                           std::move(callback));
 }
 
-std::unique_ptr<DataReductionProxyThrottleManager>
-DataReductionProxyThrottleManager::Clone() {
-  mojom::DataReductionProxyPtr data_reduction_proxy;
-
-  if (data_reduction_proxy_)
-    data_reduction_proxy_->Clone(mojo::MakeRequest(&data_reduction_proxy));
-
-  auto cloned_config = proxies_for_http_.empty()
-                           ? mojom::DataReductionProxyThrottleConfigPtr()
-                           : CreateConfig(proxies_for_http_);
-
-  return std::make_unique<DataReductionProxyThrottleManager>(
-      std::move(data_reduction_proxy), std::move(cloned_config));
-}
-
 void DataReductionProxyThrottleManager::OnThrottleConfigChanged(
     mojom::DataReductionProxyThrottleConfigPtr config) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   proxies_for_http_.clear();
 
   if (!config)
@@ -75,6 +70,7 @@
   }
 }
 
+// static
 mojom::DataReductionProxyThrottleConfigPtr
 DataReductionProxyThrottleManager::CreateConfig(
     const std::vector<DataReductionProxyServer>& proxies_for_http) {
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h
index d3d2a5f..f4a14656 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_THROTTLE_MANAGER_H_
 #define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_THROTTLE_MANAGER_H_
 
+#include "base/sequence_checker.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
@@ -19,10 +20,10 @@
 class DataReductionProxyThrottleManager
     : public mojom::DataReductionProxyThrottleConfigObserver {
  public:
-  // Observes |data_reduction_proxy| for changes to the config, and starts off
-  // with the initial value (possibly empty) |initial_config|.
+  // Observes |data_reduction_proxy| for changes to the config, and starts
+  // off with the initial value (possibly empty) |initial_config|.
   DataReductionProxyThrottleManager(
-      mojom::DataReductionProxyPtr data_reduction_proxy,
+      mojom::DataReductionProxy* data_reduction_proxy_info,
       mojom::DataReductionProxyThrottleConfigPtr initial_config);
 
   ~DataReductionProxyThrottleManager() override;
@@ -35,8 +36,6 @@
       const net::ProxyList& bad_proxies,
       mojom::DataReductionProxy::MarkProxiesAsBadCallback callback);
 
-  std::unique_ptr<DataReductionProxyThrottleManager> Clone();
-
   // mojom::DataReductionProxyThrottleConfigObserver implementation.
   void OnThrottleConfigChanged(
       mojom::DataReductionProxyThrottleConfigPtr config) override;
@@ -47,7 +46,7 @@
  private:
   void SetDataReductionProxy(mojom::DataReductionProxyPtr data_reduction_proxy);
 
-  mojom::DataReductionProxyPtr data_reduction_proxy_;
+  mojom::DataReductionProxy* const data_reduction_proxy_;
 
   // The last seen config values.
   std::vector<DataReductionProxyServer> proxies_for_http_;
@@ -56,6 +55,8 @@
       data_reduction_proxy::mojom::DataReductionProxyThrottleConfigObserver>
       binding_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(DataReductionProxyThrottleManager);
 };
 
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn
index 6bbab208..0fc188e 100644
--- a/components/exo/wayland/BUILD.gn
+++ b/components/exo/wayland/BUILD.gn
@@ -251,6 +251,7 @@
     "//third_party/wayland-protocols:linux_dmabuf_protocol",
     "//third_party/wayland-protocols:linux_explicit_synchronization_protocol",
     "//third_party/wayland-protocols:presentation_time_protocol",
+    "//third_party/wayland-protocols:vsync_feedback_protocol",
   ]
 
   if (ozone_platform_gbm) {
@@ -309,6 +310,7 @@
     "//third_party/wayland:wayland_client",
     "//third_party/wayland-protocols:linux_dmabuf_protocol",
     "//third_party/wayland-protocols:presentation_time_protocol",
+    "//third_party/wayland-protocols:vsync_feedback_protocol",
     "//ui/gfx/geometry",
     "//ui/gl",
   ]
diff --git a/components/exo/wayland/clients/client_base.cc b/components/exo/wayland/clients/client_base.cc
index f7a4f759..4e7d9fe1c 100644
--- a/components/exo/wayland/clients/client_base.cc
+++ b/components/exo/wayland/clients/client_base.cc
@@ -19,6 +19,8 @@
 
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -135,6 +137,9 @@
         static_cast<zwp_linux_explicit_synchronization_v1*>(wl_registry_bind(
             registry, id, &zwp_linux_explicit_synchronization_v1_interface,
             1)));
+  } else if (strcmp(interface, "zcr_vsync_feedback_v1") == 0) {
+    globals->vsync_feedback.reset(static_cast<zcr_vsync_feedback_v1*>(
+        wl_registry_bind(registry, id, &zcr_vsync_feedback_v1_interface, 1)));
   }
 }
 
@@ -742,11 +747,18 @@
     buffer = std::make_unique<Buffer>();
 
     size_t stride = size.width() * kBytesPerPixel;
-    buffer->shared_memory.reset(new base::SharedMemory());
-    buffer->shared_memory->CreateAndMapAnonymous(stride * size.height());
+    base::UnsafeSharedMemoryRegion shared_memory_region =
+        base::UnsafeSharedMemoryRegion::Create(stride * size.height());
+    buffer->shared_memory_mapping = shared_memory_region.Map();
+    base::subtle::PlatformSharedMemoryRegion platform_shared_memory =
+        base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
+            std::move(shared_memory_region));
+
+    // wl_shm_create_pool takes ownership of the file descriptor being passed.
     buffer->shm_pool.reset(wl_shm_create_pool(
-        globals_.shm.get(), buffer->shared_memory->handle().GetHandle(),
-        buffer->shared_memory->requested_size()));
+        globals_.shm.get(),
+        platform_shared_memory.PassPlatformHandle().fd.release(),
+        buffer->shared_memory_mapping.size()));
 
     buffer->buffer.reset(static_cast<wl_buffer*>(
         wl_shm_pool_create_buffer(buffer->shm_pool.get(), 0, size.width(),
@@ -759,7 +771,7 @@
     buffer->sk_surface = SkSurface::MakeRasterDirect(
         SkImageInfo::Make(size.width(), size.height(), kColorType,
                           kOpaque_SkAlphaType),
-        static_cast<uint8_t*>(buffer->shared_memory->memory()), stride);
+        buffer->shared_memory_mapping.GetMemoryAs<uint8_t>(), stride);
     DCHECK(buffer->sk_surface);
   }
 
diff --git a/components/exo/wayland/clients/client_base.h b/components/exo/wayland/clients/client_base.h
index 0513d411..c9bf3c5 100644
--- a/components/exo/wayland/clients/client_base.h
+++ b/components/exo/wayland/clients/client_base.h
@@ -9,7 +9,7 @@
 #include <string>
 #include <vector>
 
-#include "base/memory/shared_memory.h"
+#include "base/memory/shared_memory_mapping.h"
 #include "components/exo/wayland/clients/client_helper.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
@@ -78,6 +78,7 @@
     std::unique_ptr<zwp_input_timestamps_manager_v1> input_timestamps_manager;
     std::unique_ptr<zwp_linux_explicit_synchronization_v1>
         linux_explicit_synchronization;
+    std::unique_ptr<zcr_vsync_feedback_v1> vsync_feedback;
   };
 
   struct Buffer {
@@ -99,8 +100,8 @@
 #endif  // defined(USE_VULKAN)
 #endif  // defined(USE_GBM)
     std::unique_ptr<zwp_linux_buffer_params_v1> params;
-    std::unique_ptr<base::SharedMemory> shared_memory;
     std::unique_ptr<wl_shm_pool> shm_pool;
+    base::WritableSharedMemoryMapping shared_memory_mapping;
     sk_sp<SkSurface> sk_surface;
   };
 
diff --git a/components/exo/wayland/clients/client_helper.cc b/components/exo/wayland/clients/client_helper.cc
index 2b8e00e..6d2fd6b 100644
--- a/components/exo/wayland/clients/client_helper.cc
+++ b/components/exo/wayland/clients/client_helper.cc
@@ -62,6 +62,8 @@
                 zwp_linux_explicit_synchronization_v1_destroy)
 DEFAULT_DELETER(zwp_linux_surface_synchronization_v1,
                 zwp_linux_surface_synchronization_v1_destroy)
+DEFAULT_DELETER(zcr_vsync_feedback_v1, zcr_vsync_feedback_v1_destroy)
+DEFAULT_DELETER(zcr_vsync_timing_v1, zcr_vsync_timing_v1_destroy)
 
 #if defined(USE_GBM)
 DEFAULT_DELETER(gbm_bo, gbm_bo_destroy)
diff --git a/components/exo/wayland/clients/client_helper.h b/components/exo/wayland/clients/client_helper.h
index 9b068da9..40b7796b 100644
--- a/components/exo/wayland/clients/client_helper.h
+++ b/components/exo/wayland/clients/client_helper.h
@@ -11,6 +11,7 @@
 #include <linux-dmabuf-unstable-v1-client-protocol.h>
 #include <linux-explicit-synchronization-unstable-v1-client-protocol.h>
 #include <presentation-time-client-protocol.h>
+#include <vsync-feedback-unstable-v1-client-protocol.h>
 #include <wayland-client-core.h>
 #include <wayland-client-protocol.h>
 
@@ -64,6 +65,8 @@
 DEFAULT_DELETER_FDECL(zwp_linux_dmabuf_v1)
 DEFAULT_DELETER_FDECL(zwp_linux_explicit_synchronization_v1)
 DEFAULT_DELETER_FDECL(zwp_linux_surface_synchronization_v1)
+DEFAULT_DELETER_FDECL(zcr_vsync_feedback_v1)
+DEFAULT_DELETER_FDECL(zcr_vsync_timing_v1)
 
 #if defined(USE_GBM)
 DEFAULT_DELETER_FDECL(gbm_bo)
diff --git a/components/exo/wayland/clients/perftests.cc b/components/exo/wayland/clients/perftests.cc
index 52acea7..a88dcb5a 100644
--- a/components/exo/wayland/clients/perftests.cc
+++ b/components/exo/wayland/clients/perftests.cc
@@ -25,11 +25,11 @@
   exo::wayland::clients::Simple client;
   EXPECT_TRUE(client.Init(params));
 
-  client.Run(kWarmUpFrames, nullptr);
+  client.Run(kWarmUpFrames, false, nullptr);
 
   exo::wayland::clients::Simple::PresentationFeedback feedback;
   auto start_time = base::Time::Now();
-  client.Run(kTestFrames, &feedback);
+  client.Run(kTestFrames, false, &feedback);
   auto time_delta = base::Time::Now() - start_time;
   float fps = kTestFrames / time_delta.InSecondsF();
   perf_test::PrintResult("WaylandClientPerfTests", "", "SimpleFrameRate", fps,
diff --git a/components/exo/wayland/clients/simple.cc b/components/exo/wayland/clients/simple.cc
index d04bfa6..1a51724 100644
--- a/components/exo/wayland/clients/simple.cc
+++ b/components/exo/wayland/clients/simple.cc
@@ -5,6 +5,7 @@
 #include "components/exo/wayland/clients/simple.h"
 
 #include <presentation-time-client-protocol.h>
+#include <iostream>
 
 #include "base/command_line.h"
 #include "base/containers/circular_deque.h"
@@ -77,15 +78,46 @@
   presentation->submitted_frames.erase(it);
 }
 
+void VSyncTimingUpdate(void* data,
+                       struct zcr_vsync_timing_v1* zcr_vsync_timing_v1,
+                       uint32_t timebase_l,
+                       uint32_t timebase_h,
+                       uint32_t interval_l,
+                       uint32_t interval_h) {
+  uint64_t timebase = static_cast<uint64_t>(timebase_h) << 32 | timebase_l;
+  uint64_t interval = static_cast<uint64_t>(interval_h) << 32 | interval_l;
+  std::cout << "Received new VSyncTimingUpdate. Timebase: " << timebase
+            << ". Interval: " << interval << std::endl;
+}
+
 }  // namespace
 
 Simple::Simple() = default;
 
-void Simple::Run(int frames, PresentationFeedback* feedback) {
+void Simple::Run(int frames,
+                 const bool log_vsync_timing_updates,
+                 PresentationFeedback* feedback) {
   wl_callback_listener frame_listener = {FrameCallback};
   wp_presentation_feedback_listener feedback_listener = {
       FeedbackSyncOutput, FeedbackPresented, FeedbackDiscarded};
 
+  std::unique_ptr<zcr_vsync_timing_v1> vsync_timing;
+  if (log_vsync_timing_updates) {
+    if (globals_.vsync_feedback) {
+      vsync_timing.reset(zcr_vsync_feedback_v1_get_vsync_timing(
+          globals_.vsync_feedback.get(), globals_.output.get()));
+      DCHECK(vsync_timing);
+      static zcr_vsync_timing_v1_listener vsync_timing_listener = {
+          VSyncTimingUpdate};
+      zcr_vsync_timing_v1_add_listener(vsync_timing.get(),
+                                       &vsync_timing_listener, this);
+    } else {
+      LOG(WARNING)
+          << "VSync timing updates requested but zcr_vsync_feedback_v1 "
+             "protocol is not available on the server.";
+    }
+  }
+
   Presentation presentation;
   int frame_count = 0;
 
diff --git a/components/exo/wayland/clients/simple.h b/components/exo/wayland/clients/simple.h
index da6baa6..e4fd285 100644
--- a/components/exo/wayland/clients/simple.h
+++ b/components/exo/wayland/clients/simple.h
@@ -24,7 +24,9 @@
     uint32_t num_frames_presented = 0;
   };
 
-  void Run(int frames, PresentationFeedback* feedback = nullptr);
+  void Run(int frames,
+           const bool log_vsync_timing_updates = false,
+           PresentationFeedback* feedback = nullptr);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Simple);
diff --git a/components/exo/wayland/clients/simple_main.cc b/components/exo/wayland/clients/simple_main.cc
index 03e2af2..42a5ec0 100644
--- a/components/exo/wayland/clients/simple_main.cc
+++ b/components/exo/wayland/clients/simple_main.cc
@@ -11,6 +11,11 @@
 #include "base/message_loop/message_loop.h"
 #include "components/exo/wayland/clients/simple.h"
 
+namespace switches {
+// Specifies if VSync timing updates should be logged on the output.
+const char kLogVSyncTimingUpdates[] = "log-vsync-timing-updates";
+}  // namespace switches
+
 int main(int argc, char* argv[]) {
   base::AtExitManager exit_manager;
   base::CommandLine::Init(argc, argv);
@@ -24,7 +29,10 @@
   if (!client.Init(params))
     return 1;
 
-  client.Run(std::numeric_limits<int>::max());
+  bool log_vsync_timing_updates =
+      command_line->HasSwitch(switches::kLogVSyncTimingUpdates);
+
+  client.Run(std::numeric_limits<int>::max(), log_vsync_timing_updates);
 
   return 0;
 }
diff --git a/components/exo/wayland/wayland_keyboard_delegate.cc b/components/exo/wayland/wayland_keyboard_delegate.cc
index 3344a33..659786cc 100644
--- a/components/exo/wayland/wayland_keyboard_delegate.cc
+++ b/components/exo/wayland/wayland_keyboard_delegate.cc
@@ -177,12 +177,19 @@
       xkb_keymap_get_as_string(xkb_keymap_.get(), XKB_KEYMAP_FORMAT_TEXT_V1));
   DCHECK(keymap_string.get());
   size_t keymap_size = strlen(keymap_string.get()) + 1;
-  base::SharedMemory shared_keymap;
-  bool rv = shared_keymap.CreateAndMapAnonymous(keymap_size);
-  DCHECK(rv);
+
+  base::UnsafeSharedMemoryRegion shared_keymap_region =
+      base::UnsafeSharedMemoryRegion::Create(keymap_size);
+  base::WritableSharedMemoryMapping shared_keymap = shared_keymap_region.Map();
+  base::subtle::PlatformSharedMemoryRegion platform_shared_keymap =
+      base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
+          std::move(shared_keymap_region));
+  DCHECK(shared_keymap.IsValid());
+
   memcpy(shared_keymap.memory(), keymap_string.get(), keymap_size);
-  wl_keyboard_send_keymap(keyboard_resource_, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
-                          shared_keymap.handle().GetHandle(), keymap_size);
+  wl_keyboard_send_keymap(
+      keyboard_resource_, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+      platform_shared_keymap.PassPlatformHandle().fd.release(), keymap_size);
   wl_client_flush(client());
 }
 
diff --git a/components/keyed_service/content/browser_context_dependency_manager.h b/components/keyed_service/content/browser_context_dependency_manager.h
index 1c65a5e..16f89a69 100644
--- a/components/keyed_service/content/browser_context_dependency_manager.h
+++ b/components/keyed_service/content/browser_context_dependency_manager.h
@@ -13,8 +13,6 @@
 #include "components/keyed_service/core/dependency_manager.h"
 #include "components/keyed_service/core/keyed_service_export.h"
 
-class BrowserContextKeyedBaseFactory;
-
 namespace base {
 template <typename T>
 class NoDestructor;
@@ -29,7 +27,7 @@
 }
 
 // A singleton that listens for context destruction notifications and
-// rebroadcasts them to each BrowserContextKeyedBaseFactory in a safe order
+// rebroadcasts them to each BrowserContextKeyedServiceFactory in a safe order
 // based on the stated dependencies by each service.
 class KEYED_SERVICE_EXPORT BrowserContextDependencyManager
     : public DependencyManager {
@@ -44,14 +42,14 @@
   // Called by each BrowserContext to alert us of its creation. Several
   // services want to be started when a context is created. If you want your
   // KeyedService to be started with the BrowserContext, override
-  // BrowserContextKeyedBaseFactory::ServiceIsCreatedWithBrowserContext() to
+  // BrowserContextKeyedServiceFactory::ServiceIsCreatedWithBrowserContext() to
   // return true. This method also registers any service-related preferences
   // for non-incognito profiles.
   void CreateBrowserContextServices(content::BrowserContext* context);
 
   // Similar to CreateBrowserContextServices(), except this is used for creating
   // test BrowserContexts - these contexts will not create services for any
-  // BrowserContextKeyedBaseFactories that return true from
+  // BrowserContextKeyedServiceFactory that returns true from
   // ServiceIsNULLWhileTesting().
   void CreateBrowserContextServicesForTest(content::BrowserContext* context);
 
diff --git a/components/metrics/call_stack_profile_builder.cc b/components/metrics/call_stack_profile_builder.cc
index f6504d86..92c3b938 100644
--- a/components/metrics/call_stack_profile_builder.cc
+++ b/components/metrics/call_stack_profile_builder.cc
@@ -5,6 +5,8 @@
 #include "components/metrics/call_stack_profile_builder.h"
 
 #include <algorithm>
+#include <iterator>
+#include <map>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -44,6 +46,56 @@
   return base::HashMetricName(name_bytes);
 }
 
+std::map<uint64_t, int64_t> CreateMetadataMap(
+    base::MetadataRecorder::ItemArray items,
+    size_t item_count) {
+  std::map<uint64_t, int64_t> item_map;
+  for (size_t i = 0; i < item_count; ++i) {
+    item_map[items[i].name_hash] = items[i].value;
+  }
+  return item_map;
+}
+
+// Returns all metadata items with new values in the current sample.
+std::map<uint64_t, int64_t> GetNewOrModifiedMetadataItems(
+    const std::map<uint64_t, int64_t>& current_items,
+    const std::map<uint64_t, int64_t>& previous_items) {
+  std::map<uint64_t, int64_t> new_or_modified_items;
+  // By default, std::pairs are sorted by the first then second pair elements
+  // and therefore pairs with either element differing are treated as different.
+  std::set_difference(
+      current_items.begin(), current_items.end(), previous_items.begin(),
+      previous_items.end(),
+      std::inserter(new_or_modified_items, new_or_modified_items.begin()));
+  return new_or_modified_items;
+}
+
+// Returns all metadata items deleted since the previous sample.
+std::map<uint64_t, int64_t> GetDeletedMetadataItems(
+    const std::map<uint64_t, int64_t>& current_items,
+    const std::map<uint64_t, int64_t>& previous_items) {
+  std::map<uint64_t, int64_t> deleted_items;
+  // By default, std::pairs are sorted by the first then second pair elements
+  // and therefore pairs with either element differing are treated as different.
+  //
+  // To find removed items, we need to override this comparator to do a set
+  // subtraction based only on the item name hashes, ignoring the item values.
+  //
+  // The set_difference algorithm requires that the items in the set already be
+  // sorted according to whatever comparator is passed to set_difference.
+  // Because our new sort order is just a looser version of the existing set
+  // sort order, we can find the set_difference here without creating a new set.
+  auto name_hash_comparator = [](const std::pair<uint64_t, int64_t>& lhs,
+                                 const std::pair<uint64_t, int64_t>& rhs) {
+    return lhs.first < rhs.first;
+  };
+  std::set_difference(previous_items.begin(), previous_items.end(),
+                      current_items.begin(), current_items.end(),
+                      std::inserter(deleted_items, deleted_items.begin()),
+                      name_hash_comparator);
+  return deleted_items;
+}
+
 }  // namespace
 
 CallStackProfileBuilder::CallStackProfileBuilder(
@@ -137,22 +189,7 @@
   if (is_continued_work_)
     stack_sample_proto->set_continued_work(is_continued_work_);
 
-  for (size_t i = 0; i < metadata_item_count_; ++i) {
-    const base::MetadataRecorder::Item recorder_item = metadata_items_[i];
-    int next_item_index = call_stack_profile->metadata_name_hash_size();
-    auto result = metadata_hashes_cache_.emplace(recorder_item.name_hash,
-                                                 next_item_index);
-    if (result.second)
-      call_stack_profile->add_metadata_name_hash(recorder_item.name_hash);
-    CallStackProfile::MetadataItem* profile_item =
-        stack_sample_proto->add_metadata();
-    // TODO(crbug.com/913570): Before uploading real metadata, ensure that we
-    // add metadata items only if the value differs from the value for the
-    // previous sample, per
-    // https://cs.chromium.org/chromium/src/third_party/metrics_proto/call_stack_profile.proto?rcl=8811ddb099&l=108-110.
-    profile_item->set_name_hash_index(result.first->second);
-    profile_item->set_value(recorder_item.value);
-  }
+  AddSampleMetadata(call_stack_profile, stack_sample_proto);
 }
 
 void CallStackProfileBuilder::OnProfileCompleted(
@@ -225,4 +262,46 @@
       });
 }
 
+void CallStackProfileBuilder::AddSampleMetadata(
+    CallStackProfile* profile,
+    CallStackProfile::StackSample* sample) {
+  std::map<uint64_t, int64_t> current_items =
+      CreateMetadataMap(metadata_items_, metadata_item_count_);
+
+  for (auto item :
+       GetNewOrModifiedMetadataItems(current_items, previous_items_)) {
+    size_t name_hash_index = MaybeAddNameHashToProfile(profile, item.first);
+
+    CallStackProfile::MetadataItem* profile_item = sample->add_metadata();
+    profile_item->set_name_hash_index(name_hash_index);
+    profile_item->set_value(item.second);
+  }
+
+  for (auto item : GetDeletedMetadataItems(current_items, previous_items_)) {
+    size_t name_hash_index = MaybeAddNameHashToProfile(profile, item.first);
+
+    CallStackProfile::MetadataItem* profile_item = sample->add_metadata();
+    profile_item->set_name_hash_index(name_hash_index);
+    // Leave the value empty to indicate that the item was deleted.
+  }
+
+  previous_items_ = std::move(current_items);
+  metadata_item_count_ = 0;
+}
+
+size_t CallStackProfileBuilder::MaybeAddNameHashToProfile(
+    CallStackProfile* profile,
+    uint64_t name_hash) {
+  std::unordered_map<uint64_t, int>::iterator it;
+  bool inserted;
+  int next_item_index = profile->metadata_name_hash_size();
+
+  std::tie(it, inserted) =
+      metadata_hashes_cache_.emplace(name_hash, next_item_index);
+  if (inserted)
+    profile->add_metadata_name_hash(name_hash);
+
+  return it->second;
+}
+
 }  // namespace metrics
diff --git a/components/metrics/call_stack_profile_builder.h b/components/metrics/call_stack_profile_builder.h
index 72a5fbeb..ddbc497 100644
--- a/components/metrics/call_stack_profile_builder.h
+++ b/components/metrics/call_stack_profile_builder.h
@@ -92,6 +92,15 @@
                     const CallStackProfile::Stack* stack2) const;
   };
 
+  // Adds the already-collected metadata to the sample.
+  void AddSampleMetadata(CallStackProfile* profile,
+                         CallStackProfile::StackSample* sample);
+
+  // Adds the specified name hash to the profile's name hash collection if it's
+  // not already in it. Returns the index of the name hash in the collection.
+  size_t MaybeAddNameHashToProfile(CallStackProfile* profile,
+                                   uint64_t name_hash);
+
   // The module cache to use for the duration the sampling associated with this
   // ProfileBuilder.
   base::ModuleCache module_cache_;
@@ -120,9 +129,11 @@
   // The start time of a profile collection.
   const base::TimeTicks profile_start_time_;
 
-  // The data fetched from the MetadataRecorder for each sample.
+  // The data fetched from the MetadataRecorder for the next sample.
   base::MetadataRecorder::ItemArray metadata_items_;
   size_t metadata_item_count_ = 0;
+  // The data fetched from the MetadataRecorder for the previous sample.
+  std::map<uint64_t, int64_t> previous_items_;
 
   // Maps metadata hash to index in |metadata_name_hash| array.
   std::unordered_map<uint64_t, int> metadata_hashes_cache_;
diff --git a/components/metrics/call_stack_profile_builder_unittest.cc b/components/metrics/call_stack_profile_builder_unittest.cc
index e4922a6..4f98a15 100644
--- a/components/metrics/call_stack_profile_builder_unittest.cc
+++ b/components/metrics/call_stack_profile_builder_unittest.cc
@@ -459,10 +459,9 @@
   EXPECT_EQ(0, profile.stack_sample(0).metadata(0).name_hash_index());
   EXPECT_EQ(10, profile.stack_sample(0).metadata(0).value());
 
-  // The second sample should have the same metadata as the first sample.
-  ASSERT_EQ(1, profile.stack_sample(1).metadata_size());
-  EXPECT_EQ(0, profile.stack_sample(0).metadata(0).name_hash_index());
-  EXPECT_EQ(10, profile.stack_sample(0).metadata(0).value());
+  // The second sample shouldn't have any metadata because it's all the same as
+  // the last sample.
+  ASSERT_EQ(0, profile.stack_sample(1).metadata_size());
 }
 
 TEST(CallStackProfileBuilderTest, MetadataRecorder_ModifiedItem) {
@@ -516,6 +515,8 @@
   metadata_recorder.Set(100, 10);
   profile_builder->RecordMetadata();
   profile_builder->OnSampleCompleted({frame});
+
+  metadata_recorder.Set(100, 11);
   metadata_recorder.Set(200, 20);
   profile_builder->RecordMetadata();
   profile_builder->OnSampleCompleted({frame});
@@ -543,7 +544,8 @@
   auto sample2 = profile.stack_sample(1);
   ASSERT_EQ(2, sample2.metadata_size());
   EXPECT_EQ(0, sample2.metadata(0).name_hash_index());
-  EXPECT_EQ(10, sample2.metadata(0).value());
+  EXPECT_EQ(11, sample2.metadata(0).value());
+
   EXPECT_EQ(1, sample2.metadata(1).name_hash_index());
   EXPECT_EQ(20, sample2.metadata(1).value());
 }
@@ -581,9 +583,12 @@
   EXPECT_EQ(0, sample1.metadata(0).name_hash_index());
   EXPECT_EQ(10, sample1.metadata(0).value());
 
-  // The second sample should have no metadata.
+  // The second sample should have a metadata item with a set name hash but an
+  // empty value to indicate that the metadata item was removed.
   auto sample2 = profile.stack_sample(1);
-  ASSERT_EQ(0, sample2.metadata_size());
+  ASSERT_EQ(1, sample2.metadata_size());
+  EXPECT_EQ(0, sample2.metadata(0).name_hash_index());
+  EXPECT_FALSE(sample2.metadata(0).has_value());
 }
 
 }  // namespace metrics
diff --git a/components/ntp_tiles/constants.h b/components/ntp_tiles/constants.h
index 7395995..92875f7f 100644
--- a/components/ntp_tiles/constants.h
+++ b/components/ntp_tiles/constants.h
@@ -24,6 +24,7 @@
 extern const base::Feature kNtpMostLikelyFaviconsFromServerFeature;
 
 // Feature to provide site exploration tiles in addition to personal tiles.
+// TODO(https://crbug.com/957297): Remove this.
 extern const base::Feature kSiteExplorationUiFeature;
 
 // If this feature is enabled, we enable popular sites in the suggestions UI.
diff --git a/components/password_manager/core/browser/new_password_form_manager.cc b/components/password_manager/core/browser/new_password_form_manager.cc
index 0434334..466359ce 100644
--- a/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/components/password_manager/core/browser/new_password_form_manager.cc
@@ -681,17 +681,16 @@
   if (observed_password_form->is_new_password_reliable && !IsBlacklisted()) {
 #if defined(OS_IOS)
     driver_->FormEligibleForGenerationFound(
-        {.form_name = observed_password_form->form_data.name,
-         .new_password_element = observed_password_form->new_password_element,
-         .confirmation_password_element =
-             observed_password_form->confirmation_password_element});
+        {/*form_name*/ observed_password_form->form_data.name,
+         /*new_password_element*/ observed_password_form->new_password_element,
+         /*confirmation_password_element*/
+         observed_password_form->confirmation_password_element});
 #else
     driver_->FormEligibleForGenerationFound(
-        {.new_password_renderer_id =
-             observed_password_form->new_password_element_renderer_id,
-         .confirmation_password_renderer_id =
-             observed_password_form
-                 ->confirmation_password_element_renderer_id});
+        {/*new_password_renderer_id*/
+         observed_password_form->new_password_element_renderer_id,
+         /*confirmation_password_renderer_id*/
+         observed_password_form->confirmation_password_element_renderer_id});
 #endif
   }
 
diff --git a/components/password_manager/core/browser/password_generation_frame_helper.cc b/components/password_manager/core/browser/password_generation_frame_helper.cc
index a9cccad..d83c09a 100644
--- a/components/password_manager/core/browser/password_generation_frame_helper.cc
+++ b/components/password_manager/core/browser/password_generation_frame_helper.cc
@@ -73,8 +73,8 @@
     for (const auto& field : *form) {
       if (field->password_requirements()) {
         password_requirements_service->AddSpec(
-            form->form_signature(), field->GetFieldSignature(),
-            field->password_requirements().value());
+            form->source_url().GetOrigin(), form->form_signature(),
+            field->GetFieldSignature(), field->password_requirements().value());
       }
     }
   }
diff --git a/components/password_manager/core/browser/password_generation_frame_helper_unittest.cc b/components/password_manager/core/browser/password_generation_frame_helper_unittest.cc
index d91fa31..b239bc3 100644
--- a/components/password_manager/core/browser/password_generation_frame_helper_unittest.cc
+++ b/components/password_manager/core/browser/password_generation_frame_helper_unittest.cc
@@ -39,6 +39,7 @@
 #include "url/gurl.h"
 
 using autofill::FormStructure;
+using autofill::PasswordRequirementsSpec;
 using base::ASCIIToUTF16;
 using testing::_;
 
@@ -91,15 +92,15 @@
       found_forms_eligible_for_generation_;
 };
 
-autofill::PasswordRequirementsSpec GetDomainWideRequirements() {
-  autofill::PasswordRequirementsSpec spec;
+PasswordRequirementsSpec GetDomainWideRequirements() {
+  PasswordRequirementsSpec spec;
   spec.set_max_length(7);
   spec.set_priority(20);
   return spec;
 }
 
-autofill::PasswordRequirementsSpec GetFieldRequirements() {
-  autofill::PasswordRequirementsSpec spec;
+PasswordRequirementsSpec GetFieldRequirements() {
+  PasswordRequirementsSpec spec;
   spec.set_max_length(8);
   spec.set_priority(10);
   return spec;
@@ -117,7 +118,7 @@
   void Fetch(GURL origin, FetchCallback callback) override {
     if (origin.GetOrigin().host_piece().find(kNoServerResponse) !=
         std::string::npos) {
-      std::move(callback).Run(autofill::PasswordRequirementsSpec());
+      std::move(callback).Run(PasswordRequirementsSpec());
     } else if (origin.GetOrigin().host_piece().find(kHasServerResponse) !=
                std::string::npos) {
       std::move(callback).Run(GetDomainWideRequirements());
@@ -241,27 +242,35 @@
     const char* name;
     bool has_domain_wide_requirements = false;
     bool has_field_requirements = false;
-    autofill::PasswordRequirementsSpec expected_spec;
+    PasswordRequirementsSpec expected_spec;
+    // Assuming that a second form existed on the page for which no
+    // per-formsignature-requirements exists, this indicates the expected
+    // requirements that Chrome should conclude.
+    PasswordRequirementsSpec expected_spec_for_unknown_signature;
   } kTests[] = {
       {
           .name = "No known requirements",
-          .expected_spec = autofill::PasswordRequirementsSpec(),
+          .expected_spec = PasswordRequirementsSpec(),
+          .expected_spec_for_unknown_signature = PasswordRequirementsSpec(),
       },
       {
           .name = "Only domain wide requirements",
           .has_domain_wide_requirements = true,
           .expected_spec = GetDomainWideRequirements(),
+          .expected_spec_for_unknown_signature = GetDomainWideRequirements(),
       },
       {
           .name = "Only field requirements",
           .has_field_requirements = true,
           .expected_spec = GetFieldRequirements(),
+          .expected_spec_for_unknown_signature = GetFieldRequirements(),
       },
       {
           .name = "Domain wide requirements take precedence",
           .has_domain_wide_requirements = true,
           .has_field_requirements = true,
           .expected_spec = GetDomainWideRequirements(),
+          .expected_spec_for_unknown_signature = GetDomainWideRequirements(),
       },
   };
 
@@ -275,19 +284,25 @@
     ++test_counter;
 
     autofill::FormFieldData username;
-    username.label = ASCIIToUTF16("username");
     username.name = ASCIIToUTF16("login");
     username.form_control_type = "text";
 
     autofill::FormFieldData password;
-    password.label = ASCIIToUTF16("password");
     password.name =
         ASCIIToUTF16(base::StringPrintf("password%d", test_counter));
     password.form_control_type = "password";
 
+    // Configure the last committed entry URL with some magic constants for
+    // which the FakePasswordRequirementsFetcher is configured to respond
+    // with a filled or empty response.
+    GURL origin(base::StringPrintf("https://%d-%s/", test_counter,
+                                   test.has_domain_wide_requirements
+                                       ? kHasServerResponse
+                                       : kNoServerResponse));
+
     autofill::FormData account_creation_form;
-    account_creation_form.url = GURL("http://accounts.yahoo.com/");
-    account_creation_form.action = GURL("http://accounts.yahoo.com/signup");
+    account_creation_form.url = origin;
+    account_creation_form.action = origin;
     account_creation_form.name = ASCIIToUTF16("account_creation_form");
     account_creation_form.fields.push_back(username);
     account_creation_form.fields.push_back(password);
@@ -306,13 +321,7 @@
       *response.mutable_field(1)->mutable_password_requirements() =
           GetFieldRequirements();
     }
-    // Configure the last committed entry URL with some magic constants for
-    // which the FakePasswordRequirementsFetcher is configured to respond
-    // with a filled or empty response.
-    GURL origin(base::StringPrintf("https://%d-%s/", test_counter,
-                                   test.has_domain_wide_requirements
-                                       ? kHasServerResponse
-                                       : kNoServerResponse));
+
     client_->SetLastCommittedEntryUrl(origin);
 
     std::string response_string;
@@ -332,10 +341,16 @@
         autofill::CalculateFormSignature(account_creation_form);
     autofill::FieldSignature field_signature =
         autofill::CalculateFieldSignatureForField(password);
-    autofill::PasswordRequirementsSpec spec =
+    PasswordRequirementsSpec spec =
         client_->GetPasswordRequirementsService()->GetSpec(
             origin, form_signature, field_signature);
     EXPECT_EQ(test.expected_spec.max_length(), spec.max_length());
+
+    PasswordRequirementsSpec spec_for_unknown_signature =
+        client_->GetPasswordRequirementsService()->GetSpec(
+            origin, form_signature + 1, field_signature);
+    EXPECT_EQ(test.expected_spec_for_unknown_signature.max_length(),
+              spec.max_length());
   }
 }
 
diff --git a/components/password_manager/core/browser/password_requirements_service.cc b/components/password_manager/core/browser/password_requirements_service.cc
index 18e5925..18ef9a4 100644
--- a/components/password_manager/core/browser/password_requirements_service.cc
+++ b/components/password_manager/core/browser/password_requirements_service.cc
@@ -18,21 +18,25 @@
 constexpr size_t kCacheSizeForSignatureKeyedSpecs = 500;
 }  // namespace
 
+using autofill::PasswordRequirementsSpec;
+using autofill::PasswordRequirementsSpecFetcher;
+using autofill::PasswordRequirementsSpecFetcherImpl;
+
 namespace password_manager {
 
 PasswordRequirementsService::PasswordRequirementsService(
-    std::unique_ptr<autofill::PasswordRequirementsSpecFetcher> fetcher)
+    std::unique_ptr<PasswordRequirementsSpecFetcher> fetcher)
     : specs_for_domains_(kCacheSizeForDomainKeyedSpecs),
       specs_for_signatures_(kCacheSizeForSignatureKeyedSpecs),
       fetcher_(std::move(fetcher)) {}
 
 PasswordRequirementsService::~PasswordRequirementsService() = default;
 
-autofill::PasswordRequirementsSpec PasswordRequirementsService::GetSpec(
+PasswordRequirementsSpec PasswordRequirementsService::GetSpec(
     const GURL& main_frame_domain,
     autofill::FormSignature form_signature,
     autofill::FieldSignature field_signature) {
-  autofill::PasswordRequirementsSpec result;
+  PasswordRequirementsSpec result;
 
   auto iter_by_signature = specs_for_signatures_.Get(
       std::make_pair(form_signature, field_signature));
@@ -44,7 +48,7 @@
 
   auto iter_by_domain = specs_for_domains_.Get(main_frame_domain);
   if (iter_by_domain != specs_for_domains_.end()) {
-    const autofill::PasswordRequirementsSpec& spec = iter_by_domain->second;
+    const PasswordRequirementsSpec& spec = iter_by_domain->second;
     if (!found_item_by_signature) {
       // If nothing was found by signature, |spec| can be adopted.
       result = spec;
@@ -90,20 +94,29 @@
 
 void PasswordRequirementsService::OnFetchedRequirements(
     const GURL& main_frame_domain,
-    const autofill::PasswordRequirementsSpec& spec) {
+    const PasswordRequirementsSpec& spec) {
   VLOG(1) << "PasswordRequirementsService::OnFetchedRequirements("
           << main_frame_domain << ", " << spec << ")";
   specs_for_domains_.Put(main_frame_domain, spec);
 }
 
 void PasswordRequirementsService::AddSpec(
+    const GURL& main_frame_domain,
     autofill::FormSignature form_signature,
     autofill::FieldSignature field_signature,
-    const autofill::PasswordRequirementsSpec& spec) {
+    const PasswordRequirementsSpec& spec) {
   VLOG(1) << "PasswordRequirementsService::AddSpec(" << form_signature << ", "
           << field_signature << ", " << spec << ")";
   specs_for_signatures_.Put(std::make_pair(form_signature, field_signature),
                             spec);
+
+  auto iter_by_domain = specs_for_domains_.Get(main_frame_domain);
+  if (iter_by_domain != specs_for_domains_.end()) {
+    PasswordRequirementsSpec& existing_spec = iter_by_domain->second;
+    if (existing_spec.priority() > spec.priority())
+      return;
+  }
+  specs_for_domains_.Put(main_frame_domain, spec);
 }
 
 void PasswordRequirementsService::ClearDataForTestingImpl() {
@@ -142,8 +155,8 @@
   VLOG(1) << "PasswordGenerationRequirements parameters: " << version << ", "
           << prefix_length << ", " << timeout_in_ms << " ms";
 
-  std::unique_ptr<autofill::PasswordRequirementsSpecFetcher> fetcher =
-      std::make_unique<autofill::PasswordRequirementsSpecFetcherImpl>(
+  std::unique_ptr<PasswordRequirementsSpecFetcher> fetcher =
+      std::make_unique<PasswordRequirementsSpecFetcherImpl>(
           url_loader_factory, version, prefix_length, timeout_in_ms);
   return std::make_unique<PasswordRequirementsService>(std::move(fetcher));
 }
diff --git a/components/password_manager/core/browser/password_requirements_service.h b/components/password_manager/core/browser/password_requirements_service.h
index 2e38d08..ed33c81 100644
--- a/components/password_manager/core/browser/password_requirements_service.h
+++ b/components/password_manager/core/browser/password_requirements_service.h
@@ -51,9 +51,10 @@
   // |main_frame_domain| and stores it into the MRU cache.
   void PrefetchSpec(const GURL& main_frame_domain);
 
-  // Stores the password requirements for the field identified via
-  // |form_signature| and |field_signature| in the MRU cache.
-  void AddSpec(autofill::FormSignature form_signature,
+  // Stores the password requirements for |main_frame_domain| and for the field
+  // identified via |form_signature| and |field_signature| in the MRU caches.
+  void AddSpec(const GURL& main_frame_domain,
+               autofill::FormSignature form_signature,
                autofill::FieldSignature field_signature,
                const autofill::PasswordRequirementsSpec& spec);
 
diff --git a/components/password_manager/core/browser/password_requirements_service_unittest.cc b/components/password_manager/core/browser/password_requirements_service_unittest.cc
index b8f3a89..dd2f128 100644
--- a/components/password_manager/core/browser/password_requirements_service_unittest.cc
+++ b/components/password_manager/core/browser/password_requirements_service_unittest.cc
@@ -135,8 +135,8 @@
       service_.PrefetchSpec(test_origin_);
     }
     if (test.spec_for_signature) {
-      service_.AddSpec(test_form_signature_, test_field_signature_,
-                       *(test.spec_for_signature));
+      service_.AddSpec(test_origin_, test_form_signature_,
+                       test_field_signature_, *(test.spec_for_signature));
     }
 
     // Perform lookup.
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index a068212f..3050057 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -20,19 +20,6 @@
 const base::Feature kDeleteCorruptedPasswords = {
     "DeleteCorruptedPasswords", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Controls the ability to import passwords from Chrome's settings page.
-const base::Feature kPasswordImport = {"PasswordImport",
-                                       base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Adds password-related features to the keyboard accessory on mobile devices.
-const base::Feature kPasswordsKeyboardAccessory = {
-    "PasswordsKeyboardAccessory", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Deletes entries from local database on Mac which cannot be decrypted when
-// merging data with Sync.
-const base::Feature kRecoverPasswordsForSyncUsers = {
-    "RecoverPasswordsForSyncUsers", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables the experiment for the password manager to only fill on account
 // selection, rather than autofilling on page load, with highlighting of fields.
 const base::Feature kFillOnAccountSelect = {"fill-on-account-select",
@@ -47,6 +34,16 @@
 const base::Feature kGooglePasswordManager = {
     "google-password-manager", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Controls whether to offer manual password generation in the accessory sheet
+// on Android.
+const base::Feature kManualPasswordGenerationAndroid{
+    "ManualPasswordGenerationAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Performs a one-off migration (with retries) from a native backend into
+// logindb. Passwords are served from the new location.
+const base::Feature kMigrateLinuxToLoginDB = {"migrate-linux-to-logindb",
+                                              base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Enables new password form parsing mechanism for filling passwords, details in
 // https://goo.gl/QodPH1
 const base::Feature kNewPasswordFormParsing = {
@@ -62,15 +59,22 @@
 const base::Feature kOnlyNewParser = {"only-new-password-form-parsing",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Controls whether to offer manual password generation in the accessory sheet
-// on Android.
-const base::Feature kManualPasswordGenerationAndroid{
-    "ManualPasswordGenerationAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
+// Controls the ability to import passwords from Chrome's settings page.
+const base::Feature kPasswordImport = {"PasswordImport",
+                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Performs a one-off migration (with retries) from a native backend into
-// logindb. Passwords are served from the new location.
-const base::Feature kMigrateLinuxToLoginDB = {"migrate-linux-to-logindb",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
+// Adds password-related features to the keyboard accessory on mobile devices.
+const base::Feature kPasswordsKeyboardAccessory = {
+    "PasswordsKeyboardAccessory", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Deletes entries from local database on Mac which cannot be decrypted when
+// merging data with Sync.
+const base::Feature kRecoverPasswordsForSyncUsers = {
+    "RecoverPasswordsForSyncUsers", base::FEATURE_ENABLED_BY_DEFAULT};
+
+// Enables the touch to fill feature for Android.
+const base::Feature kTouchToFillAndroid = {"TouchToFillAndroid",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Field trial identifier for password generation requirements.
 const char* kGenerationRequirementsFieldTrial =
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h
index 8082461..5e1395ee 100644
--- a/components/password_manager/core/common/password_manager_features.h
+++ b/components/password_manager/core/common/password_manager_features.h
@@ -30,6 +30,7 @@
 extern const base::Feature kPasswordImport;
 extern const base::Feature kPasswordsKeyboardAccessory;
 extern const base::Feature kRecoverPasswordsForSyncUsers;
+extern const base::Feature kTouchToFillAndroid;
 
 // Field trial and corresponding parameters.
 // To manually override this, start Chrome with the following parameters:
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 2634d5b..c8f5263 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -598,6 +598,7 @@
         'SpokenFeedbackEnabled',
         'HighContrastEnabled',
         'VirtualKeyboardEnabled',
+        'StickyKeysEnabled',
         'KeyboardDefaultToFunctionKeys',
         'ScreenMagnifierType',
         'DeviceLoginScreenDefaultLargeCursorEnabled',
@@ -9209,6 +9210,30 @@
           If this policy is left unset, the on-screen keyboard is disabled initially but can be enabled by the user anytime.'''
     },
     {
+      'name': 'StickyKeysEnabled',
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'supported_on': ['chrome_os:76-'],
+      'features': {
+        'can_be_recommended': True,
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'example_value': True,
+      'id': 561,
+      'caption': '''Enable sticky keys''',
+      'tags': [],
+      'desc': '''Enable the sticky keys accessibility feature.
+
+          If this policy is set to true, the sticky keys will always be enabled.
+
+          If this policy is set to false, the sticky keys will always be disabled.
+
+          If you set this policy, users cannot change or override it.
+
+          If this policy is left unset, the sticky keys is disabled initially but can be enabled by the user anytime.'''
+    },
+    {
       'name': 'KeyboardDefaultToFunctionKeys',
       'type': 'main',
       'schema': { 'type': 'boolean' },
@@ -16188,5 +16213,5 @@
   },
   'placeholders': [],
   'deleted_policy_ids': [412],
-  'highest_id_currently_used':  560
+  'highest_id_currently_used':  561
 }
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index 462981bb..0752ca8 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -80,6 +80,10 @@
     "gaia_cookie_manager_service.h",
     "oauth2_token_service_delegate_android.cc",
     "oauth2_token_service_delegate_android.h",
+    "oauth_multilogin_helper.cc",
+    "oauth_multilogin_helper.h",
+    "oauth_multilogin_token_fetcher.cc",
+    "oauth_multilogin_token_fetcher.h",
     "profile_oauth2_token_service.cc",
     "profile_oauth2_token_service.h",
     "profile_oauth2_token_service_delegate_chromeos.cc",
@@ -274,9 +278,10 @@
     "device_id_helper_unittest.cc",
     "dice_account_reconcilor_delegate_unittest.cc",
     "gaia_cookie_manager_service_unittest.cc",
-    "identity_utils_unittest.cc",
     "mice_account_reconcilor_delegate_unittest.cc",
     "mutable_profile_oauth2_token_service_delegate_unittest.cc",
+    "oauth_multilogin_helper_unittest.cc",
+    "oauth_multilogin_token_fetcher_unittest.cc",
     "profile_oauth2_token_service_delegate_chromeos_unittest.cc",
     "signin_error_controller_unittest.cc",
     "signin_header_helper_unittest.cc",
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.cc b/components/signin/core/browser/gaia_cookie_manager_service.cc
index ff0011b..ba401e6 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -10,6 +10,8 @@
 #include <set>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
 #include "base/json/json_reader.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
@@ -27,7 +29,6 @@
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/oauth2_token_service.h"
-#include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/cookies/cookie_change_dispatcher.h"
@@ -108,20 +109,10 @@
                             GoogleServiceAuthError::NUM_STATES);
 }
 
-void RecordGetAccessTokenFinished(GoogleServiceAuthError error) {
-  UMA_HISTOGRAM_ENUMERATION("Signin.GetAccessTokenFinished", error.state(),
-                            GoogleServiceAuthError::NUM_STATES);
-}
-
 void RecordLogoutRequestState(LogoutRequestState logout_state) {
   UMA_HISTOGRAM_ENUMERATION("Signin.GaiaCookieManager.Logout", logout_state);
 }
 
-void RecordMultiloginFinished(GoogleServiceAuthError error) {
-  UMA_HISTOGRAM_ENUMERATION("Signin.MultiloginFinished", error.state(),
-                            GoogleServiceAuthError::NUM_STATES);
-}
-
 // Record ListAccounts errors for individual retries.
 void RecordListAccountsRetryResult(GoogleServiceAuthError error,
                                    int retry_attempt_number) {
@@ -474,8 +465,7 @@
     SigninClient* signin_client,
     base::RepeatingCallback<scoped_refptr<network::SharedURLLoaderFactory>()>
         shared_url_loader_factory_getter)
-    : OAuth2TokenService::Consumer("gaia_cookie_manager"),
-      token_service_(token_service),
+    : token_service_(token_service),
       signin_client_(signin_client),
       shared_url_loader_factory_getter_(shared_url_loader_factory_getter),
       external_cc_result_fetcher_(this),
@@ -532,33 +522,14 @@
   }
   if (requests_.size() == 1) {
     fetcher_retries_ = 0;
-    signin_client_->DelayNetworkCall(base::BindOnce(
-        &GaiaCookieManagerService::StartFetchingAccessTokensForMultilogin,
-        base::Unretained(this)));
+    // Unretained is safe because GaiaCookieManagerService owns the helper.
+    oauth_multilogin_helper_ = std::make_unique<signin::OAuthMultiloginHelper>(
+        signin_client_, token_service_, account_ids,
+        base::BindOnce(&GaiaCookieManagerService::OnSetAccountsFinished,
+                       base::Unretained(this)));
   }
 }
 
-void GaiaCookieManagerService::SetAccountsInCookieWithTokens() {
-#ifndef NDEBUG
-  // Check that there is no duplicate accounts.
-  std::set<std::string> accounts_no_duplicates(
-      requests_.front().account_ids().begin(),
-      requests_.front().account_ids().end());
-  DCHECK_EQ(requests_.front().account_ids().size(),
-            accounts_no_duplicates.size());
-#endif
-
-  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts =
-      std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>();
-  accounts.reserve(requests_.front().account_ids().size());
-  int i = 0;
-  for (const std::string& account_id : requests_.front().account_ids()) {
-    accounts.emplace_back(account_id, access_tokens_[account_id]);
-    ++i;
-  }
-  StartFetchingMultiLogin(accounts);
-}
-
 void GaiaCookieManagerService::AddAccountToCookieInternal(
     const std::string& account_id,
     gaia::GaiaSource source,
@@ -577,7 +548,7 @@
   if (requests_.size() == 1) {
     signin_client_->DelayNetworkCall(
         base::BindOnce(&GaiaCookieManagerService::StartFetchingUbertoken,
-                       base::Unretained(this)));
+                       weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
@@ -629,7 +600,7 @@
     requests_.push_back(GaiaCookieRequest::CreateListAccountsRequest());
     signin_client_->DelayNetworkCall(
         base::BindOnce(&GaiaCookieManagerService::StartFetchingListAccounts,
-                       base::Unretained(this)));
+                       weak_ptr_factory_.GetWeakPtr()));
   } else if (std::find_if(requests_.begin(), requests_.end(),
                           [](const GaiaCookieRequest& request) {
                             return request.request_type() == LIST_ACCOUNTS;
@@ -691,8 +662,9 @@
     requests_.push_back(GaiaCookieRequest::CreateLogOutRequest(source));
     if (requests_.size() == 1) {
       fetcher_retries_ = 0;
-      signin_client_->DelayNetworkCall(base::BindOnce(
-          &GaiaCookieManagerService::StartGaiaLogOut, base::Unretained(this)));
+      signin_client_->DelayNetworkCall(
+          base::BindOnce(&GaiaCookieManagerService::StartGaiaLogOut,
+                         weak_ptr_factory_.GetWeakPtr()));
     }
   }
 }
@@ -709,6 +681,7 @@
   VLOG(1) << "GaiaCookieManagerService::CancelAll";
   gaia_auth_fetcher_.reset();
   uber_token_fetcher_.reset();
+  oauth_multilogin_helper_.reset();
   requests_.clear();
   fetcher_timer_.Stop();
 }
@@ -752,7 +725,7 @@
     fetcher_retries_ = 0;
     signin_client_->DelayNetworkCall(
         base::BindOnce(&GaiaCookieManagerService::StartFetchingListAccounts,
-                       base::Unretained(this)));
+                       weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
@@ -814,54 +787,7 @@
 
   signin_client_->DelayNetworkCall(
       base::BindOnce(&GaiaCookieManagerService::StartFetchingMergeSession,
-                     base::Unretained(this)));
-}
-
-void GaiaCookieManagerService::OnTokenFetched(const std::string& account_id,
-                                              const std::string& token) {
-  access_tokens_.insert(std::make_pair(account_id, token));
-  if (access_tokens_.size() == requests_.front().account_ids().size()) {
-    fetcher_retries_ = 0;
-    token_requests_.clear();
-    signin_client_->DelayNetworkCall(
-        base::BindOnce(&GaiaCookieManagerService::SetAccountsInCookieWithTokens,
-                       base::Unretained(this)));
-  }
-}
-
-void GaiaCookieManagerService::OnGetTokenSuccess(
-    const OAuth2TokenService::Request* request,
-    const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
-  DCHECK(requests_.front().request_type() ==
-         GaiaCookieRequestType::SET_ACCOUNTS);
-  fetcher_backoff_.InformOfRequest(true);
-  OnTokenFetched(request->GetAccountId(), token_response.access_token);
-}
-
-void GaiaCookieManagerService::OnGetTokenFailure(
-    const OAuth2TokenService::Request* request,
-    const GoogleServiceAuthError& error) {
-  VLOG(1) << "Failed to retrieve accesstoken"
-          << " account=" << request->GetAccountId()
-          << " error=" << error.ToString();
-  if (++fetcher_retries_ < signin::kMaxFetcherRetries &&
-      error.IsTransientError()) {
-    fetcher_backoff_.InformOfRequest(false);
-    UMA_HISTOGRAM_ENUMERATION("Signin.GetAccessTokenRetry", error.state(),
-                              GoogleServiceAuthError::NUM_STATES);
-    OAuth2TokenService::ScopeSet scopes;
-    scopes.insert(GaiaConstants::kOAuth1LoginScope);
-    fetcher_timer_.Start(
-        FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(),
-        base::BindOnce(
-            &SigninClient::DelayNetworkCall, base::Unretained(signin_client_),
-            base::BindOnce(&GaiaCookieManagerService::
-                               StartFetchingAccessTokenForMultilogin,
-                           base::Unretained(this), request->GetAccountId())));
-    return;
-  }
-  RecordGetAccessTokenFinished(error);
-  OnSetAccountsFinished(error);
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void GaiaCookieManagerService::OnMergeSessionSuccess(const std::string& data) {
@@ -896,7 +822,7 @@
         base::BindOnce(
             &SigninClient::DelayNetworkCall, base::Unretained(signin_client_),
             base::BindOnce(&GaiaCookieManagerService::StartFetchingMergeSession,
-                           base::Unretained(this))));
+                           weak_ptr_factory_.GetWeakPtr())));
     return;
   }
 
@@ -908,53 +834,6 @@
   HandleNextRequest();
 }
 
-void GaiaCookieManagerService::OnOAuthMultiloginFinished(
-    const OAuthMultiloginResult& result) {
-  DCHECK(requests_.front().request_type() ==
-         GaiaCookieRequestType::SET_ACCOUNTS);
-  RecordMultiloginFinished(result.error());
-  if (result.error().state() == GoogleServiceAuthError::NONE) {
-    VLOG(1) << "Multilogin successful accounts="
-            << base::JoinString(requests_.front().account_ids(), " ");
-    std::vector<std::string> account_ids = requests_.front().account_ids();
-    access_tokens_.clear();
-    fetcher_backoff_.InformOfRequest(true);
-    StartSettingCookies(result);
-    return;
-  }
-  if (++fetcher_retries_ < signin::kMaxFetcherRetries &&
-      result.error().IsTransientError()) {
-    UMA_HISTOGRAM_ENUMERATION("Signin.MultiloginRetry", result.error().state(),
-                              GoogleServiceAuthError::NUM_STATES);
-    fetcher_backoff_.InformOfRequest(false);
-    fetcher_timer_.Start(
-        FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(),
-        base::BindOnce(
-            &SigninClient::DelayNetworkCall, base::Unretained(signin_client_),
-            base::BindOnce(
-                &GaiaCookieManagerService::SetAccountsInCookieWithTokens,
-                base::Unretained(this))));
-    return;
-  }
-  // If Gaia responded with status: "INVALID_TOKENS", we have to mark tokens as
-  // invalid.
-  if (result.error().state() ==
-      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) {
-    for (const std::string& account_id : result.failed_accounts()) {
-      DCHECK(base::ContainsKey(access_tokens_, account_id));
-      token_service_->InvalidateTokenForMultilogin(account_id,
-                                                   access_tokens_[account_id]);
-      access_tokens_.erase(account_id);
-    }
-    for (const std::string& account_id : result.failed_accounts()) {
-      // Maybe the access token was expired, try to get a new one.
-      StartFetchingAccessTokenForMultilogin(account_id);
-    }
-    return;
-  }
-  OnSetAccountsFinished(result.error());
-}
-
 void GaiaCookieManagerService::OnListAccountsSuccess(const std::string& data) {
   VLOG(1) << "ListAccounts successful";
   DCHECK(requests_.front().request_type() ==
@@ -1012,7 +891,7 @@
         base::BindOnce(
             &SigninClient::DelayNetworkCall, base::Unretained(signin_client_),
             base::BindOnce(&GaiaCookieManagerService::StartFetchingListAccounts,
-                           base::Unretained(this))));
+                           weak_ptr_factory_.GetWeakPtr())));
     return;
   }
 
@@ -1044,33 +923,16 @@
     fetcher_backoff_.InformOfRequest(false);
     fetcher_timer_.Start(
         FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(),
-        base::BindOnce(&SigninClient::DelayNetworkCall,
-                       base::Unretained(signin_client_),
-                       base::Bind(&GaiaCookieManagerService::StartGaiaLogOut,
-                                  base::Unretained(this))));
+        base::BindOnce(
+            &SigninClient::DelayNetworkCall, base::Unretained(signin_client_),
+            base::BindOnce(&GaiaCookieManagerService::StartGaiaLogOut,
+                           weak_ptr_factory_.GetWeakPtr())));
     return;
   }
 
   HandleNextRequest();
 }
 
-void GaiaCookieManagerService::StartFetchingAccessTokenForMultilogin(
-    const std::string& account_id) {
-  token_requests_.push_back(
-      token_service_->StartRequestForMultilogin(account_id, this));
-}
-
-void GaiaCookieManagerService::StartFetchingAccessTokensForMultilogin() {
-  DCHECK_EQ(SET_ACCOUNTS, requests_.front().request_type());
-  VLOG(1) << "GaiaCookieManagerService::StartFetchingAccessToken account_id ="
-          << base::JoinString(requests_.front().account_ids(), " ");
-  token_requests_.clear();
-  access_tokens_.clear();
-  for (const std::string& account_id : requests_.front().account_ids()) {
-    StartFetchingAccessTokenForMultilogin(account_id);
-  }
-}
-
 void GaiaCookieManagerService::StartFetchingUbertoken() {
   const std::string account_id = requests_.front().GetAccountID();
   VLOG(1) << "GaiaCookieManagerService::StartFetchingUbertoken account_id="
@@ -1089,14 +951,6 @@
           base::Unretained(signin_client_), GetURLLoaderFactory()));
 }
 
-void GaiaCookieManagerService::StartFetchingMultiLogin(
-    const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& accounts) {
-  gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(
-      this, requests_.front().source(), GetURLLoaderFactory());
-
-  gaia_auth_fetcher_->StartOAuthMultilogin(accounts);
-}
-
 void GaiaCookieManagerService::StartFetchingMergeSession() {
   DCHECK(!uber_token_.empty());
   gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(
@@ -1110,8 +964,9 @@
   DCHECK(requests_.front().request_type() == GaiaCookieRequestType::LOG_OUT);
   VLOG(1) << "GaiaCookieManagerService::StartGaiaLogOut";
 
-  signin_client_->PreGaiaLogout(base::BindOnce(
-      &GaiaCookieManagerService::StartFetchingLogOut, base::Unretained(this)));
+  signin_client_->PreGaiaLogout(
+      base::BindOnce(&GaiaCookieManagerService::StartFetchingLogOut,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void GaiaCookieManagerService::StartFetchingLogOut() {
@@ -1131,65 +986,10 @@
 void GaiaCookieManagerService::OnSetAccountsFinished(
     const GoogleServiceAuthError& error) {
   MarkListAccountsStale();
-  access_tokens_.clear();
-  token_requests_.clear();
-  cookies_to_set_.clear();
   SignalSetAccountsComplete(error);
   HandleNextRequest();
 }
 
-void GaiaCookieManagerService::OnCookieSet(
-    const std::string& cookie_name,
-    const std::string& cookie_domain,
-    net::CanonicalCookie::CookieInclusionStatus status) {
-  cookies_to_set_.erase(std::make_pair(cookie_name, cookie_domain));
-  bool success =
-      (status == net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
-  if (!success) {
-    VLOG(1) << "Failed to set cookie " << cookie_name
-            << " for domain=" << cookie_domain << ".";
-  }
-  UMA_HISTOGRAM_BOOLEAN("Signin.SetCookieSuccess", success);
-  if (cookies_to_set_.empty())
-    OnSetAccountsFinished(GoogleServiceAuthError::AuthErrorNone());
-}
-
-void GaiaCookieManagerService::StartSettingCookies(
-    const OAuthMultiloginResult& result) {
-  network::mojom::CookieManager* cookie_manager =
-      signin_client_->GetCookieManager();
-
-  DCHECK(cookies_to_set_.empty());
-
-  const std::vector<net::CanonicalCookie>& cookies = result.cookies();
-
-  for (const net::CanonicalCookie& cookie : cookies) {
-    cookies_to_set_.insert(std::make_pair(cookie.Name(), cookie.Domain()));
-  }
-  for (const net::CanonicalCookie& cookie : cookies) {
-    if (cookies_to_set_.find(std::make_pair(cookie.Name(), cookie.Domain())) !=
-        cookies_to_set_.end()) {
-      base::OnceCallback<void(net::CanonicalCookie::CookieInclusionStatus)>
-          callback = base::BindOnce(&GaiaCookieManagerService::OnCookieSet,
-                                    weak_ptr_factory_.GetWeakPtr(),
-                                    cookie.Name(), cookie.Domain());
-      net::CookieOptions options;
-      options.set_include_httponly();
-      // Permit it to set a SameSite cookie if it wants to.
-      options.set_same_site_cookie_context(
-          net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
-      cookie_manager->SetCanonicalCookie(
-          cookie, "https", options,
-          mojo::WrapCallbackWithDefaultInvokeIfNotRun(
-              std::move(callback), net::CanonicalCookie::CookieInclusionStatus::
-                                       EXCLUDE_UNKNOWN_ERROR));
-    } else {
-      LOG(ERROR) << "Duplicate cookie found: " << cookie.Name() << " "
-                 << cookie.Domain();
-    }
-  }
-}
-
 void GaiaCookieManagerService::HandleNextRequest() {
   VLOG(1) << "GaiaCookieManagerService::HandleNextRequest";
   if (requests_.front().request_type() ==
@@ -1216,26 +1016,28 @@
         DCHECK_EQ(1u, requests_.front().account_ids().size());
         signin_client_->DelayNetworkCall(
             base::BindOnce(&GaiaCookieManagerService::StartFetchingUbertoken,
-                           base::Unretained(this)));
+                           weak_ptr_factory_.GetWeakPtr()));
         break;
       case GaiaCookieRequestType::SET_ACCOUNTS:
         DCHECK(!requests_.front().account_ids().empty());
-        signin_client_->DelayNetworkCall(base::BindOnce(
-            &GaiaCookieManagerService::StartFetchingAccessTokensForMultilogin,
-            base::Unretained(this)));
+        oauth_multilogin_helper_ =
+            std::make_unique<signin::OAuthMultiloginHelper>(
+                signin_client_, token_service_, requests_.front().account_ids(),
+                base::BindOnce(&GaiaCookieManagerService::OnSetAccountsFinished,
+                               weak_ptr_factory_.GetWeakPtr()));
         break;
       case GaiaCookieRequestType::LOG_OUT:
         DCHECK(requests_.front().account_ids().empty());
         signin_client_->DelayNetworkCall(
             base::BindOnce(&GaiaCookieManagerService::StartGaiaLogOut,
-                           base::Unretained(this)));
+                           weak_ptr_factory_.GetWeakPtr()));
         break;
       case GaiaCookieRequestType::LIST_ACCOUNTS:
         DCHECK(requests_.front().account_ids().empty());
         uber_token_fetcher_.reset();
         signin_client_->DelayNetworkCall(
             base::BindOnce(&GaiaCookieManagerService::StartFetchingListAccounts,
-                           base::Unretained(this)));
+                           weak_ptr_factory_.GetWeakPtr()));
         break;
     }
   }
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.h b/components/signin/core/browser/gaia_cookie_manager_service.h
index 854d00a..832cf204 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.h
+++ b/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -12,18 +12,18 @@
 #include <utility>
 #include <vector>
 
-#include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/containers/circular_deque.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/timer/timer.h"
+#include "components/signin/core/browser/oauth_multilogin_helper.h"
 #include "components/signin/core/browser/signin_client.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
 #include "google_apis/gaia/gaia_auth_fetcher.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/oauth2_token_service.h"
-#include "google_apis/gaia/oauth_multilogin_result.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/base/backoff_entry.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
@@ -70,8 +70,7 @@
 // GAIA cookie are blocked (such as youtube). This is executed once for the
 // lifetime of this object, when the first call is made to AddAccountToCookie.
 class GaiaCookieManagerService : public GaiaAuthConsumer,
-                                 public network::mojom::CookieChangeListener,
-                                 public OAuth2TokenService::Consumer {
+                                 public network::mojom::CookieChangeListener {
  public:
   enum GaiaCookieRequestType {
     ADD_ACCOUNT,
@@ -264,11 +263,6 @@
                            SetAccountsInCookieCompletedCallback
                                set_accounts_in_cookies_completed_callback);
 
-  // Takes list of account_ids from the front request, matches them with a
-  // corresponding stored access_token and calls StartMultilogin.
-  // Virtual for testing purposes.
-  virtual void SetAccountsInCookieWithTokens();
-
   // Returns if the listed accounts are up to date or not. The out parameter
   // will be assigned the current cached accounts (whether they are not up to
   // date or not). If the accounts are not up to date, a ListAccounts fetch is
@@ -319,20 +313,10 @@
   void OnUbertokenFetchComplete(GoogleServiceAuthError error,
                                 const std::string& uber_token);
 
- private:
-  FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
-                           MultiloginSuccessAndCookiesSet);
-  FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
-                           MultiloginFailurePersistentError);
-  FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
-                           MultiloginFailureMaxRetriesReached);
-  FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
-                           FetcherRetriesZeroedBetweenCalls);
-  FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
-                           MultiloginFailureInvalidGaiaCredentialsMobile);
-  FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
-                           MultiloginFailureInvalidGaiaCredentialsDesktop);
+  // Final call in the Setting accounts in cookie procedure. Public for testing.
+  void OnSetAccountsFinished(const GoogleServiceAuthError& error);
 
+ private:
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
 
   // Calls the AddAccountToCookie completion callback.
@@ -351,57 +335,24 @@
                       network::mojom::CookieChangeCause cause) override;
   void OnCookieListenerConnectionError();
 
-  // Overridden from OAuth2TokenService::Consumer.
-  void OnGetTokenSuccess(
-      const OAuth2TokenService::Request* request,
-      const OAuth2AccessTokenConsumer::TokenResponse& token_response) override;
-  void OnGetTokenFailure(const OAuth2TokenService::Request* request,
-                         const GoogleServiceAuthError& error) override;
-  // Called when either refresh or access token becomes available.
-  void OnTokenFetched(const std::string& account_id, const std::string& token);
-
   // Overridden from GaiaAuthConsumer.
   void OnMergeSessionSuccess(const std::string& data) override;
   void OnMergeSessionFailure(const GoogleServiceAuthError& error) override;
-  void OnOAuthMultiloginFinished(const OAuthMultiloginResult& result) override;
   void OnListAccountsSuccess(const std::string& data) override;
   void OnListAccountsFailure(const GoogleServiceAuthError& error) override;
   void OnLogOutSuccess() override;
   void OnLogOutFailure(const GoogleServiceAuthError& error) override;
 
-  // Callback for CookieManager::SetCanonicalCookie.
-  void OnCookieSet(const std::string& cookie_name,
-                   const std::string& cookie_domain,
-                   net::CanonicalCookie::CookieInclusionStatus status);
-
-  // Final call in the Setting accounts in cookie procedure. Virtual for testing
-  // purposes.
-  virtual void OnSetAccountsFinished(const GoogleServiceAuthError& error);
-
   // Helper method for AddAccountToCookie* methods.
   void AddAccountToCookieInternal(
       const std::string& account_id,
       gaia::GaiaSource source,
       AddAccountToCookieCompletedCallback completion_callback);
 
-  // Helper function to trigger fetching retry in case of failure for only
-  // failed account id. Virtual for testing purposes.
-  virtual void StartFetchingAccessTokenForMultilogin(
-      const std::string& account_id);
-
-  // Starts the process of fetching the access token with OauthLogin scope and
-  // performing SetAccountsInCookie on success.  Virtual so that it can be
-  // overridden in tests.
-  virtual void StartFetchingAccessTokensForMultilogin();
-
   // Starts the proess of fetching the uber token and performing a merge session
   // for the next account.  Virtual so that it can be overriden in tests.
   virtual void StartFetchingUbertoken();
 
-  // Starts the process of setting accounts in cookie.
-  void StartFetchingMultiLogin(
-      const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& accounts);
-
   // Virtual for testing purposes.
   virtual void StartFetchingMergeSession();
 
@@ -415,9 +366,6 @@
   // Virtual for testing purpose.
   virtual void StartFetchingLogOut();
 
-  // Starts setting parsed cookies in browser.
-  void StartSettingCookies(const OAuthMultiloginResult& result);
-
   // Start the next request, if needed.
   void HandleNextRequest();
 
@@ -429,6 +377,7 @@
   std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_;
   std::unique_ptr<signin::UbertokenFetcherImpl> uber_token_fetcher_;
   ExternalCcResultFetcher external_cc_result_fetcher_;
+  std::unique_ptr<signin::OAuthMultiloginHelper> oauth_multilogin_helper_;
 
   // If the GaiaAuthFetcher or SimpleURLLoader fails, retry with exponential
   // backoff and network delay.
@@ -439,21 +388,9 @@
   // The last fetched ubertoken, for use in MergeSession retries.
   std::string uber_token_;
 
-  // Access tokens for use inside SetAccountsToCookie.
-  // TODO (valeriyas): make FetchUberToken use those instead of a separate
-  // access_token.
-  std::unordered_map<std::string, std::string> access_tokens_;
-
-  // Current list of processed token requests;
-  std::vector<std::unique_ptr<OAuth2TokenService::Request>> token_requests_;
-
   // The access token that can be used to prime the UberToken fetch.
   std::string access_token_;
 
-  // List of pairs (cookie name and cookie domain) that have to be set in
-  // cookie jar.
-  std::set<std::pair<std::string, std::string>> cookies_to_set_;
-
   // Connection to the CookieManager that signals when the GAIA cookies change.
   mojo::Binding<network::mojom::CookieChangeListener> cookie_listener_binding_;
 
diff --git a/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc b/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
index 04b96f42..727be0c5 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
@@ -110,11 +110,6 @@
   MOCK_METHOD0(StartFetchingListAccounts, void());
   MOCK_METHOD0(StartFetchingLogOut, void());
   MOCK_METHOD0(StartFetchingMergeSession, void());
-  MOCK_METHOD1(StartFetchingAccessTokenForMultilogin,
-               void(const std::string& account_id));
-  MOCK_METHOD0(SetAccountsInCookieWithTokens, void());
-  MOCK_METHOD1(OnSetAccountsFinished,
-               void(const GoogleServiceAuthError& error));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(InstrumentedGaiaCookieManagerService);
@@ -125,18 +120,7 @@
   GaiaCookieManagerServiceTest()
       : no_error_(GoogleServiceAuthError::NONE),
         error_(GoogleServiceAuthError::SERVICE_ERROR),
-        canceled_(GoogleServiceAuthError::REQUEST_CANCELED) {}
-
-  class RequestMockImpl : public OAuth2TokenService::Request {
-   public:
-    RequestMockImpl(std::string account_id) { account_id_ = account_id; }
-    std::string GetAccountId() const override { return account_id_; }
-
-   private:
-    std::string account_id_;
-  };
-
-  void SetUp() override {
+        canceled_(GoogleServiceAuthError::REQUEST_CANCELED) {
     AccountTrackerService::RegisterPrefs(pref_service_.registry());
     signin_client_.reset(new TestSigninClient(&pref_service_));
   }
@@ -366,549 +350,6 @@
   SimulateUbertokenFailure(&helper, error());
 }
 
-TEST_F(GaiaCookieManagerServiceTest, AccessTokenSuccess) {
-  InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
-  MockObserver observer(&helper);
-
-  auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
-  base::ScopedClosureRunner task_runner_ =
-      base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner);
-
-  const std::string account_id1 = "12345";
-  const std::string account_id2 = "23456";
-
-  testing::InSequence mock_sequence;
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2))
-      .Times(1);
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-  EXPECT_CALL(helper, SetAccountsInCookieWithTokens());
-
-  const std::vector<std::string> account_ids = {account_id1, account_id2};
-
-  helper.SetAccountsInCookie(
-      account_ids, gaia::GaiaSource::kChrome,
-      GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
-
-  RequestMockImpl request1(account_id1);
-  RequestMockImpl request2(account_id2);
-
-  SimulateAccessTokenSuccess(&helper, &request2);
-
-  GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE);
-
-  // Transient error, retry.
-  SimulateAccessTokenFailure(&helper, &request1, error);
-
-  DCHECK(helper.is_running());
-  Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease());
-
-  SimulateAccessTokenSuccess(&helper, &request1);
-}
-
-TEST_F(GaiaCookieManagerServiceTest,
-       AccessTokenFailureTransientErrorMaxRetriesReached) {
-  InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
-  MockObserver observer(&helper);
-
-  auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
-  base::ScopedClosureRunner task_runner_ =
-      base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner);
-
-  const std::string account_id1 = "12345";
-  const std::string account_id2 = "23456";
-
-  GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE);
-
-  testing::InSequence mock_sequence;
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2))
-      .Times(1);
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(signin::kMaxFetcherRetries - 1);
-  EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1);
-  EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(0);
-
-  const std::vector<std::string> account_ids = {account_id1, account_id2};
-
-  helper.SetAccountsInCookie(
-      account_ids, gaia::GaiaSource::kChrome,
-      GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
-
-  RequestMockImpl request1(account_id1);
-  RequestMockImpl request2(account_id2);
-
-  EXPECT_LT(helper.GetBackoffEntry()->GetTimeUntilRelease(),
-            base::TimeDelta::FromMilliseconds(1100));
-
-  // Transient error, retry, fail when maximum number of retries is reached.
-  for (int i = 0; i < signin::kMaxFetcherRetries - 1; ++i) {
-    SimulateAccessTokenFailure(&helper, &request1, error);
-    Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease());
-  }
-  SimulateAccessTokenFailure(&helper, &request1, error);
-  // Check that no Multilogin is triggered.
-  SimulateAccessTokenSuccess(&helper, &request2);
-}
-
-TEST_F(GaiaCookieManagerServiceTest, AccessTokenFailurePersistentError) {
-  InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
-  MockObserver observer(&helper);
-
-  const std::string account_id1 = "12345";
-  const std::string account_id2 = "23456";
-
-  GoogleServiceAuthError error(
-      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
-
-  testing::InSequence mock_sequence;
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2))
-      .Times(1);
-  EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1);
-  EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(0);
-
-  const std::vector<std::string> account_ids = {account_id1, account_id2};
-
-  helper.SetAccountsInCookie(
-      account_ids, gaia::GaiaSource::kChrome,
-      GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
-
-  RequestMockImpl request1(account_id1);
-  RequestMockImpl request2(account_id2);
-
-  SimulateAccessTokenFailure(&helper, &request1, error);
-
-  // Check that no Multilogin is triggered.
-  SimulateAccessTokenSuccess(&helper, &request2);
-}
-
-TEST_F(GaiaCookieManagerServiceTest, FetcherRetriesZeroedBetweenCalls) {
-  InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
-  MockObserver observer(&helper);
-
-  auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
-  base::ScopedClosureRunner task_runner_ =
-      base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner);
-
-  const std::string account_id1 = "12345";
-  const std::string account_id2 = "23456";
-
-  GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE);
-
-  std::string data =
-      R"()]}'
-        {
-          "status": "OK",
-          "cookies":[
-          {
-              "name":"SID",
-              "value":"vAlUe1",
-              "domain":".google.ru",
-              "path":"/",
-              "isSecure":true,
-              "isHttpOnly":false,
-              "priority":"HIGH",
-              "maxAge":63070000
-            }
-          ]
-        }
-      )";
-  OAuthMultiloginResult result(data);
-  ASSERT_EQ(result.error().state(), GoogleServiceAuthError::State::NONE);
-
-  testing::InSequence mock_sequence;
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2))
-      .Times(1);
-  // retry call
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(signin::kMaxFetcherRetries - 1);
-  // retry call
-  EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1);
-  EXPECT_CALL(helper,
-              OnSetAccountsFinished(GoogleServiceAuthError::AuthErrorNone()))
-      .Times(1);
-
-  const std::vector<std::string> account_ids = {account_id1, account_id2};
-
-  helper.SetAccountsInCookie(
-      account_ids, gaia::GaiaSource::kChrome,
-      GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
-
-  RequestMockImpl request1(account_id1);
-  RequestMockImpl request2(account_id2);
-  // Transient error, retry.
-  // Succeed when only one retry is left. Simulate Multilogin failure. Check
-  // that it retries.
-  for (int i = 0; i < signin::kMaxFetcherRetries - 1; ++i) {
-    SimulateAccessTokenFailure(&helper, &request1, error);
-    Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease());
-  }
-  SimulateAccessTokenSuccess(&helper, &request1);
-  SimulateAccessTokenSuccess(&helper, &request2);
-
-  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts =
-      std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>();
-  accounts.emplace_back(account_id1, "AccessToken");
-  accounts.emplace_back(account_id2, "AccessToken");
-
-  helper.StartFetchingMultiLogin(accounts);
-  SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error));
-  SimulateMultiloginFinished(&helper, result);
-}
-
-TEST_F(GaiaCookieManagerServiceTest, MultiloginSuccessAndCookiesSet) {
-  InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
-  MockObserver observer(&helper);
-
-  auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
-  base::ScopedClosureRunner task_runner_ =
-      base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner);
-
-  const std::string account_id1 = "12345";
-  const std::string account_id2 = "23456";
-  const std::vector<std::string> account_ids = {account_id1, account_id2};
-
-  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts =
-      std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>();
-  accounts.emplace_back(account_id1, "AccessToken");
-  accounts.emplace_back(account_id2, "AccessToken");
-
-  GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE);
-
-  std::string data =
-      R"()]}'
-        {
-          "status": "OK",
-          "cookies":[
-            {
-              "name":"SID",
-              "value":"vAlUe1",
-              "domain":".google.ru",
-              "path":"/",
-              "isSecure":true,
-              "isHttpOnly":false,
-              "priority":"HIGH",
-              "maxAge":63070000
-            },
-            {
-              "name":"SID",
-              "value":"vAlUe1",
-              "domain":".google.ru",
-              "path":"/",
-              "isSecure":true,
-              "isHttpOnly":false,
-              "priority":"HIGH",
-              "maxAge":63070000
-            },
-            {
-              "name":"HSID",
-              "value":"vAlUe4",
-              "host":"google.fr",
-              "path":"/",
-              "isSecure":true,
-              "isHttpOnly":false,
-              "priority":"HIGH",
-              "maxAge":0
-            }
-          ]
-        }
-      )";
-  OAuthMultiloginResult result(data);
-  ASSERT_EQ(result.error().state(), GoogleServiceAuthError::State::NONE);
-
-  testing::InSequence mock_sequence;
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2))
-      .Times(1);
-  EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1);
-  EXPECT_CALL(helper,
-              OnSetAccountsFinished(GoogleServiceAuthError::AuthErrorNone()))
-      .Times(1);
-
-  // Needed to insert request in the queue.
-  helper.SetAccountsInCookie(
-      account_ids, gaia::GaiaSource::kChrome,
-      GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
-
-  helper.StartFetchingMultiLogin(accounts);
-
-  SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error));
-
-  DCHECK(helper.is_running());
-  Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease());
-
-  SimulateMultiloginFinished(&helper, result);
-}
-
-TEST_F(GaiaCookieManagerServiceTest, MultiloginFailurePersistentError) {
-  InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
-  MockObserver observer(&helper);
-
-  const std::string account_id1 = "12345";
-  const std::string account_id2 = "23456";
-  const std::vector<std::string> account_ids = {account_id1, account_id2};
-
-  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts =
-      std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>();
-  accounts.emplace_back(account_id1, "AccessToken");
-  accounts.emplace_back(account_id2, "AccessToken");
-
-  GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_ERROR);
-
-  testing::InSequence mock_sequence;
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2))
-      .Times(1);
-  EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1);
-
-  // Needed to insert request in the queue.
-  helper.SetAccountsInCookie(
-      account_ids, gaia::GaiaSource::kChrome,
-      GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
-
-  helper.StartFetchingMultiLogin(accounts);
-
-  SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error));
-}
-
-TEST_F(GaiaCookieManagerServiceTest, MultiloginFailureMaxRetriesReached) {
-  InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
-  MockObserver observer(&helper);
-
-  auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
-  base::ScopedClosureRunner task_runner_ =
-      base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner);
-
-  const std::string account_id1 = "12345";
-  const std::string account_id2 = "23456";
-  const std::vector<std::string> account_ids = {account_id1, account_id2};
-
-  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts =
-      std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>();
-  accounts.emplace_back(account_id1, "AccessToken");
-  accounts.emplace_back(account_id2, "AccessToken");
-
-  GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE);
-
-  testing::InSequence mock_sequence;
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2))
-      .Times(1);
-  // This is the retry call, the first call is skipped as we call
-  // StartFetchingMultiLogim explicitly instead.
-  EXPECT_CALL(helper, SetAccountsInCookieWithTokens())
-      .Times(signin::kMaxFetcherRetries - 1);
-  EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1);
-
-  // Needed to insert request in the queue.
-  helper.SetAccountsInCookie(
-      account_ids, gaia::GaiaSource::kChrome,
-      GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
-
-  helper.StartFetchingMultiLogin(accounts);
-
-  // Transient error, retry, fail when maximum number of retries is reached.
-  for (int i = 0; i < signin::kMaxFetcherRetries - 1; ++i) {
-    SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error));
-    Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease());
-  }
-  SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error));
-}
-
-TEST_F(GaiaCookieManagerServiceTest,
-       MultiloginFailureInvalidGaiaCredentialsMobile) {
-  InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
-  MockObserver observer(&helper);
-
-  const std::string account_id1 = "12345";
-  const std::string account_id2 = "23456";
-  const std::vector<std::string> account_ids = {account_id1, account_id2};
-
-  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts =
-      std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>();
-  accounts.emplace_back(account_id1, "AccessToken");
-  accounts.emplace_back(account_id2, "AccessToken");
-
-  RequestMockImpl request1(account_id1);
-  RequestMockImpl request2(account_id2);
-
-  GoogleServiceAuthError error(
-      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
-
-  std::string data_ok =
-      R"()]}'
-        {
-          "status": "OK",
-          "cookies":[
-          {
-              "name":"SID",
-              "value":"vAlUe1",
-              "domain":".google.ru",
-              "path":"/",
-              "isSecure":true,
-              "isHttpOnly":false,
-              "priority":"HIGH",
-              "maxAge":63070000
-            }
-          ]
-        }
-      )";
-  OAuthMultiloginResult result_ok(data_ok);
-  ASSERT_EQ(result_ok.error().state(), GoogleServiceAuthError::State::NONE);
-
-  std::string data_failed =
-      R"()]}'
-      {
-        "status": "INVALID_TOKENS",
-        "failed_accounts": [
-          {
-            "obfuscated_id": "12345", "status": "RECOVERABLE"
-          },
-          {
-            "obfuscated_id": "23456", "status": "OK"
-          }
-        ]
-      }
-    )";
-  OAuthMultiloginResult result_failed(data_failed);
-  ASSERT_EQ(result_failed.error().state(),
-            GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS);
-
-  testing::InSequence mock_sequence;
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2))
-      .Times(1);
-  EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1);
-
-  // On mobile token_service is expected to retry getting access tokens first.
-  EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(0);
-
-  // Expect to try to refetch failed account one more time.
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-
-  // This time access tokens are supposed to be correct.
-  EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1);
-  EXPECT_CALL(helper,
-              OnSetAccountsFinished(GoogleServiceAuthError::AuthErrorNone()))
-      .Times(1);
-
-  // Needed to insert request in the queue.
-  helper.SetAccountsInCookie(
-      account_ids, gaia::GaiaSource::kChrome,
-      GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
-
-  // Both requests for access tokens are successful but they could be returned
-  // from cache and be stale.
-  SimulateAccessTokenSuccess(&helper, &request1);
-  SimulateAccessTokenSuccess(&helper, &request2);
-  // Both tokens are inserted in the map.
-  EXPECT_EQ(2u, helper.access_tokens_.size());
-
-  helper.StartFetchingMultiLogin(accounts);
-  // Access tokens were stale, Multilogin failed.
-  SimulateMultiloginFinished(&helper, result_failed);
-
-  // GaiaCookieManagerService should retry fetching access token again,
-  // it should be removed from the token map.
-  EXPECT_EQ(1u, helper.access_tokens_.size());
-  EXPECT_EQ(helper.access_tokens_.begin()->first, account_id2);
-
-  // This time access token is fresh.
-  SimulateAccessTokenSuccess(&helper, &request1);
-
-  EXPECT_EQ(2u, helper.access_tokens_.size());
-
-  // And Multilogin is successful.
-  SimulateMultiloginFinished(&helper, result_ok);
-
-  // The end.
-}
-
-TEST_F(GaiaCookieManagerServiceTest,
-       MultiloginFailureInvalidGaiaCredentialsDesktop) {
-  InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
-  MockObserver observer(&helper);
-
-  const std::string account_id1 = "12345";
-  const std::string account_id2 = "23456";
-  const std::vector<std::string> account_ids = {account_id1, account_id2};
-
-  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts =
-      std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>();
-  accounts.emplace_back(account_id1, "AccessToken");
-  accounts.emplace_back(account_id2, "AccessToken");
-
-  RequestMockImpl request1(account_id1);
-  RequestMockImpl request2(account_id2);
-
-  GoogleServiceAuthError error(
-      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
-
-  std::string data_failed =
-      R"()]}'
-      {
-        "status": "INVALID_TOKENS",
-        "failed_accounts": [
-          {
-            "obfuscated_id": "12345", "status": "RECOVERABLE"
-          },
-          {
-            "obfuscated_id": "23456", "status": "OK"
-          }
-        ]
-      }
-    )";
-  OAuthMultiloginResult result_failed(data_failed);
-  ASSERT_EQ(result_failed.error().state(),
-            GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS);
-
-  testing::InSequence mock_sequence;
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2))
-      .Times(1);
-  EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1);
-  // Expect to try to refetch failed account one more time.
-  EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1))
-      .Times(1);
-  // And fail right away.
-  EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(0);
-  EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1);
-
-  // Needed to insert request in the queue.
-  helper.SetAccountsInCookie(
-      account_ids, gaia::GaiaSource::kChrome,
-      GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
-
-  // Both requests for access tokens are successful but they could be returned
-  // from cache and be stale.
-  SimulateAccessTokenSuccess(&helper, &request1);
-  SimulateAccessTokenSuccess(&helper, &request2);
-  // Both tokens are inserted in the map.
-  EXPECT_EQ(2u, helper.access_tokens_.size());
-
-  helper.StartFetchingMultiLogin(accounts);
-
-  // On desktop refresh tokens were used and failed. Token service will retry
-  // fetching access token but refresh token is supposed to be set in error and
-  // request will return with immediate error.
-  SimulateMultiloginFinished(&helper, result_failed);
-
-  SimulateAccessTokenFailure(&helper, &request1, error);
-}
-
 TEST_F(GaiaCookieManagerServiceTest, ContinueAfterSuccess) {
   InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
   MockObserver observer(&helper);
diff --git a/components/signin/core/browser/oauth_multilogin_helper.cc b/components/signin/core/browser/oauth_multilogin_helper.cc
new file mode 100644
index 0000000..f01184e
--- /dev/null
+++ b/components/signin/core/browser/oauth_multilogin_helper.cc
@@ -0,0 +1,205 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/signin/core/browser/oauth_multilogin_helper.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "components/signin/core/browser/oauth_multilogin_token_fetcher.h"
+#include "components/signin/core/browser/signin_client.h"
+#include "google_apis/gaia/oauth_multilogin_result.h"
+#include "mojo/public/cpp/bindings/callback_helpers.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace {
+
+constexpr int kMaxFetcherRetries = 3;
+
+void RecordMultiloginFinished(GoogleServiceAuthError error) {
+  UMA_HISTOGRAM_ENUMERATION("Signin.MultiloginFinished", error.state(),
+                            GoogleServiceAuthError::NUM_STATES);
+}
+
+std::string FindTokenForAccount(
+    const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& token_id_pairs,
+    const std::string& account_id) {
+  for (auto it = token_id_pairs.cbegin(); it != token_id_pairs.cend(); ++it) {
+    if (account_id == it->gaia_id_)
+      return it->token_;
+  }
+  return std::string();
+}
+
+}  // namespace
+
+namespace signin {
+
+OAuthMultiloginHelper::OAuthMultiloginHelper(
+    SigninClient* signin_client,
+    OAuth2TokenService* token_service,
+    const std::vector<std::string>& account_ids,
+    base::OnceCallback<void(const GoogleServiceAuthError&)> callback)
+    : signin_client_(signin_client),
+      token_service_(token_service),
+      account_ids_(account_ids),
+      callback_(std::move(callback)),
+      weak_ptr_factory_(this) {
+  DCHECK(signin_client_);
+  DCHECK(token_service_);
+  DCHECK(!account_ids_.empty());
+  DCHECK(callback_);
+
+#ifndef NDEBUG
+  // Check that there is no duplicate accounts.
+  std::set<std::string> accounts_no_duplicates(account_ids_.begin(),
+                                               account_ids_.end());
+  DCHECK_EQ(account_ids_.size(), accounts_no_duplicates.size());
+#endif
+
+  StartFetchingTokens();
+}
+
+OAuthMultiloginHelper::~OAuthMultiloginHelper() = default;
+
+void OAuthMultiloginHelper::StartFetchingTokens() {
+  DCHECK(!token_fetcher_);
+  DCHECK(token_id_pairs_.empty());
+  token_fetcher_ = std::make_unique<signin::OAuthMultiloginTokenFetcher>(
+      signin_client_, token_service_, account_ids_,
+      base::BindOnce(&OAuthMultiloginHelper::OnAccessTokensSuccess,
+                     base::Unretained(this)),
+      base::BindOnce(&OAuthMultiloginHelper::OnAccessTokensFailure,
+                     base::Unretained(this)));
+}
+
+void OAuthMultiloginHelper::OnAccessTokensSuccess(
+    const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& token_id_pairs) {
+  DCHECK(token_id_pairs_.empty());
+  token_id_pairs_ = token_id_pairs;
+  DCHECK_EQ(token_id_pairs_.size(), account_ids_.size());
+  token_fetcher_.reset();
+
+  signin_client_->DelayNetworkCall(
+      base::BindOnce(&OAuthMultiloginHelper::StartFetchingMultiLogin,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void OAuthMultiloginHelper::OnAccessTokensFailure(
+    const GoogleServiceAuthError& error) {
+  // Copy the error because it is owned by token_fetcher_.
+  GoogleServiceAuthError error_copy = error;
+  token_fetcher_.reset();
+  std::move(callback_).Run(error_copy);
+  // Do not add anything below this line, because this may be deleted.
+}
+
+void OAuthMultiloginHelper::StartFetchingMultiLogin() {
+  DCHECK_EQ(token_id_pairs_.size(), account_ids_.size());
+  gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(
+      this, gaia::GaiaSource::kChrome, signin_client_->GetURLLoaderFactory());
+  gaia_auth_fetcher_->StartOAuthMultilogin(token_id_pairs_);
+}
+
+void OAuthMultiloginHelper::OnOAuthMultiloginFinished(
+    const OAuthMultiloginResult& result) {
+  RecordMultiloginFinished(result.error());
+
+  if (result.error().state() == GoogleServiceAuthError::NONE) {
+    VLOG(1) << "Multilogin successful accounts="
+            << base::JoinString(account_ids_, " ");
+    StartSettingCookies(result);
+    return;
+  }
+
+  // If Gaia responded with status: "INVALID_TOKENS", we have to mark tokens as
+  // invalid.
+  if (result.error().state() ==
+      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) {
+    for (const std::string& failed_account_id : result.failed_accounts()) {
+      std::string failed_token =
+          FindTokenForAccount(token_id_pairs_, failed_account_id);
+      if (failed_token.empty()) {
+        LOG(ERROR)
+            << "Unexpected failed token for account not present in request: "
+            << failed_account_id;
+        continue;
+      }
+      token_service_->InvalidateTokenForMultilogin(failed_account_id,
+                                                   failed_token);
+    }
+  }
+
+  // Maybe some access tokens were expired, try to get new ones.
+  bool is_transient_error =
+      result.error().IsTransientError() || !result.failed_accounts().empty();
+
+  if (is_transient_error && ++fetcher_retries_ < kMaxFetcherRetries) {
+    UMA_HISTOGRAM_ENUMERATION("Signin.MultiloginRetry", result.error().state(),
+                              GoogleServiceAuthError::NUM_STATES);
+    token_id_pairs_.clear();
+    StartFetchingTokens();
+    return;
+  }
+  std::move(callback_).Run(result.error());
+  // Do not add anything below this line, because this may be deleted.
+}
+
+void OAuthMultiloginHelper::StartSettingCookies(
+    const OAuthMultiloginResult& result) {
+  DCHECK(cookies_to_set_.empty());
+  network::mojom::CookieManager* cookie_manager =
+      signin_client_->GetCookieManager();
+  const std::vector<net::CanonicalCookie>& cookies = result.cookies();
+
+  for (const net::CanonicalCookie& cookie : cookies) {
+    cookies_to_set_.insert(std::make_pair(cookie.Name(), cookie.Domain()));
+  }
+  for (const net::CanonicalCookie& cookie : cookies) {
+    if (cookies_to_set_.find(std::make_pair(cookie.Name(), cookie.Domain())) !=
+        cookies_to_set_.end()) {
+      base::OnceCallback<void(net::CanonicalCookie::CookieInclusionStatus)>
+          callback = base::BindOnce(&OAuthMultiloginHelper::OnCookieSet,
+                                    weak_ptr_factory_.GetWeakPtr(),
+                                    cookie.Name(), cookie.Domain());
+      net::CookieOptions options;
+      options.set_include_httponly();
+      // Permit it to set a SameSite cookie if it wants to.
+      options.set_same_site_cookie_context(
+          net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
+      cookie_manager->SetCanonicalCookie(
+          cookie, "https", options,
+          mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+              std::move(callback), net::CanonicalCookie::CookieInclusionStatus::
+                                       EXCLUDE_UNKNOWN_ERROR));
+    } else {
+      LOG(ERROR) << "Duplicate cookie found: " << cookie.Name() << " "
+                 << cookie.Domain();
+    }
+  }
+}
+
+void OAuthMultiloginHelper::OnCookieSet(
+    const std::string& cookie_name,
+    const std::string& cookie_domain,
+    net::CanonicalCookie::CookieInclusionStatus status) {
+  cookies_to_set_.erase(std::make_pair(cookie_name, cookie_domain));
+  bool success =
+      (status == net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
+  if (!success) {
+    LOG(ERROR) << "Failed to set cookie " << cookie_name
+               << " for domain=" << cookie_domain << ".";
+  }
+  UMA_HISTOGRAM_BOOLEAN("Signin.SetCookieSuccess", success);
+  if (cookies_to_set_.empty())
+    std::move(callback_).Run(GoogleServiceAuthError::AuthErrorNone());
+  // Do not add anything below this line, because this may be deleted.
+}
+
+}  // namespace signin
diff --git a/components/signin/core/browser/oauth_multilogin_helper.h b/components/signin/core/browser/oauth_multilogin_helper.h
new file mode 100644
index 0000000..48d4aeb18
--- /dev/null
+++ b/components/signin/core/browser/oauth_multilogin_helper.h
@@ -0,0 +1,94 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_
+#define COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "google_apis/gaia/gaia_auth_consumer.h"
+#include "google_apis/gaia/gaia_auth_fetcher.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
+
+class GaiaAuthFetcher;
+class OAuth2TokenService;
+class SigninClient;
+
+namespace signin {
+
+class OAuthMultiloginTokenFetcher;
+
+// This is a helper class that drives the OAuth multilogin process.
+// The main steps are:
+// - fetch access tokens with login scope,
+// - call the oauth multilogin endpoint,
+// - get the cookies from the response body, and set them in the cookie manager.
+// It is safe to delete this object from within the callbacks.
+class OAuthMultiloginHelper : public GaiaAuthConsumer {
+ public:
+  OAuthMultiloginHelper(
+      SigninClient* signin_client,
+      OAuth2TokenService* token_service,
+      const std::vector<std::string>& account_ids,
+      base::OnceCallback<void(const GoogleServiceAuthError&)> callback);
+
+  ~OAuthMultiloginHelper() override;
+
+ private:
+  // Starts fetching tokens with OAuthMultiloginTokenFetcher.
+  void StartFetchingTokens();
+
+  // Callbacks for OAuthMultiloginTokenFetcher.
+  void OnAccessTokensSuccess(
+      const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&
+          token_id_pairs);
+  void OnAccessTokensFailure(const GoogleServiceAuthError& error);
+
+  // Actual call to the multilogin endpoint.
+  void StartFetchingMultiLogin();
+
+  // Overridden from GaiaAuthConsumer.
+  void OnOAuthMultiloginFinished(const OAuthMultiloginResult& result) override;
+
+  // Starts setting parsed cookies in browser.
+  void StartSettingCookies(const OAuthMultiloginResult& result);
+
+  // Callback for CookieManager::SetCanonicalCookie.
+  void OnCookieSet(const std::string& cookie_name,
+                   const std::string& cookie_domain,
+                   net::CanonicalCookie::CookieInclusionStatus status);
+
+  SigninClient* signin_client_;
+  OAuth2TokenService* token_service_;
+
+  int fetcher_retries_ = 0;
+
+  // Account ids to set in the cookie.
+  const std::vector<std::string> account_ids_;
+  // Access tokens, in the same order as the account ids.
+  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs_;
+
+  base::OnceCallback<void(const GoogleServiceAuthError&)> callback_;
+  std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_;
+  std::unique_ptr<OAuthMultiloginTokenFetcher> token_fetcher_;
+
+  // List of pairs (cookie name and cookie domain) that have to be set in
+  // cookie jar.
+  std::set<std::pair<std::string, std::string>> cookies_to_set_;
+
+  base::WeakPtrFactory<OAuthMultiloginHelper> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(OAuthMultiloginHelper);
+};
+
+}  // namespace signin
+
+#endif  // COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_
diff --git a/components/signin/core/browser/oauth_multilogin_helper_unittest.cc b/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
new file mode 100644
index 0000000..d11d6f3
--- /dev/null
+++ b/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
@@ -0,0 +1,414 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/signin/core/browser/oauth_multilogin_helper.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/signin/core/browser/test_signin_client.h"
+#include "google_apis/gaia/fake_oauth2_token_service.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "services/network/test/test_cookie_manager.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace signin {
+
+namespace {
+
+const char kAccountId[] = "account_id_1";
+const char kAccountId2[] = "account_id_2";
+const char kAccessToken[] = "access_token_1";
+const char kAccessToken2[] = "access_token_2";
+
+constexpr int kMaxFetcherRetries = 3;
+
+const char kMultiloginSuccessResponse[] =
+    R"()]}'
+       {
+         "status": "OK",
+         "cookies":[
+           {
+             "name":"SID",
+             "value":"SID_value",
+             "domain":".google.fr",
+             "path":"/",
+             "isSecure":true,
+             "isHttpOnly":false,
+             "priority":"HIGH",
+             "maxAge":63070000
+           }
+         ]
+       }
+      )";
+
+const char kMultiloginSuccessResponseTwoCookies[] =
+    R"()]}'
+       {
+         "status": "OK",
+         "cookies":[
+           {
+             "name":"SID",
+             "value":"SID_value",
+             "domain":".google.fr",
+             "path":"/",
+             "isSecure":true,
+             "isHttpOnly":false,
+             "priority":"HIGH",
+             "maxAge":63070000
+           },
+           {
+             "name":"FOO",
+             "value":"FOO_value",
+             "domain":".google.com",
+             "path":"/",
+             "isSecure":true,
+             "isHttpOnly":false,
+             "priority":"HIGH",
+             "maxAge":63070000
+           }
+         ]
+       }
+      )";
+
+const char kMultiloginRetryResponse[] =
+    R"()]}'
+       {
+         "status": "RETRY"
+       }
+      )";
+
+const char kMultiloginInvalidTokenResponse[] =
+    R"()]}'
+       {
+         "status": "INVALID_TOKENS",
+         "failed_accounts": [
+           { "obfuscated_id": "account_id_1", "status": "RECOVERABLE" },
+           { "obfuscated_id": "account_id_2", "status": "OK" }
+         ]
+       }
+      )";
+
+// GMock matcher that checks that the cookie has the expected parameters.
+MATCHER_P3(CookieMatcher, name, value, domain, "") {
+  return arg.Name() == name && arg.Value() == value && arg.Domain() == domain &&
+         arg.Path() == "/" && arg.IsSecure() && !arg.IsHttpOnly();
+}
+
+void RunSetCookieCallbackWithSuccess(
+    const net::CanonicalCookie&,
+    const std::string&,
+    const net::CookieOptions&,
+    network::mojom::CookieManager::SetCanonicalCookieCallback callback) {
+  std::move(callback).Run(net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
+}
+
+class MockCookieManager
+    : public ::testing::StrictMock<network::TestCookieManager> {
+ public:
+  MOCK_METHOD4(SetCanonicalCookie,
+               void(const net::CanonicalCookie& cookie,
+                    const std::string& source_scheme,
+                    const net::CookieOptions& cookie_options,
+                    SetCanonicalCookieCallback callback));
+};
+
+class MockTokenService : public FakeOAuth2TokenService {
+ public:
+  MOCK_METHOD2(InvalidateTokenForMultilogin,
+               void(const std::string& account_id, const std::string& token));
+};
+
+}  // namespace
+
+class OAuthMultiloginHelperTest : public testing::Test {
+ public:
+  OAuthMultiloginHelperTest() : test_signin_client_(/*prefs=*/nullptr) {
+    std::unique_ptr<MockCookieManager> cookie_manager =
+        std::make_unique<MockCookieManager>();
+    mock_cookie_manager_ = cookie_manager.get();
+    test_signin_client_.set_cookie_manager(std::move(cookie_manager));
+  }
+
+  ~OAuthMultiloginHelperTest() override = default;
+
+  std::unique_ptr<OAuthMultiloginHelper> CreateHelper(
+      const std::vector<std::string> account_ids) {
+    return std::make_unique<OAuthMultiloginHelper>(
+        &test_signin_client_, token_service(), account_ids,
+        base::BindOnce(&OAuthMultiloginHelperTest::OnOAuthMultiloginFinished,
+                       base::Unretained(this)));
+  }
+
+  network::TestURLLoaderFactory* url_loader() {
+    return test_signin_client_.test_url_loader_factory();
+  }
+
+  std::string multilogin_url() const {
+    return GaiaUrls::GetInstance()->oauth_multilogin_url().spec() +
+           "?source=ChromiumBrowser";
+  }
+
+  MockCookieManager* cookie_manager() { return mock_cookie_manager_; }
+  MockTokenService* token_service() { return &mock_token_service_; }
+
+ protected:
+  void OnOAuthMultiloginFinished(const GoogleServiceAuthError& error) {
+    DCHECK(!callback_called_);
+    callback_called_ = true;
+    error_ = error;
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  bool callback_called_ = false;
+  GoogleServiceAuthError error_;
+
+  MockCookieManager* mock_cookie_manager_;  // Owned by test_signin_client_
+  TestSigninClient test_signin_client_;
+  MockTokenService mock_token_service_;
+};
+
+// Everything succeeds.
+TEST_F(OAuthMultiloginHelperTest, Success) {
+  token_service()->AddAccount(kAccountId);
+  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+
+  // Configure mock cookie manager:
+  // - check that the cookie is the expected one
+  // - immediately invoke the callback
+  EXPECT_CALL(
+      *cookie_manager(),
+      SetCanonicalCookie(CookieMatcher("SID", "SID_value", ".google.fr"),
+                         "https", testing::_, testing::_))
+      .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess));
+
+  // Issue access token.
+  OAuth2AccessTokenConsumer::TokenResponse success_response;
+  success_response.access_token = kAccessToken;
+  token_service()->IssueAllTokensForAccount(kAccountId, success_response);
+
+  // Multilogin call.
+  EXPECT_FALSE(callback_called_);
+  EXPECT_TRUE(url_loader()->IsPending(multilogin_url()));
+  url_loader()->AddResponse(multilogin_url(), kMultiloginSuccessResponse);
+  EXPECT_FALSE(url_loader()->IsPending(multilogin_url()));
+  EXPECT_TRUE(callback_called_);
+  EXPECT_EQ(GoogleServiceAuthError::NONE, error_.state());
+}
+
+// Multiple cookies in the multilogin response.
+TEST_F(OAuthMultiloginHelperTest, MultipleCookies) {
+  token_service()->AddAccount(kAccountId);
+  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+
+  // Configure mock cookie manager:
+  // - check that the cookie is the expected one
+  // - immediately invoke the callback
+  EXPECT_CALL(
+      *cookie_manager(),
+      SetCanonicalCookie(CookieMatcher("SID", "SID_value", ".google.fr"),
+                         "https", testing::_, testing::_))
+      .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess));
+  EXPECT_CALL(
+      *cookie_manager(),
+      SetCanonicalCookie(CookieMatcher("FOO", "FOO_value", ".google.com"),
+                         "https", testing::_, testing::_))
+      .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess));
+
+  // Issue access token.
+  OAuth2AccessTokenConsumer::TokenResponse success_response;
+  success_response.access_token = kAccessToken;
+  token_service()->IssueAllTokensForAccount(kAccountId, success_response);
+
+  // Multilogin call.
+  EXPECT_FALSE(callback_called_);
+  EXPECT_TRUE(url_loader()->IsPending(multilogin_url()));
+  url_loader()->AddResponse(multilogin_url(),
+                            kMultiloginSuccessResponseTwoCookies);
+  EXPECT_FALSE(url_loader()->IsPending(multilogin_url()));
+  EXPECT_TRUE(callback_called_);
+  EXPECT_EQ(GoogleServiceAuthError::NONE, error_.state());
+}
+
+// Failure to get the access token.
+TEST_F(OAuthMultiloginHelperTest, OneAccountAccessTokenFailure) {
+  token_service()->AddAccount(kAccountId);
+  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+
+  token_service()->IssueErrorForAllPendingRequestsForAccount(
+      kAccountId,
+      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  EXPECT_TRUE(callback_called_);
+  EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error_.state());
+}
+
+// Retry on transient errors in the multilogin call.
+TEST_F(OAuthMultiloginHelperTest, OneAccountTransientMultiloginError) {
+  token_service()->AddAccount(kAccountId);
+  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+
+  // Configure mock cookie manager:
+  // - check that the cookie is the expected one
+  // - immediately invoke the callback
+  EXPECT_CALL(
+      *cookie_manager(),
+      SetCanonicalCookie(CookieMatcher("SID", "SID_value", ".google.fr"),
+                         "https", testing::_, testing::_))
+      .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess));
+
+  // Issue access token.
+  OAuth2AccessTokenConsumer::TokenResponse success_response;
+  success_response.access_token = kAccessToken;
+  token_service()->IssueAllTokensForAccount(kAccountId, success_response);
+
+  // Multilogin call fails with transient error.
+  EXPECT_TRUE(url_loader()->IsPending(multilogin_url()));
+  url_loader()->SimulateResponseForPendingRequest(multilogin_url(),
+                                                  kMultiloginRetryResponse);
+
+  // Call is retried and succeeds.
+  EXPECT_FALSE(url_loader()->IsPending(multilogin_url()));
+  token_service()->IssueAllTokensForAccount(kAccountId, success_response);
+  EXPECT_FALSE(callback_called_);
+  EXPECT_TRUE(url_loader()->IsPending(multilogin_url()));
+  url_loader()->AddResponse(multilogin_url(), kMultiloginSuccessResponse);
+  EXPECT_FALSE(url_loader()->IsPending(multilogin_url()));
+  EXPECT_TRUE(callback_called_);
+  EXPECT_EQ(GoogleServiceAuthError::NONE, error_.state());
+}
+
+// Stop retrying after too many transient errors in the multilogin call.
+TEST_F(OAuthMultiloginHelperTest,
+       OneAccountTransientMultiloginErrorMaxRetries) {
+  token_service()->AddAccount(kAccountId);
+  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+
+  // Issue access token.
+  OAuth2AccessTokenConsumer::TokenResponse success_response;
+  success_response.access_token = kAccessToken;
+
+  // Multilogin call fails with transient error, retry many times.
+  for (int i = 0; i < kMaxFetcherRetries; ++i) {
+    token_service()->IssueAllTokensForAccount(kAccountId, success_response);
+    EXPECT_TRUE(url_loader()->IsPending(multilogin_url()));
+    EXPECT_FALSE(callback_called_);
+    url_loader()->SimulateResponseForPendingRequest(multilogin_url(),
+                                                    kMultiloginRetryResponse);
+  }
+
+  // Failure after exceeding the maximum number of retries.
+  EXPECT_TRUE(callback_called_);
+  EXPECT_EQ(GoogleServiceAuthError::SERVICE_UNAVAILABLE, error_.state());
+}
+
+// Persistent error in the multilogin call.
+TEST_F(OAuthMultiloginHelperTest, OneAccountPersistentMultiloginError) {
+  token_service()->AddAccount(kAccountId);
+  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+
+  // Issue access token.
+  OAuth2AccessTokenConsumer::TokenResponse success_response;
+  success_response.access_token = kAccessToken;
+  token_service()->IssueAllTokensForAccount(kAccountId, success_response);
+
+  // Multilogin call fails with persistent error.
+  EXPECT_FALSE(callback_called_);
+  EXPECT_TRUE(url_loader()->IsPending(multilogin_url()));
+  url_loader()->AddResponse(multilogin_url(), "blah");  // Unexpected response.
+  EXPECT_FALSE(url_loader()->IsPending(multilogin_url()));
+  EXPECT_TRUE(callback_called_);
+  EXPECT_EQ(GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE,
+            error_.state());
+}
+
+// Retry on "invalid token" in the multilogin response.
+TEST_F(OAuthMultiloginHelperTest, InvalidTokenError) {
+  token_service()->AddAccount(kAccountId);
+  token_service()->AddAccount(kAccountId2);
+  std::unique_ptr<OAuthMultiloginHelper> helper =
+      CreateHelper({kAccountId, kAccountId2});
+
+  // Configure mock cookie manager:
+  // - check that the cookie is the expected one
+  // - immediately invoke the callback
+  EXPECT_CALL(
+      *cookie_manager(),
+      SetCanonicalCookie(CookieMatcher("SID", "SID_value", ".google.fr"),
+                         "https", testing::_, testing::_))
+      .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess));
+
+  // The failed access token should be invalidated.
+  EXPECT_CALL(*token_service(),
+              InvalidateTokenForMultilogin(kAccountId, kAccessToken));
+
+  // Issue access tokens.
+  OAuth2AccessTokenConsumer::TokenResponse success_response;
+  success_response.access_token = kAccessToken;
+  token_service()->IssueAllTokensForAccount(kAccountId, success_response);
+  OAuth2AccessTokenConsumer::TokenResponse success_response_2;
+  success_response_2.access_token = kAccessToken2;
+  token_service()->IssueAllTokensForAccount(kAccountId2, success_response_2);
+
+  // Multilogin call fails with invalid token for kAccountId.
+  EXPECT_TRUE(url_loader()->IsPending(multilogin_url()));
+  url_loader()->SimulateResponseForPendingRequest(
+      multilogin_url(), kMultiloginInvalidTokenResponse);
+
+  // Both tokens are retried.
+  token_service()->IssueAllTokensForAccount(kAccountId, success_response);
+  EXPECT_FALSE(callback_called_);
+  EXPECT_FALSE(url_loader()->IsPending(multilogin_url()));
+  token_service()->IssueAllTokensForAccount(kAccountId2, success_response);
+
+  // Multilogin succeeds the second time.
+  EXPECT_FALSE(callback_called_);
+  EXPECT_TRUE(url_loader()->IsPending(multilogin_url()));
+  url_loader()->AddResponse(multilogin_url(), kMultiloginSuccessResponse);
+  EXPECT_FALSE(url_loader()->IsPending(multilogin_url()));
+  EXPECT_TRUE(callback_called_);
+  EXPECT_EQ(GoogleServiceAuthError::NONE, error_.state());
+}
+
+// Retry on "invalid token" in the multilogin response.
+TEST_F(OAuthMultiloginHelperTest, InvalidTokenErrorMaxRetries) {
+  token_service()->AddAccount(kAccountId);
+  token_service()->AddAccount(kAccountId2);
+  std::unique_ptr<OAuthMultiloginHelper> helper =
+      CreateHelper({kAccountId, kAccountId2});
+
+  // The failed access token should be invalidated.
+  EXPECT_CALL(*token_service(),
+              InvalidateTokenForMultilogin(kAccountId, kAccessToken))
+      .Times(kMaxFetcherRetries);
+
+  // Issue access tokens.
+  OAuth2AccessTokenConsumer::TokenResponse success_response;
+  success_response.access_token = kAccessToken;
+  OAuth2AccessTokenConsumer::TokenResponse success_response_2;
+  success_response_2.access_token = kAccessToken2;
+
+  // Multilogin call fails with invalid token for kAccountId. Retry many times.
+  for (int i = 0; i < kMaxFetcherRetries; ++i) {
+    token_service()->IssueAllTokensForAccount(kAccountId, success_response);
+    EXPECT_FALSE(url_loader()->IsPending(multilogin_url()));
+    token_service()->IssueAllTokensForAccount(kAccountId2, success_response_2);
+
+    EXPECT_FALSE(callback_called_);
+    EXPECT_TRUE(url_loader()->IsPending(multilogin_url()));
+
+    url_loader()->SimulateResponseForPendingRequest(
+        multilogin_url(), kMultiloginInvalidTokenResponse);
+  }
+
+  // The maximum number of retries is reached, fail.
+  EXPECT_FALSE(url_loader()->IsPending(multilogin_url()));
+  EXPECT_TRUE(callback_called_);
+  EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error_.state());
+}
+
+}  // namespace signin
diff --git a/components/signin/core/browser/oauth_multilogin_token_fetcher.cc b/components/signin/core/browser/oauth_multilogin_token_fetcher.cc
new file mode 100644
index 0000000..d01a50aa
--- /dev/null
+++ b/components/signin/core/browser/oauth_multilogin_token_fetcher.cc
@@ -0,0 +1,136 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/signin/core/browser/oauth_multilogin_token_fetcher.h"
+
+#include <algorithm>
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "components/signin/core/browser/signin_client.h"
+
+namespace {
+
+void RecordGetAccessTokenFinished(GoogleServiceAuthError error) {
+  UMA_HISTOGRAM_ENUMERATION("Signin.GetAccessTokenFinished", error.state(),
+                            GoogleServiceAuthError::NUM_STATES);
+}
+
+}  // namespace
+
+namespace signin {
+
+OAuthMultiloginTokenFetcher::OAuthMultiloginTokenFetcher(
+    SigninClient* signin_client,
+    OAuth2TokenService* token_service,
+    const std::vector<std::string>& account_ids,
+    SuccessCallback success_callback,
+    FailureCallback failure_callback)
+    : OAuth2TokenService::Consumer("oauth_multilogin_token_fetcher"),
+      signin_client_(signin_client),
+      token_service_(token_service),
+      account_ids_(account_ids),
+      success_callback_(std::move(success_callback)),
+      failure_callback_(std::move(failure_callback)),
+      weak_ptr_factory_(this) {
+  DCHECK(signin_client_);
+  DCHECK(token_service_);
+  DCHECK(!account_ids_.empty());
+  DCHECK(success_callback_);
+  DCHECK(failure_callback_);
+
+#ifndef NDEBUG
+  // Check that there is no duplicate accounts.
+  std::set<std::string> accounts_no_duplicates(account_ids_.begin(),
+                                               account_ids_.end());
+  DCHECK_EQ(account_ids_.size(), accounts_no_duplicates.size());
+#endif
+
+  for (const std::string& account_id : account_ids_)
+    StartFetchingToken(account_id);
+}
+
+OAuthMultiloginTokenFetcher::~OAuthMultiloginTokenFetcher() = default;
+
+void OAuthMultiloginTokenFetcher::StartFetchingToken(
+    const std::string& account_id) {
+  DCHECK(!account_id.empty());
+  token_requests_.push_back(
+      token_service_->StartRequestForMultilogin(account_id, this));
+}
+
+void OAuthMultiloginTokenFetcher::OnGetTokenSuccess(
+    const OAuth2TokenService::Request* request,
+    const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
+  std::string account_id = request->GetAccountId();
+  DCHECK(account_ids_.cend() !=
+         std::find(account_ids_.cbegin(), account_ids_.cend(), account_id));
+
+  const std::string token = token_response.access_token;
+  DCHECK(!token.empty());
+  // Do not use |request| and |token_response| below this line, as they are
+  // deleted.
+  EraseRequest(request);
+
+  const auto& inserted =
+      access_tokens_.insert(std::make_pair(account_id, token));
+  DCHECK(inserted.second);  // If this fires, we have a duplicate account.
+
+  if (access_tokens_.size() == account_ids_.size()) {
+    std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs;
+    for (const auto& id : account_ids_) {
+      const auto& it = access_tokens_.find(id);
+      DCHECK(!it->second.empty());
+      // TODO(https://crbug.com/956503): Don't assume that the account ID is the
+      // Gaia ID.
+      token_id_pairs.emplace_back(id, it->second);
+    }
+    RecordGetAccessTokenFinished(GoogleServiceAuthError::AuthErrorNone());
+    std::move(success_callback_).Run(token_id_pairs);
+    // Do not add anything below this line, as this may be deleted.
+  }
+}
+
+void OAuthMultiloginTokenFetcher::OnGetTokenFailure(
+    const OAuth2TokenService::Request* request,
+    const GoogleServiceAuthError& error) {
+  std::string account_id = request->GetAccountId();
+  VLOG(1) << "Failed to retrieve accesstoken account=" << account_id
+          << " error=" << error.ToString();
+  if (error.IsTransientError() &&
+      retried_requests_.find(account_id) == retried_requests_.end()) {
+    retried_requests_.insert(account_id);
+    UMA_HISTOGRAM_ENUMERATION("Signin.GetAccessTokenRetry", error.state(),
+                              GoogleServiceAuthError::NUM_STATES);
+    EraseRequest(request);
+    // Fetching fresh access tokens requires network.
+    signin_client_->DelayNetworkCall(
+        base::BindOnce(&OAuthMultiloginTokenFetcher::StartFetchingToken,
+                       weak_ptr_factory_.GetWeakPtr(), account_id));
+    return;
+  }
+  RecordGetAccessTokenFinished(error);
+  // Copy the error because it is a reference owned by token_requests_.
+  GoogleServiceAuthError error_copy = error;
+  token_requests_.clear();  // Cancel pending requests.
+  std::move(failure_callback_).Run(error_copy);
+  // Do not add anything below this line, as this may be deleted.
+}
+
+void OAuthMultiloginTokenFetcher::EraseRequest(
+    const OAuth2TokenService::Request* request) {
+  for (auto it = token_requests_.begin(); it != token_requests_.end(); ++it) {
+    if (it->get() == request) {
+      token_requests_.erase(it);
+      return;
+    }
+  }
+  NOTREACHED() << "Request not found";
+}
+
+}  // namespace signin
diff --git a/components/signin/core/browser/oauth_multilogin_token_fetcher.h b/components/signin/core/browser/oauth_multilogin_token_fetcher.h
new file mode 100644
index 0000000..168ba88
--- /dev/null
+++ b/components/signin/core/browser/oauth_multilogin_token_fetcher.h
@@ -0,0 +1,74 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_
+#define COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/bind_helpers.h"
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "google_apis/gaia/gaia_auth_fetcher.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+
+class SigninClient;
+
+namespace signin {
+
+// Fetches multilogin access tokens in parallel for multiple accounts.
+// It is safe to delete this object from within the callbacks.
+class OAuthMultiloginTokenFetcher : public OAuth2TokenService::Consumer {
+ public:
+  using SuccessCallback = base::OnceCallback<void(
+      const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&)>;
+  using FailureCallback =
+      base::OnceCallback<void(const GoogleServiceAuthError&)>;
+
+  OAuthMultiloginTokenFetcher(SigninClient* signin_client,
+                              OAuth2TokenService* token_service,
+                              const std::vector<std::string>& account_ids,
+                              SuccessCallback success_callback,
+                              FailureCallback failure_callback);
+
+  ~OAuthMultiloginTokenFetcher() override;
+
+ private:
+  void StartFetchingToken(const std::string& account_id);
+
+  // Overridden from OAuth2TokenService::Consumer.
+  void OnGetTokenSuccess(
+      const OAuth2TokenService::Request* request,
+      const OAuth2AccessTokenConsumer::TokenResponse& token_response) override;
+  void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+                         const GoogleServiceAuthError& error) override;
+
+  // Helper function to remove a request from token_requests_.
+  void EraseRequest(const OAuth2TokenService::Request* request);
+
+  SigninClient* signin_client_;
+  OAuth2TokenService* token_service_;
+  const std::vector<std::string> account_ids_;
+
+  SuccessCallback success_callback_;
+  FailureCallback failure_callback_;
+
+  std::vector<std::unique_ptr<OAuth2TokenService::Request>> token_requests_;
+  std::map<std::string, std::string> access_tokens_;
+  std::set<std::string> retried_requests_;  // Requests are retried once.
+
+  base::WeakPtrFactory<OAuthMultiloginTokenFetcher> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(OAuthMultiloginTokenFetcher);
+};
+
+}  // namespace signin
+
+#endif  // COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_
diff --git a/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc b/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc
new file mode 100644
index 0000000..a40df52
--- /dev/null
+++ b/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc
@@ -0,0 +1,226 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/signin/core/browser/oauth_multilogin_token_fetcher.h"
+
+#include <map>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/signin/core/browser/test_signin_client.h"
+#include "google_apis/gaia/fake_oauth2_token_service.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace signin {
+
+namespace {
+
+const char kAccountId[] = "account_id";
+const char kAccessToken[] = "access_token";
+
+// Status of the token fetch.
+enum class FetchStatus { kSuccess, kFailure, kPending };
+
+}  // namespace
+
+class OAuthMultiloginTokenFetcherTest : public testing::Test {
+ public:
+  OAuthMultiloginTokenFetcherTest() : test_signin_client_(/*prefs=*/nullptr) {}
+
+  ~OAuthMultiloginTokenFetcherTest() override = default;
+
+  std::unique_ptr<OAuthMultiloginTokenFetcher> CreateFetcher(
+      const std::vector<std::string> account_ids) {
+    return std::make_unique<OAuthMultiloginTokenFetcher>(
+        &test_signin_client_, &token_service_, account_ids,
+        base::BindOnce(&OAuthMultiloginTokenFetcherTest::OnSuccess,
+                       base::Unretained(this)),
+        base::BindOnce(&OAuthMultiloginTokenFetcherTest::OnFailure,
+                       base::Unretained(this)));
+  }
+
+  // Returns the status of the token fetch.
+  FetchStatus GetFetchStatus() const {
+    if (success_callback_called_)
+      return FetchStatus::kSuccess;
+    return failure_callback_called_ ? FetchStatus::kFailure
+                                    : FetchStatus::kPending;
+  }
+
+ protected:
+  // Success callback for OAuthMultiloginTokenFetcher.
+  void OnSuccess(const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&
+                     token_id_pairs) {
+    DCHECK(!success_callback_called_);
+    DCHECK(token_id_pairs_.empty());
+    success_callback_called_ = true;
+    token_id_pairs_ = token_id_pairs;
+  }
+
+  // Failure callback for OAuthMultiloginTokenFetcher.
+  void OnFailure(const GoogleServiceAuthError& error) {
+    DCHECK(!failure_callback_called_);
+    failure_callback_called_ = true;
+    error_ = error;
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  bool success_callback_called_ = false;
+  bool failure_callback_called_ = false;
+  GoogleServiceAuthError error_;
+  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs_;
+
+  TestSigninClient test_signin_client_;
+  FakeOAuth2TokenService token_service_;
+};
+
+TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountSuccess) {
+  token_service_.AddAccount(kAccountId);
+  std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
+      CreateFetcher({kAccountId});
+  EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
+  OAuth2AccessTokenConsumer::TokenResponse success_response;
+  success_response.access_token = kAccessToken;
+  token_service_.IssueAllTokensForAccount(kAccountId, success_response);
+  EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
+  // Check result.
+  EXPECT_EQ(1u, token_id_pairs_.size());
+  EXPECT_EQ(kAccountId, token_id_pairs_[0].gaia_id_);
+  EXPECT_EQ(kAccessToken, token_id_pairs_[0].token_);
+}
+
+TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountPersistentError) {
+  token_service_.AddAccount(kAccountId);
+  std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
+      CreateFetcher({kAccountId});
+  EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
+  token_service_.IssueErrorForAllPendingRequestsForAccount(
+      kAccountId,
+      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  EXPECT_EQ(FetchStatus::kFailure, GetFetchStatus());
+  EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error_.state());
+}
+
+TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientError) {
+  token_service_.AddAccount(kAccountId);
+  std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
+      CreateFetcher({kAccountId});
+  // Connection failure will be retried.
+  token_service_.IssueErrorForAllPendingRequestsForAccount(
+      kAccountId,
+      GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
+  EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
+  // Success on retry.
+  OAuth2AccessTokenConsumer::TokenResponse success_response;
+  success_response.access_token = kAccessToken;
+  token_service_.IssueAllTokensForAccount(kAccountId, success_response);
+  EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
+  // Check result.
+  EXPECT_EQ(1u, token_id_pairs_.size());
+  EXPECT_EQ(kAccountId, token_id_pairs_[0].gaia_id_);
+  EXPECT_EQ(kAccessToken, token_id_pairs_[0].token_);
+}
+
+TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientErrorMaxRetries) {
+  token_service_.AddAccount(kAccountId);
+  std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
+      CreateFetcher({kAccountId});
+  // Repeated connection failures.
+  token_service_.IssueErrorForAllPendingRequestsForAccount(
+      kAccountId,
+      GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
+  EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
+  token_service_.IssueErrorForAllPendingRequestsForAccount(
+      kAccountId,
+      GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
+  // Stop retrying, and fail.
+  EXPECT_EQ(FetchStatus::kFailure, GetFetchStatus());
+  EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error_.state());
+}
+
+// The flow succeeds even if requests are received out of order.
+TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsSuccess) {
+  token_service_.AddAccount("account_1");
+  token_service_.AddAccount("account_2");
+  token_service_.AddAccount("account_3");
+  std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
+      CreateFetcher({"account_1", "account_2", "account_3"});
+  OAuth2AccessTokenConsumer::TokenResponse success_response;
+  success_response.access_token = "token_3";
+  token_service_.IssueAllTokensForAccount("account_3", success_response);
+  success_response.access_token = "token_1";
+  token_service_.IssueAllTokensForAccount("account_1", success_response);
+  EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
+  success_response.access_token = "token_2";
+  token_service_.IssueAllTokensForAccount("account_2", success_response);
+  EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
+  // Check result.
+  EXPECT_EQ(3u, token_id_pairs_.size());
+  EXPECT_EQ("account_1", token_id_pairs_[0].gaia_id_);
+  EXPECT_EQ("account_2", token_id_pairs_[1].gaia_id_);
+  EXPECT_EQ("account_3", token_id_pairs_[2].gaia_id_);
+  EXPECT_EQ("token_1", token_id_pairs_[0].token_);
+  EXPECT_EQ("token_2", token_id_pairs_[1].token_);
+  EXPECT_EQ("token_3", token_id_pairs_[2].token_);
+}
+
+TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsTransientError) {
+  token_service_.AddAccount("account_1");
+  token_service_.AddAccount("account_2");
+  token_service_.AddAccount("account_3");
+  std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
+      CreateFetcher({"account_1", "account_2", "account_3"});
+  // Connection failures will be retried.
+  token_service_.IssueErrorForAllPendingRequestsForAccount(
+      "account_1",
+      GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
+  token_service_.IssueErrorForAllPendingRequestsForAccount(
+      "account_2",
+      GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
+  token_service_.IssueErrorForAllPendingRequestsForAccount(
+      "account_3",
+      GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
+  // Success on retry.
+  OAuth2AccessTokenConsumer::TokenResponse success_response;
+  success_response.access_token = kAccessToken;
+  success_response.access_token = "token_1";
+  token_service_.IssueAllTokensForAccount("account_1", success_response);
+  success_response.access_token = "token_2";
+  token_service_.IssueAllTokensForAccount("account_2", success_response);
+  EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
+  success_response.access_token = "token_3";
+  token_service_.IssueAllTokensForAccount("account_3", success_response);
+  EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
+  // Check result.
+  EXPECT_EQ(3u, token_id_pairs_.size());
+  EXPECT_EQ("account_1", token_id_pairs_[0].gaia_id_);
+  EXPECT_EQ("account_2", token_id_pairs_[1].gaia_id_);
+  EXPECT_EQ("account_3", token_id_pairs_[2].gaia_id_);
+  EXPECT_EQ("token_1", token_id_pairs_[0].token_);
+  EXPECT_EQ("token_2", token_id_pairs_[1].token_);
+  EXPECT_EQ("token_3", token_id_pairs_[2].token_);
+}
+
+TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsPersistentError) {
+  token_service_.AddAccount("account_1");
+  token_service_.AddAccount("account_2");
+  token_service_.AddAccount("account_3");
+  std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
+      CreateFetcher({"account_1", "account_2", "account_3"});
+  EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
+  token_service_.IssueErrorForAllPendingRequestsForAccount(
+      "account_2",
+      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  // Fail as soon as one of the accounts is in error.
+  EXPECT_EQ(FetchStatus::kFailure, GetFetchStatus());
+  EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error_.state());
+}
+
+}  // namespace signin
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc
index 21106f1..fa5be0f 100644
--- a/components/sync/driver/glue/sync_engine_backend.cc
+++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -365,7 +365,6 @@
   args.report_unrecoverable_error_function =
       params.report_unrecoverable_error_function;
   args.cancelation_signal = &stop_syncing_signal_;
-  args.saved_nigori_state = std::move(params.saved_nigori_state);
   args.poll_interval = params.poll_interval;
   args.cache_guid = params.cache_guid;
   args.birthday = params.birthday;
diff --git a/components/sync/driver/glue/sync_engine_impl_unittest.cc b/components/sync/driver/glue/sync_engine_impl_unittest.cc
index c8fcd85..21464b35 100644
--- a/components/sync/driver/glue/sync_engine_impl_unittest.cc
+++ b/components/sync/driver/glue/sync_engine_impl_unittest.cc
@@ -251,7 +251,6 @@
     params.delete_sync_data_folder = true;
     params.unrecoverable_error_handler =
         MakeWeakHandle(test_unrecoverable_error_handler_.GetWeakPtr()),
-    params.saved_nigori_state = std::move(saved_nigori_state_);
     sync_prefs_->GetInvalidationVersions(&params.invalidation_versions);
 
     backend_->Initialize(std::move(params));
@@ -325,7 +324,6 @@
   ModelTypeSet engine_types_;
   ModelTypeSet enabled_types_;
   std::unique_ptr<NetworkResources> network_resources_;
-  std::unique_ptr<SyncEncryptionHandler::NigoriState> saved_nigori_state_;
   base::OnceClosure quit_loop_;
   testing::NiceMock<MockInvalidationService> invalidator_;
 };
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index dffdf255..17099ba5 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -507,7 +507,6 @@
   params.unrecoverable_error_handler = GetUnrecoverableErrorHandler();
   params.report_unrecoverable_error_function =
       base::BindRepeating(ReportUnrecoverableError, channel_);
-  params.saved_nigori_state = crypto_.TakeSavedNigoriState();
   sync_prefs_.GetInvalidationVersions(&params.invalidation_versions);
   params.poll_interval = sync_prefs_.GetPollInterval();
   if (params.poll_interval.is_zero()) {
diff --git a/components/sync/driver/sync_service_crypto.cc b/components/sync/driver/sync_service_crypto.cc
index 1ab67de..ac0d9d2 100644
--- a/components/sync/driver/sync_service_crypto.cc
+++ b/components/sync/driver/sync_service_crypto.cc
@@ -353,10 +353,4 @@
       weak_factory_.GetWeakPtr(), base::SequencedTaskRunnerHandle::Get());
 }
 
-std::unique_ptr<SyncEncryptionHandler::NigoriState>
-SyncServiceCrypto::TakeSavedNigoriState() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return std::move(state_.saved_nigori_state);
-}
-
 }  // namespace syncer
diff --git a/components/sync/driver/sync_service_crypto.h b/components/sync/driver/sync_service_crypto.h
index 599e2c87..841becd 100644
--- a/components/sync/driver/sync_service_crypto.h
+++ b/components/sync/driver/sync_service_crypto.h
@@ -68,9 +68,6 @@
   // Creates a proxy observer object that will post calls to this thread.
   std::unique_ptr<SyncEncryptionHandler::Observer> GetEncryptionObserverProxy();
 
-  // Takes the previously saved nigori state; null if there isn't any.
-  std::unique_ptr<SyncEncryptionHandler::NigoriState> TakeSavedNigoriState();
-
   PassphraseRequiredReason passphrase_required_reason() const {
     return state_.passphrase_required_reason;
   }
@@ -115,11 +112,6 @@
     // cancel if they e.g. don't remember their explicit passphrase.
     bool encryption_pending = false;
 
-    // Nigori state after user switching to custom passphrase, saved until
-    // transition steps complete. It will be injected into new engine after sync
-    // restart.
-    std::unique_ptr<SyncEncryptionHandler::NigoriState> saved_nigori_state;
-
     // We cache the cryptographer's pending keys whenever
     // NotifyPassphraseRequired is called. This way, before the UI calls
     // SetDecryptionPassphrase on the syncer, it can avoid the overhead of an
diff --git a/components/sync/engine/sync_engine.h b/components/sync/engine/sync_engine.h
index 2b6fc939..2aa5f5c2 100644
--- a/components/sync/engine/sync_engine.h
+++ b/components/sync/engine/sync_engine.h
@@ -74,7 +74,6 @@
     std::unique_ptr<EngineComponentsFactory> engine_components_factory;
     WeakHandle<UnrecoverableErrorHandler> unrecoverable_error_handler;
     base::Closure report_unrecoverable_error_function;
-    std::unique_ptr<SyncEncryptionHandler::NigoriState> saved_nigori_state;
     std::map<ModelType, int64_t> invalidation_versions;
 
     // Initial authoritative values (usually read from prefs).
diff --git a/components/sync/engine/sync_manager.h b/components/sync/engine/sync_manager.h
index 915421b..73fffdc 100644
--- a/components/sync/engine/sync_manager.h
+++ b/components/sync/engine/sync_manager.h
@@ -242,9 +242,6 @@
     // Must outlive SyncManager.
     CancelationSignal* cancelation_signal;
 
-    // Optional nigori state to be restored.
-    std::unique_ptr<SyncEncryptionHandler::NigoriState> saved_nigori_state;
-
     // Define the polling interval. Must not be zero.
     base::TimeDelta poll_interval;
 
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc
index 905b25de..c8f160b 100644
--- a/components/sync/engine_impl/sync_manager_impl.cc
+++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -314,13 +314,6 @@
     return;
   }
 
-  // Now that we have opened the Directory we can restore any previously saved
-  // nigori specifics.
-  if (args->saved_nigori_state) {
-    sync_encryption_handler_->RestoreNigori(*args->saved_nigori_state);
-    args->saved_nigori_state.reset();
-  }
-
   if (args->enable_local_sync_backend) {
     VLOG(1) << "Running against local sync backend.";
     allstatus_.SetLocalBackendFolder(
diff --git a/content/browser/devtools/devtools_protocol_encoding.cc b/content/browser/devtools/devtools_protocol_encoding.cc
index c09f1108..222e717 100644
--- a/content/browser/devtools/devtools_protocol_encoding.cc
+++ b/content/browser/devtools/devtools_protocol_encoding.cc
@@ -9,7 +9,6 @@
 namespace content {
 namespace {
 using ::inspector_protocol_encoding::span;
-using ::inspector_protocol_encoding::Status;
 using ::inspector_protocol_encoding::json::ConvertCBORToJSON;
 using ::inspector_protocol_encoding::json::ConvertJSONToCBOR;
 
@@ -31,17 +30,21 @@
 };
 }  // namespace
 
-Status ConvertCBORToJSON(span<uint8_t> cbor, std::string* json) {
+::inspector_protocol_encoding::Status ConvertCBORToJSON(span<uint8_t> cbor,
+                                                        std::string* json) {
   Platform platform;
   return ConvertCBORToJSON(platform, cbor, json);
 }
 
-Status ConvertJSONToCBOR(span<uint8_t> json, std::string* cbor) {
+::inspector_protocol_encoding::Status ConvertJSONToCBOR(span<uint8_t> json,
+                                                        std::string* cbor) {
   Platform platform;
   return ConvertJSONToCBOR(platform, json, cbor);
 }
 
-Status ConvertJSONToCBOR(span<uint8_t> json, std::vector<uint8_t>* cbor) {
+::inspector_protocol_encoding::Status ConvertJSONToCBOR(
+    span<uint8_t> json,
+    std::vector<uint8_t>* cbor) {
   Platform platform;
   return ConvertJSONToCBOR(platform, json, cbor);
 }
diff --git a/content/browser/devtools/devtools_session.cc b/content/browser/devtools/devtools_session.cc
index 25b5b8b..c5b8e53 100644
--- a/content/browser/devtools/devtools_session.cc
+++ b/content/browser/devtools/devtools_session.cc
@@ -21,7 +21,6 @@
 namespace {
 using ::inspector_protocol_encoding::span;
 using ::inspector_protocol_encoding::SpanFrom;
-using ::inspector_protocol_encoding::Status;
 using ::inspector_protocol_encoding::cbor::AppendString8EntryToCBORMap;
 using ::inspector_protocol_encoding::cbor::IsCBORMessage;
 
@@ -148,7 +147,8 @@
     if (client_->UsesBinaryProtocol()) {
       DCHECK(IsCBORMessage(SpanFrom(message)));
       std::string json;
-      Status status = ConvertCBORToJSON(SpanFrom(message), &json);
+      ::inspector_protocol_encoding::Status status =
+          ConvertCBORToJSON(SpanFrom(message), &json);
       LOG_IF(ERROR, !status.ok()) << status.ToASCIIString();
       proxy_delegate_->SendMessageToBackend(this, json);
       return true;
@@ -163,7 +163,7 @@
     // CBOR (it comes from the client).
     DCHECK(IsCBORMessage(SpanFrom(message)));
   } else {
-    Status status =
+    ::inspector_protocol_encoding::Status status =
         ConvertJSONToCBOR(SpanFrom(message), &converted_cbor_message);
     LOG_IF(ERROR, !status.ok()) << status.ToASCIIString();
     message_to_send = &converted_cbor_message;
@@ -284,7 +284,8 @@
     return;
   }
   std::string json;
-  Status status = ConvertCBORToJSON(SpanFrom(cbor), &json);
+  ::inspector_protocol_encoding::Status status =
+      ConvertCBORToJSON(SpanFrom(cbor), &json);
   LOG_IF(ERROR, !status.ok()) << status.ToASCIIString();
   client->DispatchProtocolMessage(agent_host, json);
 }
@@ -319,7 +320,7 @@
     return;
   }
   std::string json;
-  Status status = ConvertCBORToJSON(cbor, &json);
+  ::inspector_protocol_encoding::Status status = ConvertCBORToJSON(cbor, &json);
   // TODO(johannes): Should we kill renderer if !status.ok() ?
   LOG_IF(ERROR, !status.ok()) << status.ToASCIIString();
   client->DispatchProtocolMessage(agent_host, json);
@@ -356,9 +357,9 @@
     return;
   }
   std::string converted;
-  Status status = client_->UsesBinaryProtocol()
-                      ? ConvertJSONToCBOR(bytes, &converted)
-                      : ConvertCBORToJSON(bytes, &converted);
+  ::inspector_protocol_encoding::Status status =
+      client_->UsesBinaryProtocol() ? ConvertJSONToCBOR(bytes, &converted)
+                                    : ConvertCBORToJSON(bytes, &converted);
   LOG_IF(ERROR, !status.ok()) << status.ToASCIIString();
   client_->DispatchProtocolMessage(agent_host_, converted);
   // |this| may be deleted at this point.
@@ -412,8 +413,8 @@
     return;
   DCHECK(IsCBORMessage(SpanFrom(message)));
   std::string patched(message);
-  Status status = AppendString8EntryToCBORMap(SpanFrom(kSessionId),
-                                              SpanFrom(session_id), &patched);
+  ::inspector_protocol_encoding::Status status = AppendString8EntryToCBORMap(
+      SpanFrom(kSessionId), SpanFrom(session_id), &patched);
   LOG_IF(ERROR, !status.ok()) << status.ToASCIIString();
   if (!status.ok())
     return;
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 3f68561..bf9f376 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -50,7 +50,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_client.h"
-#include "content/public/common/content_switches.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/origin_util.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/ip_endpoint.h"
@@ -554,8 +554,7 @@
     case network::mojom::ReferrerPolicy::kAlways:
       return Network::Request::ReferrerPolicyEnum::UnsafeUrl;
     case network::mojom::ReferrerPolicy::kDefault:
-      if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kReducedReferrerGranularity)) {
+      if (base::FeatureList::IsEnabled(features::kReducedReferrerGranularity)) {
         return Network::Request::ReferrerPolicyEnum::
             StrictOriginWhenCrossOrigin;
       } else {
diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc
index 1221f95b..be6ad2b 100644
--- a/content/browser/loader/resource_loader.cc
+++ b/content/browser/loader/resource_loader.cc
@@ -449,7 +449,7 @@
 
   SSLManager::OnSSLCertificateError(
       weak_ptr_factory_.GetWeakPtr(), info->GetResourceType(), request_->url(),
-      info->GetWebContentsGetterForRequest(), ssl_info, fatal);
+      info->GetWebContentsGetterForRequest(), net_error, ssl_info, fatal);
 }
 
 void ResourceLoader::OnResponseStarted(net::URLRequest* unused, int net_error) {
diff --git a/content/browser/network_service_client.cc b/content/browser/network_service_client.cc
index 8978b46..b1ec081 100644
--- a/content/browser/network_service_client.cc
+++ b/content/browser/network_service_client.cc
@@ -487,6 +487,7 @@
     uint32_t request_id,
     int32_t resource_type,
     const GURL& url,
+    int net_error,
     const net::SSLInfo& ssl_info,
     bool fatal,
     OnSSLCertificateErrorCallback response) {
@@ -496,7 +497,7 @@
       base::BindRepeating(GetWebContents, process_id, routing_id);
   SSLManager::OnSSLCertificateError(
       delegate->GetWeakPtr(), static_cast<ResourceType>(resource_type), url,
-      std::move(web_contents_getter), ssl_info, fatal);
+      std::move(web_contents_getter), net_error, ssl_info, fatal);
 }
 
 #if defined(OS_CHROMEOS)
diff --git a/content/browser/network_service_client.h b/content/browser/network_service_client.h
index 58d907f..e680d29bb 100644
--- a/content/browser/network_service_client.h
+++ b/content/browser/network_service_client.h
@@ -59,6 +59,7 @@
                              uint32_t request_id,
                              int32_t resource_type,
                              const GURL& url,
+                             int net_error,
                              const net::SSLInfo& ssl_info,
                              bool fatal,
                              OnSSLCertificateErrorCallback response) override;
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
index 38d33e7..5643405 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
@@ -46,19 +46,21 @@
 const base::FilePath::CharType kProtobufFilename[] =
     FILE_PATH_LITERAL("font_unique_name_table.pb");
 
-// The unresponsive renderer timeout is 30 seconds (kDefaultCommitTimeout). As a
-// starting point, let's set the max time for indexing fonts to half of that 15
-// seconds. We're starting the font lookup table construction earlier at startup
-// before a renderer requests this.  Once we have more UMA data, we can look
-// into performance details and reducing this timeout. This timeout is meant to
-// cover pathological cases of font indexing where a Windows installation has an
-// unusually large collection of fonts. In practice, building the unique font
-// name table should not take longer than tens of milliseconds (~26 ms on a
-// developer machine, Windows 10, default fonts).
-const base::TimeDelta kFontIndexingTimeout = base::TimeDelta::FromSeconds(15);
+// Timeout after which font scanning and metadata extraction is stopped and the
+// local lookup table is cleared. Font scanning and lookup table construction is
+// only needed pre Windows 10. If the timeout is hit, no local font matching
+// will be performed on this particular pre Win 10 system. Previously we had
+// this set to 15 seconds, which did mean timeouts on a number of systems, but
+// also previously the render was waiting synchronously for a result, which is
+// no longer the case.  Experimentally raise this to 150 to see what scanning
+// times are occurring in the field.
+constexpr base::TimeDelta kFontIndexingTimeoutDefault =
+    base::TimeDelta::FromSeconds(150);
 
-const base::TimeDelta kIndexingSlowDownForTesting =
-    base::TimeDelta::FromMilliseconds(1200);
+// In timeout test case, slow down indexing of one font file to this percentage
+// of the timeout value. Assuming that at least two fonts are indexed, the
+// timeout should be usually hit during indexing the second font.
+constexpr float kIndexingSlowDownForTestingPercentage = 0.75;
 
 bool ExtractCaseFoldedLocalizedStrings(
     IDWriteLocalizedStrings* dwrite_localized_strings,
@@ -128,7 +130,8 @@
 DWriteFontLookupTableBuilder::FontFileWithUniqueNames::FontFileWithUniqueNames(
     DWriteFontLookupTableBuilder::FontFileWithUniqueNames&& other) = default;
 
-DWriteFontLookupTableBuilder::DWriteFontLookupTableBuilder() {
+DWriteFontLookupTableBuilder::DWriteFontLookupTableBuilder()
+    : font_indexing_timeout_(kFontIndexingTimeoutDefault) {
   // In FontUniqueNameBrowserTest the DWriteFontLookupTableBuilder is
   // instantiated to configure the cache directory for testing explicitly before
   // GetContentClient() is available. Catch this case here. It is safe to not
@@ -243,6 +246,10 @@
   factory3_.Reset();
 }
 
+base::TimeDelta DWriteFontLookupTableBuilder::IndexingTimeout() {
+  return font_indexing_timeout_;
+}
+
 base::FilePath DWriteFontLookupTableBuilder::TableCacheFilePath() {
   if (!EnsureCacheDirectory(cache_directory_))
     return base::FilePath();
@@ -344,13 +351,9 @@
 
   start_time_table_ready_ = base::TimeTicks::Now();
 
-  // TODO(https://crbug.com/931366): Downgrade the priority of this startup
-  // task once the UpdatePriority() API for sequenced task runners is in
-  // place. Then bump the priority when the renderer needs the table to be
-  // ready.
   scoped_refptr<base::SequencedTaskRunner> results_collection_task_runner =
       base::CreateSequencedTaskRunnerWithTraits(
-          {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
 
   results_collection_task_runner->PostTask(
@@ -411,7 +414,7 @@
             &DWriteFontLookupTableBuilder::ExtractPathAndNamesFromFamily,
             collection_, family_index, start_time_table_build_,
             slow_down_mode_for_testing_,
-            OptionalOrNullptr(hang_event_for_testing_)),
+            OptionalOrNullptr(hang_event_for_testing_), IndexingTimeout()),
         base::BindOnce(&DWriteFontLookupTableBuilder::
                            AppendFamilyResultAndFinalizeIfNeeded,
                        base::Unretained(this)));
@@ -421,7 +424,7 @@
   timeout_callback_.Reset(base::BindOnce(
       &DWriteFontLookupTableBuilder::OnTimeout, base::Unretained(this)));
   base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, timeout_callback_.callback(), kFontIndexingTimeout);
+      FROM_HERE, timeout_callback_.callback(), IndexingTimeout());
 }
 
 // static
@@ -431,7 +434,8 @@
     uint32_t family_index,
     base::TimeTicks start_time,
     SlowDownMode slow_down_mode_for_testing,
-    base::WaitableEvent* hang_event_for_testing) {
+    base::WaitableEvent* hang_event_for_testing,
+    base::TimeDelta indexing_timeout) {
   TRACE_EVENT0("dwrite,fonts",
                "DWriteFontLookupTableBuilder::ExtractPathAndNamesFromFamily");
 
@@ -440,8 +444,9 @@
 
   DWriteFontLookupTableBuilder::FamilyResult family_result;
 
-  if (base::TimeTicks::Now() - start_time > kFontIndexingTimeout)
+  if (base::TimeTicks::Now() - start_time > indexing_timeout) {
     return family_result;
+  }
 
   Microsoft::WRL::ComPtr<IDWriteFontFamily> family;
   HRESULT hr = collection->GetFontFamily(family_index, &family);
@@ -451,6 +456,10 @@
   UINT32 font_count = family->GetFontCount();
 
   for (UINT32 font_index = 0; font_index < font_count; ++font_index) {
+    if (base::TimeTicks::Now() - start_time > indexing_timeout) {
+      return DWriteFontLookupTableBuilder::FamilyResult();
+    }
+
     Microsoft::WRL::ComPtr<IDWriteFont> font;
     {
       base::ScopedBlockingCall scoped_blocking_call(
@@ -521,11 +530,12 @@
     extract_names(DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME);
     extract_names(DWRITE_INFORMATIONAL_STRING_FULL_NAME);
 
-    if (UNLIKELY(slow_down_mode_for_testing == SlowDownMode::kDelayEachTask))
-      base::PlatformThread::Sleep(kIndexingSlowDownForTesting);
-    else if (UNLIKELY(slow_down_mode_for_testing ==
-                      SlowDownMode::kHangOneTask) &&
-             family_index == 0) {
+    if (UNLIKELY(slow_down_mode_for_testing == SlowDownMode::kDelayEachTask)) {
+      base::PlatformThread::Sleep(indexing_timeout *
+                                  kIndexingSlowDownForTestingPercentage);
+    } else if (UNLIKELY(slow_down_mode_for_testing ==
+                        SlowDownMode::kHangOneTask) &&
+               family_index == 0) {
       base::ScopedAllowBaseSyncPrimitivesForTesting scoped_allow_sync_;
       DCHECK(hang_event_for_testing);
       hang_event_for_testing->Wait();
@@ -591,7 +601,7 @@
       std::move(font_unique_name_table_));
 
   bool timed_out = false;
-  if (base::TimeTicks::Now() - start_time_table_build_ > kFontIndexingTimeout) {
+  if (base::TimeTicks::Now() - start_time_table_build_ > IndexingTimeout()) {
     font_unique_name_table->clear_fonts();
     font_unique_name_table->clear_name_map();
     timed_out = true;
@@ -655,15 +665,18 @@
   FinalizeFontTable();
 }
 
-void DWriteFontLookupTableBuilder::SetSlowDownIndexingForTesting(
-    SlowDownMode slow_down_mode) {
+void DWriteFontLookupTableBuilder::SetSlowDownIndexingForTestingWithTimeout(
+    SlowDownMode slow_down_mode,
+    base::TimeDelta new_timeout) {
   slow_down_mode_for_testing_ = slow_down_mode;
+  font_indexing_timeout_ = new_timeout;
   if (slow_down_mode == SlowDownMode::kHangOneTask)
     hang_event_for_testing_.emplace();
 }
 
 void DWriteFontLookupTableBuilder::ResetLookupTableForTesting() {
   slow_down_mode_for_testing_ = SlowDownMode::kNoSlowdown;
+  font_indexing_timeout_ = kFontIndexingTimeoutDefault;
   font_table_memory_ = base::MappedReadOnlyRegion();
   caching_enabled_ = true;
   font_table_built_.Reset();
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
index 8262f59..e15f8ab 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
@@ -66,8 +66,12 @@
 
   enum class SlowDownMode { kDelayEachTask, kHangOneTask, kNoSlowdown };
 
-  // Slow down each family indexing step for testing the internal timeout.
-  void SetSlowDownIndexingForTesting(SlowDownMode slowdown_mode);
+  // Slow down each family indexing step for testing the internal timeout,
+  // either with a single hung task or by delaying each indexing step. At the
+  // same time, configure a new timeout value for testing, overriding the
+  // default timeout.
+  void SetSlowDownIndexingForTestingWithTimeout(SlowDownMode slowdown_mode,
+                                                base::TimeDelta new_timeout);
 
   // Needed to trigger rebuilding the lookup table, when testing using
   // slowed-down indexing. Otherwise, the test methods would use the already
@@ -136,7 +140,8 @@
       uint32_t family_index,
       base::TimeTicks start_time,
       SlowDownMode slow_down_mode,
-      base::WaitableEvent* hang_event_for_testing);
+      base::WaitableEvent* hang_event_for_testing,
+      base::TimeDelta indexing_timeout);
 
   // Callback from scheduled tasks to add the retrieved font names to the
   // protobuf.
@@ -160,6 +165,8 @@
   // and full font name, in which case we do not need to build this table.
   bool HasDWriteUniqueFontLookups();
 
+  base::TimeDelta IndexingTimeout();
+
   DWriteFontLookupTableBuilder();
   ~DWriteFontLookupTableBuilder();
 
@@ -170,6 +177,7 @@
   base::WaitableEvent font_table_built_;
 
   bool direct_write_initialized_ = false;
+  base::TimeDelta font_indexing_timeout_;
   Microsoft::WRL::ComPtr<IDWriteFontCollection> collection_;
   Microsoft::WRL::ComPtr<IDWriteFactory2> factory2_;
   Microsoft::WRL::ComPtr<IDWriteFactory3> factory3_;
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
index 2e8e8fc..24be3b89 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
@@ -32,6 +32,8 @@
                                                   {u8"NSimSun", 1},
                                                   {u8"calibri-bolditalic", 0}};
 
+constexpr base::TimeDelta kTestingTimeout = base::TimeDelta::FromSeconds(10);
+
 class DWriteFontLookupTableBuilderTest : public testing::Test {
  public:
   DWriteFontLookupTableBuilderTest()
@@ -95,7 +97,8 @@
 }
 
 TEST_P(DWriteFontLookupTableBuilderTimeoutTest, TestTimeout) {
-  font_lookup_table_builder_->SetSlowDownIndexingForTesting(GetParam());
+  font_lookup_table_builder_->SetSlowDownIndexingForTestingWithTimeout(
+      GetParam(), kTestingTimeout);
   font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
   font_lookup_table_builder_->EnsureFontUniqueNameTable();
   base::ReadOnlySharedMemoryRegion font_table_memory =
@@ -119,8 +122,9 @@
         DWriteFontLookupTableBuilder::SlowDownMode::kHangOneTask));
 
 TEST_F(DWriteFontLookupTableBuilderTest, TestReadyEarly) {
-  font_lookup_table_builder_->SetSlowDownIndexingForTesting(
-      DWriteFontLookupTableBuilder::SlowDownMode::kHangOneTask);
+  font_lookup_table_builder_->SetSlowDownIndexingForTestingWithTimeout(
+      DWriteFontLookupTableBuilder::SlowDownMode::kHangOneTask,
+      kTestingTimeout);
   font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
   ASSERT_FALSE(font_lookup_table_builder_->FontUniqueNameTableReady());
   font_lookup_table_builder_->ResumeFromHangForTesting();
diff --git a/content/browser/renderer_host/media/OWNERS b/content/browser/renderer_host/media/OWNERS
index bc81f30..866abc07 100644
--- a/content/browser/renderer_host/media/OWNERS
+++ b/content/browser/renderer_host/media/OWNERS
@@ -1,7 +1,6 @@
 file://media/OWNERS
 
 # WebRTC OWNERS.
-emircan@chromium.org
 guidou@chromium.org
 tommi@chromium.org
 olka@chromium.org
@@ -10,6 +9,7 @@
 per-file *video*=chfremer@chromium.org
 
 # Original (legacy) owner.
+per-file *video*=emircan@chromium.org
 per-file *video*=mcasas@chromium.org
 
 # TEAM: webrtc-dev@chromium.org
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 82c6fcd..abb4caa 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2934,7 +2934,7 @@
     switches::kDefaultTileWidth,
     switches::kDefaultTileHeight,
     switches::kDisable2dCanvasImageChromium,
-    switches::kDisableAcceleratedJpegDecoding,
+    switches::kDisableYUVImageDecoding,
     switches::kDisableAcceleratedVideoDecode,
     switches::kDisableBackgroundTimerThrottling,
     switches::kDisableBestEffortTasks,
@@ -3025,7 +3025,6 @@
     switches::kProfilingAtStart,
     switches::kProfilingFile,
     switches::kProfilingFlush,
-    switches::kReducedReferrerGranularity,
     switches::kRegisterPepperPlugins,
     switches::kRendererStartupDialog,
     switches::kReportVp9AsAnUnsupportedMimeType,
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.cc b/content/browser/scheduler/browser_ui_thread_scheduler.cc
index 16399f71..efbb0d9 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler.cc
+++ b/content/browser/scheduler/browser_ui_thread_scheduler.cc
@@ -54,8 +54,9 @@
 BrowserUIThreadScheduler::BrowserUIThreadScheduler()
     : owned_sequence_manager_(
           base::sequence_manager::CreateUnboundSequenceManager(
-              base::sequence_manager::SequenceManager::Settings{
-                  .message_loop_type = base::MessageLoop::TYPE_UI})),
+              base::sequence_manager::SequenceManager::Settings::Builder()
+                  .SetMessageLoopType(base::MessageLoop::TYPE_UI)
+                  .Build())),
       sequence_manager_(owned_sequence_manager_.get()),
       time_domain_(sequence_manager_->GetRealTimeDomain()) {
   InitialiseTaskQueues();
diff --git a/content/browser/ssl/ssl_error_handler.cc b/content/browser/ssl/ssl_error_handler.cc
index 5fc0b3c..52034f5 100644
--- a/content/browser/ssl/ssl_error_handler.cc
+++ b/content/browser/ssl/ssl_error_handler.cc
@@ -46,6 +46,7 @@
                                  BrowserThread::ID delegate_thread,
                                  ResourceType resource_type,
                                  const GURL& url,
+                                 int net_error,
                                  const net::SSLInfo& ssl_info,
                                  bool fatal)
     : delegate_(delegate),
@@ -53,7 +54,7 @@
       request_url_(url),
       resource_type_(resource_type),
       ssl_info_(ssl_info),
-      cert_error_(net::MapCertStatusToNetError(ssl_info.cert_status)),
+      cert_error_(net_error),
       fatal_(fatal),
       web_contents_(web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/browser/ssl/ssl_error_handler.h b/content/browser/ssl/ssl_error_handler.h
index 6cfe07c0..defed88 100644
--- a/content/browser/ssl/ssl_error_handler.h
+++ b/content/browser/ssl/ssl_error_handler.h
@@ -53,6 +53,7 @@
                   BrowserThread::ID delegate_thread,
                   ResourceType resource_type,
                   const GURL& url,
+                  int net_error,
                   const net::SSLInfo& ssl_info,
                   bool fatal);
 
diff --git a/content/browser/ssl/ssl_manager.cc b/content/browser/ssl/ssl_manager.cc
index 7bf4d27f..7740bf940 100644
--- a/content/browser/ssl/ssl_manager.cc
+++ b/content/browser/ssl/ssl_manager.cc
@@ -110,12 +110,13 @@
     BrowserThread::ID delegate_thread,
     const ResourceType resource_type,
     const GURL& url,
+    int net_error,
     const net::SSLInfo& ssl_info,
     bool fatal) {
   content::WebContents* web_contents = web_contents_getter.Run();
   std::unique_ptr<SSLErrorHandler> handler(
       new SSLErrorHandler(web_contents, delegate, delegate_thread,
-                          resource_type, url, ssl_info, fatal));
+                          resource_type, url, net_error, ssl_info, fatal));
 
   if (!web_contents) {
     // Requests can fail to dispatch because they don't have a WebContents. See
@@ -154,18 +155,18 @@
     const ResourceType resource_type,
     const GURL& url,
     const base::Callback<WebContents*(void)>& web_contents_getter,
+    int net_error,
     const net::SSLInfo& ssl_info,
     bool fatal) {
   DCHECK(delegate.get());
-  DVLOG(1) << "OnSSLCertificateError() cert_error: "
-           << net::MapCertStatusToNetError(ssl_info.cert_status)
+  DVLOG(1) << "OnSSLCertificateError() cert_error: " << net_error
            << " resource_type: " << static_cast<int>(resource_type)
            << " url: " << url.spec() << " cert_status: " << std::hex
            << ssl_info.cert_status;
 
   if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
     HandleSSLErrorOnUI(web_contents_getter, delegate, BrowserThread::UI,
-                       resource_type, url, ssl_info, fatal);
+                       resource_type, url, net_error, ssl_info, fatal);
     return;
   }
 
@@ -174,7 +175,8 @@
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
       base::BindOnce(&HandleSSLErrorOnUI, web_contents_getter, delegate,
-                     BrowserThread::IO, resource_type, url, ssl_info, fatal));
+                     BrowserThread::IO, resource_type, url, net_error, ssl_info,
+                     fatal));
 }
 
 // static
@@ -183,12 +185,13 @@
     const GURL& url,
     int render_process_id,
     int render_frame_id,
+    int net_error,
     const net::SSLInfo& ssl_info,
     bool fatal) {
   OnSSLCertificateError(delegate, ResourceType::kSubResource, url,
                         base::Bind(&WebContentsImpl::FromRenderFrameHostID,
                                    render_process_id, render_frame_id),
-                        ssl_info, fatal);
+                        net_error, ssl_info, fatal);
 }
 
 SSLManager::SSLManager(NavigationControllerImpl* controller)
diff --git a/content/browser/ssl/ssl_manager.h b/content/browser/ssl/ssl_manager.h
index f7d35e8..eeb0c93a 100644
--- a/content/browser/ssl/ssl_manager.h
+++ b/content/browser/ssl/ssl_manager.h
@@ -51,6 +51,7 @@
       ResourceType resource_type,
       const GURL& url,
       const base::Callback<WebContents*(void)>& web_contents_getter,
+      int net_error,
       const net::SSLInfo& ssl_info,
       bool fatal);
 
@@ -62,6 +63,7 @@
       const GURL& url,
       int render_process_id,
       int render_frame_id,
+      int net_error,
       const net::SSLInfo& ssl_info,
       bool fatal);
 
diff --git a/content/browser/webrtc/OWNERS b/content/browser/webrtc/OWNERS
index 11f9e9d..99868f9 100644
--- a/content/browser/webrtc/OWNERS
+++ b/content/browser/webrtc/OWNERS
@@ -1,11 +1,11 @@
 chfremer@chromium.org
-emircan@chromium.org
 guidou@chromium.org
 tommi@chromium.org
 
 per-file *test*=phoglund@chromium.org
 
 # Original (legacy) owner.
+emircan@chromium.org
 mcasas@chromium.org
 
 # COMPONENT: Blink>WebRTC
diff --git a/content/browser/webrtc/webrtc_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
index f627aac..ca2a8dc 100644
--- a/content/browser/webrtc/webrtc_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
@@ -23,16 +23,20 @@
 
 namespace content {
 
+// Disable FocusDistance test which fails with Logitec cameras.
+// TODO(crbug.com/957020): renable these tests when we have a way to detect
+// which device is connected and hence avoid running it if the camera is
+// Logitech.
+#define MAYBE_ManipulateFocusDistance DISABLED_ManipulateFocusDistance
+
 #if defined(OS_ANDROID)
 // TODO(crbug.com/793859): Re-enable test on Android as soon as the cause for
 // the bug is understood and fixed.
 #define MAYBE_ManipulateZoom DISABLED_ManipulateZoom
 #define MAYBE_ManipulateExposureTime DISABLED_ManipulateExposureTime
-#define MAYBE_ManipulateFocusDistance DISABLED_ManipulateFocusDistance
 #else
 #define MAYBE_ManipulateZoom ManipulateZoom
 #define MAYBE_ManipulateExposureTime ManipulateExposureTime
-#define MAYBE_ManipulateFocusDistance ManipulateFocusDistance
 #endif
 
 namespace {
@@ -159,18 +163,6 @@
     }
   }
 
-  bool RunImageCaptureTestCase(const std::string& command) override {
-    // TODO(chfremer): Enable test cases using the video capture service with
-    // real cameras as soon as root cause for https://crbug.com/733582 is
-    // understood and resolved.
-    if ((std::get<0>(GetParam()) == TargetCamera::REAL_WEBCAM) &&
-        (std::get<1>(GetParam()).use_video_capture_service)) {
-      LOG(INFO) << "Skipping this test case";
-      return true;
-    }
-    return WebRtcImageCaptureBrowserTestBase::RunImageCaptureTestCase(command);
-  }
-
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 
diff --git a/content/browser/websockets/websocket_manager.cc b/content/browser/websockets/websocket_manager.cc
index d7f7255..ebe947e7 100644
--- a/content/browser/websockets/websocket_manager.cc
+++ b/content/browser/websockets/websocket_manager.cc
@@ -57,13 +57,14 @@
       const GURL& url,
       int child_id,
       int frame_id,
+      int net_error,
       const net::SSLInfo& ssl_info,
       bool fatal) override {
     ssl_error_handler_delegate_ =
         std::make_unique<SSLErrorHandlerDelegate>(std::move(callbacks));
     SSLManager::OnSSLCertificateSubresourceError(
         ssl_error_handler_delegate_->GetWeakPtr(), url, child_id, frame_id,
-        ssl_info, fatal);
+        net_error, ssl_info, fatal);
   }
 
   void ReportBadMessage(BadMessageReason reason,
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 969374a..ddd7b411 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -101,7 +101,7 @@
   if (command_line.HasSwitch(switches::kDisableFileSystem))
     WebRuntimeFeatures::EnableFileSystem(false);
 
-  if (!command_line.HasSwitch(switches::kDisableAcceleratedJpegDecoding))
+  if (!command_line.HasSwitch(switches::kDisableYUVImageDecoding))
     WebRuntimeFeatures::EnableDecodeToYUV(true);
 
 #if defined(SUPPORT_WEBGL2_COMPUTE_CONTEXT)
@@ -166,8 +166,8 @@
     WebRuntimeFeatures::EnableNetInfoDownlinkMax(true);
   }
 
-  if (command_line.HasSwitch(switches::kReducedReferrerGranularity))
-    WebRuntimeFeatures::EnableReducedReferrerGranularity(true);
+  WebRuntimeFeatures::EnableReducedReferrerGranularity(
+      base::FeatureList::IsEnabled(features::kReducedReferrerGranularity));
 
   if (command_line.HasSwitch(switches::kDisablePermissionsAPI))
     WebRuntimeFeatures::EnablePermissionsAPI(false);
@@ -235,10 +235,6 @@
           features::kPassiveDocumentWheelEventListeners));
 
   WebRuntimeFeatures::EnableFeatureFromString(
-      "FontCacheScaling",
-      base::FeatureList::IsEnabled(features::kFontCacheScaling));
-
-  WebRuntimeFeatures::EnableFeatureFromString(
       "FontSrcLocalMatching",
       base::FeatureList::IsEnabled(features::kFontSrcLocalMatching));
 
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 4a1fdcc5..358facd 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -152,11 +152,6 @@
 const base::Feature kFeaturePolicyForSandbox{"FeaturePolicyForSandbox",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables a blink::FontCache optimization that reuses a font to serve different
-// size of font.
-const base::Feature kFontCacheScaling{"FontCacheScaling",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Enables fixes for matching src: local() for web fonts correctly against full
 // font name or postscript name. Rolling out behind a flag, as enabling this
 // enables a font indexer on Android which we need to test in the field first.
@@ -374,6 +369,11 @@
 const base::Feature kRasterInducingScroll{"RasterInducingScroll",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Reduce the amount of information in the default 'referer' header for
+// cross-origin requests.
+const base::Feature kReducedReferrerGranularity{
+    "ReducedReferrerGranularity", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // RenderDocument:
 //
 // Currently, a RenderFrameHost represents neither a frame nor a document, but a
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 7a9d778..fd48b2d3 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -45,7 +45,6 @@
 CONTENT_EXPORT extern const base::Feature kExpensiveBackgroundTimerThrottling;
 CONTENT_EXPORT extern const base::Feature kExtendedMouseButtons;
 CONTENT_EXPORT extern const base::Feature kFeaturePolicyForSandbox;
-CONTENT_EXPORT extern const base::Feature kFontCacheScaling;
 CONTENT_EXPORT extern const base::Feature kFontSrcLocalMatching;
 CONTENT_EXPORT extern const base::Feature kGuestViewCrossProcessFrames;
 CONTENT_EXPORT extern const base::Feature kHeapCompaction;
@@ -88,6 +87,7 @@
 CONTENT_EXPORT extern const base::Feature
     kProcessSharingWithStrictSiteInstances;
 CONTENT_EXPORT extern const base::Feature kRasterInducingScroll;
+CONTENT_EXPORT extern const base::Feature kReducedReferrerGranularity;
 CONTENT_EXPORT extern const base::Feature kRenderDocumentForMainFrame;
 CONTENT_EXPORT extern const base::Feature kRenderDocumentForSubframe;
 CONTENT_EXPORT extern const base::Feature kRenderingPipelineThrottling;
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index ba8877a..1075838 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -319,11 +319,9 @@
 // Disable antialiasing on 2d canvas clips
 const char kDisable2dCanvasClipAntialiasing[] = "disable-2d-canvas-clip-aa";
 
-// Disable partially decoding jpeg images using the GPU.
-// At least YUV decoding will be accelerated when not using this flag.
+// Disable YUV image decoding for those formats and cases where it's supported.
 // Has no effect unless GPU rasterization is enabled.
-const char kDisableAcceleratedJpegDecoding[] =
-    "disable-accelerated-jpeg-decoding";
+const char kDisableYUVImageDecoding[] = "disable-yuv-image-decoding";
 
 // Logs Runtime Call Stats for Blink. --single-process also needs to be
 // used along with this for the stats to be logged.
@@ -715,10 +713,6 @@
 // that's needed to show a dialog.
 const char kRendererStartupDialog[]         = "renderer-startup-dialog";
 
-// Reduce the default `referer` header's granularity.
-const char kReducedReferrerGranularity[] =
-  "reduced-referrer-granularity";
-
 // Causes the process to run as a sandbox IPC subprocess.
 const char kSandboxIPCProcess[]             = "sandbox-ipc";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 201793b8..57b89770 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -32,7 +32,7 @@
 CONTENT_EXPORT extern const char kDisable2dCanvasImageChromium[];
 CONTENT_EXPORT extern const char kDisable3DAPIs[];
 CONTENT_EXPORT extern const char kDisableAccelerated2dCanvas[];
-CONTENT_EXPORT extern const char kDisableAcceleratedJpegDecoding[];
+CONTENT_EXPORT extern const char kDisableYUVImageDecoding[];
 CONTENT_EXPORT extern const char kDisableAcceleratedVideoDecode[];
 CONTENT_EXPORT extern const char kDisableAcceleratedVideoEncode[];
 extern const char kDisableBackingStoreLimit[];
diff --git a/content/public/common/referrer.cc b/content/public/common/referrer.cc
index 1c22b85..534a755 100644
--- a/content/public/common/referrer.cc
+++ b/content/public/common/referrer.cc
@@ -7,7 +7,7 @@
 #include <string>
 
 #include "base/command_line.h"
-#include "content/public/common/content_switches.h"
+#include "content/public/common/content_features.h"
 #include "services/network/loader_util.h"
 
 namespace content {
@@ -17,8 +17,7 @@
                                       const Referrer& referrer) {
   Referrer sanitized_referrer(referrer.url.GetAsReferrer(), referrer.policy);
   if (sanitized_referrer.policy == network::mojom::ReferrerPolicy::kDefault) {
-    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kReducedReferrerGranularity)) {
+    if (base::FeatureList::IsEnabled(features::kReducedReferrerGranularity)) {
       sanitized_referrer.policy = network::mojom::ReferrerPolicy::
           kNoReferrerWhenDowngradeOriginWhenCrossOrigin;
     } else {
@@ -114,8 +113,7 @@
       return net::URLRequest::
           ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
     case network::mojom::ReferrerPolicy::kDefault:
-      if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kReducedReferrerGranularity)) {
+      if (base::FeatureList::IsEnabled(features::kReducedReferrerGranularity)) {
         return net::URLRequest::
             REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
       }
@@ -157,8 +155,7 @@
 }
 
 net::URLRequest::ReferrerPolicy Referrer::GetDefaultReferrerPolicy() {
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kReducedReferrerGranularity)) {
+  if (base::FeatureList::IsEnabled(features::kReducedReferrerGranularity)) {
     return net::URLRequest::
         REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
   }
diff --git a/content/renderer/media/OWNERS b/content/renderer/media/OWNERS
index d0c029f..f6ac99b1 100644
--- a/content/renderer/media/OWNERS
+++ b/content/renderer/media/OWNERS
@@ -2,7 +2,6 @@
 miu@chromium.org
 
 # WebRTC OWNERS.
-emircan@chromium.org
 hbos@chromium.org
 perkj@chromium.org
 tommi@chromium.org
@@ -10,4 +9,7 @@
 olka@chromium.org
 maxmorin@chromium.org
 
+# Original (legacy) owner.
+emircan@chromium.org
+
 # COMPONENT: Internals>Media
diff --git a/content/renderer/media/video_capture/OWNERS b/content/renderer/media/video_capture/OWNERS
index 8bc343a..0a6f62e 100644
--- a/content/renderer/media/video_capture/OWNERS
+++ b/content/renderer/media/video_capture/OWNERS
@@ -1,5 +1,4 @@
 chfremer@chromium.org
-emircan@chromium.org
 guidou@chromium.org
 
 # COMPONENT: Blink>GetUserMedia>Webcam
diff --git a/content/renderer/media_capture_from_element/OWNERS b/content/renderer/media_capture_from_element/OWNERS
index d176cccc..e2687ab 100644
--- a/content/renderer/media_capture_from_element/OWNERS
+++ b/content/renderer/media_capture_from_element/OWNERS
@@ -1,6 +1,7 @@
-emircan@chromium.org
+guidou@chromium.org
 
 # Original (legacy) owner.
+emircan@chromium.org
 mcasas@chromium.org
 
 # COMPONENT: Blink>MediaStream>CaptureFromElement
diff --git a/content/renderer/media_recorder/OWNERS b/content/renderer/media_recorder/OWNERS
index 75beff3..9c893fc1 100644
--- a/content/renderer/media_recorder/OWNERS
+++ b/content/renderer/media_recorder/OWNERS
@@ -1,6 +1,7 @@
-emircan@chromium.org
+guidou@chromium.org
 
 # Original (legacy) owner.
+emircan@chromium.org
 mcasas@chromium.org
 
 # COMPONENT: Blink>MediaRecording
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 510148d0..d450e160 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -320,6 +320,18 @@
 
     # This is an OpenGL driver bug on Intel platform and it is fixed in
     # Intel Driver 25.20.100.6444.
+    # Case no-over-optimization-on-uniform-array-08 always fail if run
+    # case gl-min-uniforms first.
+    # Temporarily skip these two cases now.
+    self.Skip(
+        'conformance/limits/gl-min-uniforms.html',
+        ['win', 'intel', 'opengl', 'passthrough'], bug=953243)
+    self.Skip(
+        'conformance/uniforms/no-over-optimization-on-uniform-array-08.html',
+        ['win', 'intel', 'opengl', 'passthrough'], bug=953243)
+
+    # This is an OpenGL driver bug on Intel platform and it is fixed in
+    # Intel Driver 25.20.100.6444.
     # Case no-over-optimization-on-uniform-array-09 always fail if run
     # case biuDepthRange_001_to_002 first.
     # Temporarily skip these two cases now because this issue blocks
@@ -419,9 +431,6 @@
         ['win', 'passthrough', 'opengl', 'intel'], bug=602688)
     self.Fail('deqp/functional/gles3/shaderbuiltinvar.html', # ANGLE bug
         ['win', 'passthrough', 'opengl', 'intel'], bug=2880)
-    self.Skip(
-        'conformance/uniforms/no-over-optimization-on-uniform-array-08.html',
-        ['win', 'intel', 'opengl', 'passthrough'], bug=907195)
 
     # Passthrough command decoder / Linux / OpenGL / NVIDIA
     self.Fail('conformance/textures/image_bitmap_from_video/' +
diff --git a/device/fido/README.md b/device/fido/README.md
index fc1eaaa..621a1af8 100644
--- a/device/fido/README.md
+++ b/device/fido/README.md
@@ -1,39 +1,36 @@
-# FIDO
+# Security Keys
 
-`//device/fido` contains abstractions for [FIDO](https://fidoalliance.org/)
-security keys across multiple platforms.
+Security keys are physical devices that often connect via USB and have a button. They can generate public keys and sign with them to authenticate a user and are most often used as a second factor for security.
 
-## U2F Security Keys
+Websites interact with them via two APIs: the older [U2F API](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-javascript-api-v1.2-ps-20170411.html) and the modern [W3C Webauthn API](https://www.w3.org/TR/webauthn/). In Chromium, the U2F API is not directly supported but it can be used by using `postMessage` with an internal extension called [cryptotoken](/chrome/browser/resources/cryptotoken/). Webauthn is supported by Blink and is part of [CredMan](https://www.w3.org/TR/credential-management-1/).
 
-Support for [U2F (FIDO
-1.2)](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-overview-v1.2-ps-20170411.html)
-security keys is present for both USB Human Interface Devices (USB HID) and
-Bluetooth Low Energy (BLE) devices. Clients can perform U2F operations using the
-[`U2fRegister`](u2f_register.h) and [`U2fSign`](u2f_sign.h) classes. These
-abstractions automatically perform device discovery and handle communication
-with the underlying devices. Talking to HID devices is done using the [HID Mojo
-service](/services/device/public/interfaces/hid.mojom), while communication with
-BLE devices is done using abstractions found in
-[`//device/bluetooth`](/device/bluetooth/). HID is supported on all desktop
-platforms, while BLE lacks some support on Windows (see
-[`//device/bluetooth/README.md`](/device/bluetooth/README.md) for details).
+(Historically cryptotoken contained a complete stack that interacted with USB devices directly. Now, however, it's a wrapper layer over the Webauthn APIs.)
 
-## CTAP Security Keys
+Several different types of security keys are supported. Older security keys implement the [U2F protocol](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html) while more modern ones implement [CTAP2](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html). These devices can work over USB, Bluetooth Low Energy (BLE), or NFC (not supported). Additionally Chromium contains support for using Touch ID on macOS as a security key as well support for forwarding requests to the native libraries on modern versions of Windows.
 
-Support for [CTAP2 (FIDO
-2.0)](https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html)
-security keys is in active development and aims to unify the implementations for
-both U2F and CTAP keys.
+## Life of a request
 
-## Testing
+This section provides a coarse roadmap for understanding the code involved in security key support by highlighting the path that a login request might take.
 
-### Unit Tests
+Firstly, the CredMan `get` call ends up in [`CredentialsContainer::get`](https://cs.chromium.org/search/?q=symbol:CredentialsContainer::get+exact:yes&det=matsel&sq=package:chromium&type=cs). CredMan supports several types of credentials but the code dealing with `publicKey` relates to security key support.
 
-Standard use of `*_unittest.cc` files for must code coverage. Files prefixed
-with `mock_` provide GoogleMock based fake objects for easy mocking of
-dependencies during testing.
+The request is packaged into a Mojo call defined in [authenticator.mojom](/third_party/blink/public/mojom/webauthn/authenticator.mojom). On Android, that Mojo request is handled by Android-specific code and is forwarded to support libraries in Google Play Services. Otherwise the Mojo interface will be bound to [`AuthenticatorCommon`](/content/browser/webauth/authenticator_common.cc); specifically it'll call [`AuthenticatorCommon::GetAssertion`](https://cs.chromium.org/search/?q=symbol:AuthenticatorCommon::GetAssertion+exact:yes&sq=package:chromium).
 
-### Fuzzers
+AuthenticatorCommon is part of Chromium's [content layer](https://www.chromium.org/developers/content-module) and so [calls into the embedder](https://cs.chromium.org/search/?q=symbol:GetWebAuthenticationRequestDelegate+exact:yes&sq=package:chromium) to get a [delegate object](https://cs.chromium.org/search/?q=symbol:AuthenticatorRequestClientDelegate+exact:yes) that allows it to perform actions like showing UI. It also triggers the lower-level code to start the process of finding an authenticator to handle the request. For an assertion request it'll create a [`GetAssertionRequestHandler`](https://cs.chromium.org/search/?q=symbol:GetAssertionRequestHandler+exact:yes) from this directory.
+
+The `Handler` classes manage a specific user action and their first job is to [initiate discovery](https://cs.chromium.org/search/?q=symbol:FidoRequestHandlerBase::InitDiscoveries+exact:yes) of possible security keys. The discovery process will find candidate USB, BLE, Touch ID, etc devices, each of which will be fed into [`DispatchRequest`](https://cs.chromium.org/search/?q=symbol:GetAssertionRequestHandler::DispatchRequest+exact:yes). Different actions may be taken depending on features of the discovered authenticator. For example, an authenticator which cannot handle the request may be asked to wait for a touch so that the user can still select it, even though it'll cause the request to fail. These per-authenticator operations will be dispatched via the abstract [`FidoAuthenticator`](https://cs.chromium.org/search/?q=symbol:FidoAuthenticator+exact:yes) interface.
+
+If a per-authenticator operation is complex and requires several steps it will be handled by a &ldquo;task&rdquo;. In this example, a [`GetAssertionTask`](https://cs.chromium.org/search/?q=symbol:device::GetAssertionTask+exact:yes) will likely be created by a [`FidoDeviceAuthenticator`](https://cs.chromium.org/search/?q=symbol:device::FidoDeviceAuthenticator+exact:yes), the implementation of `FidoAuthenticator` used by physical devices.
+
+The assertion task knows how to sequence a series of U2F or CTAP2 operations to implement an assertion request. In the case of U2F, there will be another layer of state machines in, e.g., [`U2fSignOperation`](https://cs.chromium.org/search/?q=symbol:device::U2FSignOperation+exact:yes) because U2F has a historical authenticator model.
+
+If interaction with UI is required, for example to prompt for a PIN, the handler will make calls via the [`Observer`](https://cs.chromium.org/search/?q=symbol:device::FidoRequestHandlerBase::Observer+exact:yes) interface, which is implemented by the embedder's UI objects that were created by `AuthenticatorCommon`.
+
+## Settings
+
+It's also possible for security key operations to be triggered by actions in the Settings UI: there are several security key actions that can be taken on `chrome://settings/securityKeys`. In this case, calls from the Javascript that implements the Settings UI end up in [`SecurityKeysHandler`](https://cs.chromium.org/search/?q=symbol:settings::SecurityKeysHandler+exact:yes), which then operates in the same way as `AuthenticatorCommon`, albeit without creating any native UI.
+
+## Fuzzers
 
 [libFuzzer] tests are in `*_fuzzer.cc` files. They test for bad input from
 devices, e.g. when parsing responses to register or sign operations.
diff --git a/docs/mac_build_instructions.md b/docs/mac_build_instructions.md
index 9003d19..3ab976e 100644
--- a/docs/mac_build_instructions.md
+++ b/docs/mac_build_instructions.md
@@ -54,7 +54,6 @@
 
 ```shell
 $ mkdir chromium && cd chromium
-$ git config --global core.precomposeUnicode true
 ```
 
 Run the `fetch` tool from `depot_tools` to check out the code and its
@@ -65,7 +64,8 @@
 ```
 
 If you don't need the full repo history, you can save time by using
-`fetch --no-history chromium`.
+`fetch --no-history chromium`. You can call `git fetch --unshallow` to retrieve
+the full history later.
 
 Expect the command to take 30 minutes on even a fast connection, and many
 hours on slower ones.
diff --git a/docs/speed/benchmark/harnesses/system_health.md b/docs/speed/benchmark/harnesses/system_health.md
index a2d5cc52..a896b8c 100644
--- a/docs/speed/benchmark/harnesses/system_health.md
+++ b/docs/speed/benchmark/harnesses/system_health.md
@@ -41,19 +41,29 @@
 team to track key user metrics on our user stories.
 
 
+## How do I debug System Health regressions?
+
+System health benchmarks run test cases against Chrome's key performance metrics.
+There is more documentation about the metrics and debugging information for
+regressions in the documentation in the docs for these benchmarks:
+* [Memory](../../../memory-infra/memory_benchmarks.md) - memory:* metrics
+* [Loading](loading.md) - timeToFirstContentfulPaint, timeToFirstMeaningfulPaint
+* [Power](power_perf.md) - cpu_time_percentage_avg
+
+
 ## Where are the System Health stories?
 
 All the System Health stories are located in
-[tools/perf/page_sets/system_health/](../../../tools/perf/page_sets/system_health/).
+[tools/perf/page_sets/system_health/](../../../../tools/perf/page_sets/system_health/).
 
 There are few groups of stories:
-1. [Accessibility stories](../../../tools/perf/page_sets/system_health/accessibility_stories.py)
-2. [Background stories](../../../tools/perf/page_sets/system_health/background_stories.py)
-3. [Browsing stories](../../../tools/perf/page_sets/system_health/browsing_stories.py)
-4. [Chrome stories](../../../tools/perf/page_sets/system_health/chrome_stories.py)
-5. [Loading stories](../../../tools/perf/page_sets/system_health/loading_stories.py)
-6. [Multi-tab stories](../../../tools/perf/page_sets/system_health/multi_tab_stories.py)
-7. [Media stories](../../../tools/perf/page_sets/system_health/media_stories.py)
+1. [Accessibility stories](../../../../tools/perf/page_sets/system_health/accessibility_stories.py)
+2. [Background stories](../../../../tools/perf/page_sets/system_health/background_stories.py)
+3. [Browsing stories](../../../../tools/perf/page_sets/system_health/browsing_stories.py)
+4. [Chrome stories](../../../../tools/perf/page_sets/system_health/chrome_stories.py)
+5. [Loading stories](../../../../tools/perf/page_sets/system_health/loading_stories.py)
+6. [Multi-tab stories](../../../../tools/perf/page_sets/system_health/multi_tab_stories.py)
+7. [Media stories](../../../../tools/perf/page_sets/system_health/media_stories.py)
 
 ## What is the structure of a System Health story?
 A System Health story is a subclass of
@@ -94,7 +104,7 @@
 
 In addition, each story also has accompanied tags that define its important
 characteristics.
-[Tags](../../../tools/perf/page_sets/system_health/story_tags.py) are used as
+[Tags](../../../../tools/perf/page_sets/system_health/story_tags.py) are used as
 the way to track coverage of System Health stories, so they should be as
 detailed as needed to distinguish each System Health story from the others.
 
@@ -131,7 +141,7 @@
 out why the story fails, fix it and re-land the patch.
 
 Add new SystemHealthStory subclass(es) to either one of the existing files or a
-new file in [tools/perf/page_sets/system_health/](../../tools/perf/page_sets/system_health).
+new file in [tools/perf/page_sets/system_health/](../../../../tools/perf/page_sets/system_health).
 The new class(es) will automatically be picked up and added to the story set.
 To run the story through the memory benchmark against live sites, use the
 following commands:
@@ -174,7 +184,7 @@
 
 The recordings are stored in `system_health_desktop_MMM.wprgo` and
 `system_health_mobile_NNN.wprgo` files in the
-[tools/perf/page_sets/data](../../../tools/perf/page_sets/data) directory.
+[tools/perf/page_sets/data](../../../../tools/perf/page_sets/data) directory.
 You can find the MMM and NNN values by inspecting the changes to
 `system_health_desktop.json` and `system_health_mobile.json`:
 
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index dbac16c..560aafd 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -152,6 +152,7 @@
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "ios/public/provider/chrome/browser/distribution/app_distribution_provider.h"
 #include "ios/public/provider/chrome/browser/mailto/mailto_handler_provider.h"
+#import "ios/public/provider/chrome/browser/overrides_provider.h"
 #include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
 #import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h"
 #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
@@ -793,6 +794,8 @@
         notifyForeground:_mainBrowserState];
   }
 
+  ios::GetChromeBrowserProvider()->GetOverridesProvider()->InstallOverrides();
+
   [self scheduleLowPriorityStartupTasks];
 
   [_browserViewWrangler updateDeviceSharingManager];
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index ea960373..007b9a1b 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1073,6 +1073,9 @@
       <message name="IDS_IOS_OPTIONS_PRELOAD_WEBPAGES" desc="Title for opening the setting for if/when to preload webpages. [Length: 20em] [iOS only]">
         Preload Webpages
       </message>
+      <message name="IDS_IOS_OPTIONS_PRIVACY_GOOGLE_SERVICES_FOOTER" desc="Footer to invite the user to open the Sync and Google Services settings.">
+        For more settings that relate to privacy, security, and data collection, see <ph name="BEGIN_LINK">BEGIN_LINK</ph>Sync and Google Services<ph name="END_LINK">END_LINK</ph>.
+      </message>
       <message name="IDS_IOS_OPTIONS_REPORT_AN_ISSUE" desc="Title for the option on Settings page to report an issue. [Length: 20em] [iOS only]">
         Report an Issue
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_OPTIONS_PRIVACY_GOOGLE_SERVICES_FOOTER.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_OPTIONS_PRIVACY_GOOGLE_SERVICES_FOOTER.png.sha1
new file mode 100644
index 0000000..26fa1b48
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_OPTIONS_PRIVACY_GOOGLE_SERVICES_FOOTER.png.sha1
@@ -0,0 +1 @@
+d3b8b81f6f3c89161e65b9c72b694ebfe8489efc
\ No newline at end of file
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/about_flags.mm
index f648ba0..2f83fbd 100644
--- a/ios/chrome/browser/about_flags.mm
+++ b/ios/chrome/browser/about_flags.mm
@@ -429,33 +429,6 @@
     {"non-modal-dialogs", flag_descriptions::kNonModalDialogsName,
      flag_descriptions::kNonModalDialogsDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(dialogs::kNonModalDialogs)},
-    {"sync-pseudo-uss-favicons", flag_descriptions::kSyncPseudoUSSFaviconsName,
-     flag_descriptions::kSyncPseudoUSSFaviconsDescription, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSFavicons)},
-    {"sync-pseudo-uss-history-delete-directives",
-     flag_descriptions::kSyncPseudoUSSHistoryDeleteDirectivesName,
-     flag_descriptions::kSyncPseudoUSSHistoryDeleteDirectivesDescription,
-     flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSHistoryDeleteDirectives)},
-    {"sync-pseudo-uss-preferences",
-     flag_descriptions::kSyncPseudoUSSPreferencesName,
-     flag_descriptions::kSyncPseudoUSSPreferencesDescription, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSPreferences)},
-    {"sync-pseudo-uss-priority-preferences",
-     flag_descriptions::kSyncPseudoUSSPriorityPreferencesName,
-     flag_descriptions::kSyncPseudoUSSPriorityPreferencesDescription,
-     flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSPriorityPreferences)},
-    {"use-nsurlsession-for-signin",
-     flag_descriptions::kUseNSURLSessionForGaiaSigninRequestsName,
-     flag_descriptions::kUseNSURLSessionForGaiaSigninRequestsDescription,
-     flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(kUseNSURLSessionForGaiaSigninRequests)},
-    {"sync-pseudo-uss-supervised-users",
-     flag_descriptions::kSyncPseudoUSSSupervisedUsersName,
-     flag_descriptions::kSyncPseudoUSSSupervisedUsersDescription,
-     flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(switches::kSyncPseudoUSSSupervisedUsers)},
     {"detect-main-thread-freeze",
      flag_descriptions::kDetectMainThreadFreezeName,
      flag_descriptions::kDetectMainThreadFreezeDescription, flags_ui::kOsIos,
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
index 85e7e33..0a98500 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -96,33 +96,6 @@
 const char kEnableSyncUSSPasswordsDescription[] =
     "Enables the new, experimental implementation of password sync";
 
-const char kSyncPseudoUSSFaviconsName[] = "Enable pseudo-USS for favicon sync.";
-const char kSyncPseudoUSSFaviconsDescription[] =
-    "Enable new USS-based codepath for sync datatypes FAVICON_IMAGES and "
-    "FAVICON_TRACKING.";
-
-const char kSyncPseudoUSSHistoryDeleteDirectivesName[] =
-    "Enable pseudo-USS for HISTORY_DELETE_DIRECTIVES sync.";
-const char kSyncPseudoUSSHistoryDeleteDirectivesDescription[] =
-    "Enable new USS-based codepath for sync datatype "
-    "HISTORY_DELETE_DIRECTIVES.";
-
-const char kSyncPseudoUSSPreferencesName[] =
-    "Enable pseudo-USS for PREFERENCES sync.";
-const char kSyncPseudoUSSPreferencesDescription[] =
-    "Enable new USS-based codepath for sync datatype PREFERENCES.";
-
-const char kSyncPseudoUSSPriorityPreferencesName[] =
-    "Enable pseudo-USS for PRIORITY_PREFERENCES sync.";
-const char kSyncPseudoUSSPriorityPreferencesDescription[] =
-    "Enable new USS-based codepath for sync datatype PRIORITY_PREFERENCES.";
-
-const char kSyncPseudoUSSSupervisedUsersName[] =
-    "Enable pseudo-USS for supervised users sync.";
-const char kSyncPseudoUSSSupervisedUsersDescription[] =
-    "Enable new USS-based codepath for sync datatypes SUPERVISED_USER_SETTINGS "
-    "and SUPERVISED_USER_WHITELISTS.";
-
 const char kSyncSandboxName[] = "Use Chrome Sync sandbox";
 const char kSyncSandboxDescription[] =
     "Connects to the testing server for Chrome Sync.";
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h
index a1236f6..08509f0 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -78,21 +78,6 @@
 extern const char kEnableSyncUSSPasswordsName[];
 extern const char kEnableSyncUSSPasswordsDescription[];
 
-extern const char kSyncPseudoUSSFaviconsName[];
-extern const char kSyncPseudoUSSFaviconsDescription[];
-
-extern const char kSyncPseudoUSSHistoryDeleteDirectivesName[];
-extern const char kSyncPseudoUSSHistoryDeleteDirectivesDescription[];
-
-extern const char kSyncPseudoUSSPreferencesName[];
-extern const char kSyncPseudoUSSPreferencesDescription[];
-
-extern const char kSyncPseudoUSSPriorityPreferencesName[];
-extern const char kSyncPseudoUSSPriorityPreferencesDescription[];
-
-extern const char kSyncPseudoUSSSupervisedUsersName[];
-extern const char kSyncPseudoUSSSupervisedUsersDescription[];
-
 // Title and description for the flag to control if Chrome Sync should use the
 // sandbox servers.
 extern const char kSyncSandboxName[];
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index 3ffd4d0..7214109 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -752,9 +752,7 @@
   if (![self getFormForGenerationFromFormName:formName])
     return;
 
-  // TODO(crbug.com/886583): form_signature, field_signature, max_length and
-  // spec_priority in PGM::GeneratePassword are being refactored, passing 0 for
-  // now to get a generic random password.
+  // TODO(crbug.com/886583): pass correct |max_length|.
   base::string16 generatedPassword =
       _passwordGenerationHelper->GeneratePassword([self lastCommittedURL], 0, 0,
                                                   0, nullptr);
diff --git a/ios/chrome/browser/providers/chromium_browser_provider.h b/ios/chrome/browser/providers/chromium_browser_provider.h
index c35603f5..f333e12 100644
--- a/ios/chrome/browser/providers/chromium_browser_provider.h
+++ b/ios/chrome/browser/providers/chromium_browser_provider.h
@@ -29,6 +29,7 @@
   SpecialUserProvider* GetSpecialUserProvider() const override;
   SpotlightProvider* GetSpotlightProvider() const override;
   FullscreenProvider* GetFullscreenProvider() const override;
+  OverridesProvider* GetOverridesProvider() const override;
 
  private:
   std::unique_ptr<AppDistributionProvider> app_distribution_provider_;
@@ -41,6 +42,7 @@
   std::unique_ptr<SpecialUserProvider> special_user_provider_;
   std::unique_ptr<SpotlightProvider> spotlight_provider_;
   std::unique_ptr<FullscreenProvider> fullscreen_provider_;
+  std::unique_ptr<OverridesProvider> overrides_provider_;
 };
 
 #endif  // IOS_CHROME_BROWSER_PROVIDERS_CHROMIUM_BROWSER_PROVIDER_H_
diff --git a/ios/chrome/browser/providers/chromium_browser_provider.mm b/ios/chrome/browser/providers/chromium_browser_provider.mm
index 91ad3a5..62599c5 100644
--- a/ios/chrome/browser/providers/chromium_browser_provider.mm
+++ b/ios/chrome/browser/providers/chromium_browser_provider.mm
@@ -13,6 +13,7 @@
 #include "ios/chrome/browser/providers/signin/chromium_signin_resources_provider.h"
 #include "ios/chrome/browser/providers/ui/chromium_styled_text_field.h"
 #include "ios/public/provider/chrome/browser/distribution/app_distribution_provider.h"
+#include "ios/public/provider/chrome/browser/overrides_provider.h"
 #include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
 #include "ios/public/provider/chrome/browser/signin/signin_error_provider.h"
 #import "ios/public/provider/chrome/browser/ui/fullscreen_provider.h"
@@ -33,7 +34,8 @@
       voice_search_provider_(std::make_unique<ChromiumVoiceSearchProvider>()),
       special_user_provider_(std::make_unique<SpecialUserProvider>()),
       spotlight_provider_(std::make_unique<ChromiumSpotlightProvider>()),
-      fullscreen_provider_(std::make_unique<FullscreenProvider>()) {}
+      fullscreen_provider_(std::make_unique<FullscreenProvider>()),
+      overrides_provider_(std::make_unique<OverridesProvider>()) {}
 
 ChromiumBrowserProvider::~ChromiumBrowserProvider() {}
 
@@ -97,3 +99,7 @@
 FullscreenProvider* ChromiumBrowserProvider::GetFullscreenProvider() const {
   return fullscreen_provider_.get();
 }
+
+OverridesProvider* ChromiumBrowserProvider::GetOverridesProvider() const {
+  return overrides_provider_.get();
+}
diff --git a/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm b/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm
index 10f6360..54bc7090 100644
--- a/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm
+++ b/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm
@@ -26,26 +26,38 @@
 // Properties of the alert view.
 constexpr CGFloat kCornerRadius = 14;
 constexpr CGFloat kAlertWidth = 270;
+constexpr CGFloat kAlertWidthAccessibilty = 402;
+constexpr CGFloat kTextFieldCornerRadius = 5;
 constexpr CGFloat kMinimumHeight = 30;
 constexpr CGFloat kMinimumMargin = 4;
 
-// Inset for the content in the alert view.
+// Insets for the content in the alert view.
 constexpr CGFloat kTitleInsetTop = 20;
 constexpr CGFloat kTitleInsetLeading = 20;
 constexpr CGFloat kTitleInsetBottom = 4;
 constexpr CGFloat kTitleInsetTrailing = 20;
+
 constexpr CGFloat kMessageInsetTop = 4;
 constexpr CGFloat kMessageInsetLeading = 20;
-constexpr CGFloat kMessageInsetBottom = 20;
+constexpr CGFloat kMessageInsetBottom = 10;
 constexpr CGFloat kMessageInsetTrailing = 20;
+
 constexpr CGFloat kButtonInsetTop = 20;
 constexpr CGFloat kButtonInsetLeading = 20;
 constexpr CGFloat kButtonInsetBottom = 20;
 constexpr CGFloat kButtonInsetTrailing = 20;
 
+constexpr CGFloat kTextfieldStackInsetTop = 10;
+constexpr CGFloat kTextfieldStackInsetLeading = 12;
+constexpr CGFloat kTextfieldStackInsetBottom = 20;
+constexpr CGFloat kTextfieldStackInsetTrailing = 12;
+
+constexpr CGFloat kTextfieldInset = 8;
+
 // Colors for the action buttons.
 constexpr int kButtonTextDefaultColor = 0x0579ff;
 constexpr int kButtonTextDestructiveColor = 0xdf322f;
+constexpr int kTextfieldBackgroundColor = 0xf7f7f7;
 
 }  // namespace
 
@@ -114,7 +126,13 @@
 
 - (void)addTextFieldWithConfigurationHandler:
     (void (^)(UITextField* textField))configurationHandler {
-  // TODO(crbug.com/951303): Implement support.
+  UITextField* textField = [[UITextField alloc] init];
+  if (!self.textFields) {
+    self.textFields = @[ textField ];
+    return;
+  }
+  self.textFields = [self.textFields arrayByAddingObject:textField];
+  // TODO(crbug.com/951303): Implement configuration handlers support.
 }
 
 - (void)loadView {
@@ -132,16 +150,25 @@
   self.contentView.layer.shadowOpacity = kShadowOpacity;
   self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
   [self.view addSubview:self.contentView];
+
+  BOOL isAccessibilityContentSize =
+      UIContentSizeCategoryIsAccessibilityCategory(
+          [UIApplication sharedApplication].preferredContentSizeCategory);
+  const CGFloat alertWidth =
+      isAccessibilityContentSize ? kAlertWidthAccessibilty : kAlertWidth;
+  NSLayoutConstraint* widthConstraint =
+      [self.contentView.widthAnchor constraintEqualToConstant:alertWidth];
+  widthConstraint.priority = 999;
+
   [NSLayoutConstraint activateConstraints:@[
+    widthConstraint,
+
     // Centering
     [self.contentView.centerXAnchor
         constraintEqualToAnchor:self.view.centerXAnchor],
     [self.contentView.centerYAnchor
         constraintEqualToAnchor:self.view.centerYAnchor],
 
-    // Width
-    [self.contentView.widthAnchor constraintEqualToConstant:kAlertWidth],
-
     // Minimum Size
     [self.contentView.heightAnchor
         constraintGreaterThanOrEqualToConstant:kMinimumHeight],
@@ -215,6 +242,66 @@
                               LayoutSides::kTrailing | LayoutSides::kLeading);
   }
 
+  if (self.textFields.count) {
+    // |container| insets |stackHolder|.
+    UIView* container = [[UIView alloc] init];
+    container.translatesAutoresizingMaskIntoConstraints = NO;
+    [stackView addArrangedSubview:container];
+    AddSameConstraintsToSides(container, self.contentView,
+                              LayoutSides::kTrailing | LayoutSides::kLeading);
+
+    // |stackHolder| has the background, border and round corners of the stacked
+    // fields.
+    UIView* stackHolder = [[UIView alloc] init];
+    stackHolder.layer.cornerRadius = kTextFieldCornerRadius;
+    stackHolder.layer.borderColor = [UIColor lightGrayColor].CGColor;
+    stackHolder.layer.borderWidth = 1.0 / [UIScreen mainScreen].scale;
+    stackHolder.clipsToBounds = YES;
+    stackHolder.backgroundColor = UIColorFromRGB(kTextfieldBackgroundColor);
+    stackHolder.translatesAutoresizingMaskIntoConstraints = NO;
+    [container addSubview:stackHolder];
+    ChromeDirectionalEdgeInsets stackHolderContentInsets =
+        ChromeDirectionalEdgeInsetsMake(
+            kTextfieldStackInsetTop, kTextfieldStackInsetLeading,
+            kTextfieldStackInsetBottom, kTextfieldStackInsetTrailing);
+    AddSameConstraintsWithInsets(stackHolder, container,
+                                 stackHolderContentInsets);
+
+    UIStackView* fieldStack = [[UIStackView alloc] init];
+    fieldStack.axis = UILayoutConstraintAxisVertical;
+    fieldStack.translatesAutoresizingMaskIntoConstraints = NO;
+    fieldStack.spacing = kTextfieldInset;
+    fieldStack.alignment = UIStackViewAlignmentCenter;
+    [stackHolder addSubview:fieldStack];
+    ChromeDirectionalEdgeInsets fieldStackContentInsets =
+        ChromeDirectionalEdgeInsetsMake(kTextfieldInset, 0.0, kTextfieldInset,
+                                        0.0);
+    AddSameConstraintsWithInsets(fieldStack, stackHolder,
+                                 fieldStackContentInsets);
+
+    for (UITextField* textField in self.textFields) {
+      if (textField != [self.textFields firstObject]) {
+        UIView* hairline = [[UIView alloc] init];
+        hairline.backgroundColor = [UIColor lightGrayColor];
+        hairline.translatesAutoresizingMaskIntoConstraints = NO;
+        [fieldStack addArrangedSubview:hairline];
+        CGFloat pixelHeight = 1.0 / [UIScreen mainScreen].scale;
+        [hairline.heightAnchor constraintEqualToConstant:pixelHeight].active =
+            YES;
+        AddSameConstraintsToSides(
+            fieldStack, hairline,
+            LayoutSides::kTrailing | LayoutSides::kLeading);
+      }
+      textField.translatesAutoresizingMaskIntoConstraints = NO;
+      [fieldStack addArrangedSubview:textField];
+      ChromeDirectionalEdgeInsets fieldInsets = ChromeDirectionalEdgeInsetsMake(
+          0.0, kTextfieldInset, 0.0, kTextfieldInset);
+      AddSameConstraintsToSidesWithInsets(
+          textField, fieldStack, LayoutSides::kTrailing | LayoutSides::kLeading,
+          fieldInsets);
+    }
+  }
+
   self.buttonAlertActionsDictionary = [[NSMutableDictionary alloc] init];
   for (AlertAction* action in self.actions) {
     GrayHighlightButton* button = [[GrayHighlightButton alloc] init];
diff --git a/ios/chrome/browser/ui/elements/BUILD.gn b/ios/chrome/browser/ui/elements/BUILD.gn
index 82327f0..d55b180 100644
--- a/ios/chrome/browser/ui/elements/BUILD.gn
+++ b/ios/chrome/browser/ui/elements/BUILD.gn
@@ -16,6 +16,8 @@
     "selector_picker_view_controller.h",
     "selector_picker_view_controller.mm",
     "selector_view_controller_delegate.h",
+    "text_field_configuration.h",
+    "text_field_configuration.mm",
     "top_aligned_image_view.h",
     "top_aligned_image_view.mm",
     "windowed_container_view.h",
@@ -37,6 +39,7 @@
     "chrome_activity_overlay_coordinator_unittest.mm",
     "selector_coordinator_unittest.mm",
     "selector_picker_view_controller_unittest.mm",
+    "text_field_configuration_unittest.mm",
     "windowed_container_view_unittest.mm",
   ]
   deps = [
diff --git a/ios/chrome/browser/ui/elements/text_field_configuration.h b/ios/chrome/browser/ui/elements/text_field_configuration.h
new file mode 100644
index 0000000..cbf36be
--- /dev/null
+++ b/ios/chrome/browser/ui/elements/text_field_configuration.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_ELEMENTS_TEXT_FIELD_CONFIGURATION_H_
+#define IOS_CHROME_BROWSER_UI_ELEMENTS_TEXT_FIELD_CONFIGURATION_H_
+
+#import <UIKit/UIKit.h>
+
+// Holds the configurable options for a UITextField.
+//
+// The properties here match the ones of UITextField. Find the respective
+// documentation of each in UITextField.h.
+@interface TextFieldConfiguration : NSObject
+
+@property(nonatomic, strong, readonly) NSString* text;
+@property(nonatomic, strong, readonly) NSString* placeholder;
+@property(nonatomic, strong, readonly) NSString* accessibilityIdentifier;
+@property(nonatomic, readonly, getter=isSecureTextEntry) BOOL secureTextEntry;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithText:(NSString*)text
+                 placeholder:(NSString*)placeholder
+     accessibilityIdentifier:(NSString*)accessibilityIdentifier
+             secureTextEntry:(BOOL)secureTextEntry NS_DESIGNATED_INITIALIZER;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_ELEMENTS_TEXT_FIELD_CONFIGURATION_H_
diff --git a/ios/chrome/browser/ui/elements/text_field_configuration.mm b/ios/chrome/browser/ui/elements/text_field_configuration.mm
new file mode 100644
index 0000000..d00891be
--- /dev/null
+++ b/ios/chrome/browser/ui/elements/text_field_configuration.mm
@@ -0,0 +1,27 @@
+// 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/elements/text_field_configuration.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation TextFieldConfiguration
+
+- (instancetype)initWithText:(NSString*)text
+                 placeholder:(NSString*)placeholder
+     accessibilityIdentifier:(NSString*)accessibilityIdentifier
+             secureTextEntry:(BOOL)secureTextEntry {
+  self = [super init];
+  if (self) {
+    _text = [text copy];
+    _placeholder = [placeholder copy];
+    _accessibilityIdentifier = [accessibilityIdentifier copy];
+    _secureTextEntry = secureTextEntry;
+  }
+  return self;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/elements/text_field_configuration_unittest.mm b/ios/chrome/browser/ui/elements/text_field_configuration_unittest.mm
new file mode 100644
index 0000000..8e85ee520
--- /dev/null
+++ b/ios/chrome/browser/ui/elements/text_field_configuration_unittest.mm
@@ -0,0 +1,29 @@
+// 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/elements/text_field_configuration.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using TextFieldConfigurationTest = PlatformTest;
+
+// Tests that invoking start and stop on the coordinator presents and dismisses
+// the activity overlay view, respectively.
+TEST_F(TextFieldConfigurationTest, Init) {
+  TextFieldConfiguration* configuration =
+      [[TextFieldConfiguration alloc] initWithText:@"Text"
+                                       placeholder:@"Placehorder"
+                           accessibilityIdentifier:@"A11y"
+                                   secureTextEntry:YES];
+  EXPECT_TRUE([configuration.text isEqualToString:@"Text"]);
+  EXPECT_TRUE([configuration.placeholder isEqualToString:@"Placehorder"]);
+  EXPECT_TRUE([configuration.accessibilityIdentifier isEqualToString:@"A11y"]);
+  EXPECT_TRUE(configuration.secureTextEntry);
+}
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
index 87ad1e26..6e0f329 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
@@ -497,6 +497,9 @@
         tableViewClearDataItem.detailText =
             l10n_util::GetNSString(IDS_DEL_COOKIES_COUNTER);
       } else {
+        // Having a placeholder |detailText| helps reduce the observable
+        // row-height changes induced by the counter callbacks.
+        tableViewClearDataItem.detailText = @"\u00A0";
         __weak ClearBrowsingDataManager* weakSelf = self;
         __weak TableViewClearBrowsingDataItem* weakTableClearDataItem =
             tableViewClearDataItem;
diff --git a/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm b/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
index 18e48eb..0dda1fe 100644
--- a/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
@@ -69,8 +69,14 @@
   ItemTypeWebServicesShowSuggestions,
   ItemTypeWebServicesFooter,
   ItemTypeClearBrowsingDataClear,
+  // Footer to suggest the user to open Sync and Google services settings.
+  ItemTypeClearBrowsingDataFooter,
 };
 
+// Only used in this class to openn the Sync and Google services settings.
+// This link should not be dispatched.
+GURL kGoogleServicesSettingsURL("settings://open_google_services");
+
 }  // namespace
 
 @interface PrivacyTableViewController () <BooleanObserver,
@@ -185,6 +191,10 @@
   [model addSectionWithIdentifier:SectionIdentifierClearBrowsingData];
   [model addItem:[self clearBrowsingDetailItem]
       toSectionWithIdentifier:SectionIdentifierClearBrowsingData];
+  if (unified_consent::IsUnifiedConsentFeatureEnabled()) {
+    [model setFooter:[self showClearBrowsingDataFooterItem]
+        forSectionWithIdentifier:SectionIdentifierClearBrowsingData];
+  }
 }
 
 #pragma mark - Model Objects
@@ -225,6 +235,19 @@
   return showSuggestionsFooterItem;
 }
 
+// Creates TableViewHeaderFooterItem instance to show a link to open the Sync
+// and Google services settings.
+- (TableViewHeaderFooterItem*)showClearBrowsingDataFooterItem {
+  TableViewLinkHeaderFooterItem* showClearBrowsingDataFooterItem =
+      [[TableViewLinkHeaderFooterItem alloc]
+          initWithType:ItemTypeClearBrowsingDataFooter];
+  showClearBrowsingDataFooterItem.text =
+      l10n_util::GetNSString(IDS_IOS_OPTIONS_PRIVACY_GOOGLE_SERVICES_FOOTER);
+  showClearBrowsingDataFooterItem.linkURL = kGoogleServicesSettingsURL;
+
+  return showClearBrowsingDataFooterItem;
+}
+
 - (TableViewItem*)clearBrowsingDetailItem {
   return [self detailItemWithType:ItemTypeClearBrowsingDataClear
                           titleId:IDS_IOS_CLEAR_BROWSING_DATA_TITLE
@@ -326,12 +349,9 @@
     viewForFooterInSection:(NSInteger)section {
   UIView* footerView =
       [super tableView:tableView viewForFooterInSection:section];
-  if (SectionIdentifierWebServices ==
-          [self.tableViewModel sectionIdentifierForSection:section] &&
-      !unified_consent::IsUnifiedConsentFeatureEnabled()) {
-    // The footer view is only shown when Unified consent flag is off.
-    TableViewLinkHeaderFooterView* footer =
-        base::mac::ObjCCastStrict<TableViewLinkHeaderFooterView>(footerView);
+  TableViewLinkHeaderFooterView* footer =
+      base::mac::ObjCCast<TableViewLinkHeaderFooterView>(footerView);
+  if (footer) {
     footer.delegate = self;
   }
   return footerView;
@@ -507,4 +527,16 @@
   }
 }
 
+#pragma mark - TableViewLinkHeaderFooterItemDelegate
+
+- (void)view:(TableViewLinkHeaderFooterView*)view didTapLinkURL:(GURL)URL {
+  if (URL == kGoogleServicesSettingsURL) {
+    // kGoogleServicesSettingsURL is not a realy link. It should be handled
+    // with a special case.
+    [self.dispatcher showGoogleServicesSettingsFromViewController:self];
+  } else {
+    [super view:view didTapLinkURL:URL];
+  }
+}
+
 @end
diff --git a/ios/public/provider/chrome/browser/BUILD.gn b/ios/public/provider/chrome/browser/BUILD.gn
index f0c9d2eb..f7340a08 100644
--- a/ios/public/provider/chrome/browser/BUILD.gn
+++ b/ios/public/provider/chrome/browser/BUILD.gn
@@ -13,6 +13,8 @@
     "chrome_browser_provider.mm",
     "geolocation_updater_provider.h",
     "geolocation_updater_provider.mm",
+    "overrides_provider.h",
+    "overrides_provider.mm",
   ]
   deps = [
     "//base",
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.h b/ios/public/provider/chrome/browser/chrome_browser_provider.h
index 785cea04..75b1f32 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.h
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.h
@@ -22,6 +22,7 @@
 class FullscreenProvider;
 class MailtoHandlerProvider;
 class OmahaServiceProvider;
+class OverridesProvider;
 class SpecialUserProvider;
 class SpotlightProvider;
 class UserFeedbackProvider;
@@ -163,6 +164,9 @@
   // Returns an instance of the BrowserURLRewriter provider.
   virtual BrowserURLRewriterProvider* GetBrowserURLRewriterProvider() const;
 
+  // Returns an instance of the Overrides provider;
+  virtual OverridesProvider* GetOverridesProvider() const;
+
   // Adds and removes observers.
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.mm b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
index 26c5bba..b03270f 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.mm
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
@@ -118,6 +118,10 @@
   return nullptr;
 }
 
+OverridesProvider* ChromeBrowserProvider::GetOverridesProvider() const {
+  return nullptr;
+}
+
 MailtoHandlerProvider* ChromeBrowserProvider::GetMailtoHandlerProvider() const {
   return mailto_handler_provider_.get();
 }
diff --git a/ios/public/provider/chrome/browser/overrides_provider.h b/ios/public/provider/chrome/browser/overrides_provider.h
new file mode 100644
index 0000000..2c64be73
--- /dev/null
+++ b/ios/public/provider/chrome/browser/overrides_provider.h
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_PUBLIC_PROVIDER_CHROME_BROWSER_OVERRIDES_PROVIDER_H_
+#define IOS_PUBLIC_PROVIDER_CHROME_BROWSER_OVERRIDES_PROVIDER_H_
+
+// Provider for installing overrides.
+class OverridesProvider {
+ public:
+  OverridesProvider();
+  virtual ~OverridesProvider();
+
+  virtual void InstallOverrides();
+};
+
+#endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_OVERRIDES_PROVIDER_H_
diff --git a/ios/public/provider/chrome/browser/overrides_provider.mm b/ios/public/provider/chrome/browser/overrides_provider.mm
new file mode 100644
index 0000000..7d9d0f3
--- /dev/null
+++ b/ios/public/provider/chrome/browser/overrides_provider.mm
@@ -0,0 +1,15 @@
+// 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/public/provider/chrome/browser/overrides_provider.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+OverridesProvider::OverridesProvider() = default;
+
+OverridesProvider::~OverridesProvider() = default;
+
+void OverridesProvider::InstallOverrides() {}
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 3bdad4390..5670754f 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -3845,7 +3845,7 @@
   // There is no reliable way to tell if there was a user gesture, so this code
   // checks if user has recently tapped on web view. TODO(crbug.com/809706):
   // Remove the usage of -userIsInteracting when rdar://19989909 is fixed.
-  bool initiatedByUser = [self userIsInteracting];
+  bool initiatedByUser = [self isUserInitiatedAction:action];
 
   if (UIAccessibilityIsVoiceOverRunning()) {
     // -userIsInteracting returns NO if VoiceOver is On. Inspect action's
@@ -3965,6 +3965,10 @@
 
 #pragma mark - WKUIDelegate Helpers
 
+- (BOOL)isUserInitiatedAction:(WKNavigationAction*)action {
+  return [self userIsInteracting];
+}
+
 // Helper to respond to |webView:runJavaScript...| delegate methods.
 // |completionHandler| must not be nil.
 - (void)runJavaScriptDialogOfType:(web::JavaScriptDialogType)type
diff --git a/ipc/ipc_message_macros.h b/ipc/ipc_message_macros.h
index e0e256e..14d4794a 100644
--- a/ipc/ipc_message_macros.h
+++ b/ipc/ipc_message_macros.h
@@ -199,6 +199,8 @@
 #include <tuple>
 
 #include "base/export_template.h"
+#include "base/location.h"
+#include "base/task/common/task_annotator.h"
 #include "ipc/ipc_message_templates.h"
 #include "ipc/ipc_message_utils.h"
 #include "ipc/param_traits_macros.h"
@@ -323,6 +325,12 @@
 //     return handled;
 //   }
 
+// A macro to be used from within the IPC_MESSAGE_FORWARD macros, for providing
+// the IPC message context to the TaskAnnotator. This allows posted tasks to be
+// associated with the incoming IPC message that caused them to be posted.
+#define IPC_TASK_ANNOTATOR_CONTEXT(msg_class)                    \
+  base::TaskAnnotator::ScopedSetIpcProgramCounter scoped_ipc_pc( \
+      base::GetProgramCounter());
 
 #define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \
   { \
@@ -338,48 +346,46 @@
     decltype(param) param__ = param;                              \
     const IPC::Message& ipc_message__ = msg;                      \
     switch (ipc_message__.type()) {
-
-#define IPC_MESSAGE_FORWARD(msg_class, obj, member_func)                       \
-    case msg_class::ID: {                                                      \
-        if (!msg_class::Dispatch(&ipc_message__, obj, this, param__,           \
-                                 &member_func))                                \
-          ipc_message__.set_dispatch_error();                                  \
-      }                                                                        \
-      break;
+#define IPC_MESSAGE_FORWARD(msg_class, obj, member_func)         \
+  case msg_class::ID: {                                          \
+    IPC_TASK_ANNOTATOR_CONTEXT(msg_class)                        \
+    if (!msg_class::Dispatch(&ipc_message__, obj, this, param__, \
+                             &member_func))                      \
+      ipc_message__.set_dispatch_error();                        \
+  } break;
 
 #define IPC_MESSAGE_HANDLER(msg_class, member_func) \
   IPC_MESSAGE_FORWARD(msg_class, this, _IpcMessageHandlerClass::member_func)
 
-#define IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, obj, member_func)           \
-    case msg_class::ID: {                                                      \
-        if (!msg_class::DispatchDelayReply(&ipc_message__, obj, param__,       \
-                                           &member_func))                      \
-          ipc_message__.set_dispatch_error();                                  \
-      }                                                                        \
-      break;
+#define IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, obj, member_func) \
+  case msg_class::ID: {                                              \
+    IPC_TASK_ANNOTATOR_CONTEXT(msg_class)                            \
+    if (!msg_class::DispatchDelayReply(&ipc_message__, obj, param__, \
+                                       &member_func))                \
+      ipc_message__.set_dispatch_error();                            \
+  } break;
 
 #define IPC_MESSAGE_HANDLER_DELAY_REPLY(msg_class, member_func)                \
     IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, this,                           \
                                     _IpcMessageHandlerClass::member_func)
 
-#define IPC_MESSAGE_FORWARD_WITH_PARAM_DELAY_REPLY(msg_class, obj,             \
-                                                   member_func)                \
-  case msg_class::ID: {                                                        \
-    if (!msg_class::DispatchWithParamDelayReply(&ipc_message__, obj, param__,  \
-                                                &member_func))                 \
-      ipc_message__.set_dispatch_error();                                      \
-  }                                                                            \
-  break;
+#define IPC_MESSAGE_FORWARD_WITH_PARAM_DELAY_REPLY(msg_class, obj,         \
+                                                   member_func)            \
+  case msg_class::ID: {                                                    \
+    IPC_TASK_ANNOTATOR_CONTEXT(msg_class)                                  \
+    if (!msg_class::DispatchWithParamDelayReply(&ipc_message__, obj,       \
+                                                param__, \ & member_func)) \
+      ipc_message__.set_dispatch_error();                                  \
+  } break;
 
 #define IPC_MESSAGE_HANDLER_WITH_PARAM_DELAY_REPLY(msg_class, member_func)     \
     IPC_MESSAGE_FORWARD_WITH_PARAM_DELAY_REPLY(                                \
         msg_class, this, _IpcMessageHandlerClass::member_func)
 
-#define IPC_MESSAGE_HANDLER_GENERIC(msg_class, code)                           \
-    case msg_class::ID: {                                                      \
-        code;                                                                  \
-      }                                                                        \
-      break;
+#define IPC_MESSAGE_HANDLER_GENERIC(msg_class, code) \
+  case msg_class::ID: {                              \
+    IPC_TASK_ANNOTATOR_CONTEXT(msg_class) { code; }  \
+  } break;
 
 #define IPC_REPLY_HANDLER(func)                                                \
     case IPC_REPLY_ID: {                                                       \
diff --git a/media/base/audio_buffer.cc b/media/base/audio_buffer.cc
index 17adcb5..6de13f0 100644
--- a/media/base/audio_buffer.cc
+++ b/media/base/audio_buffer.cc
@@ -240,7 +240,7 @@
 void AudioBuffer::ReadFrames(int frames_to_copy,
                              int source_frame_offset,
                              int dest_frame_offset,
-                             AudioBus* dest) {
+                             AudioBus* dest) const {
   // Deinterleave each channel (if necessary) and convert to 32bit
   // floating-point with nominal range -1.0 -> +1.0 (if necessary).
 
@@ -411,7 +411,7 @@
   TrimEnd(frames_to_trim);
 }
 
-bool AudioBuffer::IsBitstreamFormat() {
+bool AudioBuffer::IsBitstreamFormat() const {
   return IsBitstream(sample_format_);
 }
 
diff --git a/media/base/audio_buffer.h b/media/base/audio_buffer.h
index abd2c77..e96bfa8 100644
--- a/media/base/audio_buffer.h
+++ b/media/base/audio_buffer.h
@@ -129,7 +129,7 @@
   void ReadFrames(int frames_to_copy,
                   int source_frame_offset,
                   int dest_frame_offset,
-                  AudioBus* dest);
+                  AudioBus* dest) const;
 
   // Trim an AudioBuffer by removing |frames_to_trim| frames from the start.
   // Timestamp and duration are adjusted to reflect the fewer frames.
@@ -146,7 +146,7 @@
   void TrimRange(int start, int end);
 
   // Return true if the buffer contains compressed bitstream.
-  bool IsBitstreamFormat();
+  bool IsBitstreamFormat() const;
 
   // Return the number of channels.
   int channel_count() const { return channel_count_; }
@@ -183,7 +183,7 @@
   // mojo::TypeConverter added as a friend so that AudioBuffer can be
   // transferred across a mojo connection.
   friend struct mojo::TypeConverter<mojo::StructPtr<mojom::AudioBuffer>,
-                                    scoped_refptr<AudioBuffer>>;
+                                    AudioBuffer>;
 
   // Allocates aligned contiguous buffer to hold all channel data (1 block for
   // interleaved data, |channel_count| blocks for planar data), copies
diff --git a/media/base/audio_buffer_converter.cc b/media/base/audio_buffer_converter.cc
index 4026a2c9..17779fc6 100644
--- a/media/base/audio_buffer_converter.cc
+++ b/media/base/audio_buffer_converter.cc
@@ -19,10 +19,10 @@
 
 // Is the config presented by |buffer| a config change from |params|?
 static bool IsConfigChange(const AudioParameters& params,
-                           const scoped_refptr<AudioBuffer>& buffer) {
-  return buffer->sample_rate() != params.sample_rate() ||
-         buffer->channel_count() != params.channels() ||
-         buffer->channel_layout() != params.channel_layout();
+                           const AudioBuffer& buffer) {
+  return buffer.sample_rate() != params.sample_rate() ||
+         buffer.channel_count() != params.channels() ||
+         buffer.channel_layout() != params.channel_layout();
 }
 
 AudioBufferConverter::AudioBufferConverter(const AudioParameters& output_params)
@@ -38,29 +38,29 @@
 
 AudioBufferConverter::~AudioBufferConverter() = default;
 
-void AudioBufferConverter::AddInput(const scoped_refptr<AudioBuffer>& buffer) {
+void AudioBufferConverter::AddInput(scoped_refptr<AudioBuffer> buffer) {
   // On EOS flush any remaining buffered data.
   if (buffer->end_of_stream()) {
     Flush();
-    queued_outputs_.push_back(buffer);
+    queued_outputs_.push_back(std::move(buffer));
     return;
   }
 
   // We'll need a new |audio_converter_| if there was a config change.
-  if (IsConfigChange(input_params_, buffer))
-    ResetConverter(buffer);
+  if (IsConfigChange(input_params_, *buffer))
+    ResetConverter(*buffer);
 
   // Pass straight through if there's no work to be done.
   if (!audio_converter_) {
-    queued_outputs_.push_back(buffer);
+    queued_outputs_.push_back(std::move(buffer));
     return;
   }
 
   if (timestamp_helper_.base_timestamp() == kNoTimestamp)
     timestamp_helper_.SetBaseTimestamp(buffer->timestamp());
 
-  queued_inputs_.push_back(buffer);
   input_frames_ += buffer->frame_count();
+  queued_inputs_.push_back(std::move(buffer));
 
   ConvertIfPossible();
 }
@@ -69,7 +69,7 @@
 
 scoped_refptr<AudioBuffer> AudioBufferConverter::GetNextBuffer() {
   DCHECK(!queued_outputs_.empty());
-  scoped_refptr<AudioBuffer> out = queued_outputs_.front();
+  auto out = std::move(queued_outputs_.front());
   queued_outputs_.pop_front();
   return out;
 }
@@ -98,13 +98,13 @@
   int dest_index = 0;
 
   while (requested_frames_left > 0 && !queued_inputs_.empty()) {
-    scoped_refptr<AudioBuffer> input_buffer = queued_inputs_.front();
+    const auto& input_buffer = queued_inputs_.front();
 
     int frames_to_read =
         std::min(requested_frames_left,
                  input_buffer->frame_count() - last_input_buffer_offset_);
-    input_buffer->ReadFrames(
-        frames_to_read, last_input_buffer_offset_, dest_index, audio_bus);
+    input_buffer->ReadFrames(frames_to_read, last_input_buffer_offset_,
+                             dest_index, audio_bus);
     last_input_buffer_offset_ += frames_to_read;
 
     if (last_input_buffer_offset_ == input_buffer->frame_count()) {
@@ -135,21 +135,18 @@
   return 1.0;
 }
 
-void AudioBufferConverter::ResetConverter(
-    const scoped_refptr<AudioBuffer>& buffer) {
+void AudioBufferConverter::ResetConverter(const AudioBuffer& buffer) {
   Flush();
   audio_converter_.reset();
   input_params_.Reset(
-      input_params_.format(),
-      buffer->channel_layout(),
-      buffer->sample_rate(),
+      input_params_.format(), buffer.channel_layout(), buffer.sample_rate(),
       // If resampling is needed and the FIFO disabled, the AudioConverter will
       // always request SincResampler::kDefaultRequestSize frames.  Otherwise it
       // will use the output frame size.
-      buffer->sample_rate() == output_params_.sample_rate()
+      buffer.sample_rate() == output_params_.sample_rate()
           ? output_params_.frames_per_buffer()
           : SincResampler::kDefaultRequestSize);
-  input_params_.set_channels_for_discrete(buffer->channel_count());
+  input_params_.set_channels_for_discrete(buffer.channel_count());
 
   io_sample_rate_ratio_ = static_cast<double>(input_params_.sample_rate()) /
                           output_params_.sample_rate();
@@ -186,7 +183,7 @@
   if (!request_frames)
     return;
 
-  scoped_refptr<AudioBuffer> output_buffer = AudioBuffer::CreateBuffer(
+  auto output_buffer = AudioBuffer::CreateBuffer(
       kSampleFormatPlanarF32, output_params_.channel_layout(),
       output_params_.channels(), output_params_.sample_rate(), request_frames,
       pool_);
@@ -226,7 +223,7 @@
   output_buffer->set_timestamp(timestamp_helper_.GetTimestamp());
   timestamp_helper_.AddFrames(request_frames);
 
-  queued_outputs_.push_back(output_buffer);
+  queued_outputs_.push_back(std::move(output_buffer));
 }
 
 void AudioBufferConverter::Flush() {
diff --git a/media/base/audio_buffer_converter.h b/media/base/audio_buffer_converter.h
index 3cccb20..3a0320b 100644
--- a/media/base/audio_buffer_converter.h
+++ b/media/base/audio_buffer_converter.h
@@ -27,7 +27,7 @@
   explicit AudioBufferConverter(const AudioParameters& output_params);
   ~AudioBufferConverter() override;
 
-  void AddInput(const scoped_refptr<AudioBuffer>& buffer);
+  void AddInput(scoped_refptr<AudioBuffer> buffer);
 
   // Is an output buffer available via GetNextBuffer()?
   bool HasNextBuffer();
@@ -54,7 +54,7 @@
   double ProvideInput(AudioBus* audio_bus, uint32_t frames_delayed) override;
 
   // Reset the converter in response to a configuration change.
-  void ResetConverter(const scoped_refptr<AudioBuffer>& input_buffer);
+  void ResetConverter(const AudioBuffer& input_buffer);
 
   // Perform conversion if we have enough data.
   void ConvertIfPossible();
diff --git a/media/base/audio_buffer_converter_unittest.cc b/media/base/audio_buffer_converter_unittest.cc
index 06d88ec..b57d8b4 100644
--- a/media/base/audio_buffer_converter_unittest.cc
+++ b/media/base/audio_buffer_converter_unittest.cc
@@ -49,7 +49,7 @@
     output_frames_ = expected_output_frames_ = input_frames_ = 0;
   }
 
-  void AddInput(const scoped_refptr<AudioBuffer>& in) {
+  void AddInput(scoped_refptr<AudioBuffer> in) {
     if (!in->end_of_stream()) {
       input_frames_ += in->frame_count();
       expected_output_frames_ +=
@@ -57,7 +57,7 @@
           (static_cast<double>(output_params_.sample_rate()) /
            in->sample_rate());
     }
-    audio_buffer_converter_->AddInput(in);
+    audio_buffer_converter_->AddInput(std::move(in));
   }
 
   void ConsumeOutput() {
diff --git a/media/base/audio_buffer_queue.cc b/media/base/audio_buffer_queue.cc
index a436b1e4..3e69966 100644
--- a/media/base/audio_buffer_queue.cc
+++ b/media/base/audio_buffer_queue.cc
@@ -20,14 +20,14 @@
   frames_ = 0;
 }
 
-void AudioBufferQueue::Append(const scoped_refptr<AudioBuffer>& buffer_in) {
-  // Add the buffer to the queue. Inserting into deque invalidates all
-  // iterators, so point to the first buffer.
-  buffers_.push_back(buffer_in);
-
+void AudioBufferQueue::Append(scoped_refptr<AudioBuffer> buffer_in) {
   // Update the |frames_| counter since we have added frames.
   frames_ += buffer_in->frame_count();
   CHECK_GT(frames_, 0);  // make sure it doesn't overflow.
+
+  // Add the buffer to the queue. Inserting into deque invalidates all
+  // iterators, so point to the first buffer.
+  buffers_.push_back(std::move(buffer_in));
 }
 
 int AudioBufferQueue::ReadFrames(int frames,
@@ -71,7 +71,7 @@
            dest_frame_offset == dest->GetBitstreamFrames());
     DCHECK(!source_frame_offset);
 
-    scoped_refptr<AudioBuffer> buffer = buffers_.front();
+    const auto& buffer = buffers_.front();
     int taken = buffer->frame_count();
 
     // if |dest| is NULL, there's no need to copy.
diff --git a/media/base/audio_buffer_queue.h b/media/base/audio_buffer_queue.h
index 5148fa3..cb709e80 100644
--- a/media/base/audio_buffer_queue.h
+++ b/media/base/audio_buffer_queue.h
@@ -29,7 +29,7 @@
   void Clear();
 
   // Appends |buffer_in| to this queue.
-  void Append(const scoped_refptr<AudioBuffer>& buffer_in);
+  void Append(scoped_refptr<AudioBuffer> buffer_in);
 
   // Reads a maximum of |frames| frames into |dest| from the current position.
   // Returns the number of frames read. The current position will advance by the
diff --git a/media/base/audio_discard_helper.cc b/media/base/audio_discard_helper.cc
index f5bf3d6..ecb3e2a1 100644
--- a/media/base/audio_discard_helper.cc
+++ b/media/base/audio_discard_helper.cc
@@ -49,9 +49,8 @@
   delayed_discard_padding_ = DecoderBuffer::DiscardPadding();
 }
 
-bool AudioDiscardHelper::ProcessBuffers(
-    const DecoderBuffer& encoded_buffer,
-    const scoped_refptr<AudioBuffer>& decoded_buffer) {
+bool AudioDiscardHelper::ProcessBuffers(const DecoderBuffer& encoded_buffer,
+                                        AudioBuffer* decoded_buffer) {
   DCHECK(!encoded_buffer.end_of_stream());
   DCHECK(encoded_buffer.timestamp() != kNoTimestamp);
 
@@ -69,7 +68,7 @@
   }
   DCHECK(initialized());
 
-  if (!decoded_buffer.get()) {
+  if (!decoded_buffer) {
     // If there's a one buffer delay for decoding, we need to save it so it can
     // be processed with the next decoder buffer.
     if (delayed_discard_)
diff --git a/media/base/audio_discard_helper.h b/media/base/audio_discard_helper.h
index 7154bee..0995669 100644
--- a/media/base/audio_discard_helper.h
+++ b/media/base/audio_discard_helper.h
@@ -61,7 +61,7 @@
   // |decoded_buffer|s.  If the first buffer has a negative timestamp it will be
   // clamped to zero.
   bool ProcessBuffers(const DecoderBuffer& encoded_buffer,
-                      const scoped_refptr<AudioBuffer>& decoded_buffer);
+                      AudioBuffer* decoded_buffer);
 
   // Whether any buffers have been processed.
   bool initialized() const {
diff --git a/media/base/audio_discard_helper_unittest.cc b/media/base/audio_discard_helper_unittest.cc
index 3c2f4dc..324439d5 100644
--- a/media/base/audio_discard_helper_unittest.cc
+++ b/media/base/audio_discard_helper_unittest.cc
@@ -34,13 +34,12 @@
                          kSampleRate, 0.0f, kDataStep, frames, kNoTimestamp);
 }
 
-static float ExtractDecodedData(const scoped_refptr<AudioBuffer>& buffer,
-                                int index) {
+static float ExtractDecodedData(const AudioBuffer& buffer, int index) {
   // This is really inefficient, but we can't access the raw AudioBuffer if any
   // start trimming has been applied.
   std::unique_ptr<AudioBus> temp_bus =
-      AudioBus::Create(buffer->channel_count(), 1);
-  buffer->ReadFrames(1, index, 0, temp_bus.get());
+      AudioBus::Create(buffer.channel_count(), 1);
+  buffer.ReadFrames(1, index, 0, temp_bus.get());
   return temp_bus->channel(0)[0];
 }
 
@@ -86,7 +85,8 @@
   scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
 
   // Verify the basic case where nothing is discarded.
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
   EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
   EXPECT_EQ(kActualDuration, decoded_buffer->duration());
@@ -113,7 +113,8 @@
   scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
 
   // Verify the basic case where nothing is discarded.
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
   EXPECT_EQ(base::TimeDelta(), decoded_buffer->timestamp());
   EXPECT_EQ(kDuration, decoded_buffer->duration());
@@ -137,13 +138,14 @@
   scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
 
   // Verify half the frames end up discarded.
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
   EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
   EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
   EXPECT_EQ(kDiscardFrames, decoded_buffer->frame_count());
   ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
-                  ExtractDecodedData(decoded_buffer, 0));
+                  ExtractDecodedData(*decoded_buffer, 0));
 }
 
 TEST(AudioDiscardHelperTest, ProcessBuffersWithLargeInitialDiscard) {
@@ -162,13 +164,15 @@
   scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
 
   // The first call should fail since no output buffer remains.
-  ASSERT_FALSE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_FALSE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
 
   // Generate another set of buffers and expect half the output frames.
   encoded_buffer = CreateEncodedBuffer(kTimestamp + kDuration, kDuration);
   decoded_buffer = CreateDecodedBuffer(kTestFrames);
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
 
   // The timestamp should match that of the initial buffer.
   const int kDiscardFrames = kTestFrames / 2;
@@ -176,7 +180,7 @@
   EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
   EXPECT_EQ(kDiscardFrames, decoded_buffer->frame_count());
   ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
-                  ExtractDecodedData(decoded_buffer, 0));
+                  ExtractDecodedData(*decoded_buffer, 0));
 }
 
 TEST(AudioDiscardHelperTest, AllowNonMonotonicTimestamps) {
@@ -191,7 +195,8 @@
       CreateEncodedBuffer(kTimestamp, kDuration);
   scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
 
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
   EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
   EXPECT_EQ(kDuration, decoded_buffer->duration());
@@ -199,7 +204,8 @@
 
   // Process the same input buffer again to ensure input timestamps which go
   // backwards in time are not errors.
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   EXPECT_EQ(kTimestamp + kDuration, decoded_buffer->timestamp());
   EXPECT_EQ(kDuration, decoded_buffer->duration());
   EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());
@@ -221,7 +227,8 @@
   encoded_buffer->set_discard_padding(
       std::make_pair(base::TimeDelta(), kDuration / 2));
 
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
   EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
   EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
@@ -245,7 +252,8 @@
       std::make_pair(base::TimeDelta(), kDuration * 2));
 
   // Verify the end discard padding is rejected.
-  ASSERT_FALSE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_FALSE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
 }
 
@@ -269,13 +277,14 @@
   const int kDiscardFrames = kTestFrames / 4;
   discard_helper.Reset(kDiscardFrames);
 
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
   EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
   EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
   EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
   ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
-                  ExtractDecodedData(decoded_buffer, 0));
+                  ExtractDecodedData(*decoded_buffer, 0));
 }
 
 TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPadding) {
@@ -296,7 +305,8 @@
       std::make_pair(kDuration / 8, kDuration / 16));
   discard_helper.Reset(kDiscardFrames);
 
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
   EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
   EXPECT_EQ(kDuration - kDuration / 4 - kDuration / 8 - kDuration / 16,
@@ -333,7 +343,8 @@
   //   |--------|     |---------|     |----|
   //                    Decoded               Discard Front Padding
   //
-  ASSERT_FALSE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_FALSE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
 
   // Processing another buffer that has front discard set to half the buffer's
@@ -354,17 +365,18 @@
   encoded_buffer->set_discard_padding(
       std::make_pair(kDuration / 2, kDuration / 4));
   decoded_buffer = CreateDecodedBuffer(kTestFrames);
-  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
+  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
   ASSERT_NEAR(kDecoderDelay * kDataStep,
-              ExtractDecodedData(decoded_buffer, kDecoderDelay),
+              ExtractDecodedData(*decoded_buffer, kDecoderDelay),
               kDataStep / 1000);
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
   EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
   EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
 
   // Verify it was actually the latter half of the buffer that was removed.
-  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
+  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
 
   // Verify the end discard padding is carried over to the next buffer.  Use
   // kDuration / 2 for the end discard padding so that the next buffer has its
@@ -381,17 +393,18 @@
   encoded_buffer->set_discard_padding(
       std::make_pair(base::TimeDelta(), kDuration / 2));
   decoded_buffer = CreateDecodedBuffer(kTestFrames);
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   EXPECT_EQ(kTimestamp + kDuration / 2, decoded_buffer->timestamp());
   EXPECT_EQ(3 * kDuration / 4, decoded_buffer->duration());
   EXPECT_EQ(3 * kTestFrames / 4, decoded_buffer->frame_count());
 
   // Verify it was actually the second quarter of the buffer that was removed.
   const int kDiscardFrames = kTestFrames / 4;
-  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
+  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
   ASSERT_FLOAT_EQ(
       kDiscardFrames * 2 * kDataStep,
-      ExtractDecodedData(decoded_buffer, kDecoderDelay - kDiscardFrames));
+      ExtractDecodedData(*decoded_buffer, kDecoderDelay - kDiscardFrames));
 
   // One last test to ensure carryover discard from the start works.
   //
@@ -405,14 +418,15 @@
   encoded_buffer->set_timestamp(encoded_buffer->timestamp() + kDuration);
   encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());
   decoded_buffer = CreateDecodedBuffer(kTestFrames);
-  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   EXPECT_EQ(kTimestamp + kDuration / 2 + 3 * kDuration / 4,
             decoded_buffer->timestamp());
   EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
   EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
   ASSERT_FLOAT_EQ(kTestFrames / 2 * kDataStep,
-                  ExtractDecodedData(decoded_buffer, 0));
+                  ExtractDecodedData(*decoded_buffer, 0));
 }
 
 TEST(AudioDiscardHelperTest, DelayedDiscardInitialDiscardAndDiscardPadding) {
@@ -442,7 +456,8 @@
 
   // Verify that when the decoded buffer is consumed, the discards from the
   // previous encoded buffer are applied.
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
   EXPECT_EQ(kDuration - kDuration / 4 - kDuration / 8 - kDuration / 16,
             decoded_buffer->duration());
@@ -466,18 +481,20 @@
   scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
 
   // Verify all of the first buffer is discarded.
-  ASSERT_FALSE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_FALSE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
   encoded_buffer->set_timestamp(kTimestamp + kDuration);
   encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());
 
   // Verify a second buffer goes through untouched.
   decoded_buffer = CreateDecodedBuffer(kTestFrames / 2);
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
   EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
   EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
-  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
+  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
 }
 
 TEST(AudioDiscardHelperTest, CompleteDiscardWithDelayedDiscard) {
@@ -502,16 +519,18 @@
   // Verify the first output buffer is dropped.
   encoded_buffer->set_timestamp(kTimestamp + kDuration);
   encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());
-  ASSERT_FALSE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_FALSE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
 
   // Verify the second buffer goes through untouched.
   encoded_buffer->set_timestamp(kTimestamp + 2 * kDuration);
   decoded_buffer = CreateDecodedBuffer(kTestFrames / 2);
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
   EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
   EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
-  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
+  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
 }
 
 TEST(AudioDiscardHelperTest, CompleteDiscardWithInitialDiscardDecoderDelay) {
@@ -532,19 +551,21 @@
   scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
 
   // Verify all of the first buffer is discarded.
-  ASSERT_FALSE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_FALSE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   ASSERT_TRUE(discard_helper.initialized());
   encoded_buffer->set_timestamp(kTimestamp + kDuration);
   encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());
 
   // Verify 5ms off the front of the second buffer is discarded.
   decoded_buffer = CreateDecodedBuffer(kTestFrames * 2);
-  ASSERT_TRUE(discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer));
+  ASSERT_TRUE(
+      discard_helper.ProcessBuffers(*encoded_buffer, decoded_buffer.get()));
   EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
   EXPECT_EQ(kDuration * 2 - kDuration / 2, decoded_buffer->duration());
   EXPECT_EQ(kTestFrames * 2 - kDecoderDelay, decoded_buffer->frame_count());
   ASSERT_FLOAT_EQ(kDecoderDelay * kDataStep,
-                  ExtractDecodedData(decoded_buffer, 0));
+                  ExtractDecodedData(*decoded_buffer, 0));
 }
 
 }  // namespace media
diff --git a/media/capture/video/OWNERS b/media/capture/video/OWNERS
index 0a79c53..0448766 100644
--- a/media/capture/video/OWNERS
+++ b/media/capture/video/OWNERS
@@ -1,8 +1,8 @@
-emircan@chromium.org
 chfremer@chromium.org
 tommi@chromium.org
 
 # Original (legacy) owner.
+emircan@chromium.org
 mcasas@chromium.org
 
 # TEAM: webrtc-dev@chromium.org
diff --git a/media/filters/audio_renderer_algorithm.cc b/media/filters/audio_renderer_algorithm.cc
index fc92e5d..a8463c6 100644
--- a/media/filters/audio_renderer_algorithm.cc
+++ b/media/filters/audio_renderer_algorithm.cc
@@ -224,9 +224,9 @@
 }
 
 void AudioRendererAlgorithm::EnqueueBuffer(
-    const scoped_refptr<AudioBuffer>& buffer_in) {
+    scoped_refptr<AudioBuffer> buffer_in) {
   DCHECK(!buffer_in->end_of_stream());
-  audio_buffer_.Append(buffer_in);
+  audio_buffer_.Append(std::move(buffer_in));
 }
 
 bool AudioRendererAlgorithm::IsQueueFull() {
diff --git a/media/filters/audio_renderer_algorithm.h b/media/filters/audio_renderer_algorithm.h
index 899e8cd..b73b5d3 100644
--- a/media/filters/audio_renderer_algorithm.h
+++ b/media/filters/audio_renderer_algorithm.h
@@ -74,7 +74,7 @@
 
   // Enqueues a buffer. It is called from the owner of the algorithm after a
   // read completes.
-  void EnqueueBuffer(const scoped_refptr<AudioBuffer>& buffer_in);
+  void EnqueueBuffer(scoped_refptr<AudioBuffer> buffer_in);
 
   // Returns true if |audio_buffer_| is at or exceeds capacity.
   bool IsQueueFull();
diff --git a/media/filters/audio_timestamp_validator.cc b/media/filters/audio_timestamp_validator.cc
index cd5d42a..fb85766 100644
--- a/media/filters/audio_timestamp_validator.cc
+++ b/media/filters/audio_timestamp_validator.cc
@@ -131,18 +131,18 @@
 }
 
 void AudioTimestampValidator::RecordOutputDuration(
-    const scoped_refptr<AudioBuffer>& audio_buffer) {
+    const AudioBuffer& audio_buffer) {
   if (!audio_output_ts_helper_) {
     DCHECK_NE(audio_base_ts_, kNoTimestamp);
     // SUBTLE: deliberately creating this with output buffer sample rate because
     // demuxer stream config is potentially stale for implicit AAC.
     audio_output_ts_helper_.reset(
-        new AudioTimestampHelper(audio_buffer->sample_rate()));
+        new AudioTimestampHelper(audio_buffer.sample_rate()));
     audio_output_ts_helper_->SetBaseTimestamp(audio_base_ts_);
   }
 
-  DVLOG(3) << __func__ << " " << audio_buffer->frame_count() << " frames";
-  audio_output_ts_helper_->AddFrames(audio_buffer->frame_count());
+  DVLOG(3) << __func__ << " " << audio_buffer.frame_count() << " frames";
+  audio_output_ts_helper_->AddFrames(audio_buffer.frame_count());
 }
 
 }  // namespace media
diff --git a/media/filters/audio_timestamp_validator.h b/media/filters/audio_timestamp_validator.h
index 665501c3..7a2ebdf7 100644
--- a/media/filters/audio_timestamp_validator.h
+++ b/media/filters/audio_timestamp_validator.h
@@ -27,7 +27,7 @@
   // timestamp should roughly equal the timestamp of the previous buffer offset
   // by the previous buffer's duration.
   void CheckForTimestampGap(const DecoderBuffer& buffer);
-  void RecordOutputDuration(const scoped_refptr<AudioBuffer>& buffer);
+  void RecordOutputDuration(const AudioBuffer& buffer);
 
  private:
   bool has_codec_delay_;
diff --git a/media/filters/audio_timestamp_validator_unittest.cc b/media/filters/audio_timestamp_validator_unittest.cc
index b4770465..2424091 100644
--- a/media/filters/audio_timestamp_validator_unittest.cc
+++ b/media/filters/audio_timestamp_validator_unittest.cc
@@ -104,7 +104,7 @@
       scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
           kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
           0.0f, kFramesPerBuffer, i * kBufferDuration);
-      validator.RecordOutputDuration(decoded_buffer.get());
+      validator.RecordOutputDuration(*decoded_buffer);
     }
   }
 }
@@ -143,7 +143,7 @@
       scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
           kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
           0.0f, kFramesPerBuffer, i * kBufferDuration);
-      validator.RecordOutputDuration(decoded_buffer.get());
+      validator.RecordOutputDuration(*decoded_buffer);
     }
   }
 }
@@ -188,7 +188,7 @@
       scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
           kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
           0.0f, kFramesPerBuffer, i * kBufferDuration);
-      validator.RecordOutputDuration(decoded_buffer.get());
+      validator.RecordOutputDuration(*decoded_buffer);
     }
   }
 }
@@ -238,7 +238,7 @@
       scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
           kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
           0.0f, kFramesPerBuffer, i * kBufferDuration);
-      validator.RecordOutputDuration(decoded_buffer.get());
+      validator.RecordOutputDuration(*decoded_buffer);
     }
   }
 }
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc
index d18f70b..18804c6 100644
--- a/media/filters/decoder_stream.cc
+++ b/media/filters/decoder_stream.cc
@@ -617,7 +617,7 @@
   }
 
   // If the frame should be dropped, exit early and decode another frame.
-  if (traits_->OnDecodeDone(output) == PostDecodeAction::DROP)
+  if (traits_->OnDecodeDone(output.get()) == PostDecodeAction::DROP)
     return;
 
   if (prepare_cb_ && output->timestamp() + AverageDuration() >=
diff --git a/media/filters/decoder_stream_traits.cc b/media/filters/decoder_stream_traits.cc
index 776616c7..8af2287a 100644
--- a/media/filters/decoder_stream_traits.cc
+++ b/media/filters/decoder_stream_traits.cc
@@ -89,8 +89,8 @@
 }
 
 PostDecodeAction DecoderStreamTraits<DemuxerStream::AUDIO>::OnDecodeDone(
-    const scoped_refptr<OutputType>& buffer) {
-  audio_ts_validator_->RecordOutputDuration(buffer);
+    OutputType* buffer) {
+  audio_ts_validator_->RecordOutputDuration(*buffer);
   return PostDecodeAction::DELIVER;
 }
 
@@ -198,7 +198,7 @@
 }
 
 PostDecodeAction DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecodeDone(
-    const scoped_refptr<OutputType>& buffer) {
+    OutputType* buffer) {
   // Add a timestamp here (after decoding completed) to enable buffering delay
   // measurements down the line.
   buffer->metadata()->SetTimeTicks(media::VideoFrameMetadata::DECODE_TIME,
diff --git a/media/filters/decoder_stream_traits.h b/media/filters/decoder_stream_traits.h
index f97d02c..5eb561b 100644
--- a/media/filters/decoder_stream_traits.h
+++ b/media/filters/decoder_stream_traits.h
@@ -55,7 +55,7 @@
                          const WaitingCB& waiting_cb);
   DecoderConfigType GetDecoderConfig(DemuxerStream* stream);
   void OnDecode(const DecoderBuffer& buffer);
-  PostDecodeAction OnDecodeDone(const scoped_refptr<OutputType>& buffer);
+  PostDecodeAction OnDecodeDone(OutputType* buffer);
   void OnStreamReset(DemuxerStream* stream);
 
  private:
@@ -98,7 +98,7 @@
                          const OutputCB& output_cb,
                          const WaitingCB& waiting_cb);
   void OnDecode(const DecoderBuffer& buffer);
-  PostDecodeAction OnDecodeDone(const scoped_refptr<OutputType>& buffer);
+  PostDecodeAction OnDecodeDone(OutputType* buffer);
   void OnStreamReset(DemuxerStream* stream);
 
  private:
diff --git a/media/filters/ffmpeg_audio_decoder.cc b/media/filters/ffmpeg_audio_decoder.cc
index caf8e1a..8163ef8 100644
--- a/media/filters/ffmpeg_audio_decoder.cc
+++ b/media/filters/ffmpeg_audio_decoder.cc
@@ -267,7 +267,7 @@
     output->TrimEnd(unread_frames);
 
   *decoded_frame_this_loop = true;
-  if (discard_helper_->ProcessBuffers(buffer, output)) {
+  if (discard_helper_->ProcessBuffers(buffer, output.get())) {
     if (is_config_change &&
         output->sample_rate() != config_.samples_per_second()) {
       // At the boundary of the config change, FFmpeg's AAC decoder gives the
diff --git a/media/gpu/OWNERS b/media/gpu/OWNERS
index 1ae3f8c..5aaff96 100644
--- a/media/gpu/OWNERS
+++ b/media/gpu/OWNERS
@@ -10,6 +10,3 @@
 # For Android media gpu files.
 per-file *android*=liberato@chromium.org
 per-file *avda*=liberato@chromium.org
-
-# For Mac encoder files.
-per-file *vt_video_encode*=emircan@chromium.org
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index e75c42e..090f822 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -152,6 +152,7 @@
 
 bool V4L2VideoEncodeAccelerator::Initialize(const Config& config,
                                             Client* client) {
+  TRACE_EVENT0("media,gpu", "V4L2VEA::Initialize");
   VLOGF(2) << ": " << config.AsHumanReadableString();
 
   visible_size_ = config.input_visible_size;
@@ -720,6 +721,7 @@
 
 void V4L2VideoEncodeAccelerator::Enqueue() {
   DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
+  TRACE_EVENT0("media,gpu", "V4L2VEA::Enqueue");
 
   DVLOGF(4) << "free_input_buffers: " << free_input_buffers_.size()
             << "input_queue: " << encoder_input_queue_.size();
@@ -792,6 +794,7 @@
 void V4L2VideoEncodeAccelerator::Dequeue() {
   DVLOGF(4);
   DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
+  TRACE_EVENT0("media,gpu", "V4L2VEA::Dequeue");
 
   // Dequeue completed input (VIDEO_OUTPUT) buffers, and recycle to the free
   // list.
@@ -893,6 +896,7 @@
   DVLOGF(4);
   DCHECK(!free_input_buffers_.empty());
   DCHECK(!encoder_input_queue_.empty());
+  TRACE_EVENT0("media,gpu", "V4L2VEA::EnqueueInputRecord");
 
   // Enqueue an input (VIDEO_OUTPUT) buffer.
   InputFrameInfo frame_info = encoder_input_queue_.front();
@@ -989,6 +993,7 @@
   DVLOGF(4);
   DCHECK(!free_output_buffers_.empty());
   DCHECK(!encoder_output_queue_.empty());
+  TRACE_EVENT0("media,gpu", "V4L2VEA::EnqueueOutputRecord");
 
   // Enqueue an output (VIDEO_CAPTURE) buffer.
   const int index = free_output_buffers_.back();
@@ -1139,6 +1144,8 @@
     uint32_t framerate) {
   VLOGF(2) << "bitrate=" << bitrate << ", framerate=" << framerate;
   DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
+  TRACE_EVENT2("media,gpu", "V4L2VEA::RequestEncodingParametersChangeTask",
+               "bitrate", bitrate, "framerate", framerate);
 
   DCHECK_GT(bitrate, 0u);
   DCHECK_GT(framerate, 0u);
diff --git a/media/gpu/windows/OWNERS b/media/gpu/windows/OWNERS
deleted file mode 100644
index badaf4d..0000000
--- a/media/gpu/windows/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# For encoder files.
-per-file *encode*=emircan@chromium.org
diff --git a/media/mojo/common/media_type_converters.cc b/media/mojo/common/media_type_converters.cc
index ea1e77d..012d818 100644
--- a/media/mojo/common/media_type_converters.cc
+++ b/media/mojo/common/media_type_converters.cc
@@ -119,21 +119,21 @@
 
 // static
 media::mojom::AudioBufferPtr
-TypeConverter<media::mojom::AudioBufferPtr, scoped_refptr<media::AudioBuffer>>::
-    Convert(const scoped_refptr<media::AudioBuffer>& input) {
+TypeConverter<media::mojom::AudioBufferPtr, media::AudioBuffer>::Convert(
+    const media::AudioBuffer& input) {
   media::mojom::AudioBufferPtr buffer(media::mojom::AudioBuffer::New());
-  buffer->sample_format = input->sample_format_;
-  buffer->channel_layout = input->channel_layout();
-  buffer->channel_count = input->channel_count();
-  buffer->sample_rate = input->sample_rate();
-  buffer->frame_count = input->frame_count();
-  buffer->end_of_stream = input->end_of_stream();
-  buffer->timestamp = input->timestamp();
+  buffer->sample_format = input.sample_format_;
+  buffer->channel_layout = input.channel_layout();
+  buffer->channel_count = input.channel_count();
+  buffer->sample_rate = input.sample_rate();
+  buffer->frame_count = input.frame_count();
+  buffer->end_of_stream = input.end_of_stream();
+  buffer->timestamp = input.timestamp();
 
-  if (input->data_) {
-    DCHECK_GT(input->data_size(), 0u);
-    buffer->data.assign(input->data_.get(),
-                        input->data_.get() + input->data_size_);
+  if (input.data_) {
+    DCHECK_GT(input.data_size(), 0u);
+    buffer->data.assign(input.data_.get(),
+                        input.data_.get() + input.data_size_);
   }
 
   return buffer;
diff --git a/media/mojo/common/media_type_converters.h b/media/mojo/common/media_type_converters.h
index 95bd75d..7d51275 100644
--- a/media/mojo/common/media_type_converters.h
+++ b/media/mojo/common/media_type_converters.h
@@ -16,7 +16,7 @@
 class AudioBuffer;
 class DecoderBuffer;
 class DecryptConfig;
-}
+}  // namespace media
 
 // These are specializations of mojo::TypeConverter and have to be in the mojo
 // namespace.
@@ -47,10 +47,8 @@
 };
 
 template <>
-struct TypeConverter<media::mojom::AudioBufferPtr,
-                     scoped_refptr<media::AudioBuffer>> {
-  static media::mojom::AudioBufferPtr Convert(
-      const scoped_refptr<media::AudioBuffer>& input);
+struct TypeConverter<media::mojom::AudioBufferPtr, media::AudioBuffer> {
+  static media::mojom::AudioBufferPtr Convert(const media::AudioBuffer& input);
 };
 template <>
 struct TypeConverter<scoped_refptr<media::AudioBuffer>,
diff --git a/media/mojo/common/media_type_converters_unittest.cc b/media/mojo/common/media_type_converters_unittest.cc
index 1074c3b..7f64616 100644
--- a/media/mojo/common/media_type_converters_unittest.cc
+++ b/media/mojo/common/media_type_converters_unittest.cc
@@ -30,30 +30,30 @@
 }
 
 void CompareAudioBuffers(SampleFormat sample_format,
-                         const scoped_refptr<AudioBuffer>& original,
-                         const scoped_refptr<AudioBuffer>& result) {
-  EXPECT_EQ(original->frame_count(), result->frame_count());
-  EXPECT_EQ(original->timestamp(), result->timestamp());
-  EXPECT_EQ(original->duration(), result->duration());
-  EXPECT_EQ(original->sample_rate(), result->sample_rate());
-  EXPECT_EQ(original->channel_count(), result->channel_count());
-  EXPECT_EQ(original->channel_layout(), result->channel_layout());
-  EXPECT_EQ(original->end_of_stream(), result->end_of_stream());
+                         const AudioBuffer& original,
+                         const AudioBuffer& result) {
+  EXPECT_EQ(original.frame_count(), result.frame_count());
+  EXPECT_EQ(original.timestamp(), result.timestamp());
+  EXPECT_EQ(original.duration(), result.duration());
+  EXPECT_EQ(original.sample_rate(), result.sample_rate());
+  EXPECT_EQ(original.channel_count(), result.channel_count());
+  EXPECT_EQ(original.channel_layout(), result.channel_layout());
+  EXPECT_EQ(original.end_of_stream(), result.end_of_stream());
 
   // Compare bytes in buffer.
   int bytes_per_channel =
-      original->frame_count() * SampleFormatToBytesPerChannel(sample_format);
+      original.frame_count() * SampleFormatToBytesPerChannel(sample_format);
   if (IsPlanar(sample_format)) {
-    for (int i = 0; i < original->channel_count(); ++i) {
-      CompareBytes(original->channel_data()[i], result->channel_data()[i],
+    for (int i = 0; i < original.channel_count(); ++i) {
+      CompareBytes(original.channel_data()[i], result.channel_data()[i],
                    bytes_per_channel);
     }
     return;
   }
 
   DCHECK(IsInterleaved(sample_format)) << sample_format;
-  CompareBytes(original->channel_data()[0], result->channel_data()[0],
-               bytes_per_channel * original->channel_count());
+  CompareBytes(original.channel_data()[0], result.channel_data()[0],
+               bytes_per_channel * original.channel_count());
 }
 
 }  // namespace
@@ -206,7 +206,7 @@
   scoped_refptr<AudioBuffer> buffer(AudioBuffer::CreateEOSBuffer());
 
   // Convert to and back.
-  mojom::AudioBufferPtr ptr(mojom::AudioBuffer::From(buffer));
+  mojom::AudioBufferPtr ptr(mojom::AudioBuffer::From(*buffer));
   scoped_refptr<AudioBuffer> result(ptr.To<scoped_refptr<AudioBuffer>>());
 
   // Compare.
@@ -223,11 +223,11 @@
       kSampleRate / 100, base::TimeDelta());
 
   // Convert to and back.
-  mojom::AudioBufferPtr ptr(mojom::AudioBuffer::From(buffer));
+  mojom::AudioBufferPtr ptr(mojom::AudioBuffer::From(*buffer));
   scoped_refptr<AudioBuffer> result(ptr.To<scoped_refptr<AudioBuffer>>());
 
   // Compare.
-  CompareAudioBuffers(kSampleFormatU8, buffer, result);
+  CompareAudioBuffers(kSampleFormatU8, *buffer, *result);
 }
 
 TEST(MediaTypeConvertersTest, ConvertAudioBuffer_FLOAT) {
@@ -240,11 +240,11 @@
       ChannelLayoutToChannelCount(kChannelLayout), kSampleRate, 0.0f, 1.0f,
       kSampleRate / 10, start_time);
   // Convert to and back.
-  mojom::AudioBufferPtr ptr(mojom::AudioBuffer::From(buffer));
+  mojom::AudioBufferPtr ptr(mojom::AudioBuffer::From(*buffer));
   scoped_refptr<AudioBuffer> result(ptr.To<scoped_refptr<AudioBuffer>>());
 
   // Compare.
-  CompareAudioBuffers(kSampleFormatPlanarF32, buffer, result);
+  CompareAudioBuffers(kSampleFormatPlanarF32, *buffer, *result);
 }
 
 }  // namespace media
diff --git a/media/mojo/services/mojo_audio_decoder_service.cc b/media/mojo/services/mojo_audio_decoder_service.cc
index 55046a9..72a1d7d 100644
--- a/media/mojo/services/mojo_audio_decoder_service.cc
+++ b/media/mojo/services/mojo_audio_decoder_service.cc
@@ -137,7 +137,7 @@
   DVLOG(1) << __func__;
 
   // TODO(timav): Use DataPipe.
-  client_->OnBufferDecoded(mojom::AudioBuffer::From(std::move(audio_buffer)));
+  client_->OnBufferDecoded(mojom::AudioBuffer::From(*audio_buffer));
 }
 
 void MojoAudioDecoderService::OnWaiting(WaitingReason reason) {
diff --git a/media/mojo/services/mojo_decryptor_service.cc b/media/mojo/services/mojo_decryptor_service.cc
index 8a08b90..a57aa3b 100644
--- a/media/mojo/services/mojo_decryptor_service.cc
+++ b/media/mojo/services/mojo_decryptor_service.cc
@@ -265,7 +265,7 @@
   // improved to use shared memory (http://crbug.com/593896).
   std::vector<mojom::AudioBufferPtr> audio_buffers;
   for (const auto& frame : frames)
-    audio_buffers.push_back(mojom::AudioBuffer::From(frame));
+    audio_buffers.push_back(mojom::AudioBuffer::From(*frame));
 
   std::move(callback).Run(status, std::move(audio_buffers));
 }
diff --git a/media/renderers/audio_renderer_impl_unittest.cc b/media/renderers/audio_renderer_impl_unittest.cc
index 4e78e33..c11c8950 100644
--- a/media/renderers/audio_renderer_impl_unittest.cc
+++ b/media/renderers/audio_renderer_impl_unittest.cc
@@ -329,7 +329,7 @@
     }
     next_timestamp_->AddFrames(frames.value);
 
-    DeliverBuffer(DecodeStatus::OK, buffer);
+    DeliverBuffer(DecodeStatus::OK, std::move(buffer));
   }
 
   void DeliverEndOfStream() {
@@ -476,12 +476,11 @@
     main_thread_task_runner_->PostTask(FROM_HERE, reset_cb);
   }
 
-  void DeliverBuffer(DecodeStatus status,
-                     const scoped_refptr<AudioBuffer>& buffer) {
+  void DeliverBuffer(DecodeStatus status, scoped_refptr<AudioBuffer> buffer) {
     CHECK(decode_cb_);
 
     if (buffer.get() && !buffer->end_of_stream())
-      output_cb_.Run(buffer);
+      output_cb_.Run(std::move(buffer));
     std::move(decode_cb_).Run(status);
 
     if (reset_cb_)
@@ -739,7 +738,7 @@
   scoped_refptr<AudioBuffer> buffer = MakeAudioBuffer<float>(
       kSampleFormat, hw_params.channel_layout(), hw_params.channels(),
       kInputSamplesPerSecond, 1.0f, 0.0f, 256, base::TimeDelta());
-  DeliverBuffer(DecodeStatus::OK, buffer);
+  DeliverBuffer(DecodeStatus::OK, std::move(buffer));
 
   // All channels should now be enabled.
   mask = channel_mask();
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
index 906eae0..5ce6aa11 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -400,11 +400,15 @@
   switch (message->header()->name) {
 {%-   for method in interface.methods %}
     case internal::k{{class_name}}_{{method.name}}_Name: {
-#if BUILDFLAG(MOJO_TRACE_ENABLED)
-  TRACE_EVENT1("mojom", "(Impl){{namespace_as_string}}::{{class_name}}::{{method.name}}",
-               "message", message->name());
-#endif
 {%-     if method.response_parameters == None %}
+#if BUILDFLAG(MOJO_TRACE_ENABLED)
+      TRACE_EVENT1(
+          "mojom",
+          "(Impl){{namespace_as_string}}::{{class_name}}::{{method.name}}",
+          "message", message->name());
+#endif
+      base::TaskAnnotator::ScopedSetIpcProgramCounter scoped_ipc_pc(
+          base::GetProgramCounter());
       mojo::internal::MessageDispatchContext context(message);
 {%-       if method|method_supports_lazy_serialization %}
       if (!message->is_serialized()) {
@@ -453,11 +457,15 @@
   switch (message->header()->name) {
 {%-   for method in interface.methods %}
     case internal::k{{class_name}}_{{method.name}}_Name: {
-#if BUILDFLAG(MOJO_TRACE_ENABLED)
-  TRACE_EVENT1("mojom", "(Impl){{namespace_as_string}}::{{class_name}}::{{method.name}}",
-               "message", message->name());
-#endif
 {%-     if method.response_parameters != None %}
+#if BUILDFLAG(MOJO_TRACE_ENABLED)
+      TRACE_EVENT1(
+          "mojom",
+          "(Impl){{namespace_as_string}}::{{class_name}}::{{method.name}}",
+          "message", message->name());
+#endif
+      base::TaskAnnotator::ScopedSetIpcProgramCounter scoped_ipc_pc(
+          base::GetProgramCounter());
       mojo::internal::MessageDispatchContext context(message);
 {%-       if method|method_supports_lazy_serialization %}
       if (!message->is_serialized()) {
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
index f6f1388..eea0f5d 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -28,8 +28,10 @@
 #include <stdint.h>
 #include <utility>
 
+#include "base/location.h"
 #include "base/logging.h"
 #include "base/run_loop.h"
+#include "base/task/common/task_annotator.h"
 #include "mojo/public/cpp/bindings/lib/message_internal.h"
 #include "mojo/public/cpp/bindings/lib/serialization_util.h"
 #include "mojo/public/cpp/bindings/lib/unserialized_message_context.h"
diff --git a/net/websockets/websocket_channel.cc b/net/websockets/websocket_channel.cc
index 9bb200d..958dc50 100644
--- a/net/websockets/websocket_channel.cc
+++ b/net/websockets/websocket_channel.cc
@@ -202,8 +202,8 @@
       int net_error,
       const SSLInfo& ssl_info,
       bool fatal) override {
-    creator_->OnSSLCertificateError(std::move(ssl_error_callbacks), ssl_info,
-                                    fatal);
+    creator_->OnSSLCertificateError(std::move(ssl_error_callbacks), net_error,
+                                    ssl_info, fatal);
   }
 
   int OnAuthRequired(const AuthChallengeInfo& auth_info,
@@ -596,10 +596,11 @@
 void WebSocketChannel::OnSSLCertificateError(
     std::unique_ptr<WebSocketEventInterface::SSLErrorCallbacks>
         ssl_error_callbacks,
+    int net_error,
     const SSLInfo& ssl_info,
     bool fatal) {
-  event_interface_->OnSSLCertificateError(std::move(ssl_error_callbacks),
-                                          socket_url_, ssl_info, fatal);
+  event_interface_->OnSSLCertificateError(
+      std::move(ssl_error_callbacks), socket_url_, net_error, ssl_info, fatal);
 }
 
 int WebSocketChannel::OnAuthRequired(
diff --git a/net/websockets/websocket_channel.h b/net/websockets/websocket_channel.h
index 977fdc4..23e05260 100644
--- a/net/websockets/websocket_channel.h
+++ b/net/websockets/websocket_channel.h
@@ -208,6 +208,7 @@
   void OnSSLCertificateError(
       std::unique_ptr<WebSocketEventInterface::SSLErrorCallbacks>
           ssl_error_callbacks,
+      int net_error,
       const SSLInfo& ssl_info,
       bool fatal);
 
diff --git a/net/websockets/websocket_channel_test.cc b/net/websockets/websocket_channel_test.cc
index 1d981986..0e3f605 100644
--- a/net/websockets/websocket_channel_test.cc
+++ b/net/websockets/websocket_channel_test.cc
@@ -197,6 +197,7 @@
   void OnSSLCertificateError(
       std::unique_ptr<SSLErrorCallbacks> ssl_error_callbacks,
       const GURL& url,
+      int net_error,
       const SSLInfo& ssl_info,
       bool fatal) override {
     OnSSLCertificateErrorCalled(
@@ -247,6 +248,7 @@
   void OnSSLCertificateError(
       std::unique_ptr<SSLErrorCallbacks> ssl_error_callbacks,
       const GURL& url,
+      int net_error,
       const SSLInfo& ssl_info,
       bool fatal) override {}
   int OnAuthRequired(const AuthChallengeInfo& auth_info,
diff --git a/net/websockets/websocket_end_to_end_test.cc b/net/websockets/websocket_end_to_end_test.cc
index 4ce998a..7edcbb0 100644
--- a/net/websockets/websocket_end_to_end_test.cc
+++ b/net/websockets/websocket_end_to_end_test.cc
@@ -123,6 +123,7 @@
   void OnSSLCertificateError(
       std::unique_ptr<SSLErrorCallbacks> ssl_error_callbacks,
       const GURL& url,
+      int net_error,
       const SSLInfo& ssl_info,
       bool fatal) override;
 
@@ -200,6 +201,7 @@
 void ConnectTestingEventInterface::OnSSLCertificateError(
     std::unique_ptr<SSLErrorCallbacks> ssl_error_callbacks,
     const GURL& url,
+    int net_error,
     const SSLInfo& ssl_info,
     bool fatal) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/net/websockets/websocket_event_interface.h b/net/websockets/websocket_event_interface.h
index b7a4343..0edf249 100644
--- a/net/websockets/websocket_event_interface.h
+++ b/net/websockets/websocket_event_interface.h
@@ -123,6 +123,7 @@
   virtual void OnSSLCertificateError(
       std::unique_ptr<SSLErrorCallbacks> ssl_error_callbacks,
       const GURL& url,
+      int net_error,
       const SSLInfo& ssl_info,
       bool fatal) = 0;
 
diff --git a/services/identity/public/cpp/accounts_cookie_mutator_unittest.cc b/services/identity/public/cpp/accounts_cookie_mutator_unittest.cc
index 24c739a6..2ffb1fad 100644
--- a/services/identity/public/cpp/accounts_cookie_mutator_unittest.cc
+++ b/services/identity/public/cpp/accounts_cookie_mutator_unittest.cc
@@ -13,6 +13,8 @@
 #include "base/test/gtest_util.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/signin/core/browser/list_accounts_test_utils.h"
+#include "components/signin/core/browser/test_signin_client.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "google_apis/gaia/gaia_auth_fetcher.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/gaia_urls.h"
@@ -58,7 +60,12 @@
 namespace identity {
 class AccountsCookieMutatorTest : public testing::Test {
  public:
-  AccountsCookieMutatorTest() : identity_test_env_(&test_url_loader_factory_) {}
+  AccountsCookieMutatorTest()
+      : test_signin_client_(&prefs_),
+        identity_test_env_(test_url_loader_factory(),
+                           &prefs_,
+                           signin::AccountConsistencyMethod::kDisabled,
+                           &test_signin_client_) {}
 
   ~AccountsCookieMutatorTest() override {}
 
@@ -72,7 +79,7 @@
   void PrepareURLLoaderResponsesForAction(AccountsCookiesMutatorAction action) {
     switch (action) {
       case AccountsCookiesMutatorAction::kAddAccountToCookie:
-        test_url_loader_factory_.AddResponse(
+        test_url_loader_factory()->AddResponse(
             GaiaUrls::GetInstance()
                 ->oauth1_login_url()
                 .Resolve(base::StringPrintf("?source=%s&issueuberauth=1",
@@ -80,14 +87,14 @@
                 .spec(),
             kTestUberToken, net::HTTP_OK);
 
-        test_url_loader_factory_.AddResponse(
+        test_url_loader_factory()->AddResponse(
             GaiaUrls::GetInstance()
                 ->GetCheckConnectionInfoURLWithSource(
                     GaiaConstants::kChromeSource)
                 .spec(),
             std::string(), net::HTTP_OK);
 
-        test_url_loader_factory_.AddResponse(
+        test_url_loader_factory()->AddResponse(
             GaiaUrls::GetInstance()
                 ->merge_session_url()
                 .Resolve(base::StringPrintf(
@@ -97,7 +104,7 @@
             std::string(), net::HTTP_OK);
         break;
       case AccountsCookiesMutatorAction::kSetAccountsInCookie:
-        test_url_loader_factory_.AddResponse(
+        test_url_loader_factory()->AddResponse(
             GaiaUrls::GetInstance()
                 ->oauth_multilogin_url()
                 .Resolve(base::StringPrintf("?source=%s",
@@ -106,11 +113,11 @@
             std::string(kTestOAuthMultiLoginResponse), net::HTTP_OK);
         break;
       case AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts:
-        signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
+        signin::SetListAccountsResponseNoAccounts(test_url_loader_factory());
         break;
       case AccountsCookiesMutatorAction::kTriggerCookieJarUpdateOneAccount:
         signin::SetListAccountsResponseOneAccount(
-            kTestAccountEmail, kTestAccountGaiaId, &test_url_loader_factory_);
+            kTestAccountEmail, kTestAccountGaiaId, test_url_loader_factory());
         break;
     }
   }
@@ -128,12 +135,13 @@
   }
 
   network::TestURLLoaderFactory* test_url_loader_factory() {
-    return &test_url_loader_factory_;
+    return test_signin_client_.test_url_loader_factory();
   }
 
  private:
   base::test::ScopedTaskEnvironment task_environment_;
-  network::TestURLLoaderFactory test_url_loader_factory_;
+  sync_preferences::TestingPrefServiceSyncable prefs_;
+  TestSigninClient test_signin_client_;
   identity::IdentityTestEnvironment identity_test_env_;
 
   DISALLOW_COPY_AND_ASSIGN(AccountsCookieMutatorTest);
diff --git a/services/identity/public/cpp/identity_manager_unittest.cc b/services/identity/public/cpp/identity_manager_unittest.cc
index aa3d2e97..49d3405 100644
--- a/services/identity/public/cpp/identity_manager_unittest.cc
+++ b/services/identity/public/cpp/identity_manager_unittest.cc
@@ -353,9 +353,9 @@
                              network::mojom::CookieChangeCause::EXPLICIT);
   }
 
-  void SimulateOAuthMultiloginFinished(GaiaAuthConsumer* consumer,
-                                       const OAuthMultiloginResult& result) {
-    consumer->OnOAuthMultiloginFinished(result);
+  void SimulateOAuthMultiloginFinished(GaiaCookieManagerService* manager,
+                                       const GoogleServiceAuthError& error) {
+    manager->OnSetAccountsFinished(error);
   }
 
   std::string primary_account_id() { return primary_account_id_; }
@@ -1813,29 +1813,9 @@
   identity_manager()->GetGaiaCookieManagerService()->SetAccountsInCookie(
       account_ids, gaia::GaiaSource::kChrome, std::move(completion_callback));
 
-  // Sample success cookie response.
-  std::string data =
-      R"()]}'
-      {
-        "status": "OK",
-        "cookies":[
-        {
-            "name":"SID",
-            "value":"vAlUe1",
-            "domain":".google.ru",
-            "path":"/",
-            "isSecure":true,
-            "isHttpOnly":false,
-            "priority":"HIGH",
-            "maxAge":63070000
-          }
-        ]
-      }
-    )";
-  OAuthMultiloginResult result(data);
-
   SimulateOAuthMultiloginFinished(
-      identity_manager()->GetGaiaCookieManagerService(), result);
+      identity_manager()->GetGaiaCookieManagerService(),
+      GoogleServiceAuthError::AuthErrorNone());
 
   EXPECT_EQ(error_from_set_accounts_in_cookie_completed_callback,
             GoogleServiceAuthError::AuthErrorNone());
@@ -1861,66 +1841,27 @@
 
   // Sample an erroneous response.
   GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_ERROR);
-  OAuthMultiloginResult result(error);
 
   SimulateOAuthMultiloginFinished(
-      identity_manager()->GetGaiaCookieManagerService(), result);
+      identity_manager()->GetGaiaCookieManagerService(), error);
 
   EXPECT_EQ(error_from_set_accounts_in_cookie_completed_callback, error);
 }
 
 TEST_F(IdentityManagerTest, CallbackSentOnAccountsCookieDeletedByUserAction) {
-  const char kTestAccountId[] = "account_id";
-  const char kTestAccountId2[] = "account_id2";
-  const std::vector<std::string> account_ids = {kTestAccountId,
-                                                kTestAccountId2};
-
-  // Needed to insert request in the queue.
-  identity_manager()->GetGaiaCookieManagerService()->SetAccountsInCookie(
-      account_ids, gaia::GaiaSource::kChrome,
-      GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
-
-  // Sample success cookie response.
-  std::string data =
-      R"()]}'
-      {
-        "status": "OK",
-        "cookies":[
-        {
-            "name":"APISID",
-            "value":"vAlUe1",
-            "domain":".google.com",
-            "path":"/",
-            "isSecure":true,
-            "isHttpOnly":false,
-            "priority":"HIGH",
-            "maxAge":63070000
-          }
-        ]
-      }
-    )";
-  OAuthMultiloginResult result(data);
-
-  SimulateOAuthMultiloginFinished(
-      identity_manager()->GetGaiaCookieManagerService(), result);
-  base::RunLoop().RunUntilIdle();
-
   base::RunLoop run_loop;
   identity_manager_observer()->SetOnCookieDeletedByUserCallback(
       run_loop.QuitClosure());
-
-  const std::vector<net::CanonicalCookie>& cookies = result.cookies();
+  net::CanonicalCookie cookie("APISID", std::string(), ".google.com", "/",
+                              base::Time(), base::Time(), base::Time(), false,
+                              false, net::CookieSameSite::NO_RESTRICTION,
+                              net::COOKIE_PRIORITY_DEFAULT);
   SimulateCookieDeletedByUser(identity_manager()->GetGaiaCookieManagerService(),
-                              cookies[0]);
+                              cookie);
   run_loop.Run();
 }
 
 TEST_F(IdentityManagerTest, OnNetworkInitialized) {
-  const char kTestAccountId[] = "account_id";
-  const char kTestAccountId2[] = "account_id2";
-  const std::vector<std::string> account_ids = {kTestAccountId,
-                                                kTestAccountId2};
-
   auto test_cookie_manager = std::make_unique<network::TestCookieManager>();
   network::TestCookieManager* test_cookie_manager_ptr =
       test_cookie_manager.get();
@@ -1928,42 +1869,10 @@
 
   identity_manager()->OnNetworkInitialized();
 
-  // Needed to insert request in the queue.
-  identity_manager()->GetGaiaCookieManagerService()->SetAccountsInCookie(
-      account_ids, gaia::GaiaSource::kChrome,
-      GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
-
-  // Sample success cookie response.
-  std::string data =
-      R"()]}'
-      {
-        "status": "OK",
-        "cookies":[
-        {
-            "name":"APISID",
-            "value":"vAlUe1",
-            "domain":".google.com",
-            "path":"/",
-            "isSecure":true,
-            "isHttpOnly":false,
-            "priority":"HIGH",
-            "maxAge":63070000
-          }
-        ]
-      }
-    )";
-  OAuthMultiloginResult result(data);
-
-  SimulateOAuthMultiloginFinished(
-      identity_manager()->GetGaiaCookieManagerService(), result);
-  base::RunLoop().RunUntilIdle();
-
   base::RunLoop run_loop;
   identity_manager_observer()->SetOnCookieDeletedByUserCallback(
       run_loop.QuitClosure());
 
-  const std::vector<net::CanonicalCookie>& cookies = result.cookies();
-
   // Dispatch a known change of a known cookie instance *through the mojo
   // pipe* in order to ensure the GCMS is listening to CookieManager changes.
   //
@@ -1975,8 +1884,12 @@
   // Note that this call differs from calling SimulateCookieDeletedByUser()
   // directly in the sense that SimulateCookieDeletedByUser() does not go
   // through any mojo pipe.
+  net::CanonicalCookie cookie("APISID", std::string(), ".google.com", "/",
+                              base::Time(), base::Time(), base::Time(), false,
+                              false, net::CookieSameSite::NO_RESTRICTION,
+                              net::COOKIE_PRIORITY_DEFAULT);
   test_cookie_manager_ptr->DispatchCookieChange(
-      cookies[0], network::mojom::CookieChangeCause::EXPLICIT);
+      cookie, network::mojom::CookieChangeCause::EXPLICIT);
   run_loop.Run();
 }
 
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index e11b4b03..d308c48 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -114,6 +114,7 @@
                         uint32 request_id,
                         int32 resource_type,
                         url.mojom.Url url,
+                        int32 net_error,
                         SSLInfo ssl_info,
                         bool fatal) => (int32 net_error);
 
diff --git a/services/network/test/test_network_service_client.cc b/services/network/test/test_network_service_client.cc
index 079d523..92583275 100644
--- a/services/network/test/test_network_service_client.cc
+++ b/services/network/test/test_network_service_client.cc
@@ -48,6 +48,7 @@
     uint32_t request_id,
     int32_t resource_type,
     const GURL& url,
+    int net_error,
     const net::SSLInfo& ssl_info,
     bool fatal,
     OnSSLCertificateErrorCallback response) {
diff --git a/services/network/test/test_network_service_client.h b/services/network/test/test_network_service_client.h
index fa011107..9f4b3ca0 100644
--- a/services/network/test/test_network_service_client.h
+++ b/services/network/test/test_network_service_client.h
@@ -52,6 +52,7 @@
                              uint32_t request_id,
                              int32_t resource_type,
                              const GURL& url,
+                             int net_error,
                              const net::SSLInfo& ssl_info,
                              bool fatal,
                              OnSSLCertificateErrorCallback response) override;
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 841dac9..e8b56162 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -806,7 +806,8 @@
   }
   network_service_client_->OnSSLCertificateError(
       factory_params_->process_id, render_frame_id_, request_id_,
-      static_cast<int>(resource_type_), url_request_->url(), ssl_info, fatal,
+      static_cast<int>(resource_type_), url_request_->url(), net_error,
+      ssl_info, fatal,
       base::Bind(&URLLoader::OnSSLCertificateErrorResponse,
                  weak_ptr_factory_.GetWeakPtr(), ssl_info));
 }
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index 41756d8d..56e92ee 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -2341,6 +2341,7 @@
                              uint32_t request_id,
                              int32_t resource_type,
                              const GURL& url,
+                             int net_error,
                              const net::SSLInfo& ssl_info,
                              bool fatal,
                              OnSSLCertificateErrorCallback response) override {
diff --git a/services/network/websocket.cc b/services/network/websocket.cc
index ebf19d4a..76b61c9 100644
--- a/services/network/websocket.cc
+++ b/services/network/websocket.cc
@@ -101,6 +101,7 @@
       std::unique_ptr<net::WebSocketEventInterface::SSLErrorCallbacks>
           callbacks,
       const GURL& url,
+      int net_error,
       const net::SSLInfo& ssl_info,
       bool fatal) override;
   int OnAuthRequired(
@@ -277,6 +278,7 @@
 void WebSocket::WebSocketEventHandler::OnSSLCertificateError(
     std::unique_ptr<net::WebSocketEventInterface::SSLErrorCallbacks> callbacks,
     const GURL& url,
+    int net_error,
     const net::SSLInfo& ssl_info,
     bool fatal) {
   DVLOG(3) << "WebSocketEventHandler::OnSSLCertificateError"
@@ -284,7 +286,7 @@
            << " cert_status=" << ssl_info.cert_status << " fatal=" << fatal;
   impl_->delegate_->OnSSLCertificateError(std::move(callbacks), url,
                                           impl_->child_id_, impl_->frame_id_,
-                                          ssl_info, fatal);
+                                          net_error, ssl_info, fatal);
 }
 
 int WebSocket::WebSocketEventHandler::OnAuthRequired(
diff --git a/services/network/websocket.h b/services/network/websocket.h
index d11a5e6..c7d3653 100644
--- a/services/network/websocket.h
+++ b/services/network/websocket.h
@@ -52,6 +52,7 @@
         const GURL& url,
         int child_id,
         int frame_id,
+        int net_error,
         const net::SSLInfo& ssl_info,
         bool fatal) = 0;
     // This function may delete |impl|.
diff --git a/services/network/websocket_factory.cc b/services/network/websocket_factory.cc
index deaf8b22..6beff426 100644
--- a/services/network/websocket_factory.cc
+++ b/services/network/websocket_factory.cc
@@ -37,6 +37,7 @@
       const GURL& url,
       int process_id,
       int render_frame_id,
+      int net_error,
       const net::SSLInfo& ssl_info,
       bool fatal) override {
     DCHECK(!callbacks_);
@@ -52,8 +53,8 @@
     constexpr uint32_t request_id = static_cast<uint32_t>(-1);
 
     network_service->client()->OnSSLCertificateError(
-        process_id, render_frame_id, request_id, resource_type, url, ssl_info,
-        fatal,
+        process_id, render_frame_id, request_id, resource_type, url, net_error,
+        ssl_info, fatal,
         base::BindRepeating(&Delegate::OnSSLCertificateErrorResponse,
                             weak_factory_.GetWeakPtr(), ssl_info));
   }
diff --git a/services/video_capture/OWNERS b/services/video_capture/OWNERS
index 495d7a5..da0857b 100644
--- a/services/video_capture/OWNERS
+++ b/services/video_capture/OWNERS
@@ -1,5 +1,4 @@
 chfremer@chromium.org
-emircan@chromium.org
 
 # Original (legacy) owner.
 per-file *video*=mcasas@chromium.org
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 9dd3f4d1..343a801 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -153,11 +153,6 @@
 #define SK_SUPPORT_LEGACY_DRAWLOOPER
 #endif
 
-// Chrome should only sort opLists within DDLs
-#ifndef SK_DISABLE_OPLIST_SORTING
-#define SK_DISABLE_OPLIST_SORTING
-#endif
-
 #ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
 #define SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
 #endif
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index e729445..9d22307 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -5121,7 +5121,6 @@
     ],
     "gtest_tests": [
       {
-        "experiment_percentage": 100,
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 4c005ce..53bb516 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -52,8 +52,10 @@
       {
         "args": [
           "-v",
-          "--browser=android-chrome",
+          "--browser=exact",
           "--upload-results",
+          "--browser-executable=../../out/Release/bin/monochrome_64_32_bundle",
+          "--device=android",
           "--run-ref-build",
           "--test-shard-map-filename=android-pixel2-perf_map.json"
         ],
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index d947cd7..bf9ec81 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -4115,9 +4115,7 @@
 
     'ozone_linux_gtests': {
       'services_unittests': {},
-      'ozone_unittests': {
-        'experiment_percentage': 100,
-      },
+      'ozone_unittests': {},
     },
 
     'performance_smoke_test_isolated_scripts': {
diff --git a/third_party/blink/renderer/core/accessibility/apply_dark_mode.cc b/third_party/blink/renderer/core/accessibility/apply_dark_mode.cc
index 8166679d..1be8571 100644
--- a/third_party/blink/renderer/core/accessibility/apply_dark_mode.cc
+++ b/third_party/blink/renderer/core/accessibility/apply_dark_mode.cc
@@ -53,6 +53,7 @@
   dark_mode_settings.grayscale = frame_settings.GetDarkModeGrayscale();
   dark_mode_settings.contrast = frame_settings.GetDarkModeContrast();
   dark_mode_settings.image_policy = frame_settings.GetDarkModeImagePolicy();
+  dark_mode_settings.image_style = frame_settings.GetDarkModeImageStyle();
   return dark_mode_settings;
 }
 
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc
index f86fe6f..ce8e629 100644
--- a/third_party/blink/renderer/core/animation/animation.cc
+++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -59,10 +59,14 @@
 
 namespace {
 
-static unsigned NextSequenceNumber() {
+unsigned NextSequenceNumber() {
   static unsigned next = 0;
   return ++next;
 }
+
+double ToMilliseconds(double seconds) {
+  return seconds * 1000;
+}
 }
 
 Animation* Animation::Create(AnimationEffect* effect,
@@ -261,24 +265,40 @@
   return result;
 }
 
+// https://drafts.csswg.org/web-animations/#the-current-time-of-an-animation
 double Animation::currentTime() {
   PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand);
 
-  if (PlayStateInternal() == kIdle || (!hold_time_ && !start_time_))
-    return std::numeric_limits<double>::quiet_NaN();
+  // 1. If the animation’s hold time is resolved,
+  //    The current time is the animation’s hold time.
+  if (hold_time_.has_value())
+    return ToMilliseconds(hold_time_.value());
 
-  return CurrentTimeInternal() * 1000;
+  // 2.  If any of the following are true:
+  //    * the animation has no associated timeline, or
+  //    * the associated timeline is inactive, or
+  //    * the animation’s start time is unresolved.
+  // The current time is an unresolved time value.
+  if (!timeline_ || PlayStateInternal() == kIdle || !start_time_)
+    return NullValue();
+
+  // 3. Otherwise,
+  // current time = (timeline time - start time) × playback rate
+  double current_time =
+      (timeline_->EffectiveTime() - start_time_.value()) * playback_rate_;
+  return ToMilliseconds(current_time);
 }
 
 double Animation::CurrentTimeInternal() const {
   double result = hold_time_.value_or(CalculateCurrentTime());
 #if DCHECK_IS_ON()
-  // We can't enforce this check during Unset due to other
-  // assertions.
+  // We can't enforce this check during Unset due to other assertions.
   if (play_state_ != kUnset) {
     const_cast<Animation*>(this)->UpdateCurrentTimingState(
         kTimingUpdateOnDemand);
-    DCHECK_EQ(result, hold_time_.value_or(CalculateCurrentTime()));
+    double hold_or_current_time = hold_time_.value_or(CalculateCurrentTime());
+    DCHECK((IsNull(result) && IsNull(hold_or_current_time)) ||
+           result == hold_or_current_time);
   }
 #endif
   return result;
@@ -455,9 +475,8 @@
 }
 
 double Animation::CalculateCurrentTime() const {
-  // TODO(crbug.com/818196): By spec, this should be unresolved, not 0.
   if (!start_time_ || !timeline_)
-    return 0;
+    return NullValue();
   return (timeline_->EffectiveTime() - start_time_.value()) * playback_rate_;
 }
 
@@ -526,7 +545,8 @@
     new_effect->Attach(this);
     SetOutdated();
   }
-  SetCurrentTimeInternal(stored_current_time, kTimingUpdateOnDemand);
+  if (!IsNull(stored_current_time))
+    SetCurrentTimeInternal(stored_current_time, kTimingUpdateOnDemand);
 }
 
 const char* Animation::PlayStateString(AnimationPlayState play_state) {
@@ -609,7 +629,7 @@
   PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand);
 
   double current_time = this->CurrentTimeInternal();
-  if (playback_rate_ < 0 && current_time <= 0 &&
+  if (playback_rate_ < 0 && (current_time <= 0 || IsNull(current_time)) &&
       EffectEnd() == std::numeric_limits<double>::infinity()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
@@ -629,11 +649,12 @@
   finished_ = false;
   UnpauseInternal();
 
-  if (playback_rate_ > 0 && (current_time < 0 || current_time >= EffectEnd())) {
+  if (playback_rate_ > 0 && (IsNull(current_time) || current_time < 0 ||
+                             current_time >= EffectEnd())) {
     start_time_ = base::nullopt;
     SetCurrentTimeInternal(0, kTimingUpdateOnDemand);
-  } else if (playback_rate_ < 0 &&
-             (current_time <= 0 || current_time > EffectEnd())) {
+  } else if (playback_rate_ < 0 && (IsNull(current_time) || current_time <= 0 ||
+                                    current_time > EffectEnd())) {
     start_time_ = base::nullopt;
     SetCurrentTimeInternal(EffectEnd(), kTimingUpdateOnDemand);
   }
@@ -771,7 +792,8 @@
 
   playback_rate_ = playback_rate;
   start_time_ = base::nullopt;
-  SetCurrentTimeInternal(stored_current_time, kTimingUpdateOnDemand);
+  if (!IsNull(stored_current_time))
+    SetCurrentTimeInternal(stored_current_time, kTimingUpdateOnDemand);
 }
 
 void Animation::ClearOutdated() {
diff --git a/third_party/blink/renderer/core/dom/comment.h b/third_party/blink/renderer/core/dom/comment.h
index b8d1d7b..8a527885 100644
--- a/third_party/blink/renderer/core/dom/comment.h
+++ b/third_party/blink/renderer/core/dom/comment.h
@@ -24,6 +24,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_COMMENT_H_
 
 #include "third_party/blink/renderer/core/dom/character_data.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
 
@@ -42,7 +43,12 @@
   void DetachLayoutTree(const AttachContext&) final {}
 };
 
-DEFINE_NODE_TYPE_CASTS(Comment, getNodeType() == Node::kCommentNode);
+template <>
+struct DowncastTraits<Comment> {
+  static bool AllowFrom(const Node& node) {
+    return node.getNodeType() == Node::kCommentNode;
+  }
+};
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc b/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc
index 8c23d05..1db69e4 100644
--- a/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc
+++ b/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc
@@ -158,7 +158,7 @@
       NOTREACHED();
       break;
     case Node::kCommentNode:
-      AppendComment(result, ToComment(node).data());
+      AppendComment(result, To<Comment>(node).data());
       break;
     case Node::kDocumentNode:
       AppendXMLDeclaration(result, To<Document>(node));
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc
index e9b7a33..2d1eac3 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.cc
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
@@ -359,12 +359,12 @@
   if (!fragment->firstChild())
     return false;
   for (Node& node : NodeTraversal::StartsAt(*fragment->firstChild())) {
-    if (node.getNodeType() == Node::kCommentNode &&
-        ToComment(node).data() == kFragmentMarkerTag) {
+    auto* comment_node = DynamicTo<Comment>(node);
+    if (comment_node && comment_node->data() == kFragmentMarkerTag) {
       if (!node_before_context) {
-        node_before_context = &ToComment(node);
+        node_before_context = comment_node;
       } else {
-        node_after_context = &ToComment(node);
+        node_after_context = comment_node;
         return true;
       }
     }
diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5
index 9088fd73..005d659 100644
--- a/third_party/blink/renderer/core/frame/settings.json5
+++ b/third_party/blink/renderer/core/frame/settings.json5
@@ -918,6 +918,12 @@
       invalidate: "Paint",
     },
     {
+      name: "darkModeImageStyle",
+      initial: "DarkModeImageStyle::kDefault",
+      type: "DarkModeImageStyle",
+      invalidate: "Paint",
+    },
+    {
       name: "navigatorPlatformOverride",
       type: "String",
     },
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.cc b/third_party/blink/renderer/core/frame/visual_viewport.cc
index c42d1e2b..4324363 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport.cc
@@ -130,6 +130,7 @@
         GetChromeClient()->GetDeviceEmulationTransform();
     if (!device_emulation_transform.IsIdentity()) {
       TransformPaintPropertyNode::State state{device_emulation_transform};
+      state.in_subtree_of_page_scale = false;
       if (!device_emulation_transform_node_) {
         device_emulation_transform_node_ = TransformPaintPropertyNode::Create(
             *transform_parent, std::move(state));
@@ -152,7 +153,10 @@
   }
 
   {
+    DCHECK(!transform_parent->IsInSubtreeOfPageScale());
+
     TransformPaintPropertyNode::State state;
+    state.in_subtree_of_page_scale = false;
     state.compositor_element_id = GetCompositorOverscrollElasticityElementId();
     // TODO(crbug.com/877794) Should create overscroll elasticity transform node
     // based on settings.
@@ -170,6 +174,7 @@
   {
     TransformPaintPropertyNode::State state{
         TransformationMatrix().Scale(Scale())};
+    state.in_subtree_of_page_scale = false;
     state.compositor_element_id = GetCompositorElementId();
 
     if (!scale_transform_node_) {
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 faa01de6..6dd35ab 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -2594,5 +2594,36 @@
   EXPECT_EQ(3.f, visual_viewport.Scale());
 }
 
+// |TransformPaintPropertyNode::in_subtree_of_page_scale| should be false for
+// the page scale transform node and all ancestors, and should be true for
+// descendants of the page scale transform node.
+TEST_P(VisualViewportTest, InSubtreeOfPageScale) {
+  InitializeWithAndroidSettings();
+  RegisterMockedHttpURLLoad("200-by-800-viewport.html");
+  NavigateTo(base_url_ + "200-by-800-viewport.html");
+
+  UpdateAllLifecyclePhases();
+
+  VisualViewport& visual_viewport = GetFrame()->GetPage()->GetVisualViewport();
+  const auto* page_scale = visual_viewport.GetPageScaleNode();
+  // The page scale is not in its own subtree.
+  EXPECT_FALSE(page_scale->IsInSubtreeOfPageScale());
+  // Ancestors of the page scale are not in the page scale's subtree.
+  for (const auto* ancestor = page_scale->Parent(); ancestor;
+       ancestor = ancestor->Parent()) {
+    EXPECT_FALSE(ancestor->IsInSubtreeOfPageScale());
+  }
+
+  const auto* view = GetFrame()->View()->GetLayoutView();
+  const auto& view_contents_transform =
+      view->FirstFragment().ContentsProperties().Transform();
+  // Descendants of the page scale node should have |IsInSubtreeOfPageScale|.
+  EXPECT_TRUE(view_contents_transform.IsInSubtreeOfPageScale());
+  for (const auto* ancestor = view_contents_transform.Parent();
+       ancestor != page_scale; ancestor = ancestor->Parent()) {
+    EXPECT_TRUE(ancestor->IsInSubtreeOfPageScale());
+  }
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.cc b/third_party/blink/renderer/core/html/html_plugin_element.cc
index 898774159..c22ce1a7 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.cc
+++ b/third_party/blink/renderer/core/html/html_plugin_element.cc
@@ -262,7 +262,8 @@
 void HTMLPlugInElement::AttachLayoutTree(AttachContext& context) {
   HTMLFrameOwnerElement::AttachLayoutTree(context);
 
-  if (!GetLayoutObject() || UseFallbackContent()) {
+  LayoutObject* layout_object = GetLayoutObject();
+  if (!layout_object || UseFallbackContent()) {
     // If we don't have a layoutObject we have to dispose of any plugins
     // which we persisted over a reattach.
     if (persisted_plugin_) {
@@ -272,23 +273,31 @@
     return;
   }
 
-  if (!IsImageType() && NeedsPluginUpdate() && GetLayoutEmbeddedObject() &&
-      !GetLayoutEmbeddedObject()->ShowsUnavailablePluginIndicator() &&
-      GetObjectContentType() != ObjectContentType::kPlugin &&
-      !is_delaying_load_event_) {
+  // This element may have been attached previously, and if we created a frame
+  // back then, re-use it now. We do not want to reload the frame if we don't
+  // have to, as that would cause us to lose any state changed after loading.
+  // Re-using the frame also matters if we have to re-attach for printing; we
+  // don't support reloading anything during printing (the frame would just show
+  // up blank then).
+  const Frame* content_frame = ContentFrame();
+  if (content_frame && !dispose_view_) {
+    SetEmbeddedContentView(content_frame->View());
+  } else if (!IsImageType() && NeedsPluginUpdate() &&
+             GetLayoutEmbeddedObject() &&
+             !GetLayoutEmbeddedObject()->ShowsUnavailablePluginIndicator() &&
+             GetObjectContentType() != ObjectContentType::kPlugin &&
+             !is_delaying_load_event_) {
     is_delaying_load_event_ = true;
     GetDocument().IncrementLoadEventDelayCount();
     GetDocument().LoadPluginsSoon();
   }
-  if (LayoutObject* layout_object = GetLayoutObject()) {
-    if (image_loader_ && layout_object->IsLayoutImage()) {
-      LayoutImageResource* image_resource =
-          ToLayoutImage(layout_object)->ImageResource();
-      image_resource->SetImageResource(image_loader_->GetContent());
-    }
-    if (!layout_object->IsFloatingOrOutOfFlowPositioned())
-      context.previous_in_flow = layout_object;
+  if (image_loader_ && layout_object->IsLayoutImage()) {
+    LayoutImageResource* image_resource =
+        ToLayoutImage(layout_object)->ImageResource();
+    image_resource->SetImageResource(image_loader_->GetContent());
   }
+  if (!layout_object->IsFloatingOrOutOfFlowPositioned())
+    context.previous_in_flow = layout_object;
 
   dispose_view_ = false;
 }
@@ -356,6 +365,13 @@
     SetEmbeddedContentView(nullptr);
   }
 
+  // We should attempt to use the same view afterwards, so that we don't lose
+  // state. But only if we're reattaching. Otherwise we need to throw it away,
+  // since there's no telling what's going to happen next, and it wouldn't be
+  // safe to keep it.
+  if (!context.performing_reattach)
+    dispose_view_ = true;
+
   ResetInstance();
 
   HTMLFrameOwnerElement::DetachLayoutTree(context);
diff --git a/third_party/blink/renderer/core/html/parser/xss_auditor.cc b/third_party/blink/renderer/core/html/parser/xss_auditor.cc
index 99e239e..dc66b5d 100644
--- a/third_party/blink/renderer/core/html/parser/xss_auditor.cc
+++ b/third_party/blink/renderer/core/html/parser/xss_auditor.cc
@@ -926,7 +926,13 @@
           StartsMultiLineCommentAt(string, found_position)) {
         break;
       }
-      if (!request.should_allow_cdata) {
+      if (request.should_allow_cdata) {
+        // Under SVG/XML rules, blink may apply an additional html entity
+        // decoding to this particular string before handing it to the JS
+        // parser. So stop before anything that looks like an entity.
+        if (string[found_position] == '&')
+          break;
+      } else {
         if (StartsHTMLOpenCommentAt(string, found_position) ||
             StartsHTMLCloseCommentAt(string, found_position)) {
           break;
diff --git a/third_party/blink/renderer/core/input/event_handler_test.cc b/third_party/blink/renderer/core/input/event_handler_test.cc
index 0ef358e..ce46db7 100644
--- a/third_party/blink/renderer/core/input/event_handler_test.cc
+++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -1708,4 +1708,65 @@
   EXPECT_GT(scrollable_area->ScrollOffsetInt().Height(), 0);
 }
 
+TEST_F(EventHandlerSimTest, DoNotScrollWithTouchpadIfOverflowIsHidden) {
+  WebView().MainFrameWidget()->Resize(WebSize(400, 400));
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    #outer {
+        width: 100vw;
+        height: 100vh;
+        overflow-x: hidden;
+        overflow-y: scroll;
+    }
+    #inner {
+        width: 300vw;
+        height: 300vh;
+    }
+    </style>
+    <body>
+      <div id='outer'>
+        <div id='inner'>
+      </div>
+    </body>
+  )HTML");
+  Compositor().BeginFrame();
+
+  WebGestureEvent scroll_begin_event(
+      WebInputEvent::kGestureScrollBegin, WebInputEvent::kNoModifiers,
+      WebInputEvent::GetStaticTimeStampForTests(),
+      blink::WebGestureDevice::kTouchpad);
+  scroll_begin_event.SetPositionInWidget(WebFloatPoint(10, 10));
+  scroll_begin_event.SetPositionInScreen(WebFloatPoint(10, 10));
+  scroll_begin_event.SetFrameScale(1);
+
+  WebGestureEvent scroll_update_event(
+      WebInputEvent::kGestureScrollUpdate, WebInputEvent::kNoModifiers,
+      WebInputEvent::GetStaticTimeStampForTests(),
+      blink::WebGestureDevice::kTouchpad);
+  scroll_update_event.data.scroll_update.delta_x = -100;
+  scroll_update_event.data.scroll_update.delta_y = -100;
+  scroll_update_event.SetPositionInWidget(WebFloatPoint(10, 10));
+  scroll_update_event.SetPositionInScreen(WebFloatPoint(10, 10));
+  scroll_update_event.SetFrameScale(1);
+
+  WebGestureEvent scroll_end_event(WebInputEvent::kGestureScrollEnd,
+                                   WebInputEvent::kNoModifiers,
+                                   WebInputEvent::GetStaticTimeStampForTests(),
+                                   blink::WebGestureDevice::kTouchpad);
+  scroll_end_event.SetPositionInWidget(WebFloatPoint(10, 10));
+  scroll_end_event.SetPositionInScreen(WebFloatPoint(10, 10));
+
+  WebView().MainFrameWidget()->HandleInputEvent(
+      WebCoalescedInputEvent(scroll_begin_event));
+  WebView().MainFrameWidget()->HandleInputEvent(
+      WebCoalescedInputEvent(scroll_update_event));
+  WebView().MainFrameWidget()->HandleInputEvent(
+      WebCoalescedInputEvent(scroll_end_event));
+
+  EXPECT_EQ(0, GetDocument().getElementById("outer")->scrollLeft());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index e6f881d..56dce472 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -242,8 +242,6 @@
         // recalculation.
         SetNeedsLayoutAndPrefWidthsRecalc(
             layout_invalidation_reason::kStyleChange);
-        if (RuntimeEnabledFeatures::LayoutNGEnabled())
-          MarkParentForOutOfFlowPositionedChange();
       } else {
         MarkContainerChainForLayout();
       }
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 01ac454..e80e58f9 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -1065,9 +1065,11 @@
   // inline items again to either collect or drop the OOF-positioned object.
   object->SetNeedsCollectInlines();
 
-  while (object && !object->IsLayoutBlock())
+  const LayoutBlock* containing_block = ContainingBlock();
+  while (object != containing_block) {
+    object->SetChildNeedsLayout(kMarkOnlyThis);
     object = object->Parent();
-
+  }
   // Finally mark the parent block for layout. This will mark everything which
   // has an OOF-positioned object in a NGLayoutResult as needing layout.
   if (object)
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc
index d05f2ce..18f9bd9c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc
@@ -787,7 +787,6 @@
 template <typename OffsetMappingBuilder>
 void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendFloating(
     LayoutObject* layout_object) {
-  changes_may_affect_earlier_lines_ = true;
   AppendOpaque(NGInlineItem::kFloating, kObjectReplacementCharacter,
                layout_object);
 }
@@ -795,7 +794,6 @@
 template <typename OffsetMappingBuilder>
 void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::
     AppendOutOfFlowPositioned(LayoutObject* layout_object) {
-  changes_may_affect_earlier_lines_ = true;
   AppendOpaque(NGInlineItem::kOutOfFlowPositioned, kObjectReplacementCharacter,
                layout_object);
 }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
index 4ad6699..004e658b 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
@@ -37,6 +37,11 @@
   line_box_type_ = NGPhysicalLineBoxFragment::kEmptyLineBox;
 }
 
+bool NGLineBoxFragmentBuilder::HasPropagatedDescendants() const {
+  return has_floating_descendants_ || !oof_positioned_descendants_.IsEmpty() ||
+         unpositioned_list_marker_;
+}
+
 NGLineBoxFragmentBuilder::Child*
 NGLineBoxFragmentBuilder::ChildList::FirstInFlowChild() {
   for (auto& child : *this) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
index 682939a8..5c989b57 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
@@ -52,6 +52,9 @@
   // Mark this line box is an "empty" line box. See NGLineBoxType.
   void SetIsEmptyLineBox();
 
+  // True if descendants were propagated to outside of this fragment.
+  bool HasPropagatedDescendants() const;
+
   const NGLineHeightMetrics& Metrics() const { return metrics_; }
   void SetMetrics(const NGLineHeightMetrics& metrics) { metrics_ = metrics; }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
index 1d798b35..a7343869 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
@@ -49,6 +49,7 @@
                                   builder->line_box_type_),
       metrics_(builder->metrics_) {
   style_ = std::move(builder->style_);
+  has_propagated_descendants_ = builder->HasPropagatedDescendants();
   base_direction_ = static_cast<unsigned>(builder->base_direction_);
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
index f5f8387..61d1229e 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
@@ -41,6 +41,8 @@
   }
   bool IsEmptyLineBox() const { return LineBoxType() == kEmptyLineBox; }
 
+  // True if descendants were propagated to outside of this fragment.
+  bool HasPropagatedDescendants() const { return has_propagated_descendants_; }
 
   const ComputedStyle& Style() const { return *style_; }
   const NGLineHeightMetrics& Metrics() const { return metrics_; }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc
index 26ca7c23..5316762 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc
@@ -16,7 +16,7 @@
   NGPhysicalLineBoxFragmentTest() : NGLayoutTest() {}
 
  protected:
-  const NGPhysicalLineBoxFragment* GetLineBox() const {
+  Vector<const NGPhysicalLineBoxFragment*> GetLineBoxes() const {
     const Element* container = GetElementById("root");
     DCHECK(container);
     const LayoutObject* layout_object = container->GetLayoutObject();
@@ -26,12 +26,21 @@
         To<LayoutBlockFlow>(layout_object)->CurrentFragment();
     DCHECK(root_fragment) << container;
 
+    Vector<const NGPhysicalLineBoxFragment*> lines;
     for (const auto& child :
          NGInlineFragmentTraversal::DescendantsOf(*root_fragment)) {
-      if (child.fragment->IsLineBox())
-        return To<NGPhysicalLineBoxFragment>(child.fragment.get());
+      if (const NGPhysicalLineBoxFragment* line =
+              DynamicTo<NGPhysicalLineBoxFragment>(child.fragment.get())) {
+        lines.push_back(line);
+      }
     }
-    NOTREACHED();
+    return lines;
+  }
+
+  const NGPhysicalLineBoxFragment* GetLineBox() const {
+    Vector<const NGPhysicalLineBoxFragment*> lines = GetLineBoxes();
+    if (!lines.IsEmpty())
+      return lines.front();
     return nullptr;
   }
 };
@@ -47,6 +56,42 @@
     EXPECT_EQ(GetElementById(id), fragment->GetNode()); \
   }
 
+TEST_F(NGPhysicalLineBoxFragmentTest, HasPropagatedDescendantsFloat) {
+  SetBodyInnerHTML(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    div {
+      font-size: 10px;
+      width: 10ch;
+    }
+    .float { float: left; }
+    </style>
+    <div id=root>12345678 12345<div class=float>float</div></div>
+  )HTML");
+  Vector<const NGPhysicalLineBoxFragment*> lines = GetLineBoxes();
+  EXPECT_EQ(lines.size(), 2u);
+  EXPECT_FALSE(lines[0]->HasPropagatedDescendants());
+  EXPECT_TRUE(lines[1]->HasPropagatedDescendants());
+}
+
+TEST_F(NGPhysicalLineBoxFragmentTest, HasPropagatedDescendantsOOF) {
+  SetBodyInnerHTML(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    div {
+      font-size: 10px;
+      width: 10ch;
+    }
+    .abspos { position: absolute; }
+    </style>
+    <div id=root>12345678 12345<div class=abspos>abspos</div></div>
+  )HTML");
+  Vector<const NGPhysicalLineBoxFragment*> lines = GetLineBoxes();
+  EXPECT_EQ(lines.size(), 2u);
+  EXPECT_FALSE(lines[0]->HasPropagatedDescendants());
+  EXPECT_TRUE(lines[1]->HasPropagatedDescendants());
+}
+
 TEST_F(NGPhysicalLineBoxFragmentTest, FirstLastLogicalLeafInSimpleText) {
   SetBodyInnerHTML(
       "<div id=root>"
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index d76ad196..e04392a 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -741,56 +741,43 @@
   DCHECK_EQ(direction, lineboxes->Style().Direction());
   const PhysicalSize outer_size = lineboxes->Size();
 
-  struct FragmentWithLogicalOffset {
-    const NGPhysicalFragment& fragment;
-    LogicalOffset offset;
-  };
-  // Avoid calling |ReserveInitialCapacity()| because
-  // |lineboxes->Children().size()| is O(n), not linear, but it's critical to
-  // have enough capacity.
-  Vector<FragmentWithLogicalOffset, 64> fragments;
+  LayoutUnit used_block_size = previous_inflow_position->logical_block_offset;
+  NGBreakToken* last_break_token = nullptr;
   for (const NGPaintFragment* child : lineboxes->Children()) {
     if (child->IsDirty())
       break;
 
-    // Abort if there are floats, oof, or list marker. They need re-layout.
-    const NGPhysicalFragment& child_fragment = child->PhysicalFragment();
-    if (!child_fragment.IsLineBox())
-      return nullptr;
+    // Abort if the line propagated its descendants to outside of the line. They
+    // are propagated through NGLayoutResult, which we don't cache.
+    const NGPhysicalLineBoxFragment* line =
+        DynamicTo<NGPhysicalLineBoxFragment>(&child->PhysicalFragment());
+    if (!line || line->HasPropagatedDescendants())
+      break;
 
+    // TODO(kojii): Running the normal layout code at least once for this child
+    // helps reducing the code to setup internal states after the reuse. Remove
+    // the last fragment if it is the end of the fragmentation to do so, but we
+    // should figure out how to setup the states without doing this.
+    NGBreakToken* break_token = line->BreakToken();
+    DCHECK(break_token);
+    if (break_token->IsFinished())
+      break;
+
+    last_break_token = break_token;
     LogicalOffset logical_offset = child->Offset().ConvertToLogical(
-        writing_mode, direction, outer_size, child_fragment.Size());
-    fragments.push_back(
-        FragmentWithLogicalOffset{child_fragment, logical_offset});
+        writing_mode, direction, outer_size, line->Size());
+    container_builder_.AddChild(
+        line, {logical_offset.inline_offset, used_block_size});
+    used_block_size += line->Size().ConvertToLogical(writing_mode).block_size;
   }
-  if (fragments.IsEmpty())
+  if (!last_break_token)
     return nullptr;
 
-  // TODO(kojii): Running the normal layout code at least once for this child
-  // helps reducing the code to setup internal states after the reuse. Remove
-  // the last fragment if it is the end of the fragmentation to do so, but we
-  // should figure out how to setup the states without doing this.
-  DCHECK(fragments.back().fragment.BreakToken());
-  if (fragments.back().fragment.BreakToken()->IsFinished()) {
-    fragments.Shrink(fragments.size() - 1);
-    if (fragments.IsEmpty())
-      return nullptr;
-  }
-
-  for (const auto& fragment : fragments) {
-    container_builder_.AddChild(&fragment.fragment, fragment.offset);
-  }
-
   // Update the internal states to after the re-used fragments.
-  const auto& last_fragment = fragments.back();
-  LayoutUnit used_block_size =
-      last_fragment.offset.block_offset +
-      last_fragment.fragment.Size().ConvertToLogical(writing_mode).block_size;
   previous_inflow_position->logical_block_offset = used_block_size;
 
   // In order to layout the rest of lines, return the break token from the last
   // reused line box.
-  NGBreakToken* last_break_token = last_fragment.fragment.BreakToken();
   DCHECK(last_break_token);
   DCHECK(!last_break_token->IsFinished());
   return To<NGInlineBreakToken>(last_break_token);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
index db9967f..37bee71 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
@@ -261,6 +261,7 @@
 
   // The following bitfield is only to be used by NGPhysicalLineBoxFragment
   // (it's defined here to save memory, since that class has no bitfields).
+  unsigned has_propagated_descendants_ : 1;
   unsigned base_direction_ : 1;  // TextDirection
 
   // The following bitfield is only to be used by NGPhysicalBoxFragment (it's
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 5d1a52a..0a312155 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -984,9 +984,24 @@
     }
   }
 
-  CommitSameDocumentNavigationInternal(url, frame_load_type, history_item,
-                                       client_redirect_policy, origin_document,
-                                       has_event, std::move(extra_data));
+  // If the requesting document is cross-origin, perform the navigation
+  // asynchronously to minimize the navigator's ability to execute timing
+  // attacks.
+  if (origin_document && !origin_document->GetSecurityOrigin()->CanAccess(
+                             frame_->GetDocument()->GetSecurityOrigin())) {
+    frame_->GetTaskRunner(TaskType::kInternalLoading)
+        ->PostTask(
+            FROM_HERE,
+            WTF::Bind(&DocumentLoader::CommitSameDocumentNavigationInternal,
+                      WrapWeakPersistent(this), url, frame_load_type,
+                      WrapPersistent(history_item), client_redirect_policy,
+                      WrapPersistent(origin_document), has_event,
+                      std::move(extra_data)));
+  } else {
+    CommitSameDocumentNavigationInternal(
+        url, frame_load_type, history_item, client_redirect_policy,
+        origin_document, has_event, std::move(extra_data));
+  }
   return mojom::CommitResult::Ok;
 }
 
@@ -998,6 +1013,11 @@
     Document* initiating_document,
     bool has_event,
     std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) {
+  // If this function was scheduled to run asynchronously, this DocumentLoader
+  // might have been detached before the task ran.
+  if (!frame_)
+    return;
+
   if (!IsBackForwardLoadType(frame_load_type)) {
     SetNavigationType(has_event ? kWebNavigationTypeLinkClicked
                                 : kWebNavigationTypeOther);
diff --git a/third_party/blink/renderer/core/loader/navigation_scheduler.cc b/third_party/blink/renderer/core/loader/navigation_scheduler.cc
index 51646494..2529329 100644
--- a/third_party/blink/renderer/core/loader/navigation_scheduler.cc
+++ b/third_party/blink/renderer/core/loader/navigation_scheduler.cc
@@ -250,23 +250,15 @@
     frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
 
   base::TimeTicks input_timestamp = InputTimestamp();
-  // If the URL we're going to navigate to is the same as the current one,
-  // except for the fragment part, we don't need to schedule the location
-  // change. We'll skip this optimization for cross-origin navigations to
-  // minimize the navigator's ability to execute timing attacks.
-  if (origin_document->GetSecurityOrigin()->CanAccess(
-          frame_->GetDocument()->GetSecurityOrigin())) {
-    if (url.HasFragmentIdentifier() &&
-        EqualIgnoringFragmentIdentifier(frame_->GetDocument()->Url(), url)) {
-      FrameLoadRequest request(origin_document, ResourceRequest(url), "_self");
-      request.SetInputStartTime(input_timestamp);
-      if (frame_load_type == WebFrameLoadType::kReplaceCurrentItem) {
-        request.SetClientRedirectReason(
-            ClientNavigationReason::kFrameNavigation);
-      }
-      frame_->Loader().StartNavigation(request, frame_load_type);
-      return;
+  if (url.HasFragmentIdentifier() &&
+      EqualIgnoringFragmentIdentifier(frame_->GetDocument()->Url(), url)) {
+    FrameLoadRequest request(origin_document, ResourceRequest(url), "_self");
+    request.SetInputStartTime(input_timestamp);
+    if (frame_load_type == WebFrameLoadType::kReplaceCurrentItem) {
+      request.SetClientRedirectReason(ClientNavigationReason::kFrameNavigation);
     }
+    frame_->Loader().StartNavigation(request, frame_load_type);
+    return;
   }
 
   Schedule(ScheduledFrameNavigation::Create(origin_document, url,
diff --git a/third_party/blink/renderer/core/page/spatial_navigation.cc b/third_party/blink/renderer/core/page/spatial_navigation.cc
index 3781848..71593f89 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation.cc
@@ -66,7 +66,7 @@
       return;
 
     visible_node = node;
-    rect_in_root_frame = NodeRectInRootFrame(node, true /* ignore border */);
+    rect_in_root_frame = NodeRectInRootFrame(node);
   }
 
   focusable_node = node;
@@ -377,13 +377,28 @@
   }
 }
 
-LayoutRect NodeRectInRootFrame(const Node* node, bool ignore_border) {
+LayoutRect NodeRectInRootFrame(const Node* node) {
   DCHECK(node);
   DCHECK(node->GetLayoutObject());
   DCHECK(!node->GetDocument().View()->NeedsLayout());
 
-  LayoutRect rect = node->GetDocument().GetFrame()->View()->ConvertToRootFrame(
-      node->BoundingBox());
+  LayoutObject* object = node->GetLayoutObject();
+
+  LayoutRect rect = LayoutRect(object->LocalBoundingBoxRectForAccessibility());
+
+  // Inset the bounding box by the border.
+  // TODO(bokan): As far as I can tell, this is to work around empty iframes
+  // that have a border. It's unclear if that's still useful.
+  rect.Move(node->GetLayoutObject()->Style()->BorderLeftWidth(),
+            node->GetLayoutObject()->Style()->BorderTopWidth());
+  rect.SetWidth(LayoutUnit(
+      rect.Width() - node->GetLayoutObject()->Style()->BorderLeftWidth() -
+      node->GetLayoutObject()->Style()->BorderRightWidth()));
+  rect.SetHeight(LayoutUnit(
+      rect.Height() - node->GetLayoutObject()->Style()->BorderTopWidth() -
+      node->GetLayoutObject()->Style()->BorderBottomWidth()));
+
+  object->MapToVisualRectInAncestorSpace(/*ancestor=*/nullptr, rect);
 
   // Ensure the rect isn't empty. This can happen in some cases as the bounding
   // box is made up of the corners of multiple child elements. If the first
@@ -391,19 +406,6 @@
   // be empty. Ensure its not empty so intersections with the root frame don't
   // lie about being off-screen.
   rect.UniteEvenIfEmpty(LayoutRect(rect.Location(), LayoutSize(1, 1)));
-
-  // For authors that use border instead of outline in their CSS, we compensate
-  // by ignoring the border when calculating the rect of the focused element.
-  if (ignore_border) {
-    rect.Move(node->GetLayoutObject()->Style()->BorderLeftWidth(),
-              node->GetLayoutObject()->Style()->BorderTopWidth());
-    rect.SetWidth(LayoutUnit(
-        rect.Width() - node->GetLayoutObject()->Style()->BorderLeftWidth() -
-        node->GetLayoutObject()->Style()->BorderRightWidth()));
-    rect.SetHeight(LayoutUnit(
-        rect.Height() - node->GetLayoutObject()->Style()->BorderTopWidth() -
-        node->GetLayoutObject()->Style()->BorderBottomWidth()));
-  }
   return rect;
 }
 
@@ -674,7 +676,7 @@
     if (area_element)
       return StartEdgeForAreaElement(*area_element, direction);
 
-    LayoutRect box_in_root_frame = NodeRectInRootFrame(focus_node, true);
+    LayoutRect box_in_root_frame = NodeRectInRootFrame(focus_node);
     return Intersection(box_in_root_frame, viewport_rect_of_root_frame);
   }
 
@@ -682,7 +684,7 @@
   while (container) {
     if (!IsOffscreen(container)) {
       // The first scroller that encloses focus and is [partially] visible.
-      LayoutRect box_in_root_frame = NodeRectInRootFrame(container, true);
+      LayoutRect box_in_root_frame = NodeRectInRootFrame(container);
       return OppositeEdge(direction, Intersection(box_in_root_frame,
                                                   viewport_rect_of_root_frame));
     }
diff --git a/third_party/blink/renderer/core/page/spatial_navigation.h b/third_party/blink/renderer/core/page/spatial_navigation.h
index ca7e4c8..5a6e2725 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation.h
+++ b/third_party/blink/renderer/core/page/spatial_navigation.h
@@ -85,8 +85,7 @@
 double ComputeDistanceDataForNode(SpatialNavigationDirection,
                                   const FocusCandidate& current_interest,
                                   const FocusCandidate& candidate);
-CORE_EXPORT LayoutRect NodeRectInRootFrame(const Node*,
-                                           bool ignore_border = false);
+CORE_EXPORT LayoutRect NodeRectInRootFrame(const Node*);
 CORE_EXPORT LayoutRect OppositeEdge(SpatialNavigationDirection side,
                                     const LayoutRect& box,
                                     LayoutUnit thickness = LayoutUnit());
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
index 4fc29b1d..53ae3fd1 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
@@ -82,8 +82,8 @@
        candidate.rect_in_root_frame.IsEmpty()))
     return;
 
-  // Ignore off-screen focusables that are not exposed after one "scroll step"
-  // in the direction.
+  // Ignore off-screen focusables, if there's nothing in the direction we'll
+  // scroll until they come on-screen.
   if (candidate.is_offscreen)
     return;
 
@@ -118,12 +118,14 @@
             .HitTestResultAtLocation(
                 location, HitTestRequest::kReadOnly | HitTestRequest::kActive |
                               HitTestRequest::kIgnoreClipping);
-    if (candidate.visible_node->contains(result.InnerNode())) {
+    if (candidate.visible_node->ContainsIncludingHostElements(
+            *result.InnerNode())) {
       *best_candidate = candidate;
       *best_distance = distance;
       return;
     }
-    if (best_candidate->visible_node->contains(result.InnerNode()))
+    if (best_candidate->visible_node->ContainsIncludingHostElements(
+            *result.InnerNode()))
       return;
   }
 
@@ -345,8 +347,7 @@
 
 Node* SpatialNavigationController::StartingNode() {
   if (RuntimeEnabledFeatures::FocuslessSpatialNavigationEnabled()) {
-    if (interest_element_ && interest_element_->isConnected() &&
-        interest_element_->GetDocument().GetFrame()) {
+    if (interest_element_ && IsValidCandidate(interest_element_)) {
       // If an iframe is interested, start the search from its document node.
       // This matches the behavior in the focus case below where focusing a
       // frame means the focused document doesn't have a focused element and so
@@ -453,7 +454,23 @@
 
 bool SpatialNavigationController::IsValidCandidate(
     const Element* element) const {
-  return element && element->IsKeyboardFocusable();
+  if (!element || !element->isConnected() || !element->GetLayoutObject())
+    return false;
+
+  LocalFrame* frame = element->GetDocument().GetFrame();
+  if (!frame)
+    return false;
+
+  // If the author installed a click handler on the main document or body, we
+  // almost certainly don't want to actually interest it. Doing so leads to
+  // issues since the document/body will likely contain most of the other
+  // content on the page.
+  if (frame->IsMainFrame()) {
+    if (IsHTMLHtmlElement(element) || IsHTMLBodyElement(element))
+      return false;
+  }
+
+  return element->IsKeyboardFocusable();
 }
 
 Element* SpatialNavigationController::GetFocusedElement() const {
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_test.cc b/third_party/blink/renderer/core/page/spatial_navigation_test.cc
index e5b817b1..e0716b1 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_test.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation_test.cc
@@ -198,7 +198,7 @@
 
   EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b,
                          SpatialNavigationDirection::kDown),
-            NodeRectInRootFrame(b, true));
+            NodeRectInRootFrame(b));
 }
 
 TEST_F(SpatialNavigationTest, StartAtVisibleFocusedScroller) {
@@ -220,7 +220,7 @@
   Element* scroller = GetDocument().getElementById("scroller");
   EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), scroller,
                          SpatialNavigationDirection::kDown),
-            NodeRectInRootFrame(scroller, true));
+            NodeRectInRootFrame(scroller));
 }
 
 TEST_F(SpatialNavigationTest, StartAtVisibleFocusedIframe) {
@@ -241,7 +241,7 @@
   Element* iframe = GetDocument().getElementById("iframe");
   EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), iframe,
                          SpatialNavigationDirection::kDown),
-            NodeRectInRootFrame(iframe, true));
+            NodeRectInRootFrame(iframe));
 }
 
 TEST_F(SpatialNavigationTest, StartAtTopWhenGoingDownwardsWithoutFocus) {
@@ -317,7 +317,7 @@
 
   Element* b = GetDocument().getElementById("b");
   const Element* container = GetDocument().getElementById("container");
-  const LayoutRect container_box = NodeRectInRootFrame(container, true);
+  const LayoutRect container_box = NodeRectInRootFrame(container);
 
   // TODO(crbug.com/889840):
   // VisibleBoundsInVisualViewport does not (yet) take div-clipping into
@@ -433,7 +433,7 @@
 
   EXPECT_FALSE(IsOffscreen(b));  // <button> is not completely offscreen.
 
-  LayoutRect button_in_root_frame = NodeRectInRootFrame(b, true);
+  LayoutRect button_in_root_frame = NodeRectInRootFrame(b);
 
   EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b,
                          SpatialNavigationDirection::kUp),
@@ -442,7 +442,7 @@
   // Do some scrolling.
   ScrollableArea* root_scroller = GetDocument().View()->GetScrollableArea();
   root_scroller->SetScrollOffset(ScrollOffset(0, 600), kProgrammaticScroll);
-  LayoutRect button_after_scroll = NodeRectInRootFrame(b, true);
+  LayoutRect button_after_scroll = NodeRectInRootFrame(b);
   ASSERT_NE(button_in_root_frame,
             button_after_scroll);  // As we scrolled, the
                                    // <button>'s position in
@@ -559,7 +559,7 @@
   EXPECT_TRUE(IsOffscreen(child_element));         // Completely offscreen.
   EXPECT_FALSE(IsOffscreen(enclosing_container));  // Partially visible.
 
-  LayoutRect iframe = NodeRectInRootFrame(enclosing_container, true);
+  LayoutRect iframe = NodeRectInRootFrame(enclosing_container);
 
   // When searching downwards we start at activeElement's
   // container's (here: the iframe's) topmost visible edge.
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index 2d52a40..51e1e02 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -181,7 +181,7 @@
     sequencer->AbortAnimations();
 
   ScrollResult result =
-      GetScrollAnimator().UserScroll(granularity, pixel_delta);
+      GetScrollAnimator().UserScroll(granularity, scrollable_axis_delta);
 
   // Delta that wasn't scrolled because the axis is !userInputScrollable
   // should count as unusedScrollDelta.
diff --git a/third_party/blink/renderer/devtools/front_end/devtools_compatibility.js b/third_party/blink/renderer/devtools/front_end/devtools_compatibility.js
index 0188e67..3607966d 100644
--- a/third_party/blink/renderer/devtools/front_end/devtools_compatibility.js
+++ b/third_party/blink/renderer/devtools/front_end/devtools_compatibility.js
@@ -550,6 +550,14 @@
 
     /**
      * @override
+     * @param {string} umaName
+     */
+    recordUserMetricsAction(umaName) {
+      DevToolsAPI.sendMessageToEmbedder('recordUserMetricsAction', [umaName], null);
+    }
+
+    /**
+     * @override
      */
     requestFileSystems() {
       DevToolsAPI.sendMessageToEmbedder('requestFileSystems', [], null);
diff --git a/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js b/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js
index f760893..bcd0d2c 100644
--- a/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js
+++ b/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js
@@ -230,6 +230,13 @@
 
   /**
    * @override
+   * @param {string} umaName
+   */
+  recordUserMetricsAction(umaName) {
+  }
+
+  /**
+   * @override
    */
   requestFileSystems() {
     this.events.dispatchEventToListeners(InspectorFrontendHostAPI.Events.FileSystemsLoaded, []);
diff --git a/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHostAPI.js b/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHostAPI.js
index b8955b27..6bbe4f6 100644
--- a/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHostAPI.js
+++ b/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHostAPI.js
@@ -247,6 +247,11 @@
   recordPerformanceHistogram(histogramName, duration) {},
 
   /**
+   * @param {string} umaName
+   */
+  recordUserMetricsAction(umaName) {},
+
+  /**
    * @param {string} message
    */
   sendMessageToBackend(message) {},
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkPanel.js b/third_party/blink/renderer/devtools/front_end/network/NetworkPanel.js
index 841e4da..6bbf1a8 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkPanel.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkPanel.js
@@ -52,10 +52,20 @@
 
     const panel = new UI.VBox();
 
-    this._panelToolbar = new UI.Toolbar('', panel.contentElement);
+    const networkToolbarContainer = panel.contentElement.createChild('div', 'network-toolbar-container');
+    this._panelToolbar = new UI.Toolbar('', networkToolbarContainer);
+    this._rightToolbar = new UI.Toolbar('', networkToolbarContainer);
+
     this._filterBar = new UI.FilterBar('networkPanel', true);
     this._filterBar.show(panel.contentElement);
 
+    this._settingsPane = new UI.HBox();
+    this._settingsPane.element.classList.add('network-settings-pane');
+    this._settingsPane.show(panel.contentElement);
+    this._showSettingsPaneSetting = Common.settings.createSetting('networkShowSettingsToolbar', false);
+    this._showSettingsPaneSetting.addChangeListener(this._updateSettingsPaneVisibility.bind(this));
+    this._updateSettingsPaneVisibility();
+
     this._filmStripPlaceholderElement = panel.contentElement.createChild('div', 'network-film-strip-placeholder');
 
     // Create top overview component.
@@ -122,7 +132,6 @@
 
     this._preserveLogSetting = Common.moduleSetting('network_log.preserve-log');
 
-    this._offlineCheckbox = MobileThrottling.throttlingManager().createOfflineToolbarCheckbox();
     this._throttlingSelect = this._createThrottlingConditionsSelect();
     this._setupToolbarButtons(splitWidget);
 
@@ -161,13 +170,6 @@
   }
 
   /**
-   * @return {!UI.ToolbarCheckbox}
-   */
-  offlineCheckboxForTest() {
-    return this._offlineCheckbox;
-  }
-
-  /**
    * @return {!UI.ToolbarComboBox}
    */
   throttlingSelectForTest() {
@@ -193,9 +195,6 @@
     clearButton.addEventListener(UI.ToolbarButton.Events.Click, () => SDK.networkLog.reset(), this);
     this._panelToolbar.appendToolbarItem(clearButton);
     this._panelToolbar.appendSeparator();
-    const recordFilmStripButton = new UI.ToolbarSettingToggle(
-        this._networkRecordFilmStripSetting, 'largeicon-camera', Common.UIString('Capture screenshots'));
-    this._panelToolbar.appendToolbarItem(recordFilmStripButton);
 
     this._panelToolbar.appendToolbarItem(this._filterBar.filterButton());
     updateSidebarToggle();
@@ -209,22 +208,6 @@
     this._panelToolbar.appendToolbarItem(searchToggle);
     this._panelToolbar.appendSeparator();
 
-    this._panelToolbar.appendText(Common.UIString('View:'));
-
-    const largerRequestsButton = new UI.ToolbarSettingToggle(
-        this._networkLogLargeRowsSetting, 'largeicon-large-list', Common.UIString('Use large request rows'),
-        Common.UIString('Use small request rows'));
-    this._panelToolbar.appendToolbarItem(largerRequestsButton);
-
-    const showOverviewButton = new UI.ToolbarSettingToggle(
-        this._networkLogShowOverviewSetting, 'largeicon-waterfall', Common.UIString('Show overview'),
-        Common.UIString('Hide overview'));
-    this._panelToolbar.appendToolbarItem(showOverviewButton);
-
-    this._panelToolbar.appendToolbarItem(new UI.ToolbarSettingCheckbox(
-        Common.moduleSetting('network.group-by-frame'), '', Common.UIString('Group by frame')));
-
-    this._panelToolbar.appendSeparator();
     this._panelToolbar.appendToolbarItem(new UI.ToolbarSettingCheckbox(
         this._preserveLogSetting, Common.UIString('Do not clear log on page reload / navigation'),
         Common.UIString('Preserve log')));
@@ -235,10 +218,30 @@
     this._panelToolbar.appendToolbarItem(disableCacheCheckbox);
 
     this._panelToolbar.appendSeparator();
-    this._panelToolbar.appendToolbarItem(this._offlineCheckbox);
     this._panelToolbar.appendToolbarItem(this._throttlingSelect);
 
-    this._panelToolbar.appendToolbarItem(new UI.ToolbarItem(this._progressBarContainer));
+    this._rightToolbar.appendToolbarItem(new UI.ToolbarItem(this._progressBarContainer));
+    this._rightToolbar.appendSeparator();
+    this._rightToolbar.appendToolbarItem(
+        new UI.ToolbarSettingToggle(this._showSettingsPaneSetting, 'largeicon-settings-gear', ls`Network settings`));
+
+    const settingsToolbarLeft = new UI.Toolbar('', this._settingsPane.element);
+    settingsToolbarLeft.makeVertical();
+    settingsToolbarLeft.appendToolbarItem(
+        new UI.ToolbarSettingCheckbox(this._networkLogLargeRowsSetting, '', ls`Use large request rows`));
+    settingsToolbarLeft.appendToolbarItem(
+        new UI.ToolbarSettingCheckbox(this._networkLogShowOverviewSetting, '', ls`Show overview`));
+
+    const settingsToolbarRight = new UI.Toolbar('', this._settingsPane.element);
+    settingsToolbarRight.makeVertical();
+    settingsToolbarRight.appendToolbarItem(
+        new UI.ToolbarSettingCheckbox(Common.moduleSetting('network.group-by-frame'), '', ls`Group by frame`));
+    settingsToolbarRight.appendToolbarItem(
+        new UI.ToolbarSettingCheckbox(this._networkRecordFilmStripSetting, '', ls`Capture screenshots`));
+  }
+
+  _updateSettingsPaneVisibility() {
+    this._settingsPane.element.classList.toggle('hidden', !this._showSettingsPaneSetting.get());
   }
 
   /**
diff --git a/third_party/blink/renderer/devtools/front_end/network/networkPanel.css b/third_party/blink/renderer/devtools/front_end/network/networkPanel.css
index 7e31424..6d3a652 100644
--- a/third_party/blink/renderer/devtools/front_end/network/networkPanel.css
+++ b/third_party/blink/renderer/devtools/front_end/network/networkPanel.css
@@ -27,7 +27,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-.panel.network .toolbar {
+ .panel.network .toolbar {
     background-color: var(--toolbar-bg-color);
     border-bottom: var(--divider-border);
 }
@@ -146,3 +146,21 @@
 .network-tabbed-pane {
     background-color: var(--toolbar-bg-color);
 }
+
+.network-settings-pane {
+    flex: none;
+    background-color: var(--toolbar-bg-color);
+}
+
+.network-settings-pane .toolbar {
+    flex: 1 1;
+}
+
+.network-toolbar-container {
+    display: flex;
+    flex: none;
+}
+
+.network-toolbar-container > :first-child {
+    flex: 1 1 auto;
+}
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js b/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
index 4bdd3006..8c828ef 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
@@ -229,7 +229,7 @@
 
 /** @type {!SDK.NetworkManager.Conditions} */
 SDK.NetworkManager.NoThrottlingConditions = {
-  title: Common.UIString('No throttling'),
+  title: ls`Online`,
   download: -1,
   upload: -1,
   latency: 0
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS b/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS
index 49f3612..0a78e513 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS
@@ -1,6 +1,7 @@
-emircan@chromium.org
+guidou@chromium.org
 
 # Original (legacy) owner.
+emircan@chromium.org
 mcasas@chromium.org
 
 # TEAM: webrtc-dev@chromium.org
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track.idl b/third_party/blink/renderer/modules/mediastream/media_stream_track.idl
index d46f06b..01bf192 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track.idl
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track.idl
@@ -25,11 +25,8 @@
 
 // https://w3c.github.io/mediacapture-main/#mediastreamtrack
 
-// TODO(foolip): Remove or standardize MediaStreamTrackState "muted".
-// https://crbug.com/651414
 enum MediaStreamTrackState {
     "live",
-    "muted",
     "ended"
 };
 
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc b/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc
index 5b72b875..6255842 100644
--- a/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc
+++ b/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc
@@ -186,10 +186,15 @@
   biquad_.Process(source, destination, frames_to_process);
 }
 
-void BiquadDSPKernel::GetFrequencyResponse(int n_frequencies,
+void BiquadDSPKernel::GetFrequencyResponse(BiquadDSPKernel& kernel,
+                                           int n_frequencies,
                                            const float* frequency_hz,
                                            float* mag_response,
                                            float* phase_response) {
+  // Only allow on the main thread because we don't want the audio thread to be
+  // updating |kernel| while we're computing the response.
+  DCHECK(IsMainThread());
+
   bool is_good =
       n_frequencies > 0 && frequency_hz && mag_response && phase_response;
   DCHECK(is_good);
@@ -197,44 +202,15 @@
     return;
 
   Vector<float> frequency(n_frequencies);
-
-  double nyquist = this->Nyquist();
+  double nyquist = kernel.Nyquist();
 
   // Convert from frequency in Hz to normalized frequency (0 -> 1),
   // with 1 equal to the Nyquist frequency.
   for (int k = 0; k < n_frequencies; ++k)
     frequency[k] = frequency_hz[k] / nyquist;
 
-  float cutoff_frequency;
-  float q;
-  float gain;
-  float detune;  // in Cents
-
-  {
-    // Get a copy of the current biquad filter coefficients so we can update the
-    // biquad with these values. We need to synchronize with process() to
-    // prevent process() from updating the filter coefficients while we're
-    // trying to access them. The process will update it next time around.
-    //
-    // The BiquadDSPKernel object here (along with it's Biquad object) is for
-    // querying the frequency response and is NOT the same as the one in
-    // process() which is used for performing the actual filtering. This one is
-    // is created in BiquadProcessor::getFrequencyResponse for this purpose.
-    // Both, however, point to the same BiquadProcessor object.
-    //
-    // FIXME: Simplify this: crbug.com/390266
-    MutexLocker process_locker(process_lock_);
-
-    cutoff_frequency = GetBiquadProcessor()->Parameter1().Value();
-    q = GetBiquadProcessor()->Parameter2().Value();
-    gain = GetBiquadProcessor()->Parameter3().Value();
-    detune = GetBiquadProcessor()->Parameter4().Value();
-  }
-
-  UpdateCoefficients(1, &cutoff_frequency, &q, &gain, &detune);
-
-  biquad_.GetFrequencyResponse(n_frequencies, frequency.data(), mag_response,
-                               phase_response);
+  kernel.biquad_.GetFrequencyResponse(n_frequencies, frequency.data(),
+                                      mag_response, phase_response);
 }
 
 bool BiquadDSPKernel::RequiresTailProcessing() const {
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.h b/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.h
index c8b124a0..7bf9fff2 100644
--- a/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.h
+++ b/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.h
@@ -49,16 +49,24 @@
                uint32_t frames_to_process) override;
   void Reset() override { biquad_.Reset(); }
 
-  // Get the magnitude and phase response of the filter at the given
-  // set of frequencies (in Hz). The phase response is in radians.
-  void GetFrequencyResponse(int n_frequencies,
-                            const float* frequency_hz,
-                            float* mag_response,
-                            float* phase_response);
+  // Get the magnitude and phase response of the given BiquadDSPKernel at the
+  // given set of frequencies (in Hz). The phase response is in radians.  This
+  // must be called from the main thread.
+  static void GetFrequencyResponse(BiquadDSPKernel& kernel,
+                                   int n_frequencies,
+                                   const float* frequency_hz,
+                                   float* mag_response,
+                                   float* phase_response);
 
   bool RequiresTailProcessing() const final;
   double TailTime() const override;
   double LatencyTime() const override;
+  // Update the biquad cofficients with the given parameters
+  void UpdateCoefficients(int number_of_frames,
+                          const float* frequency,
+                          const float* q,
+                          const float* gain,
+                          const float* detune);
 
  protected:
   Biquad biquad_;
@@ -69,12 +77,6 @@
   // To prevent audio glitches when parameters are changed,
   // dezippering is used to slowly change the parameters.
   void UpdateCoefficientsIfNecessary(int);
-  // Update the biquad cofficients with the given parameters
-  void UpdateCoefficients(int,
-                          const float* frequency,
-                          const float* q,
-                          const float* gain,
-                          const float* detune);
 
  private:
   // Compute the tail time using the BiquadFilter coefficients at
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_processor.cc b/third_party/blink/renderer/modules/webaudio/biquad_processor.cc
index 9bc6a40..59eb72d9 100644
--- a/third_party/blink/renderer/modules/webaudio/biquad_processor.cc
+++ b/third_party/blink/renderer/modules/webaudio/biquad_processor.cc
@@ -150,14 +150,39 @@
                                            const float* frequency_hz,
                                            float* mag_response,
                                            float* phase_response) {
+  DCHECK(IsMainThread());
+
   // Compute the frequency response on a separate temporary kernel
   // to avoid interfering with the processing running in the audio
   // thread on the main kernels.
 
   std::unique_ptr<BiquadDSPKernel> response_kernel =
       std::make_unique<BiquadDSPKernel>(this);
-  response_kernel->GetFrequencyResponse(n_frequencies, frequency_hz,
-                                        mag_response, phase_response);
+
+  float cutoff_frequency;
+  float q;
+  float gain;
+  float detune;  // in Cents
+
+  {
+    // Get a copy of the current biquad filter coefficients so we can update
+    // |response_kernel| with these values.  We need to synchronize with
+    // |Process()| to prevent process() from updating the filter coefficients
+    // while we're trying to access them.  Since this is on the main thread, we
+    // can wait.  The audio thread will update the coefficients the next time
+    // around, it it were blocked.
+    MutexLocker process_locker(process_lock_);
+
+    cutoff_frequency = Parameter1().Value();
+    q = Parameter2().Value();
+    gain = Parameter3().Value();
+    detune = Parameter4().Value();
+  }
+
+  response_kernel->UpdateCoefficients(1, &cutoff_frequency, &q, &gain, &detune);
+  BiquadDSPKernel::GetFrequencyResponse(*response_kernel, n_frequencies,
+                                        frequency_hz, mag_response,
+                                        phase_response);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/font_cache.cc b/third_party/blink/renderer/platform/fonts/font_cache.cc
index a9740b4..cb3d4b0 100644
--- a/third_party/blink/renderer/platform/fonts/font_cache.cc
+++ b/third_party/blink/renderer/platform/fonts/font_cache.cc
@@ -127,9 +127,6 @@
   // Remove the font size from the cache key, and handle the font size
   // separately in the inner HashMap. So that different size of FontPlatformData
   // can share underlying SkTypeface.
-  if (RuntimeEnabledFeatures::FontCacheScalingEnabled())
-    key.ClearFontSize();
-
   FontPlatformData* result;
   bool found_result;
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index 03f226d6..565ffcf 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -3398,6 +3398,7 @@
   TransformationMatrix matrix;
   matrix.Scale(2);
   TransformPaintPropertyNode::State transform_state{matrix};
+  transform_state.in_subtree_of_page_scale = false;
   transform_state.compositor_element_id =
       CompositorElementIdFromUniqueObjectId(1);
 
@@ -3420,6 +3421,58 @@
   EXPECT_TRUE(cc_transform_node->pre_local.IsIdentity());
 }
 
+// Test that |cc::TransformNode::in_subtree_of_page_scale_layer| is not set on
+// the page scale transform node or ancestors, and is set on descendants.
+TEST_P(PaintArtifactCompositorTest, InSubtreeOfPageScale) {
+  TransformPaintPropertyNode::State ancestor_transform_state;
+  ancestor_transform_state.in_subtree_of_page_scale = false;
+  auto ancestor_transform = TransformPaintPropertyNode::Create(
+      TransformPaintPropertyNode::Root(), std::move(ancestor_transform_state));
+
+  TransformPaintPropertyNode::State page_scale_transform_state;
+  page_scale_transform_state.in_subtree_of_page_scale = false;
+  page_scale_transform_state.compositor_element_id =
+      CompositorElementIdFromUniqueObjectId(1);
+  auto page_scale_transform = TransformPaintPropertyNode::Create(
+      *ancestor_transform, std::move(page_scale_transform_state));
+
+  TransformPaintPropertyNode::State descendant_transform_state;
+  descendant_transform_state.compositor_element_id =
+      CompositorElementIdFromUniqueObjectId(2);
+  descendant_transform_state.in_subtree_of_page_scale = true;
+  descendant_transform_state.direct_compositing_reasons =
+      CompositingReason::kWillChangeTransform;
+  auto descendant_transform = TransformPaintPropertyNode::Create(
+      *page_scale_transform, std::move(descendant_transform_state));
+
+  TestPaintArtifact artifact;
+  artifact.Chunk(*descendant_transform, c0(), e0())
+      .RectDrawing(FloatRect(0, 0, 10, 10), Color::kBlack);
+  ViewportProperties viewport_properties;
+  viewport_properties.page_scale = page_scale_transform.get();
+  CompositorElementIdSet element_ids;
+  Update(artifact.Build(), element_ids, viewport_properties);
+
+  cc::TransformTree& transform_tree = GetPropertyTrees().transform_tree;
+  const auto* cc_page_scale_transform = transform_tree.FindNodeFromElementId(
+      page_scale_transform_state.compositor_element_id);
+  // The page scale node is not in a subtree of the page scale layer.
+  EXPECT_FALSE(cc_page_scale_transform->in_subtree_of_page_scale_layer);
+
+  // Ancestors of the page scale node are not in a page scale subtree.
+  auto cc_ancestor_id = cc_page_scale_transform->parent_id;
+  while (cc_ancestor_id != cc::TransformTree::kInvalidNodeId) {
+    const auto* ancestor = transform_tree.Node(cc_ancestor_id);
+    EXPECT_FALSE(ancestor->in_subtree_of_page_scale_layer);
+    cc_ancestor_id = ancestor->parent_id;
+  }
+
+  // Descendants of the page scale node should be in the page scale subtree.
+  const auto* cc_descendant_transform = transform_tree.FindNodeFromElementId(
+      descendant_transform_state.compositor_element_id);
+  EXPECT_TRUE(cc_descendant_transform->in_subtree_of_page_scale_layer);
+}
+
 enum {
   kNoRenderSurface = 0,
   kHasRenderSurface = 1 << 0,
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index ccefc5e..912ea0b 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -365,6 +365,9 @@
     GetTransformTree().AddNodeAffectedByOuterViewportBoundsDelta(id);
   }
 
+  compositor_node.in_subtree_of_page_scale_layer =
+      transform_node.IsInSubtreeOfPageScale();
+
   if (const auto* sticky_constraint = transform_node.GetStickyConstraint()) {
     DCHECK(sticky_constraint->is_sticky);
     cc::StickyPositionNodeData* sticky_data =
@@ -467,6 +470,7 @@
 
 int PropertyTreeManager::EnsureCompositorPageScaleTransformNode(
     const TransformPaintPropertyNode& node) {
+  DCHECK(!node.IsInSubtreeOfPageScale());
   int id = EnsureCompositorTransformNode(node);
   DCHECK(GetTransformTree().Node(id));
   cc::TransformNode& compositor_node = *GetTransformTree().Node(id);
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
index 7229c9e..5d0233c2 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
@@ -1,12 +1,32 @@
 #include "third_party/blink/renderer/platform/graphics/dark_mode_filter.h"
 
+#include "base/optional.h"
+#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
 #include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/effects/SkHighContrastFilter.h"
 #include "third_party/skia/include/effects/SkTableColorFilter.h"
 
 namespace blink {
 
-DarkModeFilter::DarkModeFilter() : default_filter_(nullptr) {
+namespace {
+
+bool ShouldApplyToImage(const DarkModeSettings& settings,
+                        const FloatRect& src_rect,
+                        Image* image) {
+  switch (settings.image_policy) {
+    case DarkModeImagePolicy::kFilterSmart:
+      return image->ShouldApplyDarkModeFilter(src_rect);
+    case DarkModeImagePolicy::kFilterAll:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace
+
+DarkModeFilter::DarkModeFilter()
+    : default_filter_(nullptr), image_filter_(nullptr) {
   settings_.mode = DarkMode::kOff;
   settings_.image_policy = DarkModeImagePolicy::kFilterNone;
 }
@@ -18,6 +38,7 @@
   switch (settings_.mode) {
     case DarkMode::kOff:
       default_filter_.reset(nullptr);
+      image_filter_.reset(nullptr);
       return;
     case DarkMode::kSimpleInvertForTesting: {
       uint8_t identity[256], invert[256];
@@ -27,6 +48,7 @@
       }
       default_filter_ =
           SkTableColorFilter::MakeARGB(identity, invert, invert, invert);
+      image_filter_.reset(nullptr);
       return;
     }
     case DarkMode::kInvertBrightness:
@@ -41,32 +63,49 @@
   config.fGrayscale = settings_.grayscale;
   config.fContrast = settings_.contrast;
   default_filter_ = SkHighContrastFilter::Make(config);
-}
 
-sk_sp<SkColorFilter> DarkModeFilter::GetColorFilter() {
-  return default_filter_;
-}
-
-bool DarkModeFilter::ShouldApplyToImage(Image& image,
-                                        const FloatRect& src_rect) {
-  if (!GetColorFilter())
-    return false;
-
-  switch (settings_.image_policy) {
-    case DarkModeImagePolicy::kFilterSmart:
-      return image.ShouldApplyDarkModeFilter(src_rect);
-    case DarkModeImagePolicy::kFilterAll:
-      return true;
-    default:
-      return false;
+  if (settings_.image_style == DarkModeImageStyle::kGrayscale) {
+    config.fGrayscale = true;
+    image_filter_ = SkHighContrastFilter::Make(config);
+  } else {
+    image_filter_.reset(nullptr);
   }
 }
 
-Color DarkModeFilter::Apply(const Color& color) {
-  sk_sp<SkColorFilter> filter = GetColorFilter();
-  if (!filter)
+Color DarkModeFilter::ApplyIfNeeded(const Color& color) {
+  if (!default_filter_)
     return color;
-  return Color(filter->filterColor(color.Rgb()));
+  return Color(default_filter_->filterColor(color.Rgb()));
+}
+
+// TODO(gilmanmh): Investigate making |image| a const reference. This code
+// relies on Image::ShouldApplyDarkModeFilter(), which is not const. If it could
+// be made const, then |image| could also be const.
+void DarkModeFilter::ApplyToImageFlagsIfNeeded(const FloatRect& src_rect,
+                                               Image* image,
+                                               cc::PaintFlags* flags) {
+  sk_sp<SkColorFilter> filter = image_filter_;
+  if (!filter)
+    filter = default_filter_;
+
+  if (!filter || !ShouldApplyToImage(settings(), src_rect, image))
+    return;
+  flags->setColorFilter(std::move(filter));
+}
+
+base::Optional<cc::PaintFlags> DarkModeFilter::ApplyToFlagsIfNeeded(
+    const cc::PaintFlags& flags) {
+  if (!default_filter_)
+    return base::nullopt;
+
+  cc::PaintFlags dark_mode_flags = flags;
+  if (flags.HasShader()) {
+    dark_mode_flags.setColorFilter(default_filter_);
+  } else {
+    dark_mode_flags.setColor(default_filter_->filterColor(flags.getColor()));
+  }
+
+  return base::make_optional<cc::PaintFlags>(std::move(dark_mode_flags));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.h b/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
index 6052af1..3f8a1fae 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
@@ -1,6 +1,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_FILTER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_FILTER_H_
 
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
@@ -18,15 +19,22 @@
   const DarkModeSettings& settings() const { return settings_; }
   void UpdateSettings(const DarkModeSettings& new_settings);
 
-  sk_sp<SkColorFilter> GetColorFilter();
+  Color ApplyIfNeeded(const Color& color);
 
-  bool ShouldApplyToImage(Image& image, const FloatRect& src_rect);
+  // |image| and |flags| must not be null.
+  void ApplyToImageFlagsIfNeeded(const FloatRect& src_rect,
+                                 Image* image,
+                                 cc::PaintFlags* flags);
 
-  Color Apply(const Color& color);
+  // |flags| must not be null.
+  base::Optional<cc::PaintFlags> ApplyToFlagsIfNeeded(
+      const cc::PaintFlags& flags);
 
  private:
   DarkModeSettings settings_;
+
   sk_sp<SkColorFilter> default_filter_;
+  sk_sp<SkColorFilter> image_filter_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_settings.h b/third_party/blink/renderer/platform/graphics/dark_mode_settings.h
index 42f67d8..21d1d9e2 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_settings.h
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_settings.h
@@ -25,6 +25,14 @@
   kFilterSmart,
 };
 
+// For images that should have a filter applied, which filter should be used?
+enum class DarkModeImageStyle {
+  // Invert images the same way as other elements
+  kDefault,
+  // Apply grayscale to images as well as inverting them
+  kGrayscale
+};
+
 enum class DarkModePagePolicy {
   // Apply dark-mode filter to all frames, regardless of content.
   kFilterAll,
@@ -37,6 +45,7 @@
   bool grayscale = false;
   float contrast = 0.0;  // Valid range from -1.0 to 1.0
   DarkModeImagePolicy image_policy = DarkModeImagePolicy::kFilterAll;
+  DarkModeImageStyle image_style = DarkModeImageStyle::kDefault;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index 340fbf38..efe0bd83 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -59,25 +59,23 @@
 
 namespace blink {
 
+// Effectively allows modifying the provided |flags| without technically
+// violating its constness.
+//
+// TODO(gilmanmh): Investigate removing const from |flags| in the calling
+// methods so that this isn't necessary.
 class GraphicsContext::DarkModeFlags final {
   STACK_ALLOCATED();
 
  public:
   // This helper's lifetime should never exceed |flags|'.
   DarkModeFlags(GraphicsContext* gc, const PaintFlags& flags) {
-    sk_sp<SkColorFilter> filter = gc->dark_mode_filter_.GetColorFilter();
-    if (!filter) {
-      flags_ = &flags;
-    } else {
-      dark_mode_flags_ = flags;
-      if (flags.HasShader()) {
-        dark_mode_flags_->setColorFilter(filter);
-      } else {
-        dark_mode_flags_->setColor(filter->filterColor(flags.getColor()));
-      }
-
+    dark_mode_flags_ = gc->dark_mode_filter_.ApplyToFlagsIfNeeded(flags);
+    if (dark_mode_flags_) {
       flags_ = &dark_mode_flags_.value();
+      return;
     }
+    flags_ = &flags;
   }
 
   operator const PaintFlags&() const { return *flags_; }
@@ -387,15 +385,15 @@
 void GraphicsContext::DrawFocusRingPath(const SkPath& path,
                                         const Color& color,
                                         float width) {
-  DrawPlatformFocusRing(path, canvas_, dark_mode_filter_.Apply(color).Rgb(),
-                        width);
+  DrawPlatformFocusRing(path, canvas_,
+                        dark_mode_filter_.ApplyIfNeeded(color).Rgb(), width);
 }
 
 void GraphicsContext::DrawFocusRingRect(const SkRect& rect,
                                         const Color& color,
                                         float width) {
-  DrawPlatformFocusRing(rect, canvas_, dark_mode_filter_.Apply(color).Rgb(),
-                        width);
+  DrawPlatformFocusRing(rect, canvas_,
+                        dark_mode_filter_.ApplyIfNeeded(color).Rgb(), width);
 }
 
 void GraphicsContext::DrawFocusRing(const Path& focus_ring_path,
@@ -470,7 +468,7 @@
   if (ContextDisabled())
     return;
 
-  Color shadow_color = dark_mode_filter_.Apply(orig_shadow_color);
+  Color shadow_color = dark_mode_filter_.ApplyIfNeeded(orig_shadow_color);
 
   FloatRect hole_rect(rect.Rect());
   hole_rect.Inflate(-shadow_spread);
@@ -873,9 +871,8 @@
   image_flags.setBlendMode(op);
   image_flags.setColor(SK_ColorBLACK);
   image_flags.setFilterQuality(ComputeFilterQuality(image, dest, src));
-  if (dark_mode_filter_.ShouldApplyToImage(*image, src)) {
-    image_flags.setColorFilter(dark_mode_filter_.GetColorFilter());
-  }
+
+  dark_mode_filter_.ApplyToImageFlagsIfNeeded(src, image, &image_flags);
 
   image->Draw(canvas_, image_flags, dest, src, should_respect_image_orientation,
               Image::kClampImageToSourceRect, decode_mode);
@@ -910,9 +907,8 @@
   image_flags.setColor(SK_ColorBLACK);
   image_flags.setFilterQuality(
       ComputeFilterQuality(image, dest.Rect(), src_rect));
-  if (dark_mode_filter_.ShouldApplyToImage(*image, src_rect)) {
-    image_flags.setColorFilter(dark_mode_filter_.GetColorFilter());
-  }
+
+  dark_mode_filter_.ApplyToImageFlagsIfNeeded(src_rect, image, &image_flags);
 
   bool use_shader = (visible_src == src_rect) &&
                     (respect_orientation == kDoNotRespectImageOrientation);
@@ -1122,7 +1118,7 @@
       canvas_->drawDRRect(outer, inner, ImmutableState()->FillFlags());
     } else {
       PaintFlags flags(ImmutableState()->FillFlags());
-      flags.setColor(dark_mode_filter_.Apply(color).Rgb());
+      flags.setColor(dark_mode_filter_.ApplyIfNeeded(color).Rgb());
       canvas_->drawDRRect(outer, inner, flags);
     }
 
@@ -1135,7 +1131,7 @@
   stroke_r_rect.inset(stroke_width / 2, stroke_width / 2);
 
   PaintFlags stroke_flags(ImmutableState()->FillFlags());
-  stroke_flags.setColor(dark_mode_filter_.Apply(color).Rgb());
+  stroke_flags.setColor(dark_mode_filter_.ApplyIfNeeded(color).Rgb());
   stroke_flags.setStyle(PaintFlags::kStroke_Style);
   stroke_flags.setStrokeWidth(stroke_width);
 
@@ -1330,7 +1326,7 @@
     return;
 
   PaintFlags flags(ImmutableState()->FillFlags());
-  flags.setColor(dark_mode_filter_.Apply(color).Rgb());
+  flags.setColor(dark_mode_filter_.ApplyIfNeeded(color).Rgb());
   canvas_->drawDRRect(SkRRect::MakeRect(rect), rounded_hole_rect, flags);
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
index a236f4f..25fde9aa 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
@@ -12,7 +12,11 @@
   DEFINE_STATIC_REF(
       TransformPaintPropertyNode, root,
       base::AdoptRef(new TransformPaintPropertyNode(
-          nullptr, State{FloatSize(), &ScrollPaintPropertyNode::Root()},
+          nullptr,
+          State{FloatSize(), &ScrollPaintPropertyNode::Root(),
+                false /* flattens_inherited_transform */,
+                false /* affected_by_outer_viewport_bounds_delta */,
+                false /* in_subtree_of_page_scale */},
           true /* is_parent_alias */)));
   return *root;
 }
@@ -59,6 +63,8 @@
   }
   if (!state_.flattens_inherited_transform)
     json->SetBoolean("flattensInheritedTransform", false);
+  if (!state_.in_subtree_of_page_scale)
+    json->SetBoolean("in_subtree_of_page_scale", false);
   if (state_.backface_visibility != BackfaceVisibility::kInherited) {
     json->SetString("backface",
                     state_.backface_visibility == BackfaceVisibility::kVisible
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
index e638902..070f2c4e 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -116,6 +116,7 @@
     scoped_refptr<const ScrollPaintPropertyNode> scroll;
     bool flattens_inherited_transform = false;
     bool affected_by_outer_viewport_bounds_delta = false;
+    bool in_subtree_of_page_scale = true;
     BackfaceVisibility backface_visibility = BackfaceVisibility::kInherited;
     unsigned rendering_context_id = 0;
     CompositingReasons direct_compositing_reasons = CompositingReason::kNone;
@@ -128,6 +129,7 @@
       if (flattens_inherited_transform != other.flattens_inherited_transform ||
           affected_by_outer_viewport_bounds_delta !=
               other.affected_by_outer_viewport_bounds_delta ||
+          in_subtree_of_page_scale != other.in_subtree_of_page_scale ||
           backface_visibility != other.backface_visibility ||
           rendering_context_id != other.rendering_context_id ||
           compositor_element_id != other.compositor_element_id ||
@@ -275,6 +277,12 @@
     return state_.affected_by_outer_viewport_bounds_delta;
   }
 
+  // If true, this node is a descendant of the page scale transform. This is
+  // important for avoiding raster during pinch-zoom (see: crbug.com/951861).
+  bool IsInSubtreeOfPageScale() const {
+    return state_.in_subtree_of_page_scale;
+  }
+
   const cc::LayerStickyPositionConstraint* GetStickyConstraint() const {
     return state_.sticky_constraint.get();
   }
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc
index efacd56..5e31670 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -44,25 +44,28 @@
   }
 
   void Add(MovableReference* slot) {
-    MovableReference reference = *slot;
-    CHECK(reference);
+    MovableReference value = *slot;
+    CHECK(value);
 
-    // All slots and references are part of Oilpan's heap.
-    CHECK(heap_->LookupPageForAddress(reinterpret_cast<Address>(slot)));
-    CHECK(heap_->LookupPageForAddress(reinterpret_cast<Address>(reference)));
+    // All slots and values are part of Oilpan's heap.
+    BasePage* const slot_page =
+        heap_->LookupPageForAddress(reinterpret_cast<Address>(slot));
+    CHECK(slot_page);
+    BasePage* const value_page =
+        heap_->LookupPageForAddress(reinterpret_cast<Address>(value));
+    CHECK(value_page);
 
-    BasePage* const reference_page = PageFromObject(reference);
     // The following cases are not compacted and do not require recording:
     // - Backings in large pages.
     // - Inline backings that are part of a non-backing arena.
-    if (reference_page->IsLargeObjectPage() ||
-        !HeapCompact::IsCompactableArena(reference_page->Arena()->ArenaIndex()))
+    if (value_page->IsLargeObjectPage() ||
+        !HeapCompact::IsCompactableArena(value_page->Arena()->ArenaIndex()))
       return;
 
     // Slots may have been recorded already but must point to the same
-    // reference. Example: Ephemeron iterations may register slots multiple
+    // value. Example: Ephemeron iterations may register slots multiple
     // times.
-    auto fixup_it = fixups_.find(reference);
+    auto fixup_it = fixups_.find(value);
     if (UNLIKELY(fixup_it != fixups_.end())) {
       CHECK_EQ(slot, fixup_it->second);
       return;
@@ -72,24 +75,27 @@
     // Slots must reside in live objects
 
     // Add regular fixup.
-    fixups_.insert({reference, slot});
+    fixups_.insert({value, slot});
 
-    BasePage* const slot_page = PageFromObject(slot);
-
-    // Slots must reside in and references must point to live objects at this
+    // Slots must reside in and values must point to live objects at this
     // point, with the exception of slots in eagerly swept arenas where objects
-    // have already been processed. |reference| usually points to a separate
+    // have already been processed. |value| usually points to a separate
     // backing store but can also point to inlined storage which is why the
     // dynamic header lookup is required.
-    CHECK(reference_page->Arena()->ArenaIndex() !=
-          BlinkGC::kEagerSweepArenaIndex);
-    CHECK(static_cast<NormalPage*>(reference_page)
-              ->FindHeaderFromAddress(reinterpret_cast<Address>(reference))
+    CHECK(value_page->Arena()->ArenaIndex() != BlinkGC::kEagerSweepArenaIndex);
+    CHECK(static_cast<NormalPage*>(value_page)
+              ->FindHeaderFromAddress(reinterpret_cast<Address>(value))
               ->IsMarked());
-    CHECK(slot_page->Arena()->ArenaIndex() == BlinkGC::kEagerSweepArenaIndex ||
-          static_cast<NormalPage*>(slot_page)
-              ->FindHeaderFromAddress(reinterpret_cast<Address>(slot))
-              ->IsMarked());
+    if (slot_page->IsLargeObjectPage()) {
+      CHECK(
+          static_cast<LargeObjectPage*>(slot_page)->ObjectHeader()->IsMarked());
+    } else {
+      CHECK(slot_page->Arena()->ArenaIndex() ==
+                BlinkGC::kEagerSweepArenaIndex ||
+            static_cast<NormalPage*>(slot_page)
+                ->FindHeaderFromAddress(reinterpret_cast<Address>(slot))
+                ->IsMarked());
+    }
 
     // Check whether the slot itself resides on a page that is compacted.
     if (LIKELY(!relocatable_pages_.Contains(slot_page)))
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 03e808fe..c0f94c4 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -577,10 +577,6 @@
       settable_from_internals: true,
     },
     {
-      name: "FontCacheScaling",
-      status: "test",
-    },
-    {
       name: "FontSrcLocalMatching",
       // No status, as the web platform runtime enabled feature is controlled by
       // a Chromium level feature.
diff --git a/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc
index 55b430a..df7f01c 100644
--- a/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc
@@ -188,8 +188,9 @@
       // it isn't bound to thread and all tasks will go through a MessageLoop.
       sequence_manager_ =
           base::sequence_manager::SequenceManagerForTest::CreateOnCurrentThread(
-              SequenceManager::Settings{
-                  .clock = test_task_runner_->GetMockTickClock()});
+              base::sequence_manager::SequenceManager::Settings::Builder()
+                  .SetTickClock(test_task_runner_->GetMockTickClock())
+                  .Build());
     }
     scheduler_helper_ = std::make_unique<NonMainThreadSchedulerHelper>(
         sequence_manager_.get(), nullptr, TaskType::kInternalTest);
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc
index e827a19..d7c393e0 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc
@@ -52,7 +52,8 @@
   SchedulerHelperTest()
       : task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED) {
+            base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::
+                QUEUED) {
     // Null clock triggers some assertions.
     task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(5));
     sequence_manager_ = base::sequence_manager::SequenceManagerForTest::Create(
diff --git a/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc
index c03bd882..62b4182 100644
--- a/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc
@@ -20,9 +20,10 @@
 WebThreadScheduler::CreateMainThreadScheduler(
     std::unique_ptr<base::MessagePump> message_pump,
     base::Optional<base::Time> initial_virtual_time) {
-  auto settings = base::sequence_manager::SequenceManager::Settings{
-      base::MessageLoop::TYPE_DEFAULT,
-      /*randomised_sampling_enabled=*/true};
+  auto settings = base::sequence_manager::SequenceManager::Settings::Builder()
+                      .SetMessageLoopType(base::MessageLoop::TYPE_DEFAULT)
+                      .SetRandomisedSamplingEnabled(true)
+                      .Build();
   auto sequence_manager =
       message_pump
           ? base::sequence_manager::
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner_unittest.cc
index 06b77b1e..443fa86 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner_unittest.cc
@@ -20,7 +20,8 @@
   DeadlineTaskRunnerTest()
       : task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED) {
+            base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::
+                QUEUED) {
     // Null clock might trigger some assertions.
     task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(5));
   }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index 74fc4076..d0ef938 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -61,7 +61,8 @@
   FrameSchedulerImplTest()
       : task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED) {
+            base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::
+                QUEUED) {
     // Null clock might trigger some assertions.
     task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(5));
   }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
index 0821cc08..ac9947c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
@@ -37,7 +37,7 @@
   FrameTaskQueueControllerTest()
       : task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED),
+            base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::QUEUED),
         task_queue_created_count_(0) {}
 
   ~FrameTaskQueueControllerTest() override = default;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator_unittest.cc
index f53a86b5..dbdea81 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator_unittest.cc
@@ -38,7 +38,7 @@
   IdleTimeEstimatorTest()
       : task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED),
+            base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::QUEUED),
         frame_length_(base::TimeDelta::FromMilliseconds(16)) {
     // Null clock might trigger some assertions.
     task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(5));
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc
index a823a6f..b42bf282 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc
@@ -51,7 +51,8 @@
   MainThreadMetricsHelperTest()
       : task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED) {
+            base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::
+                QUEUED) {
     // Null clock might trigger some assertions.
     task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(1));
   }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index 0fa8523c..bc40c31 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -2087,8 +2087,9 @@
     clock_.Advance(base::TimeDelta::FromMilliseconds(5));
     Initialize(std::make_unique<MainThreadSchedulerImplForTest>(
         base::sequence_manager::SequenceManagerForTest::CreateOnCurrentThread(
-            base::sequence_manager::SequenceManager::Settings{.clock =
-                                                                  &clock_}),
+            base::sequence_manager::SequenceManager::Settings::Builder()
+                .SetTickClock(&clock_)
+                .Build()),
         base::nullopt));
   }
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_unittest.cc
index d38de1e..5d8c88c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_unittest.cc
@@ -49,8 +49,9 @@
     clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
     scheduler_.reset(new MainThreadSchedulerImpl(
         base::sequence_manager::SequenceManagerForTest::CreateOnCurrentThread(
-            base::sequence_manager::SequenceManager::Settings{.clock =
-                                                                  &clock_}),
+            base::sequence_manager::SequenceManager::Settings::Builder()
+                .SetTickClock(&clock_)
+                .Build()),
         base::nullopt));
     scheduler_overrider_ =
         std::make_unique<ScopedSchedulerOverrider>(scheduler_.get());
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc
index 40bca2e..319a68e 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc
@@ -104,7 +104,7 @@
   WorkerSchedulerProxyTest()
       : task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED),
+            base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::QUEUED),
         main_thread_scheduler_(std::make_unique<MainThreadSchedulerImpl>(
             base::sequence_manager::SequenceManagerForTest::Create(
                 nullptr,
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread.cc
index a2e644b..edf6775 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread.cc
@@ -80,9 +80,10 @@
   // TODO(alexclarke): Do we need to unify virtual time for workers and the main
   // thread?
   sequence_manager_ = base::sequence_manager::CreateUnboundSequenceManager(
-      base::sequence_manager::SequenceManager::Settings{
-          base::MessageLoop::TYPE_DEFAULT,
-          /*randomised_sampling_enabled=*/true});
+      base::sequence_manager::SequenceManager::Settings::Builder()
+          .SetMessageLoopType(base::MessageLoop::TYPE_DEFAULT)
+          .SetRandomisedSamplingEnabled(true)
+          .Build());
   internal_task_queue_ = sequence_manager_->CreateTaskQueue(
       base::sequence_manager::TaskQueue::Spec("worker_thread_internal_tq"));
   internal_task_runner_ = internal_task_queue_->CreateTaskRunner(
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
index 28ebf50..2801016 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
@@ -102,7 +102,7 @@
   WorkerThreadSchedulerTest()
       : task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED),
+            base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::QUEUED),
         sequence_manager_(
             base::sequence_manager::SequenceManagerForTest::Create(
                 nullptr,
@@ -416,7 +416,7 @@
   WorkerThreadSchedulerWithProxyTest()
       : task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED),
+            base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::QUEUED),
         sequence_manager_(
             base::sequence_manager::SequenceManagerForTest::Create(
                 nullptr,
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index 8730070..6c1e809b 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -86,6 +86,7 @@
 crbug.com/591099 external/wpt/css/css-grid/grid-items/grid-item-percentage-sizes-003.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-multicol/multicol-span-all-list-item-002.html [ Pass ]
 crbug.com/933054 external/wpt/css/css-position/position-absolute-container-dynamic-002.html [ Pass ]
+crbug.com/952644 external/wpt/css/css-position/position-absolute-crash-chrome-006.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-position/position-absolute-in-inline-001.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-position/static-position/htb-ltr-rtl.tentative.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-position/static-position/htb-rtl-ltr.tentative.html [ Pass ]
@@ -329,7 +330,6 @@
 crbug.com/591099 paint/invalidation/scroll/fixed-under-composited-fixed-scrolled.html [ Failure ]
 crbug.com/591099 paint/invalidation/svg/svg-background-partial-redraw.html [ Failure ]
 crbug.com/591099 paint/invalidation/svg/transform-focus-ring-repaint.html [ Failure ]
-crbug.com/591099 printing/iframe-svg-in-object-print.html [ Failure ]
 crbug.com/591099 storage/indexeddb/objectstore-cursor.html [ Pass ]
 crbug.com/591099 svg/zoom/page/zoom-svg-float-border-padding.xml [ Pass ]
 crbug.com/591099 tables/mozilla/bugs/bug14159-1.html [ Pass ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 342ff6c2d..cff2ddf 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -396,6 +396,7 @@
 
 #### external/wpt/css/css-position
 crbug.com/933054 external/wpt/css/css-position/position-absolute-container-dynamic-002.html [ Failure ]
+crbug.com/952644 external/wpt/css/css-position/position-absolute-crash-chrome-006.html [ Crash ]
 crbug.com/935805 external/wpt/css/css-position/position-absolute-in-inline-001.html [ Failure ]
 crbug.com/752022 external/wpt/css/css-position/position-sticky-offset-overflow.html [ Failure ]
 crbug.com/702927 external/wpt/css/css-position/position-sticky-table-tr-top.html [ Failure ]
@@ -4175,6 +4176,90 @@
 crbug.com/626703 [ Win ] external/wpt/css/css-writing-modes/box-offsets-rel-pos-vlr-005.xht [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-writing-modes/box-offsets-rel-pos-vrl-004.xht [ Failure ]
 
+# Failure due to on-going off-thread paint worklet project.
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/background-image-alpha.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/background-image-multiple.https.html [ Failure ]
+crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/background-image-tiled.https.html [ Crash ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/geometry-background-image-001.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/geometry-background-image-002.https.html [ Failure ]
+crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/geometry-background-image-tiled-001.https.html [ Crash ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/geometry-background-image-tiled-002.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/geometry-background-image-tiled-003.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/geometry-border-image-001.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/geometry-border-image-002.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/geometry-border-image-003.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/geometry-border-image-004.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/geometry-with-float-size.https.html [ Failure ]
+crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/invalid-image-constructor-error.https.html [ Crash ]
+crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/invalid-image-paint-error.https.html [ Crash ]
+crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/invalid-image-pending-script.https.html [ Crash ]
+crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/paint-arguments.https.html [ Crash ]
+crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/paint-function-arguments.https.html [ Crash ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/paint2d-composite.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/paint2d-filter.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/paint2d-gradient.https.html [ Failure ]
+crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/paint2d-image.https.html [ Crash ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/paint2d-paths.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/paint2d-rects.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/paint2d-shadows.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/paint2d-transform.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-001.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-002.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-003.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-004.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-005.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-006.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-007.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-008.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-009.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-010.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-011.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-012.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-013.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-014.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-015.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-016.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-017.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-018.https.html [ Crash Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-019.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-020.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-021.https.html [ Failure ]
+crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/parse-input-arguments-022.https.html [ Failure ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-001.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-002.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-003.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-004.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-005.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-006.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-007.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-008.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-009.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-010.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-invalidation-001.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-invalidation-002.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-stylemap.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-001.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-002.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-003.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-004.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-005.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-006.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-007.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-008.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-009.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-010.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-011.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-012.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-013.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-014.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-015.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-016.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-017.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-018.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/style-background-image.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/style-before-pseudo.https.html [ Crash ]
+crbug.com/957458 virtual/threaded/external/wpt/css/css-paint-api/style-first-letter-pseudo.https.html [ Crash ]
+
 crbug.com/943487 external/wpt/wasm/webapi/rejected-arg.any.html [ Timeout Pass ]
 crbug.com/943487 external/wpt/wasm/webapi/rejected-arg.any.serviceworker.html [ Timeout Pass ]
 crbug.com/943487 external/wpt/wasm/webapi/rejected-arg.any.worker.html [ Timeout Pass ]
@@ -6054,12 +6139,6 @@
 # Sheriff 2019-03-29
 crbug.com/947477 external/wpt/editing/run/removeformat.html [ Pass Crash Timeout ]
 
-# Adjusting v8 console implementation, pass failure until v8 change lands
-crbug.com/948257 external/wpt/console/console-label-conversion.any.html [ Pass Failure ]
-crbug.com/948257 external/wpt/console/console-label-conversion.any.worker.html [ Pass Failure ]
-crbug.com/948678 external/wpt/console/idlharness.any.html [ Pass Failure ]
-crbug.com/948678 external/wpt/console/idlharness.any.worker.html [ Pass Failure ]
-
 ### external/wpt/fetch/sec-metadata/
 crbug.com/947023 external/wpt/fetch/sec-metadata/font.tentative.https.sub.html [ Pass Failure ]
 crbug.com/947023 external/wpt/fetch/sec-metadata/report.tentative.https.sub.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 01f93069d..c067c30 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -56,6 +56,12 @@
   },
   {
     "prefix": "threaded",
+    "base": "external/wpt/css/css-paint-api",
+    "args": ["--enable-threaded-compositing",
+             "--enable-blink-features=OffMainThreadCSSPaint"]
+  },
+  {
+    "prefix": "threaded",
     "base": "external/wpt/requestidlecallback",
     "args": ["--enable-threaded-compositing"]
   },
@@ -443,6 +449,11 @@
   },
   {
     "prefix": "dark-mode",
+    "base": "paint/dark-mode/grayscale-images",
+    "args": ["--blink-settings=darkMode=3,darkModeImagePolicy=0,darkModeImageStyle=1"]
+  },
+  {
+    "prefix": "dark-mode",
     "base": "paint/dark-mode/image-filter-all",
     "args": ["--blink-settings=darkMode=3,darkModeImagePolicy=0"]
   },
diff --git a/third_party/blink/web_tests/external/wpt/console/console-label-conversion.any-expected.txt b/third_party/blink/web_tests/external/wpt/console/console-label-conversion.any-expected.txt
deleted file mode 100644
index 1fde3cad..0000000
--- a/third_party/blink/web_tests/external/wpt/console/console-label-conversion.any-expected.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-This is a testharness.js-based test.
-FAIL console.count()'s label gets converted to string via label.toString() when label is an object assert_true: count() must call toString() on label when label is an object expected true got false
-FAIL console.count() throws exceptions generated by erroneous label.toString() conversion assert_throws: count must re-throw any exceptions thrown by label.toString() conversion function "() => {
-      console[method]({
-        toString() {
-          throw new Error('conversion error');
-        }
-      });
-    }" did not throw
-FAIL console.countReset()'s label gets converted to string via label.toString() when label is an object assert_true: countReset() must call toString() on label when label is an object expected true got false
-FAIL console.countReset() throws exceptions generated by erroneous label.toString() conversion assert_throws: countReset must re-throw any exceptions thrown by label.toString() conversion function "() => {
-      console[method]({
-        toString() {
-          throw new Error('conversion error');
-        }
-      });
-    }" did not throw
-FAIL console.time()'s label gets converted to string via label.toString() when label is an object assert_true: time() must call toString() on label when label is an object expected true got false
-FAIL console.time() throws exceptions generated by erroneous label.toString() conversion assert_throws: time must re-throw any exceptions thrown by label.toString() conversion function "() => {
-      console[method]({
-        toString() {
-          throw new Error('conversion error');
-        }
-      });
-    }" did not throw
-FAIL console.timeLog()'s label gets converted to string via label.toString() when label is an object assert_true: timeLog() must call toString() on label when label is an object expected true got false
-FAIL console.timeLog() throws exceptions generated by erroneous label.toString() conversion assert_throws: timeLog must re-throw any exceptions thrown by label.toString() conversion function "() => {
-      console[method]({
-        toString() {
-          throw new Error('conversion error');
-        }
-      });
-    }" did not throw
-FAIL console.timeEnd()'s label gets converted to string via label.toString() when label is an object assert_true: timeEnd() must call toString() on label when label is an object expected true got false
-FAIL console.timeEnd() throws exceptions generated by erroneous label.toString() conversion assert_throws: timeEnd must re-throw any exceptions thrown by label.toString() conversion function "() => {
-      console[method]({
-        toString() {
-          throw new Error('conversion error');
-        }
-      });
-    }" did not throw
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/console/console-label-conversion.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/console/console-label-conversion.any.worker-expected.txt
deleted file mode 100644
index 1fde3cad..0000000
--- a/third_party/blink/web_tests/external/wpt/console/console-label-conversion.any.worker-expected.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-This is a testharness.js-based test.
-FAIL console.count()'s label gets converted to string via label.toString() when label is an object assert_true: count() must call toString() on label when label is an object expected true got false
-FAIL console.count() throws exceptions generated by erroneous label.toString() conversion assert_throws: count must re-throw any exceptions thrown by label.toString() conversion function "() => {
-      console[method]({
-        toString() {
-          throw new Error('conversion error');
-        }
-      });
-    }" did not throw
-FAIL console.countReset()'s label gets converted to string via label.toString() when label is an object assert_true: countReset() must call toString() on label when label is an object expected true got false
-FAIL console.countReset() throws exceptions generated by erroneous label.toString() conversion assert_throws: countReset must re-throw any exceptions thrown by label.toString() conversion function "() => {
-      console[method]({
-        toString() {
-          throw new Error('conversion error');
-        }
-      });
-    }" did not throw
-FAIL console.time()'s label gets converted to string via label.toString() when label is an object assert_true: time() must call toString() on label when label is an object expected true got false
-FAIL console.time() throws exceptions generated by erroneous label.toString() conversion assert_throws: time must re-throw any exceptions thrown by label.toString() conversion function "() => {
-      console[method]({
-        toString() {
-          throw new Error('conversion error');
-        }
-      });
-    }" did not throw
-FAIL console.timeLog()'s label gets converted to string via label.toString() when label is an object assert_true: timeLog() must call toString() on label when label is an object expected true got false
-FAIL console.timeLog() throws exceptions generated by erroneous label.toString() conversion assert_throws: timeLog must re-throw any exceptions thrown by label.toString() conversion function "() => {
-      console[method]({
-        toString() {
-          throw new Error('conversion error');
-        }
-      });
-    }" did not throw
-FAIL console.timeEnd()'s label gets converted to string via label.toString() when label is an object assert_true: timeEnd() must call toString() on label when label is an object expected true got false
-FAIL console.timeEnd() throws exceptions generated by erroneous label.toString() conversion assert_throws: timeEnd must re-throw any exceptions thrown by label.toString() conversion function "() => {
-      console[method]({
-        toString() {
-          throw new Error('conversion error');
-        }
-      });
-    }" did not throw
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/console/idlharness.any-expected.txt b/third_party/blink/web_tests/external/wpt/console/idlharness.any-expected.txt
deleted file mode 100644
index 3b3bae1..0000000
--- a/third_party/blink/web_tests/external/wpt/console/idlharness.any-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-FAIL console namespace: operation assert(boolean, any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation clear() assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation debug(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation error(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation info(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation log(any...) assert_equals: operation has wrong .length expected 0 but got 1
-PASS console namespace: operation table(any, [object Object])
-FAIL console namespace: operation trace(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation warn(any...) assert_equals: operation has wrong .length expected 0 but got 1
-PASS console namespace: operation dir(any, object)
-FAIL console namespace: operation dirxml(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation count(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation countReset(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation group(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation groupCollapsed(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation groupEnd() assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation time(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation timeLog(DOMString, any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation timeEnd(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/console/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/console/idlharness.any.worker-expected.txt
deleted file mode 100644
index 3b3bae1..0000000
--- a/third_party/blink/web_tests/external/wpt/console/idlharness.any.worker-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-FAIL console namespace: operation assert(boolean, any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation clear() assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation debug(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation error(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation info(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation log(any...) assert_equals: operation has wrong .length expected 0 but got 1
-PASS console namespace: operation table(any, [object Object])
-FAIL console namespace: operation trace(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation warn(any...) assert_equals: operation has wrong .length expected 0 but got 1
-PASS console namespace: operation dir(any, object)
-FAIL console namespace: operation dirxml(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation count(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation countReset(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation group(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation groupCollapsed(any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation groupEnd() assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation time(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation timeLog(DOMString, any...) assert_equals: operation has wrong .length expected 0 but got 1
-FAIL console namespace: operation timeEnd(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/CSSAnimation-canceling.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-animations/CSSAnimation-canceling.tentative-expected.txt
index 63c0e591..113de1a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/CSSAnimation-canceling.tentative-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/CSSAnimation-canceling.tentative-expected.txt
@@ -3,7 +3,7 @@
 PASS Animated style is cleared after canceling a filling CSS animation
 PASS After canceling an animation, it can still be seeked
 PASS After canceling an animation, it can still be re-used
-FAIL After canceling an animation, updating animation properties doesn't make it live again assert_equals: margin-left style is still not animated after updating animation-duration expected "0px" but got "100px"
+PASS After canceling an animation, updating animation properties doesn't make it live again
 PASS After canceling an animation, updating animation-play-state doesn't make it live again
 PASS Setting animation-name to 'none' cancels the animation
 FAIL Setting display:none on an element cancel its animations assert_equals: expected "idle" but got "running"
diff --git a/third_party/blink/web_tests/external/wpt/css/css-display/display-change-iframe.html b/third_party/blink/web_tests/external/wpt/css/css-display/display-change-iframe.html
new file mode 100644
index 0000000..c12c413
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-display/display-change-iframe.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+  <link rel="help" href="https://www.w3.org/TR/css-display-3/">
+  <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#replaced-elements">
+  <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+  <iframe id="victim" src="support/red-square.html" style="width:100px; height:100px; border:none;"></iframe>
+  <script>
+    var loaded_once = false;
+    victim.onload = function() {
+        // The child document should only load once. It should not be reloaded
+        // by changing the display type of the IFRAME.
+        if (loaded_once)
+            return;
+        loaded_once = true;
+        var childDoc = victim.contentWindow.document;
+        // Change the red background to green. This will revert back to red if
+        // the document gets reloaded (which shouldn't happen) when re-attaching
+        // #victim.
+        childDoc.getElementById("square").style.background = "green";
+        document.offsetTop;
+        victim.style.display = "block";
+        document.documentElement.className = "";
+    }
+  </script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-display/display-change-object-iframe.html b/third_party/blink/web_tests/external/wpt/css/css-display/display-change-object-iframe.html
new file mode 100644
index 0000000..8b56aff
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-display/display-change-object-iframe.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+  <link rel="help" href="https://www.w3.org/TR/css-display-3/">
+  <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#replaced-elements">
+  <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+  <object id="victim" data="support/red-square.html" style="width:100px; height:100px;"></object>
+  <script>
+    var loaded_once = false;
+    victim.onload = function() {
+        // The child document should only load once. It should not be reloaded
+        // by changing the display type of the OBJECT.
+        if (loaded_once)
+            return;
+        loaded_once = true;
+        var childDoc = victim.contentWindow.document;
+        // Change the red background to green. This will revert back to red if
+        // the document gets reloaded (which shouldn't happen) when re-attaching
+        // #victim.
+        childDoc.getElementById("square").style.background = "green";
+        document.offsetTop;
+        victim.style.display = "block";
+        document.documentElement.className = "";
+    }
+  </script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-display/support/red-square.html b/third_party/blink/web_tests/external/wpt/css/css-display/support/red-square.html
new file mode 100644
index 0000000..e4e48cc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-display/support/red-square.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<style>
+  body { margin:0; }
+</style>
+<div id="square" style="width:100px; height:100px; background:red;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-006.html b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-006.html
new file mode 100644
index 0000000..94b2469
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-006.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<title>CSS Position Absolute: Chrome crash</title>
+<link rel="author" href="mailto:atotic@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=952644">
+<meta name="assert" content="abspos iframe with zoom gets laid out">
+<style>
+  .boundary {
+    overflow: hidden;
+    width: 100px;
+    height: 100px;
+  }
+  .abs {
+    position: absolute;
+    background: green;
+    zoom: 2;
+  }
+</style>
+<!-- Containing block with zoom causes zoomed abspos iframe
+not to be laid out-->
+<div class="boundary">
+  <div id="parent">
+  </div>
+</div>
+<script>
+  document.body.offsetTop;
+  let abs = document.createElement("iframe");
+  abs.classList.add("abs");
+  document.querySelector("#parent").appendChild(abs);
+  test(() => {
+  }, 'test passes if it does not crash');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaDevices-getSupportedConstraints-expected.txt b/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaDevices-getSupportedConstraints-expected.txt
deleted file mode 100644
index 73ce1c5c8..0000000
--- a/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaDevices-getSupportedConstraints-expected.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-This is a testharness.js-based test.
-FAIL navigator.mediaDevices.getSupportedConstraints exists assert_inherits: provided value is not an object
-FAIL width is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL height is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL aspectRatio is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL frameRate is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL facingMode is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL resizeMode is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL volume is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL sampleRate is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL sampleSize is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL echoCancellation is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL autoGainControl is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL noiseSuppression is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL latency is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL channelCount is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL deviceId is supported Cannot read property 'getSupportedConstraints' of undefined
-FAIL groupId is supported Cannot read property 'getSupportedConstraints' of undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaDevices-getSupportedConstraints.html b/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaDevices-getSupportedConstraints.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaDevices-getSupportedConstraints.html
rename to third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaDevices-getSupportedConstraints.https.html
diff --git a/third_party/blink/web_tests/external/wpt/screen-capture/OWNERS b/third_party/blink/web_tests/external/wpt/screen-capture/OWNERS
index a1ef426..db41c4e 100644
--- a/third_party/blink/web_tests/external/wpt/screen-capture/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/screen-capture/OWNERS
@@ -1,3 +1,8 @@
+guidou@chromium.org
+
+# Original (legacy) owner.
+emircan@chromium.org
+
 # COMPONENT: Blink>WebRTC
 # WPT-NOTIFY: true
 emircan@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animations/setting-the-current-time-of-an-animation-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animations/setting-the-current-time-of-an-animation-expected.txt
index 79fae9f..a5269f2 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animations/setting-the-current-time-of-an-animation-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animations/setting-the-current-time-of-an-animation-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL Setting the current time of a pending animation to unresolved does not throw a TypeError Failed to set the 'currentTime' property on 'Animation': currentTime may not be changed from resolved to unresolved
+PASS Setting the current time of a pending animation to unresolved does not throw a TypeError
 PASS Setting the current time of a playing animation to unresolved throws a TypeError
 PASS Setting the current time of a paused animation to unresolved throws a TypeError
 FAIL Setting the current time of a pausing animation applies a pending playback rate assert_true: expected true got undefined
diff --git a/third_party/blink/web_tests/http/tests/devtools/throttling/mobile-throttling-expected.txt b/third_party/blink/web_tests/http/tests/devtools/throttling/mobile-throttling-expected.txt
index b07b84c..dc9c020 100644
--- a/third_party/blink/web_tests/http/tests/devtools/throttling/mobile-throttling-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/throttling/mobile-throttling-expected.txt
@@ -4,11 +4,10 @@
 === THROTTLING STATE ===
 Network throttling - download: -1 upload: -1 latency: 0
 CPU throttling rate: 1
-Device mode throttling: No throttling
-Network panel offline checkbox: false
-Network panel throttling: No throttling
-Network conditions drawer throttling: No throttling
-Performance panel network throttling: No throttling
+Device mode throttling: Online
+Network panel throttling: Online
+Network conditions drawer throttling: Online
+Performance panel network throttling: Online
 Performance panel CPU throttling: No throttling
 ========================
 
@@ -17,31 +16,17 @@
 Network throttling - download: 0 upload: 0 latency: 0
 CPU throttling rate: 1
 Device mode throttling: Offline
-Network panel offline checkbox: true
 Network panel throttling: Offline
 Network conditions drawer throttling: Offline
 Performance panel network throttling: Offline
 Performance panel CPU throttling: No throttling
 ========================
 
-Toggle network offline checkbox
-=== THROTTLING STATE ===
-Network throttling - download: -1 upload: -1 latency: 0
-CPU throttling rate: 1
-Device mode throttling: No throttling
-Network panel offline checkbox: false
-Network panel throttling: No throttling
-Network conditions drawer throttling: No throttling
-Performance panel network throttling: No throttling
-Performance panel CPU throttling: No throttling
-========================
-
 Change to low-end mobile in device mode
 === THROTTLING STATE ===
 Network throttling - download: 51200 upload: 51200 latency: 2000
 CPU throttling rate: 6
 Device mode throttling: Low-end mobile
-Network panel offline checkbox: false
 Network panel throttling: Slow 3G
 Network conditions drawer throttling: Slow 3G
 Performance panel network throttling: Slow 3G
@@ -53,31 +38,6 @@
 Network throttling - download: 188743.68000000002 upload: 86400 latency: 562.5
 CPU throttling rate: 6
 Device mode throttling: Custom
-Network panel offline checkbox: false
-Network panel throttling: Fast 3G
-Network conditions drawer throttling: Fast 3G
-Performance panel network throttling: Fast 3G
-Performance panel CPU throttling: 6× slowdown
-========================
-
-Toggle network offline checkbox (enable offline)
-=== THROTTLING STATE ===
-Network throttling - download: 0 upload: 0 latency: 0
-CPU throttling rate: 6
-Device mode throttling: Custom
-Network panel offline checkbox: true
-Network panel throttling: Offline
-Network conditions drawer throttling: Offline
-Performance panel network throttling: Offline
-Performance panel CPU throttling: 6× slowdown
-========================
-
-Toggle network offline checkbox (disable offline)
-=== THROTTLING STATE ===
-Network throttling - download: 188743.68000000002 upload: 86400 latency: 562.5
-CPU throttling rate: 6
-Device mode throttling: Custom
-Network panel offline checkbox: false
 Network panel throttling: Fast 3G
 Network conditions drawer throttling: Fast 3G
 Performance panel network throttling: Fast 3G
@@ -89,7 +49,6 @@
 Network throttling - download: 188743.68000000002 upload: 86400 latency: 562.5
 CPU throttling rate: 4
 Device mode throttling: Mid-tier mobile
-Network panel offline checkbox: false
 Network panel throttling: Fast 3G
 Network conditions drawer throttling: Fast 3G
 Performance panel network throttling: Fast 3G
@@ -101,7 +60,6 @@
 Network throttling - download: 188743.68000000002 upload: 86400 latency: 562.5
 CPU throttling rate: 6
 Device mode throttling: Custom
-Network panel offline checkbox: false
 Network panel throttling: Fast 3G
 Network conditions drawer throttling: Fast 3G
 Performance panel network throttling: Fast 3G
@@ -113,7 +71,6 @@
 Network throttling - download: 188743.68000000002 upload: 86400 latency: 562.5
 CPU throttling rate: 4
 Device mode throttling: Mid-tier mobile
-Network panel offline checkbox: false
 Network panel throttling: Fast 3G
 Network conditions drawer throttling: Fast 3G
 Performance panel network throttling: Fast 3G
@@ -124,11 +81,10 @@
 === THROTTLING STATE ===
 Network throttling - download: -1 upload: -1 latency: 0
 CPU throttling rate: 1
-Device mode throttling: No throttling
-Network panel offline checkbox: false
-Network panel throttling: No throttling
-Network conditions drawer throttling: No throttling
-Performance panel network throttling: No throttling
+Device mode throttling: Online
+Network panel throttling: Online
+Network conditions drawer throttling: Online
+Performance panel network throttling: Online
 Performance panel CPU throttling: No throttling
 ========================
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/throttling/mobile-throttling.js b/third_party/blink/web_tests/http/tests/devtools/throttling/mobile-throttling.js
index a174a86..18da20b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/throttling/mobile-throttling.js
+++ b/third_party/blink/web_tests/http/tests/devtools/throttling/mobile-throttling.js
@@ -12,7 +12,6 @@
 
   var deviceModeThrottling = deviceModeView._toolbar._throttlingConditionsItem;
   var networkPanelThrottling = UI.panels.network.throttlingSelectForTest();
-  var networkPanelOfflineCheckbox = UI.panels.network.offlineCheckboxForTest().inputElement;
   var networkConfigView = self.runtime.sharedInstance(Network.NetworkConfigView);
   var networkConditionsDrawerThrottlingSelector =
       networkConfigView.contentElement.querySelector('.network-config-throttling select.chrome-select');
@@ -25,7 +24,6 @@
     TestRunner.addResult(`Network throttling - download: ${download} upload: ${upload} latency: ${latency}`);
     TestRunner.addResult('CPU throttling rate: ' + MobileThrottling.throttlingManager().cpuThrottlingRate());
     TestRunner.addResult('Device mode throttling: ' + deviceModeThrottling._text);
-    TestRunner.addResult('Network panel offline checkbox: ' + networkPanelOfflineCheckbox.checked);
     TestRunner.addResult('Network panel throttling: ' + networkPanelThrottling.selectedOption().text);
     TestRunner.addResult('Network conditions drawer throttling: ' + networkConditionsDrawerThrottlingSelector.value);
     TestRunner.addResult(
@@ -42,10 +40,6 @@
   MobileThrottling.throttlingManager().setCPUThrottlingRate(MobileThrottling.OfflineConditions.cpuThrottlingRate);
   dumpThrottlingState();
 
-  TestRunner.addResult('Toggle network offline checkbox');
-  networkPanelOfflineCheckbox.click();
-  dumpThrottlingState();
-
   TestRunner.addResult('Change to low-end mobile in device mode');
   SDK.multitargetNetworkManager.setNetworkConditions(MobileThrottling.LowEndMobileConditions.network);
   MobileThrottling.throttlingManager().setCPUThrottlingRate(MobileThrottling.LowEndMobileConditions.cpuThrottlingRate);
@@ -55,14 +49,6 @@
   SDK.multitargetNetworkManager.setNetworkConditions(SDK.NetworkManager.Fast3GConditions);
   dumpThrottlingState();
 
-  TestRunner.addResult('Toggle network offline checkbox (enable offline)');
-  networkPanelOfflineCheckbox.click();
-  dumpThrottlingState();
-
-  TestRunner.addResult('Toggle network offline checkbox (disable offline)');
-  networkPanelOfflineCheckbox.click();
-  dumpThrottlingState();
-
   TestRunner.addResult('Change to mid-tier mobile in device mode');
   SDK.multitargetNetworkManager.setNetworkConditions(MobileThrottling.MidTierMobileConditions.network);
   MobileThrottling.throttlingManager().setCPUThrottlingRate(MobileThrottling.MidTierMobileConditions.cpuThrottlingRate);
diff --git a/third_party/blink/web_tests/http/tests/security/xssAuditor/script-tag-inside-svg-tag6-expected.txt b/third_party/blink/web_tests/http/tests/security/xssAuditor/script-tag-inside-svg-tag6-expected.txt
new file mode 100644
index 0000000..509c6f9
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/xssAuditor/script-tag-inside-svg-tag6-expected.txt
@@ -0,0 +1,2 @@
+CONSOLE ERROR: line 4: The XSS Auditor refused to execute a script in 'http://localhost:8000/security/xssAuditor/resources/echo-intertag.pl?clutter=blah&q=%3Csvg%3E%3Cscript%3Ealert(0)%26%23x2f/&q2=%3C/script%3E%3C/svg%3E' because its source code was found within the request. The server sent an 'X-XSS-Protection' header requesting this behavior.
+ Ensures script blocks in a context where CDATA is allowed respect JS comments encoded as entities.
diff --git a/third_party/blink/web_tests/http/tests/security/xssAuditor/script-tag-inside-svg-tag6.html b/third_party/blink/web_tests/http/tests/security/xssAuditor/script-tag-inside-svg-tag6.html
new file mode 100644
index 0000000..4e1ef1bb
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/xssAuditor/script-tag-inside-svg-tag6.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+  testRunner.dumpAsText();
+  testRunner.setXSSAuditorEnabled(true);
+}
+</script>
+</head>
+<body>
+<iframe src="http://localhost:8000/security/xssAuditor/resources/echo-intertag.pl?clutter=blah&q=<svg><script>alert(0)%26%23x2f/&q2=</script></svg>">
+</iframe>
+Ensures script blocks in a context where CDATA is allowed respect JS comments encoded as entities.
+</body>
+</html>
diff --git a/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png b/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png
new file mode 100644
index 0000000..87a54af4
--- /dev/null
+++ b/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion.html b/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion.html
new file mode 100644
index 0000000..09ebc168
--- /dev/null
+++ b/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100">
+  <circle cx="50" cy="50" r="40" stroke="black" stroke-width="2" fill="navy" />
+</svg>
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png
new file mode 100644
index 0000000..9b9bd4a6
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/text/basic/generic-family-reset-expected.png b/third_party/blink/web_tests/platform/win/fast/text/basic/generic-family-reset-expected.png
index c5ce5ed3..88874656 100644
--- a/third_party/blink/web_tests/platform/win/fast/text/basic/generic-family-reset-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/text/basic/generic-family-reset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/text/basic/generic-family-reset-expected.png b/third_party/blink/web_tests/platform/win7/fast/text/basic/generic-family-reset-expected.png
index d651079d..ad6926d 100644
--- a/third_party/blink/web_tests/platform/win7/fast/text/basic/generic-family-reset-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/text/basic/generic-family-reset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/README.txt b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/README.txt
new file mode 100644
index 0000000..895eda8
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in LayoutTests/paint/dark-mode
+# with --blink-settings="darkMode=3,darkModeImagePolicy=0,darkModeImageStyle=1"
+# See the virtual_test_suites() method in tools/blinkpy/web_tests/port/base.py.
diff --git a/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png
new file mode 100644
index 0000000..cc7960a
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-account-for-clip.html b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-account-for-clip.html
new file mode 100644
index 0000000..2364b16
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-account-for-clip.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/third_party/blink/public/mojom/page/spatial_navigation.mojom.js"></script>
+<script src="../../../../../fast/spatial-navigation/resources/mock-snav-service.js"></script>
+<script src="../../../../../fast/spatial-navigation/resources/snav-testharness.js"></script>
+
+<style>
+  div {
+    width: 130px;
+    height: 130px;
+    border: 1px solid black;
+  }
+
+  #clip {
+    margin: 5px;
+    width: 100px;
+    height: 100px;
+    overflow: hidden;
+    background-color: lightgrey;
+  }
+
+  #target {
+    margin: 20px;
+    height: 50px;
+  }
+
+  pre {
+    position: absolute;
+    top: 400px;
+  }
+
+</style>
+<div tabindex="0">
+  Outer Target
+  <div id="clip" tabindex="0">
+    Clip
+    <div id="target" tabindex="0">Inner Target</div>
+  </div>
+</div>
+
+<script>
+  const target = document.getElementById("target");
+
+  // This test checks that the spatial navigation overlap testing works
+  // correctly in the presence of clipping. The spatial navigation algorithm
+  // attempts to prioritize the inner element when one valid target is fully
+  // contained by another valid target. This test ensures the inner is
+  // considered fully contained even if it has clipped overflow that would
+  // spill outside the outer target.
+  test(() => {
+    assert_true(!!window.internals);
+    snav.triggerMove('Down');
+    assert_equals(window.internals.interestedElement,
+                  target,
+                  "Expected interest to move to |target| element.");
+  }, "Navigation to fully contained but clipped element.");
+</script>
diff --git a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-avoids-document-and-body.html b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-avoids-document-and-body.html
new file mode 100644
index 0000000..2511bb3
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-avoids-document-and-body.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/third_party/blink/public/mojom/page/spatial_navigation.mojom.js"></script>
+<script src="../../../../../fast/spatial-navigation/resources/mock-snav-service.js"></script>
+<script src="../../../../../fast/spatial-navigation/resources/snav-testharness.js"></script>
+
+<style>
+  body {
+    margin: 0;
+  }
+  div {
+    margin-top: 605px;
+    width: 100px;
+    height: 100px;
+    border: 1px solid black;
+  }
+  iframe {
+    margin-top: 10px;
+    width: 100px;
+    height: 100px;
+  }
+</style>
+
+<div id="target" tabindex="0">target</div>
+<iframe id="iframe" srcdoc="<!DOCTYPE>
+    <style>
+      html {
+        width: 100%;
+        height: 10px;
+      }
+      body {
+        position: absolute;
+        top: 30px;
+        width: 100%;
+        height: 10px;
+        margin: 0;
+      }
+    </style>"></iframe>
+
+<script>
+  iframe.addEventListener('load', () => {
+    const target = document.getElementById("target");
+    const iframe = document.getElementById("iframe");
+
+    document.documentElement.addEventListener('click', () => {});
+    document.body.addEventListener('click', () => {});
+    iframe.contentDocument.documentElement.addEventListener('click', () => {});
+    iframe.contentDocument.body.addEventListener('click', () => {});
+
+    test(() => {
+      assert_true(!!window.internals);
+    }, "This test requires |window.internals|");
+
+    test(() => {
+
+      snav.triggerMove('Down');
+      assert_equals(window.internals.interestedElement,
+                    null,
+                    "Nothing interestable on initial screen, shouldn't move interest.");
+      assert_greater_than(window.scrollY, 0, "Expected to scroll");
+
+      snav.triggerMove('Down');
+      assert_equals(window.internals.interestedElement,
+                    target,
+                    "Expected to interest |target| element.");
+
+    }, "Navigation while document and body have click handlers.");
+
+    test(() => {
+      assert_equals(window.internals.interestedElement,
+                    target,
+                    "|target| element should initially be focused.");
+
+      // Scroll to the bottom to ensure iframe is visible.
+      window.scrollTo(0, 100000);
+
+      // First |Down| will interest the <iframe>. Second |Down| should interest
+      // the iframe's <html> element.
+      snav.triggerMove('Down');
+      snav.triggerMove('Down');
+      assert_equals(window.internals.interestedElement,
+                    iframe.contentDocument.documentElement,
+                    "Expected iframe's documentElement to be interested.");
+
+      snav.triggerMove('Down');
+      assert_equals(window.internals.interestedElement,
+                    iframe.contentDocument.body,
+                    "Expected iframe's documentElement to be interested.");
+
+    }, "Document and body inside iframe should be a valid candidates.");
+  });
+</script>
diff --git a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-fully-contained-input.html b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-fully-contained-input.html
new file mode 100644
index 0000000..f4d6436
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-fully-contained-input.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/third_party/blink/public/mojom/page/spatial_navigation.mojom.js"></script>
+<script src="../../../../../fast/spatial-navigation/resources/mock-snav-service.js"></script>
+<script src="../../../../../fast/spatial-navigation/resources/snav-testharness.js"></script>
+
+<style>
+  div {
+    width: 200px;
+    height: 200px;
+    margin: 5px;
+    border: 1px solid black;
+  }
+
+  input {
+    margin: 5px;
+  }
+</style>
+
+<div tabindex="0">
+  <input id="input" type="text" value="second"></input>
+</div>
+
+<script>
+  const input = document.getElementById("input");
+
+  // Spatial navigation attempts to determine when one valid target fully
+  // contains another. In this case, the contained "inner" target should be
+  // selected. This is done by performing a hit test.
+  //
+  // This test ensures the hit test works correctly on an input box. This
+  // requires special attention because the hit element will be in the input's
+  // shadow dom.
+  test(() => {
+    assert_true(!!window.internals);
+    snav.triggerMove('Down');
+    assert_equals(window.internals.interestedElement,
+                  input,
+                  "Expected interest to move to input box.");
+  }, "Navigation to fully contained input box.");
+</script>
diff --git a/third_party/blink/web_tests/virtual/threaded/external/wpt/css/css-paint-api/README.txt b/third_party/blink/web_tests/virtual/threaded/external/wpt/css/css-paint-api/README.txt
new file mode 100644
index 0000000..e1b7fe3
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/threaded/external/wpt/css/css-paint-api/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the test in external/wpt/css/css-paint-api/ with
+# --enable-threaded-compositing, and --enable-blink-features=OffMainThreadCSSPaint
+
diff --git a/third_party/libwebm/OWNERS b/third_party/libwebm/OWNERS
index a385efb..a4f56cf 100644
--- a/third_party/libwebm/OWNERS
+++ b/third_party/libwebm/OWNERS
@@ -1,11 +1,11 @@
 # The following OWNERS refer to libwebm Chromium integration.
-emircan@chromium.org
 niklase@chromium.org
 
 # The following OWNER refer to libwebm content.
 tomfinegan@chromium.org
 
 # Original (legacy) owner.
+emircan@chromium.org
 mcasas@chromium.org
 
 # COMPONENT: Blink>MediaRecording
diff --git a/tools/chrome_proxy/webdriver/lofi.py b/tools/chrome_proxy/webdriver/lofi.py
index db3a06f..16f4c21 100644
--- a/tools/chrome_proxy/webdriver/lofi.py
+++ b/tools/chrome_proxy/webdriver/lofi.py
@@ -197,6 +197,47 @@
 
       self.assertNotEqual(0, intervention_headers)
 
+  @ChromeVersionEqualOrAfterM(75)
+  def testServerLoFiWithForcingFlag(self):
+    with TestDriver() as test_driver:
+      test_driver.AddChromeArg('--enable-spdy-proxy-auth')
+      test_driver.AddChromeArg('--enable-features=' + ','.join([
+                                 'Previews',
+                                 'DataReductionProxyDecidesTransform',
+                                 'DataReductionProxyEnabledWithNetworkService',
+                               ]))
+      test_driver.AddChromeArg('--force-effective-connection-type=Slow-2G')
+      test_driver.SetExperiment('force_page_policies_empty_image')
+
+      test_driver.LoadURL('http://check.googlezip.net/static/index.html')
+
+      responses = test_driver.GetHTTPResponses()
+      self.assertNotEqual(len(responses), 0)
+      for response in responses:
+        if response.url.endswith('html'):
+          self.assertIn('empty-image',
+            response.response_headers['chrome-proxy'])
+
+  @ChromeVersionEqualOrAfterM(74)
+  def testNoServerLoFiByDefault(self):
+    with TestDriver() as test_driver:
+      test_driver.AddChromeArg('--enable-spdy-proxy-auth')
+      test_driver.AddChromeArg('--enable-features=' + ','.join([
+                                 'Previews',
+                                 'DataReductionProxyDecidesTransform',
+                                 'DataReductionProxyEnabledWithNetworkService',
+                               ]))
+      test_driver.AddChromeArg('--force-effective-connection-type=Slow-2G')
+
+      test_driver.LoadURL('http://check.googlezip.net/static/index.html')
+
+      responses = test_driver.GetHTTPResponses()
+      self.assertNotEqual(len(responses), 0)
+      for response in responses:
+        if response.url.endswith('html'):
+          self.assertNotIn('empty-image',
+            response.response_headers['chrome-proxy'])
+
   # Checks that Client LoFi range requests that go through the Data Reduction
   # Proxy are returned correctly.
   @ChromeVersionEqualOrAfterM(62)
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index c284aff1..ab03116 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -37,11 +37,6 @@
 # Reverting problematic clang rolls is safe, though.
 CLANG_REVISION = '357692'
 
-use_head_revision = bool(os.environ.get('LLVM_FORCE_HEAD_REVISION', '0')
-                         in ('1', 'YES'))
-if use_head_revision:
-  CLANG_REVISION = 'HEAD'
-
 # This is incremented when pushing a new build of Clang at the same revision.
 CLANG_SUB_REVISION=1
 
@@ -439,7 +434,7 @@
 def VeryifyVersionOfBuiltClangMatchesVERSION():
   """Checks that `clang --version` outputs VERSION.  If this fails, VERSION
   in this file is out-of-date and needs to be updated (possibly in an
-  `if use_head_revision:` block in main() first)."""
+  `if args.llvm_force_head_revision:` block in main() first)."""
   clang = os.path.join(LLVM_BUILD_DIR, 'bin', 'clang')
   if sys.platform == 'win32':
     # TODO: Parse `clang-cl /?` output for built clang's version and check that
@@ -626,7 +621,7 @@
 
   # If building at head, define a macro that plugins can use for #ifdefing
   # out code that builds at head, but not at CLANG_REVISION or vice versa.
-  if use_head_revision:
+  if args.llvm_force_head_revision:
     cflags += ['-DLLVM_FORCE_HEAD_REVISION']
     cxxflags += ['-DLLVM_FORCE_HEAD_REVISION']
 
@@ -927,7 +922,7 @@
                fuchsia_lib_dst_dir)
 
   # Run tests.
-  if args.run_tests or use_head_revision:
+  if args.run_tests or args.llvm_force_head_revision:
     os.chdir(LLVM_BUILD_DIR)
     RunCommand(['ninja', 'cr-check-all'], msvc_arch='x64')
   if args.run_tests:
@@ -995,6 +990,9 @@
                       default=sys.platform in ('linux2', 'darwin'))
   args = parser.parse_args()
 
+  if (os.environ.get('LLVM_FORCE_HEAD_REVISION', '0') in ('1', 'YES')):
+    args.llvm_force_head_revision = True
+
   if args.lto_lld and not args.bootstrap:
     print('--lto-lld requires --bootstrap')
     return 1
@@ -1003,8 +1001,7 @@
     args.lto_lld = False
 
   # Get svn if we're going to use it to check the revision or do a local build.
-  if (use_head_revision or args.llvm_force_head_revision or
-      args.force_local_build):
+  if args.llvm_force_head_revision or args.force_local_build:
     AddSvnToPathOnWin()
 
   if args.verify_version and args.verify_version != VERSION:
@@ -1022,7 +1019,7 @@
 
   global CLANG_REVISION, PACKAGE_VERSION
   if args.print_revision:
-    if use_head_revision or args.llvm_force_head_revision:
+    if args.llvm_force_head_revision:
       print(GetSvnRevision(LLVM_DIR))
     else:
       print(PACKAGE_VERSION)
@@ -1037,7 +1034,7 @@
   # an error message when this script is run from gn for some reason.
   sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
 
-  if use_head_revision:
+  if args.llvm_force_head_revision:
     # Use a real revision number rather than HEAD to make sure that the stamp
     # file logic works.
     CLANG_REVISION = GetSvnRevision(LLVM_REPO_URL)
diff --git a/tools/gdb/gdbinit b/tools/gdb/gdbinit
index bf9f632d..78d3933 100644
--- a/tools/gdb/gdbinit
+++ b/tools/gdb/gdbinit
@@ -71,7 +71,10 @@
   src_dir, _ = git.communicate()
   if git.returncode:
     return
-  src_dir = str(src_dir).rstrip()
+  if isinstance(src_dir, str):
+    src_dir = src_dir.rstrip()
+  else:
+    src_dir = src_dir.decode('utf-8').rstrip()
 
   load_libcxx_pretty_printers(src_dir)
 
diff --git a/tools/metrics/actions/extract_actions.py b/tools/metrics/actions/extract_actions.py
index 169f5b3e..5c49416 100755
--- a/tools/metrics/actions/extract_actions.py
+++ b/tools/metrics/actions/extract_actions.py
@@ -72,6 +72,16 @@
   """,
   re.VERBOSE | re.DOTALL      # Verbose syntax and makes . also match new lines.
 )
+USER_METRICS_ACTION_RE_DEVTOOLS = re.compile(r"""
+  InspectorFrontendHost\.recordUserMetricsAction     # Start of function call.
+  \(                          # Opening parenthesis.
+  \s*                         # Any amount of whitespace, including new lines.
+  (.+?)                       # A sequence of characters for the param.
+  \s*                         # Any amount of whitespace, including new lines.
+  \)                          # Closing parenthesis.
+  """,
+  re.VERBOSE | re.DOTALL      # Verbose syntax and makes . also match new lines.
+)
 COMPUTED_ACTION_RE = re.compile(r'RecordComputedAction')
 QUOTED_STRING_RE = re.compile(r"""('[^']+'|"[^"]+")$""")
 
@@ -99,6 +109,7 @@
   'pepper_pdf_host.cc',  # see AddClosedSourceActions()
   'record_user_action.cc', # see RecordUserAction.java
   'blink_platform_impl.cc', # see WebKit/public/platform/Platform.h
+  'devtools_ui_bindings.cc', # see AddDevToolsActions()
 )
 
 # Language codes used in Chrome. The list should be updated when a new
@@ -433,6 +444,31 @@
     if not close_called:
       parser.close()
 
+def GrepForDevToolsActions(path, actions):
+  """Grep a DevTools source file for calls to UserMetrics functions.
+
+  Arguments:
+    path: path to the file
+    actions: set of actions to add to
+  """
+  global number_of_files_total
+  number_of_files_total = number_of_files_total + 1
+
+  ext = os.path.splitext(path)[1].lower()
+  if ext != '.js':
+    return
+
+  finder = ActionNameFinder(path, open(path).read(),
+      USER_METRICS_ACTION_RE_DEVTOOLS)
+  while True:
+    try:
+      action_name = finder.FindNextAction()
+      if not action_name:
+        break
+      actions.add(action_name)
+    except InvalidStatementException, e:
+      logging.warning(str(e))
+
 def WalkDirectory(root_path, actions, extensions, callback):
   for path, dirs, files in os.walk(root_path):
     if '.svn' in dirs:
@@ -485,6 +521,16 @@
   WalkDirectory(resources_root, actions, ('.html'), GrepForWebUIActions)
   WalkDirectory(resources_root, actions, ('.js'), GrepForActions)
 
+def AddDevToolsActions(actions):
+  """Add user actions defined in DevTools frontend files.
+
+  Arguments:
+    actions: set of actions to add to.
+  """
+  resources_root = os.path.join(REPOSITORY_ROOT, 'third_party', 'blink',
+                                'renderer', 'devtools', 'front_end')
+  WalkDirectory(resources_root, actions, ('.js'), GrepForDevToolsActions)
+
 def AddHistoryPageActions(actions):
   """Add actions that are used in History page.
 
@@ -721,6 +767,7 @@
   actions = set()
   AddComputedActions(actions)
   AddWebUIActions(actions)
+  AddDevToolsActions(actions)
 
   AddLiteralActions(actions)
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6137ecb..c4d3f59e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -16653,6 +16653,7 @@
   <int value="558" label="KerberosRememberPasswordEnabled"/>
   <int value="559" label="KerberosAddAccountsAllowed"/>
   <int value="560" label="KerberosAccounts"/>
+  <int value="561" label="StickyKeysEnabled"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">
@@ -34488,6 +34489,7 @@
   <int value="1324623677"
       label="SessionRestorePrioritizesBackgroundUseCases:enabled"/>
   <int value="1330264457" label="OmniboxUIExperimentVerticalLayout:disabled"/>
+  <int value="1331098784" label="ReducedReferrerGranularity:enabled"/>
   <int value="1332120969" label="ChromeDuet:disabled"/>
   <int value="1333847867" label="NoScriptPreviews:enabled"/>
   <int value="1338356182" label="AutofillAssistantChromeEntry:disabled"/>
@@ -34814,6 +34816,7 @@
   <int value="1849379463" label="OfflinePagesCTV2:enabled"/>
   <int value="1849580433" label="disable-oop-rasterization"/>
   <int value="1851358497" label="enable-ash-sidebar"/>
+  <int value="1851746253" label="ReducedReferrerGranularity:disabled"/>
   <int value="1852630189" label="NTPBookmarkSuggestions:disabled"/>
   <int value="1854226565" label="AutofillNoLocalSaveOnUnmaskSuccess:enabled"/>
   <int value="1855524566" label="allow-insecure-websocket-from-https-origin"/>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 4cab5a4..74b7406 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -130,6 +130,7 @@
       }
     ],
     'platform': 'android-chrome',
+      'browser': 'bin/monochrome_64_32_bundle',
     'dimension': {
       'pool': 'chrome.tests.perf-fyi',
       'os': 'Android',
@@ -968,6 +969,8 @@
   # For trybot testing we always use the reference build
   if tester_config.get('testing', False):
     browser_name = 'reference'
+  elif 'browser' in tester_config:
+    browser_name = 'exact'
   elif tester_config['platform'] == 'android':
     browser_name = 'android-chromium'
   elif tester_config['platform'].startswith('android-'):
@@ -984,7 +987,13 @@
     '--upload-results'
   ]
 
-  if browser_name.startswith('android-webview'):
+  if 'browser' in tester_config:
+    test_args.append('--browser-executable=../../out/Release/%s' %
+                     tester_config['browser'])
+    if tester_config['platform'].startswith('android'):
+      test_args.append('--device=android')
+
+  if tester_config['platform'].startswith('android-webview'):
     test_args.append(
         '--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk')
 
diff --git a/tools/perf/core/perf_data_generator_unittest.py b/tools/perf/core/perf_data_generator_unittest.py
index 3f326e7..7697b49 100644
--- a/tools/perf/core/perf_data_generator_unittest.py
+++ b/tools/perf/core/perf_data_generator_unittest.py
@@ -97,6 +97,61 @@
       }
     self.assertEquals(returned_test, expected_generated_test)
 
+  def testGeneratePerformanceTestSuiteExact(self):
+    swarming_dimensions = [
+        {'os': 'SkyNet', 'pool': 'T-RIP'}
+    ]
+    test_config = {
+        'platform': 'android-webview',
+        'browser': 'bin/monochrome_64_32_bundle',
+        'dimension': swarming_dimensions,
+    }
+    test = {
+        'isolate': 'performance_test_suite',
+        'extra_args': [
+            '--run-ref-build',
+            '--test-shard-map-filename=shard_map.json',
+          ],
+        'num_shards': 26
+    }
+    returned_test = perf_data_generator.generate_performance_test(
+        test_config, test)
+
+    expected_generated_test = {
+        'override_compile_targets': ['performance_test_suite'],
+        'isolate_name': 'performance_test_suite',
+        'args': ['-v', '--browser=exact', '--upload-results',
+                 '--browser-executable=../../out/Release'
+                 '/bin/monochrome_64_32_bundle',
+                 '--device=android',
+                 '--webview-embedder-apk=../../out/Release'
+                 '/apks/SystemWebViewShell.apk',
+                 '--run-ref-build',
+                 '--test-shard-map-filename=shard_map.json'],
+        'trigger_script': {
+          'args': [
+            '--multiple-dimension-script-verbose',
+            'True'
+          ],
+          'requires_simultaneous_shard_dispatch': True,
+          'script': '//testing/trigger_scripts/perf_device_trigger.py'
+        },
+        'merge': {
+          'script': '//tools/perf/process_perf_results.py'
+        },
+        'swarming': {
+          'ignore_task_failure': False,
+          'can_use_on_swarming_builders': True,
+          'expiration': 7200,
+          'io_timeout': 1800,
+          'hard_timeout': 36000,
+          'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
+          'shards': 26
+        },
+        'name': 'performance_test_suite'
+      }
+    self.assertEquals(returned_test, expected_generated_test)
+
   def testGeneratePerformanceTestSuiteWebview(self):
     swarming_dimensions = [
         {'os': 'SkyNet', 'pool': 'T-RIP'}
diff --git a/tools/perf/core/perf_json_config_validator.py b/tools/perf/core/perf_json_config_validator.py
index df7cd111..7ace5f3 100644
--- a/tools/perf/core/perf_json_config_validator.py
+++ b/tools/perf/core/perf_json_config_validator.py
@@ -81,17 +81,18 @@
   browser_options = _ParseBrowserFlags(test_config['args'])
   if 'WebView' in builder_name or 'webview' in builder_name:
     if browser_options.browser not in (
-        'android-webview', 'android-webview-google'):
+        'android-webview', 'android-webview-google', 'exact'):
       raise ValueError(
-          "%s must use 'android-webview' or 'android-webview-google' browser" %
-          builder_name)
+          "%s must use 'android-webview', 'android-webview-google' or 'exact' "
+          "browser" % builder_name)
     if not browser_options.webview_embedder_apk:
       raise ValueError('%s must set --webview-embedder-apk flag' % builder_name)
   elif 'Android' in builder_name or 'android' in builder_name:
-    if browser_options.browser not in ('android-chromium', 'android-chrome'):
+    if browser_options.browser not in (
+        'android-chromium', 'android-chrome', 'exact'):
       raise ValueError(
-          "%s must use 'android-chromium' or 'android-chrome' browser" %
-          builder_name)
+          "%s must use 'android-chromium', 'android-chrome' or 'exact' "
+          "browser" % builder_name)
   elif builder_name in ('win-10-perf', 'Win 7 Nvidia GPU Perf'):
     if browser_options.browser != 'release_x64':
       raise ValueError("%s must use 'release_x64' browser type" %
diff --git a/tools/perf/page_sets/rendering/rendering_stories.py b/tools/perf/page_sets/rendering/rendering_stories.py
index 9cd818e..98cb201 100644
--- a/tools/perf/page_sets/rendering/rendering_stories.py
+++ b/tools/perf/page_sets/rendering/rendering_stories.py
@@ -61,8 +61,7 @@
           shared_page_state_class=shared_page_state_class,
           extra_browser_args=required_args))
 
-      if (platform == platforms.MOBILE and
-          story_class.TAGS and
+      if (story_class.TAGS and
           story_tags.IMAGE_DECODING in story_class.TAGS):
         self.AddStory(story_class(
             page_set=self,
@@ -70,7 +69,6 @@
             name_suffix='_gpu_rasterization_and_decoding',
             extra_browser_args=required_args + [
                 '--force-gpu-rasterization',
-                '--enable-accelerated-jpeg-decoding',
             ]))
 
 
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index 70b5ecd..87241afa 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -77,9 +77,6 @@
   <message name="IDS_FILE_BROWSER_MEDIA_VIEW_AUDIO_ROOT_LABEL" desc="A label for the 'Audio' root of media views.">
     Audio
   </message>
-  <message name="IDS_FILE_BROWSER_PLUGIN_VM_DIRECTORY_LABEL" desc="PluginVm local directory label.">
-    Plugin VM
-  </message>
   <message name="IDS_FILE_BROWSER_RECENT_ROOT_LABEL" desc="A label for the 'Recent' root which shows files recently modified by the user.">
     Recent
   </message>
diff --git a/ui/file_manager/file_manager/common/js/file_type.js b/ui/file_manager/file_manager/common/js/file_type.js
index 6f024c5..ba2337b 100644
--- a/ui/file_manager/file_manager/common/js/file_type.js
+++ b/ui/file_manager/file_manager/common/js/file_type.js
@@ -620,7 +620,6 @@
   const overrides = {
     [VolumeManagerCommon.RootType.DOWNLOADS]: {
       '/Downloads': VolumeManagerCommon.VolumeType.DOWNLOADS,
-      '/PluginVm': 'plugin_vm',
     },
   };
   const root = overrides[opt_rootType];
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index 04c5145..ff75ff4 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -1151,15 +1151,11 @@
     return util.getRootTypeLabel(locationInfo);
   }
 
-  // Special case for MyFiles/Downloads and MyFiles/PluginVm.
-  if (locationInfo &&
-      locationInfo.rootType == VolumeManagerCommon.RootType.DOWNLOADS) {
-    if (util.isMyFilesVolumeEnabled() && entry.fullPath == '/Downloads') {
-      return str('DOWNLOADS_DIRECTORY_LABEL');
-    }
-    if (util.isPluginVmEnabled() && entry.fullPath == '/PluginVm') {
-      return str('PLUGIN_VM_DIRECTORY_LABEL');
-    }
+  // Special case for MyFiles/Downloads.
+  if (locationInfo && util.isMyFilesVolumeEnabled() &&
+      locationInfo.rootType == VolumeManagerCommon.RootType.DOWNLOADS &&
+      entry.fullPath == '/Downloads') {
+    return str('DOWNLOADS_DIRECTORY_LABEL');
   }
 
   return entry.name;
@@ -1514,12 +1510,6 @@
       loadTimeData.getBoolean('MY_FILES_VOLUME_ENABLED');
 };
 
-/** @return {boolean} */
-util.isPluginVmEnabled = () => {
-  return loadTimeData.valueExists('PLUGIN_VM_ENABLED') &&
-      loadTimeData.getBoolean('PLUGIN_VM_ENABLED');
-};
-
 /**
  * Used for logs and debugging. It tries to tell what type is the entry, its
  * path and URL.
diff --git a/ui/file_manager/file_manager/foreground/css/file_types.css b/ui/file_manager/file_manager/foreground/css/file_types.css
index 66d646b3..a2a3b76 100644
--- a/ui/file_manager/file_manager/foreground/css/file_types.css
+++ b/ui/file_manager/file_manager/foreground/css/file_types.css
@@ -529,11 +529,3 @@
       url(../images/volumes/android_active.png) 1x,
       url(../images/volumes/2x/android_active.png) 2x);
 }
-
-[file-type-icon='plugin_vm'] {
-  background-image: url(../images/volumes/plugin_vm.svg);
-}
-
-.tree-row[selected] [file-type-icon='plugin_vm'] {
-  background-image: url(../images/volumes/plugin_vm_active.svg);
-}
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm.svg b/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm.svg
deleted file mode 100644
index 7cdd3608..0000000
--- a/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" fill="rgb(90,90,90)">
-  <path d="M7 12v4H4V4a1 1 0 0 1 1-1h8a3 3 0 0 1 3 3v3a3 3 0 0 1-3 3H7zm0-6v3h5a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H7z"/>
-</svg>
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm_active.svg b/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm_active.svg
deleted file mode 100644
index 66d714c..0000000
--- a/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm_active.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" fill="rgb(51,103,214)">
-  <path d="M7 12v4H4V4a1 1 0 0 1 1-1h8a3 3 0 0 1 3 3v3a3 3 0 0 1-3 3H7zm0-6v3h5a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H7z"/>
-</svg>
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 8682728..c1c9610c 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -272,13 +272,12 @@
 };
 
 /**
- * If entry is MyFiles/Downloads or MyFiles/PluginVm, we don't allow
- * cut/delete/rename.
+ * If entry is MyFiles/Downloads, we don't allow cut/delete/rename.
  * @param {!VolumeManager} volumeManager
  * @param {(Entry|FakeEntry)} entry Entry or a fake entry.
  * @return {boolean}
  */
-CommandUtil.isReadOnly = (volumeManager, entry) => {
+CommandUtil.isDownloads = (volumeManager, entry) => {
   if (!entry) {
     return false;
   }
@@ -296,13 +295,10 @@
     return false;
   }
 
-  if (volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS) {
-    if (util.isMyFilesVolumeEnabled() && entry.fullPath === '/Downloads') {
-      return true;
-    }
-    if (util.isPluginVmEnabled() && entry.fullPath === '/PluginVm') {
-      return true;
-    }
+  if (util.isMyFilesVolumeEnabled() &&
+      volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS &&
+      entry.fullPath === '/Downloads') {
+    return true;
   }
   return false;
 };
@@ -1035,7 +1031,7 @@
 
     /**
      * Returns True if any entry belongs to a read-only volume or is
-     * forced to be read-only like MyFiles>Downloads.
+     * MyFiles>Downloads.
      * @param {!Array<!Entry>} entries
      * @param {!CommandHandlerDeps} fileManager
      * @return {boolean} True if entries contain read only entry.
@@ -1044,7 +1040,7 @@
       return entries.some(entry => {
         const locationInfo = fileManager.volumeManager.getLocationInfo(entry);
         return (locationInfo && locationInfo.isReadOnly) ||
-            CommandUtil.isReadOnly(fileManager.volumeManager, entry);
+            CommandUtil.isDownloads(fileManager.volumeManager, entry);
       });
     }
   };
@@ -1199,7 +1195,7 @@
    */
   execute: function(event, fileManager) {
     const entry = CommandUtil.getCommandEntry(fileManager, event.target);
-    if (CommandUtil.isReadOnly(fileManager.volumeManager, entry)) {
+    if (CommandUtil.isDownloads(fileManager.volumeManager, entry)) {
       return;
     }
     if (event.target instanceof DirectoryTree ||
@@ -1274,7 +1270,7 @@
         !CommandUtil.shouldShowMenuItemsForEntry(
             fileManager.volumeManager, entries[0]) ||
         entries.some(
-            CommandUtil.isReadOnly.bind(null, fileManager.volumeManager))) {
+            CommandUtil.isDownloads.bind(null, fileManager.volumeManager))) {
       event.canExecute = false;
       event.command.setHidden(true);
       return;
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index 8ab46bf4..51d9afe 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -1430,7 +1430,7 @@
     }
 
     // For MyFiles/Downloads we only allow copy.
-    if (isMove && this.isCopyOnly_(entry)) {
+    if (isMove && this.isDownloads_(entry)) {
       return false;
     }
 
@@ -1464,7 +1464,7 @@
 
   // For MyFiles/Downloads we only allow copy.
   if (isMove &&
-      this.selectionHandler_.selection.entries.some(this.isCopyOnly_, this)) {
+      this.selectionHandler_.selection.entries.some(this.isDownloads_, this)) {
     return false;
   }
 
@@ -1831,12 +1831,11 @@
 };
 
 /**
- * Returns True if entry is folder which we enforce to be read-only
- * or copy-only such as MyFiles>Downloads or MyFiles>PluginVm.
+ * Returns True if entry is MyFiles>Downloads.
  * @param {(!Entry|!FakeEntry)} entry Entry or a fake entry.
  * @return {boolean}
  */
-FileTransferController.prototype.isCopyOnly_ = function(entry) {
+FileTransferController.prototype.isDownloads_ = function(entry) {
   if (util.isFakeEntry(entry)) {
     return false;
   }
@@ -1846,13 +1845,10 @@
     return false;
   }
 
-  if (volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS) {
-    if (util.isMyFilesVolumeEnabled() && entry.fullPath === '/Downloads') {
-      return true;
-    }
-    if (util.isPluginVmEnabled() && entry.fullPath === '/PluginVm') {
-      return true;
-    }
+  if (util.isMyFilesVolumeEnabled() &&
+      volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS &&
+      entry.fullPath === '/Downloads') {
+    return true;
   }
   return false;
 };
diff --git a/ui/file_manager/file_manager/test/BUILD.gn b/ui/file_manager/file_manager/test/BUILD.gn
index 16cf61f..d65a38d 100644
--- a/ui/file_manager/file_manager/test/BUILD.gn
+++ b/ui/file_manager/file_manager/test/BUILD.gn
@@ -43,7 +43,6 @@
     ":crostini_share",
     ":crostini_tasks",
     ":menu",
-    ":plugin_vm",
     ":progress_center",
     ":uma",
   ]
@@ -103,13 +102,6 @@
   ]
 }
 
-js_library("plugin_vm") {
-  deps = [
-    "js:test_util",
-    "//ui/webui/resources/js:webui_resource_test",
-  ]
-}
-
 js_library("progress_center") {
   deps = [
     "js:test_util",
diff --git a/ui/file_manager/file_manager/test/js/strings.js b/ui/file_manager/file_manager/test/js/strings.js
index 5d8a144..bdc2a301 100644
--- a/ui/file_manager/file_manager/test/js/strings.js
+++ b/ui/file_manager/file_manager/test/js/strings.js
@@ -26,4 +26,4 @@
 // Overwrite LoadTimeData.prototype.data setter as nop.
 // Default implementation throws errors when both background and
 // foreground re-set loadTimeData.data.
-Object.defineProperty(LoadTimeData.prototype, 'data', {set: () => {}});
\ No newline at end of file
+Object.defineProperty(LoadTimeData.prototype, 'data', {set: () => {}});
diff --git a/ui/file_manager/file_manager/test/js/test_util.js b/ui/file_manager/file_manager/test/js/test_util.js
index 9328787..0b0208da 100644
--- a/ui/file_manager/file_manager/test/js/test_util.js
+++ b/ui/file_manager/file_manager/test/js/test_util.js
@@ -234,15 +234,6 @@
   linuxFiles: new test.TestEntryInfo(
       test.EntryType.DIRECTORY, '', 'Linux files', '', test.SharedOption.NONE,
       '...', 'Linux files', '--', 'Folder'),
-
-  pluginVm: new test.TestEntryInfo(
-      test.EntryType.DIRECTORY, '', 'PluginVm', '', test.SharedOption.NONE,
-      'Jan 1, 2000, 1:00 AM', 'Plugin VM', '--', 'Folder'),
-
-  photosInPluginVm: new test.TestEntryInfo(
-      test.EntryType.DIRECTORY, '', 'PluginVm/photos', '',
-      test.SharedOption.NONE, 'Jan 1, 1980, 11:59 PM', 'photos', '--',
-      'Folder'),
 };
 
 /**
diff --git a/ui/file_manager/file_manager/test/plugin_vm.js b/ui/file_manager/file_manager/test/plugin_vm.js
deleted file mode 100644
index 05c978c..0000000
--- a/ui/file_manager/file_manager/test/plugin_vm.js
+++ /dev/null
@@ -1,83 +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.
-
-const pluginVm = {};
-
-pluginVm.testLabelIconContextMenu = async (done) => {
-  const fileMenu = [
-    ['#cut', false],
-    ['#copy', true],
-    ['#paste-into-folder', false],
-    ['#get-info', true],
-    ['#delete', false],
-    ['#zip-selection', true],
-    ['#share-with-linux', true],
-    ['#manage-plugin-vm-sharing', true],
-    ['#new-folder', true],
-  ];
-
-  const fileMenuSubfolder = [
-    ['#cut', true],
-    ['#copy', true],
-    ['#paste-into-folder', false],
-    ['#get-info', true],
-    ['#rename', true],
-    ['#delete', true],
-    ['#zip-selection', true],
-    ['#share-with-linux', true],
-    ['#manage-plugin-vm-sharing', true],
-    ['#new-folder', true],
-  ];
-
-  const pluginVmFolder = '#file-list [file-name="PluginVm"]';
-  const iconFolder =
-      '#file-list [file-name="PluginVm"] [file-type-icon="plugin_vm"]';
-  const fileMenuShown = '#file-context-menu:not([hidden])';
-
-  const iconDirTree = '#directory-tree [file-type-icon="plugin_vm"]';
-  const dirTreeMenuShown = '#directory-tree-context-menu:not([hidden])';
-  const itemsShown = ' cr-menu-item:not([hidden])';
-
-  async function menuItems(menuTypeShown) {
-    await test.waitForElement(menuTypeShown);
-    const list = document.querySelectorAll(menuTypeShown + itemsShown);
-    return Array.from(document.querySelectorAll(menuTypeShown + itemsShown))
-        .map(e => [e.attributes['command'].value, !e.disabled]);
-  }
-
-  // Verify that /PluginVm has label 'Plugin VM'.
-  await test.setupAndWaitUntilReady([], [], []);
-  test.addEntries(
-      [test.ENTRIES.pluginVm, test.ENTRIES.photosInPluginVm], [], []);
-  assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh');
-  await test.waitForFiles(test.TestEntryInfo.getExpectedRows(
-      [test.ENTRIES.pluginVm, test.ENTRIES.linuxFiles]));
-
-  // Register /PluginVm as shared.
-  const pluginVmEntry =
-      mockVolumeManager
-          .getCurrentProfileVolumeInfo(VolumeManagerCommon.VolumeType.DOWNLOADS)
-          .fileSystem.entries['/PluginVm'];
-  fileManager.crostini.registerSharedPath('PluginVm', pluginVmEntry);
-
-  // Verify folder icon.
-  await test.waitForElement(iconFolder);
-
-  // Verify /PluginVm folder context menu.
-  assertTrue(test.fakeMouseRightClick(pluginVmFolder));
-  let items = await menuItems(fileMenuShown);
-  assertEquals(JSON.stringify(fileMenu), JSON.stringify(items));
-
-  // Change to 'PluginVm' directory, photos folder is shown.
-  assertTrue(test.fakeMouseDoubleClick(pluginVmFolder));
-  await test.waitForFiles(
-      test.TestEntryInfo.getExpectedRows([test.ENTRIES.photos]));
-
-  // Verify /PluginVm/photos folder context menu.
-  assertTrue(test.fakeMouseRightClick('#file-list [file-name="photos"]'));
-  items = await menuItems(fileMenuShown);
-  assertEquals(JSON.stringify(fileMenuSubfolder), JSON.stringify(items));
-
-  done();
-};
\ No newline at end of file
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc
index 08e0146b..4817b5a 100644
--- a/ui/message_center/views/message_view.cc
+++ b/ui/message_center/views/message_view.cc
@@ -320,9 +320,6 @@
 }
 
 void MessageView::OnSlideOut() {
-  for (auto* observer : slide_observers_)
-    observer->OnSlideOut(notification_id_);
-
   MessageCenter::Get()->RemoveNotification(notification_id_,
                                            true /* by_user */);
 }
diff --git a/ui/message_center/views/message_view.h b/ui/message_center/views/message_view.h
index 68d7872..8b59c16 100644
--- a/ui/message_center/views/message_view.h
+++ b/ui/message_center/views/message_view.h
@@ -54,7 +54,6 @@
 
     virtual void OnSlideStarted(const std::string& notification_id) {}
     virtual void OnSlideChanged(const std::string& notification_id) {}
-    virtual void OnSlideOut(const std::string& notification_id) {}
   };
 
   enum class Mode {
diff --git a/ui/message_center/views/slide_out_controller.cc b/ui/message_center/views/slide_out_controller.cc
index 374a9af..d9d975b1 100644
--- a/ui/message_center/views/slide_out_controller.cc
+++ b/ui/message_center/views/slide_out_controller.cc
@@ -132,11 +132,9 @@
       gesture_amount_ = 0.f;
       break;
     case SwipeControlOpenState::OPEN_ON_RIGHT:
-      gesture_amount_ = -swipe_control_width_;
       transform.Translate(-swipe_control_width_, 0);
       break;
     case SwipeControlOpenState::OPEN_ON_LEFT:
-      gesture_amount_ = swipe_control_width_;
       transform.Translate(swipe_control_width_, 0);
       break;
   }
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc
index e3fdceb..3dd69b5 100644
--- a/ui/native_theme/native_theme.cc
+++ b/ui/native_theme/native_theme.cc
@@ -44,14 +44,32 @@
 NativeTheme::NativeTheme()
     : thumb_inactive_color_(0xeaeaea),
       thumb_active_color_(0xf4f4f4),
-      track_color_(0xd3d3d3) {
-}
+      track_color_(0xd3d3d3),
+      is_dark_mode_(IsForcedDarkMode()),
+      is_high_contrast_(IsForcedHighContrast()) {}
 
 NativeTheme::~NativeTheme() {}
 
 bool NativeTheme::SystemDarkModeEnabled() const {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kForceDarkMode);
+  return is_dark_mode_;
+}
+
+bool NativeTheme::UsesHighContrastColors() const {
+  return is_high_contrast_;
+}
+
+bool NativeTheme::IsForcedDarkMode() const {
+  static bool kIsForcedDarkMode =
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kForceDarkMode);
+  return kIsForcedDarkMode;
+}
+
+bool NativeTheme::IsForcedHighContrast() const {
+  static bool kIsForcedHighContrast =
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kForceHighContrast);
+  return kIsForcedHighContrast;
 }
 
 CaptionStyle NativeTheme::GetSystemCaptionStyle() const {
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index cf93b2c..7e3ee539 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -415,7 +415,7 @@
 
   // Returns whether this NativeTheme uses higher-contrast colors, controlled by
   // system accessibility settings and the system theme.
-  virtual bool UsesHighContrastColors() const = 0;
+  virtual bool UsesHighContrastColors() const;
 
   // Whether OS-level dark mode (as in macOS Mojave or Windows 10) is enabled.
   virtual bool SystemDarkModeEnabled() const;
@@ -427,6 +427,16 @@
   NativeTheme();
   virtual ~NativeTheme();
 
+  // Whether high contrast is forced via command-line flag.
+  bool IsForcedHighContrast() const;
+  // Whether dark mode is forced via command-line flag.
+  bool IsForcedDarkMode() const;
+
+  void set_dark_mode(bool is_dark_mode) { is_dark_mode_ = is_dark_mode; }
+  void set_high_contrast(bool is_high_contrast) {
+    is_high_contrast_ = is_high_contrast;
+  }
+
   unsigned int thumb_inactive_color_;
   unsigned int thumb_active_color_;
   unsigned int track_color_;
@@ -435,6 +445,9 @@
   // Observers to notify when the native theme changes.
   base::ObserverList<NativeThemeObserver>::Unchecked native_theme_observers_;
 
+  bool is_dark_mode_ = false;
+  bool is_high_contrast_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(NativeTheme);
 };
 
diff --git a/ui/native_theme/native_theme_base.cc b/ui/native_theme/native_theme_base.cc
index 7a5c941..c2d9778b 100644
--- a/ui/native_theme/native_theme_base.cc
+++ b/ui/native_theme/native_theme_base.cc
@@ -264,11 +264,6 @@
   return gfx::Rect();
 }
 
-bool NativeThemeBase::UsesHighContrastColors() const {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kForceHighContrast);
-}
-
 NativeThemeBase::NativeThemeBase()
     : scrollbar_width_(kDefaultScrollbarWidth),
       scrollbar_button_length_(kDefaultScrollbarButtonLength) {
diff --git a/ui/native_theme/native_theme_base.h b/ui/native_theme/native_theme_base.h
index b5feed4..0e349c5d 100644
--- a/ui/native_theme/native_theme_base.h
+++ b/ui/native_theme/native_theme_base.h
@@ -36,7 +36,6 @@
   bool SupportsNinePatch(Part part) const override;
   gfx::Size GetNinePatchCanvasSize(Part part) const override;
   gfx::Rect GetNinePatchAperture(Part part) const override;
-  bool UsesHighContrastColors() const override;
 
  protected:
   NativeThemeBase();
diff --git a/ui/native_theme/native_theme_mac.h b/ui/native_theme/native_theme_mac.h
index 3605eae..e27bde7f 100644
--- a/ui/native_theme/native_theme_mac.h
+++ b/ui/native_theme/native_theme_mac.h
@@ -56,8 +56,6 @@
       State state,
       const gfx::Rect& rect,
       const MenuItemExtraParams& menu_item) const override;
-  bool UsesHighContrastColors() const override;
-  bool SystemDarkModeEnabled() const override;
 
   // Paints the styled button shape used for default controls on Mac. The basic
   // style is used for dialog buttons, comboboxes, and tabbed pane tabs.
@@ -70,9 +68,6 @@
                                         bool round_right,
                                         bool focus);
 
-  // Updates cached dark mode status and notifies observers if it has changed.
-  void UpdateDarkModeStatus();
-
  protected:
   friend class NativeTheme;
   friend class base::NoDestructor<NativeThemeMac>;
@@ -91,8 +86,6 @@
       appearance_observer_;
   id high_contrast_notification_token_;
 
-  bool is_dark_mode_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(NativeThemeMac);
 };
 
diff --git a/ui/native_theme/native_theme_mac.mm b/ui/native_theme/native_theme_mac.mm
index 6246830..8734a239 100644
--- a/ui/native_theme/native_theme_mac.mm
+++ b/ui/native_theme/native_theme_mac.mm
@@ -9,6 +9,7 @@
 
 #include "base/command_line.h"
 #include "base/mac/mac_util.h"
+#include "base/mac/scoped_block.h"
 #include "base/mac/sdk_forward_declarations.h"
 #include "base/macros.h"
 #import "skia/ext/skia_utils_mac.h"
@@ -29,7 +30,15 @@
         ]];
     return [appearance isEqual:NSAppearanceNameDarkAqua];
   }
+  return false;
+}
 
+bool IsHighContrast() {
+  NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
+  if ([workspace respondsToSelector:@selector
+                 (accessibilityDisplayShouldIncreaseContrast)]) {
+    return workspace.accessibilityDisplayShouldIncreaseContrast;
+  }
   return false;
 }
 }  // namespace
@@ -45,13 +54,13 @@
 @end
 
 @implementation NativeThemeEffectiveAppearanceObserver {
-  ui::NativeThemeMac* owner_;
+  base::mac::ScopedBlock<void (^)()> handler_;
 }
 
-- (instancetype)initWithOwner:(ui::NativeThemeMac*)owner {
+- (instancetype)initWithHandler:(void (^)())handler {
   self = [super init];
   if (self) {
-    owner_ = owner;
+    handler_.reset([handler copy]);
     if (@available(macOS 10.14, *)) {
       [NSApp addObserver:self
               forKeyPath:@"effectiveAppearance"
@@ -73,7 +82,7 @@
                       ofObject:(id)object
                         change:(NSDictionary*)change
                        context:(void*)context {
-  owner_->UpdateDarkModeStatus();
+  handler_.get()();
 }
 
 @end
@@ -267,42 +276,31 @@
   }
 }
 
-bool NativeThemeMac::UsesHighContrastColors() const {
-  if (NativeThemeBase::UsesHighContrastColors())
-    return true;
-  NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
-  if ([workspace respondsToSelector:@selector
-                 (accessibilityDisplayShouldIncreaseContrast)]) {
-    return workspace.accessibilityDisplayShouldIncreaseContrast;
-  }
-  return false;
-}
-
-bool NativeThemeMac::SystemDarkModeEnabled() const {
-  if (@available(macOS 10.14, *)) {
-    return is_dark_mode_;
-  } else {
-    // Support "--force-dark-mode" in macOS < 10.14.
-    return NativeThemeBase::SystemDarkModeEnabled();
-  }
-}
-
 NativeThemeMac::NativeThemeMac() {
   if (base::FeatureList::IsEnabled(features::kDarkMode)) {
-    is_dark_mode_ = IsDarkMode();
+    __block auto theme = this;
+    set_dark_mode(IsDarkMode());
     appearance_observer_.reset(
-        [[NativeThemeEffectiveAppearanceObserver alloc] initWithOwner:this]);
+        [[NativeThemeEffectiveAppearanceObserver alloc] initWithHandler:^{
+          theme->set_dark_mode(IsDarkMode());
+          theme->NotifyObservers();
+        }]);
   }
   if (@available(macOS 10.10, *)) {
-    high_contrast_notification_token_ = [[[NSWorkspace sharedWorkspace]
-        notificationCenter]
-        addObserverForName:
-            NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification
-                    object:nil
-                     queue:nil
-                usingBlock:^(NSNotification* notification) {
-                  ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers();
-                }];
+    if (!IsForcedHighContrast()) {
+      set_high_contrast(IsHighContrast());
+      __block auto theme = this;
+      high_contrast_notification_token_ =
+          [[[NSWorkspace sharedWorkspace] notificationCenter]
+              addObserverForName:
+                  NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification
+                          object:nil
+                           queue:nil
+                      usingBlock:^(NSNotification* notification) {
+                        theme->set_high_contrast(IsHighContrast());
+                        theme->NotifyObservers();
+                      }];
+    }
   }
 }
 
@@ -319,11 +317,4 @@
   canvas->drawRect(gfx::RectToSkRect(rect), flags);
 }
 
-void NativeThemeMac::UpdateDarkModeStatus() {
-  bool was_dark_mode = is_dark_mode_;
-  is_dark_mode_ = IsDarkMode();
-  if (was_dark_mode != is_dark_mode_)
-    NotifyObservers();
-}
-
 }  // namespace ui
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc
index 04037466..24972aa 100644
--- a/ui/native_theme/native_theme_win.cc
+++ b/ui/native_theme/native_theme_win.cc
@@ -238,9 +238,7 @@
       open_theme_(NULL),
       close_theme_(NULL),
       theme_dll_(LoadLibrary(L"uxtheme.dll")),
-      color_change_listener_(this),
-      is_using_high_contrast_(false),
-      is_using_high_contrast_valid_(false) {
+      color_change_listener_(this) {
   if (theme_dll_) {
     draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
         GetProcAddress(theme_dll_, "DrawThemeBackground"));
@@ -255,7 +253,9 @@
     close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
         GetProcAddress(theme_dll_, "CloseThemeData"));
   }
-  if (base::FeatureList::IsEnabled(features::kDarkMode)) {
+
+  if (!IsForcedDarkMode() && !IsForcedHighContrast() &&
+      base::FeatureList::IsEnabled(features::kDarkMode)) {
     // Dark Mode currently targets UWP apps, which means Win32 apps need to use
     // alternate, less reliable means of detecting the state. The following
     // can break in future Windows versions.
@@ -265,9 +265,13 @@
             L"Software\\Microsoft\\Windows\\CurrentVersion\\"
             L"Themes\\Personalize",
             KEY_READ | KEY_NOTIFY) == ERROR_SUCCESS;
-    if (key_open_succeeded)
+    if (key_open_succeeded) {
+      UpdateDarkModeStatus();
       RegisterThemeRegkeyObserver();
+    }
   }
+  if (!IsForcedHighContrast())
+    set_high_contrast(IsUsingHighContrastThemeInternal());
   memset(theme_handles_, 0, sizeof(theme_handles_));
 
   // Initialize the cached system colors.
@@ -284,15 +288,10 @@
 }
 
 bool NativeThemeWin::IsUsingHighContrastThemeInternal() const {
-  if (is_using_high_contrast_valid_)
-    return is_using_high_contrast_;
   HIGHCONTRAST result;
   result.cbSize = sizeof(HIGHCONTRAST);
-  is_using_high_contrast_ =
-      SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
-      (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
-  is_using_high_contrast_valid_ = true;
-  return is_using_high_contrast_;
+  return SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
+         (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
 }
 
 void NativeThemeWin::CloseHandlesInternal() {
@@ -309,7 +308,8 @@
 
 void NativeThemeWin::OnSysColorChange() {
   UpdateSystemColors();
-  is_using_high_contrast_valid_ = false;
+  if (!IsForcedHighContrast())
+    set_high_contrast(IsUsingHighContrastThemeInternal());
   NotifyObservers();
 }
 
@@ -579,26 +579,13 @@
   return gfx::Rect();
 }
 
-bool NativeThemeWin::UsesHighContrastColors() const {
-  bool force_enabled = base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kForceHighContrast);
-  return force_enabled || IsUsingHighContrastThemeInternal();
-}
-
 bool NativeThemeWin::SystemDarkModeEnabled() const {
   // Windows high contrast modes are entirely different themes,
   // so let them take priority over dark mode.
   // ...unless --force-dark-mode was specified in which case caveat emptor.
-  if (UsesHighContrastColors() && !NativeTheme::SystemDarkModeEnabled())
+  if (UsesHighContrastColors() && !IsForcedDarkMode())
     return false;
-  bool fDarkModeEnabled = false;
-  if (hkcu_themes_regkey_.Valid()) {
-    DWORD apps_use_light_theme = 1;
-    hkcu_themes_regkey_.ReadValueDW(L"AppsUseLightTheme",
-                                    &apps_use_light_theme);
-    fDarkModeEnabled = (apps_use_light_theme == 0);
-  }
-  return fDarkModeEnabled || NativeTheme::SystemDarkModeEnabled();
+  return NativeTheme::SystemDarkModeEnabled();
 }
 
 void NativeThemeWin::PaintIndirect(cc::PaintCanvas* destination_canvas,
@@ -1933,6 +1920,7 @@
   DCHECK(hkcu_themes_regkey_.Valid());
   hkcu_themes_regkey_.StartWatching(base::BindOnce(
       [](NativeThemeWin* native_theme) {
+        native_theme->UpdateDarkModeStatus();
         native_theme->NotifyObservers();
         // RegKey::StartWatching only provides one notification. Reregistration
         // is required to get future notifications.
@@ -1941,4 +1929,15 @@
       base::Unretained(this)));
 }
 
+void NativeThemeWin::UpdateDarkModeStatus() {
+  bool fDarkModeEnabled = false;
+  if (hkcu_themes_regkey_.Valid()) {
+    DWORD apps_use_light_theme = 1;
+    hkcu_themes_regkey_.ReadValueDW(L"AppsUseLightTheme",
+                                    &apps_use_light_theme);
+    fDarkModeEnabled = (apps_use_light_theme == 0);
+  }
+  set_dark_mode(fDarkModeEnabled);
+}
+
 }  // namespace ui
diff --git a/ui/native_theme/native_theme_win.h b/ui/native_theme/native_theme_win.h
index cdc2157..cabb8bc 100644
--- a/ui/native_theme/native_theme_win.h
+++ b/ui/native_theme/native_theme_win.h
@@ -80,7 +80,6 @@
   bool SupportsNinePatch(Part part) const override;
   gfx::Size GetNinePatchCanvasSize(Part part) const override;
   gfx::Rect GetNinePatchAperture(Part part) const override;
-  bool UsesHighContrastColors() const override;
   bool SystemDarkModeEnabled() const override;
 
  protected:
@@ -263,6 +262,7 @@
   HANDLE GetThemeHandle(ThemeName theme_name) const;
 
   void RegisterThemeRegkeyObserver();
+  void UpdateDarkModeStatus();
 
   typedef HRESULT (WINAPI* DrawThemeBackgroundPtr)(HANDLE theme,
                                                    HDC hdc,
@@ -328,12 +328,6 @@
   gfx::ScopedSysColorChangeListener color_change_listener_;
   mutable std::map<int, SkColor> system_colors_;
 
-  // Is a high contrast theme active?
-  mutable bool is_using_high_contrast_;
-
-  // Is |is_using_high_contrast_| valid?
-  mutable bool is_using_high_contrast_valid_;
-
   DISALLOW_COPY_AND_ASSIGN(NativeThemeWin);
 };
 
diff --git a/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc b/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc
index a8fb25a..e21bff0 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc
@@ -9,7 +9,8 @@
 
 #include "base/files/scoped_file.h"
 #include "base/macros.h"
-#include "base/memory/shared_memory.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "base/posix/eintr_wrapper.h"
 #include "ui/gfx/vsync_provider.h"
 #include "ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h"
@@ -18,8 +19,8 @@
 
 namespace {
 
-void DeleteSharedMemory(void* pixels, void* context) {
-  delete static_cast<base::SharedMemory*>(context);
+void DeleteSharedMemoryMapping(void* pixels, void* context) {
+  delete static_cast<base::WritableSharedMemoryMapping*>(context);
 }
 
 }  // namespace
@@ -39,29 +40,34 @@
   DCHECK(!size_.IsEmpty());
 
   size_t length = size_.width() * size_.height() * 4;
-  auto shared_memory = std::make_unique<base::SharedMemory>();
-  if (!shared_memory->CreateAndMapAnonymous(length)) {
+  base::UnsafeSharedMemoryRegion shm_region =
+      base::UnsafeSharedMemoryRegion::Create(length);
+  if (!shm_region.IsValid())
     return nullptr;
-  }
 
-  base::ScopedFD fd(HANDLE_EINTR(dup(shared_memory->handle().GetHandle())));
-  if (!fd.is_valid()) {
-    PLOG(FATAL) << "dup";
+  base::WritableSharedMemoryMapping shm_mapping = shm_region.Map();
+  if (!shm_mapping.IsValid())
     return nullptr;
-  }
 
-  base::File file(fd.release());
+  base::subtle::PlatformSharedMemoryRegion platform_shm =
+      base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
+          std::move(shm_region));
+  base::subtle::ScopedFDPair fd_pair = platform_shm.PassPlatformHandle();
+  base::File file(fd_pair.fd.release());
   connection_->CreateShmBufferForWidget(widget_, std::move(file), length,
                                         size_);
 
+  auto shm_mapping_on_heap =
+      std::make_unique<base::WritableSharedMemoryMapping>(
+          std::move(shm_mapping));
   sk_surface_ = SkSurface::MakeRasterDirectReleaseProc(
       SkImageInfo::MakeN32Premul(size_.width(), size_.height()),
-      shared_memory->memory(), size_.width() * 4, &DeleteSharedMemory,
-      shared_memory.get(), nullptr);
+      shm_mapping_on_heap->memory(), size_.width() * 4,
+      &DeleteSharedMemoryMapping, shm_mapping_on_heap.get(), nullptr);
   if (!sk_surface_)
     return nullptr;
 
-  ignore_result(shared_memory.release());
+  ignore_result(shm_mapping_on_heap.release());
   return sk_surface_;
 }
 
diff --git a/ui/ozone/platform/wayland/host/wayland_cursor.cc b/ui/ozone/platform/wayland/host/wayland_cursor.cc
index 79d5834..d397281 100644
--- a/ui/ozone/platform/wayland/host/wayland_cursor.cc
+++ b/ui/ozone/platform/wayland/host/wayland_cursor.cc
@@ -7,7 +7,6 @@
 #include <memory>
 #include <vector>
 
-#include "base/memory/shared_memory.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/ozone/platform/wayland/common/wayland_util.h"
diff --git a/ui/ozone/platform/wayland/host/wayland_keyboard_unittest.cc b/ui/ozone/platform/wayland/host/wayland_keyboard_unittest.cc
index 60eec44..2ca1faa 100644
--- a/ui/ozone/platform/wayland/host/wayland_keyboard_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_keyboard_unittest.cc
@@ -18,7 +18,9 @@
 
 #if BUILDFLAG(USE_XKBCOMMON)
 #include "base/memory/free_deleter.h"
-#include "base/memory/shared_memory.h"
+#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "ui/events/keycodes/scoped_xkb.h"  // nogncheck
 #endif
 
@@ -53,13 +55,20 @@
         xkb_keymap_get_as_string(xkb_keymap_.get(), XKB_KEYMAP_FORMAT_TEXT_V1));
     DCHECK(keymap_string.get());
     size_t keymap_size = strlen(keymap_string.get()) + 1;
-    base::SharedMemory shared_keymap;
-    bool rv = shared_keymap.CreateAndMapAnonymous(keymap_size);
-    DCHECK(rv);
+
+    base::UnsafeSharedMemoryRegion shared_keymap_region =
+        base::UnsafeSharedMemoryRegion::Create(keymap_size);
+    base::WritableSharedMemoryMapping shared_keymap =
+        shared_keymap_region.Map();
+    base::subtle::PlatformSharedMemoryRegion platform_shared_keymap =
+        base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
+            std::move(shared_keymap_region));
+    DCHECK(shared_keymap.IsValid());
+
     memcpy(shared_keymap.memory(), keymap_string.get(), keymap_size);
-    wl_keyboard_send_keymap(keyboard_->resource(),
-                            WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
-                            shared_keymap.handle().GetHandle(), keymap_size);
+    wl_keyboard_send_keymap(
+        keyboard_->resource(), WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+        platform_shared_keymap.PassPlatformHandle().fd.release(), keymap_size);
 #endif
   }
 
diff --git a/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc b/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc
index d5d2c25..839fd07 100644
--- a/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc
+++ b/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc
@@ -4,6 +4,8 @@
 
 #include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h"
 
+#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
 
@@ -16,7 +18,7 @@
 namespace ui {
 
 WaylandShmBuffer::WaylandShmBuffer(wl_shm* shm, const gfx::Size& size)
-    : size_(size), shared_memory_(std::make_unique<base::SharedMemory>()) {
+    : size_(size) {
   Initialize(shm);
 }
 
@@ -35,19 +37,25 @@
   if (buffer_size == SIZE_MAX)
     return;
 
-  if (!shared_memory_->CreateAndMapAnonymous(buffer_size)) {
+  base::UnsafeSharedMemoryRegion shared_memory_region =
+      base::UnsafeSharedMemoryRegion::Create(buffer_size);
+  shared_memory_mapping_ = shared_memory_region.Map();
+  if (!shared_memory_mapping_.IsValid()) {
     PLOG(ERROR) << "Create and mmap failed.";
     return;
   }
+  base::subtle::PlatformSharedMemoryRegion platform_shared_memory =
+      base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
+          std::move(shared_memory_region));
 
-  auto handle = shared_memory_->handle().GetHandle();
-  wl::Object<wl_shm_pool> pool(wl_shm_create_pool(shm, handle, buffer_size));
+  wl::Object<wl_shm_pool> pool(wl_shm_create_pool(
+      shm, platform_shared_memory.PassPlatformHandle().fd.release(),
+      buffer_size));
 
   auto* new_buffer = wl_shm_pool_create_buffer(
       pool.get(), 0, size_.width(), size_.height(), stride, kShmFormat);
   if (!new_buffer) {
-    shared_memory_->Unmap();
-    shared_memory_->Close();
+    shared_memory_mapping_ = base::WritableSharedMemoryMapping();
     return;
   }
 
@@ -56,9 +64,9 @@
 }
 
 uint8_t* WaylandShmBuffer::GetMemory() const {
-  if (!IsValid() || !shared_memory_)
+  if (!IsValid() || !shared_memory_mapping_.IsValid())
     return nullptr;
-  return static_cast<uint8_t*>(shared_memory_->memory());
+  return shared_memory_mapping_.GetMemoryAs<uint8_t>();
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_shm_buffer.h b/ui/ozone/platform/wayland/host/wayland_shm_buffer.h
index 8ed08df..1b70920e 100644
--- a/ui/ozone/platform/wayland/host/wayland_shm_buffer.h
+++ b/ui/ozone/platform/wayland/host/wayland_shm_buffer.h
@@ -10,7 +10,7 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/memory/shared_memory.h"
+#include "base/memory/shared_memory_mapping.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "ui/gfx/geometry/size.h"
@@ -23,7 +23,7 @@
 // (2) Buffers created using file descriptor (e.g: sent by gpu process/thread,
 // through IPC), not mapped in local memory address space.
 // WaylandShmBuffer is moveable, non-copyable, and is assumed to own both
-// wl_buffer and SharedMemory (if any) instance.
+// wl_buffer and WritableSharedMemoryMapping (if any) instance.
 class WaylandShmBuffer {
  public:
   WaylandShmBuffer(wl_shm* shm, const gfx::Size& size);
@@ -54,7 +54,7 @@
   gfx::Size size_;
   int stride_;
   wl::Object<wl_buffer> buffer_;
-  std::unique_ptr<base::SharedMemory> shared_memory_ = nullptr;
+  base::WritableSharedMemoryMapping shared_memory_mapping_;
 
   DISALLOW_COPY_AND_ASSIGN(WaylandShmBuffer);
 };
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index 48b8161..bbcd9e7 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -168,8 +168,13 @@
       provider->GetInsetsMetric(INSETS_DIALOG_SUBSECTION));
   frame->SetFootnoteView(CreateFootnoteView());
 
+  BubbleBorder::Arrow adjusted_arrow = arrow();
+  if (base::i18n::IsRTL()) {
+    adjusted_arrow = BubbleBorder::horizontal_mirror(adjusted_arrow);
+    arrow_ = adjusted_arrow;
+  }
   std::unique_ptr<BubbleBorder> border =
-      std::make_unique<BubbleBorder>(arrow(), GetShadow(), color());
+      std::make_unique<BubbleBorder>(adjusted_arrow, GetShadow(), color());
   if (CustomShadowsSupported() && ShouldHaveRoundCorners()) {
     border->SetCornerRadius(
         base::FeatureList::IsEnabled(features::kEnableMDRoundedCornersOnDialogs)
@@ -273,8 +278,6 @@
 }
 
 void BubbleDialogDelegateView::SetArrow(BubbleBorder::Arrow arrow) {
-  if (base::i18n::IsRTL())
-    arrow = BubbleBorder::horizontal_mirror(arrow);
   if (arrow_ == arrow)
     return;
   arrow_ = arrow;
@@ -318,12 +321,12 @@
     : close_on_deactivate_(true),
       anchor_view_tracker_(std::make_unique<ViewTracker>()),
       anchor_widget_(nullptr),
+      arrow_(arrow),
       shadow_(shadow),
       color_explicitly_set_(false),
       accept_events_(true),
       adjust_if_offscreen_(true),
       parent_window_(nullptr) {
-  SetArrow(arrow);
   LayoutProvider* provider = LayoutProvider::Get();
   // An individual bubble should override these margins if its layout differs
   // from the typical title/text/buttons.
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h
index 00b5453..887bc541 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.h
+++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -81,7 +81,7 @@
   // The anchor rect is used in the absence of an assigned anchor view.
   const gfx::Rect& anchor_rect() const { return anchor_rect_; }
 
-  // Set the desired arrow for the bubble. The arrow will be mirrored for RTL.
+  BubbleBorder::Arrow arrow() const { return arrow_; }
   void SetArrow(BubbleBorder::Arrow arrow);
 
   BubbleBorder::Shadow GetShadow() const;
@@ -140,8 +140,6 @@
       BubbleBorder::Arrow arrow,
       BubbleBorder::Shadow shadow = BubbleBorder::DIALOG_SHADOW);
 
-  BubbleBorder::Arrow arrow() const { return arrow_; }
-
   // Get bubble bounds from the anchor rect and client view's preferred size.
   virtual gfx::Rect GetBubbleBounds();
 
@@ -220,8 +218,8 @@
   // The anchor rect used in the absence of an anchor view.
   mutable gfx::Rect anchor_rect_;
 
-  // The arrow's default location on the bubble post-RTL mirroring if needed.
-  BubbleBorder::Arrow arrow_ = BubbleBorder::NONE;
+  // The arrow's location on the bubble.
+  BubbleBorder::Arrow arrow_;
 
   // Bubble border shadow to use.
   BubbleBorder::Shadow shadow_;